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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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/229] 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 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 038/229] 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 039/229] 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 040/229] 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 041/229] 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 042/229] 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 043/229] 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 044/229] 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 045/229] 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 046/229] 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 047/229] 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 048/229] 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 049/229] 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 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 050/229] 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 051/229] 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 052/229] 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 053/229] 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 054/229] 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 055/229] 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 056/229] 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 057/229] 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 058/229] 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 059/229] 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 060/229] 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 061/229] 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 062/229] 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 063/229] 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 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 064/229] 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 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 065/229] 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 066/229] 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 067/229] 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 068/229] 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 069/229] 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 070/229] 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 071/229] 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 072/229] 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 073/229] 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 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 074/229] 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 075/229] 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 076/229] 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 077/229] 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 078/229] 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 079/229] 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 080/229] 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 081/229] 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 082/229] 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 083/229] 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 084/229] 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 085/229] 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 086/229] 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 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 087/229] 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 088/229] 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 089/229] Fix indentation in CircleCI configuration Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> 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 090/229] 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 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 091/229] 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 092/229] 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 093/229] 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 094/229] 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 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 095/229] 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 096/229] 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 097/229] 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 098/229] 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 099/229] 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 100/229] 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 101/229] 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 102/229] 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 103/229] 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 104/229] 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 105/229] 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 106/229] 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 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 107/229] 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 108/229] 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 109/229] 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 110/229] 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 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 111/229] 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 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 112/229] 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 113/229] 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 114/229] 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 115/229] 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 116/229] 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 117/229] 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 118/229] 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 119/229] 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 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 120/229] 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 121/229] 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 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 122/229] 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 123/229] 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 124/229] 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 125/229] 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 126/229] 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 127/229] 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 128/229] 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 129/229] 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 130/229] 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 131/229] 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 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 132/229] 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 133/229] 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 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 134/229] 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 135/229] 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 136/229] 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 137/229] 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 138/229] 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 139/229] 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 140/229] 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 141/229] 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 142/229] 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 143/229] 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 144/229] 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 145/229] 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 146/229] 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 147/229] 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 148/229] 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 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 149/229] 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 150/229] 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 151/229] 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 152/229] 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 153/229] 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 154/229] 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 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 155/229] 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 156/229] 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 157/229] 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 158/229] 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 159/229] 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 160/229] 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 161/229] 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 162/229] 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 163/229] 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 164/229] 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 165/229] 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 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 166/229] 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 167/229] 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 168/229] 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 169/229] 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 170/229] 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 171/229] 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 172/229] 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 173/229] 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 174/229] 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 175/229] 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 176/229] 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 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 177/229] 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 178/229] 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 179/229] 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 180/229] 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 181/229] 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 182/229] 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 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 183/229] 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 184/229] 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 185/229] 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 186/229] 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 187/229] 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 188/229] 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 189/229] 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 190/229] 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 191/229] 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 192/229] 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 193/229] 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 194/229] 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 195/229] 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 196/229] 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 197/229] 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 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 198/229] 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 199/229] 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 200/229] 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 201/229] 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 202/229] 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 203/229] 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 204/229] 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 205/229] 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 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 206/229] 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 207/229] 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 208/229] 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 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 209/229] 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 210/229] 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 211/229] 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 d6cfd684ee5c956757525985179cb310a36e149a Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 20 Feb 2026 01:50:18 +0700 Subject: [PATCH 212/229] 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> --- crates/test-utils/src/util.rs | 52 +++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index f98bfb941cfc0..d9e0839cc7974 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -3,7 +3,7 @@ use foundry_config::Config; use std::{ env, fs::{self, File}, - io::{IsTerminal, Read, Seek, Write}, + io::{self, IsTerminal, Read, Seek, Write}, path::{Path, PathBuf}, process::Command, sync::LazyLock, @@ -235,8 +235,54 @@ 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 { + // Choose the current working directory as the safe base for test utilities. + let base = env::current_dir()?; + + // 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<()> { From 2cf885fac38538dd8c0c00a3420561c3294c7328 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 21 Feb 2026 05:02:26 +0000 Subject: [PATCH 213/229] 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> --- crates/test-utils/src/util.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index d9e0839cc7974..8e0d7912871bb 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -9,6 +9,17 @@ use std::{ 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(|| { + let mut base = env::temp_dir(); + 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. @@ -247,8 +258,9 @@ pub fn copy_dir_filtered(src: &Path, dst: &Path) -> std::io::Result<()> { /// 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 { - // Choose the current working directory as the safe base for test utilities. - let base = env::current_dir()?; + // 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 From b6f27b02192279c883259dbf6cbbe8ccee581213 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 6 Mar 2026 02:07:05 +0700 Subject: [PATCH 214/229] Hardhat project (#378) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 history storage support (#13588) * fix(test): add missing rpc_before assertion in deep reorg blockhash test (#13586) * refactor(cheatcodes,evm): use JournalTr and JournalExt trait methods (#13581) Co-authored-by: Amp * fix(traces): filter empty string labels (#13601) * refactor(evm): extract `FoundryInspectorExt` from `InspectorExt` (#13600) Co-authored-by: Amp Co-authored-by: onbjerg * refactor(evm): pass &EvmState instead of &JournaledState to diagnose_revert (#13583) Co-authored-by: Amp * feat(wallets): introduce `BrowserWalletOpts` (#13602) * fix(script): auto-set sender from single keystore (#13525) * chore: clean-up `inner.inner` patterns using traits impls (#13595) * feat(primitives): impl `FoundryTransactionBuilder` for `Ethereum` (#13598) Co-authored-by: onbjerg * feat(common): add pretty-printing for `TempoTransactionReceipt` (#13594) Co-authored-by: onbjerg * fix(forge): correct solar Solidity version support message (#13592) Co-authored-by: onbjerg * chore(cheatcodes): use `JournalTr` methods instead of `as_db_env_and_journal` (#13596) Co-authored-by: Amp * chore: use `fs::write_json_file` in `NodeConfig::print` (#13567) * chore(test): tighten reorg blockhash assertions (#13590) * chore: remove duplicate dev-dependencies (#13597) Co-authored-by: onbjerg * fix(foundryup): support versioned installs for tempo network (#13607) Co-authored-by: onbjerg <8862627+onbjerg@users.noreply.github.com> * feat(evm): add `FoundryJournalExt` trait (#13570) * feat(evm): add `FoundryContextExt` trait (#13605) Co-authored-by: Claude Opus 4.6 * refactor(anvil): generic impersonated tx hash update (#13619) * feat(evm): add `NestedEvm` and `NestedEvmExt` traits (#13606) Co-authored-by: Claude Opus 4.6 * chore(deps): bump actions/attest-build-provenance from 3 to 4 (#13609) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/upload-artifact from 6 to 7 (#13608) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/update-flake-lock from 5909792a83875ddb5dd4b18734534a98a74a709c to e80a657d7603606be0c69b117cfdc240f1e6af88 (#13610) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.68.9 to 2.68.17 (#13612) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump mikepenz/release-changelog-builder-action from 6.1.0 to 6.1.1 (#13611) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * refactor(wallets): extract `Browser` from `WalletSigner` (#13613) * chore(evm): remove useless `configure_tx_req_env` arg `impersonated_from` (#13620) * feat(cheatcodes): generic `Cheatcode` (#13536) Co-authored-by: Claude Opus 4.6 * refactor(fmt): add tempo pprinting + cleanup/dedup (#13631) * chore: remove `ContextExt` trait (#13628) * chore: relax bounds on `FoundryTransactionBuilder` (#13627) * refactor(anvil): make Signer generic over Network (#13632) * chore(cheatcodes): use `Cfg`/`Transaction` trait methods in `Inspector` impl (#13621) Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * refactor(cheatcodes): generic `Inspector` for `Cheatcodes` (#13623) * chore: use trait methods for env access in `InspectorStack` (#13639) * chore(anvil): clean-up impersonated tx building (#13637) * chore(anvil): remove unused alloy-eip5792 dev-dependency (#13640) * 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> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@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: Amp Co-authored-by: Matthias Seitz Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Georgios Konstantopoulos Co-authored-by: zerosnacks Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Matt D Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: albertov19 <64150856+albertov19@users.noreply.github.com> Co-authored-by: aganisgash Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol 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: onbjerg 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: Gengar 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: Claude Opus 4.6 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 + .github/FLAKY_TEST_FAILURE_TEMPLATE.md | 10 + .../FLAKY_TEST_ISOLATE_FAILURE_TEMPLATE.md | 10 + .github/TEST_ISOLATE_FAILURE_TEMPLATE.md | 10 + .github/dependabot.yml | 2 + .github/workflows/benchmarks.yml | 4 +- .github/workflows/bump-forge-std.yml | 2 +- .github/workflows/ci.yml | 13 +- .github/workflows/docker-publish.yml | 21 +- .github/workflows/docs.yml | 2 +- .github/workflows/nix.yml | 6 +- .github/workflows/npm.yml | 4 +- .github/workflows/release.yml | 14 +- .github/workflows/test-flaky.yml | 68 + .github/workflows/test-isolate.yml | 76 + .github/workflows/test.yml | 4 +- Cargo.lock | 2278 ++++---- Cargo.toml | 42 +- Dockerfile | 18 +- Makefile | 31 +- README.md | 307 +- benches/Cargo.toml | 3 - crates/anvil/Cargo.toml | 2 - crates/anvil/core/src/eth/mod.rs | 27 +- crates/anvil/core/src/eth/transaction/mod.rs | 26 +- crates/anvil/server/src/pubsub.rs | 2 + crates/anvil/src/args.rs | 2 + crates/anvil/src/cmd.rs | 28 +- crates/anvil/src/config.rs | 83 +- crates/anvil/src/eth/api.rs | 164 +- crates/anvil/src/eth/backend/db.rs | 52 +- crates/anvil/src/eth/backend/executor.rs | 50 +- crates/anvil/src/eth/backend/fork.rs | 18 +- crates/anvil/src/eth/backend/genesis.rs | 2 + crates/anvil/src/eth/backend/mem/cache.rs | 31 +- crates/anvil/src/eth/backend/mem/fork_db.rs | 15 +- .../anvil/src/eth/backend/mem/in_memory_db.rs | 6 +- crates/anvil/src/eth/backend/mem/inspector.rs | 6 +- crates/anvil/src/eth/backend/mem/mod.rs | 284 +- crates/anvil/src/eth/backend/mem/state.rs | 17 +- crates/anvil/src/eth/backend/mem/storage.rs | 23 +- crates/anvil/src/eth/backend/time.rs | 2 +- crates/anvil/src/eth/error.rs | 7 + crates/anvil/src/eth/fees.rs | 20 +- crates/anvil/src/eth/otterscan/api.rs | 17 +- crates/anvil/src/eth/pool/mod.rs | 24 +- crates/anvil/src/eth/pool/transactions.rs | 8 +- crates/anvil/src/eth/sign.rs | 87 +- crates/anvil/src/evm.rs | 54 +- crates/anvil/src/filter.rs | 11 +- crates/anvil/src/lib.rs | 3 +- crates/anvil/test-data/state-dump.json | 315 +- crates/anvil/tests/it/anvil_api.rs | 126 +- crates/anvil/tests/it/beacon_api.rs | 3 +- crates/anvil/tests/it/eip2935.rs | 70 + crates/anvil/tests/it/eip4844.rs | 47 +- crates/anvil/tests/it/fork.rs | 6 +- crates/anvil/tests/it/main.rs | 1 + crates/anvil/tests/it/proof.rs | 17 +- crates/anvil/tests/it/state.rs | 60 + crates/anvil/tests/it/traces.rs | 85 +- crates/anvil/tests/it/transaction.rs | 38 +- crates/cast/src/args.rs | 8 +- crates/cast/src/cmd/call.rs | 9 +- crates/cast/src/cmd/create2.rs | 23 +- crates/cast/src/cmd/erc20.rs | 82 +- crates/cast/src/cmd/interface.rs | 24 +- crates/cast/src/cmd/mktx.rs | 7 +- crates/cast/src/cmd/rpc.rs | 2 +- crates/cast/src/cmd/run.rs | 8 +- crates/cast/src/cmd/send.rs | 58 +- crates/cast/src/cmd/storage.rs | 12 +- crates/cast/src/cmd/trace.rs | 2 +- crates/cast/src/cmd/wallet/list.rs | 20 +- crates/cast/src/lib.rs | 10 +- crates/cast/src/tx.rs | 26 +- crates/cast/tests/cli/erc20.rs | 105 + crates/cast/tests/cli/main.rs | 229 +- crates/cast/tests/cli/selectors.rs | 22 +- .../fixtures/interface_inherited_struct.json | 1 + crates/cheatcodes/Cargo.toml | 2 + crates/cheatcodes/assets/cheatcodes.json | 100 + crates/cheatcodes/spec/src/vm.rs | 36 + crates/cheatcodes/src/base64.rs | 8 +- crates/cheatcodes/src/crypto.rs | 219 +- crates/cheatcodes/src/env.rs | 64 +- crates/cheatcodes/src/evm.rs | 721 ++- crates/cheatcodes/src/evm/fork.rs | 277 +- crates/cheatcodes/src/evm/mapping.rs | 10 +- crates/cheatcodes/src/evm/mock.rs | 65 +- crates/cheatcodes/src/evm/prank.rs | 68 +- .../cheatcodes/src/evm/record_debug_step.rs | 8 +- crates/cheatcodes/src/fs.rs | 194 +- crates/cheatcodes/src/inspector.rs | 501 +- crates/cheatcodes/src/inspector/utils.rs | 43 +- crates/cheatcodes/src/json.rs | 84 +- crates/cheatcodes/src/lib.rs | 75 +- crates/cheatcodes/src/script.rs | 174 +- crates/cheatcodes/src/string.rs | 46 +- crates/cheatcodes/src/test.rs | 33 +- crates/cheatcodes/src/test/assert.rs | 24 +- crates/cheatcodes/src/test/assume.rs | 21 +- crates/cheatcodes/src/test/expect.rs | 250 +- crates/cheatcodes/src/toml.rs | 46 +- crates/cheatcodes/src/utils.rs | 91 +- crates/cheatcodes/src/version.rs | 4 +- crates/chisel/src/args.rs | 2 + crates/chisel/src/executor.rs | 33 +- crates/chisel/src/solidity_helper.rs | 12 +- crates/chisel/tests/it/repl/mod.rs | 10 + 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/evm.rs | 9 + crates/cli/src/opts/global.rs | 11 + crates/cli/src/opts/rpc.rs | 14 + crates/cli/src/utils/abi.rs | 4 + crates/cli/src/utils/cmd.rs | 5 +- crates/cli/src/utils/mod.rs | 45 +- crates/common/Cargo.toml | 2 +- crates/common/build.rs | 8 +- crates/common/fmt/Cargo.toml | 4 + crates/common/fmt/src/lib.rs | 5 +- crates/common/fmt/src/ui.rs | 1555 +++--- crates/common/src/comments/inline_config.rs | 1 + crates/common/src/comments/mod.rs | 9 +- crates/common/src/fs.rs | 6 +- crates/common/src/mapping_slots.rs | 19 +- crates/common/src/preprocessor/deps.rs | 1 + crates/common/src/preprocessor/mod.rs | 2 +- crates/common/src/provider/mod.rs | 109 +- .../common/src/provider/runtime_transport.rs | 22 + crates/common/src/selectors.rs | 1 + crates/common/src/term.rs | 15 +- crates/common/src/traits.rs | 17 +- crates/common/src/transactions.rs | 92 +- crates/config/README.md | 327 +- crates/config/src/endpoints.rs | 24 - crates/config/src/error.rs | 12 + crates/config/src/etherscan.rs | 79 +- crates/config/src/fmt.rs | 15 + crates/config/src/fuzz.rs | 7 +- crates/config/src/invariant.rs | 9 + crates/config/src/lib.rs | 758 ++- crates/config/src/lint.rs | 17 +- crates/config/src/providers/ext.rs | 37 + crates/config/src/providers/remappings.rs | 14 +- crates/config/src/providers/warnings.rs | 195 +- crates/config/src/utils.rs | 7 +- crates/config/src/warning.rs | 15 + crates/debugger/src/node.rs | 7 +- crates/debugger/src/tui/context.rs | 10 +- crates/debugger/src/tui/draw.rs | 5 +- crates/doc/src/builder.rs | 2 +- crates/doc/src/writer/as_doc.rs | 51 +- crates/evm/core/src/backend/cow.rs | 37 +- crates/evm/core/src/backend/error.rs | 2 - crates/evm/core/src/backend/mod.rs | 81 +- crates/evm/core/src/decode.rs | 2 +- crates/evm/core/src/either_evm.rs | 2 +- crates/evm/core/src/env.rs | 83 +- crates/evm/core/src/evm.rs | 143 +- crates/evm/core/src/fork/database.rs | 35 +- crates/evm/core/src/fork/multi.rs | 92 +- crates/evm/core/src/hardfork.rs | 1 + crates/evm/core/src/lib.rs | 33 +- crates/evm/core/src/opts.rs | 21 +- crates/evm/core/src/utils.rs | 27 +- crates/evm/evm/Cargo.toml | 2 + crates/evm/evm/src/executors/fuzz/mod.rs | 714 ++- crates/evm/evm/src/executors/invariant/mod.rs | 218 +- .../evm/evm/src/executors/invariant/replay.rs | 30 +- .../evm/evm/src/executors/invariant/result.rs | 57 +- .../evm/evm/src/executors/invariant/shrink.rs | 195 +- crates/evm/evm/src/executors/mod.rs | 26 +- crates/evm/evm/src/executors/trace.rs | 4 +- .../evm/evm/src/inspectors/custom_printer.rs | 10 +- crates/evm/evm/src/inspectors/logs.rs | 60 +- crates/evm/evm/src/inspectors/stack.rs | 140 +- crates/evm/evm/src/lib.rs | 3 +- crates/evm/fuzz/src/inspector.rs | 100 +- .../evm/fuzz/src/invariant/call_override.rs | 25 +- crates/evm/fuzz/src/invariant/mod.rs | 23 + crates/evm/fuzz/src/lib.rs | 26 +- crates/evm/fuzz/src/strategies/invariants.rs | 28 +- crates/evm/fuzz/src/strategies/mod.rs | 5 +- crates/evm/fuzz/src/strategies/param.rs | 254 +- crates/evm/traces/Cargo.toml | 3 - crates/evm/traces/src/decoder/mod.rs | 2 +- crates/evm/traces/src/folded_stack_trace.rs | 32 +- crates/evm/traces/src/identifier/external.rs | 20 +- crates/fmt/Cargo.toml | 1 - crates/fmt/README.md | 1 + crates/fmt/src/state/mod.rs | 15 +- crates/fmt/src/state/sol.rs | 104 +- crates/fmt/testdata/CommentEmptyLine/fmt.sol | 5 + .../testdata/CommentEmptyLine/original.sol | 6 + .../namespace-import-prefer-glob.fmt.sol | 26 + .../namespace-import-prefer-plain.fmt.sol | 26 + .../namespace-import-preserve.fmt.sol | 26 + crates/fmt/testdata/Repros/fmt.sol | 34 + crates/fmt/testdata/Repros/original.sol | 19 + crates/fmt/testdata/Repros/sorted.fmt.sol | 34 + crates/fmt/testdata/Repros/tab.fmt.sol | 34 + crates/fmt/testdata/StructFieldAccess/fmt.sol | 46 + .../testdata/StructFieldAccess/original.sol | 43 + .../WhileStatement/block-multi.fmt.sol | 5 + .../WhileStatement/block-single.fmt.sol | 5 + crates/fmt/testdata/WhileStatement/fmt.sol | 5 + .../fmt/testdata/WhileStatement/original.sol | 2 + crates/fmt/tests/formatter.rs | 27 + crates/forge/Cargo.toml | 3 +- .../assets/solidity/workflowTemplate.yml | 3 - .../forge/assets/tempo/workflowTemplate.yml | 3 - .../forge/assets/vyper/workflowTemplate.yml | 3 - crates/forge/src/args.rs | 2 + crates/forge/src/cmd/bind_json.rs | 2 +- crates/forge/src/cmd/build.rs | 100 +- crates/forge/src/cmd/clone.rs | 4 +- crates/forge/src/cmd/compiler.rs | 4 +- crates/forge/src/cmd/coverage.rs | 7 +- crates/forge/src/cmd/create.rs | 17 + crates/forge/src/cmd/eip712.rs | 2 +- crates/forge/src/cmd/inspect.rs | 10 +- crates/forge/src/cmd/lint.rs | 4 +- crates/forge/src/cmd/selectors.rs | 6 +- crates/forge/src/cmd/snapshot.rs | 3 + crates/forge/src/cmd/test/mod.rs | 22 +- crates/forge/src/coverage.rs | 34 +- crates/forge/src/gas_report.rs | 30 +- crates/forge/src/lockfile.rs | 14 +- crates/forge/src/multi_runner.rs | 5 +- crates/forge/src/result.rs | 116 +- crates/forge/src/runner.rs | 128 +- crates/forge/tests/cli/build.rs | 75 +- crates/forge/tests/cli/cmd.rs | 170 +- crates/forge/tests/cli/compiler.rs | 16 +- crates/forge/tests/cli/config.rs | 51 +- crates/forge/tests/cli/coverage.rs | 84 + crates/forge/tests/cli/doc.rs | 175 + crates/forge/tests/cli/eip712.rs | 3 +- crates/forge/tests/cli/ext_integration.rs | 12 +- crates/forge/tests/cli/failure_assertions.rs | 4 +- crates/forge/tests/cli/fmt.rs | 8 +- crates/forge/tests/cli/lint.rs | 159 +- crates/forge/tests/cli/lint/geiger.rs | 60 +- crates/forge/tests/cli/script.rs | 75 +- crates/forge/tests/cli/svm.rs | 2 +- crates/forge/tests/cli/test_cmd/fuzz.rs | 40 +- .../tests/cli/test_cmd/invariant/common.rs | 376 +- .../forge/tests/cli/test_cmd/invariant/mod.rs | 186 + .../tests/cli/test_cmd/invariant/target.rs | 8 + crates/forge/tests/cli/test_cmd/logs.rs | 142 + crates/forge/tests/cli/test_cmd/mod.rs | 75 +- crates/forge/tests/cli/test_cmd/repros.rs | 98 + crates/forge/tests/cli/test_optimizer.rs | 8 +- crates/forge/tests/cli/verify_bytecode.rs | 460 +- .../fixtures/SimpleContractTestVerbose.json | 599 +-- .../forge/tests/fixtures/invariant_traces.svg | 8 +- crates/linking/src/lib.rs | 2 +- crates/lint/README.md | 14 +- crates/lint/src/linter/early.rs | 36 +- crates/lint/src/linter/late.rs | 2 +- crates/lint/src/sol/gas/custom_errors.rs | 53 + crates/lint/src/sol/gas/mod.rs | 4 +- crates/lint/src/sol/high/unchecked_calls.rs | 8 +- crates/lint/src/sol/info/imports.rs | 1 + crates/lint/src/sol/macros.rs | 4 - crates/lint/src/sol/mod.rs | 10 +- crates/lint/testdata/CustomErrors.sol | 42 + crates/lint/testdata/CustomErrors.stderr | 40 + crates/lint/testdata/Imports.stderr | 156 +- crates/lint/testdata/IncorrectShift.stderr | 60 +- crates/lint/testdata/Keccak256.sol | 15 +- crates/lint/testdata/Keccak256.stderr | 168 +- crates/lint/testdata/MixedCase.sol | 13 + crates/lint/testdata/MixedCase.stderr | 204 +- crates/lint/testdata/NamedStructFields.stderr | 12 +- .../lint/testdata/ScreamingSnakeCase.stderr | 96 +- crates/lint/testdata/StructPascalCase.stderr | 72 +- crates/lint/testdata/UncheckedCall.stderr | 96 +- .../testdata/UncheckedTransferERC20.stderr | 72 +- crates/lint/testdata/UnsafeCheatcodes.stderr | 156 +- crates/lint/testdata/UnsafeTypecast.stderr | 4560 ++++++++--------- .../testdata/UnwrappedModifierLogic.stderr | 282 +- crates/primitives/Cargo.toml | 3 + crates/primitives/src/network/mod.rs | 15 + crates/primitives/src/network/receipt.rs | 18 + crates/primitives/src/network/transaction.rs | 232 + crates/primitives/src/network/wallet.rs | 69 + crates/primitives/src/transaction/envelope.rs | 9 +- crates/primitives/src/transaction/receipt.rs | 8 +- crates/primitives/src/transaction/request.rs | 447 +- crates/script-sequence/src/reader.rs | 27 +- crates/script-sequence/src/sequence.rs | 13 +- crates/script/src/broadcast.rs | 80 +- crates/script/src/lib.rs | 20 +- crates/script/src/multi_sequence.rs | 13 +- crates/script/src/progress.rs | 12 +- crates/script/src/receipts.rs | 167 +- crates/script/src/simulate.rs | 2 +- crates/script/src/verify.rs | 1 + crates/sol-macro-gen/src/sol_macro_gen.rs | 2 +- crates/test-utils/Cargo.toml | 3 +- crates/test-utils/src/etherscan.rs | 32 + crates/test-utils/src/lib.rs | 1 + crates/test-utils/src/prj.rs | 16 +- crates/test-utils/src/rpc.rs | 17 +- crates/test-utils/src/util.rs | 71 +- crates/verify/src/bytecode.rs | 15 +- crates/verify/src/etherscan/mod.rs | 21 +- crates/verify/src/provider.rs | 62 +- crates/verify/src/utils.rs | 37 +- crates/verify/src/verify.rs | 40 +- crates/wallets/Cargo.toml | 5 - crates/wallets/src/lib.rs | 1 + crates/wallets/src/opts.rs | 41 - crates/wallets/src/signer.rs | 100 +- .../src/wallet_browser/app/assets/main.js | 2 +- crates/wallets/src/wallet_browser/handlers.rs | 31 +- crates/wallets/src/wallet_browser/mod.rs | 133 +- crates/wallets/src/wallet_browser/opts.rs | 49 + crates/wallets/src/wallet_browser/queue.rs | 3 +- crates/wallets/src/wallet_browser/router.rs | 7 +- crates/wallets/src/wallet_browser/server.rs | 9 +- crates/wallets/src/wallet_browser/signer.rs | 23 +- crates/wallets/src/wallet_browser/state.rs | 12 +- crates/wallets/src/wallet_browser/types.rs | 6 +- crates/wallets/src/wallet_multi/mod.rs | 78 +- deny.toml | 3 +- flake.lock | 18 +- foundryup/foundryup | 9 +- testdata/default/cheats/Ed25519.t.sol | 105 + .../default/cheats/ExecuteTransaction.t.sol | 215 + testdata/default/cheats/ExpectRevert.t.sol | 12 + testdata/default/cheats/MockFunction.t.sol | 178 +- testdata/default/cheats/RpcUrls.t.sol | 2 +- testdata/forge-std-rev | 2 +- testdata/foundry.toml | 5 + testdata/utils/Vm.sol | 5 + typos.toml | 1 + 342 files changed, 18034 insertions(+), 9889 deletions(-) create mode 100644 .circleci/ci_deploy.yml create mode 100644 .github/FLAKY_TEST_FAILURE_TEMPLATE.md create mode 100644 .github/FLAKY_TEST_ISOLATE_FAILURE_TEMPLATE.md create mode 100644 .github/TEST_ISOLATE_FAILURE_TEMPLATE.md create mode 100644 .github/workflows/test-flaky.yml create mode 100644 crates/anvil/tests/it/eip2935.rs create mode 100644 crates/cast/tests/fixtures/interface_inherited_struct.json 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 create mode 100644 crates/fmt/testdata/ImportDirective/namespace-import-prefer-glob.fmt.sol create mode 100644 crates/fmt/testdata/ImportDirective/namespace-import-prefer-plain.fmt.sol create mode 100644 crates/fmt/testdata/ImportDirective/namespace-import-preserve.fmt.sol create mode 100644 crates/fmt/testdata/StructFieldAccess/fmt.sol create mode 100644 crates/fmt/testdata/StructFieldAccess/original.sol create mode 100644 crates/lint/src/sol/gas/custom_errors.rs create mode 100644 crates/lint/testdata/CustomErrors.sol create mode 100644 crates/lint/testdata/CustomErrors.stderr create mode 100644 crates/primitives/src/network/transaction.rs create mode 100644 crates/primitives/src/network/wallet.rs create mode 100644 crates/test-utils/src/etherscan.rs create mode 100644 crates/wallets/src/wallet_browser/opts.rs create mode 100644 testdata/default/cheats/Ed25519.t.sol create mode 100644 testdata/default/cheats/ExecuteTransaction.t.sol 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/FLAKY_TEST_FAILURE_TEMPLATE.md b/.github/FLAKY_TEST_FAILURE_TEMPLATE.md new file mode 100644 index 0000000000000..1553914b7ad3f --- /dev/null +++ b/.github/FLAKY_TEST_FAILURE_TEMPLATE.md @@ -0,0 +1,10 @@ +--- +title: "bug: flaky tests workflow failed" +labels: P-normal, T-bug +--- + +The nightly flaky tests workflow has failed. This indicates external API rate limiting, RPC reliability issues, or other intermittent failures that may affect users. + +Check the [flaky tests workflow page]({{ env.WORKFLOW_URL }}) for details. + +This issue was raised by the workflow at `.github/workflows/test-flaky.yml`. diff --git a/.github/FLAKY_TEST_ISOLATE_FAILURE_TEMPLATE.md b/.github/FLAKY_TEST_ISOLATE_FAILURE_TEMPLATE.md new file mode 100644 index 0000000000000..0d7a9bb84959d --- /dev/null +++ b/.github/FLAKY_TEST_ISOLATE_FAILURE_TEMPLATE.md @@ -0,0 +1,10 @@ +--- +title: "bug: flaky tests workflow failed (isolate)" +labels: P-normal, T-bug +--- + +The nightly flaky tests workflow (with isolation mode enabled) has failed. This indicates external API rate limiting, RPC reliability issues, or other intermittent failures that may affect users. + +Check the [flaky tests workflow page]({{ env.WORKFLOW_URL }}) for details. + +This issue was raised by the workflow at `.github/workflows/test-isolate.yml`. diff --git a/.github/TEST_ISOLATE_FAILURE_TEMPLATE.md b/.github/TEST_ISOLATE_FAILURE_TEMPLATE.md new file mode 100644 index 0000000000000..25bab79f97edf --- /dev/null +++ b/.github/TEST_ISOLATE_FAILURE_TEMPLATE.md @@ -0,0 +1,10 @@ +--- +title: "bug: test-isolate workflow failed" +labels: P-high, T-bug +--- + +The daily test-isolate workflow (tests with isolation mode enabled) has failed. This indicates a regression in Foundry's test isolation behavior. + +Check the [test-isolate workflow page]({{ env.WORKFLOW_URL }}) for details. + +This issue was raised by the workflow at `.github/workflows/test-isolate.yml`. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5ace4600a1f26..54106f083501d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,5 @@ updates: directory: "/" schedule: interval: "weekly" + ignore: + - dependency-name: "softprops/action-gh-release" diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 34622ca7979a5..f53f9aeb55ded 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: @@ -137,7 +137,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@v6 + uses: actions/upload-artifact@v7 with: name: benchmark-results path: | diff --git a/.github/workflows/bump-forge-std.yml b/.github/workflows/bump-forge-std.yml index 00109defa89b1..23696639589a3 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@98357b18bf14b5342f975ff684046ec3b2a07725 # v7 + uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # 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 461c3d835bfcd..b12bb1b3242d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,8 +3,9 @@ name: CI permissions: {} on: + push: + branches: [master] pull_request: - merge_group: concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} @@ -58,7 +59,7 @@ jobs: - uses: actions/checkout@v6 with: persist-credentials: false - - uses: crate-ci/typos@bb4666ad77b539a6b4ce4eda7ebb6de553704021 # v1 + - uses: crate-ci/typos@57b11c6b7e54c402ccd9cda953f1072ec4f78e33 # v1 shellcheck: runs-on: depot-ubuntu-latest @@ -84,7 +85,7 @@ jobs: persist-credentials: false - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master with: - toolchain: nightly-2026-01-10 + toolchain: nightly components: clippy - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 @@ -104,7 +105,7 @@ jobs: persist-credentials: false - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master with: - toolchain: nightly-2026-01-10 + toolchain: nightly components: rustfmt - run: cargo fmt --all --check @@ -140,7 +141,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@03ef6f57d573ca4522fb02950f326083373b85bf # v2 + - uses: taiki-e/install-action@edba51d32f66935a112c0516fec65a13d420cbc6 # v2 with: tool: cargo-hack - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 @@ -148,7 +149,7 @@ jobs: - run: cargo hack check deny: - uses: ithacaxyz/ci/.github/workflows/deny.yml@9c8d0dc20e7ad02455d3fdab2378a05f29907630 # main + uses: tempoxyz/ci/.github/workflows/deny.yml@268b3ce142717ff86c58fbbcc3abc3f109f0fb8d # main permissions: contents: read diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 614aa6a508f67..16de916a8993b 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 }} @@ -65,15 +65,24 @@ jobs: - name: Finalize Docker Metadata id: docker_tagging run: | + TAG="${{ inputs.tag_name }}" + REGISTRY="${{ env.REGISTRY }}" + IMAGE="${{ env.IMAGE_NAME }}" + if [[ "${{ github.event_name }}" == "schedule" ]]; then printf "cron trigger, assigning nightly tag\n" - printf "docker_tags=%s/%s:nightly,%s/%s:nightly-%s\n" "${{ env.REGISTRY }}" "${{ env.IMAGE_NAME }}" "${{ env.REGISTRY }}" "${{ env.IMAGE_NAME }}" "$GITHUB_SHA" >> "$GITHUB_OUTPUT" + printf "docker_tags=%s/%s:nightly,%s/%s:nightly-%s\n" "$REGISTRY" "$IMAGE" "$REGISTRY" "$IMAGE" "$GITHUB_SHA" >> "$GITHUB_OUTPUT" elif [[ "${GITHUB_REF##*/}" == "main" ]] || [[ "${GITHUB_REF##*/}" == "master" ]]; then printf "manual trigger from master/main branch, assigning latest tag\n" - printf "docker_tags=%s/%s:%s,%s/%s:latest\n" "${{ env.REGISTRY }}" "${{ env.IMAGE_NAME }}" "${GITHUB_REF##*/}" "${{ env.REGISTRY }}" "${{ env.IMAGE_NAME }}" >> "$GITHUB_OUTPUT" + printf "docker_tags=%s/%s:%s,%s/%s:latest\n" "$REGISTRY" "$IMAGE" "${GITHUB_REF##*/}" "$REGISTRY" "$IMAGE" >> "$GITHUB_OUTPUT" + elif [[ "$TAG" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then + MAJOR="v${BASH_REMATCH[1]}" + MINOR="v${BASH_REMATCH[1]}.${BASH_REMATCH[2]}" + printf "version tag release, assigning %s, %s, and %s tags\n" "$TAG" "$MINOR" "$MAJOR" + printf "docker_tags=%s/%s:%s,%s/%s:%s,%s/%s:%s\n" "$REGISTRY" "$IMAGE" "$TAG" "$REGISTRY" "$IMAGE" "$MINOR" "$REGISTRY" "$IMAGE" "$MAJOR" >> "$GITHUB_OUTPUT" else printf "Neither scheduled nor manual release from main branch. Just tagging as branch name\n" - printf "docker_tags=%s/%s:%s\n" "${{ env.REGISTRY }}" "${{ env.IMAGE_NAME }}" "${GITHUB_REF##*/}" >> "$GITHUB_OUTPUT" + printf "docker_tags=%s/%s:%s\n" "$REGISTRY" "$IMAGE" "${GITHUB_REF##*/}" >> "$GITHUB_OUTPUT" fi # Log docker metadata to explicitly know what is being pushed @@ -83,10 +92,10 @@ jobs: printf "LABELS -> %s\n" "${{ steps.meta.outputs.labels }}" - name: Set up Depot CLI - uses: depot/setup-action@b0b1ea4f69e92ebf5dea3f8713a1b0c37b2126a5 # v1 + uses: depot/setup-action@15c09a5f77a0840ad4bce955686522a257853461 # v1 - name: Build and push Foundry image - uses: depot/build-push-action@9785b135c3c76c33db102e45be96a25ab55cd507 # v1 + uses: depot/build-push-action@5f3b3c2e5a00f0093de47f657aeaefcedff27d18 # v1 with: build-args: | RUST_PROFILE=${{ env.RUST_PROFILE }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ef3fc12adc9c2..2ace7a6c6fcf1 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -29,7 +29,7 @@ jobs: persist-credentials: false - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master with: - toolchain: nightly-2026-01-10 + toolchain: nightly - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 7082dc5dff403..b70a21228c7df 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -19,11 +19,11 @@ jobs: contents: write pull-requests: write steps: - - uses: DeterminateSystems/determinate-nix-action@1d699fc25db3f9e079cd2f168ca007a4183389be # v3 + - uses: DeterminateSystems/determinate-nix-action@73327eb48f028efaaf5013656ba216ca3cdeca7b # v3 - uses: actions/checkout@v6 with: persist-credentials: false - - uses: DeterminateSystems/update-flake-lock@2235257b90962ba68499d64da5b0d1eaa3b46c1e # main + - uses: DeterminateSystems/update-flake-lock@e80a657d7603606be0c69b117cfdc240f1e6af88 # main with: pr-title: "Update flake.lock" pr-labels: | @@ -38,7 +38,7 @@ jobs: permissions: contents: read steps: - - uses: DeterminateSystems/determinate-nix-action@1d699fc25db3f9e079cd2f168ca007a4183389be # v3 + - uses: DeterminateSystems/determinate-nix-action@73327eb48f028efaaf5013656ba216ca3cdeca7b # v3 - uses: actions/checkout@v6 with: persist-credentials: false diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 3bfadac860785..df45e41a50295 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@b7a1c7ccf290d58743029c4f6903da283811b979 # v2 + uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2 with: bun-version: latest @@ -325,7 +325,7 @@ jobs: persist-credentials: false - name: Setup Bun - uses: oven-sh/setup-bun@b7a1c7ccf290d58743029c4f6903da283811b979 # v2 + uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2 with: bun-version: latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 85cc3e6f5859d..3c144e3510d97 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,7 @@ env: RUST_PROFILE: dist RUST_FEATURES: aws-kms,gcp-kms,turnkey,cli,asm-keccak,js-tracer - LAST_STABLE_VERSION: "v1.5.0" + LAST_STABLE_VERSION: "v1.5.1" jobs: prepare: @@ -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@a34a8009a9588bb86b02a873cf592440e96a5da8 # v6 with: configuration: "./.github/changelog.json" fromTag: ${{ env.IS_NIGHTLY == 'true' && 'nightly' || env.STABLE_VERSION }} @@ -163,7 +163,7 @@ jobs: - name: cross setup if: contains(matrix.target, 'musl') - uses: taiki-e/cache-cargo-install-action@34ce5120836e5f9f1508d8713d7fdea0e8facd6f # v2 + uses: taiki-e/cache-cargo-install-action@2bfc3cedaf2ee5e7fa5d0ae034ccd5fb50cf8e1f # v2 with: git: https://github.com/cross-rs/cross rev: baf457efc2555225af47963475bd70e8d2f5993f @@ -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@v6 + uses: actions/upload-artifact@v7 with: retention-days: 1 name: ${{ steps.artifacts.outputs.file_name }} @@ -260,7 +260,7 @@ jobs: - name: Binaries attestation id: attestation - uses: actions/attest-build-provenance@v3 + uses: actions/attest-build-provenance@v4 with: subject-path: | ${{ env.anvil_bin_path }} @@ -279,7 +279,7 @@ jobs: # Creates the release for this specific version - name: Create release - uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 + uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2 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@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 + uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2 with: name: "Nightly" tag_name: "nightly" diff --git a/.github/workflows/test-flaky.yml b/.github/workflows/test-flaky.yml new file mode 100644 index 0000000000000..c64b3ef1d963e --- /dev/null +++ b/.github/workflows/test-flaky.yml @@ -0,0 +1,68 @@ +# Daily CI job to run flaky tests that are excluded from regular CI via nextest default-filter + +name: test-flaky + +permissions: {} + +on: + schedule: + - cron: "0 1 * * *" # Run daily at 1 AM UTC (offset from test-isolate) + workflow_dispatch: # Needed so we can run it manually + +concurrency: + group: tests-${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: full + RUSTC_WRAPPER: "sccache" + +jobs: + test: + name: flaky tests + runs-on: depot-ubuntu-latest-16 + timeout-minutes: 60 + permissions: + contents: read + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master + with: + toolchain: stable + - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + - uses: taiki-e/install-action@edba51d32f66935a112c0516fec65a13d420cbc6 # v2 + with: + tool: nextest + - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 + - name: Test flaky tests + env: + SVM_TARGET_PLATFORM: linux-amd64 + HTTP_ARCHIVE_URLS: ${{ secrets.HTTP_ARCHIVE_URLS }} + ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_API_KEY }} + 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. + issue: + name: Open an issue + runs-on: ubuntu-latest + needs: [test] + if: failure() + permissions: + contents: read + issues: write + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 # v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + WORKFLOW_URL: | + ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + with: + update_existing: true + filename: .github/FLAKY_TEST_FAILURE_TEMPLATE.md diff --git a/.github/workflows/test-isolate.yml b/.github/workflows/test-isolate.yml index ddd80b3e01ae6..a4aaefdbfe7df 100644 --- a/.github/workflows/test-isolate.yml +++ b/.github/workflows/test-isolate.yml @@ -9,6 +9,11 @@ on: - cron: "0 0 * * *" # Run daily at midnight UTC workflow_dispatch: # Needed so we can run it manually +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: full + RUSTC_WRAPPER: "sccache" + jobs: nextest: uses: ./.github/workflows/test.yml @@ -16,3 +21,74 @@ jobs: contents: read with: profile: isolate + + # Run flaky tests with isolation enabled + flaky: + name: flaky tests (isolate) + runs-on: depot-ubuntu-latest-16 + timeout-minutes: 60 + permissions: + contents: read + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master + with: + toolchain: stable + - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + - uses: taiki-e/install-action@edba51d32f66935a112c0516fec65a13d420cbc6 # v2 + with: + tool: nextest + - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # 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 }} + run: cargo nextest run --profile flaky --features=isolate-by-default --no-fail-fast + + # If nextest fails, create a high-priority issue for isolation failures. + issue-isolate: + name: Open isolation issue + runs-on: ubuntu-latest + needs: [nextest] + if: failure() + permissions: + contents: read + issues: write + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 # v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + WORKFLOW_URL: | + ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + with: + update_existing: true + filename: .github/TEST_ISOLATE_FAILURE_TEMPLATE.md + + # If flaky tests fail, create a normal-priority issue for flaky failures. + issue-flaky: + name: Open flaky issue + runs-on: ubuntu-latest + needs: [flaky] + if: failure() + permissions: + contents: read + issues: write + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 # v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + WORKFLOW_URL: | + ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + with: + update_existing: true + filename: .github/FLAKY_TEST_ISOLATE_FAILURE_TEMPLATE.md diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5861399b8c812..2f9c5793ab138 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@03ef6f57d573ca4522fb02950f326083373b85bf # v2 + - uses: taiki-e/install-action@edba51d32f66935a112c0516fec65a13d420cbc6 # 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@b7a1c7ccf290d58743029c4f6903da283811b979 # v2 + uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2 with: bun-version: latest - name: Setup Python diff --git a/Cargo.lock b/Cargo.lock index cf412e491eba3..7443d5e8e108a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,9 +67,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-chains" -version = "0.2.27" +version = "0.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db5bcdd086f0b1b9610140a12c59b757397be90bd130d8d836fc8da0815a34" +checksum = "90f374d3c6d729268bbe2d0e0ff992bb97898b2df756691a62ee1d5f0506bc39" dependencies = [ "alloy-primitives", "num_enum", @@ -79,9 +79,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c3a590d13de3944675987394715f37537b50b856e3b23a0e66e97d963edbf38" +checksum = "b0c0dc44157867da82c469c13186015b86abef209bf0e41625e4b68bac61d728" dependencies = [ "alloy-eips", "alloy-primitives", @@ -101,14 +101,14 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-consensus-any" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f28f769d5ea999f0d8a105e434f483456a15b4e1fcb08edbbbe1650a497ff6d" +checksum = "ba4cdb42df3871cd6b346d6a938ec2ba69a9a0f49d1f82714bc5c48349268434" dependencies = [ "alloy-consensus", "alloy-eips", @@ -120,9 +120,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990fa65cd132a99d3c3795a82b9f93ec82b81c7de3bab0bf26ca5c73286f7186" +checksum = "ca63b7125a981415898ffe2a2a696c83696c9c6bdb1671c8a912946bbd8e49e7" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -138,14 +138,14 @@ dependencies = [ "futures", "futures-util", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-dyn-abi" -version = "1.5.2" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "369f5707b958927176265e8a58627fc6195e5dfa5c55689396e68b241b3a72e6" +checksum = "cc2db5c583aaef0255aa63a4fe827f826090142528bba48d1bf4119b62780cad" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -170,7 +170,7 @@ dependencies = [ "alloy-rlp", "crc", "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -187,9 +187,9 @@ dependencies = [ [[package]] name = "alloy-eip5792" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c4f72d43613ad52a68de0148bf14440305ee795cdb95984b2848bce3a2cff9" +checksum = "98ec415c2141bef46ea7d1b80877d7dc4639c6ccf6a774b75f05e3deadb27469" dependencies = [ "alloy-primitives", "alloy-serde", @@ -208,18 +208,31 @@ dependencies = [ "borsh", "k256", "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-eip7928" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3231de68d5d6e75332b7489cfcc7f4dfabeba94d990a10e4b923af0e6623540" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "borsh", + "serde", ] [[package]] name = "alloy-eips" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09535cbc646b0e0c6fcc12b7597eaed12cf86dff4c4fba9507a61e71b94f30eb" +checksum = "b9f7ef09f21bd1e9cb8a686f168cb4a206646804567f0889eadb8dcc4c9288c8" dependencies = [ "alloy-eip2124", "alloy-eip2930", "alloy-eip7702", + "alloy-eip7928", "alloy-primitives", "alloy-rlp", "alloy-serde", @@ -232,29 +245,29 @@ dependencies = [ "ethereum_ssz_derive", "serde", "serde_with", - "sha2", - "thiserror 2.0.17", + "sha2 0.10.9", + "thiserror 2.0.18", ] [[package]] name = "alloy-ens" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03770ec5adb3ea02155690584cac277c38674a1e03f530eb398ed5e49880640" +checksum = "79460500df1a98836253ab24871ecd54d12809ac9045b0d10eed727c3555efa9" dependencies = [ "alloy-contract", "alloy-primitives", "alloy-provider", "alloy-sol-types", "async-trait", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-evm" -version = "0.25.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ccc4c702c840148af1ce784cc5c6ed9274a020ef32417c5b1dbeab8c317673" +checksum = "7b99ba7b74a87176f31ee1cd26768f7155b0eeff61ed925f59b13085ffe5f891" dependencies = [ "alloy-consensus", "alloy-eips", @@ -269,14 +282,14 @@ dependencies = [ "op-alloy", "op-revm", "revm", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-genesis" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1005520ccf89fa3d755e46c1d992a9e795466c2e7921be2145ef1f749c5727de" +checksum = "7c9cf3b99f46615fbf7dc1add0c96553abb7bf88fc9ec70dfbe7ad0b47ba7fe8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -302,9 +315,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "1.5.2" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84e3cf01219c966f95a460c95f1d4c30e12f6c18150c21a30b768af2a2a29142" +checksum = "e9dbe713da0c737d9e5e387b0ba790eb98b14dd207fe53eef50e19a5a8ec3dac" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -314,24 +327,24 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b626409c98ba43aaaa558361bca21440c88fd30df7542c7484b9c7a1489cdb" +checksum = "ff42cd777eea61f370c0b10f2648a1c81e0b783066cd7269228aa993afd487f7" dependencies = [ "alloy-primitives", "alloy-sol-types", "http 1.4.0", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", ] [[package]] name = "alloy-network" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89924fdcfeee0e0fa42b1f10af42f92802b5d16be614a70897382565663bf7cf" +checksum = "8cbca04f9b410fdc51aaaf88433cbac761213905a65fe832058bcf6690585762" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -350,14 +363,14 @@ dependencies = [ "futures-utils-wasm", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-network-primitives" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f0dbe56ff50065713ff8635d8712a0895db3ad7f209db9793ad8fcb6b1734aa" +checksum = "42d6d15e069a8b11f56bef2eccbad2a873c6dd4d4c81d04dda29710f5ea52f04" dependencies = [ "alloy-consensus", "alloy-eips", @@ -368,9 +381,9 @@ dependencies = [ [[package]] name = "alloy-op-evm" -version = "0.25.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f640da852f93ddaa3b9a602b7ca41d80e0023f77a67b68aaaf511c32f1fe0ce" +checksum = "646a01ebc9778ee08bcc33cfa6714efc548f94e53de1aff6b34d9da55a2e5d41" dependencies = [ "alloy-consensus", "alloy-eips", @@ -381,7 +394,7 @@ dependencies = [ "op-alloy", "op-revm", "revm", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -398,9 +411,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "1.5.2" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6a0fb18dd5fb43ec5f0f6a20be1ce0287c79825827de5744afaa6c957737c33" +checksum = "de3b431b4e72cd8bd0ec7a50b4be18e73dab74de0dba180eef171055e5d5926e" dependencies = [ "alloy-rlp", "arbitrary", @@ -408,8 +421,8 @@ dependencies = [ "cfg-if", "const-hex", "derive_more", - "foldhash", - "getrandom 0.3.4", + "foldhash 0.2.0", + "getrandom 0.4.1", "hashbrown 0.16.1", "indexmap 2.13.0", "itoa", @@ -424,14 +437,13 @@ dependencies = [ "rustc-hash", "serde", "sha3", - "tiny-keccak", ] [[package]] name = "alloy-provider" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b56f7a77513308a21a2ba0e9d57785a9d9d2d609e77f4e71a78a1192b83ff2d" +checksum = "d181c8cc7cf4805d7e589bf4074d56d55064fa1a979f005a45a62b047616d870" dependencies = [ "alloy-chains", "alloy-consensus", @@ -461,11 +473,11 @@ dependencies = [ "futures-utils-wasm", "lru", "parking_lot", - "pin-project 1.1.10", - "reqwest", + "pin-project 1.1.11", + "reqwest 0.12.28", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", "url", @@ -474,9 +486,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94813abbd7baa30c700ea02e7f92319dbcb03bff77aeea92a3a9af7ba19c5c70" +checksum = "e8bd82953194dec221aa4cbbbb0b1e2df46066fe9d0333ac25b43a311e122d13" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -496,9 +508,9 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f70d83b765fdc080dbcd4f4db70d8d23fe4761f2f02ebfa9146b833900634b4" +checksum = "e93e50f64a77ad9c5470bf2ad0ca02f228da70c792a8f06634801e202579f35e" dependencies = [ "alloy-rlp-derive", "arrayvec", @@ -507,20 +519,20 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" +checksum = "ce8849c74c9ca0f5a03da1c865e3eb6f768df816e67dd3721a398a8a7e398011" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "alloy-rpc-client" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff01723afc25ec4c5b04de399155bef7b6a96dfde2475492b1b7b4e7a4f46445" +checksum = "f2792758a93ae32a32e9047c843d536e1448044f78422d71bf7d7c05149e103f" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -530,8 +542,8 @@ dependencies = [ "alloy-transport-ipc", "alloy-transport-ws", "futures", - "pin-project 1.1.10", - "reqwest", + "pin-project 1.1.11", + "reqwest 0.12.28", "serde", "serde_json", "tokio", @@ -544,9 +556,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91bf006bb06b7d812591b6ac33395cb92f46c6a65cda11ee30b348338214f0f" +checksum = "7bdcbf9dfd5eea8bfeb078b1d906da8cd3a39c4d4dbe7a628025648e323611f6" dependencies = [ "alloy-primitives", "alloy-rpc-types-anvil", @@ -560,9 +572,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e82145856df8abb1fefabef58cdec0f7d9abf337d4abd50c1ed7e581634acdd" +checksum = "e0a3100b76987c1b1dc81f3abe592b7edc29e92b1242067a69d65e0030b35cf9" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -572,9 +584,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212ca1c1dab27f531d3858f8b1a2d6bfb2da664be0c1083971078eb7b71abe4b" +checksum = "dd720b63f82b457610f2eaaf1f32edf44efffe03ae25d537632e7d23e7929e1a" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -583,9 +595,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-beacon" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d92a9b4b268fac505ef7fb1dac9bb129d4fd7de7753f22a5b6e9f666f7f7de6" +checksum = "4a22e13215866f5dfd5d3278f4c41f1fad9410dc68ce39022f58593c873c26f8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -594,14 +606,14 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-rpc-types-debug" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab1ebed118b701c497e6541d2d11dfa6f3c6ae31a3c52999daa802fcdcc16b7" +checksum = "e1b21e1ad18ff1b31ff1030e046462ab8168cf8894e6778cd805c8bdfe2bd649" dependencies = [ "alloy-primitives", "derive_more", @@ -611,9 +623,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "232f00fcbcd3ee3b9399b96223a8fc884d17742a70a44f9d7cef275f93e6e872" +checksum = "e4ac61f03f1edabccde1c687b5b25fff28f183afee64eaa2e767def3929e4457" dependencies = [ "alloy-consensus", "alloy-eips", @@ -631,9 +643,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5715d0bf7efbd360873518bd9f6595762136b5327a9b759a8c42ccd9b5e44945" +checksum = "9b2dc411f13092f237d2bf6918caf80977fc2f51485f9b90cb2a2f956912c8c9" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -647,28 +659,28 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-rpc-types-trace" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9763cc931a28682bd4b9a68af90057b0fbe80e2538a82251afd69d7ae00bbebf" +checksum = "1ad79f1e27e161943b5a4f99fe5534ef0849876214be411e0032c12f38e94daa" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", "alloy-serde", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-rpc-types-txpool" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "359a8caaa98cb49eed62d03f5bc511dd6dd5dee292238e8627a6e5690156df0f" +checksum = "d459f902a2313737bc66d18ed094c25d2aeb268b74d98c26bbbda2aa44182ab0" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -678,9 +690,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ed8531cae8d21ee1c6571d0995f8c9f0652a6ef6452fde369283edea6ab7138" +checksum = "e2ce1e0dbf7720eee747700e300c99aac01b1a95bb93f493a01e78ee28bb1a37" dependencies = [ "alloy-primitives", "serde", @@ -689,9 +701,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb10ccd49d0248df51063fce6b716f68a315dd912d55b32178c883fd48b4021d" +checksum = "2425c6f314522c78e8198979c8cbf6769362be4da381d4152ea8eefce383535d" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -701,14 +713,14 @@ dependencies = [ "either", "elliptic-curve", "k256", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-signer-aws" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4e8f1e3be115a00199cd6613f36cac93b5be965e65d57b125f22008bcfad6f2" +checksum = "e38b411077d7b17e464de7dfa599f5b94161cdffc25c2f28a90a3a345b6d6490" dependencies = [ "alloy-consensus", "alloy-network", @@ -719,15 +731,15 @@ dependencies = [ "aws-sdk-kms", "k256", "spki", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", ] [[package]] name = "alloy-signer-gcp" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f30890cebc37d343cef14aa913e258551aee5ff81e885cf2821024b7af4461f" +checksum = "485d0c73c53a36580be4d882a5c6c9a069759088de88ff759e59342a793adb16" dependencies = [ "alloy-consensus", "alloy-network", @@ -737,15 +749,15 @@ dependencies = [ "gcloud-sdk", "k256", "spki", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", ] [[package]] name = "alloy-signer-ledger" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237c572040b85cc1a16f7e006a73daa7a75be900d733662509856370354d08c2" +checksum = "ff7a41e469bce9a836a9fbba7c09f8eba25703062accf6a64bd90b5ed61c1b01" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -757,15 +769,15 @@ dependencies = [ "coins-ledger", "futures-util", "semver 1.0.27", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", ] [[package]] name = "alloy-signer-local" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4d992d44e6c414ece580294abbadb50e74cfd4eaa69787350a4dfd4b20eaa1b" +checksum = "c3ecb71ee53d8d9c3fa7bac17542c8116ebc7a9726c91b1bf333ec3d04f5a789" dependencies = [ "alloy-consensus", "alloy-network", @@ -777,15 +789,15 @@ dependencies = [ "eth-keystore", "k256", "rand 0.8.5", - "thiserror 2.0.17", + "thiserror 2.0.18", "zeroize", ] [[package]] name = "alloy-signer-trezor" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dea2a8e94ad2d084f5e57c99c83b0d96ffbdce5795d2e1a82885164275c8eaa" +checksum = "fb778ec3bef46eb5c0edab6e25e0c6f40d98a5e81e423940f819824b879680a8" dependencies = [ "alloy-consensus", "alloy-network", @@ -793,46 +805,46 @@ dependencies = [ "alloy-signer", "async-trait", "semver 1.0.27", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "trezor-client", ] [[package]] name = "alloy-signer-turnkey" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dedbaa085c63a796e1649dc5a5244c0065c849a0284b6aee7e8b607a64e8b48" +checksum = "589b334f4cf9de0d80568e8eaf11479b492e74dc2c08991306361065bde2321a" dependencies = [ "alloy-consensus", "alloy-network", "alloy-primitives", "alloy-signer", "async-trait", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "turnkey_client", ] [[package]] name = "alloy-sol-macro" -version = "1.5.2" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09eb18ce0df92b4277291bbaa0ed70545d78b02948df756bbd3d6214bf39a218" +checksum = "ab81bab693da9bb79f7a95b64b394718259fdd7e41dceeced4cad57cb71c4f6a" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "alloy-sol-macro-expander" -version = "1.5.2" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95d9fa2daf21f59aa546d549943f10b5cce1ae59986774019fbedae834ffe01b" +checksum = "489f1620bb7e2483fb5819ed01ab6edc1d2f93939dce35a5695085a1afd1d699" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -842,16 +854,16 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.114", + "sha3", + "syn 2.0.117", "syn-solidity", - "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "1.5.2" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9396007fe69c26ee118a19f4dee1f5d1d6be186ea75b3881adf16d87f8444686" +checksum = "56cef806ad22d4392c5fc83cf8f2089f988eb99c7067b4e0c6f1971fc1cca318" dependencies = [ "alloy-json-abi", "const-hex", @@ -861,15 +873,15 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.114", + "syn 2.0.117", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "1.5.2" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af67a0b0dcebe14244fc92002cd8d96ecbf65db4639d479f5fcd5805755a4c27" +checksum = "a6df77fea9d6a2a75c0ef8d2acbdfd92286cc599983d3175ccdc170d3433d249" dependencies = [ "serde", "winnow", @@ -877,9 +889,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.5.2" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09aeea64f09a7483bdcd4193634c7e5cf9fd7775ee767585270cd8ce2d69dc95" +checksum = "64612d29379782a5dde6f4b6570d9c756d734d760c0c94c254d361e678a6591f" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -889,9 +901,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f50a9516736d22dd834cc2240e5bf264f338667cc1d9e514b55ec5a78b987ca" +checksum = "fa186e560d523d196580c48bf00f1bf62e63041f28ecf276acc22f8b27bb9f53" dependencies = [ "alloy-json-rpc", "auto_impl", @@ -902,7 +914,7 @@ dependencies = [ "parking_lot", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tower", "tracing", @@ -912,13 +924,14 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a18b541a6197cf9a084481498a766fdf32fefda0c35ea6096df7d511025e9f1" +checksum = "aa501ad58dd20acddbfebc65b52e60f05ebf97c52fa40d1b35e91f5e2da0ad0e" dependencies = [ "alloy-json-rpc", "alloy-transport", - "reqwest", + "itertools 0.14.0", + "reqwest 0.12.28", "serde_json", "tower", "tracing", @@ -927,9 +940,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8075911680ebc537578cacf9453464fd394822a0f68614884a9c63f9fbaf5e89" +checksum = "c2ef85688e5ac2da72afc804e0a1f153a1f309f05a864b1998bbbed7804dbaab" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -937,7 +950,7 @@ dependencies = [ "bytes", "futures", "interprocess", - "pin-project 1.1.10", + "pin-project 1.1.11", "serde", "serde_json", "tokio", @@ -947,9 +960,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "921d37a57e2975e5215f7dd0f28873ed5407c7af630d4831a4b5c737de4b0b8b" +checksum = "b9f00445db69d63298e2b00a0ea1d859f00e6424a3144ffc5eba9c31da995e16" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -964,9 +977,9 @@ dependencies = [ [[package]] name = "alloy-trie" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428aa0f0e0658ff091f8f667c406e034b431cb10abd39de4f507520968acc499" +checksum = "4d7fd448ab0a017de542de1dcca7a58e7019fe0e7a34ed3f9543ebddf6aceffa" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -975,19 +988,20 @@ dependencies = [ "nybbles", "serde", "smallvec", + "thiserror 2.0.18", "tracing", ] [[package]] name = "alloy-tx-macros" -version = "1.4.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2289a842d02fe63f8c466db964168bb2c7a9fdfb7b24816dbb17d45520575fb" +checksum = "6fa0c53e8c1e1ef4d01066b01c737fb62fc9397ab52c6e7bb5669f97d281b9bc" dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1011,9 +1025,9 @@ dependencies = [ [[package]] name = "annotate-snippets" -version = "0.12.10" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15580ece6ea97cbf832d60ba19c021113469480852c6a2a6beb0db28f097bf1f" +checksum = "c86cd1c51b95d71dde52bca69ed225008f6ff4c8cc825b08042aa1ef823e1980" dependencies = [ "anstyle", "memchr", @@ -1094,7 +1108,7 @@ dependencies = [ [[package]] name = "anvil" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-chains", "alloy-consensus", @@ -1148,14 +1162,14 @@ dependencies = [ "parking_lot", "rand 0.8.5", "rand 0.9.2", - "reqwest", + "reqwest 0.12.28", "revm", "revm-inspectors", "serde", "serde_json", "tempfile", "tempo-primitives", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", "tracing-subscriber 0.3.22", @@ -1164,7 +1178,7 @@ dependencies = [ [[package]] name = "anvil-core" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -1182,12 +1196,12 @@ dependencies = [ "revm", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "anvil-rpc" -version = "1.5.1" +version = "1.6.0" dependencies = [ "serde", "serde_json", @@ -1195,7 +1209,7 @@ dependencies = [ [[package]] name = "anvil-server" -version = "1.5.1" +version = "1.6.0" dependencies = [ "anvil-rpc", "async-trait", @@ -1205,10 +1219,10 @@ dependencies = [ "futures", "interprocess", "parking_lot", - "pin-project 1.1.10", + "pin-project 1.1.11", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio-util", "tower-http", "tracing", @@ -1216,9 +1230,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "arbitrary" @@ -1359,7 +1373,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1397,7 +1411,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1486,7 +1500,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1545,9 +1559,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.37" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d10e4f991a553474232bc0a31799f6d24b034a84c0971d80d2e2f78b2e576e40" +checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" dependencies = [ "compression-codecs", "compression-core", @@ -1583,7 +1597,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1594,7 +1608,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1647,7 +1661,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1658,9 +1672,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-config" -version = "1.8.12" +version = "1.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96571e6996817bf3d58f6b569e4b9fd2e9d2fcf9f7424eed07b2ce9bb87535e5" +checksum = "8a8fc176d53d6fe85017f230405e3255cedb4a02221cb55ed6d76dccbbb099b2" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1688,9 +1702,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.2.11" +version = "1.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd362783681b15d136480ad555a099e82ecd8e2d10a841e14dfd0078d67fee3" +checksum = "6d203b0bf2626dcba8665f5cd0871d7c2c0930223d6b6be9097592fea21242d0" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -1700,9 +1714,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.15.3" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e84ce723ab67259cfeb9877c6a639ee9eb7a27b28123abd71db7f0d5d0cc9d86" +checksum = "d9a7b350e3bb1767102698302bc37256cbd48422809984b98d292c40e2579aa9" dependencies = [ "aws-lc-sys", "zeroize", @@ -1710,9 +1724,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.36.0" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a442ece363113bd4bd4c8b18977a7798dd4d3c3383f34fb61936960e8f4ad8" +checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549" dependencies = [ "cc", "cmake", @@ -1722,9 +1736,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.5.18" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "959dab27ce613e6c9658eb3621064d0e2027e5f2acb65bc526a43577facea557" +checksum = "ede2ddc593e6c8acc6ce3358c28d6677a6dc49b65ba4b37a2befe14a11297e75" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -1735,20 +1749,21 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", + "bytes-utils", "fastrand", - "http 0.2.12", - "http-body 0.4.6", + "http 1.4.0", + "http-body 1.0.1", "percent-encoding", "pin-project-lite", "tracing", - "uuid 1.19.0", + "uuid 1.21.0", ] [[package]] name = "aws-sdk-kms" -version = "1.98.0" +version = "1.102.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c74fef3d08159467cad98300f33a2e3bd1a985d527ad66ab0ea83c95e3a615" +checksum = "22b682ef733ec24c300b11cec2df9bfea7ee4bf48ab2030c832e27db92b69c68" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1763,15 +1778,16 @@ dependencies = [ "bytes", "fastrand", "http 0.2.12", + "http 1.4.0", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-sso" -version = "1.92.0" +version = "1.95.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7d63bd2bdeeb49aa3f9b00c15e18583503b778b2e792fc06284d54e7d5b6566" +checksum = "00c5ff27c6ba2cbd95e6e26e2e736676fdf6bcf96495b187733f521cfe4ce448" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1786,15 +1802,16 @@ dependencies = [ "bytes", "fastrand", "http 0.2.12", + "http 1.4.0", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-ssooidc" -version = "1.94.0" +version = "1.97.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532d93574bf731f311bafb761366f9ece345a0416dbcc273d81d6d1a1205239b" +checksum = "4d186f1e5a3694a188e5a0640b3115ccc6e084d104e16fd6ba968dca072ffef8" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1809,15 +1826,16 @@ dependencies = [ "bytes", "fastrand", "http 0.2.12", + "http 1.4.0", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-sts" -version = "1.96.0" +version = "1.99.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357e9a029c7524db6a0099cd77fbd5da165540339e7296cca603531bc783b56c" +checksum = "9acba7c62f3d4e2408fa998a3a8caacd8b9a5b5549cf36e2372fbdae329d5449" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1833,15 +1851,16 @@ dependencies = [ "aws-types", "fastrand", "http 0.2.12", + "http 1.4.0", "regex-lite", "tracing", ] [[package]] name = "aws-sigv4" -version = "1.3.7" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e523e1c4e8e7e8ff219d732988e22bfeae8a1cafdbe6d9eca1546fa080be7c" +checksum = "37411f8e0f4bea0c3ca0958ce7f18f6439db24d555dbd809787262cd00926aa9" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -1854,16 +1873,16 @@ dependencies = [ "http 0.2.12", "http 1.4.0", "percent-encoding", - "sha2", + "sha2 0.10.9", "time", "tracing", ] [[package]] name = "aws-smithy-async" -version = "1.2.7" +version = "1.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee19095c7c4dda59f1697d028ce704c24b2d33c6718790c7f1d5a3015b4107c" +checksum = "5cc50d0f63e714784b84223abd7abbc8577de8c35d699e0edd19f0a88a08ae13" dependencies = [ "futures-util", "pin-project-lite", @@ -1872,9 +1891,9 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.62.6" +version = "0.63.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826141069295752372f8203c17f28e30c464d22899a43a0c9fd9c458d469c88b" +checksum = "d619373d490ad70966994801bc126846afaa0d1ee920697a031f0cf63f2568e7" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", @@ -1882,9 +1901,9 @@ dependencies = [ "bytes-utils", "futures-core", "futures-util", - "http 0.2.12", "http 1.4.0", - "http-body 0.4.6", + "http-body 1.0.1", + "http-body-util", "percent-encoding", "pin-project-lite", "pin-utils", @@ -1893,9 +1912,9 @@ dependencies = [ [[package]] name = "aws-smithy-http-client" -version = "1.1.5" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e62db736db19c488966c8d787f52e6270be565727236fd5579eaa301e7bc4a" +checksum = "00ccbb08c10f6bcf912f398188e42ee2eab5f1767ce215a02a73bc5df1bbdd95" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -1917,27 +1936,27 @@ dependencies = [ [[package]] name = "aws-smithy-json" -version = "0.61.9" +version = "0.62.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49fa1213db31ac95288d981476f78d05d9cbb0353d22cdf3472cc05bb02f6551" +checksum = "27b3a779093e18cad88bbae08dc4261e1d95018c4c5b9356a52bcae7c0b6e9bb" dependencies = [ "aws-smithy-types", ] [[package]] name = "aws-smithy-observability" -version = "0.2.0" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1fcbefc7ece1d70dcce29e490f269695dfca2d2bacdeaf9e5c3f799e4e6a42" +checksum = "4d3f39d5bb871aaf461d59144557f16d5927a5248a983a40654d9cf3b9ba183b" dependencies = [ "aws-smithy-runtime-api", ] [[package]] name = "aws-smithy-query" -version = "0.60.9" +version = "0.60.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae5d689cf437eae90460e944a58b5668530d433b4ff85789e69d2f2a556e057d" +checksum = "05f76a580e3d8f8961e5d48763214025a2af65c2fa4cd1fb7f270a0e107a71b0" dependencies = [ "aws-smithy-types", "urlencoding", @@ -1945,9 +1964,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.9.8" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb5b6167fcdf47399024e81ac08e795180c576a20e4d4ce67949f9a88ae37dc1" +checksum = "22ccf7f6eba8b2dcf8ce9b74806c6c185659c311665c4bf8d6e71ebd454db6bf" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1961,6 +1980,7 @@ dependencies = [ "http 1.4.0", "http-body 0.4.6", "http-body 1.0.1", + "http-body-util", "pin-project-lite", "pin-utils", "tokio", @@ -1969,9 +1989,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.10.0" +version = "1.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efce7aaaf59ad53c5412f14fc19b2d5c6ab2c3ec688d272fd31f76ec12f44fb0" +checksum = "b4af6e5def28be846479bbeac55aa4603d6f7986fc5da4601ba324dd5d377516" dependencies = [ "aws-smithy-async", "aws-smithy-types", @@ -1986,9 +2006,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.3.6" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65f172bcb02424eb94425db8aed1b6d583b5104d4d5ddddf22402c661a320048" +checksum = "8ca2734c16913a45343b37313605d84e7d8b34a4611598ce1d25b35860a2bed3" dependencies = [ "base64-simd", "bytes", @@ -2009,18 +2029,18 @@ dependencies = [ [[package]] name = "aws-smithy-xml" -version = "0.60.13" +version = "0.60.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11b2f670422ff42bf7065031e72b45bc52a3508bd089f743ea90731ca2b6ea57" +checksum = "b53543b4b86ed43f051644f704a98c7291b3618b67adf057ee77a366fa52fcaa" dependencies = [ "xmlparser", ] [[package]] name = "aws-types" -version = "1.3.11" +version = "1.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d980627d2dd7bfc32a3c025685a033eeab8d365cc840c631ef59d1b8f428164" +checksum = "0470cc047657c6e286346bdf10a8719d26efd6a91626992e0e64481e44323e96" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -2085,12 +2105,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.76" @@ -2103,7 +2117,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -2191,9 +2205,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" dependencies = [ "serde_core", ] @@ -2211,6 +2225,15 @@ dependencies = [ "wyz", ] +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -2247,7 +2270,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc119a5ad34c3f459062a96907f53358989b173d104258891bb74f95d93747e8" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "boa_interner", "boa_macros", "boa_string", @@ -2264,7 +2287,7 @@ checksum = "e637ec52ea66d76b0ca86180c259d6c7bb6e6a6e14b2f36b85099306d8b00cc3" dependencies = [ "aligned-vec", "arrayvec", - "bitflags 2.10.0", + "bitflags 2.11.0", "boa_ast", "boa_gc", "boa_interner", @@ -2303,7 +2326,7 @@ dependencies = [ "tag_ptr", "tap", "thin-vec", - "thiserror 2.0.17", + "thiserror 2.0.18", "time", "xsum", ] @@ -2346,7 +2369,7 @@ dependencies = [ "cow-utils", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "synstructure", ] @@ -2356,7 +2379,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02f99bf5b684f0de946378fcfe5f38c3a0fbd51cbf83a0f39ff773a0e218541f" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "boa_ast", "boa_interner", "boa_macros", @@ -2384,9 +2407,9 @@ dependencies = [ [[package]] name = "bon" -version = "3.8.2" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234655ec178edd82b891e262ea7cf71f6584bcd09eff94db786be23f1821825c" +checksum = "2d13a61f2963b88eef9c1be03df65d42f6996dfeac1054870d950fcf66686f83" dependencies = [ "bon-macros", "rustversion", @@ -2394,9 +2417,9 @@ dependencies = [ [[package]] name = "bon-macros" -version = "3.8.2" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ec27229c38ed0eb3c0feee3d2c1d6a4379ae44f418a29a658890e062d8f365" +checksum = "d314cc62af2b6b0c65780555abb4d02a03dd3b799cd42419044f0c38d99738c0" dependencies = [ "darling 0.23.0", "ident_case", @@ -2404,7 +2427,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2427,7 +2450,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2442,7 +2465,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ - "sha2", + "sha2 0.10.9", "tinyvec", ] @@ -2459,9 +2482,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "byte-slice-cast" @@ -2471,9 +2494,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", ] @@ -2486,7 +2509,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2497,9 +2520,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", ] @@ -2516,9 +2539,9 @@ dependencies = [ [[package]] name = "c-kzg" -version = "2.1.5" +version = "2.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e00bf4b112b07b505472dbefd19e37e53307e2bfed5a79e0cc161d58ccd0e687" +checksum = "1a0f582957c24870b7bfd12bf562c40b4734b533cafbaf8ded31d6d85f462c01" dependencies = [ "blst", "cc", @@ -2547,16 +2570,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cargo-platform" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87a0c0e6148f11f01f32650a2ea02d532b2ad4e81d8bd41e6e565b5adc5e6082" -dependencies = [ - "serde", - "serde_core", -] - [[package]] name = "cargo_metadata" version = "0.18.1" @@ -2564,30 +2577,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", - "cargo-platform 0.1.9", + "cargo-platform", "semver 1.0.27", "serde", "serde_json", "thiserror 1.0.69", ] -[[package]] -name = "cargo_metadata" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9" -dependencies = [ - "camino", - "cargo-platform 0.3.2", - "semver 1.0.27", - "serde", - "serde_json", - "thiserror 2.0.17", -] - [[package]] name = "cast" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-chains", "alloy-consensus", @@ -2658,9 +2657,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.53" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", "jobserver", @@ -2688,7 +2687,7 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chisel" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -2706,7 +2705,7 @@ dependencies = [ "foundry-solang-parser", "foundry-test-utils", "itertools 0.14.0", - "reqwest", + "reqwest 0.12.28", "rexpect", "rustyline", "semver 1.0.27", @@ -2723,16 +2722,16 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", "num-traits", "serde", "wasm-bindgen", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -2774,9 +2773,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.54" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" dependencies = [ "clap_builder", "clap_derive", @@ -2794,9 +2793,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.54" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" dependencies = [ "anstream", "anstyle", @@ -2809,9 +2808,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.65" +version = "4.5.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "430b4dc2b5e3861848de79627b2bedc9f3342c7da5173a14eaa5d0f8dc18ae5d" +checksum = "c757a3b7e39161a4e56f9365141ada2a6c915a8622c408ab6bb4b5d047371031" dependencies = [ "clap", ] @@ -2828,42 +2827,42 @@ 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", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "clap_lex" -version = "0.7.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" [[package]] name = "clearscreen" -version = "4.0.2" +version = "4.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a8ab73a1c02b0c15597b22e09c7dc36e63b2f601f9d1e83ac0c3decd38b1ae" +checksum = "5def4343d62f01f67ff1a49147e4a15112e936c6a6a3f8ff7a29394e76468244" dependencies = [ - "nix 0.29.0", + "nix 0.31.2", "terminfo", - "thiserror 2.0.17", + "thiserror 2.0.18", "which", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "cliclack" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2381872509dfa50d8b92b92a5da8367ba68458ab9494be4134b57ad6ca26295f" +checksum = "4797110534d49f4e38465be8d84c911f3a9e0f6582f70d3aa4cb30c8fa737851" dependencies = [ - "console 0.15.11", + "console 0.16.2", "indicatif", "once_cell", "strsim", @@ -2901,7 +2900,7 @@ dependencies = [ "hmac", "k256", "serde", - "sha2", + "sha2 0.10.9", "thiserror 1.0.69", ] @@ -2917,7 +2916,7 @@ dependencies = [ "once_cell", "pbkdf2 0.12.2", "rand 0.8.5", - "sha2", + "sha2 0.10.9", "thiserror 1.0.69", ] @@ -2935,7 +2934,7 @@ dependencies = [ "generic-array", "ripemd", "serde", - "sha2", + "sha2 0.10.9", "sha3", "thiserror 1.0.69", ] @@ -3048,9 +3047,9 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.36" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00828ba6fd27b45a448e57dbfe84f1029d4c9f26b368157e9a448a5f49a2ec2a" +checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" dependencies = [ "compression-core", "flate2", @@ -3081,7 +3080,6 @@ dependencies = [ "encode_unicode", "libc", "once_cell", - "unicode-width 0.2.2", "windows-sys 0.59.0", ] @@ -3110,9 +3108,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bb320cac8a0750d7f25280aa97b09c26edfe161164238ecbbb31092b079e735" +checksum = "af9a108e542ddf1de36743a6126e94d6659dccda38fc8a77e80b915102ac784a" dependencies = [ "cfg-if", "cpufeatures", @@ -3266,7 +3264,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "crossterm_winapi", "derive_more", "document-features", @@ -3326,15 +3324,28 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.5.1" +version = "3.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73736a89c4aff73035ba2ed2e565061954da00d4970fc9ac25dcc85a2a20d790" +checksum = "e0b1fab2ae45819af2d0731d60f2afe17227ebb1a1538a236da84c93e9a60162" dependencies = [ "dispatch2", - "nix 0.30.1", + "nix 0.31.2", "windows-sys 0.61.2", ] +[[package]] +name = "curve25519-dalek-ng" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.6.4", + "subtle-ng", + "zeroize", +] + [[package]] name = "darling" version = "0.20.11" @@ -3376,7 +3387,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3391,7 +3402,7 @@ dependencies = [ "quote", "serde", "strsim", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3404,7 +3415,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3415,7 +3426,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3426,7 +3437,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3437,7 +3448,7 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core 0.23.0", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3473,9 +3484,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", "serde_core", @@ -3500,7 +3511,7 @@ checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3511,7 +3522,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3532,7 +3543,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3542,7 +3553,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3564,7 +3575,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.114", + "syn 2.0.117", "unicode-xid", ] @@ -3579,6 +3590,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" @@ -3594,7 +3611,7 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "const-oid", "crypto-common", "subtle", @@ -3623,11 +3640,11 @@ dependencies = [ [[package]] name = "dispatch2" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "block2", "libc", "objc2", @@ -3641,7 +3658,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3700,7 +3717,7 @@ checksum = "1ec431cd708430d5029356535259c5d645d60edd3d39c54e5eea9782d46caa7d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3718,6 +3735,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" @@ -3727,7 +3759,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3780,22 +3812,22 @@ dependencies = [ [[package]] name = "email-address-parser" -version = "2.0.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe19a4967eca30062be4abaf813d929ba48b3bfb21830367f7e1baae37f213a" +checksum = "e981c3b50d728bb498dd0f860a7228ef17e19efef5cc2c6e30d78ebce13bcaa7" dependencies = [ "console_error_panic_hook", "pest", "pest_derive", - "quick-xml 0.18.1", + "quick-xml 0.39.2", "wasm-bindgen", ] [[package]] name = "ena" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" +checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1" dependencies = [ "log", ] @@ -3838,14 +3870,14 @@ checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "env_filter" -version = "0.1.4" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +checksum = "7a1c3cc8e57274ec99de65301228b537f1e4eedc1b8e0f9411c6caac8ae7308f" dependencies = [ "log", "regex", @@ -3859,9 +3891,9 @@ checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" [[package]] name = "env_logger" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" dependencies = [ "anstream", "anstyle", @@ -3887,7 +3919,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3939,7 +3971,7 @@ dependencies = [ "scrypt", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "sha3", "thiserror 1.0.69", "uuid 0.8.2", @@ -3997,7 +4029,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4124,9 +4156,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" @@ -4148,9 +4180,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", @@ -4173,6 +4205,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foldhash" version = "0.2.0" @@ -4187,7 +4225,7 @@ checksum = "932dcfbd51320af5f27f1ba02d2e567dec332cac7d2c221ba45d8e767264c4dc" [[package]] name = "forge" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-chains", "alloy-dyn-abi", @@ -4239,9 +4277,10 @@ dependencies = [ "path-slash", "proptest", "quick-junit", + "rand 0.9.2", "rayon", "regex", - "reqwest", + "reqwest 0.12.28", "revm", "semver 1.0.27", "serde", @@ -4250,12 +4289,13 @@ dependencies = [ "similar-asserts", "solar-compiler", "soldeer-commands", + "soldeer-core", "strum", "svm-rs", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", - "toml_edit 0.24.0+spec-1.1.0", + "toml_edit 0.24.1+spec-1.1.0", "tower-http", "tracing", "url", @@ -4267,7 +4307,7 @@ dependencies = [ [[package]] name = "forge-doc" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-primitives", "derive_more", @@ -4284,14 +4324,14 @@ dependencies = [ "serde", "serde_json", "solar-compiler", - "thiserror 2.0.17", + "thiserror 2.0.18", "toml", "tracing", ] [[package]] name = "forge-fmt" -version = "1.5.1" +version = "1.6.0" dependencies = [ "foundry-common", "foundry-config", @@ -4305,7 +4345,7 @@ dependencies = [ [[package]] name = "forge-lint" -version = "1.5.1" +version = "1.6.0" dependencies = [ "eyre", "foundry-common", @@ -4314,12 +4354,12 @@ dependencies = [ "heck", "rayon", "solar-compiler", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "forge-script" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-chains", "alloy-consensus", @@ -4356,7 +4396,7 @@ dependencies = [ "serde", "serde_json", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", "yansi", @@ -4364,7 +4404,7 @@ dependencies = [ [[package]] name = "forge-script-sequence" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-network", "alloy-primitives", @@ -4380,7 +4420,7 @@ dependencies = [ [[package]] name = "forge-sol-macro-gen" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -4390,12 +4430,12 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "forge-verify" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -4416,7 +4456,7 @@ dependencies = [ "futures", "itertools 0.14.0", "regex", - "reqwest", + "reqwest 0.12.28", "revm", "semver 1.0.27", "serde", @@ -4439,7 +4479,7 @@ dependencies = [ [[package]] name = "foundry-bench" -version = "1.5.1" +version = "1.6.0" dependencies = [ "chrono", "clap", @@ -4464,17 +4504,17 @@ dependencies = [ "alloy-json-abi", "alloy-primitives", "foundry-compilers", - "reqwest", + "reqwest 0.12.28", "semver 1.0.27", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", ] [[package]] name = "foundry-cheatcodes" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-chains", "alloy-consensus", @@ -4494,6 +4534,7 @@ dependencies = [ "base64 0.22.1", "dialoguer", "ecdsa", + "ed25519-consensus", "eyre", "forge-script-sequence", "foundry-cheatcodes-spec", @@ -4503,6 +4544,7 @@ dependencies = [ "foundry-evm-core", "foundry-evm-fuzz", "foundry-evm-traces", + "foundry-primitives", "foundry-wallets", "itertools 0.14.0", "jsonpath_lib", @@ -4518,7 +4560,7 @@ dependencies = [ "serde", "serde_json", "solar-compiler", - "thiserror 2.0.17", + "thiserror 2.0.18", "toml", "tracing", "walkdir", @@ -4526,18 +4568,18 @@ dependencies = [ [[package]] name = "foundry-cheatcodes-spec" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-sol-types", "foundry-macros", - "schemars 1.2.0", + "schemars 1.2.1", "serde", "serde_json", ] [[package]] name = "foundry-cli" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-chains", "alloy-dyn-abi", @@ -4556,6 +4598,7 @@ dependencies = [ "dunce", "eyre", "foundry-block-explorers", + "foundry-cli-markdown", "foundry-common", "foundry-compilers", "foundry-config", @@ -4583,9 +4626,17 @@ dependencies = [ "yansi", ] +[[package]] +name = "foundry-cli-markdown" +version = "1.6.0" +dependencies = [ + "clap", + "pretty_assertions", +] + [[package]] name = "foundry-common" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-chains", "alloy-consensus", @@ -4618,31 +4669,31 @@ dependencies = [ "foundry-block-explorers", "foundry-common-fmt", "foundry-compilers", + "foundry-config", "itertools 0.14.0", "jiff", "num-format", "path-slash", "regex", - "reqwest", + "reqwest 0.12.28", "revm", "semver 1.0.27", "serde", "serde_json", "solar-compiler", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tower", "tracing", "url", - "vergen", - "vergen-git2", + "vergen-gitcl", "walkdir", "yansi", ] [[package]] name = "foundry-common-fmt" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -4654,18 +4705,20 @@ dependencies = [ "eyre", "foundry-macros", "foundry-primitives", + "op-alloy-consensus", "revm", "serde", "serde_json", "similar-asserts", + "tempo-alloy", "yansi", ] [[package]] name = "foundry-compilers" -version = "0.19.10" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "366854900404df2e7dce32662bdf76e442ec2001b72eb3f15cb1af7f6c2c3501" +checksum = "e639f98fe54d1cc0011a4bdb2eb1d838b379c9f004991ae7555a4cc09e8da32a" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -4682,12 +4735,12 @@ dependencies = [ "semver 1.0.27", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "solar-compiler", "svm-rs", "svm-rs-builds", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "winnow", "yansi", @@ -4695,9 +4748,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" -version = "0.19.10" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d46ae5cbaccf86b1019194309398b04bb65ab9cfb07c8e4ca2e79a95bbc85f" +checksum = "93ec96df20055211f4e46b5a61fa479b2ea7d1ce0659818e0359afadfcded8d2" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -4705,9 +4758,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" -version = "0.19.10" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0265397f148271a9e5733c9598685181ce3f3d0b878187220fb9ec98c208ef7d" +checksum = "f8a206e475b5dd1a77dc33cd917cde4846148f5136729a24edb3a16ab431b90a" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -4719,16 +4772,16 @@ dependencies = [ "semver 1.0.27", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "yansi", ] [[package]] name = "foundry-compilers-artifacts-vyper" -version = "0.19.10" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c5e38bda9f97e2a3b36a08f24ea1bf343c90f5c8398bd07ebf48a6ed65d8cf" +checksum = "f74883db8036522fa21d0853c21ac318e165ec88e141f1ef1d6f7b4dfa841ff7" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -4741,9 +4794,9 @@ dependencies = [ [[package]] name = "foundry-compilers-core" -version = "0.19.10" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4f2dda845ce5933e70dcd6dd5b3edfb953d3dcf764bd64f96a31d0de56d6d8" +checksum = "2ab384daeaea5c33cad8c3c094a1eb6f98e70922e18380c660980c74c19e362b" dependencies = [ "alloy-primitives", "cfg-if", @@ -4756,7 +4809,7 @@ dependencies = [ "serde_json", "svm-rs", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "walkdir", "xxhash-rust", @@ -4764,7 +4817,7 @@ dependencies = [ [[package]] name = "foundry-config" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-chains", "alloy-primitives", @@ -4784,7 +4837,7 @@ dependencies = [ "path-slash", "rayon", "regex", - "reqwest", + "reqwest 0.12.28", "revm", "semver 1.0.27", "serde", @@ -4794,9 +4847,9 @@ dependencies = [ "solar-compiler", "soldeer-core", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.18", "toml", - "toml_edit 0.24.0+spec-1.1.0", + "toml_edit 0.24.1+spec-1.1.0", "tracing", "unit-prefix", "walkdir", @@ -4805,7 +4858,7 @@ dependencies = [ [[package]] name = "foundry-debugger" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-primitives", "crossterm", @@ -4823,7 +4876,7 @@ dependencies = [ [[package]] name = "foundry-evm" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-dyn-abi", "alloy-evm", @@ -4844,18 +4897,20 @@ dependencies = [ "indicatif", "parking_lot", "proptest", + "rayon", "revm", "revm-inspectors", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", + "tokio", "tracing", - "uuid 1.19.0", + "uuid 1.21.0", ] [[package]] name = "foundry-evm-abi" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -4867,7 +4922,7 @@ dependencies = [ [[package]] name = "foundry-evm-core" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-chains", "alloy-consensus", @@ -4900,7 +4955,7 @@ dependencies = [ "revm-inspectors", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", "url", @@ -4908,7 +4963,7 @@ dependencies = [ [[package]] name = "foundry-evm-coverage" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-primitives", "eyre", @@ -4924,7 +4979,7 @@ dependencies = [ [[package]] name = "foundry-evm-fuzz" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -4943,13 +4998,13 @@ dependencies = [ "revm", "serde", "solar-compiler", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", ] [[package]] name = "foundry-evm-networks" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-chains", "alloy-eips", @@ -4963,7 +5018,7 @@ dependencies = [ [[package]] name = "foundry-evm-traces" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -4981,7 +5036,7 @@ dependencies = [ "itertools 0.14.0", "memchr", "rayon", - "reqwest", + "reqwest 0.12.28", "revm", "revm-inspectors", "serde", @@ -4995,9 +5050,9 @@ dependencies = [ [[package]] name = "foundry-fork-db" -version = "0.21.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df2fd495cf7337b247d960f90355329cc625fe27fe7da9fe5e598c42df21526" +checksum = "ad537243394b8cda523e63749f2c9833be4c612500d35518efad34a3d1086710" dependencies = [ "alloy-chains", "alloy-consensus", @@ -5011,7 +5066,7 @@ dependencies = [ "revm", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", "url", @@ -5019,37 +5074,39 @@ dependencies = [ [[package]] name = "foundry-linking" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-primitives", "foundry-compilers", "rayon", "semver 1.0.27", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "foundry-macros" -version = "1.5.1" +version = "1.6.0" dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "foundry-primitives" -version = "1.5.1" +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", @@ -5057,6 +5114,7 @@ dependencies = [ "revm", "serde", "serde_json", + "tempo-alloy", "tempo-primitives", ] @@ -5070,14 +5128,15 @@ dependencies = [ "lalrpop", "lalrpop-util", "phf 0.11.3", - "thiserror 2.0.17", + "thiserror 2.0.18", "unicode-xid", ] [[package]] name = "foundry-test-utils" -version = "1.5.1" +version = "1.6.0" dependencies = [ + "alloy-chains", "alloy-primitives", "alloy-provider", "eyre", @@ -5090,7 +5149,7 @@ dependencies = [ "parking_lot", "rand 0.9.2", "regex", - "reqwest", + "reqwest 0.12.28", "serde_json", "snapbox", "svm-rs", @@ -5103,13 +5162,12 @@ dependencies = [ [[package]] name = "foundry-wallets" -version = "1.5.1" +version = "1.6.0" dependencies = [ "alloy-consensus", "alloy-dyn-abi", "alloy-network", "alloy-primitives", - "alloy-rpc-types", "alloy-signer", "alloy-signer-aws", "alloy-signer-gcp", @@ -5127,18 +5185,16 @@ dependencies = [ "eyre", "foundry-common", "foundry-config", - "foundry-primitives", - "reqwest", + "reqwest 0.12.28", "rpassword", "serde", "serde_json", - "tempo-primitives", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tower", "tower-http", "tracing", - "uuid 1.19.0", + "uuid 1.21.0", "webbrowser", ] @@ -5181,9 +5237,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -5196,9 +5252,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -5206,28 +5262,28 @@ dependencies = [ [[package]] name = "futures-concurrency" -version = "7.7.0" +version = "7.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69a9561702beff46b705a8ac9c0803ec4c7fc5d01330a99b1feaf86e206e92ba" +checksum = "175cd8cca9e1d45b87f18ffa75088f2099e3c4fe5e2f83e42de112560bea8ea6" dependencies = [ "fixedbitset", "futures-core", "futures-lite", - "pin-project 1.1.10", + "pin-project 1.1.11", "smallvec", ] [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -5236,9 +5292,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-lite" @@ -5255,32 +5311,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -5290,7 +5346,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -5315,7 +5370,7 @@ dependencies = [ "once_cell", "prost 0.13.5", "prost-types 0.13.5", - "reqwest", + "reqwest 0.12.28", "secret-vault-value", "serde", "serde_json", @@ -5339,8 +5394,8 @@ dependencies = [ "libc", "log", "rustversion", - "windows-link 0.2.1", - "windows-result 0.4.1", + "windows-link", + "windows-result", ] [[package]] @@ -5382,24 +5437,36 @@ dependencies = [ ] [[package]] -name = "gimli" -version = "0.32.3" +name = "getrandom" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] [[package]] -name = "git2" -version = "0.20.3" +name = "getset" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2b37e2f62729cdada11f0e6b3b6fe383c69c29fc619e391223e12856af308c" +checksum = "9cf0fc11e47561d47397154977bc219f4cf809b2974facc3ccb3b89e2436f912" dependencies = [ - "bitflags 2.10.0", - "libc", - "libgit2-sys", - "log", - "url", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", ] +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + [[package]] name = "glob" version = "0.3.3" @@ -5419,16 +5486,6 @@ dependencies = [ "regex-syntax", ] -[[package]] -name = "gmp-mpfr-sys" -version = "1.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60f8970a75c006bb2f8ae79c6768a116dd215fa8346a87aed99bf9d82ca43394" -dependencies = [ - "libc", - "windows-sys 0.60.2", -] - [[package]] name = "group" version = "0.13.0" @@ -5483,7 +5540,7 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -5505,6 +5562,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", + "foldhash 0.1.5", ] [[package]] @@ -5515,7 +5573,7 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.2.0", "serde", "serde_core", ] @@ -5707,7 +5765,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots 1.0.5", + "webpki-roots 1.0.6", ] [[package]] @@ -5725,14 +5783,13 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", - "futures-core", "futures-util", "http 1.4.0", "http-body 1.0.1", @@ -5741,7 +5798,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.1", + "socket2 0.6.2", "system-configuration", "tokio", "tower-service", @@ -5751,9 +5808,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", @@ -5761,7 +5818,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core", ] [[package]] @@ -5862,6 +5919,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" @@ -5932,7 +5995,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5973,9 +6036,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.18.3" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9375e112e4b463ec1b1c6c011953545c65a30164fbab5b581df32b3abf0dcb88" +checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb" dependencies = [ "console 0.16.2", "portable-atomic", @@ -5995,16 +6058,16 @@ dependencies = [ [[package]] name = "inferno" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d35223c50fdd26419a4ccea2c73be68bd2b29a3d7d6123ffe101c17f4c20a52a" +checksum = "20dd69640582458beceefcf045f8de34263d45194999c9a49fcd53e5b503d522" dependencies = [ "ahash", "itoa", "log", "num-format", "once_cell", - "quick-xml 0.38.4", + "quick-xml 0.39.2", "rgb", "str_stack", ] @@ -6015,7 +6078,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "inotify-sys", "libc", ] @@ -6048,14 +6111,14 @@ dependencies = [ "indoc", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "interprocess" -version = "2.2.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d941b405bd2322993887859a8ee6ac9134945a24ec5ec763a8a962fc64dfec2d" +checksum = "6be5e5c847dbdb44564bd85294740d031f4f8aeb3464e5375ef7141f7538db69" dependencies = [ "doctest-file", "futures-core", @@ -6165,9 +6228,9 @@ checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jiff" -version = "0.2.18" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67e8da4c49d6d9909fe03361f9b620f58898859f5c7aded68351e85e71ecf50" +checksum = "819b44bc7c87d9117eb522f14d46e918add69ff12713c475946b0a29363ed1c2" dependencies = [ "jiff-static", "log", @@ -6178,13 +6241,13 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.18" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78" +checksum = "470252db18ecc35fd766c0891b1e3ec6cbbcd62507e85276c01bf75d8e94d4a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -6221,9 +6284,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" dependencies = [ "once_cell", "wasm-bindgen", @@ -6266,7 +6329,7 @@ dependencies = [ "elliptic-curve", "once_cell", "serdect", - "sha2", + "sha2 0.10.9", "signature", ] @@ -6278,23 +6341,23 @@ checksum = "8fe90c1150662e858c7d5f945089b7517b0a80d8bf7ba4b1b5ffc984e7230a5b" dependencies = [ "hashbrown 0.16.1", "portable-atomic", - "thiserror 2.0.17", + "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", @@ -6357,6 +6420,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[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" @@ -6365,27 +6434,15 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.180" +version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" - -[[package]] -name = "libgit2-sys" -version = "0.18.3+1.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487" -dependencies = [ - "cc", - "libc", - "libz-sys", - "pkg-config", -] +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "libm" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libmimalloc-sys" @@ -6399,11 +6456,10 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" dependencies = [ - "bitflags 2.10.0", "libc", ] @@ -6419,32 +6475,20 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "libz-sys" -version = "1.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "line-clipping" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f4de44e98ddbf09375cbf4d17714d18f39195f4f4894e8524501726fd9a8a4a" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" @@ -6518,7 +6562,7 @@ checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -6606,7 +6650,7 @@ dependencies = [ "regex", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "tracing", ] @@ -6661,9 +6705,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memoffset" @@ -6713,7 +6757,7 @@ checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -6792,7 +6836,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -6834,7 +6878,7 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c012d14ef788ab066a347d19e3dda699916c92293b05b85ba2c76b8c82d2830" dependencies = [ - "uuid 1.19.0", + "uuid 1.21.0", ] [[package]] @@ -6861,11 +6905,11 @@ dependencies = [ [[package]] name = "nix" -version = "0.29.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cfg-if", "cfg_aliases", "libc", @@ -6873,11 +6917,11 @@ dependencies = [ [[package]] name = "nix" -version = "0.30.1" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cfg-if", "cfg_aliases", "libc", @@ -6920,7 +6964,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "fsevent-sys", "inotify", "kqueue", @@ -6934,9 +6978,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.11.0", +] [[package]] name = "nu-ansi-term" @@ -6983,9 +7030,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" @@ -7082,7 +7129,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -7096,9 +7143,9 @@ dependencies = [ [[package]] name = "nybbles" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5676b5c379cf5b03da1df2b3061c4a4e2aa691086a56ac923e08c143f53f59" +checksum = "0d49ff0c0d00d4a502b39df9af3a525e1efeb14b9dabb5bb83335284c1309210" dependencies = [ "alloy-rlp", "cfg-if", @@ -7110,9 +7157,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" dependencies = [ "objc2-encode", ] @@ -7129,7 +7176,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "objc2", ] @@ -7198,7 +7245,7 @@ dependencies = [ "alloy-serde", "derive_more", "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -7254,7 +7301,7 @@ dependencies = [ "op-alloy-consensus", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -7274,38 +7321,44 @@ dependencies = [ "ethereum_ssz_derive", "op-alloy-consensus", "serde", - "sha2", + "sha2 0.10.9", "snap", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "op-revm" -version = "14.1.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1475a779c73999fc803778524042319691b31f3d6699d2b560c4ed8be1db802a" +checksum = "79c92b75162c2ed1661849fa51683b11254a5b661798360a2c24be918edafd40" dependencies = [ "auto_impl", "revm", "serde", ] +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "opener" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb9024962ab91e00c89d2a14352a8d0fc1a64346bf96f1839b45c09149564e47" +checksum = "a2fa337e0cf13357c13ef1dc108df1333eb192f75fc170bea03fcf1fd404c2ee" dependencies = [ "bstr", "normpath", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "openssl-probe" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "option-ext" @@ -7321,9 +7374,9 @@ checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" [[package]] name = "owo-colors" -version = "4.2.3" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" +checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" [[package]] name = "p256" @@ -7335,7 +7388,7 @@ dependencies = [ "elliptic-curve", "primeorder", "serdect", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -7363,7 +7416,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -7392,7 +7445,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -7453,9 +7506,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.5" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" dependencies = [ "memchr", "ucd-trie", @@ -7463,9 +7516,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.5" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" dependencies = [ "pest", "pest_generator", @@ -7473,25 +7526,25 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.5" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "pest_meta" -version = "2.8.5" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" dependencies = [ "pest", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -7585,7 +7638,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -7598,7 +7651,7 @@ dependencies = [ "phf_shared 0.13.1", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -7630,11 +7683,11 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.10" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" dependencies = [ - "pin-project-internal 1.1.10", + "pin-project-internal 1.1.11", ] [[package]] @@ -7650,20 +7703,20 @@ dependencies = [ [[package]] name = "pin-project-internal" -version = "1.1.10" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pin-utils" @@ -7689,15 +7742,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", ] @@ -7734,9 +7787,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "predicates" -version = "3.1.3" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe" dependencies = [ "anstyle", "predicates-core", @@ -7744,20 +7797,30 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" +checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144" [[package]] name = "predicates-tree" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2" dependencies = [ "predicates-core", "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" @@ -7774,7 +7837,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -7826,27 +7889,27 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "proc-macro2" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "process-wrap" -version = "8.2.1" +version = "9.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ef4f2f0422f23a82ec9f628ea2acd12871c81a9362b02c43c1aa86acfc3ba1" +checksum = "ccd9713fe2c91c3c85ac388b31b89de339365d2c995146e630b5e0da9d06526a" dependencies = [ "futures", "indexmap 2.13.0", - "nix 0.30.1", + "nix 0.31.2", "tokio", "tracing", "windows", @@ -7854,13 +7917,13 @@ dependencies = [ [[package]] name = "proptest" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" +checksum = "37566cb3fdacef14c0737f9546df7cfeadbfbc9fef10991038bf5015d0c80532" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.10.0", + "bitflags 2.11.0", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", @@ -7873,13 +7936,13 @@ dependencies = [ [[package]] name = "proptest-derive" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "095a99f75c69734802359b682be8daaf8980296731f6470434ea2c652af1dd30" +checksum = "fb6dc647500e84a25a85b100e76c85b8ace114c209432dc174f20aac11d4ed6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -7922,7 +7985,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -7935,7 +7998,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -7948,7 +8011,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -8000,11 +8063,11 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0" +checksum = "83c41efbf8f90ac44de7f3a868f0867851d261b56291732d0cbf7cceaaeb55a6" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "memchr", "pulldown-cmark-escape", "unicase", @@ -8033,24 +8096,24 @@ dependencies = [ "newtype-uuid", "quick-xml 0.38.4", "strip-ansi-escapes", - "thiserror 2.0.17", - "uuid 1.19.0", + "thiserror 2.0.18", + "uuid 1.21.0", ] [[package]] name = "quick-xml" -version = "0.18.1" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc440ee4802a86e357165021e3e255a9143724da31db1e2ea540214c96a0f82" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" dependencies = [ "memchr", ] [[package]] name = "quick-xml" -version = "0.38.4" +version = "0.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" dependencies = [ "memchr", ] @@ -8068,8 +8131,8 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.1", - "thiserror 2.0.17", + "socket2 0.6.2", + "thiserror 2.0.18", "tokio", "tracing", "web-time", @@ -8081,6 +8144,7 @@ version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ + "aws-lc-rs", "bytes", "getrandom 0.3.4", "lru-slab", @@ -8090,7 +8154,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.17", + "thiserror 2.0.18", "tinyvec", "tracing", "web-time", @@ -8105,16 +8169,16 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.1", + "socket2 0.6.2", "tracing", "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -8214,9 +8278,9 @@ dependencies = [ [[package]] name = "rapidhash" -version = "4.2.1" +version = "4.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8b5b858a440a0bc02625b62dd95131b9201aa9f69f411195dd4a7cfb1de3d7" +checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" dependencies = [ "rand 0.9.2", "rustversion", @@ -8240,7 +8304,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "compact_str", "hashbrown 0.16.1", "indoc", @@ -8248,7 +8312,7 @@ dependencies = [ "kasuari", "lru", "strum", - "thiserror 2.0.17", + "thiserror 2.0.18", "unicode-segmentation", "unicode-truncate", "unicode-width 0.2.2", @@ -8272,7 +8336,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "hashbrown 0.16.1", "indoc", "instability", @@ -8317,7 +8381,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] @@ -8328,7 +8392,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -8348,14 +8412,14 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -8365,9 +8429,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -8376,15 +8440,15 @@ dependencies = [ [[package]] name = "regex-lite" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da" +checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "regress" @@ -8405,7 +8469,6 @@ dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", - "futures-channel", "futures-core", "futures-util", "h2", @@ -8440,13 +8503,52 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.5", + "webpki-roots 1.0.6", +] + +[[package]] +name = "reqwest" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] name = "reth-codecs" -version = "1.9.3" -source = "git+https://github.com/paradigmxyz/reth?rev=64909d3#64909d33e6b7ab60774e37f5508fb5ad17f41897" +version = "1.10.0" +source = "git+https://github.com/paradigmxyz/reth?rev=b25f32a977b489f9b84254c7811a2a5a25a81369#b25f32a977b489f9b84254c7811a2a5a25a81369" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8463,18 +8565,18 @@ dependencies = [ [[package]] name = "reth-codecs-derive" -version = "1.9.3" -source = "git+https://github.com/paradigmxyz/reth?rev=64909d3#64909d33e6b7ab60774e37f5508fb5ad17f41897" +version = "1.10.0" +source = "git+https://github.com/paradigmxyz/reth?rev=b25f32a977b489f9b84254c7811a2a5a25a81369#b25f32a977b489f9b84254c7811a2a5a25a81369" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "reth-ethereum-primitives" -version = "1.9.3" -source = "git+https://github.com/paradigmxyz/reth?rev=64909d3#64909d33e6b7ab60774e37f5508fb5ad17f41897" +version = "1.10.0" +source = "git+https://github.com/paradigmxyz/reth?rev=b25f32a977b489f9b84254c7811a2a5a25a81369#b25f32a977b489f9b84254c7811a2a5a25a81369" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8489,8 +8591,8 @@ dependencies = [ [[package]] name = "reth-primitives-traits" -version = "1.9.3" -source = "git+https://github.com/paradigmxyz/reth?rev=64909d3#64909d33e6b7ab60774e37f5508fb5ad17f41897" +version = "1.10.0" +source = "git+https://github.com/paradigmxyz/reth?rev=b25f32a977b489f9b84254c7811a2a5a25a81369#b25f32a977b489f9b84254c7811a2a5a25a81369" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8505,29 +8607,29 @@ dependencies = [ "once_cell", "op-alloy-consensus", "reth-codecs", - "revm-bytecode", - "revm-primitives", - "revm-state", + "revm-bytecode 7.1.1", + "revm-primitives 21.0.2", + "revm-state 8.1.1", "secp256k1 0.30.0", "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "reth-zstd-compressors" -version = "1.9.3" -source = "git+https://github.com/paradigmxyz/reth?rev=64909d3#64909d33e6b7ab60774e37f5508fb5ad17f41897" +version = "1.10.0" +source = "git+https://github.com/paradigmxyz/reth?rev=b25f32a977b489f9b84254c7811a2a5a25a81369#b25f32a977b489f9b84254c7811a2a5a25a81369" dependencies = [ "zstd", ] [[package]] name = "revm" -version = "33.1.0" +version = "34.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c85ed0028f043f87b3c88d4a4cb6f0a76440085523b6a8afe5ff003cf418054" +checksum = "c2aabdebaa535b3575231a88d72b642897ae8106cf6b0d12eafc6bfdf50abfc7" dependencies = [ - "revm-bytecode", + "revm-bytecode 8.0.0", "revm-context", "revm-context-interface", "revm-database", @@ -8536,8 +8638,8 @@ dependencies = [ "revm-inspector", "revm-interpreter", "revm-precompile", - "revm-primitives", - "revm-state", + "revm-primitives 22.0.0", + "revm-state 9.0.0", ] [[package]] @@ -8548,94 +8650,107 @@ checksum = "e2c6b5e6e8dd1e28a4a60e5f46615d4ef0809111c9e63208e55b5c7058200fb0" dependencies = [ "bitvec", "phf 0.13.1", - "revm-primitives", + "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.0.0", "serde", ] [[package]] name = "revm-context" -version = "12.1.0" +version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f038f0c9c723393ac897a5df9140b21cfa98f5753a2cb7d0f28fa430c4118abf" +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.0.0", + "revm-state 9.0.0", "serde", ] [[package]] name = "revm-context-interface" -version = "13.1.0" +version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431c9a14e4ef1be41ae503708fd02d974f80ef1f2b6b23b5e402e8d854d1b225" +checksum = "57f61cc6d23678c4840af895b19f8acfbbd546142ec8028b6526c53cc1c16c98" dependencies = [ "alloy-eip2930", "alloy-eip7702", "auto_impl", "either", "revm-database-interface", - "revm-primitives", - "revm-state", + "revm-primitives 22.0.0", + "revm-state 9.0.0", "serde", ] [[package]] name = "revm-database" -version = "9.0.6" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "980d8d6bba78c5dd35b83abbb6585b0b902eb25ea4448ed7bfba6283b0337191" +checksum = "529528d0b05fe646be86223032c3e77aa8b05caa2a35447d538c55965956a511" dependencies = [ "alloy-eips", - "revm-bytecode", + "revm-bytecode 8.0.0", "revm-database-interface", - "revm-primitives", - "revm-state", + "revm-primitives 22.0.0", + "revm-state 9.0.0", "serde", ] [[package]] name = "revm-database-interface" -version = "8.0.5" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cce03e3780287b07abe58faf4a7f5d8be7e81321f93ccf3343c8f7755602bae" +checksum = "b7bf93ac5b91347c057610c0d96e923db8c62807e03f036762d03e981feddc1d" dependencies = [ "auto_impl", "either", - "revm-primitives", - "revm-state", + "revm-primitives 22.0.0", + "revm-state 9.0.0", "serde", + "thiserror 2.0.18", ] [[package]] name = "revm-handler" -version = "14.1.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d44f8f6dbeec3fecf9fe55f78ef0a758bdd92ea46cd4f1ca6e2a946b32c367f3" +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.0.0", + "revm-state 9.0.0", "serde", ] [[package]] name = "revm-inspector" -version = "14.1.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5617e49216ce1ca6c8826bcead0386bc84f49359ef67cde6d189961735659f93" +checksum = "4f3ccad59db91ef93696536a0dbaf2f6f17cfe20d4d8843ae118edb7e97947ef" dependencies = [ "auto_impl", "either", @@ -8643,17 +8758,17 @@ dependencies = [ "revm-database-interface", "revm-handler", "revm-interpreter", - "revm-primitives", - "revm-state", + "revm-primitives 22.0.0", + "revm-state 9.0.0", "serde", "serde_json", ] [[package]] name = "revm-inspectors" -version = "0.33.2" +version = "0.34.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01def7351cd9af844150b8e88980bcd11304f33ce23c3d7c25f2a8dab87c1345" +checksum = "6e435414e9de50a1b930da602067c76365fea2fea11e80ceb50783c94ddd127f" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -8666,27 +8781,27 @@ dependencies = [ "revm", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "revm-interpreter" -version = "31.1.0" +version = "32.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26ec36405f7477b9dccdc6caa3be19adf5662a7a0dffa6270cdb13a090c077e5" +checksum = "11406408597bc249392d39295831c4b641b3a6f5c471a7c41104a7a1e3564c07" dependencies = [ - "revm-bytecode", + "revm-bytecode 8.0.0", "revm-context-interface", - "revm-primitives", - "revm-state", + "revm-primitives 22.0.0", + "revm-state 9.0.0", "serde", ] [[package]] name = "revm-precompile" -version = "31.0.0" +version = "32.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a62958af953cc4043e93b5be9b8497df84cc3bd612b865c49a7a7dfa26a84e2" +checksum = "50c1285c848d240678bf69cb0f6179ff5a4aee6fc8e921d89708087197a0aff3" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -8700,11 +8815,10 @@ dependencies = [ "cfg-if", "k256", "p256", - "revm-primitives", + "revm-primitives 22.0.0", "ripemd", - "rug", "secp256k1 0.31.1", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -8719,15 +8833,40 @@ dependencies = [ "serde", ] +[[package]] +name = "revm-primitives" +version = "22.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba580c56a8ec824a64f8a1683577876c2e1dbe5247044199e9b881421ad5dcf9" +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.10.0", - "revm-bytecode", - "revm-primitives", + "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.0.0", "serde", ] @@ -8740,7 +8879,7 @@ dependencies = [ "nix 0.30.1", "regex", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -8755,9 +8894,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.52" +version = "0.8.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" dependencies = [ "bytemuck", ] @@ -8816,18 +8955,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rug" -version = "1.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ad2e973fe3c3214251a840a621812a4f40468da814b1a3d6947d433c2af11f" -dependencies = [ - "az", - "gmp-mpfr-sys", - "libc", - "libm", -] - [[package]] name = "ruint" version = "1.17.2" @@ -8935,11 +9062,11 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys", @@ -8948,9 +9075,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.36" +version = "0.23.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ "aws-lc-rs", "log", @@ -8984,6 +9111,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.103.9" @@ -9020,7 +9174,7 @@ version = "17.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e902948a25149d50edc1a8e0141aad50f54e22ba83ff988cf8f7c9ef07f50564" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cfg-if", "clipboard-win", "fd-lock", @@ -9038,9 +9192,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "ryu-js" @@ -9098,9 +9252,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" dependencies = [ "dyn-clone", "ref-cast", @@ -9111,14 +9265,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4908ad288c5035a8eb12cfdf0d49270def0a268ee162b75eeee0f85d155a7c45" +checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -9142,7 +9296,7 @@ dependencies = [ "hmac", "pbkdf2 0.11.0", "salsa20", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -9216,11 +9370,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.5.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -9229,9 +9383,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -9313,7 +9467,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -9324,7 +9478,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -9384,9 +9538,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.16.1" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" +checksum = "381b283ce7bc6b476d903296fb59d0d36633652b633b27f64db4fb46dcbfc3b9" dependencies = [ "base64 0.22.1", "chrono", @@ -9394,7 +9548,7 @@ dependencies = [ "indexmap 1.9.3", "indexmap 2.13.0", "schemars 0.9.0", - "schemars 1.2.0", + "schemars 1.2.1", "serde_core", "serde_json", "serde_with_macros", @@ -9403,14 +9557,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.16.1" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +checksum = "a6d4e30573c8cb306ed6ab1dca8423eec9a463ea0e155f45399455e0368b27e0" dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -9434,6 +9588,19 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha2" version = "0.10.9" @@ -9457,9 +9624,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", @@ -9555,27 +9722,27 @@ dependencies = [ [[package]] name = "simple_asn1" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d" dependencies = [ "num-bigint", "num-traits", - "thiserror 2.0.17", + "thiserror 2.0.18", "time", ] [[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" @@ -9645,9 +9812,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" dependencies = [ "libc", "windows-sys 0.60.2", @@ -9656,7 +9823,7 @@ dependencies = [ [[package]] name = "solar-ast" version = "0.1.8" -source = "git+https://github.com/paradigmxyz/solar.git?rev=1f28069#1f2806951b5c6a166edd975ebd797a3ebd5ff9f1" +source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ "alloy-primitives", "bumpalo", @@ -9672,7 +9839,7 @@ dependencies = [ [[package]] name = "solar-compiler" version = "0.1.8" -source = "git+https://github.com/paradigmxyz/solar.git?rev=1f28069#1f2806951b5c6a166edd975ebd797a3ebd5ff9f1" +source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ "alloy-primitives", "solar-ast", @@ -9687,7 +9854,7 @@ dependencies = [ [[package]] name = "solar-config" version = "0.1.8" -source = "git+https://github.com/paradigmxyz/solar.git?rev=1f28069#1f2806951b5c6a166edd975ebd797a3ebd5ff9f1" +source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ "colorchoice", "strum", @@ -9696,7 +9863,7 @@ dependencies = [ [[package]] name = "solar-data-structures" version = "0.1.8" -source = "git+https://github.com/paradigmxyz/solar.git?rev=1f28069#1f2806951b5c6a166edd975ebd797a3ebd5ff9f1" +source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ "bumpalo", "index_vec", @@ -9710,9 +9877,9 @@ dependencies = [ [[package]] name = "solar-interface" version = "0.1.8" -source = "git+https://github.com/paradigmxyz/solar.git?rev=1f28069#1f2806951b5c6a166edd975ebd797a3ebd5ff9f1" +source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ - "annotate-snippets 0.12.10", + "annotate-snippets 0.12.12", "anstream", "anstyle", "derive_more", @@ -9730,7 +9897,7 @@ dependencies = [ "solar-config", "solar-data-structures", "solar-macros", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "unicode-width 0.2.2", ] @@ -9738,20 +9905,20 @@ dependencies = [ [[package]] name = "solar-macros" version = "0.1.8" -source = "git+https://github.com/paradigmxyz/solar.git?rev=1f28069#1f2806951b5c6a166edd975ebd797a3ebd5ff9f1" +source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "solar-parse" version = "0.1.8" -source = "git+https://github.com/paradigmxyz/solar.git?rev=1f28069#1f2806951b5c6a166edd975ebd797a3ebd5ff9f1" +source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ "alloy-primitives", - "bitflags 2.10.0", + "bitflags 2.11.0", "bumpalo", "itertools 0.14.0", "memchr", @@ -9769,11 +9936,11 @@ dependencies = [ [[package]] name = "solar-sema" version = "0.1.8" -source = "git+https://github.com/paradigmxyz/solar.git?rev=1f28069#1f2806951b5c6a166edd975ebd797a3ebd5ff9f1" +source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ "alloy-json-abi", "alloy-primitives", - "bitflags 2.10.0", + "bitflags 2.11.0", "bumpalo", "derive_more", "either", @@ -9828,16 +9995,16 @@ dependencies = [ "path-slash", "rayon", "regex", - "reqwest", + "reqwest 0.12.28", "sanitize-filename", "semver 1.0.27", "serde", "serde_json", - "sha2", - "thiserror 2.0.17", + "sha2 0.10.9", + "thiserror 2.0.18", "tokio", "toml_edit 0.23.10+spec-1.0.0", - "uuid 1.19.0", + "uuid 1.21.0", "zip", "zip-extract", ] @@ -9950,7 +10117,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -9959,17 +10126,23 @@ 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.16.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502b8906c4736190684646827fbab1e954357dfe541013bbd7994d033d53a1ca" +checksum = "c1aaf178a50bbdd86043fce9bf0a5867007d9b382db89d1c96ccae4601ff1ff9" [[package]] name = "sval_buffer" -version = "2.16.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4b854348b15b6c441bdd27ce9053569b016a0723eab2d015b1fd8e6abe4f708" +checksum = "f89273e48f03807ebf51c4d81c52f28d35ffa18a593edf97e041b52de143df89" dependencies = [ "sval", "sval_ref", @@ -9977,18 +10150,18 @@ dependencies = [ [[package]] name = "sval_dynamic" -version = "2.16.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0bd9e8b74410ddad37c6962587c5f9801a2caadba9e11f3f916ee3f31ae4a1f" +checksum = "0430f4e18e7eba21a49d10d25a8dec3ce0e044af40b162347e99a8e3c3ced864" dependencies = [ "sval", ] [[package]] name = "sval_fmt" -version = "2.16.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe17b8deb33a9441280b4266c2d257e166bafbaea6e66b4b34ca139c91766d9" +checksum = "835f51b9d7331b9d7fc48fc716c02306fa88c4a076b1573531910c91a525882d" dependencies = [ "itoa", "ryu", @@ -9997,9 +10170,9 @@ dependencies = [ [[package]] name = "sval_json" -version = "2.16.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854addb048a5bafb1f496c98e0ab5b9b581c3843f03ca07c034ae110d3b7c623" +checksum = "13cbfe3ef406ee2366e7e8ab3678426362085fa9eaedf28cb878a967159dced3" dependencies = [ "itoa", "ryu", @@ -10008,9 +10181,9 @@ dependencies = [ [[package]] name = "sval_nested" -version = "2.16.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cf068f482108ff44ae8013477cb047a1665d5f1a635ad7cf79582c1845dce9" +checksum = "8b20358af4af787c34321a86618c3cae12eabdd0e9df22cd9dd2c6834214c518" dependencies = [ "sval", "sval_buffer", @@ -10019,18 +10192,18 @@ dependencies = [ [[package]] name = "sval_ref" -version = "2.16.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed02126365ffe5ab8faa0abd9be54fbe68d03d607cd623725b0a71541f8aaa6f" +checksum = "fb5e500f8eb2efa84f75e7090f7fc43f621b9f8b6cde571c635b3855f97b332a" dependencies = [ "sval", ] [[package]] name = "sval_serde" -version = "2.16.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a263383c6aa2076c4ef6011d3bae1b356edf6ea2613e3d8e8ebaa7b57dd707d5" +checksum = "ca2032ae39b11dcc6c18d5fbc50a661ea191cac96484c59ccf49b002261ca2c1" dependencies = [ "serde_core", "sval", @@ -10039,28 +10212,28 @@ dependencies = [ [[package]] name = "svm-rs" -version = "0.5.23" +version = "0.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "415b159b54c22d9810087f0991371fd6242a912673e982a7c4ca8ea122f7e00a" +checksum = "230df06b463c7251e4d1b39b1b3e6f25a9b3a42630179053a1e5f919e6e15534" dependencies = [ "const-hex", "dirs", - "reqwest", + "reqwest 0.13.2", "semver 1.0.27", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.18", "url", "zip", ] [[package]] name = "svm-rs-builds" -version = "0.5.23" +version = "0.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab96ac3275ad299c6e5455b69a2f72443c4d3afb4933d92a0f859d48432dea49" +checksum = "b271921143e5b12947a526de464db02b00363919d582a7ea712374840f928328" dependencies = [ "const-hex", "semver 1.0.27", @@ -10081,9 +10254,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.114" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -10092,14 +10265,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.5.2" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f92d01b5de07eaf324f7fca61cc6bd3d82bbc1de5b6c963e6fe79e86f36580d" +checksum = "53f425ae0b12e2f5ae65542e00898d500d4d318b4baf09f40fd0d410454e9947" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -10119,16 +10292,16 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "system-configuration" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -10157,21 +10330,53 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.24.0" +version = "3.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.1", "once_cell", "rustix", "windows-sys 0.61.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 = "0.7.5" -source = "git+https://github.com/tempoxyz/tempo?tag=v0.7.5#d1c2d656fb657e3c6f46a8bc3889bdb595d45576" +version = "1.0.0" +source = "git+https://github.com/tempoxyz/tempo?tag=v1.0.0#9ad04e2058993dfba4984cc7d413474b0278d427" dependencies = [ "alloy-consensus", "alloy-eips", @@ -10185,7 +10390,8 @@ dependencies = [ "reth-ethereum-primitives", "reth-primitives-traits", "serde", - "sha2", + "serde_json", + "sha2 0.10.9", ] [[package]] @@ -10264,11 +10470,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -10279,18 +10485,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -10333,9 +10539,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.45" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", @@ -10351,29 +10557,20 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.25" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" +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.8.2" @@ -10412,7 +10609,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.1", + "socket2 0.6.2", "tokio-macros", "windows-sys 0.61.2", ] @@ -10425,7 +10622,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -10493,9 +10690,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.11+spec-1.1.0" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ "indexmap 2.13.0", "serde_core", @@ -10532,9 +10729,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.24.0+spec-1.1.0" +version = "0.24.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c740b185920170a6d9191122cafef7010bd6270a3824594bff6784c04d7f09e" +checksum = "01f2eadbbc6b377a847be05f60791ef1058d9f696ecb51d2c07fe911d8569d8e" dependencies = [ "indexmap 2.13.0", "toml_datetime", @@ -10545,9 +10742,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.6+spec-1.1.0" +version = "1.0.9+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" dependencies = [ "winnow", ] @@ -10576,7 +10773,7 @@ dependencies = [ "hyper-timeout", "hyper-util", "percent-encoding", - "pin-project 1.1.10", + "pin-project 1.1.11", "prost 0.13.5", "rustls-native-certs", "socket2 0.5.10", @@ -10621,7 +10818,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "async-compression", - "bitflags 2.10.0", + "bitflags 2.11.0", "bytes", "futures-core", "futures-util", @@ -10687,7 +10884,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -10791,7 +10988,7 @@ dependencies = [ "hex", "protobuf", "rusb", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", ] @@ -10816,7 +11013,7 @@ dependencies = [ "rustls", "rustls-pki-types", "sha1", - "thiserror 2.0.17", + "thiserror 2.0.18", "utf-8", ] @@ -10833,7 +11030,7 @@ dependencies = [ "log", "rand 0.9.2", "sha1", - "thiserror 2.0.17", + "thiserror 2.0.18", "utf-8", ] @@ -10850,7 +11047,7 @@ dependencies = [ "rand_core 0.6.4", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -10862,11 +11059,11 @@ dependencies = [ "mime", "prost 0.12.6", "prost-types 0.12.6", - "reqwest", + "reqwest 0.12.28", "serde", "serde_json", "serde_with", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "turnkey_api_key_stamper", ] @@ -10898,8 +11095,8 @@ dependencies = [ "annotate-snippets 0.11.5", "anyhow", "bstr", - "cargo-platform 0.1.9", - "cargo_metadata 0.18.1", + "cargo-platform", + "cargo_metadata", "color-eyre", "colored", "comma", @@ -10956,9 +11153,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-joining-type" @@ -11038,6 +11235,7 @@ dependencies = [ "idna", "percent-encoding", "serde", + "serde_derive", ] [[package]] @@ -11088,11 +11286,11 @@ dependencies = [ [[package]] name = "uuid" -version = "1.19.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.1", "js-sys", "serde_core", "wasm-bindgen", @@ -11148,28 +11346,25 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vergen" -version = "9.1.0" +version = "10.0.0-beta.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b849a1f6d8639e8de261e81ee0fc881e3e3620db1af9f2e0da015d4382ceaf75" +checksum = "4065d92137ae477a3701e18a7afccdf584229fe504eaa4d290bc722df4076141" dependencies = [ "anyhow", - "cargo_metadata 0.23.1", - "derive_builder", - "regex", + "bon", "rustversion", "time", "vergen-lib", ] [[package]] -name = "vergen-git2" -version = "9.1.0" +name = "vergen-gitcl" +version = "10.0.0-beta.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51ab55ddf1188c8d679f349775362b0fa9e90bd7a4ac69838b2a087623f0d57" +checksum = "ba3911418c678932ff5b8f8162bfb73753b6f3d1e22bf37d07d53aff44a6f224" dependencies = [ "anyhow", - "derive_builder", - "git2", + "bon", "rustversion", "time", "vergen", @@ -11178,12 +11373,13 @@ dependencies = [ [[package]] name = "vergen-lib" -version = "9.1.0" +version = "10.0.0-beta.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34a29ba7e9c59e62f229ae1932fb1b8fb8a6fdcc99215a641913f5f5a59a569" +checksum = "fbabf186269781b2b81f38937d07c37bbd980853dea6edc89b90820c09aab4c3" dependencies = [ "anyhow", - "derive_builder", + "bon", + "getset", "rustversion", ] @@ -11251,11 +11447,20 @@ 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.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ "cfg-if", "once_cell", @@ -11266,9 +11471,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.58" +version = "0.4.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" dependencies = [ "cfg-if", "futures-util", @@ -11280,9 +11485,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -11290,26 +11495,48 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" 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" @@ -11323,6 +11550,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.5", + "indexmap 2.13.0", + "semver 1.0.27", +] + [[package]] name = "wasmtimer" version = "0.4.3" @@ -11339,29 +11578,31 @@ dependencies = [ [[package]] name = "watchexec" -version = "8.0.1" +version = "8.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc35794a21139060aca512393e9b1a225fe48fc11edee65c84d6d76b25a53331" +checksum = "1c948fe78603e339bea80c4da9d9c05f72e9ec2fe0933be33034ad0b719b6851" dependencies = [ "async-priority-channel", "atomic-take", "futures", + "libc", "miette", "normalize-path", "notify", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", "watchexec-events", "watchexec-signals", "watchexec-supervisor", + "windows-sys 0.61.2", ] [[package]] name = "watchexec-events" -version = "6.0.0" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c4a8973a20c7d30198a12272519163168a9ba8b687693ec9d1f027b75b860d1" +checksum = "ad87c046fa1050d22100e7d234db2cbf6ffd020b0ae2deff4bef6faa8f71ac44" dependencies = [ "notify-types", "watchexec-signals", @@ -11369,20 +11610,20 @@ dependencies = [ [[package]] name = "watchexec-signals" -version = "5.0.0" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "377729679262964c27e6a28f360a84b7aedb172b59841301c1c77922305dfd83" +checksum = "3fd4537617a323437550d34c73a6aeeb1b489bbcc526e63f044ca3e59347101f" dependencies = [ "miette", "nix 0.30.1", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "watchexec-supervisor" -version = "5.0.1" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92a45c50ea6b2795f3d070ad621618c8737bb98f6bc2eb4847e8e8e2ce2f446c" +checksum = "ddb766a4282cd9e71a6011e800ed7f863bb7134435bf4c7abc6b55ca3afc78ec" dependencies = [ "futures", "process-wrap", @@ -11394,9 +11635,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.85" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" dependencies = [ "js-sys", "wasm-bindgen", @@ -11414,9 +11655,9 @@ dependencies = [ [[package]] name = "web_atoms" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e588f10c7bc3465f5fc1ab087fc97877ec1064a7ec89fb685ac4ee998dac4a" +checksum = "57a9779e9f04d2ac1ce317aee707aa2f6b773afba7b931222bff6983843b1576" dependencies = [ "phf 0.13.1", "phf_codegen 0.13.1", @@ -11426,9 +11667,9 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.0.6" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f1243ef785213e3a32fa0396093424a3a6ea566f9948497e5a2309261a4c97" +checksum = "3f00bb839c1cf1e3036066614cbdcd035ecf215206691ea646aa3c60a24f68f2" dependencies = [ "core-foundation 0.10.1", "jni", @@ -11440,20 +11681,29 @@ dependencies = [ "web-sys", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webpki-roots" version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.5", + "webpki-roots 1.0.6", ] [[package]] name = "webpki-roots" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" dependencies = [ "rustls-pki-types", ] @@ -11508,37 +11758,23 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.61.3" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ "windows-collections", - "windows-core 0.61.2", + "windows-core", "windows-future", - "windows-link 0.1.3", "windows-numerics", ] [[package]] name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core 0.61.2", -] - -[[package]] -name = "windows-core" -version = "0.61.2" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-implement", - "windows-interface", - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", + "windows-core", ] [[package]] @@ -11549,19 +11785,19 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] name = "windows-future" -version = "0.2.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", + "windows-core", + "windows-link", "windows-threading", ] @@ -11573,7 +11809,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -11584,15 +11820,9 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - [[package]] name = "windows-link" version = "0.2.1" @@ -11601,12 +11831,12 @@ checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-numerics" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", + "windows-core", + "windows-link", ] [[package]] @@ -11615,18 +11845,9 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link 0.1.3", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] @@ -11635,16 +11856,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -11653,7 +11865,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -11698,7 +11910,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -11738,7 +11950,7 @@ version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.2.1", + "windows-link", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", @@ -11751,11 +11963,11 @@ dependencies = [ [[package]] name = "windows-threading" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -11916,6 +12128,88 @@ 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-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.27", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "write16" @@ -11942,7 +12236,7 @@ dependencies = [ "pharos", "rustc_version 0.4.1", "send_wrapper", - "thiserror 2.0.17", + "thiserror 2.0.18", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -12003,28 +12297,28 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.33" +version = "0.8.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.33" +version = "0.8.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -12044,7 +12338,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "synstructure", ] @@ -12065,7 +12359,7 @@ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -12099,7 +12393,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -12123,21 +12417,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fa5b9958fd0b5b685af54f2c3fa21fca05fe295ebaf3e77b6d24d96c4174037" dependencies = [ "log", - "thiserror 2.0.17", + "thiserror 2.0.18", "zip", ] [[package]] name = "zlib-rs" -version = "0.5.5" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40990edd51aae2c2b6907af74ffb635029d5788228222c4bb811e9351c0caad3" +checksum = "c745c48e1007337ed136dc99df34128b9faa6ed542d80a1c673cf55a6d7236c8" [[package]] name = "zmij" -version = "1.0.14" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zopfli" diff --git a/Cargo.toml b/Cargo.toml index 3d6e79125e72b..506e88164bc46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,11 +26,12 @@ members = [ "crates/primitives/", "crates/script-sequence/", "crates/test-utils/", + "crates/cli-markdown/", ] resolver = "2" [workspace.package] -version = "1.5.1" +version = "1.6.0" edition = "2024" rust-version = "1.89" authors = ["Foundry Contributors"] @@ -149,7 +150,6 @@ ruint.opt-level = 3 sha2.opt-level = 3 sha3.opt-level = 3 -tiny-keccak.opt-level = 3 keccak.opt-level = 3 # Fuzzing. @@ -255,6 +255,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" } @@ -274,11 +275,11 @@ foundry-primitives = { path = "crates/primitives" } # solc & compilation utilities foundry-block-explorers = { version = "0.22.0", default-features = false } -foundry-compilers = { version = "0.19.10", default-features = false, features = [ +foundry-compilers = { version = "0.19.14", default-features = false, features = [ "rustls", "svm-solc", ] } -foundry-fork-db = "0.21" +foundry-fork-db = "0.23" 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 = [ @@ -338,13 +339,13 @@ op-alloy-rpc-types = "0.23.1" op-alloy-flz = "0.13.1" ## alloy-evm -alloy-evm = "0.25.2" -alloy-op-evm = "0.25.2" +alloy-evm = "0.26.3" +alloy-op-evm = "0.26.3" # revm -revm = { version = "33.1.0", default-features = false } -revm-inspectors = { version = "0.33.2", features = ["serde"] } -op-revm = { version = "14.1.0", default-features = false } +revm = { version = "34.0.0", default-features = false } +revm-inspectors = { version = "0.34.2", features = ["serde"] } +op-revm = { version = "15.0.0", default-features = false } ## cli anstream = "0.6" @@ -423,8 +424,10 @@ tower-http = "0.6" tracing = "0.1" tracing-subscriber = "0.3" url = "2" -vergen = { version = "9", default-features = false, features = ["build", "cargo"] } -vergen-git2 = "9" +vergen = { package = "vergen-gitcl", version = "10.0.0-beta.5", default-features = false, features = [ + "build", + "cargo", +] } yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } path-slash = "0.2" jiff = { version = "0.2", default-features = false, features = [ @@ -437,7 +440,8 @@ flate2 = "1.1" ethereum_ssz = "0.10" # Tempo -tempo-primitives = { git = "https://github.com/tempoxyz/tempo", tag = "v0.7.5", default-features = false, features = ["serde"] } +tempo-primitives = { git = "https://github.com/tempoxyz/tempo", tag = "v1.0.0", default-features = false, features = ["serde"] } +tempo-alloy = { git = "https://github.com/tempoxyz/tempo", tag = "v1.0.0", default-features = false } ## Pinned dependencies. Enabled for the workspace in crates/test-utils. @@ -492,13 +496,13 @@ rexpect = { git = "https://github.com/rust-cli/rexpect", rev = "2ed0b1898d7edaf6 # 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 = "237d0a0" } -# alloy-op-evm = { git = "https://github.com/alloy-rs/evm.git", rev = "237d0a0" } +# alloy-evm = { git = "https://github.com/alloy-rs/evm.git", branch = "staging-revm" } +# alloy-op-evm = { git = "https://github.com/alloy-rs/evm.git", branch = "staging-revm" } ## revm # revm = { git = "https://github.com/bluealloy/revm.git", rev = "7e59936" } # op-revm = { git = "https://github.com/bluealloy/revm.git", rev = "7e59936" } -# revm-inspectors = { git = "https://github.com/zerosnacks/revm-inspectors.git", rev = "0aaab71" } +# revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors.git", branch = "staging-revm" } ## foundry # foundry-block-explorers = { git = "https://github.com/foundry-rs/block-explorers.git", rev = "f5b46b2" } @@ -506,7 +510,7 @@ rexpect = { git = "https://github.com/rust-cli/rexpect", rev = "2ed0b1898d7edaf6 # foundry-fork-db = { git = "https://github.com/foundry-rs/foundry-fork-db", rev = "b4299fc" } # solar -solar = { package = "solar-compiler", git = "https://github.com/paradigmxyz/solar.git", rev = "1f28069" } -solar-interface = { package = "solar-interface", git = "https://github.com/paradigmxyz/solar.git", rev = "1f28069" } -solar-ast = { package = "solar-ast", git = "https://github.com/paradigmxyz/solar.git", rev = "1f28069" } -solar-sema = { package = "solar-sema", git = "https://github.com/paradigmxyz/solar.git", rev = "1f28069" } +solar = { package = "solar-compiler", git = "https://github.com/paradigmxyz/solar", rev = "530f129" } +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" } diff --git a/Dockerfile b/Dockerfile index 4aa660450d17b..5c402cce672f8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,21 +24,23 @@ ENV CARGO_INCREMENTAL=0 \ SCCACHE_DIR=/sccache # Build dependencies. -RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \ - --mount=type=cache,target=/usr/local/cargo/git,sharing=locked \ - --mount=type=cache,target=$SCCACHE_DIR,sharing=locked \ +RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=shared \ + --mount=type=cache,target=/usr/local/cargo/git,sharing=shared \ + --mount=type=cache,target=$SCCACHE_DIR,sharing=shared \ cargo chef cook --recipe-path recipe.json --profile ${RUST_PROFILE} --no-default-features --features "${RUST_FEATURES}" ARG TAG_NAME="dev" ENV TAG_NAME=$TAG_NAME ARG VERGEN_GIT_SHA="ffffffffffffffffffffffffffffffffffffffff" +ENV VERGEN_GIT_SHA=$VERGEN_GIT_SHA # Build the project. COPY . . -RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \ - --mount=type=cache,target=/usr/local/cargo/git,sharing=locked \ - --mount=type=cache,target=$SCCACHE_DIR,sharing=locked \ - cargo build --profile ${RUST_PROFILE} --no-default-features --features "${RUST_FEATURES}" +RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=shared \ + --mount=type=cache,target=/usr/local/cargo/git,sharing=shared \ + --mount=type=cache,target=$SCCACHE_DIR,sharing=shared \ + cargo build --profile ${RUST_PROFILE} --no-default-features --features "${RUST_FEATURES}" \ + && sccache --show-stats || true # `dev` profile outputs to the `target/debug` directory. RUN ln -s /app/target/debug /app/target/dev \ @@ -50,8 +52,6 @@ RUN ln -s /app/target/debug /app/target/dev \ /app/target/${RUST_PROFILE}/chisel \ /app/output/ -RUN sccache --show-stats || true - FROM ubuntu:22.04 AS runtime # Install runtime dependencies. diff --git a/Makefile b/Makefile index 40e21190764c6..fe768f19abf43 100644 --- a/Makefile +++ b/Makefile @@ -47,13 +47,13 @@ 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)/)' && \ + 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 report --doctests --open .PHONY: test-unit test-unit: ## Run unit tests. - cargo nextest run -E 'kind(test) & !test(/\b(issue|ext_integration)/)' + cargo nextest run -E 'kind(test) & !test(/\b(issue|ext_integration|flaky_)/)' .PHONY: test-doc test-doc: ## Run doc tests. @@ -61,8 +61,8 @@ test-doc: ## Run doc tests. .PHONY: test test: ## Run all tests. - make test-unit && \ - make test-doc + $(MAKE) test-unit && \ + $(MAKE) test-doc ##@ Linting @@ -79,6 +79,17 @@ lint-clippy: ## Run clippy on the codebase. --all-features \ -- -D warnings +.PHONY: lint-clippy-fix +lint-clippy-fix: ## Run clippy on the codebase and fix warnings. + cargo +nightly clippy \ + --workspace \ + --all-targets \ + --all-features \ + --fix \ + --allow-dirty \ + --allow-staged \ + -- -D warnings + .PHONY: lint-typos lint-typos: ## Run typos on the codebase. @command -v typos >/dev/null || { \ @@ -89,9 +100,9 @@ lint-typos: ## Run typos on the codebase. .PHONY: lint lint: ## Run all linters. - make fmt && \ - make lint-clippy && \ - make lint-typos + $(MAKE) fmt && \ + $(MAKE) lint-clippy && \ + $(MAKE) lint-typos ##@ Other @@ -105,9 +116,9 @@ deny: ## Perform a `cargo` deny check. .PHONY: pr pr: ## Run all checks and tests. - make deny && \ - make lint && \ - make test + $(MAKE) deny && \ + $(MAKE) lint && \ + $(MAKE) test # dprint formatting commands .PHONY: dprint-fmt diff --git a/README.md b/README.md index 4cb63bb467ebc..c9f0a45c57b0a 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,8 @@   [![Github Actions][gha-badge]][gha-url] [![Telegram Chat][tg-badge]][tg-url] [![Telegram Support][tg-support-badge]][tg-support-url] -![Foundry](https://img.shields.io/badge/Foundry-grey?style=flat&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAElElEQVR4nH1VUUhUaRg9984YdzBpkqR0Z210rIESIXSabEbcHgydrpNRRj00kWaztj0U1MOW0MOIbD300IvLMqBpMTGYxdoqyoRNDUESBDWwUuPugCSSsTM7u0Oj1/+efdiMcmnP2/fDd77D4f/OB6xCa2urQZbllVICYGtqanK1tLS4AdgAyAAgyzJaW1sNq/ulT4twOGw4fPiwAGDp7Ow8VV1d7bVarRWxWCw/k8mgsbExm0wmZ+Lx+M/Xr1//CcAsSVmSJH01McLhsAEAnE5nx+Tk5B/xeJxOp5N9fX2sqqqixWLhnTt36HA4GIvFGI1GU3V1df5Pe/9D1t7eHkgkEuzo6GBPT49WWloq7Ha7fujQITocDu7atUs3m83i6tWr2okTJ/jixQuePn265zPScDhskGUZe/fubXv8+DFv3rypbdiwQaxbt46RSIT79u3j0NAQb926RVVVOT4+TqvVyvz8fD0YDC5NTk6ysbHxlCRJ/5KSlAAURyKRTFNTkwAg7t69S5/Px76+Pq7GyMgI9+/fz9HRUQIQO3bsEKOjo38DsJCUJADw+/0BVVW7otHo8ps3b4yvXr3CxMQETCYTTCYTNE0DAOTl5SGXy0FRFOzZswdmsxkVFRXLNTU1xmg0+kNvb+/3AGAcGBiI7969Wwcg6urq+OTJE967d49btmzh9PT0R3WJRIKBQIDBYJBTU1NsaGggAGGz2fTe3t5fAeQZAWwuLi4uP3nypOT1emEwGFBeXo7a2losLCygoaEB/f39MJlMCIVCkCQJBw8ehNVqhcfjQXNzs1RSUiKtX7++DEAZqqqq3KFQiABYUFDAM2fOkCQXFxdJkvfv32dhYSG9Xi+vXbvG2dnZj4oDgQCLioqoKAqHhobodDq/Mc7NzUklJSUIBoOw2WzYtm0blpeXsWbNGkxMTODp06doa2vD4OAgNm7cCIvFApLQdR3nzp3Dzp078fLlSxQVFeHdu3cAgIpHjx69/zBUX5k+MDBAt9vNY8eOsbu7m6lUigcOHKDL5WImkyHJz9TGYrEcALsMIPn69esZTdMIgM+ePUNXVxdu376NsrIyuN1uXLp0CWazGcPDw3C5XFBVFWfPnkVNTQ18Pp+ezWY5MzPzO4DfAABHjhzpJslUKqVdvHiR4+PjbG9vZy6XI0kuLS0xmUxSCEGS9Pv9LC0tpdFoZGVlpSaEoM/nuwIAKx/7q5GRkb9CoZBQVVWcP3+ez58/J0mm02kODg7ywoULjMViTKfTtNvtXLt2LTdt2qTncrnlsbGxLICvSUqfrl5HJBLh1NTUkhBCJ8mFhQX29/dTVVUWFBTwwYMH1HWdly9fpqIoeiKRWJqfn2d1dXWnLMuf7zMAHD16tGd+fn7FZy2bzYrKykodAAFQVVV9cXFRkNTevn3Lubk5trS0XPnfxHE4HN8ODw+nV/yanp6mx+Ohx+P5aIMQgmNjY3/W1tZ+t5rsSwG7+fjx4/76+vrm7du32woLC00AkE6n38fj8ZmHDx/+cuPGjR8BJL8YsCtYdQIMALYqilKvKEo9APuHty+egH8A3GfFDJXmxmMAAAAASUVORK5CYII%3D&link=https%3A%2F%2Fbook.getfoundry.sh%2F) -[gha-badge]: https://img.shields.io/github/actions/workflow/status/foundry-rs/foundry/test.yml?branch=master +[gha-badge]: https://img.shields.io/github/actions/workflow/status/foundry-rs/foundry/test.yml?branch=master&style=flat-square [gha-url]: https://github.com/foundry-rs/foundry/actions [tg-badge]: https://img.shields.io/endpoint?color=neon&logo=telegram&label=chat&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Ffoundry_rs [tg-url]: https://t.me/foundry_rs @@ -15,6 +14,7 @@ **[Install](https://getfoundry.sh/getting-started/installation)** | [Docs][foundry-docs] +| [Benchmarks](https://www.getfoundry.sh/benchmarks) | [Developer Guidelines](./docs/dev/README.md) | [Contributing](./CONTRIBUTING.md) | [Crate Docs](https://foundry-rs.github.io/foundry) @@ -23,305 +23,58 @@ --- -### Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust. +Blazing fast, portable and modular toolkit for Ethereum application development, written in Rust. -Foundry consists of: - -- [**Forge**](#forge): Build, test, fuzz, debug and deploy [Solidity][solidity] contracts, like Hardhat, Brownie, Ape. -- [**Cast**](#cast): A Swiss Army knife for interacting with EVM smart contracts, sending transactions and getting chain data. -- [**Anvil**](#anvil): Fast local Ethereum development node, akin to Hardhat Network, Tenderly. -- [**Chisel**](#chisel): Fast, utilitarian, and verbose Solidity REPL. - -**Need help getting started with Foundry? Read the [📖 Foundry Docs][foundry-docs]!** +- [**Forge**](https://getfoundry.sh/forge) — Build, test, fuzz, debug and deploy Solidity contracts. +- [**Cast**](https://getfoundry.sh/cast) — Swiss Army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- [**Anvil**](https://getfoundry.sh/anvil) — Fast local Ethereum development node. +- [**Chisel**](https://getfoundry.sh/chisel) — Fast, utilitarian and verbose Solidity REPL. ![Demo](.github/assets/demo.gif) -## Features - -- **High-Performance Compilation** - - - **Fast and Flexible**: Automatically detects and installs the required Solidity compiler version. - - **Solidity and Vyper Support**: Fully supports both Solidity and Vyper out-of-the-box. - - **Incremental Compilation**: Re-compiles only changed files, saving time. - - **Parallelized Pipeline**: Leverages multi-core systems for ultra-fast builds. - - **Broad Compatibility**: Supports non-standard directory structures, including [Hardhat repos](https://twitter.com/gakonst/status/1461289225337421829). - -- **Advanced Testing** - - - **No Context Switching**: Write tests directly in Solidity. - - **Fuzz Testing**: Quickly identify edge cases with input shrinking and counter-example generation. - - **Invariant Testing**: Ensure complex system properties hold across a wide range of inputs. - - **Debugging Made Easy**: Use [forge-std](https://github.com/foundry-rs/forge-std)'s `console.sol` for flexible debug logging. - - **Interactive Debugger**: Step through your Solidity code with Foundry's interactive debugger, making it easy to pinpoint issues. - -- **Powerful Runtime Features** - - - **RPC Forking**: Fast and efficient remote RPC forking backed by [Alloy][alloy]. - - **Lightweight & Portable**: No dependency on Nix or other package managers for installation. - -- **Streamlined CI/CD** - - - **Optimized CI**: Accelerate builds, run tests and execute scripts using [Foundry's GitHub action][foundry-gha]. - ## Installation -Getting started is very easy: - -Install `foundryup`: - -``` +```sh curl -L https://foundry.paradigm.xyz | bash -``` - -Next, run `foundryup`. - -It will automatically install the latest version of the precompiled binaries: [`forge`](#forge), [`cast`](#cast), [`anvil`](#anvil), and [`chisel`](#chisel). - -``` foundryup ``` -**Done!** - -For additional details see the [installation guide](https://getfoundry.sh/getting-started/installation) in the [Foundry Docs][foundry-docs]. - -If you're experiencing any issues while installing, check out [Getting Help](#getting-help) and the [FAQ](https://getfoundry.sh/faq). - -## How Fast? - -Forge is quite fast at both compiling (leveraging `solc` with [foundry-compilers]) and testing. - -See the benchmarks below. Older benchmarks against [DappTools][dapptools] can be found in the [v0.2.0 announcement post][benchmark-post] and in the [Convex Shutdown Simulation][convex] repository. - -### Testing Benchmarks - -| Project | Type | [Forge 1.0][foundry-1.0] | [Forge 0.2][foundry-0.2] | DappTools | Speedup | -| --------------------------------------------- | -------------------- | ------------------------ | ------------------------ | --------- | -------------- | -| [vectorized/solady][solady] | Unit / Fuzz | 0.9s | 2.3s | - | 2.6x | -| [morpho-org/morpho-blue][morpho-blue] | Invariant | 0.7s | 1m43s | - | 147.1x | -| [morpho-org/morpho-blue-oracles][morpho-blue] | Integration (Cold) | 6.1s | 6.3s | - | 1.04x | -| [morpho-org/morpho-blue-oracles][morpho-blue] | Integration (Cached) | 0.6s | 0.9s | - | 1.50x | -| [transmissions11/solmate][solmate] | Unit / Fuzz | 2.7s | 2.8s | 6m34s | 1.03x / 140.0x | -| [reflexer-labs/geb][geb] | Unit / Fuzz | 0.2s | 0.4s | 23s | 2.0x / 57.5x | - -_In the above benchmarks, compilation was always skipped_ - -**Takeaway: Forge dramatically outperforms the competition, delivering blazing-fast execution speeds while continuously expanding its robust feature set.** - -### Compilation Benchmarks +See the [installation guide](https://getfoundry.sh/getting-started/installation) for more details. -
- - - - - - - - - - -  - -
- -**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 215/229] 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 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 216/229] 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 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 217/229] 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 218/229] 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 219/229] 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 220/229] 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 221/229] 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 222/229] 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 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 223/229] 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 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 224/229] 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 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 225/229] 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 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 226/229] 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 b2931c358e7d33b11c12886b6c20808fbbe43f50 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 7 May 2026 07:29:33 +0700 Subject: [PATCH 227/229] 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 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 53a505774ac88..edd3e4a15ddbc 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -25,6 +25,8 @@ 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] From 1e8a1e52350afe5aec223ab0acc2977fd9146126 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 9 May 2026 19:58:02 +0700 Subject: [PATCH 228/229] fix(anvil): load blocks and transactions atomically in `load_state` (#14624) (#548) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `load_state` previously took two separate write locks — one for `load_blocks`, one for `load_transactions` — leaving a window where `storage.blocks` was populated but `storage.transactions` was still empty. Concurrent `eth_getBlockByHash` / `eth_getTransactionReceipt` / `eth_getBlockReceipts` requests hitting that window read inconsistent state and `mined_transactions_in_block` / `mined_receipts` returned `None` via `?`, hiding freshly loaded blocks. Holding a single `write()` across both calls closes the gap. Co-authored-by: Aïssata --- crates/anvil/src/eth/backend/mem/mod.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 19f39f3d368de..a340822aef7df 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -3893,9 +3893,13 @@ impl> Backend { /// 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()); + // load the blocks and transactions into the storage atomically so concurrent readers + // never observe blocks without their transactions + { + let mut storage = self.blockchain.storage.write(); + storage.load_blocks(state.blocks.clone()); + storage.load_transactions(state.transactions.clone()); + } // reset the block env if let Some(block) = state.block.clone() { self.evm_env.write().block_env = block.clone(); From 0ed034e65bddcbc94c730dcba76441d40853cce2 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 9 May 2026 19:58:53 +0700 Subject: [PATCH 229/229] Update crates/evm/evm/src/inspectors/stack.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/inspectors/stack.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 2a3bce3dac89e..961e0a7d28bf3 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -1298,7 +1298,7 @@ impl Inspector> .journal() .evm_state() .get(&create.caller()) - .map(|acc| create.caller().create(acc.info.nonce)); + .map(|acc| create.caller().create(acc.info.nonce.saturating_sub(1))); let (result, address) = self.transact_inner( ecx,