From 4f28ff8aafe305ce9e16bd251745b76b5fcb3bb9 Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Thu, 19 Oct 2023 15:23:15 -0500 Subject: [PATCH] chore: Consolidate dependency updates, linting, and feature additions - Update dependencies and Foundry version - Lint and format codebase - Introduce zkcast and zkforge - Modify namespace from cast to zkcast - Include latest test adjustments - Add secret workflow --- .github/leaked_secrets.yaml | 17 + Cargo.lock | 5624 +++++++++- Cargo.toml | 45 +- README.md | 765 +- crates/abi/abi/HardhatConsole.json | 9515 ++++++++++++++++- crates/chisel/src/executor.rs | 3 +- crates/common/Cargo.toml | 3 + crates/common/src/lib.rs | 2 + crates/common/src/zk_utils.rs | 143 + crates/config/src/lib.rs | 2 +- crates/debugger/Cargo.toml | 11 +- crates/evm/core/Cargo.toml | 12 + crates/evm/core/src/backend/mod.rs | 10 +- crates/evm/core/test-data/storage.json | 47 +- crates/evm/evm/src/executors/mod.rs | 20 +- crates/evm/evm/src/executors/tracing.rs | 4 +- crates/evm/test-data/solc-obj.json | 403 +- crates/zkcast/Cargo.toml | 90 + crates/zkcast/README.md | 5 + crates/zkcast/benches/vanity.rs | 51 + crates/zkcast/bin/cmd/access_list.rs | 115 + crates/zkcast/bin/cmd/bind.rs | 107 + crates/zkcast/bin/cmd/call.rs | 294 + crates/zkcast/bin/cmd/create2.rs | 314 + crates/zkcast/bin/cmd/estimate.rs | 126 + crates/zkcast/bin/cmd/find_block.rs | 84 + crates/zkcast/bin/cmd/interface.rs | 88 + crates/zkcast/bin/cmd/logs.rs | 413 + crates/zkcast/bin/cmd/mod.rs | 22 + crates/zkcast/bin/cmd/rpc.rs | 63 + crates/zkcast/bin/cmd/run.rs | 212 + crates/zkcast/bin/cmd/send.rs | 254 + crates/zkcast/bin/cmd/storage.rs | 258 + crates/zkcast/bin/cmd/wallet/mod.rs | 350 + crates/zkcast/bin/cmd/wallet/vanity.rs | 339 + crates/zkcast/bin/cmd/zk_deposit.rs | 265 + crates/zkcast/bin/cmd/zk_send.rs | 261 + crates/zkcast/bin/main.rs | 505 + crates/zkcast/bin/opts.rs | 988 ++ crates/zkcast/build.rs | 3 + crates/zkcast/src/base.rs | 800 ++ crates/zkcast/src/errors.rs | 34 + crates/zkcast/src/lib.rs | 2100 ++++ crates/zkcast/src/rlp_converter.rs | 172 + crates/zkcast/src/tx.rs | 391 + crates/zkcast/tests/cli/main.rs | 512 + .../zkcast/tests/fixtures/ERC20Artifact.json | 385 + crates/zkcast/tests/fixtures/cast_logs.stdout | 13 + ...--560d246fcddc9ea98a8b032c9a2f474efb493c28 | 1 + ...--ec554aeafe75601aaab43bd4621a22284db566c2 | 1 + .../zkcast/tests/fixtures/keystore/password | 1 + .../tests/fixtures/keystore/password-ec554 | 1 + .../tests/fixtures/sign_typed_data.json | 38 + crates/zkforge/Cargo.toml | 98 + crates/zkforge/README.md | 446 + crates/zkforge/assets/.gitignoreTemplate | 14 + crates/zkforge/assets/CounterTemplate.s.sol | 12 + crates/zkforge/assets/CounterTemplate.sol | 14 + crates/zkforge/assets/CounterTemplate.t.sol | 24 + crates/zkforge/assets/README.md | 66 + .../assets/generated/TestTemplate.t.sol | 13 + crates/zkforge/assets/workflowTemplate.yml | 34 + crates/zkforge/benches/test.rs | 24 + crates/zkforge/bin/cmd/bind.rs | 213 + crates/zkforge/bin/cmd/build.rs | 164 + crates/zkforge/bin/cmd/cache.rs | 192 + crates/zkforge/bin/cmd/config.rs | 60 + crates/zkforge/bin/cmd/coverage.rs | 405 + crates/zkforge/bin/cmd/create.rs | 373 + crates/zkforge/bin/cmd/debug.rs | 58 + crates/zkforge/bin/cmd/doc.rs | 107 + crates/zkforge/bin/cmd/flatten.rs | 74 + crates/zkforge/bin/cmd/fmt.rs | 250 + crates/zkforge/bin/cmd/fourbyte.rs | 93 + crates/zkforge/bin/cmd/geiger/error.rs | 12 + crates/zkforge/bin/cmd/geiger/find.rs | 168 + crates/zkforge/bin/cmd/geiger/mod.rs | 119 + crates/zkforge/bin/cmd/geiger/visitor.rs | 333 + crates/zkforge/bin/cmd/generate/mod.rs | 70 + crates/zkforge/bin/cmd/init.rs | 246 + crates/zkforge/bin/cmd/inspect.rs | 441 + crates/zkforge/bin/cmd/install.rs | 513 + crates/zkforge/bin/cmd/mod.rs | 72 + crates/zkforge/bin/cmd/remappings.rs | 58 + crates/zkforge/bin/cmd/remove.rs | 46 + crates/zkforge/bin/cmd/retry.rs | 61 + crates/zkforge/bin/cmd/script/artifacts.rs | 9 + crates/zkforge/bin/cmd/script/broadcast.rs | 679 ++ crates/zkforge/bin/cmd/script/build.rs | 289 + crates/zkforge/bin/cmd/script/cmd.rs | 360 + crates/zkforge/bin/cmd/script/executor.rs | 333 + crates/zkforge/bin/cmd/script/mod.rs | 993 ++ crates/zkforge/bin/cmd/script/multi.rs | 182 + crates/zkforge/bin/cmd/script/providers.rs | 101 + crates/zkforge/bin/cmd/script/receipts.rs | 153 + crates/zkforge/bin/cmd/script/runner.rs | 373 + crates/zkforge/bin/cmd/script/sequence.rs | 398 + crates/zkforge/bin/cmd/script/transaction.rs | 457 + crates/zkforge/bin/cmd/script/verify.rs | 119 + crates/zkforge/bin/cmd/selectors.rs | 182 + crates/zkforge/bin/cmd/snapshot.rs | 507 + crates/zkforge/bin/cmd/test/filter.rs | 217 + crates/zkforge/bin/cmd/test/mod.rs | 884 ++ crates/zkforge/bin/cmd/test/summary.rs | 111 + crates/zkforge/bin/cmd/tree.rs | 37 + crates/zkforge/bin/cmd/update.rs | 56 + .../bin/cmd/verify/etherscan/flatten.rs | 112 + .../zkforge/bin/cmd/verify/etherscan/mod.rs | 597 ++ .../bin/cmd/verify/etherscan/standard_json.rs | 48 + crates/zkforge/bin/cmd/verify/mod.rs | 232 + crates/zkforge/bin/cmd/verify/provider.rs | 84 + crates/zkforge/bin/cmd/verify/sourcify.rs | 208 + crates/zkforge/bin/cmd/watch.rs | 452 + crates/zkforge/bin/cmd/zk_build.rs | 266 + crates/zkforge/bin/cmd/zk_create.rs | 375 + crates/zkforge/bin/cmd/zk_solc.rs | 874 ++ crates/zkforge/bin/cmd/zksolc_manager.rs | 583 + crates/zkforge/bin/main.rs | 97 + crates/zkforge/bin/opts.rs | 173 + crates/zkforge/build.rs | 3 + crates/zkforge/src/coverage.rs | 170 + crates/zkforge/src/gas_report.rs | 168 + crates/zkforge/src/lib.rs | 218 + crates/zkforge/src/multi_runner.rs | 417 + crates/zkforge/src/result.rs | 281 + crates/zkforge/src/runner.rs | 674 ++ crates/zkforge/tests/cli/cache.rs | 20 + crates/zkforge/tests/cli/cmd.rs | 1634 +++ crates/zkforge/tests/cli/config.rs | 639 ++ crates/zkforge/tests/cli/constants.rs | 11 + crates/zkforge/tests/cli/coverage.rs | 16 + crates/zkforge/tests/cli/create.rs | 220 + crates/zkforge/tests/cli/doc.rs | 11 + crates/zkforge/tests/cli/ext_integration.rs | 26 + crates/zkforge/tests/cli/heavy_integration.rs | 5 + crates/zkforge/tests/cli/main.rs | 19 + crates/zkforge/tests/cli/multi_script.rs | 88 + crates/zkforge/tests/cli/script.rs | 1133 ++ crates/zkforge/tests/cli/svm.rs | 62 + crates/zkforge/tests/cli/test_cmd.rs | 406 + crates/zkforge/tests/cli/utils.rs | 141 + crates/zkforge/tests/cli/verify.rs | 131 + .../zkforge/tests/fixtures/ScriptVerify.sol | 72 + .../fixtures/can_build_skip_contracts.stdout | 3 + .../tests/fixtures/can_build_skip_glob.stdout | 3 + .../tests/fixtures/can_check_snapshot.stdout | 9 + .../can_create_template_contract-2nd.stdout | 4 + .../can_create_template_contract.stdout | 6 + .../can_create_using_unlocked-2nd.stdout | 4 + .../fixtures/can_create_using_unlocked.stdout | 6 + ...can_detect_dirty_git_status_on_init.stderr | 10 + ...n_execute_script_and_skip_contracts.stdout | 12 + .../can_execute_script_command.stdout | 8 + .../can_execute_script_command_fqn.stdout | 8 + ...an_execute_script_command_with_args.stdout | 10 + ...xecute_script_command_with_returned.stdout | 12 + ...can_execute_script_command_with_sig.stdout | 8 + .../can_run_test_in_custom_test_folder.stdout | 9 + .../fixtures/can_set_yul_optimizer.stderr | 9 + .../tests/fixtures/can_test_repeatedly.stdout | 8 + .../can_use_libs_in_multi_fork.stdout | 9 + ...y_once_with_changed_versions.0.8.10.stdout | 9 + ...y_once_with_changed_versions.0.8.13.stdout | 9 + .../suggest_when_no_tests_match.stdout | 13 + .../tests/fixtures/warn_no_tests.stdout | 5 + .../tests/fixtures/warn_no_tests_match.stdout | 11 + crates/zkforge/tests/it/cheats.rs | 24 + crates/zkforge/tests/it/config.rs | 290 + crates/zkforge/tests/it/core.rs | 725 ++ crates/zkforge/tests/it/fork.rs | 100 + crates/zkforge/tests/it/fs.rs | 25 + crates/zkforge/tests/it/fuzz.rs | 83 + crates/zkforge/tests/it/inline.rs | 107 + crates/zkforge/tests/it/invariant.rs | 184 + crates/zkforge/tests/it/main.rs | 11 + crates/zkforge/tests/it/repros.rs | 303 + crates/zkforge/tests/it/spec.rs | 8 + crates/zkforge/tests/it/test_helpers.rs | 181 + crates/zkforge/tests/rpc-cache-keyfile | 5 + testdata/artifacts-counter/artifacts.json | 478 + testdata/artifacts-counter/empty.json | 22 + testdata/fixtures/Json/write_test.json | 4 +- 182 files changed, 50335 insertions(+), 661 deletions(-) create mode 100644 .github/leaked_secrets.yaml create mode 100644 crates/common/src/zk_utils.rs create mode 100644 crates/zkcast/Cargo.toml create mode 100644 crates/zkcast/README.md create mode 100644 crates/zkcast/benches/vanity.rs create mode 100644 crates/zkcast/bin/cmd/access_list.rs create mode 100644 crates/zkcast/bin/cmd/bind.rs create mode 100644 crates/zkcast/bin/cmd/call.rs create mode 100644 crates/zkcast/bin/cmd/create2.rs create mode 100644 crates/zkcast/bin/cmd/estimate.rs create mode 100644 crates/zkcast/bin/cmd/find_block.rs create mode 100644 crates/zkcast/bin/cmd/interface.rs create mode 100644 crates/zkcast/bin/cmd/logs.rs create mode 100644 crates/zkcast/bin/cmd/mod.rs create mode 100644 crates/zkcast/bin/cmd/rpc.rs create mode 100644 crates/zkcast/bin/cmd/run.rs create mode 100644 crates/zkcast/bin/cmd/send.rs create mode 100644 crates/zkcast/bin/cmd/storage.rs create mode 100644 crates/zkcast/bin/cmd/wallet/mod.rs create mode 100644 crates/zkcast/bin/cmd/wallet/vanity.rs create mode 100644 crates/zkcast/bin/cmd/zk_deposit.rs create mode 100644 crates/zkcast/bin/cmd/zk_send.rs create mode 100644 crates/zkcast/bin/main.rs create mode 100644 crates/zkcast/bin/opts.rs create mode 100644 crates/zkcast/build.rs create mode 100644 crates/zkcast/src/base.rs create mode 100644 crates/zkcast/src/errors.rs create mode 100644 crates/zkcast/src/lib.rs create mode 100644 crates/zkcast/src/rlp_converter.rs create mode 100644 crates/zkcast/src/tx.rs create mode 100644 crates/zkcast/tests/cli/main.rs create mode 100644 crates/zkcast/tests/fixtures/ERC20Artifact.json create mode 100644 crates/zkcast/tests/fixtures/cast_logs.stdout create mode 100644 crates/zkcast/tests/fixtures/keystore/UTC--2022-10-30T06-51-20.130356000Z--560d246fcddc9ea98a8b032c9a2f474efb493c28 create mode 100644 crates/zkcast/tests/fixtures/keystore/UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2 create mode 100644 crates/zkcast/tests/fixtures/keystore/password create mode 100644 crates/zkcast/tests/fixtures/keystore/password-ec554 create mode 100644 crates/zkcast/tests/fixtures/sign_typed_data.json create mode 100644 crates/zkforge/Cargo.toml create mode 100644 crates/zkforge/README.md create mode 100644 crates/zkforge/assets/.gitignoreTemplate create mode 100644 crates/zkforge/assets/CounterTemplate.s.sol create mode 100644 crates/zkforge/assets/CounterTemplate.sol create mode 100644 crates/zkforge/assets/CounterTemplate.t.sol create mode 100644 crates/zkforge/assets/README.md create mode 100644 crates/zkforge/assets/generated/TestTemplate.t.sol create mode 100644 crates/zkforge/assets/workflowTemplate.yml create mode 100644 crates/zkforge/benches/test.rs create mode 100644 crates/zkforge/bin/cmd/bind.rs create mode 100644 crates/zkforge/bin/cmd/build.rs create mode 100644 crates/zkforge/bin/cmd/cache.rs create mode 100644 crates/zkforge/bin/cmd/config.rs create mode 100644 crates/zkforge/bin/cmd/coverage.rs create mode 100644 crates/zkforge/bin/cmd/create.rs create mode 100644 crates/zkforge/bin/cmd/debug.rs create mode 100644 crates/zkforge/bin/cmd/doc.rs create mode 100644 crates/zkforge/bin/cmd/flatten.rs create mode 100644 crates/zkforge/bin/cmd/fmt.rs create mode 100644 crates/zkforge/bin/cmd/fourbyte.rs create mode 100644 crates/zkforge/bin/cmd/geiger/error.rs create mode 100644 crates/zkforge/bin/cmd/geiger/find.rs create mode 100644 crates/zkforge/bin/cmd/geiger/mod.rs create mode 100644 crates/zkforge/bin/cmd/geiger/visitor.rs create mode 100644 crates/zkforge/bin/cmd/generate/mod.rs create mode 100644 crates/zkforge/bin/cmd/init.rs create mode 100644 crates/zkforge/bin/cmd/inspect.rs create mode 100644 crates/zkforge/bin/cmd/install.rs create mode 100644 crates/zkforge/bin/cmd/mod.rs create mode 100644 crates/zkforge/bin/cmd/remappings.rs create mode 100644 crates/zkforge/bin/cmd/remove.rs create mode 100644 crates/zkforge/bin/cmd/retry.rs create mode 100644 crates/zkforge/bin/cmd/script/artifacts.rs create mode 100644 crates/zkforge/bin/cmd/script/broadcast.rs create mode 100644 crates/zkforge/bin/cmd/script/build.rs create mode 100644 crates/zkforge/bin/cmd/script/cmd.rs create mode 100644 crates/zkforge/bin/cmd/script/executor.rs create mode 100644 crates/zkforge/bin/cmd/script/mod.rs create mode 100644 crates/zkforge/bin/cmd/script/multi.rs create mode 100644 crates/zkforge/bin/cmd/script/providers.rs create mode 100644 crates/zkforge/bin/cmd/script/receipts.rs create mode 100644 crates/zkforge/bin/cmd/script/runner.rs create mode 100644 crates/zkforge/bin/cmd/script/sequence.rs create mode 100644 crates/zkforge/bin/cmd/script/transaction.rs create mode 100644 crates/zkforge/bin/cmd/script/verify.rs create mode 100644 crates/zkforge/bin/cmd/selectors.rs create mode 100644 crates/zkforge/bin/cmd/snapshot.rs create mode 100644 crates/zkforge/bin/cmd/test/filter.rs create mode 100644 crates/zkforge/bin/cmd/test/mod.rs create mode 100644 crates/zkforge/bin/cmd/test/summary.rs create mode 100644 crates/zkforge/bin/cmd/tree.rs create mode 100644 crates/zkforge/bin/cmd/update.rs create mode 100644 crates/zkforge/bin/cmd/verify/etherscan/flatten.rs create mode 100644 crates/zkforge/bin/cmd/verify/etherscan/mod.rs create mode 100644 crates/zkforge/bin/cmd/verify/etherscan/standard_json.rs create mode 100644 crates/zkforge/bin/cmd/verify/mod.rs create mode 100644 crates/zkforge/bin/cmd/verify/provider.rs create mode 100644 crates/zkforge/bin/cmd/verify/sourcify.rs create mode 100644 crates/zkforge/bin/cmd/watch.rs create mode 100644 crates/zkforge/bin/cmd/zk_build.rs create mode 100644 crates/zkforge/bin/cmd/zk_create.rs create mode 100644 crates/zkforge/bin/cmd/zk_solc.rs create mode 100644 crates/zkforge/bin/cmd/zksolc_manager.rs create mode 100644 crates/zkforge/bin/main.rs create mode 100644 crates/zkforge/bin/opts.rs create mode 100644 crates/zkforge/build.rs create mode 100644 crates/zkforge/src/coverage.rs create mode 100644 crates/zkforge/src/gas_report.rs create mode 100644 crates/zkforge/src/lib.rs create mode 100644 crates/zkforge/src/multi_runner.rs create mode 100644 crates/zkforge/src/result.rs create mode 100644 crates/zkforge/src/runner.rs create mode 100644 crates/zkforge/tests/cli/cache.rs create mode 100644 crates/zkforge/tests/cli/cmd.rs create mode 100644 crates/zkforge/tests/cli/config.rs create mode 100644 crates/zkforge/tests/cli/constants.rs create mode 100644 crates/zkforge/tests/cli/coverage.rs create mode 100644 crates/zkforge/tests/cli/create.rs create mode 100644 crates/zkforge/tests/cli/doc.rs create mode 100644 crates/zkforge/tests/cli/ext_integration.rs create mode 100644 crates/zkforge/tests/cli/heavy_integration.rs create mode 100644 crates/zkforge/tests/cli/main.rs create mode 100644 crates/zkforge/tests/cli/multi_script.rs create mode 100644 crates/zkforge/tests/cli/script.rs create mode 100644 crates/zkforge/tests/cli/svm.rs create mode 100644 crates/zkforge/tests/cli/test_cmd.rs create mode 100644 crates/zkforge/tests/cli/utils.rs create mode 100644 crates/zkforge/tests/cli/verify.rs create mode 100644 crates/zkforge/tests/fixtures/ScriptVerify.sol create mode 100644 crates/zkforge/tests/fixtures/can_build_skip_contracts.stdout create mode 100644 crates/zkforge/tests/fixtures/can_build_skip_glob.stdout create mode 100644 crates/zkforge/tests/fixtures/can_check_snapshot.stdout create mode 100644 crates/zkforge/tests/fixtures/can_create_template_contract-2nd.stdout create mode 100644 crates/zkforge/tests/fixtures/can_create_template_contract.stdout create mode 100644 crates/zkforge/tests/fixtures/can_create_using_unlocked-2nd.stdout create mode 100644 crates/zkforge/tests/fixtures/can_create_using_unlocked.stdout create mode 100644 crates/zkforge/tests/fixtures/can_detect_dirty_git_status_on_init.stderr create mode 100644 crates/zkforge/tests/fixtures/can_execute_script_and_skip_contracts.stdout create mode 100644 crates/zkforge/tests/fixtures/can_execute_script_command.stdout create mode 100644 crates/zkforge/tests/fixtures/can_execute_script_command_fqn.stdout create mode 100644 crates/zkforge/tests/fixtures/can_execute_script_command_with_args.stdout create mode 100644 crates/zkforge/tests/fixtures/can_execute_script_command_with_returned.stdout create mode 100644 crates/zkforge/tests/fixtures/can_execute_script_command_with_sig.stdout create mode 100644 crates/zkforge/tests/fixtures/can_run_test_in_custom_test_folder.stdout create mode 100644 crates/zkforge/tests/fixtures/can_set_yul_optimizer.stderr create mode 100644 crates/zkforge/tests/fixtures/can_test_repeatedly.stdout create mode 100644 crates/zkforge/tests/fixtures/can_use_libs_in_multi_fork.stdout create mode 100644 crates/zkforge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.0.8.10.stdout create mode 100644 crates/zkforge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.0.8.13.stdout create mode 100644 crates/zkforge/tests/fixtures/suggest_when_no_tests_match.stdout create mode 100644 crates/zkforge/tests/fixtures/warn_no_tests.stdout create mode 100644 crates/zkforge/tests/fixtures/warn_no_tests_match.stdout create mode 100644 crates/zkforge/tests/it/cheats.rs create mode 100644 crates/zkforge/tests/it/config.rs create mode 100644 crates/zkforge/tests/it/core.rs create mode 100644 crates/zkforge/tests/it/fork.rs create mode 100644 crates/zkforge/tests/it/fs.rs create mode 100644 crates/zkforge/tests/it/fuzz.rs create mode 100644 crates/zkforge/tests/it/inline.rs create mode 100644 crates/zkforge/tests/it/invariant.rs create mode 100644 crates/zkforge/tests/it/main.rs create mode 100644 crates/zkforge/tests/it/repros.rs create mode 100644 crates/zkforge/tests/it/spec.rs create mode 100644 crates/zkforge/tests/it/test_helpers.rs create mode 100644 crates/zkforge/tests/rpc-cache-keyfile create mode 100644 testdata/artifacts-counter/artifacts.json create mode 100644 testdata/artifacts-counter/empty.json diff --git a/.github/leaked_secrets.yaml b/.github/leaked_secrets.yaml new file mode 100644 index 000000000..190c8190d --- /dev/null +++ b/.github/leaked_secrets.yaml @@ -0,0 +1,17 @@ +name: Leaked Secrets Scan +on: [pull_request] +jobs: + TruffleHog: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3 + with: + fetch-depth: 0 + - name: TruffleHog OSS + uses: trufflesecurity/trufflehog@0c66d30c1f4075cee1aada2e1ab46dabb1b0071a + with: + path: ./ + base: ${{ github.event.repository.default_branch }} + head: HEAD + extra_args: --debug --only-verified \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 166bcbde6..a7ed174c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,6 +12,212 @@ dependencies = [ "regex", ] +[[package]] +name = "actix-codec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617a8268e3537fe1d8c9ead925fca49ef6400927ee7bc26750e90ecee14ce4b8" +dependencies = [ + "bitflags 1.3.2", + "bytes 1.5.0", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util 0.7.9", + "tracing", +] + +[[package]] +name = "actix-cors" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b340e9cfa5b08690aae90fb61beb44e9b06f44fe3d0f93781aaa58cfba86245e" +dependencies = [ + "actix-utils", + "actix-web", + "derive_more", + "futures-util", + "log", + "once_cell", + "smallvec", +] + +[[package]] +name = "actix-http" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92ef85799cba03f76e4f7c10f533e66d87c9a7e7055f3391f09000ad8351bc9" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "ahash 0.8.3", + "base64 0.21.4", + "bitflags 2.4.1", + "brotli", + "bytes 1.5.0", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "futures-core", + "h2", + "http", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand 0.8.5", + "sha1", + "smallvec", + "tokio", + "tokio-util 0.7.9", + "tracing", + "zstd 0.12.4", +] + +[[package]] +name = "actix-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" +dependencies = [ + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "actix-router" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66ff4d247d2b160861fa2866457e85706833527840e4133f8f49aa423a38799" +dependencies = [ + "bytestring", + "http", + "regex", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d" +dependencies = [ + "actix-macros", + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb13e7eef0423ea6eab0e59f6c72e7cb46d33691ad56a726b3cd07ddec2c2d4" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio 0.8.8", + "socket2 0.5.5", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" +dependencies = [ + "futures-core", + "paste", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4a5b5e29603ca8c94a77c65cf874718ceb60292c5a5c3e5f4ace041af462b9" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "ahash 0.8.3", + "bytes 1.5.0", + "bytestring", + "cfg-if 1.0.0", + "cookie", + "derive_more", + "encoding_rs", + "futures-core", + "futures-util", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2 0.5.5", + "time", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1f50ebbb30eca122b188319a4398b3f7bb4a8cdf50ecfb73bfc6a3c3ce54f5" +dependencies = [ + "actix-router", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "addchain" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2e69442aa5628ea6951fa33e24efe8313f4321a91bd729fc2f75bdfc858570" +dependencies = [ + "num-bigint 0.3.3", + "num-integer", + "num-traits", +] + [[package]] name = "addr2line" version = "0.21.0" @@ -27,17 +233,60 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +dependencies = [ + "aes-soft", + "aesni", + "cipher 0.2.5", +] + [[package]] name = "aes" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ - "cfg-if", - "cipher", + "cfg-if 1.0.0", + "cipher 0.4.4", "cpufeatures", ] +[[package]] +name = "aes-ctr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7729c3cde54d67063be556aeac75a81330d802f0259500ca40cb52967f975763" +dependencies = [ + "aes-soft", + "aesni", + "cipher 0.2.5", + "ctr 0.6.0", +] + +[[package]] +name = "aes-soft" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" +dependencies = [ + "cipher 0.2.5", + "opaque-debug 0.3.0", +] + +[[package]] +name = "aesni" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +dependencies = [ + "cipher 0.2.5", + "opaque-debug 0.3.0", +] + [[package]] name = "ahash" version = "0.7.7" @@ -55,7 +304,8 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", + "getrandom 0.2.10", "once_cell", "version_check", "zerocopy", @@ -70,6 +320,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "allocator-api2" version = "0.2.16" @@ -114,8 +379,8 @@ source = "git+https://github.com/alloy-rs/core/#818071e6f20cec5c0b7fb465fda5e055 dependencies = [ "alloy-rlp", "arbitrary", - "bytes", - "cfg-if", + "bytes 1.5.0", + "cfg-if 1.0.0", "const-hex", "derive_arbitrary", "derive_more", @@ -127,7 +392,7 @@ dependencies = [ "rand 0.8.5", "ruint", "serde", - "tiny-keccak", + "tiny-keccak 2.0.2", ] [[package]] @@ -137,8 +402,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc0fac0fc16baf1f63f78b47c3d24718f3619b0714076f6a02957d808d52cbef" dependencies = [ "alloy-rlp-derive", - "arrayvec", - "bytes", + "arrayvec 0.7.4", + "bytes 1.5.0", "smol_str", ] @@ -167,7 +432,7 @@ dependencies = [ "quote", "syn 2.0.39", "syn-solidity", - "tiny-keccak", + "tiny-keccak 2.0.2", ] [[package]] @@ -223,6 +488,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "anstream" version = "0.6.4" @@ -281,10 +555,10 @@ dependencies = [ "anvil-server", "async-trait", "auto_impl", - "axum", - "bytes", + "axum 0.5.17", + "bytes 1.5.0", "chrono", - "clap", + "clap 4.4.6", "clap_complete", "clap_complete_fig", "crc 3.0.1", @@ -298,12 +572,12 @@ dependencies = [ "foundry-config", "foundry-evm", "foundry-utils", - "futures", + "futures 0.3.28", "hash-db", "hyper", "itertools 0.11.0", "memory-db", - "parking_lot", + "parking_lot 0.12.1", "pretty_assertions", "rand 0.8.5", "serde", @@ -326,7 +600,7 @@ name = "anvil-core" version = "0.2.0" dependencies = [ "alloy-primitives", - "bytes", + "bytes 1.5.0", "ethers-core", "foundry-evm", "foundry-utils", @@ -356,18 +630,18 @@ version = "0.2.0" dependencies = [ "anvil-rpc", "async-trait", - "axum", - "bytes", - "clap", - "futures", + "axum 0.5.17", + "bytes 1.5.0", + "clap 4.4.6", + "futures 0.3.28", "hyper", "parity-tokio-ipc", - "parking_lot", + "parking_lot 0.12.1", "pin-project", "serde", "serde_json", "thiserror", - "tokio-util", + "tokio-util 0.7.9", "tower-http 0.4.4", "tracing", ] @@ -405,7 +679,7 @@ dependencies = [ "ark-serialize 0.3.0", "ark-std 0.3.0", "derivative", - "num-bigint", + "num-bigint 0.4.4", "num-traits", "paste", "rustc_version 0.3.3", @@ -425,7 +699,7 @@ dependencies = [ "derivative", "digest 0.10.7", "itertools 0.10.5", - "num-bigint", + "num-bigint 0.4.4", "num-traits", "paste", "rustc_version 0.4.0", @@ -438,7 +712,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" dependencies = [ - "quote", + "quote 1.0.33", "syn 1.0.109", ] @@ -448,7 +722,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "quote", + "quote 1.0.33", "syn 1.0.109", ] @@ -458,9 +732,9 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" dependencies = [ - "num-bigint", + "num-bigint 0.4.4", "num-traits", - "quote", + "quote 1.0.33", "syn 1.0.109", ] @@ -470,10 +744,10 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ - "num-bigint", + "num-bigint 0.4.4", "num-traits", - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] @@ -495,7 +769,7 @@ checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" dependencies = [ "ark-std 0.4.0", "digest 0.10.7", - "num-bigint", + "num-bigint 0.4.4", ] [[package]] @@ -518,6 +792,48 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "arr_macro" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a105bfda48707cf19220129e78fca01e9639433ffaef4163546ed8fb04120a5" +dependencies = [ + "arr_macro_impl", + "proc-macro-hack", +] + +[[package]] +name = "arr_macro_impl" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0609c78bd572f4edc74310dfb63a01f5609d53fa8b4dd7c4d98aef3b3e8d72d1" +dependencies = [ + "proc-macro-hack", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +dependencies = [ + "nodrop", +] + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "arrayvec" version = "0.7.4" @@ -533,6 +849,31 @@ dependencies = [ "term", ] +[[package]] +name = "async-compression" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f658e2baef915ba0f26f1f7c42bfb8e12f532a01f449a090ded75ae7a07e9ba2" +dependencies = [ + "brotli", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "zstd 0.13.0", + "zstd-safe 7.0.0", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener", +] + [[package]] name = "async-priority-channel" version = "0.1.0" @@ -570,11 +911,20 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" dependencies = [ - "futures", + "futures 0.3.28", "pharos", "rustc_version 0.4.0", ] +[[package]] +name = "atoi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616896e05fc0e2649463a93a15183c6a16bf03413a7af88ef1285ddedfa9cda5" +dependencies = [ + "num-traits", +] + [[package]] name = "atomic" version = "0.6.0" @@ -591,17 +941,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8ab6b55fe97976e46f91ddbed8d147d966475dc29b2032757ba47e02376fbc3" [[package]] -name = "auto_impl" -version = "1.1.0" +name = "atty" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", + "hermit-abi 0.1.19", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "auto_impl" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" +dependencies = [ + "proc-macro-error", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] +[[package]] +name = "autocfg" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +dependencies = [ + "autocfg 1.1.0", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -615,16 +985,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acee9fd5073ab6b045a275b3e709c163dd36c90685219cb21804a147b58dba43" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.2.9", "base64 0.13.1", "bitflags 1.3.2", - "bytes", + "bytes 1.5.0", "futures-util", "http", "http-body", "hyper", "itoa", - "matchit", + "matchit 0.5.0", "memchr", "mime", "percent-encoding", @@ -632,7 +1002,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sha-1", + "sha-1 0.10.1", "sync_wrapper", "tokio", "tokio-tungstenite 0.17.2", @@ -642,6 +1012,37 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core 0.3.4", + "bitflags 1.3.2", + "bytes 1.5.0", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit 0.7.3", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "axum-core" version = "0.2.9" @@ -649,15 +1050,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37e5939e02c56fecd5c017c37df4238c0a839fa76b7f97acdd7efb804fd181cc" dependencies = [ "async-trait", - "bytes", + "bytes 1.5.0", + "futures-util", + "http", + "http-body", + "mime", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes 1.5.0", "futures-util", "http", "http-body", "mime", + "rustversion", "tower-layer", "tower-service", ] +[[package]] +name = "backon" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c1a6197b2120bb2185a267f6515038558b019e92b832bb0320e96d66268dcf9" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "pin-project", + "tokio", +] + [[package]] name = "backtrace" version = "0.3.69" @@ -666,13 +1096,19 @@ checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", - "cfg-if", + "cfg-if 1.0.0", "libc", "miniz_oxide", "object", "rustc-demangle", ] +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + [[package]] name = "base16ct" version = "0.2.0" @@ -703,6 +1139,103 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde", +] + +[[package]] +name = "bellman_ce" +version = "0.3.2" +source = "git+https://github.com/matter-labs/bellman?branch=dev#5520aa2274afe73d281373c92b007a2ecdebfbea" +dependencies = [ + "arrayvec 0.7.4", + "bit-vec", + "blake2s_const 0.6.0 (git+https://github.com/matter-labs/bellman?branch=dev)", + "blake2s_simd", + "byteorder", + "cfg-if 1.0.0", + "crossbeam 0.7.3", + "futures 0.3.28", + "hex", + "lazy_static", + "num_cpus", + "pairing_ce 0.28.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.6", + "serde", + "smallvec", + "tiny-keccak 1.5.0", +] + +[[package]] +name = "bellman_ce" +version = "0.3.2" +source = "git+https://github.com/matter-labs/bellman?branch=snark-wrapper#e01e5fa08a97a113e76ec8a69d06fe6cc2c82d17" +dependencies = [ + "arrayvec 0.7.4", + "bit-vec", + "blake2s_const 0.6.0 (git+https://github.com/matter-labs/bellman?branch=snark-wrapper)", + "blake2s_simd", + "byteorder", + "cfg-if 1.0.0", + "crossbeam 0.7.3", + "futures 0.3.28", + "hex", + "lazy_static", + "num_cpus", + "pairing_ce 0.28.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.6", + "serde", + "smallvec", + "tiny-keccak 1.5.0", +] + +[[package]] +name = "bigdecimal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1e50562e37200edf7c6c43e54a08e64a5553bfb59d9c297d5572512aa517256" +dependencies = [ + "num-bigint 0.3.3", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "prettyplease", + "proc-macro2 1.0.69", + "quote 1.0.33", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.38", +] + [[package]] name = "bindgen" version = "0.66.1" @@ -717,8 +1250,8 @@ dependencies = [ "log", "peeking_take_while", "prettyplease", - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "regex", "rustc-hash", "shlex", @@ -740,6 +1273,9 @@ name = "bit-vec" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +dependencies = [ + "serde", +] [[package]] name = "bitflags" @@ -757,17 +1293,111 @@ dependencies = [ "serde", ] +[[package]] +name = "bitvec" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" +dependencies = [ + "funty 1.1.0", + "radium 0.6.2", + "tap", + "wyz 0.2.0", +] + [[package]] name = "bitvec" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ - "funty", - "radium", + "funty 2.0.0", + "radium 0.7.0", "serde", "tap", - "wyz", + "wyz 0.5.1", +] + +[[package]] +name = "blake2" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a4e37d16930f5459780f5621038b6382b9bb37c19016f39fb6b5808d831f174" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "git+https://github.com/RustCrypto/hashes.git?rev=1f727ce37ff40fa0cce84eb8543a45bdd3ca4a4e#1f727ce37ff40fa0cce84eb8543a45bdd3ca4a4e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "blake2-rfc_bellman_edition" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc60350286c7c3db13b98e91dbe5c8b6830a6821bc20af5b0c310ce94d74915" +dependencies = [ + "arrayvec 0.4.12", + "byteorder", + "constant_time_eq", +] + +[[package]] +name = "blake2s_const" +version = "0.6.0" +source = "git+https://github.com/matter-labs/bellman?branch=dev#5520aa2274afe73d281373c92b007a2ecdebfbea" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "constant_time_eq", +] + +[[package]] +name = "blake2s_const" +version = "0.6.0" +source = "git+https://github.com/matter-labs/bellman?branch=snark-wrapper#e01e5fa08a97a113e76ec8a69d06fe6cc2c82d17" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "constant_time_eq", +] + +[[package]] +name = "blake2s_simd" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e461a7034e85b211a4acb57ee2e6730b32912b06c08cc242243c39fc21ae6a2" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding 0.1.5", + "byte-tools", + "byteorder", + "generic-array 0.12.4", ] [[package]] @@ -776,7 +1406,8 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array", + "block-padding 0.2.1", + "generic-array 0.14.7", ] [[package]] @@ -785,9 +1416,34 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array", + "generic-array 0.14.7", +] + +[[package]] +name = "block-modes" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0" +dependencies = [ + "block-padding 0.2.1", + "cipher 0.2.5", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", ] +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + [[package]] name = "blst" version = "0.3.11" @@ -800,6 +1456,58 @@ dependencies = [ "zeroize", ] +[[package]] +name = "boojum" +version = "0.1.0" +source = "git+https://github.com/matter-labs/era-boojum.git?branch=main#84754b066959c8fdfb77edf730fc13ed87404907" +dependencies = [ + "arrayvec 0.7.4", + "bincode", + "blake2 0.10.6 (registry+https://github.com/rust-lang/crates.io-index)", + "const_format", + "convert_case 0.6.0", + "crossbeam 0.8.2", + "crypto-bigint 0.5.3", + "cs_derive 0.1.0 (git+https://github.com/matter-labs/era-boojum.git?branch=main)", + "derivative", + "ethereum-types 0.14.1", + "firestorm", + "itertools 0.10.5", + "lazy_static", + "num-modular", + "num_cpus", + "packed_simd", + "pairing_ce 0.28.5 (git+https://github.com/matter-labs/pairing.git)", + "rand 0.8.5", + "rayon", + "serde", + "sha2 0.10.8", + "sha3 0.10.6", + "smallvec", + "unroll", +] + +[[package]] +name = "brotli" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da74e2b81409b1b743f8f0c62cc6254afefb8b8e50bbfe3735550f7aeefa3448" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bs58" version = "0.5.0" @@ -848,6 +1556,18 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "bytecount" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad152d03a2c813c80bb94fedbf3a3f02b28f793e39e7c214c8a0bcc196343de7" + [[package]] name = "bytemuck" version = "1.14.0" @@ -862,16 +1582,35 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" dependencies = [ - "serde", + "byteorder", + "iovec", ] [[package]] -name = "bzip2" -version = "0.4.4" +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +dependencies = [ + "serde", +] + +[[package]] +name = "bytestring" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238e4886760d98c4f899360c834fa93e62cf7f721ac3c2da375cbdf4b8679aae" +dependencies = [ + "bytes 1.5.0", +] + +[[package]] +name = "bzip2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" dependencies = [ @@ -896,7 +1635,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac926d808fb72fe09ebf471a091d6d72918876ccf0b4989766093d2d0d24a0ef" dependencies = [ - "bindgen", + "bindgen 0.66.1", "blst", "cc", "glob", @@ -923,6 +1662,19 @@ dependencies = [ "serde", ] +[[package]] +name = "cargo_metadata" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.20", + "serde", + "serde_json", +] + [[package]] name = "cargo_metadata" version = "0.18.1" @@ -952,7 +1704,7 @@ dependencies = [ "alloy-primitives", "async-trait", "chrono", - "clap", + "clap 4.4.6", "clap_complete", "clap_complete_fig", "comfy-table", @@ -974,7 +1726,7 @@ dependencies = [ "foundry-evm", "foundry-test-utils", "foundry-utils", - "futures", + "futures 0.3.28", "indicatif", "itertools 0.11.0", "rayon", @@ -1017,6 +1769,12 @@ dependencies = [ "nom", ] +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.0" @@ -1030,7 +1788,7 @@ dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", - "clap", + "clap 4.4.6", "criterion", "dirs 5.0.1", "ethers", @@ -1067,8 +1825,10 @@ checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-targets 0.48.5", ] @@ -1099,6 +1859,15 @@ dependencies = [ "half", ] +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array 0.14.7", +] + [[package]] name = "cipher" version = "0.4.4" @@ -1109,6 +1878,27 @@ dependencies = [ "inout", ] +[[package]] +name = "circuit_definitions" +version = "0.1.0" +source = "git+https://github.com/matter-labs/era-zkevm_test_harness.git?branch=v1.4.0#43aeb53d7d9c909508a98f9fc140edff0e9d2357" +dependencies = [ + "crossbeam 0.8.2", + "derivative", + "serde", + "snark_wrapper", + "zk_evm 1.4.0", + "zkevm_circuits", +] + +[[package]] +name = "circuit_testing" +version = "0.1.0" +source = "git+https://github.com/matter-labs/era-circuit_testing.git?branch=main#164c0adac85be39ee44bd9456b2b91cdede5af80" +dependencies = [ + "bellman_ce 0.3.2 (git+https://github.com/matter-labs/bellman?branch=dev)", +] + [[package]] name = "clang-sys" version = "1.6.1" @@ -1139,7 +1929,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.10.0", "terminal_size", "unicase", "unicode-width", @@ -1151,7 +1941,7 @@ version = "4.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bffe91f06a11b4b9420f62103854e90867812cd5d01557f853c5ee8e791b12ae" dependencies = [ - "clap", + "clap 4.4.6", ] [[package]] @@ -1160,7 +1950,7 @@ version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87e571d70e22ec91d34e1c5317c8308035a2280d925167646bf094fc5de1737c" dependencies = [ - "clap", + "clap 4.4.6", "clap_complete", ] @@ -1192,7 +1982,7 @@ dependencies = [ "terminfo", "thiserror", "which", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1203,7 +1993,41 @@ checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" dependencies = [ "error-code", "str-buf", - "winapi", + "winapi 0.3.9", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "codegen" +version = "0.1.0" +source = "git+https://github.com/matter-labs/solidity_plonk_verifier.git?branch=dev#82f96b7156551087f1c9bfe4f0ea68845b6debfc" +dependencies = [ + "ethereum-types 0.14.1", + "franklin-crypto 0.0.5 (git+https://github.com/matter-labs/franklin-crypto?branch=dev)", + "handlebars", + "hex", + "paste", + "rescue_poseidon 0.4.1 (git+https://github.com/matter-labs/rescue-poseidon)", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "codegen" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff61280aed771c3070e7dcc9e050c66f1eb1e3b96431ba66f9f74641d02fc41d" +dependencies = [ + "indexmap 1.9.3", ] [[package]] @@ -1216,7 +2040,7 @@ dependencies = [ "coins-core", "digest 0.10.7", "hmac 0.12.1", - "k256", + "k256 0.13.1", "serde", "sha2 0.10.8", "thiserror", @@ -1228,7 +2052,7 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528" dependencies = [ - "bitvec", + "bitvec 1.0.1", "coins-bip32", "hmac 0.12.1", "once_cell", @@ -1248,13 +2072,13 @@ dependencies = [ "bech32", "bs58", "digest 0.10.7", - "generic-array", + "generic-array 0.14.7", "hex", "ripemd", "serde", "serde_derive", "sha2 0.10.8", - "sha3", + "sha3 0.10.8", "thiserror", ] @@ -1266,8 +2090,8 @@ checksum = "8fa6094030951ce3efad50fdba0efe088a93ffe05ec58c2f47cc60d9e90c715d" dependencies = [ "async-trait", "byteorder", - "cfg-if", - "futures", + "cfg-if 1.0.0", + "futures 0.3.28", "getrandom 0.2.10", "hex", "hidapi-rusb", @@ -1339,7 +2163,7 @@ dependencies = [ "async-trait", "nix 0.26.4", "tokio", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1361,19 +2185,45 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5104de16b218eddf8e34ffe2f86f74bfa4e61e95a1b89732fccf6325efd0557" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "hex", "proptest", "serde", ] +[[package]] +name = "const-oid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" + [[package]] name = "const-oid" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +[[package]] +name = "const_format" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "unicode-xid 0.2.4", +] + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -1386,6 +2236,26 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -1420,13 +2290,22 @@ dependencies = [ "build_const", ] +[[package]] +name = "crc" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23" +dependencies = [ + "crc-catalog 1.1.1", +] + [[package]] name = "crc" version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" dependencies = [ - "crc-catalog", + "crc-catalog 2.2.0", ] [[package]] @@ -1441,7 +2320,7 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1453,9 +2332,9 @@ dependencies = [ "anes", "cast 0.3.0", "ciborium", - "clap", + "clap 4.4.6", "criterion-plot", - "futures", + "futures 0.3.28", "is-terminal", "itertools 0.10.5", "num-traits", @@ -1482,14 +2361,63 @@ dependencies = [ "itertools 0.10.5", ] +[[package]] +name = "crossbeam" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e" +dependencies = [ + "cfg-if 0.1.10", + "crossbeam-channel 0.4.4", + "crossbeam-deque 0.7.4", + "crossbeam-epoch 0.8.2", + "crossbeam-queue 0.2.3", + "crossbeam-utils 0.7.2", +] + +[[package]] +name = "crossbeam" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-channel 0.5.8", + "crossbeam-deque 0.8.3", + "crossbeam-epoch 0.9.15", + "crossbeam-queue 0.3.8", + "crossbeam-utils 0.8.16", +] + +[[package]] +name = "crossbeam-channel" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" +dependencies = [ + "crossbeam-utils 0.7.2", + "maybe-uninit", +] + [[package]] name = "crossbeam-channel" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ - "cfg-if", - "crossbeam-utils", + "cfg-if 1.0.0", + "crossbeam-utils 0.8.16", +] + +[[package]] +name = "crossbeam-deque" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed" +dependencies = [ + "crossbeam-epoch 0.8.2", + "crossbeam-utils 0.7.2", + "maybe-uninit", ] [[package]] @@ -1498,9 +2426,24 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", + "cfg-if 1.0.0", + "crossbeam-epoch 0.9.15", + "crossbeam-utils 0.8.16", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +dependencies = [ + "autocfg 1.1.0", + "cfg-if 0.1.10", + "crossbeam-utils 0.7.2", + "lazy_static", + "maybe-uninit", + "memoffset 0.5.6", + "scopeguard", ] [[package]] @@ -1509,13 +2452,45 @@ version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", + "autocfg 1.1.0", + "cfg-if 1.0.0", + "crossbeam-utils 0.8.16", "memoffset 0.9.0", "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" +dependencies = [ + "cfg-if 0.1.10", + "crossbeam-utils 0.7.2", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils 0.8.16", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg 1.1.0", + "cfg-if 0.1.10", + "lazy_static", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -1534,11 +2509,11 @@ dependencies = [ "bitflags 2.4.1", "crossterm_winapi", "libc", - "mio", - "parking_lot", + "mio 0.8.8", + "parking_lot 0.12.1", "signal-hook", "signal-hook-mio", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1547,7 +2522,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1556,13 +2531,35 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +dependencies = [ + "generic-array 0.14.7", + "subtle", +] + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-bigint" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" dependencies = [ - "generic-array", + "generic-array 0.14.7", "rand_core 0.6.4", "subtle", "zeroize", @@ -1574,27 +2571,79 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array", + "generic-array 0.14.7", "typenum", ] +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.7", + "subtle", +] + +[[package]] +name = "crypto-mac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" +dependencies = [ + "generic-array 0.14.7", + "subtle", +] + [[package]] name = "crypto-mac" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "generic-array", + "generic-array 0.14.7", "subtle", ] +[[package]] +name = "cs_derive" +version = "0.1.0" +source = "git+https://github.com/matter-labs/era-boojum.git?branch=main#84754b066959c8fdfb77edf730fc13ed87404907" +dependencies = [ + "proc-macro-error", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "cs_derive" +version = "0.1.0" +source = "git+https://github.com/matter-labs/era-sync_vm.git?branch=v1.3.3#dad50e7eb7462a3819af8d5209d6ca243395bf51" +dependencies = [ + "proc-macro-error", + "proc-macro2 1.0.69", + "quote 1.0.33", + "serde", + "syn 1.0.109", +] + +[[package]] +name = "ctr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" +dependencies = [ + "cipher 0.2.5", +] + [[package]] name = "ctr" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] @@ -1608,31 +2657,97 @@ dependencies = [ ] [[package]] -name = "dashmap" -version = "5.5.3" +name = "darling" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "cfg-if", - "hashbrown 0.14.2", - "lock_api", - "once_cell", - "parking_lot_core", + "darling_core", + "darling_macro", ] [[package]] -name = "data-encoding" -version = "2.4.0" +name = "darling_core" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2 1.0.69", + "quote 1.0.33", + "strsim 0.10.0", + "syn 1.0.109", +] [[package]] -name = "der" -version = "0.7.8" +name = "darling_macro" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "const-oid", + "darling_core", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.2", + "lock_api", + "once_cell", + "parking_lot_core 0.9.9", +] + +[[package]] +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "serde", + "uuid 1.5.0", +] + +[[package]] +name = "der" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +dependencies = [ + "const-oid 0.7.1", + "crypto-bigint 0.3.2", + "pem-rfc7468", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid 0.9.5", + "zeroize", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid 0.9.5", "zeroize", ] @@ -1643,6 +2758,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" dependencies = [ "powerfmt", + "serde", ] [[package]] @@ -1651,8 +2767,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] @@ -1673,9 +2789,9 @@ version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "convert_case", - "proc-macro2", - "quote", + "convert_case 0.4.0", + "proc-macro2 1.0.69", + "quote 1.0.33", "rustc_version 0.4.0", "syn 1.0.109", ] @@ -1697,13 +2813,22 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -1713,7 +2838,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", - "const-oid", + "const-oid 0.9.5", "crypto-common", "subtle", ] @@ -1742,7 +2867,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "dirs-sys-next", ] @@ -1754,7 +2879,7 @@ checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1777,15 +2902,27 @@ checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", "redox_users", - "winapi", + "winapi 0.3.9", ] +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + [[package]] name = "dotenvy" version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + [[package]] name = "dunce" version = "1.0.4" @@ -1798,18 +2935,30 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + [[package]] name = "ecdsa" version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" dependencies = [ - "der", + "der 0.7.8", "digest 0.10.7", - "elliptic-curve", - "rfc6979", - "signature", - "spki", + "elliptic-curve 0.13.6", + "rfc6979 0.4.0", + "signature 2.1.0", + "spki 0.7.2", ] [[package]] @@ -1817,6 +2966,9 @@ name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +dependencies = [ + "serde", +] [[package]] name = "elasticlunr-rs" @@ -1830,25 +2982,54 @@ dependencies = [ "serde_json", ] +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.7", + "ff 0.12.1", + "generic-array 0.14.7", + "group 0.12.1", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle", + "zeroize", +] + [[package]] name = "elliptic-curve" version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914" dependencies = [ - "base16ct", - "crypto-bigint", + "base16ct 0.2.0", + "crypto-bigint 0.5.3", "digest 0.10.7", - "ff", - "generic-array", - "group", - "pkcs8", + "ff 0.13.0", + "generic-array 0.14.7", + "group 0.13.0", + "pkcs8 0.10.2", "rand_core 0.6.4", - "sec1", + "sec1 0.7.3", "subtle", "zeroize", ] +[[package]] +name = "elsa" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714f766f3556b44e7e4776ad133fcc3445a489517c25c704ace411bb14790194" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "ena" version = "0.14.2" @@ -1870,7 +3051,7 @@ version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1888,12 +3069,12 @@ dependencies = [ "base64 0.21.5", "bytes", "hex", - "k256", + "k256 0.13.1", "log", "rand 0.8.5", "rlp", "serde", - "sha3", + "sha3 0.10.8", "zeroize", ] @@ -1908,6 +3089,19 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "env_logger" version = "0.10.0" @@ -1921,12 +3115,82 @@ dependencies = [ "termcolor", ] +[[package]] +name = "envy" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965" +dependencies = [ + "serde", +] + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "era_revm" +version = "0.0.1-alpha" +source = "git+https://github.com/matter-labs/era-revm.git?tag=v0.0.1-alpha#ef9b86b6f139224990c5e5c21052f97f57a4135d" +dependencies = [ + "era_test_node", + "ethabi 18.0.0", + "eyre", + "hex", + "multivm", + "revm", + "serde", + "serde_json", + "zk_evm 1.3.3 (git+https://github.com/matter-labs/era-zk_evm.git?tag=v1.3.3-rc1)", + "zksync_basic_types", + "zksync_types", + "zksync_utils", + "zksync_web3_decl", +] + +[[package]] +name = "era_test_node" +version = "0.1.0-alpha.9" +source = "git+https://github.com/matter-labs/era-test-node.git?rev=e9c8ee02da305cd07f69ba6838107c933b3b1b8f#e9c8ee02da305cd07f69ba6838107c933b3b1b8f" +dependencies = [ + "anyhow", + "bigdecimal", + "chrono", + "clap 4.4.6", + "colored", + "ethabi 16.0.0", + "eyre", + "futures 0.3.28", + "hex", + "indexmap 2.0.2", + "itertools 0.10.5", + "jsonrpc-core 18.0.0 (git+https://github.com/matter-labs/jsonrpc.git?branch=master)", + "jsonrpc-core-client", + "jsonrpc-derive", + "jsonrpc-http-server", + "lazy_static", + "multivm", + "once_cell", + "openssl-sys", + "reqwest", + "rustc-hash", + "serde", + "serde_json", + "sha3 0.10.8", + "tokio", + "tracing", + "tracing-subscriber", + "zksync_basic_types", + "zksync_contracts", + "zksync_core", + "zksync_state", + "zksync_types", + "zksync_utils", + "zksync_web3_decl", +] + [[package]] name = "errno" version = "0.3.5" @@ -1937,6 +3201,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "version_check", +] + [[package]] name = "error-code" version = "2.3.1" @@ -1953,20 +3226,35 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" dependencies = [ - "aes", - "ctr", + "aes 0.8.3", + "ctr 0.9.2", "digest 0.10.7", "hex", "hmac 0.12.1", "pbkdf2 0.11.0", "rand 0.8.5", - "scrypt", + "scrypt 0.10.0", "serde", "serde_json", "sha2 0.10.8", - "sha3", + "sha3 0.10.8", + "thiserror", + "uuid 0.8.2", +] + +[[package]] +name = "ethabi" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c98847055d934070b90e806e12d3936b787d0a115068981c1d8dfd5dfef5a5" +dependencies = [ + "ethereum-types 0.12.1", + "hex", + "serde", + "serde_json", + "sha3 0.9.1", "thiserror", - "uuid", + "uint", ] [[package]] @@ -1975,17 +3263,30 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" dependencies = [ - "ethereum-types", + "ethereum-types 0.14.1", "hex", "once_cell", "regex", "serde", "serde_json", - "sha3", + "sha3 0.10.8", "thiserror", "uint", ] +[[package]] +name = "ethbloom" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb684ac8fa8f6c5759f788862bb22ec6fe3cb392f6bfd08e3c64b603661e3f8" +dependencies = [ + "crunchy", + "fixed-hash 0.7.0", + "impl-rlp", + "impl-serde 0.3.2", + "tiny-keccak 2.0.2", +] + [[package]] name = "ethbloom" version = "0.13.0" @@ -1993,12 +3294,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" dependencies = [ "crunchy", - "fixed-hash", - "impl-codec", + "fixed-hash 0.8.0", + "impl-codec 0.6.0", "impl-rlp", - "impl-serde", + "impl-serde 0.4.0", "scale-info", - "tiny-keccak", + "tiny-keccak 2.0.2", ] [[package]] @@ -2010,22 +3311,36 @@ dependencies = [ "crc 1.8.1", "fastrlp", "maplit", - "primitive-types", + "primitive-types 0.12.2", "thiserror", ] +[[package]] +name = "ethereum-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05136f7057fe789f06e6d41d07b34e6f70d8c86e5693b60f97aaa6553553bdaf" +dependencies = [ + "ethbloom 0.11.1", + "fixed-hash 0.7.0", + "impl-rlp", + "impl-serde 0.3.2", + "primitive-types 0.10.1", + "uint", +] + [[package]] name = "ethereum-types" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" dependencies = [ - "ethbloom", - "fixed-hash", - "impl-codec", + "ethbloom 0.13.0", + "fixed-hash 0.8.0", + "impl-codec 0.6.0", "impl-rlp", - "impl-serde", - "primitive-types", + "impl-serde 0.4.0", + "primitive-types 0.12.2", "scale-info", "uint", ] @@ -2086,8 +3401,8 @@ dependencies = [ "ethers-etherscan", "eyre", "prettyplease", - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "regex", "reqwest", "serde", @@ -2106,8 +3421,8 @@ dependencies = [ "const-hex", "ethers-contract-abigen", "ethers-core", - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "serde_json", "syn 2.0.39", ] @@ -2117,16 +3432,16 @@ name = "ethers-core" version = "2.0.10" source = "git+https://github.com/gakonst/ethers-rs?rev=4f7b354ecec0aea38ed165560ae4f3224d644c9e#4f7b354ecec0aea38ed165560ae4f3224d644c9e" dependencies = [ - "arrayvec", - "bytes", - "cargo_metadata", + "arrayvec 0.7.4", + "bytes 1.5.0", + "cargo_metadata 0.18.1", "chrono", "const-hex", - "elliptic-curve", - "ethabi", - "generic-array", - "k256", - "num_enum", + "elliptic-curve 0.13.6", + "ethabi 18.0.0", + "generic-array 0.14.7", + "k256 0.13.1", + "num_enum 0.7.0", "once_cell", "open-fastrlp", "rand 0.8.5", @@ -2137,8 +3452,8 @@ dependencies = [ "syn 2.0.39", "tempfile", "thiserror", - "tiny-keccak", - "unicode-xid", + "tiny-keccak 2.0.2", + "unicode-xid 0.2.4", ] [[package]] @@ -2217,7 +3532,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winapi", + "winapi 0.3.9", "ws_stream_wasm", ] @@ -2231,7 +3546,7 @@ dependencies = [ "coins-bip39", "coins-ledger", "const-hex", - "elliptic-curve", + "elliptic-curve 0.13.6", "eth-keystore", "ethers-core", "futures-executor", @@ -2243,7 +3558,7 @@ dependencies = [ "rusoto_kms", "semver 1.0.20", "sha2 0.10.8", - "spki", + "spki 0.7.2", "thiserror", "tracing", "trezor-client", @@ -2254,7 +3569,7 @@ name = "ethers-solc" version = "2.0.10" source = "git+https://github.com/gakonst/ethers-rs?rev=4f7b354ecec0aea38ed165560ae4f3224d644c9e#4f7b354ecec0aea38ed165560ae4f3224d644c9e" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "const-hex", "dirs 5.0.1", "dunce", @@ -2279,7 +3594,7 @@ dependencies = [ "svm-rs-builds", "tempfile", "thiserror", - "tiny-keccak", + "tiny-keccak 2.0.2", "tokio", "tracing", "walkdir", @@ -2312,6 +3627,21 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + [[package]] name = "fastrand" version = "2.0.1" @@ -2324,9 +3654,9 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" dependencies = [ - "arrayvec", + "arrayvec 0.7.4", "auto_impl", - "bytes", + "bytes 1.5.0", "fastrlp-derive", ] @@ -2348,7 +3678,7 @@ version = "3.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "rustix", "windows-sys 0.48.0", ] @@ -2373,6 +3703,16 @@ dependencies = [ "libc", ] +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "ff" version = "0.13.0" @@ -2383,6 +3723,34 @@ dependencies = [ "subtle", ] +[[package]] +name = "ff_ce" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b538e4231443a5b9c507caee3356f016d832cf7393d2d90f03ea3180d4e3fbc" +dependencies = [ + "byteorder", + "ff_derive_ce", + "hex", + "rand 0.4.6", + "serde", +] + +[[package]] +name = "ff_derive_ce" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96fbccd88dbb1fac4ee4a07c2fcc4ca719a74ffbd9d2b9d41d8c8eb073d8b20" +dependencies = [ + "num-bigint 0.4.4", + "num-integer", + "num-traits", + "proc-macro2 1.0.69", + "quote 1.0.33", + "serde", + "syn 1.0.109", +] + [[package]] name = "figment" version = "0.10.12" @@ -2390,7 +3758,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "649f3e5d826594057e9a519626304d8da859ea8a0b18ce99500c586b8d45faee" dependencies = [ "atomic", - "parking_lot", + "parking_lot 0.12.1", "pear", "serde", "tempfile", @@ -2405,43 +3773,79 @@ version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall 0.3.5", "windows-sys 0.48.0", ] [[package]] -name = "fixed-hash" -version = "0.8.0" +name = "findshlibs" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" dependencies = [ - "byteorder", - "rand 0.8.5", - "rustc-hex", - "static_assertions", + "cc", + "lazy_static", + "libc", + "winapi 0.3.9", ] [[package]] -name = "fixedbitset" -version = "0.4.2" +name = "finl_unicode" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" [[package]] -name = "flate2" -version = "1.0.28" +name = "firestorm" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" -dependencies = [ - "crc32fast", - "miniz_oxide", -] +checksum = "2c5f6c2c942da57e2aaaa84b8a521489486f14e75e7fa91dab70aba913975f98" [[package]] -name = "fnv" -version = "1.0.7" +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" @@ -2469,7 +3873,7 @@ dependencies = [ "alloy-primitives", "anvil", "async-trait", - "clap", + "clap 4.4.6", "clap_complete", "clap_complete_fig", "comfy-table", @@ -2490,12 +3894,12 @@ dependencies = [ "foundry-evm", "foundry-test-utils", "foundry-utils", - "futures", + "futures 0.3.28", "globset", "indicatif", "itertools 0.11.0", "once_cell", - "parking_lot", + "parking_lot 0.12.1", "path-slash", "pretty_assertions", "proptest", @@ -2661,7 +4065,7 @@ dependencies = [ "alloy-json-abi", "alloy-primitives", "async-trait", - "clap", + "clap 4.4.6", "color-eyre", "const-hex", "dotenvy", @@ -2701,7 +4105,7 @@ dependencies = [ "alloy-primitives", "async-trait", "auto_impl", - "clap", + "clap 4.4.6", "comfy-table", "const-hex", "dunce", @@ -2727,6 +4131,7 @@ dependencies = [ "url", "walkdir", "yansi 0.5.1", + "zksync-web3-rs", ] [[package]] @@ -2788,7 +4193,7 @@ dependencies = [ "pretty_assertions", "regex", "reqwest", - "revm-primitives", + "revm", "semver 1.0.20", "serde", "serde_json", @@ -2852,6 +4257,7 @@ dependencies = [ "alloy-primitives", "alloy-sol-types", "const-hex", + "era_revm", "ethers", "eyre", "foundry-abi", @@ -2976,7 +4382,7 @@ dependencies = [ "foundry-config", "foundry-utils", "once_cell", - "parking_lot", + "parking_lot 0.12.1", "pretty_assertions", "regex", "serde_json", @@ -3010,6 +4416,68 @@ dependencies = [ "tracing", ] +[[package]] +name = "franklin-crypto" +version = "0.0.5" +source = "git+https://github.com/matter-labs/franklin-crypto?branch=dev#5695d07c7bc604c2c39a27712ffac171d39ee1ed" +dependencies = [ + "arr_macro", + "bellman_ce 0.3.2 (git+https://github.com/matter-labs/bellman?branch=dev)", + "bit-vec", + "blake2 0.9.2", + "blake2-rfc_bellman_edition", + "blake2s_simd", + "byteorder", + "digest 0.9.0", + "hex", + "indexmap 1.9.3", + "itertools 0.10.5", + "lazy_static", + "num-bigint 0.4.4", + "num-derive 0.2.5", + "num-integer", + "num-traits", + "rand 0.4.6", + "serde", + "sha2 0.9.9", + "sha3 0.9.1", + "smallvec", + "splitmut", + "tiny-keccak 1.5.0", +] + +[[package]] +name = "franklin-crypto" +version = "0.0.5" +source = "git+https://github.com/matter-labs/franklin-crypto?branch=snark_wrapper#900332b8c2fe528b5008bb4e6bf2d3f206a9ae56" +dependencies = [ + "arr_macro", + "bellman_ce 0.3.2 (git+https://github.com/matter-labs/bellman?branch=snark-wrapper)", + "bit-vec", + "blake2 0.9.2", + "blake2-rfc_bellman_edition", + "blake2s_simd", + "boojum", + "byteorder", + "derivative", + "digest 0.9.0", + "hex", + "indexmap 1.9.3", + "itertools 0.10.5", + "lazy_static", + "num-bigint 0.4.4", + "num-derive 0.2.5", + "num-integer", + "num-traits", + "rand 0.4.6", + "serde", + "sha2 0.9.9", + "sha3 0.9.1", + "smallvec", + "splitmut", + "tiny-keccak 1.5.0", +] + [[package]] name = "fs2" version = "0.4.3" @@ -3017,7 +4485,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3035,6 +4503,34 @@ dependencies = [ "libc", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags 1.3.2", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + [[package]] name = "funty" version = "2.0.0" @@ -3091,6 +4587,18 @@ dependencies = [ "futures-core", "futures-task", "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-intrusive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604f7a68fbf8103337523b1fadc8ade7361ee3f112f7c680ad179651616aed5" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot 0.11.2", ] [[package]] @@ -3148,6 +4656,7 @@ version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ + "futures 0.1.31", "futures-channel", "futures-core", "futures-io", @@ -3169,6 +4678,15 @@ dependencies = [ "byteorder", ] +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -3186,7 +4704,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] @@ -3197,7 +4715,7 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -3411,7 +4929,7 @@ dependencies = [ "gix-fs", "libc", "once_cell", - "parking_lot", + "parking_lot 0.12.1", "tempfile", ] @@ -3427,7 +4945,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b85d89dc728613e26e0ed952a19583744e7f5240fcd4aa30d6c824ffd8b52f0f" dependencies = [ - "fastrand", + "fastrand 2.0.1", ] [[package]] @@ -3459,6 +4977,27 @@ dependencies = [ "regex", ] +[[package]] +name = "gloo-net" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "gloo-timers" version = "0.2.6" @@ -3471,13 +5010,126 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gloo-utils" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "google-cloud-auth" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644f40175857d0b8d7b6cad6cd9594284da5041387fa2ddff30ab6d8faef65eb" +dependencies = [ + "async-trait", + "base64 0.21.4", + "google-cloud-metadata", + "google-cloud-token", + "home", + "jsonwebtoken", + "reqwest", + "serde", + "serde_json", + "thiserror", + "time", + "tokio", + "tracing", + "urlencoding", +] + +[[package]] +name = "google-cloud-metadata" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96e4ad0802d3f416f62e7ce01ac1460898ee0efc98f8b45cd4aab7611607012f" +dependencies = [ + "reqwest", + "thiserror", + "tokio", +] + +[[package]] +name = "google-cloud-storage" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "215abab97e07d144428425509c1dad07e57ea72b84b21bcdb6a8a5f12a5c4932" +dependencies = [ + "async-stream", + "base64 0.21.4", + "bytes 1.5.0", + "futures-util", + "google-cloud-auth", + "google-cloud-metadata", + "google-cloud-token", + "hex", + "once_cell", + "percent-encoding", + "regex", + "reqwest", + "ring 0.16.20", + "rsa", + "serde", + "serde_json", + "sha2 0.10.8", + "thiserror", + "time", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "google-cloud-token" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcd62eb34e3de2f085bcc33a09c3e17c4f65650f36d53eb328b00d63bcb536a" +dependencies = [ + "async-trait", +] + +[[package]] +name = "governor" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19775995ee20209163239355bc3ad2f33f83da35d9ef72dea26e5af753552c87" +dependencies = [ + "dashmap", + "futures 0.3.28", + "futures-timer", + "no-std-compat", + "nonzero_ext", + "parking_lot 0.12.1", + "quanta 0.9.3", + "rand 0.8.5", + "smallvec", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "group" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "ff", + "ff 0.13.0", "rand_core 0.6.4", "subtle", ] @@ -3488,7 +5140,7 @@ version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ - "bytes", + "bytes 1.5.0", "fnv", "futures-core", "futures-sink", @@ -3497,7 +5149,7 @@ dependencies = [ "indexmap 1.9.3", "slab", "tokio", - "tokio-util", + "tokio-util 0.7.9", "tracing", ] @@ -3536,6 +5188,15 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash 0.7.6", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -3565,6 +5226,25 @@ dependencies = [ "fxhash", ] +[[package]] +name = "hashlink" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" +dependencies = [ + "hashbrown 0.11.2", +] + +[[package]] +name = "hdrhistogram" +version = "7.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f19b9f54f7c7f55e31401bb647626ce0cf0f67b0004982ce815b3ee72a02aa8" +dependencies = [ + "byteorder", + "num-traits", +] + [[package]] name = "headers" version = "0.3.9" @@ -3589,19 +5269,40 @@ dependencies = [ "http", ] +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" - -[[package]] +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3628,13 +5329,32 @@ dependencies = [ "rusb", ] +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac 0.12.1", +] + +[[package]] +name = "hmac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" +dependencies = [ + "crypto-mac 0.10.1", + "digest 0.9.0", +] + [[package]] name = "hmac" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" dependencies = [ - "crypto-mac", + "crypto-mac 0.11.1", "digest 0.9.0", ] @@ -3656,6 +5376,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi 0.3.9", +] + [[package]] name = "html5ever" version = "0.26.0" @@ -3665,8 +5396,8 @@ dependencies = [ "log", "mac", "markup5ever", - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] @@ -3676,7 +5407,7 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ - "bytes", + "bytes 1.5.0", "fnv", "itoa", ] @@ -3687,7 +5418,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ - "bytes", + "bytes 1.5.0", "http", "pin-project-lite", ] @@ -3722,7 +5453,7 @@ version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ - "bytes", + "bytes 1.5.0", "futures-channel", "futures-core", "futures-util", @@ -3775,7 +5506,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "bytes", + "bytes 1.5.0", "hyper", "native-tls", "tokio", @@ -3805,6 +5536,12 @@ dependencies = [ "cc", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.4.0" @@ -3839,7 +5576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a4d73056a8d335492938cabeef794f38968ef43e6db9bc946638cfd6281003b" dependencies = [ "dunce", - "futures", + "futures 0.3.28", "gix-config", "ignore", "miette", @@ -3850,13 +5587,22 @@ dependencies = [ "tracing", ] +[[package]] +name = "impl-codec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" +dependencies = [ + "parity-scale-codec 2.3.1", +] + [[package]] name = "impl-codec" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 3.6.5", ] [[package]] @@ -3868,6 +5614,15 @@ dependencies = [ "rlp", ] +[[package]] +name = "impl-serde" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +dependencies = [ + "serde", +] + [[package]] name = "impl-serde" version = "0.4.0" @@ -3883,8 +5638,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] @@ -3900,7 +5655,7 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "autocfg", + "autocfg 1.1.0", "hashbrown 0.12.3", ] @@ -3965,7 +5720,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -3974,7 +5729,16 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", ] [[package]] @@ -3989,7 +5753,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.3", "rustix", "windows-sys 0.48.0", ] @@ -4048,170 +5812,483 @@ dependencies = [ ] [[package]] -name = "jsonwebtoken" -version = "8.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" +name = "jsonrpc-client-transports" +version = "18.0.0" +source = "git+https://github.com/matter-labs/jsonrpc.git?branch=master#12c53e3e20c09c2fb9966a4ef1b0ea63de172540" dependencies = [ - "base64 0.21.5", - "pem", - "ring 0.16.20", + "derive_more", + "futures 0.3.28", + "jsonrpc-core 18.0.0 (git+https://github.com/matter-labs/jsonrpc.git?branch=master)", + "jsonrpc-pubsub", + "log", "serde", "serde_json", - "simple_asn1", ] [[package]] -name = "k256" -version = "0.13.1" +name = "jsonrpc-core" +version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "once_cell", - "sha2 0.10.8", - "signature", + "futures 0.3.28", + "futures-executor", + "futures-util", + "log", + "serde", + "serde_derive", + "serde_json", ] [[package]] -name = "keccak" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +name = "jsonrpc-core" +version = "18.0.0" +source = "git+https://github.com/matter-labs/jsonrpc.git?branch=master#12c53e3e20c09c2fb9966a4ef1b0ea63de172540" dependencies = [ - "cpufeatures", + "futures 0.3.28", + "futures-executor", + "futures-util", + "log", + "serde", + "serde_derive", + "serde_json", ] [[package]] -name = "keccak-hasher" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711adba9940a039f4374fc5724c0a5eaca84a2d558cce62256bfe26f0dbef05e" +name = "jsonrpc-core-client" +version = "18.0.0" +source = "git+https://github.com/matter-labs/jsonrpc.git?branch=master#12c53e3e20c09c2fb9966a4ef1b0ea63de172540" dependencies = [ - "hash-db", - "hash256-std-hasher", - "tiny-keccak", + "futures 0.3.28", + "jsonrpc-client-transports", ] [[package]] -name = "kqueue" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +name = "jsonrpc-derive" +version = "18.0.0" +source = "git+https://github.com/matter-labs/jsonrpc.git?branch=master#12c53e3e20c09c2fb9966a4ef1b0ea63de172540" dependencies = [ - "kqueue-sys", - "libc", + "proc-macro-crate 0.1.5", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] -name = "kqueue-sys" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +name = "jsonrpc-http-server" +version = "18.0.0" +source = "git+https://github.com/matter-labs/jsonrpc.git?branch=master#12c53e3e20c09c2fb9966a4ef1b0ea63de172540" dependencies = [ - "bitflags 1.3.2", - "libc", + "futures 0.3.28", + "hyper", + "jsonrpc-core 18.0.0 (git+https://github.com/matter-labs/jsonrpc.git?branch=master)", + "jsonrpc-server-utils", + "log", + "net2", + "parking_lot 0.11.2", + "unicase", ] [[package]] -name = "lalrpop" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da4081d44f4611b66c6dd725e6de3169f9f63905421e8626fcb86b6a898998b8" +name = "jsonrpc-pubsub" +version = "18.0.0" +source = "git+https://github.com/matter-labs/jsonrpc.git?branch=master#12c53e3e20c09c2fb9966a4ef1b0ea63de172540" dependencies = [ - "ascii-canvas", - "bit-set", - "diff", - "ena", - "is-terminal", - "itertools 0.10.5", - "lalrpop-util", - "petgraph", - "regex", - "regex-syntax 0.7.5", - "string_cache", - "term", - "tiny-keccak", - "unicode-xid", + "futures 0.3.28", + "jsonrpc-core 18.0.0 (git+https://github.com/matter-labs/jsonrpc.git?branch=master)", + "lazy_static", + "log", + "parking_lot 0.11.2", + "rand 0.7.3", + "serde", ] [[package]] -name = "lalrpop-util" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f35c735096c0293d313e8f2a641627472b83d01b937177fe76e5e2708d31e0d" +name = "jsonrpc-server-utils" +version = "18.0.0" +source = "git+https://github.com/matter-labs/jsonrpc.git?branch=master#12c53e3e20c09c2fb9966a4ef1b0ea63de172540" +dependencies = [ + "bytes 1.5.0", + "futures 0.3.28", + "globset", + "jsonrpc-core 18.0.0 (git+https://github.com/matter-labs/jsonrpc.git?branch=master)", + "lazy_static", + "log", + "tokio", + "tokio-stream", + "tokio-util 0.6.10", + "unicase", +] [[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +name = "jsonrpc-ws-server" +version = "18.0.0" +source = "git+https://github.com/matter-labs/jsonrpc.git?branch=master#12c53e3e20c09c2fb9966a4ef1b0ea63de172540" dependencies = [ - "spin 0.5.2", + "futures 0.3.28", + "jsonrpc-core 18.0.0 (git+https://github.com/matter-labs/jsonrpc.git?branch=master)", + "jsonrpc-server-utils", + "log", + "parity-ws", + "parking_lot 0.11.2", + "slab", ] [[package]] -name = "lazycell" -version = "1.3.0" +name = "jsonrpsee" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +checksum = "e5f3783308bddc49d0218307f66a09330c106fbd792c58bac5c8dc294fdd0f98" +dependencies = [ + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-http-client", + "jsonrpsee-proc-macros", + "jsonrpsee-server", + "jsonrpsee-types", + "jsonrpsee-wasm-client", + "jsonrpsee-ws-client", + "tracing", +] [[package]] -name = "libc" -version = "0.2.150" +name = "jsonrpsee-client-transport" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "abc5630e4fa0096f00ec7b44d520701fda4504170cb85e22dca603ae5d7ad0d7" +dependencies = [ + "futures-channel", + "futures-util", + "gloo-net", + "http", + "jsonrpsee-core", + "pin-project", + "rustls-native-certs", + "soketto", + "thiserror", + "tokio", + "tokio-rustls 0.24.1", + "tokio-util 0.7.9", + "tracing", + "webpki-roots 0.24.0", +] [[package]] -name = "libgit2-sys" -version = "0.16.1+1.7.1" +name = "jsonrpsee-core" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2a2bb3680b094add03bb3732ec520ece34da31a8cd2d633d1389d0f0fb60d0c" +checksum = "5aaa4c4d5fb801dcc316d81f76422db259809037a86b3194ae538dd026b05ed7" dependencies = [ - "cc", - "libc", - "libz-sys", - "pkg-config", + "anyhow", + "async-lock", + "async-trait", + "beef", + "futures-timer", + "futures-util", + "globset", + "hyper", + "jsonrpsee-types", + "parking_lot 0.12.1", + "rand 0.8.5", + "rustc-hash", + "serde", + "serde_json", + "soketto", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "wasm-bindgen-futures", ] [[package]] -name = "libloading" -version = "0.7.4" +name = "jsonrpsee-http-client" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "aa7165efcbfbc951d180162ff28fe91b657ed81925e37a35e4a396ce12109f96" dependencies = [ - "cfg-if", - "winapi", + "async-trait", + "hyper", + "hyper-rustls 0.24.1", + "jsonrpsee-core", + "jsonrpsee-types", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "tracing", ] [[package]] -name = "libm" -version = "0.2.8" +name = "jsonrpsee-proc-macros" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "21dc12b1d4f16a86e8c522823c4fab219c88c03eb7c924ec0501a64bf12e058b" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 1.3.1", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] [[package]] -name = "libredox" -version = "0.0.1" +name = "jsonrpsee-server" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "6e79d78cfd5abd8394da10753723093c3ff64391602941c9c4b1d80a3414fd53" dependencies = [ - "bitflags 2.4.1", - "libc", - "redox_syscall 0.4.1", + "futures-util", + "hyper", + "jsonrpsee-core", + "jsonrpsee-types", + "serde", + "serde_json", + "soketto", + "tokio", + "tokio-stream", + "tokio-util 0.7.9", + "tower", + "tracing", ] [[package]] -name = "libusb1-sys" -version = "0.6.4" +name = "jsonrpsee-types" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d0e2afce4245f2c9a418511e5af8718bcaf2fa408aefb259504d1a9cb25f27" +checksum = "00aa7cc87bc42e04e26c8ac3e7186142f7fd2949c763d9b6a7e64a69672d8fb2" dependencies = [ - "cc", - "libc", - "pkg-config", + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "jsonrpsee-wasm-client" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fe953c2801356f214d3f4051f786b3d11134512a46763ee8c39a9e3fa2cc1c0" +dependencies = [ + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", +] + +[[package]] +name = "jsonrpsee-ws-client" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c71b2597ec1c958c6d5bc94bb61b44d74eb28e69dc421731ab0035706f13882" +dependencies = [ + "http", + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", +] + +[[package]] +name = "jsonwebtoken" +version = "8.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" +dependencies = [ + "base64 0.21.5", + "pem", + "ring 0.16.20", + "serde", + "serde_json", + "simple_asn1", +] + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.8", +] + +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa 0.16.8", + "elliptic-curve 0.13.6", + "once_cell", + "sha2 0.10.8", + "signature 2.1.0", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-hasher" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711adba9940a039f4374fc5724c0a5eaca84a2d558cce62256bfe26f0dbef05e" +dependencies = [ + "hash-db", + "hash256-std-hasher", + "tiny-keccak 2.0.2", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + +[[package]] +name = "lalrpop" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da4081d44f4611b66c6dd725e6de3169f9f63905421e8626fcb86b6a898998b8" +dependencies = [ + "ascii-canvas", + "bit-set", + "diff", + "ena", + "is-terminal", + "itertools 0.10.5", + "lalrpop-util", + "petgraph", + "regex", + "regex-syntax 0.7.5", + "string_cache", + "term", + "tiny-keccak 2.0.2", + "unicode-xid 0.2.4", +] + +[[package]] +name = "lalrpop-util" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f35c735096c0293d313e8f2a641627472b83d01b937177fe76e5e2708d31e0d" + +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "libgit2-sys" +version = "0.16.1+1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2a2bb3680b094add03bb3732ec520ece34da31a8cd2d633d1389d0f0fb60d0c" +dependencies = [ + "cc", + "libc", + "libz-sys", + "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if 1.0.0", + "winapi 0.3.9", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall 0.4.1", +] + +[[package]] +name = "libusb1-sys" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d0e2afce4245f2c9a418511e5af8718bcaf2fa408aefb259504d1a9cb25f27" +dependencies = [ + "cc", + "libc", + "pkg-config", "vcpkg", ] @@ -4227,6 +6304,26 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linkme" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ed2ee9464ff9707af8e9ad834cffa4802f072caad90639c583dd3c62e6e608" +dependencies = [ + "linkme-impl", +] + +[[package]] +name = "linkme-impl" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba125974b109d512fccbc6c0244e7580143e460895dfd6ea7f8bbb692fd94396" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + [[package]] name = "linux-raw-sys" version = "0.4.10" @@ -4239,7 +6336,24 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ - "autocfg", + "futures-core", + "futures-sink", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg 1.1.0", "scopeguard", ] @@ -4255,6 +6369,24 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "mach2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8" +dependencies = [ + "libc", +] + [[package]] name = "maplit" version = "1.0.2" @@ -4275,6 +6407,12 @@ dependencies = [ "tendril", ] +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + [[package]] name = "matchers" version = "0.1.0" @@ -4296,6 +6434,18 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + [[package]] name = "md-5" version = "0.9.1" @@ -4304,7 +6454,7 @@ checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" dependencies = [ "block-buffer 0.9.0", "digest 0.9.0", - "opaque-debug", + "opaque-debug 0.3.0", ] [[package]] @@ -4313,7 +6463,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "digest 0.10.7", ] @@ -4326,10 +6476,10 @@ dependencies = [ "ammonia", "anyhow", "chrono", - "clap", + "clap 4.4.6", "clap_complete", "elasticlunr-rs", - "env_logger", + "env_logger 0.10.0", "handlebars", "log", "memchr", @@ -4360,13 +6510,22 @@ dependencies = [ "libc", ] +[[package]] +name = "memoffset" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" +dependencies = [ + "autocfg 1.1.0", +] + [[package]] name = "memoffset" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" dependencies = [ - "autocfg", + "autocfg 1.1.0", ] [[package]] @@ -4375,7 +6534,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ - "autocfg", + "autocfg 1.1.0", ] [[package]] @@ -4390,7 +6549,62 @@ dependencies = [ ] [[package]] -name = "miette" +name = "metrics" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde3af1a009ed76a778cb84fdef9e7dbbdf5775ae3e4cc1f434a6a307f6f76c5" +dependencies = [ + "ahash 0.8.3", + "metrics-macros", + "portable-atomic", +] + +[[package]] +name = "metrics-exporter-prometheus" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a4964177ddfdab1e3a2b37aec7cf320e14169abb0ed73999f558136409178d5" +dependencies = [ + "base64 0.21.4", + "hyper", + "indexmap 1.9.3", + "ipnet", + "metrics", + "metrics-util", + "quanta 0.11.1", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "metrics-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddece26afd34c31585c74a4db0630c376df271c285d682d1e55012197830b6df" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "metrics-util" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4de2ed6e491ed114b40b732e4d1659a9d53992ebd87490c44a6ffe23739d973e" +dependencies = [ + "crossbeam-epoch 0.9.15", + "crossbeam-utils 0.8.16", + "hashbrown 0.13.1", + "metrics", + "num_cpus", + "quanta 0.11.1", + "sketches-ddsketch", +] + +[[package]] +name = "miette" version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e" @@ -4428,6 +6642,21 @@ dependencies = [ "unicase", ] +[[package]] +name = "mini-moka" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e0b72e7c9042467008b10279fc732326bd605459ae03bda88825909dd19b56" +dependencies = [ + "crossbeam-channel 0.5.8", + "crossbeam-utils 0.8.16", + "dashmap", + "skeptic", + "smallvec", + "tagptr", + "triomphe", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -4455,6 +6684,51 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "mio-extras" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" +dependencies = [ + "lazycell", + "log", + "mio 0.6.23", + "slab", +] + +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "multivm" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "anyhow", + "hex", + "itertools 0.10.5", + "once_cell", + "thiserror", + "tracing", + "vise", + "zk_evm 1.3.1", + "zk_evm 1.3.3 (git+https://github.com/matter-labs/era-zk_evm.git?tag=v1.3.3-rc1)", + "zksync_contracts", + "zksync_state", + "zksync_system_constants", + "zksync_types", + "zksync_utils", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -4473,6 +6747,17 @@ dependencies = [ "tempfile", ] +[[package]] +name = "net2" +version = "0.2.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -4495,7 +6780,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ "bitflags 1.3.2", - "cfg-if", + "cfg-if 1.0.0", "libc", "memoffset 0.7.1", "pin-utils", @@ -4512,6 +6797,18 @@ dependencies = [ "libc", ] +[[package]] +name = "no-std-compat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + [[package]] name = "nom" version = "7.1.3" @@ -4522,6 +6819,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonzero_ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" + [[package]] name = "normalize-path" version = "0.2.1" @@ -4544,13 +6847,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "729f63e1ca555a43fe3efa4f3efdf4801c479da85b432242a7b726f353c88486" dependencies = [ "bitflags 1.3.2", - "crossbeam-channel", + "crossbeam-channel 0.5.8", "filetime", "fsevent-sys", "inotify", "kqueue", "libc", - "mio", + "mio 0.8.8", "walkdir", "windows-sys 0.45.0", ] @@ -4562,7 +6865,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ "overload", - "winapi", + "winapi 0.3.9", +] + +[[package]] +name = "num" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f" +dependencies = [ + "num-bigint 0.3.3", + "num-complex 0.3.1", + "num-integer", + "num-iter", + "num-rational 0.3.2", + "num-traits", ] [[package]] @@ -4571,12 +6888,24 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" dependencies = [ - "num-bigint", - "num-complex", + "num-bigint 0.4.4", + "num-complex 0.4.4", "num-integer", "num-iter", - "num-rational", + "num-rational 0.4.1", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" +dependencies = [ + "autocfg 1.1.0", + "num-integer", "num-traits", + "serde", ] [[package]] @@ -4585,9 +6914,37 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ - "autocfg", + "autocfg 1.1.0", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-complex" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5" +dependencies = [ "num-traits", + "serde", ] [[package]] @@ -4599,13 +6956,35 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-derive" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eafd0b45c5537c3ba526f79d3e75120036502bebacbb3f3220914067ce39dbf2" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", +] + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "num-integer" version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "autocfg", + "autocfg 1.1.0", "num-traits", ] @@ -4615,9 +6994,32 @@ version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" dependencies = [ - "autocfg", + "autocfg 1.1.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-modular" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a5fe11d4135c3bcdf3a95b18b194afa9608a5f6ff034f5d857bc9a27fb0119" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg 1.1.0", + "num-bigint 0.3.3", "num-integer", "num-traits", + "serde", ] [[package]] @@ -4626,8 +7028,8 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ - "autocfg", - "num-bigint", + "autocfg 1.1.0", + "num-bigint 0.4.4", "num-integer", "num-traits", ] @@ -4638,7 +7040,7 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ - "autocfg", + "autocfg 1.1.0", "libm", ] @@ -4648,7 +7050,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.3", "libc", ] @@ -4658,7 +7060,19 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" dependencies = [ - "num_enum_derive", + "num_enum_derive 0.7.0", +] + +[[package]] +name = "num_enum_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", ] [[package]] @@ -4709,6 +7123,12 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + [[package]] name = "opaque-debug" version = "0.3.0" @@ -4721,10 +7141,10 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" dependencies = [ - "arrayvec", + "arrayvec 0.7.4", "auto_impl", - "bytes", - "ethereum-types", + "bytes 1.5.0", + "ethereum-types 0.14.1", "open-fastrlp-derive", ] @@ -4734,9 +7154,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" dependencies = [ - "bytes", - "proc-macro2", - "quote", + "bytes 1.5.0", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] @@ -4748,7 +7168,7 @@ checksum = "6c62dcb6174f9cb326eac248f07e955d5d559c272730b6c03e396b443b562788" dependencies = [ "bstr", "normpath", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -4783,6 +7203,15 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-src" +version = "300.1.5+3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "559068e4c12950d7dcaa1857a61725c0d38d4fc03ff8e070ab31a75d6e316491" +dependencies = [ + "cc", +] + [[package]] name = "openssl-sys" version = "0.9.95" @@ -4791,6 +7220,7 @@ checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9" dependencies = [ "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] @@ -4810,6 +7240,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "os_info" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e" +dependencies = [ + "log", + "serde", + "winapi 0.3.9", +] + [[package]] name = "overload" version = "0.1.1" @@ -4822,20 +7263,105 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[package]] +name = "packed_simd" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f9f08af0c877571712e2e3e686ad79efad9657dbf0f7c3c8ba943ff6c38932d" +dependencies = [ + "cfg-if 1.0.0", + "num-traits", +] + +[[package]] +name = "pairing_ce" +version = "0.28.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db007b21259660d025918e653508f03050bf23fb96a88601f9936329faadc597" +dependencies = [ + "byteorder", + "cfg-if 1.0.0", + "ff_ce", + "rand 0.4.6", + "serde", +] + +[[package]] +name = "pairing_ce" +version = "0.28.5" +source = "git+https://github.com/matter-labs/pairing.git#d06c2a112913b0abfb75996cc29a6b6075717e99" +dependencies = [ + "byteorder", + "cfg-if 1.0.0", + "ff_ce", + "rand 0.4.6", + "serde", +] + +[[package]] +name = "parity-crypto" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b92ea9ddac0d6e1db7c49991e7d397d34a9fd814b4c93cda53788e8eef94e35" +dependencies = [ + "aes 0.6.0", + "aes-ctr", + "block-modes", + "digest 0.9.0", + "ethereum-types 0.12.1", + "hmac 0.10.1", + "lazy_static", + "pbkdf2 0.7.5", + "ripemd160", + "rustc-hex", + "scrypt 0.5.0", + "secp256k1 0.20.3", + "sha2 0.9.9", + "subtle", + "tiny-keccak 2.0.2", + "zeroize", +] + +[[package]] +name = "parity-scale-codec" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" +dependencies = [ + "arrayvec 0.7.4", + "bitvec 0.20.4", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive 2.3.1", + "serde", +] + [[package]] name = "parity-scale-codec" version = "3.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" dependencies = [ - "arrayvec", - "bitvec", + "arrayvec 0.7.4", + "bitvec 1.0.1", "byte-slice-cast", "impl-trait-for-tuples", - "parity-scale-codec-derive", + "parity-scale-codec-derive 3.6.5", "serde", ] +[[package]] +name = "parity-scale-codec-derive" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "parity-scale-codec-derive" version = "3.6.5" @@ -4854,12 +7380,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9981e32fb75e004cc148f5fb70342f393830e0a4aa62e3cc93b50976218d42b6" dependencies = [ - "futures", + "futures 0.3.28", "libc", "log", "rand 0.7.3", "tokio", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -4868,12 +7394,12 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c32561d248d352148124f036cac253a644685a21dc9fea383eb4907d7bd35a8f" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "hashbrown 0.12.3", "impl-trait-for-tuples", "parity-util-mem-derive", - "parking_lot", - "winapi", + "parking_lot 0.12.1", + "winapi 0.3.9", ] [[package]] @@ -4882,34 +7408,74 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.69", "syn 1.0.109", "synstructure", ] [[package]] -name = "parking_lot" -version = "0.12.1" +name = "parity-ws" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "5983d3929ad50f12c3eb9a6743f19d691866ecd44da74c0a3308c3f8a56df0c6" dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" + "byteorder", + "bytes 0.4.12", + "httparse", + "log", + "mio 0.6.23", + "mio-extras", + "rand 0.7.3", + "sha-1 0.8.2", + "slab", + "url", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.9", +] + +[[package]] +name = "parking_lot_core" version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", + "instant", "libc", "redox_syscall 0.4.1", "smallvec", "windows-targets 0.48.5", ] +[[package]] +name = "password-hash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54986aa4bfc9b98c6a5f40184223658d187159d7b3c6af33f2b2aa25ae1db0fa" +dependencies = [ + "base64ct", + "rand_core 0.6.4", +] + [[package]] name = "password-hash" version = "0.4.2" @@ -4933,6 +7499,28 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" +[[package]] +name = "pbkdf2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3b8c0d71734018084da0c0354193a5edfb81b20d2d57a92c5b154aefc554a4a" +dependencies = [ + "crypto-mac 0.10.1", +] + +[[package]] +name = "pbkdf2" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf916dd32dd26297907890d99dc2740e33f6bd9073965af4ccff2967962f5508" +dependencies = [ + "base64ct", + "crypto-mac 0.10.1", + "hmac 0.10.1", + "password-hash 0.1.4", + "sha2 0.9.9", +] + [[package]] name = "pbkdf2" version = "0.11.0" @@ -4941,7 +7529,7 @@ checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ "digest 0.10.7", "hmac 0.12.1", - "password-hash", + "password-hash 0.4.2", "sha2 0.10.8", ] @@ -4972,7 +7560,7 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da9f0f13dac8069c139e8300a6510e3f4143ecf5259c60b116a9b271b4ca0d54" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.69", "proc-macro2-diagnostics", "quote", "syn 2.0.39", @@ -4993,6 +7581,15 @@ dependencies = [ "base64 0.13.1", ] +[[package]] +name = "pem-rfc7468" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01de5d978f34aa4b2296576379fcc416034702fd94117c56ffd8a1a767cefb30" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.0" @@ -5060,7 +7657,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" dependencies = [ - "futures", + "futures 0.3.28", "rustc_version 0.4.0", ] @@ -5186,14 +7783,46 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a78f66c04ccc83dd4486fd46c33896f4e17b24a7a3a6400dedc48ed0ddd72320" +dependencies = [ + "der 0.5.1", + "pkcs8 0.8.0", + "zeroize", +] + +[[package]] +name = "pkcs8" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +dependencies = [ + "der 0.5.1", + "spki 0.5.4", + "zeroize", +] + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der 0.6.1", + "spki 0.6.0", +] + [[package]] name = "pkcs8" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der", - "spki", + "der 0.7.8", + "spki 0.7.2", ] [[package]] @@ -5274,20 +7903,42 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "primitive-types" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" +dependencies = [ + "fixed-hash 0.7.0", + "impl-codec 0.5.1", + "impl-rlp", + "impl-serde 0.3.2", + "uint", +] + [[package]] name = "primitive-types" version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ - "fixed-hash", - "impl-codec", + "fixed-hash 0.8.0", + "impl-codec 0.6.0", "impl-rlp", - "impl-serde", + "impl-serde 0.4.0", "scale-info", "uint", ] +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml 0.5.11", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -5314,8 +7965,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", "version_check", ] @@ -5326,11 +7977,26 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "version_check", ] +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", +] + [[package]] name = "proc-macro2" version = "1.0.69" @@ -5359,11 +8025,47 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "629e0d57f265ca8238345cb616eea8847b8ecb86b5d97d155be2c8963a314379" dependencies = [ - "futures", + "futures 0.3.28", "tokio", "tokio-stream", ] +[[package]] +name = "prometheus-client" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c99afa9a01501019ac3a14d71d9f94050346f55ca471ce90c799a15c58f61e2" +dependencies = [ + "dtoa", + "itoa", + "parking_lot 0.12.1", + "prometheus-client-derive-encode", +] + +[[package]] +name = "prometheus-client-derive-encode" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "prometheus_exporter" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "anyhow", + "metrics", + "metrics-exporter-prometheus", + "tokio", + "vise", + "vise-exporter", +] + [[package]] name = "proptest" version = "1.3.1" @@ -5377,7 +8079,7 @@ dependencies = [ "num-traits", "rand 0.8.5", "rand_chacha 0.3.1", - "rand_xorshift", + "rand_xorshift 0.3.0", "regex-syntax 0.7.5", "rusty-fork", "tempfile", @@ -5390,8 +8092,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cf16337405ca084e9c78985114633b6827711d22b9e6ef6c6c0d665eb3f0b6e" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] @@ -5426,21 +8128,68 @@ dependencies = [ "unicase", ] +[[package]] +name = "quanta" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20afe714292d5e879d8b12740aa223c6a88f118af41870e8b6196e39a02238a8" +dependencies = [ + "crossbeam-utils 0.8.16", + "libc", + "mach", + "once_cell", + "raw-cpuid", + "wasi 0.10.2+wasi-snapshot-preview1", + "web-sys", + "winapi 0.3.9", +] + +[[package]] +name = "quanta" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17e662a7a8291a865152364c20c7abc5e60486ab2001e8ec10b24862de0b9ab" +dependencies = [ + "crossbeam-utils 0.8.16", + "libc", + "mach2", + "once_cell", + "raw-cpuid", + "wasi 0.11.0+wasi-snapshot-preview1", + "web-sys", + "winapi 0.3.9", +] + [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", +] + [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.69", ] +[[package]] +name = "radium" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" + [[package]] name = "radium" version = "0.7.0" @@ -5457,6 +8206,38 @@ dependencies = [ "nibble_vec", ] +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi 0.3.9", +] + +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +dependencies = [ + "autocfg 0.1.8", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc 0.1.0", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg", + "rand_xorshift 0.1.1", + "winapi 0.3.9", +] + [[package]] name = "rand" version = "0.7.3" @@ -5467,7 +8248,7 @@ dependencies = [ "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", - "rand_hc", + "rand_hc 0.2.0", ] [[package]] @@ -5481,6 +8262,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.3.1", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -5501,6 +8292,21 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.5.1" @@ -5519,6 +8325,15 @@ dependencies = [ "getrandom 0.2.10", ] +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -5528,6 +8343,59 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +dependencies = [ + "libc", + "rand_core 0.4.2", + "winapi 0.3.9", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi 0.3.9", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.4.2", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "rand_xorshift" version = "0.3.0" @@ -5554,6 +8422,15 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "raw-cpuid" +version = "10.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "rayon" version = "1.8.0" @@ -5570,8 +8447,17 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ - "crossbeam-deque", - "crossbeam-utils", + "crossbeam-deque 0.8.3", + "crossbeam-utils 0.8.16", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", ] [[package]] @@ -5612,7 +8498,7 @@ dependencies = [ "hash-db", "hash256-std-hasher", "keccak-hasher", - "parity-scale-codec", + "parity-scale-codec 3.6.5", "trie-db", "trie-root", ] @@ -5688,6 +8574,7 @@ dependencies = [ "js-sys", "log", "mime", + "mime_guess", "native-tls", "once_cell", "percent-encoding", @@ -5702,20 +8589,63 @@ dependencies = [ "tokio", "tokio-native-tls", "tokio-rustls 0.24.1", + "tokio-util 0.7.9", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", - "webpki-roots", + "webpki-roots 0.25.2", "winreg", ] +[[package]] +name = "rescue_poseidon" +version = "0.4.1" +source = "git+https://github.com/matter-labs/rescue-poseidon.git?branch=poseidon2#c4a788471710bdb7aa0f59e8756b45ef93cdd2b2" +dependencies = [ + "addchain", + "arrayvec 0.7.4", + "blake2 0.10.6 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "derivative", + "franklin-crypto 0.0.5 (git+https://github.com/matter-labs/franklin-crypto?branch=snark_wrapper)", + "log", + "num-bigint 0.3.3", + "num-integer", + "num-iter", + "num-traits", + "rand 0.4.6", + "serde", + "sha3 0.9.1", + "smallvec", +] + +[[package]] +name = "rescue_poseidon" +version = "0.4.1" +source = "git+https://github.com/matter-labs/rescue-poseidon#d059b5042df5ed80e151f05751410b524a54d16c" +dependencies = [ + "addchain", + "arrayvec 0.7.4", + "blake2 0.10.6 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "franklin-crypto 0.0.5 (git+https://github.com/matter-labs/franklin-crypto?branch=dev)", + "num-bigint 0.3.3", + "num-integer", + "num-iter", + "num-traits", + "rand 0.4.6", + "serde", + "sha3 0.9.1", + "smallvec", +] + [[package]] name = "revm" version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f4ca8ae0345104523b4af1a8a7ea97cfa1865cdb7a7c25d23c1a18d9b48598" +source = "git+https://github.com/dutterbutter/revm.git?tag=sha3_0.10.6#7b96ba436a76eee078fa9fa95b5ef5ba6dbd24f0" dependencies = [ "auto_impl", "revm-interpreter", @@ -5727,8 +8657,7 @@ dependencies = [ [[package]] name = "revm-interpreter" version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f959cafdf64a7f89b014fa73dc2325001cf654b3d9400260b212d19a2ebe3da0" +source = "git+https://github.com/dutterbutter/revm.git?tag=sha3_0.10.6#7b96ba436a76eee078fa9fa95b5ef5ba6dbd24f0" dependencies = [ "revm-primitives", "serde", @@ -5737,25 +8666,24 @@ dependencies = [ [[package]] name = "revm-precompile" version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d360a88223d85709d2e95d4609eb1e19c649c47e28954bfabae5e92bb37e83e" +source = "git+https://github.com/dutterbutter/revm.git?tag=sha3_0.10.6#7b96ba436a76eee078fa9fa95b5ef5ba6dbd24f0" dependencies = [ "c-kzg", - "k256", - "num", + "k256 0.13.1", + "num 0.4.1", "once_cell", "revm-primitives", "ripemd", - "secp256k1", + "secp256k1 0.27.0", "sha2 0.10.8", + "sha3 0.10.8", "substrate-bn", ] [[package]] name = "revm-primitives" version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51187b852d9e458816a2e19c81f1dd6c924077e1a8fccd16e4f044f865f299d7" +source = "git+https://github.com/dutterbutter/revm.git?tag=sha3_0.10.6#7b96ba436a76eee078fa9fa95b5ef5ba6dbd24f0" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -5766,9 +8694,21 @@ dependencies = [ "enumn", "hashbrown 0.14.2", "hex", + "once_cell", "serde", ] +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac 0.12.1", + "zeroize", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -5791,7 +8731,7 @@ dependencies = [ "spin 0.5.2", "untrusted 0.7.1", "web-sys", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -5817,13 +8757,24 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "ripemd160" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + [[package]] name = "rlp" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" dependencies = [ - "bytes", + "bytes 1.5.0", "rlp-derive", "rustc-hex", ] @@ -5834,11 +8785,21 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] +[[package]] +name = "rocksdb" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe" +dependencies = [ + "libc", + "librocksdb-sys", +] + [[package]] name = "rpassword" version = "7.2.0" @@ -5847,7 +8808,27 @@ checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" dependencies = [ "libc", "rtoolbox", - "winapi", + "winapi 0.3.9", +] + +[[package]] +name = "rsa" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf22754c49613d2b3b119f0e5d46e34a2c628a937e3024b8762de4e7d8c710b" +dependencies = [ + "byteorder", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits", + "pkcs1", + "pkcs8 0.8.0", + "rand_core 0.6.4", + "smallvec", + "subtle", + "zeroize", ] [[package]] @@ -5857,7 +8838,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -5870,7 +8851,7 @@ dependencies = [ "arbitrary", "ark-ff 0.3.0", "ark-ff 0.4.2", - "bytes", + "bytes 1.5.0", "fastrlp", "num-bigint", "num-traits", @@ -5909,9 +8890,9 @@ checksum = "1db30db44ea73551326269adcf7a2169428a054f14faf9e1768f2163494f2fa2" dependencies = [ "async-trait", "base64 0.13.1", - "bytes", + "bytes 1.5.0", "crc32fast", - "futures", + "futures 0.3.28", "http", "hyper", "hyper-rustls 0.23.2", @@ -5935,7 +8916,7 @@ dependencies = [ "async-trait", "chrono", "dirs-next", - "futures", + "futures 0.3.28", "hyper", "serde", "serde_json", @@ -5951,8 +8932,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e1fc19cfcfd9f6b2f96e36d5b0dddda9004d2cbfc2d17543e3b9f10cc38fce8" dependencies = [ "async-trait", - "bytes", - "futures", + "bytes 1.5.0", + "futures 0.3.28", "rusoto_core", "serde", "serde_json", @@ -5965,10 +8946,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ae95491c8b4847931e291b151127eccd6ff8ca13f33603eb3d0035ecb05272" dependencies = [ "base64 0.13.1", - "bytes", + "bytes 1.5.0", "chrono", "digest 0.9.0", - "futures", + "futures 0.3.28", "hex", "hmac 0.11.0", "http", @@ -6126,7 +9107,7 @@ dependencies = [ "unicode-segmentation", "unicode-width", "utf8parse", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -6135,13 +9116,22 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "salsa20" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "399f290ffc409596022fce5ea5d4138184be4784f2b28c62c59f0d8389059a15" +dependencies = [ + "cipher 0.2.5", +] + [[package]] name = "salsa20" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] @@ -6159,9 +9149,9 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "derive_more", - "parity-scale-codec", + "parity-scale-codec 3.6.5", "scale-info-derive", ] @@ -6204,8 +9194,8 @@ version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "serde_derive_internals", "syn 1.0.109", ] @@ -6222,6 +9212,22 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scrypt" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da492dab03f925d977776a0b7233d7b934d6dc2b94faead48928e2e9bacedb9" +dependencies = [ + "base64 0.13.1", + "hmac 0.10.1", + "pbkdf2 0.6.0", + "rand 0.7.3", + "rand_core 0.5.1", + "salsa20 0.7.2", + "sha2 0.9.9", + "subtle", +] + [[package]] name = "scrypt" version = "0.10.0" @@ -6230,7 +9236,7 @@ checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" dependencies = [ "hmac 0.12.1", "pbkdf2 0.11.0", - "salsa20", + "salsa20 0.10.2", "sha2 0.10.8", ] @@ -6244,27 +9250,60 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct 0.1.1", + "der 0.6.1", + "generic-array 0.14.7", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + [[package]] name = "sec1" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", + "base16ct 0.2.0", + "der 0.7.8", + "generic-array 0.14.7", + "pkcs8 0.10.2", "subtle", "zeroize", ] +[[package]] +name = "secp256k1" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d03ceae636d0fed5bae6a7f4f664354c5f4fcedf6eef053fef17e49f837d0a" +dependencies = [ + "rand 0.6.5", + "secp256k1-sys 0.4.2", +] + [[package]] name = "secp256k1" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" dependencies = [ - "secp256k1-sys", + "secp256k1-sys 0.8.1", +] + +[[package]] +name = "secp256k1-sys" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957da2573cde917463ece3570eab4a0b3f19de6f1646cde62e6fd3868f566036" +dependencies = [ + "cc", ] [[package]] @@ -6338,6 +9377,114 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" +[[package]] +name = "sentry" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0097a48cd1999d983909f07cb03b15241c5af29e5e679379efac1c06296abecc" +dependencies = [ + "httpdate", + "native-tls", + "reqwest", + "sentry-backtrace", + "sentry-contexts", + "sentry-core", + "sentry-debug-images", + "sentry-panic", + "sentry-tracing", + "tokio", + "ureq", +] + +[[package]] +name = "sentry-backtrace" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a7b80fa1dd6830a348d38a8d3a9761179047757b7dca29aef82db0118b9670" +dependencies = [ + "backtrace", + "once_cell", + "regex", + "sentry-core", +] + +[[package]] +name = "sentry-contexts" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7615dc588930f1fd2e721774f25844ae93add2dbe2d3c2f995ce5049af898147" +dependencies = [ + "hostname", + "libc", + "os_info", + "rustc_version 0.4.0", + "sentry-core", + "uname", +] + +[[package]] +name = "sentry-core" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f51264e4013ed9b16558cce43917b983fa38170de2ca480349ceb57d71d6053" +dependencies = [ + "once_cell", + "rand 0.8.5", + "sentry-types", + "serde", + "serde_json", +] + +[[package]] +name = "sentry-debug-images" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fe6180fa564d40bb942c9f0084ffb5de691c7357ead6a2b7a3154fae9e401dd" +dependencies = [ + "findshlibs", + "once_cell", + "sentry-core", +] + +[[package]] +name = "sentry-panic" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "323160213bba549f9737317b152af116af35c0410f4468772ee9b606d3d6e0fa" +dependencies = [ + "sentry-backtrace", + "sentry-core", +] + +[[package]] +name = "sentry-tracing" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38033822128e73f7b6ca74c1631cef8868890c6cb4008a291cf73530f87b4eac" +dependencies = [ + "sentry-backtrace", + "sentry-core", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sentry-types" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e663b3eb62ddfc023c9cf5432daf5f1a4f6acb1df4d78dd80b740b32dd1a740" +dependencies = [ + "debugid", + "hex", + "rand 0.8.5", + "serde", + "serde_json", + "thiserror", + "time", + "url", + "uuid 1.5.0", +] + [[package]] name = "serde" version = "1.0.192" @@ -6364,8 +9511,8 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] @@ -6381,6 +9528,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_regex" version = "1.1.0" @@ -6423,6 +9580,29 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +dependencies = [ + "base64 0.13.1", + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "serial_test" version = "2.0.0" @@ -6430,10 +9610,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" dependencies = [ "dashmap", - "futures", + "futures 0.3.28", "lazy_static", "log", - "parking_lot", + "parking_lot 0.12.1", "serial_test_derive", ] @@ -6448,13 +9628,38 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + [[package]] name = "sha-1" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest 0.10.7", ] @@ -6465,7 +9670,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest 0.10.7", ] @@ -6483,44 +9688,75 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest 0.9.0", - "opaque-debug", + "opaque-debug 0.3.0", ] [[package]] name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +version = "0.10.6" +source = "git+https://github.com/RustCrypto/hashes.git?rev=1731ced4a116d61ba9dc6ee6d0f38fb8102e357a#1731ced4a116d61ba9dc6ee6d0f38fb8102e357a" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest 0.10.7", ] [[package]] -name = "sha3" +name = "sha2" version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", "digest 0.10.7", - "keccak", ] [[package]] -name = "sharded-slab" -version = "0.1.7" +name = "sha3" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" dependencies = [ - "lazy_static", + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug 0.3.0", ] [[package]] -name = "shell-words" +name = "sha3" +version = "0.10.6" +source = "git+https://github.com/RustCrypto/hashes.git?rev=7a187e934c1f6c68e4b4e5cf37541b7a0d64d303#7a187e934c1f6c68e4b4e5cf37541b7a0d64d303" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shell-words" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" @@ -6548,7 +9784,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" dependencies = [ "libc", - "mio", + "mio 0.8.8", "signal-hook", ] @@ -6561,6 +9797,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + [[package]] name = "signature" version = "2.1.0" @@ -6583,7 +9829,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ - "num-bigint", + "num-bigint 0.4.4", "num-traits", "thiserror", "time", @@ -6595,13 +9841,34 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "skeptic" +version = "0.13.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8" +dependencies = [ + "bytecount", + "cargo_metadata 0.14.2", + "error-chain", + "glob", + "pulldown-cmark", + "tempfile", + "walkdir", +] + +[[package]] +name = "sketches-ddsketch" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a406c1882ed7f29cd5e248c9848a80e7cb6ae0fea82346d2746f2f941c07e1" + [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "autocfg", + "autocfg 1.1.0", ] [[package]] @@ -6609,6 +9876,9 @@ name = "smallvec" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +dependencies = [ + "serde", +] [[package]] name = "smol_str" @@ -6625,8 +9895,9 @@ version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ - "libc", - "winapi", + "derivative", + "rand 0.4.6", + "rescue_poseidon 0.4.1 (git+https://github.com/matter-labs/rescue-poseidon.git?branch=poseidon2)", ] [[package]] @@ -6639,6 +9910,22 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "soketto" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" +dependencies = [ + "base64 0.13.1", + "bytes 1.5.0", + "futures 0.3.28", + "http", + "httparse", + "log", + "rand 0.8.5", + "sha-1 0.9.8", +] + [[package]] name = "solang-parser" version = "0.3.3" @@ -6650,7 +9937,7 @@ dependencies = [ "lalrpop-util", "phf 0.11.2", "thiserror", - "unicode-xid", + "unicode-xid 0.2.4", ] [[package]] @@ -6665,6 +9952,26 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +dependencies = [ + "base64ct", + "der 0.5.1", +] + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der 0.6.1", +] + [[package]] name = "spki" version = "0.7.2" @@ -6672,9 +9979,129 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", - "der", + "der 0.7.8", +] + +[[package]] +name = "splitmut" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85070f382340e8b23a75808e83573ddf65f9ad9143df9573ca37c1ed2ee956a" + +[[package]] +name = "sqlformat" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4" +dependencies = [ + "itertools 0.10.5", + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "551873805652ba0d912fec5bbb0f8b4cdd96baf8e2ebf5970e5671092966019b" +dependencies = [ + "sqlx-core", + "sqlx-macros", +] + +[[package]] +name = "sqlx-core" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5" +dependencies = [ + "ahash 0.7.6", + "atoi", + "base64 0.13.1", + "bigdecimal", + "bitflags 1.3.2", + "byteorder", + "bytes 1.5.0", + "chrono", + "crc 2.1.0", + "crossbeam-queue 0.3.8", + "dirs 4.0.0", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-util", + "hashlink", + "hex", + "hkdf", + "hmac 0.12.1", + "indexmap 1.9.3", + "ipnetwork", + "itoa", + "libc", + "log", + "md-5 0.10.6", + "memchr", + "num-bigint 0.3.3", + "once_cell", + "paste", + "percent-encoding", + "rand 0.8.5", + "serde", + "serde_json", + "sha-1 0.10.1", + "sha2 0.10.8", + "smallvec", + "sqlformat", + "sqlx-rt", + "stringprep", + "thiserror", + "tokio-stream", + "url", + "whoami", +] + +[[package]] +name = "sqlx-macros" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0fba2b0cae21fc00fe6046f8baa4c7fcb49e379f0f592b04696607f69ed2e1" +dependencies = [ + "dotenv", + "either", + "heck 0.4.1", + "hex", + "once_cell", + "proc-macro2 1.0.69", + "quote 1.0.33", + "serde", + "serde_json", + "sha2 0.10.8", + "sqlx-core", + "sqlx-rt", + "syn 1.0.109", + "url", ] +[[package]] +name = "sqlx-rt" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4db708cd3e459078f85f39f96a00960bd841f66ee2a669e90bf36907f5a79aae" +dependencies = [ + "native-tls", + "once_cell", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -6695,7 +10122,7 @@ checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" dependencies = [ "new_debug_unreachable", "once_cell", - "parking_lot", + "parking_lot 0.12.1", "phf_shared 0.10.0", "precomputed-hash", "serde", @@ -6709,16 +10136,57 @@ checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" dependencies = [ "phf_generator 0.10.0", "phf_shared 0.10.0", - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", +] + +[[package]] +name = "stringprep" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" +dependencies = [ + "finl_unicode", + "unicode-bidi", + "unicode-normalization", ] +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap 2.34.0", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck 0.3.3", + "proc-macro-error", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "strum" version = "0.25.0" @@ -6734,9 +10202,9 @@ version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ - "heck", - "proc-macro2", - "quote", + "heck 0.4.1", + "proc-macro2 1.0.69", + "quote 1.0.33", "rustversion", "syn 2.0.39", ] @@ -6793,14 +10261,25 @@ dependencies = [ "svm-rs", ] +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", +] + [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "unicode-ident", ] @@ -6810,8 +10289,8 @@ version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "unicode-ident", ] @@ -6826,6 +10305,32 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "sync_vm" +version = "1.3.3" +source = "git+https://github.com/matter-labs/era-sync_vm.git?branch=v1.3.3#dad50e7eb7462a3819af8d5209d6ca243395bf51" +dependencies = [ + "arrayvec 0.7.4", + "cs_derive 0.1.0 (git+https://github.com/matter-labs/era-sync_vm.git?branch=v1.3.3)", + "derivative", + "franklin-crypto 0.0.5 (git+https://github.com/matter-labs/franklin-crypto?branch=dev)", + "hex", + "itertools 0.10.5", + "num-bigint 0.4.4", + "num-derive 0.3.3", + "num-integer", + "num-traits", + "once_cell", + "rand 0.4.6", + "rescue_poseidon 0.4.1 (git+https://github.com/matter-labs/rescue-poseidon)", + "serde", + "sha2 0.10.8", + "sha3 0.10.6", + "smallvec", + "zk_evm 1.3.3 (git+https://github.com/matter-labs/era-zk_evm.git?branch=v1.3.3)", + "zkevm_opcode_defs 1.3.2", +] + [[package]] name = "sync_wrapper" version = "0.1.2" @@ -6838,10 +10343,10 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", - "unicode-xid", + "unicode-xid 0.2.4", ] [[package]] @@ -6865,6 +10370,12 @@ dependencies = [ "libc", ] +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + [[package]] name = "tap" version = "1.0.1" @@ -6903,7 +10414,7 @@ checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" dependencies = [ "dirs-next", "rustversion", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -6964,7 +10475,7 @@ version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "once_cell", ] @@ -7008,6 +10519,15 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-keccak" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2" +dependencies = [ + "crunchy", +] + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -7049,11 +10569,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" dependencies = [ "backtrace", - "bytes", + "bytes 1.5.0", "libc", - "mio", + "mio 0.8.8", "num_cpus", - "parking_lot", + "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", "socket2 0.5.5", @@ -7140,7 +10660,21 @@ dependencies = [ "tokio-native-tls", "tokio-rustls 0.24.1", "tungstenite 0.20.1", - "webpki-roots", + "webpki-roots 0.25.2", +] + +[[package]] +name = "tokio-util" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" +dependencies = [ + "bytes 1.5.0", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", ] [[package]] @@ -7149,8 +10683,9 @@ version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ - "bytes", + "bytes 1.5.0", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -7188,6 +10723,17 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_edit" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5376256e44f2443f8896ac012507c19a012df0fe8758b55246ae51a2279db51f" +dependencies = [ + "combine", + "indexmap 1.9.3", + "itertools 0.10.5", +] + [[package]] name = "toml_edit" version = "0.19.15" @@ -7237,9 +10783,14 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", + "hdrhistogram", + "indexmap 1.9.3", "pin-project", "pin-project-lite", + "rand 0.8.5", + "slab", "tokio", + "tokio-util 0.7.9", "tower-layer", "tower-service", "tracing", @@ -7252,7 +10803,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" dependencies = [ "bitflags 1.3.2", - "bytes", + "bytes 1.5.0", "futures-core", "futures-util", "http", @@ -7277,10 +10828,19 @@ dependencies = [ "http", "http-body", "http-range-header", - "pin-project-lite", - "tower-layer", + "httpdate", + "iri-string", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util 0.7.9", + "tower", + "tower-layer", "tower-service", "tracing", + "uuid 1.5.0", ] [[package]] @@ -7359,6 +10919,16 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.17" @@ -7369,12 +10939,16 @@ dependencies = [ "nu-ansi-term", "once_cell", "regex", + "serde", + "serde_json", "sharded-slab", "smallvec", "thread_local", + "time", "tracing", "tracing-core", "tracing-log", + "tracing-serde", ] [[package]] @@ -7385,7 +10959,7 @@ checksum = "2cddb76a030b141d9639470eca2a236f3057a651bba78227cfa77830037a8286" dependencies = [ "byteorder", "hex", - "primitive-types", + "primitive-types 0.12.2", "protobuf", "rusb", "thiserror", @@ -7424,6 +10998,12 @@ dependencies = [ "rlp", ] +[[package]] +name = "triomphe" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee8098afad3fb0c54a9007aab6804558410503ad676d4633f9c2559a00ac0f" + [[package]] name = "try-lock" version = "0.2.4" @@ -7438,12 +11018,12 @@ checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" dependencies = [ "base64 0.13.1", "byteorder", - "bytes", + "bytes 1.5.0", "http", "httparse", "log", "rand 0.8.5", - "sha-1", + "sha-1 0.10.1", "thiserror", "url", "utf-8", @@ -7456,7 +11036,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" dependencies = [ "byteorder", - "bytes", + "bytes 1.5.0", "data-encoding", "http", "httparse", @@ -7494,6 +11074,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "uname" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" +dependencies = [ + "libc", +] + [[package]] name = "unarray" version = "0.1.4" @@ -7557,12 +11146,34 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + [[package]] name = "unicode-xid" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "unroll" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ad948c1cb799b1a70f836077721a92a35ac177d4daddf4c20a633786d4cf618" +dependencies = [ + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "untrusted" version = "0.7.1" @@ -7575,6 +11186,19 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "ureq" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5ccd538d4a604753ebc2f17cd9946e89b77bf87f6a8e2309667c6f2e87855e3" +dependencies = [ + "base64 0.21.4", + "log", + "native-tls", + "once_cell", + "url", +] + [[package]] name = "url" version = "2.4.1" @@ -7584,8 +11208,15 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf-8" version = "0.7.6" @@ -7608,6 +11239,16 @@ dependencies = [ "serde", ] +[[package]] +name = "uuid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" +dependencies = [ + "getrandom 0.2.10", + "serde", +] + [[package]] name = "valuable" version = "0.1.0" @@ -7620,6 +11261,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "vergen" version = "8.2.6" @@ -7638,6 +11285,53 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vise" +version = "0.1.0" +source = "git+https://github.com/matter-labs/vise.git?rev=dd05139b76ab0843443ab3ff730174942c825dae#dd05139b76ab0843443ab3ff730174942c825dae" +dependencies = [ + "elsa", + "linkme", + "once_cell", + "prometheus-client", + "vise-macros", +] + +[[package]] +name = "vise-exporter" +version = "0.1.0" +source = "git+https://github.com/matter-labs/vise.git?rev=dd05139b76ab0843443ab3ff730174942c825dae#dd05139b76ab0843443ab3ff730174942c825dae" +dependencies = [ + "hyper", + "metrics-exporter-prometheus", + "once_cell", + "tokio", + "tracing", + "vise", +] + +[[package]] +name = "vise-macros" +version = "0.1.0" +source = "git+https://github.com/matter-labs/vise.git?rev=dd05139b76ab0843443ab3ff730174942c825dae#dd05139b76ab0843443ab3ff730174942c825dae" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "vlog" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "chrono", + "sentry", + "serde_json", + "tracing", + "tracing-subscriber", +] + [[package]] name = "wait-timeout" version = "0.2.0" @@ -7672,7 +11366,7 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1e92e22e03ff1230c03a1a8ee37d2f89cd489e2e541b7550d6afad96faed169" dependencies = [ - "bytes", + "bytes 1.5.0", "futures-channel", "futures-util", "headers", @@ -7691,7 +11385,7 @@ dependencies = [ "tokio", "tokio-stream", "tokio-tungstenite 0.20.1", - "tokio-util", + "tokio-util 0.7.9", "tower-service", "tracing", ] @@ -7702,6 +11396,12 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -7714,7 +11414,7 @@ version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "wasm-bindgen-macro", ] @@ -7739,7 +11439,7 @@ version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "wasm-bindgen", "web-sys", @@ -7751,7 +11451,7 @@ version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" dependencies = [ - "quote", + "quote 1.0.33", "wasm-bindgen-macro-support", ] @@ -7774,6 +11474,19 @@ version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" +[[package]] +name = "wasm-streams" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "watchexec" version = "2.3.0" @@ -7785,7 +11498,7 @@ dependencies = [ "atomic-take", "clearscreen", "command-group", - "futures", + "futures 0.3.28", "ignore-files", "miette", "nix 0.26.4", @@ -7832,6 +11545,37 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web3" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5388522c899d1e1c96a4c307e3797e0f697ba7c77dd8e0e625ecba9dd0342937" +dependencies = [ + "arrayvec 0.7.4", + "base64 0.21.4", + "bytes 1.5.0", + "derive_more", + "ethabi 18.0.0", + "ethereum-types 0.14.1", + "futures 0.3.28", + "futures-timer", + "headers", + "hex", + "idna", + "jsonrpc-core 18.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log", + "once_cell", + "parking_lot 0.12.1", + "pin-project", + "reqwest", + "rlp", + "secp256k1 0.27.0", + "serde", + "serde_json", + "tiny-keccak 2.0.2", + "url", +] + [[package]] name = "webpki" version = "0.22.4" @@ -7842,6 +11586,15 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "webpki-roots" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888" +dependencies = [ + "rustls-webpki", +] + [[package]] name = "webpki-roots" version = "0.25.2" @@ -7860,6 +11613,22 @@ dependencies = [ "rustix", ] +[[package]] +name = "whoami" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + [[package]] name = "winapi" version = "0.3.9" @@ -7870,6 +11639,12 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -7882,7 +11657,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -8056,10 +11831,20 @@ version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "windows-sys 0.48.0", ] +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + [[package]] name = "ws_stream_wasm" version = "0.7.4" @@ -8067,7 +11852,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" dependencies = [ "async_io_stream", - "futures", + "futures 0.3.28", "js-sys", "log", "pharos", @@ -8079,6 +11864,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + [[package]] name = "wyz" version = "0.5.1" @@ -8152,36 +11943,845 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ - "aes", + "aes 0.8.3", "byteorder", "bzip2", "constant_time_eq", "crc32fast", - "crossbeam-utils", + "crossbeam-utils 0.8.16", "flate2", "hmac 0.12.1", "pbkdf2 0.11.0", "sha1", "time", - "zstd", + "zstd 0.11.2+zstd.1.5.2", ] [[package]] -name = "zstd" -version = "0.11.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +name = "zk_evm" +version = "1.3.1" +source = "git+https://github.com/matter-labs/era-zk_evm.git?tag=v1.3.1-rc2#0a7c775932db4839ff6b7fb0db9bdb3583ab54c0" dependencies = [ - "zstd-safe", + "blake2 0.10.6 (git+https://github.com/RustCrypto/hashes.git?rev=1f727ce37ff40fa0cce84eb8543a45bdd3ca4a4e)", + "k256 0.11.6", + "lazy_static", + "num 0.4.1", + "serde", + "serde_json", + "sha2 0.10.6", + "sha3 0.10.6", + "static_assertions", + "zkevm_opcode_defs 1.3.1", ] [[package]] -name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +name = "zk_evm" +version = "1.3.3" +source = "git+https://github.com/matter-labs/era-zk_evm.git?tag=v1.3.3-rc1#fe8215a7047d24430ad470cf15a19bedb4d6ba0b" +dependencies = [ + "anyhow", + "lazy_static", + "num 0.4.1", + "serde", + "serde_json", + "static_assertions", + "zk_evm_abstractions", + "zkevm_opcode_defs 1.3.2", +] + +[[package]] +name = "zk_evm" +version = "1.3.3" +source = "git+https://github.com/matter-labs/era-zk_evm.git?branch=v1.3.3#fbee20f5bac7d6ca3e22ae69b2077c510a07de4e" +dependencies = [ + "anyhow", + "lazy_static", + "num 0.4.1", + "serde", + "serde_json", + "static_assertions", + "zk_evm_abstractions", + "zkevm_opcode_defs 1.3.2", +] + +[[package]] +name = "zk_evm" +version = "1.4.0" +source = "git+https://github.com/matter-labs/era-zk_evm.git?branch=v1.4.0#dd76fc5badf2c05278a21b38015a7798fe2fe358" +dependencies = [ + "anyhow", + "lazy_static", + "num 0.4.1", + "serde", + "serde_json", + "static_assertions", + "zk_evm_abstractions", + "zkevm_opcode_defs 1.3.2", +] + +[[package]] +name = "zk_evm_abstractions" +version = "0.1.0" +source = "git+https://github.com/matter-labs/era-zk_evm_abstractions.git#7502a661d7d38906d849dcd3e7a15e5848af6581" +dependencies = [ + "anyhow", + "serde", + "static_assertions", + "zkevm_opcode_defs 1.3.2", +] + +[[package]] +name = "zkcast" +version = "0.2.0" +dependencies = [ + "ansi_term", + "anyhow", + "async-trait", + "chrono", + "clap 4.4.6", + "clap_complete", + "clap_complete_fig", + "comfy-table", + "const-hex", + "criterion", + "dirs 5.0.1", + "dunce", + "eth-keystore", + "ethabi 18.0.0", + "ethers", + "ethers-core", + "ethers-etherscan", + "ethers-providers", + "evm-disassembler", + "eyre", + "foundry-cli", + "foundry-common", + "foundry-config", + "foundry-evm", + "foundry-test-utils", + "foundry-utils", + "futures 0.3.28", + "indicatif", + "itertools 0.11.0", + "rayon", + "regex", + "rpassword", + "rusoto_core", + "rusoto_kms", + "semver 1.0.20", + "serde", + "serde_json", + "tempfile", + "tokio", + "tracing", + "uint", + "url", + "vergen", + "yansi 0.5.1", + "zksync-web3-rs", +] + +[[package]] +name = "zkevm-assembly" +version = "1.3.2" +source = "git+https://github.com/matter-labs/era-zkEVM-assembly.git?branch=v1.3.2#3c61d450cbe6548068be8f313ed02f1bd229a865" +dependencies = [ + "env_logger 0.9.3", + "hex", + "lazy_static", + "log", + "nom", + "num-bigint 0.4.4", + "num-traits", + "sha3 0.10.8", + "smallvec", + "structopt", + "thiserror", + "zkevm_opcode_defs 1.3.2", +] + +[[package]] +name = "zkevm_circuits" +version = "1.4.0" +source = "git+https://github.com/matter-labs/era-zkevm_circuits.git?branch=main#4fba537ccecc238e2da9c80844dc8c185e42466f" +dependencies = [ + "arrayvec 0.7.4", + "bincode", + "boojum", + "cs_derive 0.1.0 (git+https://github.com/matter-labs/era-boojum.git?branch=main)", + "derivative", + "hex", + "itertools 0.10.5", + "rand 0.4.6", + "rand 0.8.5", + "serde", + "serde_json", + "smallvec", + "zkevm_opcode_defs 1.3.2", +] + +[[package]] +name = "zkevm_opcode_defs" +version = "1.3.1" +source = "git+https://github.com/matter-labs/era-zkevm_opcode_defs.git?branch=v1.3.1#00d4ad2292bd55374a0fa10fe11686d7a109d8a0" +dependencies = [ + "bitflags 1.3.2", + "ethereum-types 0.14.1", + "lazy_static", + "sha2 0.10.8", +] + +[[package]] +name = "zkevm_opcode_defs" +version = "1.3.2" +source = "git+https://github.com/matter-labs/era-zkevm_opcode_defs.git?branch=v1.3.2#dffacadeccdfdbff4bc124d44c595c4a6eae5013" +dependencies = [ + "bitflags 2.4.1", + "blake2 0.10.6 (git+https://github.com/RustCrypto/hashes.git?rev=1f727ce37ff40fa0cce84eb8543a45bdd3ca4a4e)", + "ethereum-types 0.14.1", + "k256 0.11.6", + "lazy_static", + "sha2 0.10.6", + "sha3 0.10.6", +] + +[[package]] +name = "zkevm_test_harness" +version = "1.3.3" +source = "git+https://github.com/matter-labs/era-zkevm_test_harness.git?branch=v1.3.3#d26aa4133f2b5e114c0bf084b8c3f1eca9aa9929" +dependencies = [ + "bincode", + "circuit_testing", + "codegen 0.2.0", + "crossbeam 0.8.2", + "derivative", + "env_logger 0.10.0", + "hex", + "num-bigint 0.4.4", + "num-integer", + "num-traits", + "rayon", + "serde", + "serde_json", + "smallvec", + "structopt", + "sync_vm", + "test-log", + "tracing", + "zk_evm 1.3.3 (git+https://github.com/matter-labs/era-zk_evm.git?branch=v1.3.3)", + "zkevm-assembly", +] + +[[package]] +name = "zkevm_test_harness" +version = "1.4.0" +source = "git+https://github.com/matter-labs/era-zkevm_test_harness.git?branch=v1.4.0#43aeb53d7d9c909508a98f9fc140edff0e9d2357" +dependencies = [ + "bincode", + "circuit_definitions", + "codegen 0.2.0", + "crossbeam 0.8.2", + "derivative", + "env_logger 0.10.0", + "hex", + "rand 0.4.6", + "rayon", + "serde", + "serde_json", + "smallvec", + "structopt", + "test-log", + "tracing", + "zkevm-assembly", +] + +[[package]] +name = "zkforge" +version = "0.2.0" +dependencies = [ + "alloy-primitives", + "ansi_term", + "anvil", + "anyhow", + "async-trait", + "clap 4.4.6", + "clap_complete", + "clap_complete_fig", + "comfy-table", + "const-hex", + "criterion", + "dialoguer", + "dirs 5.0.1", + "dunce", + "era_revm", + "ethers", + "eyre", + "forge-doc", + "forge-fmt", + "foundry-cli", + "foundry-common", + "foundry-config", + "foundry-debugger", + "foundry-evm", + "foundry-test-utils", + "foundry-utils", + "futures 0.3.28", + "globset", + "indicatif", + "itertools 0.11.0", + "once_cell", + "parking_lot 0.12.1", + "path-slash", + "pretty_assertions", + "proptest", + "rayon", + "regex", + "reqwest", + "semver 1.0.20", + "serde", + "serde_json", + "serial_test", + "similar", + "solang-parser", + "strum 0.25.0", + "svm-rs", + "thiserror", + "tokio", + "tracing", + "tracing-subscriber", + "url", + "vergen", + "watchexec", + "yansi 0.5.1", + "zkcast", + "zksync-web3-rs", +] + +[[package]] +name = "zksync-web3-rs" +version = "0.1.0" +source = "git+https://github.com/lambdaclass/zksync-web3-rs.git?rev=70327ae5413c517bd4d27502507cdd96ee40cd22#70327ae5413c517bd4d27502507cdd96ee40cd22" +dependencies = [ + "async-trait", + "clap 4.4.6", + "env_logger 0.10.0", + "ethers", + "ethers-contract", + "hex", + "log", + "serde", + "serde_json", + "sha2 0.9.9", + "thiserror", + "tokio", +] + +[[package]] +name = "zksync_basic_types" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "serde", + "serde_json", + "web3", +] + +[[package]] +name = "zksync_circuit_breaker" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "anyhow", + "async-trait", + "backon", + "convert_case 0.6.0", + "futures 0.3.28", + "hex", + "metrics", + "serde_json", + "thiserror", + "tokio", + "tracing", + "zksync_config", + "zksync_contracts", + "zksync_dal", + "zksync_eth_client", + "zksync_types", +] + +[[package]] +name = "zksync_commitment_utils" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "zkevm_test_harness 1.4.0", + "zksync_types", + "zksync_utils", +] + +[[package]] +name = "zksync_config" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "anyhow", + "bigdecimal", + "envy", + "hex", + "num 0.3.1", + "once_cell", + "serde", + "serde_json", + "url", + "zksync_basic_types", + "zksync_contracts", + "zksync_utils", +] + +[[package]] +name = "zksync_contracts" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "envy", + "ethabi 18.0.0", + "hex", + "once_cell", + "serde", + "serde_json", + "zksync_utils", +] + +[[package]] +name = "zksync_core" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "actix-cors", + "actix-rt", + "actix-web", + "anyhow", + "async-trait", + "axum 0.6.20", + "bigdecimal", + "bitflags 1.3.2", + "chrono", + "ctrlc", + "futures 0.3.28", + "governor", + "hex", + "itertools 0.10.5", + "jsonrpc-core 18.0.0 (git+https://github.com/matter-labs/jsonrpc.git?branch=master)", + "jsonrpc-core-client", + "jsonrpc-derive", + "jsonrpc-http-server", + "jsonrpc-pubsub", + "jsonrpc-ws-server", + "metrics", + "multivm", + "num 0.3.1", + "once_cell", + "prometheus_exporter", + "rand 0.8.5", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "tower-http 0.4.4", + "tracing", + "vise", + "vlog", + "zksync_circuit_breaker", + "zksync_commitment_utils", + "zksync_config", + "zksync_contracts", + "zksync_dal", + "zksync_eth_client", + "zksync_eth_signer", + "zksync_health_check", + "zksync_mempool", + "zksync_merkle_tree", + "zksync_mini_merkle_tree", + "zksync_object_store", + "zksync_prover_utils", + "zksync_queued_job_processor", + "zksync_state", + "zksync_storage", + "zksync_system_constants", + "zksync_types", + "zksync_utils", + "zksync_verification_key_generator_and_server", + "zksync_web3_decl", +] + +[[package]] +name = "zksync_crypto" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "base64 0.13.1", + "blake2 0.10.6 (registry+https://github.com/rust-lang/crates.io-index)", + "hex", + "once_cell", + "serde", + "sha2 0.9.9", + "thiserror", + "zksync_basic_types", +] + +[[package]] +name = "zksync_dal" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "anyhow", + "bigdecimal", + "bincode", + "hex", + "itertools 0.10.5", + "num 0.3.1", + "once_cell", + "serde", + "serde_json", + "sqlx", + "strum 0.24.1", + "thiserror", + "tokio", + "tracing", + "vise", + "zksync_contracts", + "zksync_health_check", + "zksync_system_constants", + "zksync_types", + "zksync_utils", +] + +[[package]] +name = "zksync_eth_client" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "anyhow", + "async-trait", + "hex", + "jsonrpc-core 18.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", + "thiserror", + "tokio", + "tracing", + "vise", + "zksync_config", + "zksync_contracts", + "zksync_eth_signer", + "zksync_types", +] + +[[package]] +name = "zksync_eth_signer" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "async-trait", + "hex", + "jsonrpc-core 18.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-crypto", + "reqwest", + "rlp", + "secp256k1 0.27.0", + "serde", + "serde_derive", + "serde_json", + "thiserror", + "zksync_types", +] + +[[package]] +name = "zksync_health_check" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "async-trait", + "futures 0.3.28", + "serde", + "serde_json", + "tokio", + "tracing", +] + +[[package]] +name = "zksync_mempool" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "tracing", + "zksync_types", +] + +[[package]] +name = "zksync_merkle_tree" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "leb128", + "once_cell", + "rayon", + "thiserror", + "tracing", + "vise", + "zksync_crypto", + "zksync_storage", + "zksync_types", +] + +[[package]] +name = "zksync_mini_merkle_tree" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "once_cell", + "zksync_basic_types", + "zksync_crypto", +] + +[[package]] +name = "zksync_object_store" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "anyhow", + "async-trait", + "bincode", + "google-cloud-auth", + "google-cloud-storage", + "http", + "tokio", + "tracing", + "vise", + "zksync_config", + "zksync_types", +] + +[[package]] +name = "zksync_prover_utils" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "anyhow", + "async-trait", + "ctrlc", + "futures 0.3.28", + "regex", + "reqwest", + "tokio", + "toml_edit 0.14.4", + "tracing", + "zksync_config", + "zksync_object_store", + "zksync_types", + "zksync_utils", +] + +[[package]] +name = "zksync_queued_job_processor" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "anyhow", + "async-trait", + "tokio", + "tracing", + "zksync_utils", +] + +[[package]] +name = "zksync_state" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "mini-moka", + "tokio", + "tracing", + "vise", + "zksync_dal", + "zksync_storage", + "zksync_types", + "zksync_utils", +] + +[[package]] +name = "zksync_storage" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "num_cpus", + "once_cell", + "rocksdb", + "tracing", + "vise", +] + +[[package]] +name = "zksync_system_constants" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "anyhow", + "bigdecimal", + "hex", + "num 0.3.1", + "once_cell", + "serde", + "serde_json", + "url", + "zksync_basic_types", + "zksync_contracts", + "zksync_utils", +] + +[[package]] +name = "zksync_types" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "blake2 0.10.6 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono", + "codegen 0.1.0", + "ethereum-types 0.12.1", + "hex", + "num 0.3.1", + "num_enum 0.6.1", + "once_cell", + "parity-crypto", + "rlp", + "serde", + "serde_json", + "serde_with", + "strum 0.24.1", + "thiserror", + "zk_evm 1.3.3 (git+https://github.com/matter-labs/era-zk_evm.git?tag=v1.3.3-rc1)", + "zkevm_test_harness 1.3.3", + "zksync_basic_types", + "zksync_contracts", + "zksync_mini_merkle_tree", + "zksync_system_constants", + "zksync_utils", +] + +[[package]] +name = "zksync_utils" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "anyhow", + "bigdecimal", + "futures 0.3.28", + "hex", + "itertools 0.10.5", + "metrics", + "num 0.3.1", + "reqwest", + "serde", + "thiserror", + "tokio", + "tracing", + "vlog", + "zk_evm 1.3.3 (git+https://github.com/matter-labs/era-zk_evm.git?tag=v1.3.3-rc1)", + "zksync_basic_types", +] + +[[package]] +name = "zksync_verification_key_generator_and_server" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "anyhow", + "bincode", + "circuit_testing", + "ff_ce", + "hex", + "itertools 0.10.5", + "once_cell", + "serde_json", + "structopt", + "tracing", + "vlog", + "zksync_prover_utils", + "zksync_types", +] + +[[package]] +name = "zksync_web3_decl" +version = "0.1.0" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=73a1e8ff564025d06e02c2689da238ae47bb10c3#73a1e8ff564025d06e02c2689da238ae47bb10c3" +dependencies = [ + "bigdecimal", + "chrono", + "itertools 0.10.5", + "jsonrpsee", + "rlp", + "serde", + "serde_json", + "thiserror", + "zksync_types", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe 5.0.2+zstd.1.5.2", +] + +[[package]] +name = "zstd" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" +dependencies = [ + "zstd-safe 6.0.6", +] + +[[package]] +name = "zstd" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110" +dependencies = [ + "zstd-safe 7.0.0", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-safe" +version = "6.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-safe" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e" dependencies = [ - "libc", "zstd-sys", ] diff --git a/Cargo.toml b/Cargo.toml index b666914ce..2141ed264 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -109,8 +109,10 @@ trezor-client.opt-level = 1 [workspace.dependencies] anvil = { path = "crates/anvil" } cast = { path = "crates/cast" } +zkcast = { path = "crates/zkcast" } chisel = { path = "crates/chisel" } forge = { path = "crates/forge" } +zkforge = { path = "crates/zkforge" } forge-doc = { path = "crates/doc" } forge-fmt = { path = "crates/fmt" } @@ -138,20 +140,23 @@ foundry-compilers = { version = "0.1", default-features = false } ## revm # no default features to avoid c-kzg -revm = { version = "3", default-features = false } # -revm-primitives = { version = "1", default-features = false } - -## ethers -ethers = { version = "2.0", default-features = false } -ethers-addressbook = { version = "2.0", default-features = false } -ethers-core = { version = "2.0", default-features = false } -ethers-contract = { version = "2.0", default-features = false } -ethers-contract-abigen = { version = "2.0", default-features = false } -ethers-providers = { version = "2.0", default-features = false } -ethers-signers = { version = "2.0", default-features = false } -ethers-middleware = { version = "2.0", default-features = false } -ethers-etherscan = { version = "2.0", default-features = false } -ethers-solc = { version = "2.0", default-features = false } +# Using a fork of revm as zksync-era requires the usage of sha3 0.10.6, and the latest revm uses 0.10.8 +revm = { git = "https://github.com/dutterbutter/revm.git", tag = "sha3_0.10.6", default-features = false } + +# We use a fork of `ethers` to gain access to a previously private variable. +# This ensures that all artifact files, freshly compiled and saved in the `output` directory, are publicly accessible. +# For details on this modification, refer to: +# https://github.com/mm-zk/ethers-rs/commit/2b2c7312f328a431fa40a7b87a722eaf28e27061 +ethers = { git = "https://github.com/mm-zk/ethers-rs", tag = "main_public_artifact", default-features = false } +ethers-addressbook = { git = "https://github.com/mm-zk/ethers-rs", tag = "main_public_artifact", default-features = false } +ethers-core = { git = "https://github.com/mm-zk/ethers-rs", tag = "main_public_artifact", default-features = false } +ethers-contract = { git = "https://github.com/mm-zk/ethers-rs", tag = "main_public_artifact", default-features = false } +ethers-contract-abigen = { git = "https://github.com/mm-zk/ethers-rs", tag = "main_public_artifact", default-features = false } +ethers-providers = { git = "https://github.com/mm-zk/ethers-rs", tag = "main_public_artifact", default-features = false } +ethers-signers = { git = "https://github.com/mm-zk/ethers-rs", tag = "main_public_artifact", default-features = false } +ethers-middleware = { git = "https://github.com/mm-zk/ethers-rs", tag = "main_public_artifact", default-features = false } +ethers-etherscan = { git = "https://github.com/mm-zk/ethers-rs", tag = "main_public_artifact", default-features = false } +ethers-solc = { git = "https://github.com/mm-zk/ethers-rs", tag = "main_public_artifact", default-features = false } ## alloy alloy-primitives = "0.4.1" @@ -175,18 +180,6 @@ jsonpath_lib = "0.3" eyre = "0.6" color-eyre = "0.6" -#[patch."https://github.com/gakonst/ethers-rs"] -#ethers = { path = "../ethers-rs/ethers" } -#ethers-addressbook = { path = "../ethers-rs/ethers-addressbook" } -#ethers-contract = { path = "../ethers-rs/ethers-contract" } -#ethers-contract-abigen = { path = "../ethers-rs/ethers-contract/ethers-contract-abigen" } -#ethers-core = { path = "../ethers-rs/ethers-core" } -#ethers-etherscan = { path = "../ethers-rs/ethers-etherscan" } -#ethers-middleware = { path = "../ethers-rs/ethers-middleware" } -#ethers-providers = { path = "../ethers-rs/ethers-providers" } -#ethers-signers = { path = "../ethers-rs/ethers-signers" } -#ethers-solc = { path = "../ethers-rs/ethers-solc" } - [patch.crates-io] ethers = { git = "https://github.com/gakonst/ethers-rs", rev = "4f7b354ecec0aea38ed165560ae4f3224d644c9e" } ethers-addressbook = { git = "https://github.com/gakonst/ethers-rs", rev = "4f7b354ecec0aea38ed165560ae4f3224d644c9e" } diff --git a/README.md b/README.md index 8f205f13d..aeb88f341 100644 --- a/README.md +++ b/README.md @@ -1,138 +1,725 @@ -Foundry logo +# Foundry with zkSync Era v0.1 -## Foundry +This repository provides [Foundry](https://github.com/foundry-rs/foundry) functionality in Solidity for compiling, deploying, and interacting with smart contracts on zkSync Era. -![Github Actions][gha-badge] [![Telegram Chat][tg-badge]][tg-url] [![Telegram Support][tg-support-badge]][tg-support-url] +### Supported features -[gha-badge]: https://img.shields.io/github/actions/workflow/status/foundry-rs/foundry/test.yml?branch=master -[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 -[tg-support-badge]: https://img.shields.io/endpoint?color=neon&logo=telegram&label=support&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Ffoundry_support -[tg-support-url]: https://t.me/foundry_support +- Compile smart contracts with the [zksolc compiler](https://github.com/matter-labs/zksolc-bin). +- Deploy smart contracts to zkSync Era mainnet, testnet, or local test node. +- Bridge assets L1 <-> L2. +- Call deployed contracts on zkSync Era testnet or local test node. +- Send transactions to deployed contracts on zkSync Era testnet or local test node. +- Simple 'PASS | FAIL' testing -**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** +### Known Issues -Foundry consists of: +- Currently users of `foundry-zksync` have to update import paths to be relative paths based on the complied source code location (e.g. `import "../lib/forge-std/src/console.sol";`) +- `script` command does not support `zksolc` currently +- Cheat codes are currently not supported -- [**Forge**](./crates/forge): Ethereum testing framework (like Truffle, Hardhat and DappTools). -- [**Cast**](./crates/cast): Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. -- [**Anvil**](./crates/anvil): Local Ethereum node, akin to Ganache, Hardhat Network. -- [**Chisel**](./crates/chisel): Fast, utilitarian, and verbose solidity REPL. +## Set up -**Need help getting started with Foundry? Read the [📖 Foundry Book][foundry-book] (WIP)!** +### Prerequisites -![Demo](.github/demo.gif) +- [Rust compiler](https://www.rust-lang.org/tools/install). -## Installation +### Installation -See the [installation guide](https://book.getfoundry.sh/getting-started/installation) in the book. +#### `zkforge` -If you're experiencing any issues while installing, check out [Getting Help](#getting-help) and the [FAQ](https://book.getfoundry.sh/faq). +To install: -## Forge +``` +cargo install --path ./crates/zkforge --profile local --force --locked +``` -### Features +#### `zkcast` -- **Fast & flexible compilation pipeline** - - Automatic Solidity compiler version detection & installation (under `~/.svm`) - - **Incremental compilation & caching**: Only changed files are re-compiled - - Parallel compilation - - Non-standard directory structures support (e.g. [Hardhat repos](https://twitter.com/gakonst/status/1461289225337421829)) -- **Tests are written in Solidity** (like in DappTools) -- **Fast fuzz testing** with shrinking of inputs & printing of counter-examples -- **Fast remote RPC forking mode**, leveraging Rust's async infrastructure like tokio -- **Flexible debug logging** - - DappTools-style, using `DsTest`'s emitted logs - - Hardhat-style, using the popular `console.sol` contract -- **Portable (5-10MB) & easy to install** without requiring Nix or any other package manager -- **Fast CI** with the [Foundry GitHub action][foundry-gha]. +To install: -### How Fast? +``` +cargo install --path ./crates/zkcast --profile local --force --locked +``` -Forge is quite fast at both compiling (leveraging [ethers-solc][ethers-solc]) and testing. +## Usage -See the benchmarks below. More benchmarks can be found in the [v0.2.0 announcement post][benchmark-post] and in the [Convex Shutdown Simulation][convex] repository. +### `zkcast` -**Testing Benchmarks** +Spin up local Docker node: +- [Instructions to setup local-setup](https://era.zksync.io/docs/tools/testing/dockerized-testing.html) -| Project | Forge | DappTools | Speedup | -| ---------------------------------- | ----- | --------- | ------- | -| [transmissions11/solmate][solmate] | 2.8s | 6m34s | 140x | -| [reflexer-labs/geb][geb] | 0.4s | 23s | 57.5x | -| [Rari-Capital/vaults][vaults] | 0.28s | 6.5s | 23x | +Interact with blockchain: -_Note: In the above benchmarks, compilation was always skipped_ +#### Get chain id of local node -**Compilation Benchmarks** +```sh +zkcast chain-id --rpc-url http://localhost:3050 +``` -Compilation benchmarks +**Output** -**Takeaway: Forge compilation is consistently faster by a factor of 1.7-11.3x, depending on the amount of caching involved.** +270 -## Cast +#### Get chain id of testnet -Cast is a swiss army knife for interacting with Ethereum applications from the command line. +```sh +zkcast chain-id --rpc-url https://zksync2-testnet.zksync.dev:443 +``` -More documentation can be found in the [cast package](./crates/cast). +**Output** -## Configuration +```sh +280 +``` -### Using `foundry.toml` +#### Get client -Foundry is designed to be very configurable. You can configure Foundry using a file called [`foundry.toml`](./crates/config) in the root of your project, or any other parent directory. See [config package](./crates/config/README.md#all-options) for all available options. +```sh +zkcast client --rpc-url https://zksync2-testnet.zksync.dev:443 +``` -Configuration can be arbitrarily namespaced by profiles. The default profile is named `default` (see ["Default Profile"](./crates/config/README.md#default-profile)). +**Output** -You can select another profile using the `FOUNDRY_PROFILE` environment variable. You can also override parts of your configuration using `FOUNDRY_` or `DAPP_` prefixed environment variables, like `FOUNDRY_SRC`. +```sh +zkSync/v2.0 +``` -`forge init` creates a basic, extendable `foundry.toml` file. +#### Get account's L2 ETH balance -To see your current configuration, run `forge config`. To see only basic options (as set with `forge init`), run `forge config --basic`. This can be used to create a new `foundry.toml` file with `forge config --basic > foundry.toml`. +```sh +zkcast balance 0x42C7eF198f8aC9888E2B1b73e5B71f1D4535194A --rpc-url https://zksync2-testnet.zksync.dev:443 +``` -By default `forge config` shows the currently selected foundry profile and its values. It also accepts the same arguments as `forge build`. +**Output** -### DappTools Compatibility +```sh +447551277794355871 +``` -You can re-use your `.dapprc` environment variables by running `source .dapprc` before using a Foundry tool. +#### Get gas price -### Additional Configuration +```sh +zkcast gas-price --rpc-url https://zksync2-testnet.zksync.dev:443 +``` -You can find additional setup and configurations guides in the [Foundry Book][foundry-book]: +**Example output** -- [Setting up VSCode][vscode-setup] -- [Shell autocompletions][shell-setup] +```sh +250000000 +``` -## Contributing +#### Get timestamp of latest block -See our [contributing guidelines](./CONTRIBUTING.md). +```sh +zkcast age --block latest --rpc-url https://zksync2-testnet.zksync.dev:443 +``` -## Getting Help +**Example output** -First, see if the answer to your question can be found in [book][foundry-book], or in the relevant crate. +```sh +Mon May 1 16:11:07 2023 +``` -If the answer is not there: +#### Get latest block -- Join the [support Telegram][tg-support-url] to get help, or -- Open a [discussion](https://github.com/foundry-rs/foundry/discussions/new) with your question, or -- Open an issue with [the bug](https://github.com/foundry-rs/foundry/issues/new) +```sh +zkcast block latest --rpc-url https://zksync2-testnet.zksync.dev:443 +``` -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! +**Example output** -## Acknowledgements +```sh +baseFeePerGas 250000000 +difficulty 0 +extraData 0x +gasLimit 4294967295 +gasUsed 40277767 +hash 0x6c5b7c9b82b48bd77c0f506d74ed32aec6ab5c52e6c9c604ee8825a0b4a68289 +logsBloom 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +miner 0x0000000000000000000000000000000000000000 +mixHash 0x0000000000000000000000000000000000000000000000000000000000000000 +nonce 0x0000000000000000 +number 5024177 +parentHash 0x9fbb3c9e5ef3b7807152367eeab5759cce14c290118de0e9011777a640cd7068 +receiptsRoot 0x0000000000000000000000000000000000000000000000000000000000000000 +sealFields [] +sha3Uncles 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347 +size 0 +stateRoot 0x0000000000000000000000000000000000000000000000000000000000000000 +timestamp 1682957640 +totalDifficulty 0 +l1BatchNumber null +l1BatchTimestamp null +``` +--- -- Foundry is a clean-room rewrite of the testing framework [DappTools](https://github.com/dapphub/dapptools). None of this would have been possible without the DappHub team's work over the years. -- [Matthias Seitz](https://twitter.com/mattsse_): Created [ethers-solc](https://github.com/gakonst/ethers-rs/tree/master/ethers-solc/) which is the backbone of our compilation pipeline, as well as countless contributions to ethers, in particular the `abigen` macros. -- [Rohit Narurkar](https://twitter.com/rohitnarurkar): Created the Rust Solidity version manager [svm-rs](https://github.com/roynalnaruto/svm-rs) which we use to auto-detect and manage multiple Solidity versions. -- [Brock Elmore](https://twitter.com/brockjelmore): For extending the VM's cheatcodes and implementing [structured call tracing](https://github.com/foundry-rs/foundry/pull/192), a critical feature for debugging smart contract calls. -- All the other [contributors](https://github.com/foundry-rs/foundry/graphs/contributors) to the [ethers-rs](https://github.com/gakonst/ethers-rs) & [foundry](https://github.com/foundry-rs/foundry) repositories and chatrooms. +### Compile with `zkforge zk-build` -[foundry-book]: https://book.getfoundry.sh -[foundry-gha]: https://github.com/foundry-rs/foundry-toolchain -[ethers-solc]: https://github.com/gakonst/ethers-rs/tree/master/ethers-solc/ -[solmate]: https://github.com/transmissions11/solmate/ -[geb]: https://github.com/reflexer-labs/geb -[vaults]: https://github.com/rari-capital/vaults -[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://book.getfoundry.sh/config/vscode.html -[shell-setup]: https://book.getfoundry.sh/config/shell-autocompletion.html +> Aliases: `zkforge zkbuild`, `zkforge zk-compile`, `zkforge zkb`. + +Compile smart contracts to zkEVM bytecode and store the compiled output files in a logical directory structure `/zkout/` for easy retrieval by other components of the application. + +```sh + +Compiler subcommands for zkSync + +Usage: +zkforge zk-build [OPTIONS] + +Options: + --use-zksolc Specify zksolc compiler version (default if left blank) + --is-system Enable the system contract compilation mode. + --force-evmla Sets the EVM legacy assembly pipeline forcibly + -h, --help Print help +``` + +> `--is-system` flag: It is necessary to compile some contracts, including those that deploy other contracts (such as factory contracts), using the `--is-system` flag. These contracts should be placed in the `src/is-system/` folder. If the folder does not exist, manually create it. + +![image](https://user-images.githubusercontent.com/76663878/236301037-2a536ab0-3d09-44f3-a74d-5f5891af335b.png) + +### Example usage + +To compile with default compiler options (v1.3.11). + +```sh +zkforge zk-build +``` + +### Compiler settings + +Configure the `zksolc` compiler version using the optional `--use` flag. + +```bash +zkforge zkb --use 0.8.19 +``` + +**Example output** + +`zksolc` compiler artifacts can be found in the output folder: + +```bash +/zkout/ +``` +![image](https://user-images.githubusercontent.com/76663878/234152279-e144e489-41ab-4cbd-8321-8ccd9b0aa6ef.png) + +Example terminal output: + +![image](https://user-images.githubusercontent.com/76663878/236305625-8c7519e2-0c5e-492f-a4bc-3b019a95e34f.png) + +NOTE: Currently, until `forge remappings` are implemented, import paths must be relative to the contract importing it: + +![image](https://github.com/matter-labs/foundry-zksync/assets/76663878/490b34f4-e286-42a7-8570-d4b228ec10c7) + +`SimpleFactory.sol` and `AAFactory.sol` are in the `src/is-system/` folder. + +--- + +### Deploy with `zkforge zk-create` + +> Aliases: `zkforge zkcreate`, `zkforge zk-deploy`, `zkforge zkc` + +```sh +Deploy smart contracts to zksync. + +Usage: zkforge zk-create [OPTIONS] --rpc-url --chain --private-key + +Options: + -h, --help + Print help (see a summary with '-h') + +ZkCreate options: + --constructor-args ... + The constructor arguments. + + --constructor-args-path + The path to a file containing the constructor arguments. + + + The contract identifier in the form `:`. + +ZkSync Features: + --factory-deps ... + The factory dependencies in the form `:`. +``` + +#### Example + +To deploy `src/Greeter.sol` to zkSync testnet: + +```bash +zkforge zkc src/Greeter.sol:Greeter --constructor-args "ZkSync + Pineapple" --private-key <"PRIVATE_KEY"> --rpc-url https://zksync2-testnet.zksync.dev:443 --chain 280 +``` + +#### Output + +```txt +Deploying contract... ++-------------------------------------------------+ +Contract successfully deployed to address: 0x07d485ff2df314b240ec392ed86b137a661ddd35 +Transaction Hash: 0xdb6864fe1d19572a3ff509c5c7ed43f033d2dab8261a843808ed46e6e6ee51be +Gas used: 89879008 +Effective gas price: 250000000 +Block Number: 6651906 ++-------------------------------------------------+ +``` + +--- + +### Bridge assets L1 ↔ L2 with `zkcast zk-send` and `zkcast zk-deposit` + +### L1 → L2 deposits + +```sh +zkcast zk-deposit --l1-rpc-url --l2-url --chain --private-key +``` +NOTE: Leave `` blank to bridge ETH + +```bash +Usage: zkcast zk-deposit --l1-rpc-url --l2-url [OPTIONS] [BRIDGE] [TIP] + +Arguments: + + The L2 address that receives the tokens. + + + Amount of token to deposit. + + [BRIDGE] + The address of a custom bridge to call. + + [TIP] + Optional fee that the user can choose to pay in addition to the regular transaction fee. + +Options: + -z, --l2-url + The zkSync RPC Layer 2 endpoint. Can be provided via the env var ZKSYNC_RPC_URL or --l2-url from the command line. + + NOTE: For Deposits, ETH_RPC_URL, or --rpc-url should be set to the Layer 1 RPC URL + + [env: ZKSYNC_RPC_URL=https://zksync2-testnet.zksync.dev] + + --token + Token to bridge. Leave blank for ETH. + + -h, --help + Print help (see a summary with '-h') +``` + +#### Example - error on this one + +```sh +zkcast zkdeposit 0x36615Cf349d7F6344891B1e7CA7C72883F5dc049 1000000 --rpc-url http://localhost:8545 --l2-url http://localhost:3050 --private-key 7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110 --chain 270 +``` + +#### Output + +```txt +Bridging assets.... +Transaction Hash: 0x55793df0a636aedd098309e3487c6d9ec0910422d5b9f0bdbdf764bc82dc1b9f +``` +--- + +### L2 → L1 withdrawals + +```sh +zkcast zk-send --withdraw --amount --rpc-url --private-key + + +Arguments: + [TO] The withdraw recipient. + + +Bridging options: + -w, --withdraw For L2 -> L1 withdrawals. + + --token Token to bridge. Leave blank for ETH. + + -a, --amount Amount of token to bridge. Required value when bridging +``` + +#### Example + +```sh +zkcast zk-send --withdraw 0x36615Cf349d7F6344891B1e7CA7C72883F5dc049 --amount 1000000 --rpc-url http://localhost:3050 --private-key 7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110 --chain 270 +``` + +#### Output + +```text +Bridging assets.... ++-------------------------------------------------+ +Transaction Hash: 0x3562f47db61de149fb7266c3a65935c4e8324cceb5a1db8718390a8a5a210191 +Gas used: 10276475 +Effective gas price: 250000000 +Block Number: 6652714 ++-------------------------------------------------+ +``` + +--- + +## Interact with contract with `zkcast zk-send` + +> Aliases: `zkcast zks`, `zkcast zksend` + +Interact with deployed contracts in the native foundry/zkforge fashion using the CLI `zkcast zk-send` command. + +```sh +Sign and publish a zksync transaction. + +Usage: zkcast zk-send [OPTIONS] [TO] [SIG] [ARGS]... + +Arguments: + [TO] The destination of the transaction. + + [SIG] The signature of the function to call. + + [ARGS]... The arguments of the function to call. + +Options: + -h, --help Print help (see a summary with '-h') + +Bridging options: + -d, --deposit For L1 -> L2 deposits. + + -w, --withdraw For L2 -> L1 withdrawals. + + --token Token to bridge. Leave blank for ETH. + + -a, --amount Amount of token to bridge. Required value when bridging +``` + +- Retrieve and interact with chain data. For example, block numbers and gas estimates. +- Interact with deployed contracts on (zkSync Era testnet or local Docker node). + +### Non-state changing calls + +```sh +zkcast call --rpc-url +``` + +#### Example + +```bash +zkcast call 0x97b985951fd3e0c1d996421cc783d46c12d00082 "greet()(string)" --rpc-url http://localhost:3050 +``` + +#### Output + +```txt +ZkSync + Pineapple +``` + +### Send transactions + +```sh +zkcast zk-send --rpc-url --private-key --chain +``` + +#### Example + +```sh +zkcast zk-send 0x97b985951fd3e0c1d996421cc783d46c12d00082 "setGreeting(string)" "Killer combo!" --rpc-url http://localhost:3050 --private-key 7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110 --chain 270 +``` + +#### Output + +```txt +Sending transaction.... +Transaction Hash: 0x7651fba8ddeb624cca93f89da493675ccbc5c6d36ee25ed620b07424ce338552 +``` + +#### Verify output + +```sh +zkcast call 0x97b985951fd3e0c1d996421cc783d46c12d00082 "greet()(string)" --rpc-url http://localhost:3050 +``` + +#### Output + +```txt +Killer combo! +``` + +--- + +## Deploy and interact with `SimpleFactory.sol` + +### Compile contract + +`SimpleFactory.sol` must be compiled with the `is-system` flag, so they need to be placed in the `src/is-system/` folder + +```bash +zkforge zk-build +``` + +### Deploy `SimpleFactory.sol` + +```sh +zkforge zkc src/SimpleFactory.sol:SimpleFactory --constructor-args 01000041691510d85ddfc6047cba6643748dc028636d276f09a546ab330697ef 010000238a587670be26087b7812eab86eca61e7c4014522bdceda86adb2e82f --factory-deps src/Child.sol:Child src/StepChild.sol:StepChild --private-key 7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110 --rpc-url http://localhost:3050 --chain 270 +``` + +#### Output + +```txt +Deploying contract... ++-------------------------------------------------+ +Contract successfully deployed to address: 0xa1b809005e589f81de6ef9f48d67e35606c05fc3 +Transaction Hash: 0x34782985ba7c70b6bc4a8eb2b95787baec29356171fdbb18608037a2fcd7eda8 +Gas used: 168141 +Effective gas price: 250000000 +Block Number: 249 ++-------------------------------------------------+ +``` + +### Deploy `StepChild.sol` via `SimpleFactory.sol` + +```sh +zkcast zk-send 0x23cee3fb585b1e5092b7cfb222e8e873b05e9519 "newStepChild()" --rpc-url http://localhost:3050 --private-key 7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110 --chain 270 +``` + +#### Output + +```sh +Sending transaction.... +Transaction Hash: 0xa82a0636b71af058d4916d81868eebc41173ca07b78d30fe57f4b74e9294ef25 +``` + +### Interact with `SimpleFactory.sol` + +```sh +../foundry-zksync/target/debug/zkcast call 0x23cee3fb585b1e5092b7cfb222e8e873b05e9519 "stepChildren(uint256)(address)" 0 --rpc-url http://localhost:3050 +``` + +#### Output + +`StepChild.sol` deployed address: + +```txt +0xbc88C5Cdfe2659ebDD5dbb7e1a695A4cb189Df96 +``` + +### Interact with `StepChild.sol` + +Use `zkcast call` to check initial state: + +```sh +zkcast call 0xbc88C5Cdfe2659ebDD5dbb7e1a695A4cb189Df96 "isEnabled()(bool)" --rpc-url http://localhost:3050 +``` + +#### Output: + +```txt +false +``` + +Use `zkcast zk-send` to modify state: + +```sh +zkcast zk-send 0xbc88C5Cdfe2659ebDD5dbb7e1a695A4cb189Df96 "enable()" --rpc-url http://localhost:3050 --private-key 7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110 --chain 270 +``` + +#### Output + +```sh +Sending transaction.... +Transaction Hash: 0xe005e15e9f58b7dcdcc7b16a9d5c706ddef7a4c9cab82216ea944d5344ba01ae +``` + +Use `zkcast call` to check modified state. + +```sh +zkcast call 0xbc88C5Cdfe2659ebDD5dbb7e1a695A4cb189Df96 "isEnabled()(bool)" --rpc-url http://localhost:3050 +``` + +#### Output + +```txt +true +``` + +--- + +## Account abstraction multisig + +This section compiles, deploys, and interacts with the contracts from the zkSync Era [**Account Abstraction Multisig example**](https://era.zksync.io/docs/dev/tutorials/custom-aa-tutorial.html) + +Contracts: + +- [**AAFactory.sol**](https://era.zksync.io/docs/dev/tutorials/custom-aa-tutorial.html) +- [**TwoUserMultiSig.sol**](https://github.com/sammyshakes/sample-fzksync-project/blob/main/src/TwoUserMultiSig.sol) + +### Compile `AAFactory.sol` + +`AAFactory.sol` needs to be compiled with the `--is-system` flag because it will be interacting with system contracts to deploy the multisig wallets. + +Place the contract in the `src/is-system/` folder + +```sh +# command line using zkforge zk-build +../foundry-zksync/target/debug/zkforge zk-build +``` + +#### Output + +```sh +AAFactory -> Bytecode Hash: "010000791703a54dbe2502b00ee470989c267d0f6c0d12a9009a947715683744" +Compiled Successfully +``` + +### Deploy `AAFactory.sol`: + +To deploy the factory we need the `Bytecode Hash` of the `TwoUserMultiSig.sol` contract to provide to the constructor of `AAFactory.sol`. + +```js +constructor(bytes32 _aaBytecodeHash) { + aaBytecodeHash = _aaBytecodeHash; + } +``` + +Note: `aaBytecodeHash` = Bytecode hash of `TwoUserMultiSig.sol` + +To deploy a contract that deploys other contracts, it is necessary to provide the bytecodes of the child contracts in the `factory-deps` field of the transaction. This can be accomplished by using the `--factory-deps` flag and providing the full contract path in the format: `:` + +```sh +# command line using zkforge zk-create +../foundry-zksync/target/debug/zkforge zkc src/is-system/AAFactory.sol:AAFactory --constructor-args 010007572230f4df5b4e855ff48d4cdfffc9405522117d7e020ee42650223460 --factory-deps src/TwoUserMultiSig.sol:TwoUserMultisig --private-key 7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110 --rpc-url http://localhost:3050 --chain 270 +``` + +#### Output + +```sh +Deploying contract... ++-------------------------------------------------+ +Contract successfully deployed to address: 0xd5608cec132ed4875d19f8d815ec2ac58498b4e5 +Transaction Hash: 0x0e6f55ff1619af8b3277853a8f2941d0481635880358316f03ae264e2de059ed +Gas used: 154379 +Effective gas price: 250000000 +Block Number: 291 ++-------------------------------------------------+ +``` + +Now that we have the `AAFactory.sol` contract address we can call `deployAccount` function to deploy a new `TwoUserMultiSig.sol` instance. + +Here is the interface of `deployAccount`. + +```js +function deployAccount(bytes32 salt, address owner1, address owner2) external returns (address accountAddress) +``` + +We need to provide the two owner addresses for the newly deployed multisig: + +```js +owner1 = 0xa61464658AfeAf65CccaaFD3a512b69A83B77618 +owner2 = 0x0D43eB5B8a47bA8900d84AA36656c92024e9772e +``` + +We are also just using a `0x00` value for the ***salt*** parameter. (You will need a unique value for salt for each instance that uses same owner wallets). + +```sh +# command line using zkcast zk-send +../foundry-zksync/target/debug/zkcast zk-send 0xd5608cec132ed4875d19f8d815ec2ac58498b4e5 "deployAccount(bytes32,address,address)(address)" 0x00 0xa61464658AfeAf65CccaaFD3a512b69A83B77618 0x0D43eB5B8a47bA8900d84AA36656c92024e9772e --rpc-url http://localhost:3050 --private-key 7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110 --chain 270 +``` + +#### Output + +```sh +Sending transaction.... +Transaction Hash: 0x43a4dded84a12891dfae4124b42b9f091750e953193bd779a7e5e4d422909e73 +0x03e50ec034f1d363de0add752c33d4831a2731bf, <---- Deployed contract address +``` + +The new `TwoUserMultiSig.sol` contract has been deployed to: + +```txt +0x03e50ec034f1d363de0add752c33d4831a2731bf +``` + +Check the tx receipt using `zkcast tx ` + +```sh +../foundry-zksync/target/debug/zkcast tx 0x22364a3e191ad10013c5f20036e9696e743a4f686bc58a0106ef0b9e7592347c --rpc-url http://localhost:3050 +``` + +#### Output + +```sh +blockHash 0x2f3e2be46a7cb9f9e9df503903990e6670e88224e52232c988b5a730c82d98c0 +blockNumber 297 +from 0x36615Cf349d7F6344891B1e7CA7C72883F5dc049 +gas 217367 +gasPrice 250000000 +hash 0x43a4dded84a12891dfae4124b42b9f091750e953193bd779a7e5e4d422909e73 +input 0x76fb8b650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a61464658afeaf65cccaafd3a512b69a83b776180000000000000000000000000d43eb5b8a47ba8900d84aa36656c92024e9772e +nonce 147 +r 0x16385d99ccaaa5e84bb97d76a0afb310350c2ca4165ed41d458efa80cd76d3bd +s 0x3ec55287f223e760b7dd82a676feece939832e4c5a3d73f3aa979bd2cd48801c +to 0xd5608cEC132ED4875D19f8d815EC2ac58498B4E5 +transactionIndex 0 +v 1 +value 0 +l1BatchNumber 149 +l1BatchTxIndex 0 +``` + +Verify with `zkcast call` to call the public variables 'owner1' and 'owner2' on the newly deployed `TwoUserMultiSig.sol` contract. + +Verify `owner1`: + +```sh +# command line using zkcast call +../foundry-zksync/target/debug/zkcast call 0x03e50ec034f1d363de0add752c33d4831a2731bf "owner1()(address)" --rpc-url http://localhost:3050 +``` + +#### Output + +```txt +0xa61464658AfeAf65CccaaFD3a512b69A83B77618 +``` + +Verify `owner2`: + +```sh +# command line using zkcast call +../foundry-zksync/target/debug/zkcast call 0x03e50ec034f1d363de0add752c33d4831a2731bf "owner2()(address)" --rpc-url http://localhost:3050 +``` + +#### Output + +```txt +0x0D43eB5B8a47bA8900d84AA36656c92024e9772e +``` + +## Troubleshooting + +### Verify arguments + +Make sure that: + +* You are using zksync specific methods (`zkcreate` not `create`, `zksend` not `send`). +* You set the correct `--rpc-url`. +* You have the proper contract address - the bytecodes in zkSync Era are different to in EVM - so the resulting contracts will be deployed at different addresses. + +### 'Method not found' when calling 'send' + +If you get errors like `(code: -32601, message: Method not found, data: None)` - you are probably using a `send` method instead of `zksend`. + +### 'Could not get solc: Unknown version provided', 'checksum not found' + +These errors might show up on the Mac with ARM chip (M1, M2) due to the fact that most recent solc compilers are not auto-downloaded there. + +There are 2 workarounds: + + - Use an older compiler by adding `--use 0.8.19` flag to the `zk-build` command. + - Download the compiler manually and then use the `--offline` mode. (Download the compiler into ~/.svm/VERSION/solc-VERSION -- for example ~/.svm/0.8.20/solc-0.8.20). + +You can get the latest compiler version for MacOs AARCH here: https://github.com/ethers-rs/solc-builds/tree/master/macosx/aarch64 + +You might have to remove the `zkout` directory (that holds the compilation artifacts) and in some rare scenarios also cleanup the installed solc versions (by removing `~/.svm/` directory) + +### `solc` versions >0.8.19 are not supported, found 0.8.20 + +This means that our zksync compiler doesn't support that version of solidity yet. + +In such case, please remove the artifacts (by removing `zkout` directory) and re-run with the older version of solidity (`--use 0.8.19`) for example. + +You might also have to remove the `~/.svm/0.8.20/solc-0.8.20` file. \ No newline at end of file diff --git a/crates/abi/abi/HardhatConsole.json b/crates/abi/abi/HardhatConsole.json index c1b1b46cf..45fcc118e 100644 --- a/crates/abi/abi/HardhatConsole.json +++ b/crates/abi/abi/HardhatConsole.json @@ -1 +1,9514 @@ -[{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"}],"name":"logAddress","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"}],"name":"logBool","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"p0","type":"bytes"}],"name":"logBytes","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes1","name":"p0","type":"bytes1"}],"name":"logBytes1","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes10","name":"p0","type":"bytes10"}],"name":"logBytes10","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes11","name":"p0","type":"bytes11"}],"name":"logBytes11","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes12","name":"p0","type":"bytes12"}],"name":"logBytes12","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes13","name":"p0","type":"bytes13"}],"name":"logBytes13","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes14","name":"p0","type":"bytes14"}],"name":"logBytes14","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes15","name":"p0","type":"bytes15"}],"name":"logBytes15","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes16","name":"p0","type":"bytes16"}],"name":"logBytes16","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes17","name":"p0","type":"bytes17"}],"name":"logBytes17","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes18","name":"p0","type":"bytes18"}],"name":"logBytes18","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes19","name":"p0","type":"bytes19"}],"name":"logBytes19","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes2","name":"p0","type":"bytes2"}],"name":"logBytes2","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes20","name":"p0","type":"bytes20"}],"name":"logBytes20","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes21","name":"p0","type":"bytes21"}],"name":"logBytes21","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes22","name":"p0","type":"bytes22"}],"name":"logBytes22","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes23","name":"p0","type":"bytes23"}],"name":"logBytes23","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes24","name":"p0","type":"bytes24"}],"name":"logBytes24","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes25","name":"p0","type":"bytes25"}],"name":"logBytes25","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes26","name":"p0","type":"bytes26"}],"name":"logBytes26","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes27","name":"p0","type":"bytes27"}],"name":"logBytes27","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes28","name":"p0","type":"bytes28"}],"name":"logBytes28","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes29","name":"p0","type":"bytes29"}],"name":"logBytes29","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes3","name":"p0","type":"bytes3"}],"name":"logBytes3","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes30","name":"p0","type":"bytes30"}],"name":"logBytes30","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes31","name":"p0","type":"bytes31"}],"name":"logBytes31","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"p0","type":"bytes32"}],"name":"logBytes32","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"p0","type":"bytes4"}],"name":"logBytes4","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes5","name":"p0","type":"bytes5"}],"name":"logBytes5","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes6","name":"p0","type":"bytes6"}],"name":"logBytes6","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes7","name":"p0","type":"bytes7"}],"name":"logBytes7","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes8","name":"p0","type":"bytes8"}],"name":"logBytes8","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes9","name":"p0","type":"bytes9"}],"name":"logBytes9","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"p0","type":"int256"}],"name":"logInt","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"}],"name":"logString","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"}],"name":"logUint","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"p0","type":"int256"}],"outputs":[],"stateMutability":"view","type":"function","name":"log"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"int256","name":"p1","type":"int256"}],"outputs":[],"stateMutability":"view","type":"function","name":"log"}] +[ + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "bool", + "name": "p2", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p3", + "type": "uint256" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "bool", + "name": "p1", + "type": "bool" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "string", + "name": "p1", + "type": "string" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "p3", + "type": "bool" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "address", + "name": "p2", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + }, + { + "internalType": "address", + "name": "p1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "p2", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p3", + "type": "string" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "p1", + "type": "uint256" + }, + { + "internalType": "string", + "name": "p2", + "type": "string" + }, + { + "internalType": "address", + "name": "p3", + "type": "address" + } + ], + "name": "log", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "p0", + "type": "address" + } + ], + "name": "logAddress", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "p0", + "type": "bool" + } + ], + "name": "logBool", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "p0", + "type": "bytes" + } + ], + "name": "logBytes", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes1", + "name": "p0", + "type": "bytes1" + } + ], + "name": "logBytes1", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes10", + "name": "p0", + "type": "bytes10" + } + ], + "name": "logBytes10", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes11", + "name": "p0", + "type": "bytes11" + } + ], + "name": "logBytes11", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes12", + "name": "p0", + "type": "bytes12" + } + ], + "name": "logBytes12", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes13", + "name": "p0", + "type": "bytes13" + } + ], + "name": "logBytes13", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes14", + "name": "p0", + "type": "bytes14" + } + ], + "name": "logBytes14", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes15", + "name": "p0", + "type": "bytes15" + } + ], + "name": "logBytes15", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes16", + "name": "p0", + "type": "bytes16" + } + ], + "name": "logBytes16", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes17", + "name": "p0", + "type": "bytes17" + } + ], + "name": "logBytes17", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes18", + "name": "p0", + "type": "bytes18" + } + ], + "name": "logBytes18", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes19", + "name": "p0", + "type": "bytes19" + } + ], + "name": "logBytes19", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes2", + "name": "p0", + "type": "bytes2" + } + ], + "name": "logBytes2", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes20", + "name": "p0", + "type": "bytes20" + } + ], + "name": "logBytes20", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes21", + "name": "p0", + "type": "bytes21" + } + ], + "name": "logBytes21", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes22", + "name": "p0", + "type": "bytes22" + } + ], + "name": "logBytes22", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes23", + "name": "p0", + "type": "bytes23" + } + ], + "name": "logBytes23", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes24", + "name": "p0", + "type": "bytes24" + } + ], + "name": "logBytes24", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes25", + "name": "p0", + "type": "bytes25" + } + ], + "name": "logBytes25", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes26", + "name": "p0", + "type": "bytes26" + } + ], + "name": "logBytes26", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes27", + "name": "p0", + "type": "bytes27" + } + ], + "name": "logBytes27", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes28", + "name": "p0", + "type": "bytes28" + } + ], + "name": "logBytes28", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes29", + "name": "p0", + "type": "bytes29" + } + ], + "name": "logBytes29", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes3", + "name": "p0", + "type": "bytes3" + } + ], + "name": "logBytes3", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes30", + "name": "p0", + "type": "bytes30" + } + ], + "name": "logBytes30", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes31", + "name": "p0", + "type": "bytes31" + } + ], + "name": "logBytes31", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "p0", + "type": "bytes32" + } + ], + "name": "logBytes32", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "p0", + "type": "bytes4" + } + ], + "name": "logBytes4", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes5", + "name": "p0", + "type": "bytes5" + } + ], + "name": "logBytes5", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes6", + "name": "p0", + "type": "bytes6" + } + ], + "name": "logBytes6", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes7", + "name": "p0", + "type": "bytes7" + } + ], + "name": "logBytes7", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes8", + "name": "p0", + "type": "bytes8" + } + ], + "name": "logBytes8", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes9", + "name": "p0", + "type": "bytes9" + } + ], + "name": "logBytes9", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int256", + "name": "p0", + "type": "int256" + } + ], + "name": "logInt", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + } + ], + "name": "logString", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "p0", + "type": "uint256" + } + ], + "name": "logUint", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int256", + "name": "p0", + "type": "int256" + } + ], + "outputs": [], + "stateMutability": "view", + "type": "function", + "name": "log" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "p0", + "type": "string" + }, + { + "internalType": "int256", + "name": "p1", + "type": "int256" + } + ], + "outputs": [], + "stateMutability": "view", + "type": "function", + "name": "log" + } +] \ No newline at end of file diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index 5c9050615..62be7dbb1 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -16,6 +16,7 @@ use foundry_evm::{ inspectors::CheatsConfig, }; use foundry_utils::types::ToEthers; +use revm::primitives::SpecId; use solang_parser::pt::{self, CodeLocation}; use std::str::FromStr; use yansi::Paint; @@ -297,7 +298,7 @@ impl SessionSource { ) }) .gas_limit(self.config.evm_opts.gas_limit()) - .spec(self.config.foundry_config.evm_spec_id()) + .spec(SpecId::LATEST) .build(env, backend); // Create a [ChiselRunner] with a default balance of [U256::MAX] and diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index f12289aaa..b8447d6ab 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -26,6 +26,9 @@ alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true foundry-compilers.workspace = true +# zksync +zksync-web3-rs = {git = "https://github.com/lambdaclass/zksync-web3-rs.git", rev = "70327ae5413c517bd4d27502507cdd96ee40cd22"} + # io reqwest = { version = "0.11", default-features = false } diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 8b6484a02..66cfd4626 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -22,9 +22,11 @@ pub mod term; pub mod traits; pub mod transactions; pub mod units; +pub mod zk_utils; pub use constants::*; pub use contracts::*; pub use provider::*; pub use traits::*; pub use transactions::*; +pub use zk_utils::*; diff --git a/crates/common/src/zk_utils.rs b/crates/common/src/zk_utils.rs new file mode 100644 index 000000000..b71a00705 --- /dev/null +++ b/crates/common/src/zk_utils.rs @@ -0,0 +1,143 @@ +//! The `zk_utils` module provides utility functions specifically designed for interacting with +/// zkSync, an Ethereum layer 2 scaling solution. +/// +/// This module encapsulates various functionalities related to zkSync, including retrieving +/// the RPC URL for Ethereum, parsing and attaching a default port to a URL string, obtaining +/// the private key, retrieving the chain configuration, and creating a signer for zkSync +/// transactions. +/// +/// Functions in this module: +/// +/// - `get_rpc_url`: Retrieves the RPC URL for Ethereum. Returns `Result` with the RPC +/// URL if successful, or an error message if the RPC URL was not provided. +/// +/// - `get_url_with_port`: Parses a URL string and attaches a default port if one is not +/// specified. Returns an `Option` with the parsed URL if successful, or `None` if +/// the input was not a valid URL. +/// +/// - `get_private_key`: Gets the private key from the Ethereum options. Returns `Result` +/// with the private key as `H256` if successful, or an error message if the private key was +/// not provided. +/// +/// - `get_chain`: Gets the chain from the Ethereum options. Returns `Result` with the +/// chain configuration if successful, or an error message if the chain was not provided. +/// +/// - `get_signer`: Creates a signer from the private key and the chain. Returns a +/// `Signer` instance for signing transactions on the zkSync network. +/// +/// - `decode_hex`: Decodes a hexadecimal string into a byte vector. Returns `Result>` +/// with the decoded byte vector if successful, or a `ParseIntError` if the decoding fails. +use eyre::Result; +use foundry_config::Chain; +use std::num::ParseIntError; +use url::Url; +use zksync_web3_rs::types::H256; +/// Gets the RPC URL for Ethereum. +/// +/// If the `eth.rpc_url` is `None`, an error is returned. +/// +/// # Returns +/// +/// A `Result` which is: +/// - Ok: Contains the RPC URL as a String. +/// - Err: Contains an error message indicating that the RPC URL was not provided. +pub fn get_rpc_url(rpc_url: &Option) -> eyre::Result { + match rpc_url { + Some(url) => { + let rpc_url = get_url_with_port(url) + .ok_or_else(|| eyre::Report::msg("Invalid RPC_URL"))?; + Ok(rpc_url) + }, + None => Err(eyre::Report::msg("RPC URL was not provided. Try using --rpc-url flag or environment variable 'ETH_RPC_URL= '")), + } +} + +/// Parses a URL string and attaches a default port if one is not specified. +/// +/// This function takes a URL string as input and attempts to parse it. +/// If the URL string is not a valid URL, the function returns `None`. +/// If the URL is valid and has a specified port, the function returns the URL as is. +/// If the URL is valid but does not have a specified port, the function attaches a default +/// port. The default port is 443 if the URL uses the HTTPS scheme, and 80 otherwise. +/// +/// # Parameters +/// +/// - `url_str`: The URL string to parse. +/// +/// # Returns +/// +/// An `Option` which contains a String with the parsed URL if successful, or `None` if the +/// input was not a valid URL. +pub fn get_url_with_port(url_str: &str) -> Option { + let url = Url::parse(url_str).ok()?; + let default_port = url.scheme() == "https" && url.port().is_none(); + let port = url.port().unwrap_or(if default_port { 443 } else { 80 }); + Some(format!("{}://{}:{}{}", url.scheme(), url.host_str()?, port, url.path())) +} + +/// Gets the private key from the Ethereum options. +/// +/// If the `eth.wallet.private_key` is `None`, an error is returned. +/// +/// # Returns +/// +/// A `Result` which is: +/// - Ok: Contains the private key as `H256`. +/// - Err: Contains an error message indicating that the private key was not provided. +pub fn get_private_key(private_key: &Option) -> Result { + match private_key { + Some(pkey) => { + let val = decode_hex(pkey) + .map_err(|e| eyre::Report::msg(format!("Error parsing private key: {}", e)))?; + Ok(H256::from_slice(&val)) + } + None => { + Err(eyre::Report::msg("Private key was not provided. Try using --private-key flag")) + } + } +} + +/// Gets the chain from the Ethereum options. +/// +/// If the `eth.chain` is `None`, an error is returned. +/// +/// # Returns +/// +/// A `Result` which is: +/// - Ok: Contains the chain as `Chain`. +/// - Err: Contains an error message indicating that the chain was not provided. +pub fn get_chain(chain: Option) -> Result { + match chain { + Some(chain) => Ok(chain), + None => Err(eyre::Report::msg( + "Chain was not provided. Use --chain flag (ex. --chain 270 ) \nor environment variable 'CHAIN= ' (ex.'CHAIN=270')", + )), + } +} + +/// Decodes a hexadecimal string into a byte vector. +/// +/// This function takes a hexadecimal string as input and decodes it into a vector of bytes. +/// Each pair of hexadecimal characters in the input string represents one byte in the output +/// vector. +/// +/// # Arguments +/// +/// * `s` - A string representing a hexadecimal value. +/// +/// # Returns +/// +/// A `Result` containing the decoded byte vector if successful, or a `ParseIntError` if the +/// decoding fails. +/// +/// # Examples +/// +/// ``` +/// use foundry_cli::cmd::cast::zk_utils::decode_hex; +/// let hex_string = "48656c6c6f2c20576f726c6421"; +/// let bytes = decode_hex(hex_string).expect("Error decoding hex"); +/// assert_eq!(bytes, vec![72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33]); +/// ``` +pub fn decode_hex(s: &str) -> std::result::Result, ParseIntError> { + (0..s.len()).step_by(2).map(|i| u8::from_str_radix(&s[i..i + 2], 16)).collect() +} diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 8515f6df2..85d162b6f 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -77,7 +77,7 @@ pub mod fix; // reexport so cli types can implement `figment::Provider` to easily merge compiler arguments pub use figment; -use revm_primitives::SpecId; +use revm::primitives::SpecId; use tracing::warn; /// config providers diff --git a/crates/debugger/Cargo.toml b/crates/debugger/Cargo.toml index 2a0159460..88dafb374 100644 --- a/crates/debugger/Cargo.toml +++ b/crates/debugger/Cargo.toml @@ -18,5 +18,14 @@ alloy-primitives.workspace = true crossterm = "0.27" eyre.workspace = true tracing.workspace = true -revm = { workspace = true, default-features = false, features = ["std", "serde", "arbitrary"] } +# Using a fork of revm as zksync-era requires the usage of sha3 0.10.6, and the latest revm uses 0.10.8 +revm = { git = "https://github.com/dutterbutter/revm.git", tag = "sha3_0.10.6", default-features = false, features = [ + "std", + "serde", + "memory_limit", + "optional_eip3607", + "optional_block_gas_limit", + "optional_no_base_fee", + "arbitrary", +]} ratatui = { version = "0.23.0", default-features = false, features = ["crossterm"] } diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 14e686b03..ddcc9c52c 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -52,6 +52,18 @@ parking_lot = "0.12" futures = "0.3" once_cell = "1" +# EVM +bytes = "1" +hashbrown = { version = "0.14", features = ["serde"] } + +era_revm = { git = "https://github.com/matter-labs/era-revm.git", tag = "v0.0.1-alpha" } + +# Fuzzer +proptest = "1" + +# Display +yansi = "0.5" + # Misc url = "2" itertools.workspace = true diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index bdfe0a4c1..f4ef59cb9 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -762,17 +762,17 @@ impl Backend { pub fn inspect_ref( &mut self, env: &mut Env, - mut inspector: INSP, + inspector: INSP, ) -> eyre::Result where INSP: Inspector, { self.initialize(env); - match revm::evm_inner::(env, self, &mut inspector).transact() { - Ok(res) => Ok(res), - Err(e) => eyre::bail!("backend: failed while inspecting: {:?}", e), - } + let result: EVMResult = + era_revm::transactions::run_era_transaction(env, self, inspector); + + Ok(result.unwrap()) } /// Returns true if the address is a precompile diff --git a/crates/evm/core/test-data/storage.json b/crates/evm/core/test-data/storage.json index 4f2562591..e28a16998 100644 --- a/crates/evm/core/test-data/storage.json +++ b/crates/evm/core/test-data/storage.json @@ -1 +1,46 @@ -{"meta":{"cfg_env":{"chain_id":1,"spec_id":"LATEST","perf_all_precompiles_have_balance":false,"memory_limit":4294967295, "perf_analyse_created_bytecodes":"Analyse", "limit_contract_code_size": 24576, "disable_coinbase_tip": false},"block_env":{"number":"0xdc42b8","coinbase":"0x0000000000000000000000000000000000000000","timestamp":"0x1","difficulty":"0x0","basefee":"0x0","gas_limit":"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},"hosts":["mainnet.infura.io"]},"accounts":{"0x63091244180ae240c87d1f528f5f269134cb07b3":{"balance":"0x0","code_hash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","code":null,"nonce":0}},"storage":{"0x63091244180ae240c87d1f528f5f269134cb07b3":{"0x0":"0x0","0x1":"0x0","0x2":"0x0","0x3":"0x0","0x4":"0x0","0x5":"0x0","0x6":"0x0","0x7":"0x0","0x8":"0x0","0x9":"0x0"}},"block_hashes":{}} \ No newline at end of file +{ + "meta": { + "cfg_env": { + "chain_id": "0x1", + "spec_id": "LATEST", + "perf_all_precompiles_have_balance": false, + "memory_limit": 4294967295, + "perf_analyse_created_bytecodes": "Analyse", + "limit_contract_code_size": 24576 + }, + "block_env": { + "number": "0xdc42b8", + "coinbase": "0x0000000000000000000000000000000000000000", + "timestamp": "0x1", + "difficulty": "0x0", + "basefee": "0x0", + "gas_limit": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + }, + "hosts": [ + "mainnet.infura.io" + ] + }, + "accounts": { + "0x63091244180ae240c87d1f528f5f269134cb07b3": { + "balance": "0x0", + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "code": null, + "nonce": 0 + } + }, + "storage": { + "0x63091244180ae240c87d1f528f5f269134cb07b3": { + "0x0": "0x0", + "0x1": "0x0", + "0x2": "0x0", + "0x3": "0x0", + "0x4": "0x0", + "0x5": "0x0", + "0x6": "0x0", + "0x7": "0x0", + "0x8": "0x0", + "0x9": "0x0" + } + }, + "block_hashes": {} +} \ No newline at end of file diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 19b256541..85559d998 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -91,26 +91,8 @@ impl Executor { /// Creates the default CREATE2 Contract Deployer for local tests and scripts. pub fn deploy_create2_deployer(&mut self) -> eyre::Result<()> { trace!("deploying local create2 deployer"); - let create2_deployer_account = self - .backend - .basic(DEFAULT_CREATE2_DEPLOYER)? - .ok_or_else(|| DatabaseError::MissingAccount(DEFAULT_CREATE2_DEPLOYER))?; - // if the deployer is not currently deployed, deploy the default one - if create2_deployer_account.code.map_or(true, |code| code.is_empty()) { - let creator = "0x3fAB184622Dc19b6109349B94811493BF2a45362".parse().unwrap(); - - // Probably 0, but just in case. - let initial_balance = self.get_balance(creator)?; - - self.set_balance(creator, U256::MAX)?; - let res = - self.deploy(creator, DEFAULT_CREATE2_DEPLOYER_CODE.into(), U256::ZERO, None)?; - trace!(create2=?res.address, "deployed local create2 deployer"); - - self.set_balance(creator, initial_balance)?; - } - Ok(()) + return Ok(()) } /// Set the balance of an account. diff --git a/crates/evm/evm/src/executors/tracing.rs b/crates/evm/evm/src/executors/tracing.rs index 5734355af..429d298cf 100644 --- a/crates/evm/evm/src/executors/tracing.rs +++ b/crates/evm/evm/src/executors/tracing.rs @@ -14,7 +14,7 @@ impl TracingExecutor { pub async fn new( env: revm::primitives::Env, fork: Option, - version: Option, + _version: Option, debug: bool, ) -> Self { let db = Backend::spawn(fork).await; @@ -23,7 +23,7 @@ impl TracingExecutor { // tracing will be enabled only for the targeted transaction executor: ExecutorBuilder::new() .inspectors(|stack| stack.trace(true).debug(debug)) - .spec(evm_spec_id(&version.unwrap_or_default())) + .spec(SpecId::SHANGHAI) .build(env, db), } } diff --git a/crates/evm/test-data/solc-obj.json b/crates/evm/test-data/solc-obj.json index 9fd292e7b..5f7c5a33a 100644 --- a/crates/evm/test-data/solc-obj.json +++ b/crates/evm/test-data/solc-obj.json @@ -1 +1,402 @@ -{"abi":[{"inputs":[{"internalType":"address","name":"_feeToSetter","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token0","type":"address"},{"indexed":true,"internalType":"address","name":"token1","type":"address"},{"indexed":false,"internalType":"address","name":"pair","type":"address"},{"indexed":false,"internalType":"uint256","name":"","type":"uint256"}],"name":"PairCreated","type":"event"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allPairs","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"allPairsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"name":"createPair","outputs":[{"internalType":"address","name":"pair","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"feeTo","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"feeToSetter","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"getPair","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_feeTo","type":"address"}],"name":"setFeeTo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_feeToSetter","type":"address"}],"name":"setFeeToSetter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}],"evm":{"bytecode":{"linkReferences":{},"object":"608060405234801561001057600080fd5b506040516136863803806136868339818101604052602081101561003357600080fd5b5051600180546001600160a01b0319166001600160a01b03909216919091179055613623806100636000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c8063a2e74af61161005b578063a2e74af6146100fd578063c9c6539614610132578063e6a439051461016d578063f46901ed146101a857610088565b8063017e7e581461008d578063094b7415146100be5780631e3dd18b146100c6578063574f2ba3146100e3575b600080fd5b6100956101db565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b6100956101f7565b610095600480360360208110156100dc57600080fd5b5035610213565b6100eb610247565b60408051918252519081900360200190f35b6101306004803603602081101561011357600080fd5b503573ffffffffffffffffffffffffffffffffffffffff1661024d565b005b6100956004803603604081101561014857600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135811691602001351661031a565b6100956004803603604081101561018357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135811691602001351661076d565b610130600480360360208110156101be57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166107a0565b60005473ffffffffffffffffffffffffffffffffffffffff1681565b60015473ffffffffffffffffffffffffffffffffffffffff1681565b6003818154811061022057fe5b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16905081565b60035490565b60015473ffffffffffffffffffffffffffffffffffffffff1633146102d357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f556e697377617056323a20464f5242494444454e000000000000000000000000604482015290519081900360640190fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60008173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156103b757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f556e697377617056323a204944454e544943414c5f4144445245535345530000604482015290519081900360640190fd5b6000808373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16106103f45783856103f7565b84845b909250905073ffffffffffffffffffffffffffffffffffffffff821661047e57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f556e697377617056323a205a45524f5f41444452455353000000000000000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff82811660009081526002602090815260408083208585168452909152902054161561051f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f556e697377617056323a20504149525f45584953545300000000000000000000604482015290519081900360640190fd5b6060604051806020016105319061086d565b6020820181038252601f19601f82011660405250905060008383604051602001808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b81526014018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140192505050604051602081830303815290604052805190602001209050808251602084016000f5604080517f485cc95500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152868116602483015291519297509087169163485cc9559160448082019260009290919082900301818387803b15801561065e57600080fd5b505af1158015610672573d6000803e3d6000fd5b5050505073ffffffffffffffffffffffffffffffffffffffff84811660008181526002602081815260408084208987168086529083528185208054978d167fffffffffffffffffffffffff000000000000000000000000000000000000000098891681179091559383528185208686528352818520805488168517905560038054600181018255958190527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b90950180549097168417909655925483519283529082015281517f0d3648bd0f6ba80134a33ba9275ac585d9d315f0ad8355cddefde31afa28d0e9929181900390910190a35050505092915050565b600260209081526000928352604080842090915290825290205473ffffffffffffffffffffffffffffffffffffffff1681565b60015473ffffffffffffffffffffffffffffffffffffffff16331461082657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f556e697377617056323a20464f5242494444454e000000000000000000000000604482015290519081900360640190fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b612d748061087b8339019056fe60806040526001600c5534801561001557600080fd5b506040514690806052612d228239604080519182900360520182208282018252600a8352692ab734b9bbb0b8102b1960b11b6020938401528151808301835260018152603160f81b908401528151808401919091527fbfcc8ef98ffbf7b6c3fec7bf5185b566b9863e35a9d83acd49ad6824b5969738818301527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6606082015260808101949094523060a0808601919091528151808603909101815260c09094019052825192019190912060035550600580546001600160a01b03191633179055612c1d806101056000396000f3fe608060405234801561001057600080fd5b50600436106101b95760003560e01c80636a627842116100f9578063ba9a7a5611610097578063d21220a711610071578063d21220a7146105da578063d505accf146105e2578063dd62ed3e14610640578063fff6cae91461067b576101b9565b8063ba9a7a5614610597578063bc25cf771461059f578063c45a0155146105d2576101b9565b80637ecebe00116100d35780637ecebe00146104d757806389afcb441461050a57806395d89b4114610556578063a9059cbb1461055e576101b9565b80636a6278421461046957806370a082311461049c5780637464fc3d146104cf576101b9565b806323b872dd116101665780633644e515116101405780633644e51514610416578063485cc9551461041e5780635909c0d5146104595780635a3d549314610461576101b9565b806323b872dd146103ad57806330adf81f146103f0578063313ce567146103f8576101b9565b8063095ea7b311610197578063095ea7b3146103155780630dfe16811461036257806318160ddd14610393576101b9565b8063022c0d9f146101be57806306fdde03146102595780630902f1ac146102d6575b600080fd5b610257600480360360808110156101d457600080fd5b81359160208101359173ffffffffffffffffffffffffffffffffffffffff604083013516919081019060808101606082013564010000000081111561021857600080fd5b82018360208201111561022a57600080fd5b8035906020019184600183028401116401000000008311171561024c57600080fd5b509092509050610683565b005b610261610d57565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561029b578181015183820152602001610283565b50505050905090810190601f1680156102c85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102de610d90565b604080516dffffffffffffffffffffffffffff948516815292909316602083015263ffffffff168183015290519081900360600190f35b61034e6004803603604081101561032b57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610de5565b604080519115158252519081900360200190f35b61036a610dfc565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b61039b610e18565b60408051918252519081900360200190f35b61034e600480360360608110156103c357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160208101359091169060400135610e1e565b61039b610efd565b610400610f21565b6040805160ff9092168252519081900360200190f35b61039b610f26565b6102576004803603604081101561043457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020013516610f2c565b61039b611005565b61039b61100b565b61039b6004803603602081101561047f57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16611011565b61039b600480360360208110156104b257600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166113cb565b61039b6113dd565b61039b600480360360208110156104ed57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166113e3565b61053d6004803603602081101561052057600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166113f5565b6040805192835260208301919091528051918290030190f35b610261611892565b61034e6004803603604081101561057457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81351690602001356118cb565b61039b6118d8565b610257600480360360208110156105b557600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166118de565b61036a611ad4565b61036a611af0565b610257600480360360e08110156105f857600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160208101359091169060408101359060608101359060ff6080820135169060a08101359060c00135611b0c565b61039b6004803603604081101561065657600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020013516611dd8565b610257611df5565b600c546001146106f457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c55841515806107075750600084115b61075c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180612b2f6025913960400191505060405180910390fd5b600080610767610d90565b5091509150816dffffffffffffffffffffffffffff168710801561079a5750806dffffffffffffffffffffffffffff1686105b6107ef576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180612b786021913960400191505060405180910390fd5b600654600754600091829173ffffffffffffffffffffffffffffffffffffffff91821691908116908916821480159061085457508073ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614155b6108bf57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f556e697377617056323a20494e56414c49445f544f0000000000000000000000604482015290519081900360640190fd5b8a156108d0576108d0828a8d611fdb565b89156108e1576108e1818a8c611fdb565b86156109c3578873ffffffffffffffffffffffffffffffffffffffff166310d1e85c338d8d8c8c6040518663ffffffff1660e01b8152600401808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509650505050505050600060405180830381600087803b1580156109aa57600080fd5b505af11580156109be573d6000803e3d6000fd5b505050505b604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff8416916370a08231916024808301926020929190829003018186803b158015610a2f57600080fd5b505afa158015610a43573d6000803e3d6000fd5b505050506040513d6020811015610a5957600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905191955073ffffffffffffffffffffffffffffffffffffffff8316916370a0823191602480820192602092909190829003018186803b158015610acb57600080fd5b505afa158015610adf573d6000803e3d6000fd5b505050506040513d6020811015610af557600080fd5b5051925060009150506dffffffffffffffffffffffffffff85168a90038311610b1f576000610b35565b89856dffffffffffffffffffffffffffff160383035b9050600089856dffffffffffffffffffffffffffff16038311610b59576000610b6f565b89856dffffffffffffffffffffffffffff160383035b90506000821180610b805750600081115b610bd5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526024815260200180612b546024913960400191505060405180910390fd5b6000610c09610beb84600363ffffffff6121e816565b610bfd876103e863ffffffff6121e816565b9063ffffffff61226e16565b90506000610c21610beb84600363ffffffff6121e816565b9050610c59620f4240610c4d6dffffffffffffffffffffffffffff8b8116908b1663ffffffff6121e816565b9063ffffffff6121e816565b610c69838363ffffffff6121e816565b1015610cd657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f556e697377617056323a204b0000000000000000000000000000000000000000604482015290519081900360640190fd5b5050610ce4848488886122e0565b60408051838152602081018390528082018d9052606081018c9052905173ffffffffffffffffffffffffffffffffffffffff8b169133917fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d8229181900360800190a350506001600c55505050505050505050565b6040518060400160405280600a81526020017f556e69737761702056320000000000000000000000000000000000000000000081525081565b6008546dffffffffffffffffffffffffffff808216926e0100000000000000000000000000008304909116917c0100000000000000000000000000000000000000000000000000000000900463ffffffff1690565b6000610df233848461259c565b5060015b92915050565b60065473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b73ffffffffffffffffffffffffffffffffffffffff831660009081526002602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14610ee85773ffffffffffffffffffffffffffffffffffffffff84166000908152600260209081526040808320338452909152902054610eb6908363ffffffff61226e16565b73ffffffffffffffffffffffffffffffffffffffff851660009081526002602090815260408083203384529091529020555b610ef384848461260b565b5060019392505050565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b601281565b60035481565b60055473ffffffffffffffffffffffffffffffffffffffff163314610fb257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f556e697377617056323a20464f5242494444454e000000000000000000000000604482015290519081900360640190fd5b6006805473ffffffffffffffffffffffffffffffffffffffff9384167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560078054929093169116179055565b60095481565b600a5481565b6000600c5460011461108457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c81905580611094610d90565b50600654604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905193955091935060009273ffffffffffffffffffffffffffffffffffffffff909116916370a08231916024808301926020929190829003018186803b15801561110e57600080fd5b505afa158015611122573d6000803e3d6000fd5b505050506040513d602081101561113857600080fd5b5051600754604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905192935060009273ffffffffffffffffffffffffffffffffffffffff909216916370a0823191602480820192602092909190829003018186803b1580156111b157600080fd5b505afa1580156111c5573d6000803e3d6000fd5b505050506040513d60208110156111db57600080fd5b505190506000611201836dffffffffffffffffffffffffffff871663ffffffff61226e16565b90506000611225836dffffffffffffffffffffffffffff871663ffffffff61226e16565b9050600061123387876126ec565b600054909150806112705761125c6103e8610bfd611257878763ffffffff6121e816565b612878565b985061126b60006103e86128ca565b6112cd565b6112ca6dffffffffffffffffffffffffffff8916611294868463ffffffff6121e816565b8161129b57fe5b046dffffffffffffffffffffffffffff89166112bd868563ffffffff6121e816565b816112c457fe5b0461297a565b98505b60008911611326576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180612bc16028913960400191505060405180910390fd5b6113308a8a6128ca565b61133c86868a8a6122e0565b811561137e5760085461137a906dffffffffffffffffffffffffffff808216916e01000000000000000000000000000090041663ffffffff6121e816565b600b555b6040805185815260208101859052815133927f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f928290030190a250506001600c5550949695505050505050565b60016020526000908152604090205481565b600b5481565b60046020526000908152604090205481565b600080600c5460011461146957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c81905580611479610d90565b50600654600754604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905194965092945073ffffffffffffffffffffffffffffffffffffffff9182169391169160009184916370a08231916024808301926020929190829003018186803b1580156114fb57600080fd5b505afa15801561150f573d6000803e3d6000fd5b505050506040513d602081101561152557600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905191925060009173ffffffffffffffffffffffffffffffffffffffff8516916370a08231916024808301926020929190829003018186803b15801561159957600080fd5b505afa1580156115ad573d6000803e3d6000fd5b505050506040513d60208110156115c357600080fd5b5051306000908152600160205260408120549192506115e288886126ec565b600054909150806115f9848763ffffffff6121e816565b8161160057fe5b049a5080611614848663ffffffff6121e816565b8161161b57fe5b04995060008b11801561162e575060008a115b611683576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180612b996028913960400191505060405180910390fd5b61168d3084612992565b611698878d8d611fdb565b6116a3868d8c611fdb565b604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff8916916370a08231916024808301926020929190829003018186803b15801561170f57600080fd5b505afa158015611723573d6000803e3d6000fd5b505050506040513d602081101561173957600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905191965073ffffffffffffffffffffffffffffffffffffffff8816916370a0823191602480820192602092909190829003018186803b1580156117ab57600080fd5b505afa1580156117bf573d6000803e3d6000fd5b505050506040513d60208110156117d557600080fd5b505193506117e585858b8b6122e0565b811561182757600854611823906dffffffffffffffffffffffffffff808216916e01000000000000000000000000000090041663ffffffff6121e816565b600b555b604080518c8152602081018c9052815173ffffffffffffffffffffffffffffffffffffffff8f169233927fdccd412f0b1252819cb1fd330b93224ca42612892bb3f4f789976e6d81936496929081900390910190a35050505050505050506001600c81905550915091565b6040518060400160405280600681526020017f554e492d5632000000000000000000000000000000000000000000000000000081525081565b6000610df233848461260b565b6103e881565b600c5460011461194f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c55600654600754600854604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff9485169490931692611a2b9285928792611a26926dffffffffffffffffffffffffffff169185916370a0823191602480820192602092909190829003018186803b1580156119ee57600080fd5b505afa158015611a02573d6000803e3d6000fd5b505050506040513d6020811015611a1857600080fd5b50519063ffffffff61226e16565b611fdb565b600854604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051611aca9284928792611a26926e01000000000000000000000000000090046dffffffffffffffffffffffffffff169173ffffffffffffffffffffffffffffffffffffffff8616916370a0823191602480820192602092909190829003018186803b1580156119ee57600080fd5b50506001600c5550565b60055473ffffffffffffffffffffffffffffffffffffffff1681565b60075473ffffffffffffffffffffffffffffffffffffffff1681565b42841015611b7b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f556e697377617056323a20455850495245440000000000000000000000000000604482015290519081900360640190fd5b60035473ffffffffffffffffffffffffffffffffffffffff80891660008181526004602090815260408083208054600180820190925582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98186015280840196909652958d166060860152608085018c905260a085019590955260c08085018b90528151808603909101815260e0850182528051908301207f19010000000000000000000000000000000000000000000000000000000000006101008601526101028501969096526101228085019690965280518085039096018652610142840180825286519683019690962095839052610162840180825286905260ff89166101828501526101a284018890526101c28401879052519193926101e2808201937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081019281900390910190855afa158015611cdc573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590611d5757508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b611dc257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f556e697377617056323a20494e56414c49445f5349474e415455524500000000604482015290519081900360640190fd5b611dcd89898961259c565b505050505050505050565b600260209081526000928352604080842090915290825290205481565b600c54600114611e6657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c55600654604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051611fd49273ffffffffffffffffffffffffffffffffffffffff16916370a08231916024808301926020929190829003018186803b158015611edd57600080fd5b505afa158015611ef1573d6000803e3d6000fd5b505050506040513d6020811015611f0757600080fd5b5051600754604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff909216916370a0823191602480820192602092909190829003018186803b158015611f7a57600080fd5b505afa158015611f8e573d6000803e3d6000fd5b505050506040513d6020811015611fa457600080fd5b50516008546dffffffffffffffffffffffffffff808216916e0100000000000000000000000000009004166122e0565b6001600c55565b604080518082018252601981527f7472616e7366657228616464726573732c75696e743235362900000000000000602091820152815173ffffffffffffffffffffffffffffffffffffffff85811660248301526044808301869052845180840390910181526064909201845291810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001781529251815160009460609489169392918291908083835b602083106120e157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016120a4565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612143576040519150601f19603f3d011682016040523d82523d6000602084013e612148565b606091505b5091509150818015612176575080511580612176575080806020019051602081101561217357600080fd5b50515b6121e157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f556e697377617056323a205452414e534645525f4641494c4544000000000000604482015290519081900360640190fd5b5050505050565b60008115806122035750508082028282828161220057fe5b04145b610df657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f64732d6d6174682d6d756c2d6f766572666c6f77000000000000000000000000604482015290519081900360640190fd5b80820382811115610df657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f64732d6d6174682d7375622d756e646572666c6f770000000000000000000000604482015290519081900360640190fd5b6dffffffffffffffffffffffffffff841180159061230c57506dffffffffffffffffffffffffffff8311155b61237757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f556e697377617056323a204f564552464c4f5700000000000000000000000000604482015290519081900360640190fd5b60085463ffffffff428116917c0100000000000000000000000000000000000000000000000000000000900481168203908116158015906123c757506dffffffffffffffffffffffffffff841615155b80156123e257506dffffffffffffffffffffffffffff831615155b15612492578063ffffffff16612425856123fb86612a57565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169063ffffffff612a7b16565b600980547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff929092169290920201905563ffffffff8116612465846123fb87612a57565b600a80547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff92909216929092020190555b600880547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000166dffffffffffffffffffffffffffff888116919091177fffffffff0000000000000000000000000000ffffffffffffffffffffffffffff166e0100000000000000000000000000008883168102919091177bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c010000000000000000000000000000000000000000000000000000000063ffffffff871602179283905560408051848416815291909304909116602082015281517f1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1929181900390910190a1505050505050565b73ffffffffffffffffffffffffffffffffffffffff808416600081815260026020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260016020526040902054612641908263ffffffff61226e16565b73ffffffffffffffffffffffffffffffffffffffff8085166000908152600160205260408082209390935590841681522054612683908263ffffffff612abc16565b73ffffffffffffffffffffffffffffffffffffffff80841660008181526001602090815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600080600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663017e7e586040518163ffffffff1660e01b815260040160206040518083038186803b15801561275757600080fd5b505afa15801561276b573d6000803e3d6000fd5b505050506040513d602081101561278157600080fd5b5051600b5473ffffffffffffffffffffffffffffffffffffffff821615801594509192509061286457801561285f5760006127d86112576dffffffffffffffffffffffffffff88811690881663ffffffff6121e816565b905060006127e583612878565b90508082111561285c576000612813612804848463ffffffff61226e16565b6000549063ffffffff6121e816565b905060006128388361282c86600563ffffffff6121e816565b9063ffffffff612abc16565b9050600081838161284557fe5b04905080156128585761285887826128ca565b5050505b50505b612870565b8015612870576000600b555b505092915050565b600060038211156128bb575080600160028204015b818110156128b5578091506002818285816128a457fe5b0401816128ad57fe5b04905061288d565b506128c5565b81156128c5575060015b919050565b6000546128dd908263ffffffff612abc16565b600090815573ffffffffffffffffffffffffffffffffffffffff8316815260016020526040902054612915908263ffffffff612abc16565b73ffffffffffffffffffffffffffffffffffffffff831660008181526001602090815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6000818310612989578161298b565b825b9392505050565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600160205260409020546129c8908263ffffffff61226e16565b73ffffffffffffffffffffffffffffffffffffffff831660009081526001602052604081209190915554612a02908263ffffffff61226e16565b600090815560408051838152905173ffffffffffffffffffffffffffffffffffffffff8516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef919081900360200190a35050565b6dffffffffffffffffffffffffffff166e0100000000000000000000000000000290565b60006dffffffffffffffffffffffffffff82167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff841681612ab457fe5b049392505050565b80820182811015610df657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f64732d6d6174682d6164642d6f766572666c6f77000000000000000000000000604482015290519081900360640190fdfe556e697377617056323a20494e53554646494349454e545f4f55545055545f414d4f554e54556e697377617056323a20494e53554646494349454e545f494e5055545f414d4f554e54556e697377617056323a20494e53554646494349454e545f4c4951554944495459556e697377617056323a20494e53554646494349454e545f4c49515549444954595f4255524e4544556e697377617056323a20494e53554646494349454e545f4c49515549444954595f4d494e544544a265627a7a723158207dca18479e58487606bf70c79e44d8dee62353c9ee6d01f9a9d70885b8765f2264736f6c63430005100032454950373132446f6d61696e28737472696e67206e616d652c737472696e672076657273696f6e2c75696e7432353620636861696e49642c6164647265737320766572696679696e67436f6e747261637429a265627a7a723158202760f92d7fa1db6f5aa16307bad65df4ebcc8550c4b1f03755ab8dfd830c178f64736f6c63430005100032","opcodes":"PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 MLOAD PUSH2 0x3686 CODESIZE SUB DUP1 PUSH2 0x3686 DUP4 CODECOPY DUP2 DUP2 ADD PUSH1 0x40 MSTORE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x33 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x1 DUP1 SLOAD PUSH1 0x1 PUSH1 0x1 PUSH1 0xA0 SHL SUB NOT AND PUSH1 0x1 PUSH1 0x1 PUSH1 0xA0 SHL SUB SWAP1 SWAP3 AND SWAP2 SWAP1 SWAP2 OR SWAP1 SSTORE PUSH2 0x3623 DUP1 PUSH2 0x63 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH2 0x88 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0xA2E74AF6 GT PUSH2 0x5B JUMPI DUP1 PUSH4 0xA2E74AF6 EQ PUSH2 0xFD JUMPI DUP1 PUSH4 0xC9C65396 EQ PUSH2 0x132 JUMPI DUP1 PUSH4 0xE6A43905 EQ PUSH2 0x16D JUMPI DUP1 PUSH4 0xF46901ED EQ PUSH2 0x1A8 JUMPI PUSH2 0x88 JUMP JUMPDEST DUP1 PUSH4 0x17E7E58 EQ PUSH2 0x8D JUMPI DUP1 PUSH4 0x94B7415 EQ PUSH2 0xBE JUMPI DUP1 PUSH4 0x1E3DD18B EQ PUSH2 0xC6 JUMPI DUP1 PUSH4 0x574F2BA3 EQ PUSH2 0xE3 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x95 PUSH2 0x1DB JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x95 PUSH2 0x1F7 JUMP JUMPDEST PUSH2 0x95 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0xDC JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH2 0x213 JUMP JUMPDEST PUSH2 0xEB PUSH2 0x247 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x130 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x113 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x24D JUMP JUMPDEST STOP JUMPDEST PUSH2 0x95 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x148 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 ADD CALLDATALOAD AND PUSH2 0x31A JUMP JUMPDEST PUSH2 0x95 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x183 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 ADD CALLDATALOAD AND PUSH2 0x76D JUMP JUMPDEST PUSH2 0x130 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1BE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x7A0 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x3 DUP2 DUP2 SLOAD DUP2 LT PUSH2 0x220 JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 SWAP1 SWAP2 KECCAK256 ADD SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 POP DUP2 JUMP JUMPDEST PUSH1 0x3 SLOAD SWAP1 JUMP JUMPDEST PUSH1 0x1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x2D3 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20464F5242494444454E000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x1 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP3 SWAP1 SWAP3 AND SWAP2 SWAP1 SWAP2 OR SWAP1 SSTORE JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO PUSH2 0x3B7 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x1E PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204944454E544943414C5F4144445245535345530000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 DUP1 DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND LT PUSH2 0x3F4 JUMPI DUP4 DUP6 PUSH2 0x3F7 JUMP JUMPDEST DUP5 DUP5 JUMPDEST SWAP1 SWAP3 POP SWAP1 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 AND PUSH2 0x47E JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x17 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A205A45524F5F41444452455353000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 DUP6 DUP6 AND DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SLOAD AND ISZERO PUSH2 0x51F JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x16 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20504149525F45584953545300000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x60 PUSH1 0x40 MLOAD DUP1 PUSH1 0x20 ADD PUSH2 0x531 SWAP1 PUSH2 0x86D JUMP JUMPDEST PUSH1 0x20 DUP3 ADD DUP2 SUB DUP3 MSTORE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND PUSH1 0x40 MSTORE POP SWAP1 POP PUSH1 0x0 DUP4 DUP4 PUSH1 0x40 MLOAD PUSH1 0x20 ADD DUP1 DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH1 0x60 SHL DUP2 MSTORE PUSH1 0x14 ADD DUP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH1 0x60 SHL DUP2 MSTORE PUSH1 0x14 ADD SWAP3 POP POP POP PUSH1 0x40 MLOAD PUSH1 0x20 DUP2 DUP4 SUB SUB DUP2 MSTORE SWAP1 PUSH1 0x40 MSTORE DUP1 MLOAD SWAP1 PUSH1 0x20 ADD KECCAK256 SWAP1 POP DUP1 DUP3 MLOAD PUSH1 0x20 DUP5 ADD PUSH1 0x0 CREATE2 PUSH1 0x40 DUP1 MLOAD PUSH32 0x485CC95500000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 DUP2 AND PUSH1 0x4 DUP4 ADD MSTORE DUP7 DUP2 AND PUSH1 0x24 DUP4 ADD MSTORE SWAP2 MLOAD SWAP3 SWAP8 POP SWAP1 DUP8 AND SWAP2 PUSH4 0x485CC955 SWAP2 PUSH1 0x44 DUP1 DUP3 ADD SWAP3 PUSH1 0x0 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP4 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x65E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x672 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 DUP2 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 DUP2 DUP2 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 DUP10 DUP8 AND DUP1 DUP7 MSTORE SWAP1 DUP4 MSTORE DUP2 DUP6 KECCAK256 DUP1 SLOAD SWAP8 DUP14 AND PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 SWAP9 DUP10 AND DUP2 OR SWAP1 SWAP2 SSTORE SWAP4 DUP4 MSTORE DUP2 DUP6 KECCAK256 DUP7 DUP7 MSTORE DUP4 MSTORE DUP2 DUP6 KECCAK256 DUP1 SLOAD DUP9 AND DUP6 OR SWAP1 SSTORE PUSH1 0x3 DUP1 SLOAD PUSH1 0x1 DUP2 ADD DUP3 SSTORE SWAP6 DUP2 SWAP1 MSTORE PUSH32 0xC2575A0E9E593C00F959F8C92F12DB2869C3395A3B0502D05E2516446F71F85B SWAP1 SWAP6 ADD DUP1 SLOAD SWAP1 SWAP8 AND DUP5 OR SWAP1 SWAP7 SSTORE SWAP3 SLOAD DUP4 MLOAD SWAP3 DUP4 MSTORE SWAP1 DUP3 ADD MSTORE DUP2 MLOAD PUSH32 0xD3648BD0F6BA80134A33BA9275AC585D9D315F0AD8355CDDEFDE31AFA28D0E9 SWAP3 SWAP2 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x826 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20464F5242494444454E000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP3 SWAP1 SWAP3 AND SWAP2 SWAP1 SWAP2 OR SWAP1 SSTORE JUMP JUMPDEST PUSH2 0x2D74 DUP1 PUSH2 0x87B DUP4 CODECOPY ADD SWAP1 JUMP INVALID PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x1 PUSH1 0xC SSTORE CALLVALUE DUP1 ISZERO PUSH2 0x15 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 MLOAD CHAINID SWAP1 DUP1 PUSH1 0x52 PUSH2 0x2D22 DUP3 CODECOPY PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 SWAP1 SUB PUSH1 0x52 ADD DUP3 KECCAK256 DUP3 DUP3 ADD DUP3 MSTORE PUSH1 0xA DUP4 MSTORE PUSH10 0x2AB734B9BBB0B8102B19 PUSH1 0xB1 SHL PUSH1 0x20 SWAP4 DUP5 ADD MSTORE DUP2 MLOAD DUP1 DUP4 ADD DUP4 MSTORE PUSH1 0x1 DUP2 MSTORE PUSH1 0x31 PUSH1 0xF8 SHL SWAP1 DUP5 ADD MSTORE DUP2 MLOAD DUP1 DUP5 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH32 0xBFCC8EF98FFBF7B6C3FEC7BF5185B566B9863E35A9D83ACD49AD6824B5969738 DUP2 DUP4 ADD MSTORE PUSH32 0xC89EFDAA54C0F20C7ADF612882DF0950F5A951637E0307CDCB4C672F298B8BC6 PUSH1 0x60 DUP3 ADD MSTORE PUSH1 0x80 DUP2 ADD SWAP5 SWAP1 SWAP5 MSTORE ADDRESS PUSH1 0xA0 DUP1 DUP7 ADD SWAP2 SWAP1 SWAP2 MSTORE DUP2 MLOAD DUP1 DUP7 SUB SWAP1 SWAP2 ADD DUP2 MSTORE PUSH1 0xC0 SWAP1 SWAP5 ADD SWAP1 MSTORE DUP3 MLOAD SWAP3 ADD SWAP2 SWAP1 SWAP2 KECCAK256 PUSH1 0x3 SSTORE POP PUSH1 0x5 DUP1 SLOAD PUSH1 0x1 PUSH1 0x1 PUSH1 0xA0 SHL SUB NOT AND CALLER OR SWAP1 SSTORE PUSH2 0x2C1D DUP1 PUSH2 0x105 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH2 0x1B9 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x6A627842 GT PUSH2 0xF9 JUMPI DUP1 PUSH4 0xBA9A7A56 GT PUSH2 0x97 JUMPI DUP1 PUSH4 0xD21220A7 GT PUSH2 0x71 JUMPI DUP1 PUSH4 0xD21220A7 EQ PUSH2 0x5DA JUMPI DUP1 PUSH4 0xD505ACCF EQ PUSH2 0x5E2 JUMPI DUP1 PUSH4 0xDD62ED3E EQ PUSH2 0x640 JUMPI DUP1 PUSH4 0xFFF6CAE9 EQ PUSH2 0x67B JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0xBA9A7A56 EQ PUSH2 0x597 JUMPI DUP1 PUSH4 0xBC25CF77 EQ PUSH2 0x59F JUMPI DUP1 PUSH4 0xC45A0155 EQ PUSH2 0x5D2 JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x7ECEBE00 GT PUSH2 0xD3 JUMPI DUP1 PUSH4 0x7ECEBE00 EQ PUSH2 0x4D7 JUMPI DUP1 PUSH4 0x89AFCB44 EQ PUSH2 0x50A JUMPI DUP1 PUSH4 0x95D89B41 EQ PUSH2 0x556 JUMPI DUP1 PUSH4 0xA9059CBB EQ PUSH2 0x55E JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x6A627842 EQ PUSH2 0x469 JUMPI DUP1 PUSH4 0x70A08231 EQ PUSH2 0x49C JUMPI DUP1 PUSH4 0x7464FC3D EQ PUSH2 0x4CF JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x23B872DD GT PUSH2 0x166 JUMPI DUP1 PUSH4 0x3644E515 GT PUSH2 0x140 JUMPI DUP1 PUSH4 0x3644E515 EQ PUSH2 0x416 JUMPI DUP1 PUSH4 0x485CC955 EQ PUSH2 0x41E JUMPI DUP1 PUSH4 0x5909C0D5 EQ PUSH2 0x459 JUMPI DUP1 PUSH4 0x5A3D5493 EQ PUSH2 0x461 JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x23B872DD EQ PUSH2 0x3AD JUMPI DUP1 PUSH4 0x30ADF81F EQ PUSH2 0x3F0 JUMPI DUP1 PUSH4 0x313CE567 EQ PUSH2 0x3F8 JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x95EA7B3 GT PUSH2 0x197 JUMPI DUP1 PUSH4 0x95EA7B3 EQ PUSH2 0x315 JUMPI DUP1 PUSH4 0xDFE1681 EQ PUSH2 0x362 JUMPI DUP1 PUSH4 0x18160DDD EQ PUSH2 0x393 JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x22C0D9F EQ PUSH2 0x1BE JUMPI DUP1 PUSH4 0x6FDDE03 EQ PUSH2 0x259 JUMPI DUP1 PUSH4 0x902F1AC EQ PUSH2 0x2D6 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x257 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x80 DUP2 LT ISZERO PUSH2 0x1D4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD SWAP2 PUSH1 0x20 DUP2 ADD CALLDATALOAD SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x40 DUP4 ADD CALLDATALOAD AND SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH1 0x80 DUP2 ADD PUSH1 0x60 DUP3 ADD CALLDATALOAD PUSH5 0x100000000 DUP2 GT ISZERO PUSH2 0x218 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP3 ADD DUP4 PUSH1 0x20 DUP3 ADD GT ISZERO PUSH2 0x22A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP2 DUP5 PUSH1 0x1 DUP4 MUL DUP5 ADD GT PUSH5 0x100000000 DUP4 GT OR ISZERO PUSH2 0x24C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP SWAP1 SWAP3 POP SWAP1 POP PUSH2 0x683 JUMP JUMPDEST STOP JUMPDEST PUSH2 0x261 PUSH2 0xD57 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 ADD SWAP1 DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x29B JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x283 JUMP JUMPDEST POP POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x2C8 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH2 0x2DE PUSH2 0xD90 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP5 DUP6 AND DUP2 MSTORE SWAP3 SWAP1 SWAP4 AND PUSH1 0x20 DUP4 ADD MSTORE PUSH4 0xFFFFFFFF AND DUP2 DUP4 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x60 ADD SWAP1 RETURN JUMPDEST PUSH2 0x34E PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x32B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD AND SWAP1 PUSH1 0x20 ADD CALLDATALOAD PUSH2 0xDE5 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x36A PUSH2 0xDFC JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x39B PUSH2 0xE18 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x34E PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x60 DUP2 LT ISZERO PUSH2 0x3C3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 DUP2 ADD CALLDATALOAD SWAP1 SWAP2 AND SWAP1 PUSH1 0x40 ADD CALLDATALOAD PUSH2 0xE1E JUMP JUMPDEST PUSH2 0x39B PUSH2 0xEFD JUMP JUMPDEST PUSH2 0x400 PUSH2 0xF21 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0xFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x39B PUSH2 0xF26 JUMP JUMPDEST PUSH2 0x257 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x434 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 ADD CALLDATALOAD AND PUSH2 0xF2C JUMP JUMPDEST PUSH2 0x39B PUSH2 0x1005 JUMP JUMPDEST PUSH2 0x39B PUSH2 0x100B JUMP JUMPDEST PUSH2 0x39B PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x47F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x1011 JUMP JUMPDEST PUSH2 0x39B PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x4B2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x13CB JUMP JUMPDEST PUSH2 0x39B PUSH2 0x13DD JUMP JUMPDEST PUSH2 0x39B PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x4ED JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x13E3 JUMP JUMPDEST PUSH2 0x53D PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x520 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x13F5 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP3 DUP4 MSTORE PUSH1 0x20 DUP4 ADD SWAP2 SWAP1 SWAP2 MSTORE DUP1 MLOAD SWAP2 DUP3 SWAP1 SUB ADD SWAP1 RETURN JUMPDEST PUSH2 0x261 PUSH2 0x1892 JUMP JUMPDEST PUSH2 0x34E PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x574 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD AND SWAP1 PUSH1 0x20 ADD CALLDATALOAD PUSH2 0x18CB JUMP JUMPDEST PUSH2 0x39B PUSH2 0x18D8 JUMP JUMPDEST PUSH2 0x257 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x5B5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x18DE JUMP JUMPDEST PUSH2 0x36A PUSH2 0x1AD4 JUMP JUMPDEST PUSH2 0x36A PUSH2 0x1AF0 JUMP JUMPDEST PUSH2 0x257 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0xE0 DUP2 LT ISZERO PUSH2 0x5F8 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 DUP2 ADD CALLDATALOAD SWAP1 SWAP2 AND SWAP1 PUSH1 0x40 DUP2 ADD CALLDATALOAD SWAP1 PUSH1 0x60 DUP2 ADD CALLDATALOAD SWAP1 PUSH1 0xFF PUSH1 0x80 DUP3 ADD CALLDATALOAD AND SWAP1 PUSH1 0xA0 DUP2 ADD CALLDATALOAD SWAP1 PUSH1 0xC0 ADD CALLDATALOAD PUSH2 0x1B0C JUMP JUMPDEST PUSH2 0x39B PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x656 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 ADD CALLDATALOAD AND PUSH2 0x1DD8 JUMP JUMPDEST PUSH2 0x257 PUSH2 0x1DF5 JUMP JUMPDEST PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x6F4 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC SSTORE DUP5 ISZERO ISZERO DUP1 PUSH2 0x707 JUMPI POP PUSH1 0x0 DUP5 GT JUMPDEST PUSH2 0x75C JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x25 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2B2F PUSH1 0x25 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH1 0x0 DUP1 PUSH2 0x767 PUSH2 0xD90 JUMP JUMPDEST POP SWAP2 POP SWAP2 POP DUP2 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP8 LT DUP1 ISZERO PUSH2 0x79A JUMPI POP DUP1 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP7 LT JUMPDEST PUSH2 0x7EF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x21 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2B78 PUSH1 0x21 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH1 0x6 SLOAD PUSH1 0x7 SLOAD PUSH1 0x0 SWAP2 DUP3 SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP2 DUP3 AND SWAP2 SWAP1 DUP2 AND SWAP1 DUP10 AND DUP3 EQ DUP1 ISZERO SWAP1 PUSH2 0x854 JUMPI POP DUP1 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP10 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO JUMPDEST PUSH2 0x8BF JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x15 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20494E56414C49445F544F0000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST DUP11 ISZERO PUSH2 0x8D0 JUMPI PUSH2 0x8D0 DUP3 DUP11 DUP14 PUSH2 0x1FDB JUMP JUMPDEST DUP10 ISZERO PUSH2 0x8E1 JUMPI PUSH2 0x8E1 DUP2 DUP11 DUP13 PUSH2 0x1FDB JUMP JUMPDEST DUP7 ISZERO PUSH2 0x9C3 JUMPI DUP9 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0x10D1E85C CALLER DUP14 DUP14 DUP13 DUP13 PUSH1 0x40 MLOAD DUP7 PUSH4 0xFFFFFFFF AND PUSH1 0xE0 SHL DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP7 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 MSTORE PUSH1 0x20 ADD DUP6 DUP2 MSTORE PUSH1 0x20 ADD DUP5 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE DUP5 DUP5 DUP3 DUP2 DUP2 MSTORE PUSH1 0x20 ADD SWAP3 POP DUP1 DUP3 DUP5 CALLDATACOPY PUSH1 0x0 DUP2 DUP5 ADD MSTORE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND SWAP1 POP DUP1 DUP4 ADD SWAP3 POP POP POP SWAP7 POP POP POP POP POP POP POP PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x9AA JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x9BE JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0xA2F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0xA43 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0xA59 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP2 SWAP6 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0xACB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0xADF JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0xAF5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD SWAP3 POP PUSH1 0x0 SWAP2 POP POP PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND DUP11 SWAP1 SUB DUP4 GT PUSH2 0xB1F JUMPI PUSH1 0x0 PUSH2 0xB35 JUMP JUMPDEST DUP10 DUP6 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SUB DUP4 SUB JUMPDEST SWAP1 POP PUSH1 0x0 DUP10 DUP6 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SUB DUP4 GT PUSH2 0xB59 JUMPI PUSH1 0x0 PUSH2 0xB6F JUMP JUMPDEST DUP10 DUP6 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SUB DUP4 SUB JUMPDEST SWAP1 POP PUSH1 0x0 DUP3 GT DUP1 PUSH2 0xB80 JUMPI POP PUSH1 0x0 DUP2 GT JUMPDEST PUSH2 0xBD5 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x24 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2B54 PUSH1 0x24 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xC09 PUSH2 0xBEB DUP5 PUSH1 0x3 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH2 0xBFD DUP8 PUSH2 0x3E8 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0xC21 PUSH2 0xBEB DUP5 PUSH1 0x3 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 POP PUSH2 0xC59 PUSH3 0xF4240 PUSH2 0xC4D PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP12 DUP2 AND SWAP1 DUP12 AND PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH2 0xC69 DUP4 DUP4 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST LT ISZERO PUSH2 0xCD6 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0xC PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204B0000000000000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST POP POP PUSH2 0xCE4 DUP5 DUP5 DUP9 DUP9 PUSH2 0x22E0 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP4 DUP2 MSTORE PUSH1 0x20 DUP2 ADD DUP4 SWAP1 MSTORE DUP1 DUP3 ADD DUP14 SWAP1 MSTORE PUSH1 0x60 DUP2 ADD DUP13 SWAP1 MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP12 AND SWAP2 CALLER SWAP2 PUSH32 0xD78AD95FA46C994B6551D0DA85FC275FE613CE37657FB8D5E3D130840159D822 SWAP2 DUP2 SWAP1 SUB PUSH1 0x80 ADD SWAP1 LOG3 POP POP PUSH1 0x1 PUSH1 0xC SSTORE POP POP POP POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 PUSH1 0x40 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0xA DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x556E697377617020563200000000000000000000000000000000000000000000 DUP2 MSTORE POP DUP2 JUMP JUMPDEST PUSH1 0x8 SLOAD PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP3 AND SWAP3 PUSH15 0x10000000000000000000000000000 DUP4 DIV SWAP1 SWAP2 AND SWAP2 PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV PUSH4 0xFFFFFFFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xDF2 CALLER DUP5 DUP5 PUSH2 0x259C JUMP JUMPDEST POP PUSH1 0x1 JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x6 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x0 SLOAD DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE SWAP1 SWAP2 MSTORE DUP2 KECCAK256 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF EQ PUSH2 0xEE8 JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SLOAD PUSH2 0xEB6 SWAP1 DUP4 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SSTORE JUMPDEST PUSH2 0xEF3 DUP5 DUP5 DUP5 PUSH2 0x260B JUMP JUMPDEST POP PUSH1 0x1 SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH32 0x6E71EDAE12B1B97F4D1F60370FEF10105FA2FAAE0126114A169C64845D6126C9 DUP2 JUMP JUMPDEST PUSH1 0x12 DUP2 JUMP JUMPDEST PUSH1 0x3 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x5 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0xFB2 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20464F5242494444454E000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x6 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP4 DUP5 AND PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 SWAP2 DUP3 AND OR SWAP1 SWAP2 SSTORE PUSH1 0x7 DUP1 SLOAD SWAP3 SWAP1 SWAP4 AND SWAP2 AND OR SWAP1 SSTORE JUMP JUMPDEST PUSH1 0x9 SLOAD DUP2 JUMP JUMPDEST PUSH1 0xA SLOAD DUP2 JUMP JUMPDEST PUSH1 0x0 PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x1084 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC DUP2 SWAP1 SSTORE DUP1 PUSH2 0x1094 PUSH2 0xD90 JUMP JUMPDEST POP PUSH1 0x6 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP4 SWAP6 POP SWAP2 SWAP4 POP PUSH1 0x0 SWAP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP2 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x110E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1122 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1138 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x7 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP3 SWAP4 POP PUSH1 0x0 SWAP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP3 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x11B1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x11C5 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x11DB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD SWAP1 POP PUSH1 0x0 PUSH2 0x1201 DUP4 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x1225 DUP4 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x1233 DUP8 DUP8 PUSH2 0x26EC JUMP JUMPDEST PUSH1 0x0 SLOAD SWAP1 SWAP2 POP DUP1 PUSH2 0x1270 JUMPI PUSH2 0x125C PUSH2 0x3E8 PUSH2 0xBFD PUSH2 0x1257 DUP8 DUP8 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH2 0x2878 JUMP JUMPDEST SWAP9 POP PUSH2 0x126B PUSH1 0x0 PUSH2 0x3E8 PUSH2 0x28CA JUMP JUMPDEST PUSH2 0x12CD JUMP JUMPDEST PUSH2 0x12CA PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP10 AND PUSH2 0x1294 DUP7 DUP5 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST DUP2 PUSH2 0x129B JUMPI INVALID JUMPDEST DIV PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP10 AND PUSH2 0x12BD DUP7 DUP6 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST DUP2 PUSH2 0x12C4 JUMPI INVALID JUMPDEST DIV PUSH2 0x297A JUMP JUMPDEST SWAP9 POP JUMPDEST PUSH1 0x0 DUP10 GT PUSH2 0x1326 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x28 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2BC1 PUSH1 0x28 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH2 0x1330 DUP11 DUP11 PUSH2 0x28CA JUMP JUMPDEST PUSH2 0x133C DUP7 DUP7 DUP11 DUP11 PUSH2 0x22E0 JUMP JUMPDEST DUP2 ISZERO PUSH2 0x137E JUMPI PUSH1 0x8 SLOAD PUSH2 0x137A SWAP1 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP3 AND SWAP2 PUSH15 0x10000000000000000000000000000 SWAP1 DIV AND PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH1 0xB SSTORE JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP6 DUP2 MSTORE PUSH1 0x20 DUP2 ADD DUP6 SWAP1 MSTORE DUP2 MLOAD CALLER SWAP3 PUSH32 0x4C209B5FC8AD50758F13E2E1088BA56A560DFF690A1C6FEF26394F4C03821C4F SWAP3 DUP3 SWAP1 SUB ADD SWAP1 LOG2 POP POP PUSH1 0x1 PUSH1 0xC SSTORE POP SWAP5 SWAP7 SWAP6 POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH1 0xB SLOAD DUP2 JUMP JUMPDEST PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x1469 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC DUP2 SWAP1 SSTORE DUP1 PUSH2 0x1479 PUSH2 0xD90 JUMP JUMPDEST POP PUSH1 0x6 SLOAD PUSH1 0x7 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP5 SWAP7 POP SWAP3 SWAP5 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP2 DUP3 AND SWAP4 SWAP2 AND SWAP2 PUSH1 0x0 SWAP2 DUP5 SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x14FB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x150F JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1525 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP2 SWAP3 POP PUSH1 0x0 SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x1599 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x15AD JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x15C3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD ADDRESS PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 SLOAD SWAP2 SWAP3 POP PUSH2 0x15E2 DUP9 DUP9 PUSH2 0x26EC JUMP JUMPDEST PUSH1 0x0 SLOAD SWAP1 SWAP2 POP DUP1 PUSH2 0x15F9 DUP5 DUP8 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST DUP2 PUSH2 0x1600 JUMPI INVALID JUMPDEST DIV SWAP11 POP DUP1 PUSH2 0x1614 DUP5 DUP7 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST DUP2 PUSH2 0x161B JUMPI INVALID JUMPDEST DIV SWAP10 POP PUSH1 0x0 DUP12 GT DUP1 ISZERO PUSH2 0x162E JUMPI POP PUSH1 0x0 DUP11 GT JUMPDEST PUSH2 0x1683 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x28 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2B99 PUSH1 0x28 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH2 0x168D ADDRESS DUP5 PUSH2 0x2992 JUMP JUMPDEST PUSH2 0x1698 DUP8 DUP14 DUP14 PUSH2 0x1FDB JUMP JUMPDEST PUSH2 0x16A3 DUP7 DUP14 DUP13 PUSH2 0x1FDB JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP10 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x170F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1723 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1739 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP2 SWAP7 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x17AB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x17BF JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x17D5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD SWAP4 POP PUSH2 0x17E5 DUP6 DUP6 DUP12 DUP12 PUSH2 0x22E0 JUMP JUMPDEST DUP2 ISZERO PUSH2 0x1827 JUMPI PUSH1 0x8 SLOAD PUSH2 0x1823 SWAP1 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP3 AND SWAP2 PUSH15 0x10000000000000000000000000000 SWAP1 DIV AND PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH1 0xB SSTORE JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP13 DUP2 MSTORE PUSH1 0x20 DUP2 ADD DUP13 SWAP1 MSTORE DUP2 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP16 AND SWAP3 CALLER SWAP3 PUSH32 0xDCCD412F0B1252819CB1FD330B93224CA42612892BB3F4F789976E6D81936496 SWAP3 SWAP1 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP POP POP POP POP POP POP POP POP PUSH1 0x1 PUSH1 0xC DUP2 SWAP1 SSTORE POP SWAP2 POP SWAP2 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 PUSH1 0x40 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x6 DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x554E492D56320000000000000000000000000000000000000000000000000000 DUP2 MSTORE POP DUP2 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xDF2 CALLER DUP5 DUP5 PUSH2 0x260B JUMP JUMPDEST PUSH2 0x3E8 DUP2 JUMP JUMPDEST PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x194F JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC SSTORE PUSH1 0x6 SLOAD PUSH1 0x7 SLOAD PUSH1 0x8 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP5 DUP6 AND SWAP5 SWAP1 SWAP4 AND SWAP3 PUSH2 0x1A2B SWAP3 DUP6 SWAP3 DUP8 SWAP3 PUSH2 0x1A26 SWAP3 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP2 DUP6 SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x19EE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1A02 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1A18 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH2 0x1FDB JUMP JUMPDEST PUSH1 0x8 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH2 0x1ACA SWAP3 DUP5 SWAP3 DUP8 SWAP3 PUSH2 0x1A26 SWAP3 PUSH15 0x10000000000000000000000000000 SWAP1 DIV PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x19EE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP POP PUSH1 0x1 PUSH1 0xC SSTORE POP JUMP JUMPDEST PUSH1 0x5 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x7 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST TIMESTAMP DUP5 LT ISZERO PUSH2 0x1B7B JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x12 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20455850495245440000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x3 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP10 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 DUP1 SLOAD PUSH1 0x1 DUP1 DUP3 ADD SWAP1 SWAP3 SSTORE DUP3 MLOAD PUSH32 0x6E71EDAE12B1B97F4D1F60370FEF10105FA2FAAE0126114A169C64845D6126C9 DUP2 DUP7 ADD MSTORE DUP1 DUP5 ADD SWAP7 SWAP1 SWAP7 MSTORE SWAP6 DUP14 AND PUSH1 0x60 DUP7 ADD MSTORE PUSH1 0x80 DUP6 ADD DUP13 SWAP1 MSTORE PUSH1 0xA0 DUP6 ADD SWAP6 SWAP1 SWAP6 MSTORE PUSH1 0xC0 DUP1 DUP6 ADD DUP12 SWAP1 MSTORE DUP2 MLOAD DUP1 DUP7 SUB SWAP1 SWAP2 ADD DUP2 MSTORE PUSH1 0xE0 DUP6 ADD DUP3 MSTORE DUP1 MLOAD SWAP1 DUP4 ADD KECCAK256 PUSH32 0x1901000000000000000000000000000000000000000000000000000000000000 PUSH2 0x100 DUP7 ADD MSTORE PUSH2 0x102 DUP6 ADD SWAP7 SWAP1 SWAP7 MSTORE PUSH2 0x122 DUP1 DUP6 ADD SWAP7 SWAP1 SWAP7 MSTORE DUP1 MLOAD DUP1 DUP6 SUB SWAP1 SWAP7 ADD DUP7 MSTORE PUSH2 0x142 DUP5 ADD DUP1 DUP3 MSTORE DUP7 MLOAD SWAP7 DUP4 ADD SWAP7 SWAP1 SWAP7 KECCAK256 SWAP6 DUP4 SWAP1 MSTORE PUSH2 0x162 DUP5 ADD DUP1 DUP3 MSTORE DUP7 SWAP1 MSTORE PUSH1 0xFF DUP10 AND PUSH2 0x182 DUP6 ADD MSTORE PUSH2 0x1A2 DUP5 ADD DUP9 SWAP1 MSTORE PUSH2 0x1C2 DUP5 ADD DUP8 SWAP1 MSTORE MLOAD SWAP2 SWAP4 SWAP3 PUSH2 0x1E2 DUP1 DUP3 ADD SWAP4 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP2 ADD SWAP3 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 DUP6 GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1CDC JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP PUSH1 0x40 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 ADD MLOAD SWAP2 POP POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 AND ISZERO DUP1 ISZERO SWAP1 PUSH2 0x1D57 JUMPI POP DUP9 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ JUMPDEST PUSH2 0x1DC2 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x1C PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20494E56414C49445F5349474E415455524500000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH2 0x1DCD DUP10 DUP10 DUP10 PUSH2 0x259C JUMP JUMPDEST POP POP POP POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x1E66 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC SSTORE PUSH1 0x6 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH2 0x1FD4 SWAP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x1EDD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1EF1 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1F07 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x7 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP3 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x1F7A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1F8E JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1FA4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x8 SLOAD PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP3 AND SWAP2 PUSH15 0x10000000000000000000000000000 SWAP1 DIV AND PUSH2 0x22E0 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xC SSTORE JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD DUP3 MSTORE PUSH1 0x19 DUP2 MSTORE PUSH32 0x7472616E7366657228616464726573732C75696E743235362900000000000000 PUSH1 0x20 SWAP2 DUP3 ADD MSTORE DUP2 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 DUP2 AND PUSH1 0x24 DUP4 ADD MSTORE PUSH1 0x44 DUP1 DUP4 ADD DUP7 SWAP1 MSTORE DUP5 MLOAD DUP1 DUP5 SUB SWAP1 SWAP2 ADD DUP2 MSTORE PUSH1 0x64 SWAP1 SWAP3 ADD DUP5 MSTORE SWAP2 DUP2 ADD DUP1 MLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xA9059CBB00000000000000000000000000000000000000000000000000000000 OR DUP2 MSTORE SWAP3 MLOAD DUP2 MLOAD PUSH1 0x0 SWAP5 PUSH1 0x60 SWAP5 DUP10 AND SWAP4 SWAP3 SWAP2 DUP3 SWAP2 SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x20E1 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x20A4 JUMP JUMPDEST PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB DUP1 NOT DUP3 MLOAD AND DUP2 DUP5 MLOAD AND DUP1 DUP3 OR DUP6 MSTORE POP POP POP POP POP POP SWAP1 POP ADD SWAP2 POP POP PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP7 GAS CALL SWAP2 POP POP RETURNDATASIZE DUP1 PUSH1 0x0 DUP2 EQ PUSH2 0x2143 JUMPI PUSH1 0x40 MLOAD SWAP2 POP PUSH1 0x1F NOT PUSH1 0x3F RETURNDATASIZE ADD AND DUP3 ADD PUSH1 0x40 MSTORE RETURNDATASIZE DUP3 MSTORE RETURNDATASIZE PUSH1 0x0 PUSH1 0x20 DUP5 ADD RETURNDATACOPY PUSH2 0x2148 JUMP JUMPDEST PUSH1 0x60 SWAP2 POP JUMPDEST POP SWAP2 POP SWAP2 POP DUP2 DUP1 ISZERO PUSH2 0x2176 JUMPI POP DUP1 MLOAD ISZERO DUP1 PUSH2 0x2176 JUMPI POP DUP1 DUP1 PUSH1 0x20 ADD SWAP1 MLOAD PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x2173 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD JUMPDEST PUSH2 0x21E1 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x1A PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A205452414E534645525F4641494C4544000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 ISZERO DUP1 PUSH2 0x2203 JUMPI POP POP DUP1 DUP3 MUL DUP3 DUP3 DUP3 DUP2 PUSH2 0x2200 JUMPI INVALID JUMPDEST DIV EQ JUMPDEST PUSH2 0xDF6 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x64732D6D6174682D6D756C2D6F766572666C6F77000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST DUP1 DUP3 SUB DUP3 DUP2 GT ISZERO PUSH2 0xDF6 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x15 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x64732D6D6174682D7375622D756E646572666C6F770000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 GT DUP1 ISZERO SWAP1 PUSH2 0x230C JUMPI POP PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 GT ISZERO JUMPDEST PUSH2 0x2377 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x13 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204F564552464C4F5700000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x8 SLOAD PUSH4 0xFFFFFFFF TIMESTAMP DUP2 AND SWAP2 PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV DUP2 AND DUP3 SUB SWAP1 DUP2 AND ISZERO DUP1 ISZERO SWAP1 PUSH2 0x23C7 JUMPI POP PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND ISZERO ISZERO JUMPDEST DUP1 ISZERO PUSH2 0x23E2 JUMPI POP PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND ISZERO ISZERO JUMPDEST ISZERO PUSH2 0x2492 JUMPI DUP1 PUSH4 0xFFFFFFFF AND PUSH2 0x2425 DUP6 PUSH2 0x23FB DUP7 PUSH2 0x2A57 JUMP JUMPDEST PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x2A7B AND JUMP JUMPDEST PUSH1 0x9 DUP1 SLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP3 SWAP1 SWAP3 AND SWAP3 SWAP1 SWAP3 MUL ADD SWAP1 SSTORE PUSH4 0xFFFFFFFF DUP2 AND PUSH2 0x2465 DUP5 PUSH2 0x23FB DUP8 PUSH2 0x2A57 JUMP JUMPDEST PUSH1 0xA DUP1 SLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP3 SWAP1 SWAP3 AND SWAP3 SWAP1 SWAP3 MUL ADD SWAP1 SSTORE JUMPDEST PUSH1 0x8 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000 AND PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 DUP2 AND SWAP2 SWAP1 SWAP2 OR PUSH32 0xFFFFFFFF0000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH15 0x10000000000000000000000000000 DUP9 DUP4 AND DUP2 MUL SWAP2 SWAP1 SWAP2 OR PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH4 0xFFFFFFFF DUP8 AND MUL OR SWAP3 DUP4 SWAP1 SSTORE PUSH1 0x40 DUP1 MLOAD DUP5 DUP5 AND DUP2 MSTORE SWAP2 SWAP1 SWAP4 DIV SWAP1 SWAP2 AND PUSH1 0x20 DUP3 ADD MSTORE DUP2 MLOAD PUSH32 0x1C411E9A96E071241C2F21F7726B17AE89E3CAB4C78BE50E062B03A9FFFBBAD1 SWAP3 SWAP2 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG1 POP POP POP POP POP POP JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP5 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 DUP8 AND DUP1 DUP5 MSTORE SWAP5 DUP3 MSTORE SWAP2 DUP3 SWAP1 KECCAK256 DUP6 SWAP1 SSTORE DUP2 MLOAD DUP6 DUP2 MSTORE SWAP2 MLOAD PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP3 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP POP POP JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH2 0x2641 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 DUP1 DUP3 KECCAK256 SWAP4 SWAP1 SWAP4 SSTORE SWAP1 DUP5 AND DUP2 MSTORE KECCAK256 SLOAD PUSH2 0x2683 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x2ABC AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP5 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP2 DUP3 SWAP1 KECCAK256 SWAP5 SWAP1 SWAP5 SSTORE DUP1 MLOAD DUP6 DUP2 MSTORE SWAP1 MLOAD SWAP2 SWAP4 SWAP3 DUP8 AND SWAP3 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP3 SWAP2 DUP3 SWAP1 SUB ADD SWAP1 LOG3 POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x5 PUSH1 0x0 SWAP1 SLOAD SWAP1 PUSH2 0x100 EXP SWAP1 DIV PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0x17E7E58 PUSH1 0x40 MLOAD DUP2 PUSH4 0xFFFFFFFF AND PUSH1 0xE0 SHL DUP2 MSTORE PUSH1 0x4 ADD PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x2757 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x276B JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x2781 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0xB SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 AND ISZERO DUP1 ISZERO SWAP5 POP SWAP2 SWAP3 POP SWAP1 PUSH2 0x2864 JUMPI DUP1 ISZERO PUSH2 0x285F JUMPI PUSH1 0x0 PUSH2 0x27D8 PUSH2 0x1257 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 DUP2 AND SWAP1 DUP9 AND PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x27E5 DUP4 PUSH2 0x2878 JUMP JUMPDEST SWAP1 POP DUP1 DUP3 GT ISZERO PUSH2 0x285C JUMPI PUSH1 0x0 PUSH2 0x2813 PUSH2 0x2804 DUP5 DUP5 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH1 0x0 SLOAD SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x2838 DUP4 PUSH2 0x282C DUP7 PUSH1 0x5 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x2ABC AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 DUP2 DUP4 DUP2 PUSH2 0x2845 JUMPI INVALID JUMPDEST DIV SWAP1 POP DUP1 ISZERO PUSH2 0x2858 JUMPI PUSH2 0x2858 DUP8 DUP3 PUSH2 0x28CA JUMP JUMPDEST POP POP POP JUMPDEST POP POP JUMPDEST PUSH2 0x2870 JUMP JUMPDEST DUP1 ISZERO PUSH2 0x2870 JUMPI PUSH1 0x0 PUSH1 0xB SSTORE JUMPDEST POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x3 DUP3 GT ISZERO PUSH2 0x28BB JUMPI POP DUP1 PUSH1 0x1 PUSH1 0x2 DUP3 DIV ADD JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x28B5 JUMPI DUP1 SWAP2 POP PUSH1 0x2 DUP2 DUP3 DUP6 DUP2 PUSH2 0x28A4 JUMPI INVALID JUMPDEST DIV ADD DUP2 PUSH2 0x28AD JUMPI INVALID JUMPDEST DIV SWAP1 POP PUSH2 0x288D JUMP JUMPDEST POP PUSH2 0x28C5 JUMP JUMPDEST DUP2 ISZERO PUSH2 0x28C5 JUMPI POP PUSH1 0x1 JUMPDEST SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH2 0x28DD SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x2ABC AND JUMP JUMPDEST PUSH1 0x0 SWAP1 DUP2 SSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH2 0x2915 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x2ABC AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 SWAP1 SWAP5 SSTORE DUP4 MLOAD DUP6 DUP2 MSTORE SWAP4 MLOAD SWAP3 SWAP4 SWAP2 SWAP3 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP3 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 DUP4 LT PUSH2 0x2989 JUMPI DUP2 PUSH2 0x298B JUMP JUMPDEST DUP3 JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH2 0x29C8 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 SWAP2 SWAP1 SWAP2 SSTORE SLOAD PUSH2 0x2A02 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH1 0x0 SWAP1 DUP2 SSTORE PUSH1 0x40 DUP1 MLOAD DUP4 DUP2 MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND SWAP2 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP2 SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 LOG3 POP POP JUMP JUMPDEST PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH15 0x10000000000000000000000000000 MUL SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 AND PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND DUP2 PUSH2 0x2AB4 JUMPI INVALID JUMPDEST DIV SWAP4 SWAP3 POP POP POP JUMP JUMPDEST DUP1 DUP3 ADD DUP3 DUP2 LT ISZERO PUSH2 0xDF6 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x64732D6D6174682D6164642D6F766572666C6F77000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT INVALID SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x4F SSTORE SLOAD POP SSTORE SLOAD 0x5F COINBASE 0x4D 0x4F SSTORE 0x4E SLOAD SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x49 0x4E POP SSTORE SLOAD 0x5F COINBASE 0x4D 0x4F SSTORE 0x4E SLOAD SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x4C 0x49 MLOAD SSTORE 0x49 DIFFICULTY 0x49 SLOAD MSIZE SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x4C 0x49 MLOAD SSTORE 0x49 DIFFICULTY 0x49 SLOAD MSIZE 0x5F TIMESTAMP SSTORE MSTORE 0x4E GASLIMIT DIFFICULTY SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x4C 0x49 MLOAD SSTORE 0x49 DIFFICULTY 0x49 SLOAD MSIZE 0x5F 0x4D 0x49 0x4E SLOAD GASLIMIT DIFFICULTY LOG2 PUSH6 0x627A7A723158 KECCAK256 PUSH30 0xCA18479E58487606BF70C79E44D8DEE62353C9EE6D01F9A9D70885B8765F 0x22 PUSH5 0x736F6C6343 STOP SDIV LT STOP ORIGIN GASLIMIT 0x49 POP CALLDATACOPY BALANCE ORIGIN DIFFICULTY PUSH16 0x6D61696E28737472696E67206E616D65 0x2C PUSH20 0x7472696E672076657273696F6E2C75696E743235 CALLDATASIZE KECCAK256 PUSH4 0x6861696E 0x49 PUSH5 0x2C61646472 PUSH6 0x737320766572 PUSH10 0x6679696E67436F6E7472 PUSH2 0x6374 0x29 LOG2 PUSH6 0x627A7A723158 KECCAK256 0x27 PUSH1 0xF9 0x2D PUSH32 0xA1DB6F5AA16307BAD65DF4EBCC8550C4B1F03755AB8DFD830C178F64736F6C63 NUMBER STOP SDIV LT STOP ORIGIN ","sourceMap":"102:1764:1:-;;;406:84;8:9:-1;5:2;;;30:1;27;20:12;5:2;406:84:1;;;;;;;;;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;406:84:1;457:11;:26;;-1:-1:-1;;;;;;457:26:1;-1:-1:-1;;;;;457:26:1;;;;;;;;;102:1764;;;-1:-1:-1;102:1764:1;;"},"deployedBytecode":{"linkReferences":{},"object":"608060405234801561001057600080fd5b50600436106100885760003560e01c8063a2e74af61161005b578063a2e74af6146100fd578063c9c6539614610132578063e6a439051461016d578063f46901ed146101a857610088565b8063017e7e581461008d578063094b7415146100be5780631e3dd18b146100c6578063574f2ba3146100e3575b600080fd5b6100956101db565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b6100956101f7565b610095600480360360208110156100dc57600080fd5b5035610213565b6100eb610247565b60408051918252519081900360200190f35b6101306004803603602081101561011357600080fd5b503573ffffffffffffffffffffffffffffffffffffffff1661024d565b005b6100956004803603604081101561014857600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135811691602001351661031a565b6100956004803603604081101561018357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135811691602001351661076d565b610130600480360360208110156101be57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166107a0565b60005473ffffffffffffffffffffffffffffffffffffffff1681565b60015473ffffffffffffffffffffffffffffffffffffffff1681565b6003818154811061022057fe5b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16905081565b60035490565b60015473ffffffffffffffffffffffffffffffffffffffff1633146102d357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f556e697377617056323a20464f5242494444454e000000000000000000000000604482015290519081900360640190fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60008173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156103b757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f556e697377617056323a204944454e544943414c5f4144445245535345530000604482015290519081900360640190fd5b6000808373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16106103f45783856103f7565b84845b909250905073ffffffffffffffffffffffffffffffffffffffff821661047e57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f556e697377617056323a205a45524f5f41444452455353000000000000000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff82811660009081526002602090815260408083208585168452909152902054161561051f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f556e697377617056323a20504149525f45584953545300000000000000000000604482015290519081900360640190fd5b6060604051806020016105319061086d565b6020820181038252601f19601f82011660405250905060008383604051602001808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b81526014018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140192505050604051602081830303815290604052805190602001209050808251602084016000f5604080517f485cc95500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152868116602483015291519297509087169163485cc9559160448082019260009290919082900301818387803b15801561065e57600080fd5b505af1158015610672573d6000803e3d6000fd5b5050505073ffffffffffffffffffffffffffffffffffffffff84811660008181526002602081815260408084208987168086529083528185208054978d167fffffffffffffffffffffffff000000000000000000000000000000000000000098891681179091559383528185208686528352818520805488168517905560038054600181018255958190527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b90950180549097168417909655925483519283529082015281517f0d3648bd0f6ba80134a33ba9275ac585d9d315f0ad8355cddefde31afa28d0e9929181900390910190a35050505092915050565b600260209081526000928352604080842090915290825290205473ffffffffffffffffffffffffffffffffffffffff1681565b60015473ffffffffffffffffffffffffffffffffffffffff16331461082657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f556e697377617056323a20464f5242494444454e000000000000000000000000604482015290519081900360640190fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b612d748061087b8339019056fe60806040526001600c5534801561001557600080fd5b506040514690806052612d228239604080519182900360520182208282018252600a8352692ab734b9bbb0b8102b1960b11b6020938401528151808301835260018152603160f81b908401528151808401919091527fbfcc8ef98ffbf7b6c3fec7bf5185b566b9863e35a9d83acd49ad6824b5969738818301527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6606082015260808101949094523060a0808601919091528151808603909101815260c09094019052825192019190912060035550600580546001600160a01b03191633179055612c1d806101056000396000f3fe608060405234801561001057600080fd5b50600436106101b95760003560e01c80636a627842116100f9578063ba9a7a5611610097578063d21220a711610071578063d21220a7146105da578063d505accf146105e2578063dd62ed3e14610640578063fff6cae91461067b576101b9565b8063ba9a7a5614610597578063bc25cf771461059f578063c45a0155146105d2576101b9565b80637ecebe00116100d35780637ecebe00146104d757806389afcb441461050a57806395d89b4114610556578063a9059cbb1461055e576101b9565b80636a6278421461046957806370a082311461049c5780637464fc3d146104cf576101b9565b806323b872dd116101665780633644e515116101405780633644e51514610416578063485cc9551461041e5780635909c0d5146104595780635a3d549314610461576101b9565b806323b872dd146103ad57806330adf81f146103f0578063313ce567146103f8576101b9565b8063095ea7b311610197578063095ea7b3146103155780630dfe16811461036257806318160ddd14610393576101b9565b8063022c0d9f146101be57806306fdde03146102595780630902f1ac146102d6575b600080fd5b610257600480360360808110156101d457600080fd5b81359160208101359173ffffffffffffffffffffffffffffffffffffffff604083013516919081019060808101606082013564010000000081111561021857600080fd5b82018360208201111561022a57600080fd5b8035906020019184600183028401116401000000008311171561024c57600080fd5b509092509050610683565b005b610261610d57565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561029b578181015183820152602001610283565b50505050905090810190601f1680156102c85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102de610d90565b604080516dffffffffffffffffffffffffffff948516815292909316602083015263ffffffff168183015290519081900360600190f35b61034e6004803603604081101561032b57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610de5565b604080519115158252519081900360200190f35b61036a610dfc565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b61039b610e18565b60408051918252519081900360200190f35b61034e600480360360608110156103c357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160208101359091169060400135610e1e565b61039b610efd565b610400610f21565b6040805160ff9092168252519081900360200190f35b61039b610f26565b6102576004803603604081101561043457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020013516610f2c565b61039b611005565b61039b61100b565b61039b6004803603602081101561047f57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16611011565b61039b600480360360208110156104b257600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166113cb565b61039b6113dd565b61039b600480360360208110156104ed57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166113e3565b61053d6004803603602081101561052057600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166113f5565b6040805192835260208301919091528051918290030190f35b610261611892565b61034e6004803603604081101561057457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81351690602001356118cb565b61039b6118d8565b610257600480360360208110156105b557600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166118de565b61036a611ad4565b61036a611af0565b610257600480360360e08110156105f857600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160208101359091169060408101359060608101359060ff6080820135169060a08101359060c00135611b0c565b61039b6004803603604081101561065657600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020013516611dd8565b610257611df5565b600c546001146106f457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c55841515806107075750600084115b61075c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180612b2f6025913960400191505060405180910390fd5b600080610767610d90565b5091509150816dffffffffffffffffffffffffffff168710801561079a5750806dffffffffffffffffffffffffffff1686105b6107ef576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180612b786021913960400191505060405180910390fd5b600654600754600091829173ffffffffffffffffffffffffffffffffffffffff91821691908116908916821480159061085457508073ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614155b6108bf57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f556e697377617056323a20494e56414c49445f544f0000000000000000000000604482015290519081900360640190fd5b8a156108d0576108d0828a8d611fdb565b89156108e1576108e1818a8c611fdb565b86156109c3578873ffffffffffffffffffffffffffffffffffffffff166310d1e85c338d8d8c8c6040518663ffffffff1660e01b8152600401808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509650505050505050600060405180830381600087803b1580156109aa57600080fd5b505af11580156109be573d6000803e3d6000fd5b505050505b604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff8416916370a08231916024808301926020929190829003018186803b158015610a2f57600080fd5b505afa158015610a43573d6000803e3d6000fd5b505050506040513d6020811015610a5957600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905191955073ffffffffffffffffffffffffffffffffffffffff8316916370a0823191602480820192602092909190829003018186803b158015610acb57600080fd5b505afa158015610adf573d6000803e3d6000fd5b505050506040513d6020811015610af557600080fd5b5051925060009150506dffffffffffffffffffffffffffff85168a90038311610b1f576000610b35565b89856dffffffffffffffffffffffffffff160383035b9050600089856dffffffffffffffffffffffffffff16038311610b59576000610b6f565b89856dffffffffffffffffffffffffffff160383035b90506000821180610b805750600081115b610bd5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526024815260200180612b546024913960400191505060405180910390fd5b6000610c09610beb84600363ffffffff6121e816565b610bfd876103e863ffffffff6121e816565b9063ffffffff61226e16565b90506000610c21610beb84600363ffffffff6121e816565b9050610c59620f4240610c4d6dffffffffffffffffffffffffffff8b8116908b1663ffffffff6121e816565b9063ffffffff6121e816565b610c69838363ffffffff6121e816565b1015610cd657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f556e697377617056323a204b0000000000000000000000000000000000000000604482015290519081900360640190fd5b5050610ce4848488886122e0565b60408051838152602081018390528082018d9052606081018c9052905173ffffffffffffffffffffffffffffffffffffffff8b169133917fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d8229181900360800190a350506001600c55505050505050505050565b6040518060400160405280600a81526020017f556e69737761702056320000000000000000000000000000000000000000000081525081565b6008546dffffffffffffffffffffffffffff808216926e0100000000000000000000000000008304909116917c0100000000000000000000000000000000000000000000000000000000900463ffffffff1690565b6000610df233848461259c565b5060015b92915050565b60065473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b73ffffffffffffffffffffffffffffffffffffffff831660009081526002602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14610ee85773ffffffffffffffffffffffffffffffffffffffff84166000908152600260209081526040808320338452909152902054610eb6908363ffffffff61226e16565b73ffffffffffffffffffffffffffffffffffffffff851660009081526002602090815260408083203384529091529020555b610ef384848461260b565b5060019392505050565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b601281565b60035481565b60055473ffffffffffffffffffffffffffffffffffffffff163314610fb257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f556e697377617056323a20464f5242494444454e000000000000000000000000604482015290519081900360640190fd5b6006805473ffffffffffffffffffffffffffffffffffffffff9384167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560078054929093169116179055565b60095481565b600a5481565b6000600c5460011461108457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c81905580611094610d90565b50600654604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905193955091935060009273ffffffffffffffffffffffffffffffffffffffff909116916370a08231916024808301926020929190829003018186803b15801561110e57600080fd5b505afa158015611122573d6000803e3d6000fd5b505050506040513d602081101561113857600080fd5b5051600754604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905192935060009273ffffffffffffffffffffffffffffffffffffffff909216916370a0823191602480820192602092909190829003018186803b1580156111b157600080fd5b505afa1580156111c5573d6000803e3d6000fd5b505050506040513d60208110156111db57600080fd5b505190506000611201836dffffffffffffffffffffffffffff871663ffffffff61226e16565b90506000611225836dffffffffffffffffffffffffffff871663ffffffff61226e16565b9050600061123387876126ec565b600054909150806112705761125c6103e8610bfd611257878763ffffffff6121e816565b612878565b985061126b60006103e86128ca565b6112cd565b6112ca6dffffffffffffffffffffffffffff8916611294868463ffffffff6121e816565b8161129b57fe5b046dffffffffffffffffffffffffffff89166112bd868563ffffffff6121e816565b816112c457fe5b0461297a565b98505b60008911611326576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180612bc16028913960400191505060405180910390fd5b6113308a8a6128ca565b61133c86868a8a6122e0565b811561137e5760085461137a906dffffffffffffffffffffffffffff808216916e01000000000000000000000000000090041663ffffffff6121e816565b600b555b6040805185815260208101859052815133927f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f928290030190a250506001600c5550949695505050505050565b60016020526000908152604090205481565b600b5481565b60046020526000908152604090205481565b600080600c5460011461146957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c81905580611479610d90565b50600654600754604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905194965092945073ffffffffffffffffffffffffffffffffffffffff9182169391169160009184916370a08231916024808301926020929190829003018186803b1580156114fb57600080fd5b505afa15801561150f573d6000803e3d6000fd5b505050506040513d602081101561152557600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905191925060009173ffffffffffffffffffffffffffffffffffffffff8516916370a08231916024808301926020929190829003018186803b15801561159957600080fd5b505afa1580156115ad573d6000803e3d6000fd5b505050506040513d60208110156115c357600080fd5b5051306000908152600160205260408120549192506115e288886126ec565b600054909150806115f9848763ffffffff6121e816565b8161160057fe5b049a5080611614848663ffffffff6121e816565b8161161b57fe5b04995060008b11801561162e575060008a115b611683576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180612b996028913960400191505060405180910390fd5b61168d3084612992565b611698878d8d611fdb565b6116a3868d8c611fdb565b604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff8916916370a08231916024808301926020929190829003018186803b15801561170f57600080fd5b505afa158015611723573d6000803e3d6000fd5b505050506040513d602081101561173957600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905191965073ffffffffffffffffffffffffffffffffffffffff8816916370a0823191602480820192602092909190829003018186803b1580156117ab57600080fd5b505afa1580156117bf573d6000803e3d6000fd5b505050506040513d60208110156117d557600080fd5b505193506117e585858b8b6122e0565b811561182757600854611823906dffffffffffffffffffffffffffff808216916e01000000000000000000000000000090041663ffffffff6121e816565b600b555b604080518c8152602081018c9052815173ffffffffffffffffffffffffffffffffffffffff8f169233927fdccd412f0b1252819cb1fd330b93224ca42612892bb3f4f789976e6d81936496929081900390910190a35050505050505050506001600c81905550915091565b6040518060400160405280600681526020017f554e492d5632000000000000000000000000000000000000000000000000000081525081565b6000610df233848461260b565b6103e881565b600c5460011461194f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c55600654600754600854604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff9485169490931692611a2b9285928792611a26926dffffffffffffffffffffffffffff169185916370a0823191602480820192602092909190829003018186803b1580156119ee57600080fd5b505afa158015611a02573d6000803e3d6000fd5b505050506040513d6020811015611a1857600080fd5b50519063ffffffff61226e16565b611fdb565b600854604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051611aca9284928792611a26926e01000000000000000000000000000090046dffffffffffffffffffffffffffff169173ffffffffffffffffffffffffffffffffffffffff8616916370a0823191602480820192602092909190829003018186803b1580156119ee57600080fd5b50506001600c5550565b60055473ffffffffffffffffffffffffffffffffffffffff1681565b60075473ffffffffffffffffffffffffffffffffffffffff1681565b42841015611b7b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f556e697377617056323a20455850495245440000000000000000000000000000604482015290519081900360640190fd5b60035473ffffffffffffffffffffffffffffffffffffffff80891660008181526004602090815260408083208054600180820190925582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98186015280840196909652958d166060860152608085018c905260a085019590955260c08085018b90528151808603909101815260e0850182528051908301207f19010000000000000000000000000000000000000000000000000000000000006101008601526101028501969096526101228085019690965280518085039096018652610142840180825286519683019690962095839052610162840180825286905260ff89166101828501526101a284018890526101c28401879052519193926101e2808201937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081019281900390910190855afa158015611cdc573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590611d5757508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b611dc257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f556e697377617056323a20494e56414c49445f5349474e415455524500000000604482015290519081900360640190fd5b611dcd89898961259c565b505050505050505050565b600260209081526000928352604080842090915290825290205481565b600c54600114611e6657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c55600654604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051611fd49273ffffffffffffffffffffffffffffffffffffffff16916370a08231916024808301926020929190829003018186803b158015611edd57600080fd5b505afa158015611ef1573d6000803e3d6000fd5b505050506040513d6020811015611f0757600080fd5b5051600754604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff909216916370a0823191602480820192602092909190829003018186803b158015611f7a57600080fd5b505afa158015611f8e573d6000803e3d6000fd5b505050506040513d6020811015611fa457600080fd5b50516008546dffffffffffffffffffffffffffff808216916e0100000000000000000000000000009004166122e0565b6001600c55565b604080518082018252601981527f7472616e7366657228616464726573732c75696e743235362900000000000000602091820152815173ffffffffffffffffffffffffffffffffffffffff85811660248301526044808301869052845180840390910181526064909201845291810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001781529251815160009460609489169392918291908083835b602083106120e157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016120a4565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612143576040519150601f19603f3d011682016040523d82523d6000602084013e612148565b606091505b5091509150818015612176575080511580612176575080806020019051602081101561217357600080fd5b50515b6121e157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f556e697377617056323a205452414e534645525f4641494c4544000000000000604482015290519081900360640190fd5b5050505050565b60008115806122035750508082028282828161220057fe5b04145b610df657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f64732d6d6174682d6d756c2d6f766572666c6f77000000000000000000000000604482015290519081900360640190fd5b80820382811115610df657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f64732d6d6174682d7375622d756e646572666c6f770000000000000000000000604482015290519081900360640190fd5b6dffffffffffffffffffffffffffff841180159061230c57506dffffffffffffffffffffffffffff8311155b61237757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f556e697377617056323a204f564552464c4f5700000000000000000000000000604482015290519081900360640190fd5b60085463ffffffff428116917c0100000000000000000000000000000000000000000000000000000000900481168203908116158015906123c757506dffffffffffffffffffffffffffff841615155b80156123e257506dffffffffffffffffffffffffffff831615155b15612492578063ffffffff16612425856123fb86612a57565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169063ffffffff612a7b16565b600980547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff929092169290920201905563ffffffff8116612465846123fb87612a57565b600a80547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff92909216929092020190555b600880547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000166dffffffffffffffffffffffffffff888116919091177fffffffff0000000000000000000000000000ffffffffffffffffffffffffffff166e0100000000000000000000000000008883168102919091177bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c010000000000000000000000000000000000000000000000000000000063ffffffff871602179283905560408051848416815291909304909116602082015281517f1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1929181900390910190a1505050505050565b73ffffffffffffffffffffffffffffffffffffffff808416600081815260026020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260016020526040902054612641908263ffffffff61226e16565b73ffffffffffffffffffffffffffffffffffffffff8085166000908152600160205260408082209390935590841681522054612683908263ffffffff612abc16565b73ffffffffffffffffffffffffffffffffffffffff80841660008181526001602090815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600080600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663017e7e586040518163ffffffff1660e01b815260040160206040518083038186803b15801561275757600080fd5b505afa15801561276b573d6000803e3d6000fd5b505050506040513d602081101561278157600080fd5b5051600b5473ffffffffffffffffffffffffffffffffffffffff821615801594509192509061286457801561285f5760006127d86112576dffffffffffffffffffffffffffff88811690881663ffffffff6121e816565b905060006127e583612878565b90508082111561285c576000612813612804848463ffffffff61226e16565b6000549063ffffffff6121e816565b905060006128388361282c86600563ffffffff6121e816565b9063ffffffff612abc16565b9050600081838161284557fe5b04905080156128585761285887826128ca565b5050505b50505b612870565b8015612870576000600b555b505092915050565b600060038211156128bb575080600160028204015b818110156128b5578091506002818285816128a457fe5b0401816128ad57fe5b04905061288d565b506128c5565b81156128c5575060015b919050565b6000546128dd908263ffffffff612abc16565b600090815573ffffffffffffffffffffffffffffffffffffffff8316815260016020526040902054612915908263ffffffff612abc16565b73ffffffffffffffffffffffffffffffffffffffff831660008181526001602090815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6000818310612989578161298b565b825b9392505050565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600160205260409020546129c8908263ffffffff61226e16565b73ffffffffffffffffffffffffffffffffffffffff831660009081526001602052604081209190915554612a02908263ffffffff61226e16565b600090815560408051838152905173ffffffffffffffffffffffffffffffffffffffff8516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef919081900360200190a35050565b6dffffffffffffffffffffffffffff166e0100000000000000000000000000000290565b60006dffffffffffffffffffffffffffff82167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff841681612ab457fe5b049392505050565b80820182811015610df657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f64732d6d6174682d6164642d6f766572666c6f77000000000000000000000000604482015290519081900360640190fdfe556e697377617056323a20494e53554646494349454e545f4f55545055545f414d4f554e54556e697377617056323a20494e53554646494349454e545f494e5055545f414d4f554e54556e697377617056323a20494e53554646494349454e545f4c4951554944495459556e697377617056323a20494e53554646494349454e545f4c49515549444954595f4255524e4544556e697377617056323a20494e53554646494349454e545f4c49515549444954595f4d494e544544a265627a7a723158207dca18479e58487606bf70c79e44d8dee62353c9ee6d01f9a9d70885b8765f2264736f6c63430005100032454950373132446f6d61696e28737472696e67206e616d652c737472696e672076657273696f6e2c75696e7432353620636861696e49642c6164647265737320766572696679696e67436f6e747261637429a265627a7a723158202760f92d7fa1db6f5aa16307bad65df4ebcc8550c4b1f03755ab8dfd830c178f64736f6c63430005100032","opcodes":"PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH2 0x88 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0xA2E74AF6 GT PUSH2 0x5B JUMPI DUP1 PUSH4 0xA2E74AF6 EQ PUSH2 0xFD JUMPI DUP1 PUSH4 0xC9C65396 EQ PUSH2 0x132 JUMPI DUP1 PUSH4 0xE6A43905 EQ PUSH2 0x16D JUMPI DUP1 PUSH4 0xF46901ED EQ PUSH2 0x1A8 JUMPI PUSH2 0x88 JUMP JUMPDEST DUP1 PUSH4 0x17E7E58 EQ PUSH2 0x8D JUMPI DUP1 PUSH4 0x94B7415 EQ PUSH2 0xBE JUMPI DUP1 PUSH4 0x1E3DD18B EQ PUSH2 0xC6 JUMPI DUP1 PUSH4 0x574F2BA3 EQ PUSH2 0xE3 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x95 PUSH2 0x1DB JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x95 PUSH2 0x1F7 JUMP JUMPDEST PUSH2 0x95 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0xDC JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH2 0x213 JUMP JUMPDEST PUSH2 0xEB PUSH2 0x247 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x130 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x113 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x24D JUMP JUMPDEST STOP JUMPDEST PUSH2 0x95 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x148 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 ADD CALLDATALOAD AND PUSH2 0x31A JUMP JUMPDEST PUSH2 0x95 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x183 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 ADD CALLDATALOAD AND PUSH2 0x76D JUMP JUMPDEST PUSH2 0x130 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1BE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x7A0 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x3 DUP2 DUP2 SLOAD DUP2 LT PUSH2 0x220 JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 SWAP1 SWAP2 KECCAK256 ADD SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 POP DUP2 JUMP JUMPDEST PUSH1 0x3 SLOAD SWAP1 JUMP JUMPDEST PUSH1 0x1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x2D3 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20464F5242494444454E000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x1 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP3 SWAP1 SWAP3 AND SWAP2 SWAP1 SWAP2 OR SWAP1 SSTORE JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO PUSH2 0x3B7 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x1E PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204944454E544943414C5F4144445245535345530000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 DUP1 DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND LT PUSH2 0x3F4 JUMPI DUP4 DUP6 PUSH2 0x3F7 JUMP JUMPDEST DUP5 DUP5 JUMPDEST SWAP1 SWAP3 POP SWAP1 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 AND PUSH2 0x47E JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x17 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A205A45524F5F41444452455353000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 DUP6 DUP6 AND DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SLOAD AND ISZERO PUSH2 0x51F JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x16 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20504149525F45584953545300000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x60 PUSH1 0x40 MLOAD DUP1 PUSH1 0x20 ADD PUSH2 0x531 SWAP1 PUSH2 0x86D JUMP JUMPDEST PUSH1 0x20 DUP3 ADD DUP2 SUB DUP3 MSTORE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND PUSH1 0x40 MSTORE POP SWAP1 POP PUSH1 0x0 DUP4 DUP4 PUSH1 0x40 MLOAD PUSH1 0x20 ADD DUP1 DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH1 0x60 SHL DUP2 MSTORE PUSH1 0x14 ADD DUP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH1 0x60 SHL DUP2 MSTORE PUSH1 0x14 ADD SWAP3 POP POP POP PUSH1 0x40 MLOAD PUSH1 0x20 DUP2 DUP4 SUB SUB DUP2 MSTORE SWAP1 PUSH1 0x40 MSTORE DUP1 MLOAD SWAP1 PUSH1 0x20 ADD KECCAK256 SWAP1 POP DUP1 DUP3 MLOAD PUSH1 0x20 DUP5 ADD PUSH1 0x0 CREATE2 PUSH1 0x40 DUP1 MLOAD PUSH32 0x485CC95500000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 DUP2 AND PUSH1 0x4 DUP4 ADD MSTORE DUP7 DUP2 AND PUSH1 0x24 DUP4 ADD MSTORE SWAP2 MLOAD SWAP3 SWAP8 POP SWAP1 DUP8 AND SWAP2 PUSH4 0x485CC955 SWAP2 PUSH1 0x44 DUP1 DUP3 ADD SWAP3 PUSH1 0x0 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP4 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x65E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x672 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 DUP2 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 DUP2 DUP2 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 DUP10 DUP8 AND DUP1 DUP7 MSTORE SWAP1 DUP4 MSTORE DUP2 DUP6 KECCAK256 DUP1 SLOAD SWAP8 DUP14 AND PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 SWAP9 DUP10 AND DUP2 OR SWAP1 SWAP2 SSTORE SWAP4 DUP4 MSTORE DUP2 DUP6 KECCAK256 DUP7 DUP7 MSTORE DUP4 MSTORE DUP2 DUP6 KECCAK256 DUP1 SLOAD DUP9 AND DUP6 OR SWAP1 SSTORE PUSH1 0x3 DUP1 SLOAD PUSH1 0x1 DUP2 ADD DUP3 SSTORE SWAP6 DUP2 SWAP1 MSTORE PUSH32 0xC2575A0E9E593C00F959F8C92F12DB2869C3395A3B0502D05E2516446F71F85B SWAP1 SWAP6 ADD DUP1 SLOAD SWAP1 SWAP8 AND DUP5 OR SWAP1 SWAP7 SSTORE SWAP3 SLOAD DUP4 MLOAD SWAP3 DUP4 MSTORE SWAP1 DUP3 ADD MSTORE DUP2 MLOAD PUSH32 0xD3648BD0F6BA80134A33BA9275AC585D9D315F0AD8355CDDEFDE31AFA28D0E9 SWAP3 SWAP2 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x826 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20464F5242494444454E000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP3 SWAP1 SWAP3 AND SWAP2 SWAP1 SWAP2 OR SWAP1 SSTORE JUMP JUMPDEST PUSH2 0x2D74 DUP1 PUSH2 0x87B DUP4 CODECOPY ADD SWAP1 JUMP INVALID PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x1 PUSH1 0xC SSTORE CALLVALUE DUP1 ISZERO PUSH2 0x15 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 MLOAD CHAINID SWAP1 DUP1 PUSH1 0x52 PUSH2 0x2D22 DUP3 CODECOPY PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 SWAP1 SUB PUSH1 0x52 ADD DUP3 KECCAK256 DUP3 DUP3 ADD DUP3 MSTORE PUSH1 0xA DUP4 MSTORE PUSH10 0x2AB734B9BBB0B8102B19 PUSH1 0xB1 SHL PUSH1 0x20 SWAP4 DUP5 ADD MSTORE DUP2 MLOAD DUP1 DUP4 ADD DUP4 MSTORE PUSH1 0x1 DUP2 MSTORE PUSH1 0x31 PUSH1 0xF8 SHL SWAP1 DUP5 ADD MSTORE DUP2 MLOAD DUP1 DUP5 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH32 0xBFCC8EF98FFBF7B6C3FEC7BF5185B566B9863E35A9D83ACD49AD6824B5969738 DUP2 DUP4 ADD MSTORE PUSH32 0xC89EFDAA54C0F20C7ADF612882DF0950F5A951637E0307CDCB4C672F298B8BC6 PUSH1 0x60 DUP3 ADD MSTORE PUSH1 0x80 DUP2 ADD SWAP5 SWAP1 SWAP5 MSTORE ADDRESS PUSH1 0xA0 DUP1 DUP7 ADD SWAP2 SWAP1 SWAP2 MSTORE DUP2 MLOAD DUP1 DUP7 SUB SWAP1 SWAP2 ADD DUP2 MSTORE PUSH1 0xC0 SWAP1 SWAP5 ADD SWAP1 MSTORE DUP3 MLOAD SWAP3 ADD SWAP2 SWAP1 SWAP2 KECCAK256 PUSH1 0x3 SSTORE POP PUSH1 0x5 DUP1 SLOAD PUSH1 0x1 PUSH1 0x1 PUSH1 0xA0 SHL SUB NOT AND CALLER OR SWAP1 SSTORE PUSH2 0x2C1D DUP1 PUSH2 0x105 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH2 0x1B9 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x6A627842 GT PUSH2 0xF9 JUMPI DUP1 PUSH4 0xBA9A7A56 GT PUSH2 0x97 JUMPI DUP1 PUSH4 0xD21220A7 GT PUSH2 0x71 JUMPI DUP1 PUSH4 0xD21220A7 EQ PUSH2 0x5DA JUMPI DUP1 PUSH4 0xD505ACCF EQ PUSH2 0x5E2 JUMPI DUP1 PUSH4 0xDD62ED3E EQ PUSH2 0x640 JUMPI DUP1 PUSH4 0xFFF6CAE9 EQ PUSH2 0x67B JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0xBA9A7A56 EQ PUSH2 0x597 JUMPI DUP1 PUSH4 0xBC25CF77 EQ PUSH2 0x59F JUMPI DUP1 PUSH4 0xC45A0155 EQ PUSH2 0x5D2 JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x7ECEBE00 GT PUSH2 0xD3 JUMPI DUP1 PUSH4 0x7ECEBE00 EQ PUSH2 0x4D7 JUMPI DUP1 PUSH4 0x89AFCB44 EQ PUSH2 0x50A JUMPI DUP1 PUSH4 0x95D89B41 EQ PUSH2 0x556 JUMPI DUP1 PUSH4 0xA9059CBB EQ PUSH2 0x55E JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x6A627842 EQ PUSH2 0x469 JUMPI DUP1 PUSH4 0x70A08231 EQ PUSH2 0x49C JUMPI DUP1 PUSH4 0x7464FC3D EQ PUSH2 0x4CF JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x23B872DD GT PUSH2 0x166 JUMPI DUP1 PUSH4 0x3644E515 GT PUSH2 0x140 JUMPI DUP1 PUSH4 0x3644E515 EQ PUSH2 0x416 JUMPI DUP1 PUSH4 0x485CC955 EQ PUSH2 0x41E JUMPI DUP1 PUSH4 0x5909C0D5 EQ PUSH2 0x459 JUMPI DUP1 PUSH4 0x5A3D5493 EQ PUSH2 0x461 JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x23B872DD EQ PUSH2 0x3AD JUMPI DUP1 PUSH4 0x30ADF81F EQ PUSH2 0x3F0 JUMPI DUP1 PUSH4 0x313CE567 EQ PUSH2 0x3F8 JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x95EA7B3 GT PUSH2 0x197 JUMPI DUP1 PUSH4 0x95EA7B3 EQ PUSH2 0x315 JUMPI DUP1 PUSH4 0xDFE1681 EQ PUSH2 0x362 JUMPI DUP1 PUSH4 0x18160DDD EQ PUSH2 0x393 JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x22C0D9F EQ PUSH2 0x1BE JUMPI DUP1 PUSH4 0x6FDDE03 EQ PUSH2 0x259 JUMPI DUP1 PUSH4 0x902F1AC EQ PUSH2 0x2D6 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x257 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x80 DUP2 LT ISZERO PUSH2 0x1D4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD SWAP2 PUSH1 0x20 DUP2 ADD CALLDATALOAD SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x40 DUP4 ADD CALLDATALOAD AND SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH1 0x80 DUP2 ADD PUSH1 0x60 DUP3 ADD CALLDATALOAD PUSH5 0x100000000 DUP2 GT ISZERO PUSH2 0x218 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP3 ADD DUP4 PUSH1 0x20 DUP3 ADD GT ISZERO PUSH2 0x22A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP2 DUP5 PUSH1 0x1 DUP4 MUL DUP5 ADD GT PUSH5 0x100000000 DUP4 GT OR ISZERO PUSH2 0x24C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP SWAP1 SWAP3 POP SWAP1 POP PUSH2 0x683 JUMP JUMPDEST STOP JUMPDEST PUSH2 0x261 PUSH2 0xD57 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 ADD SWAP1 DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x29B JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x283 JUMP JUMPDEST POP POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x2C8 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH2 0x2DE PUSH2 0xD90 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP5 DUP6 AND DUP2 MSTORE SWAP3 SWAP1 SWAP4 AND PUSH1 0x20 DUP4 ADD MSTORE PUSH4 0xFFFFFFFF AND DUP2 DUP4 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x60 ADD SWAP1 RETURN JUMPDEST PUSH2 0x34E PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x32B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD AND SWAP1 PUSH1 0x20 ADD CALLDATALOAD PUSH2 0xDE5 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x36A PUSH2 0xDFC JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x39B PUSH2 0xE18 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x34E PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x60 DUP2 LT ISZERO PUSH2 0x3C3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 DUP2 ADD CALLDATALOAD SWAP1 SWAP2 AND SWAP1 PUSH1 0x40 ADD CALLDATALOAD PUSH2 0xE1E JUMP JUMPDEST PUSH2 0x39B PUSH2 0xEFD JUMP JUMPDEST PUSH2 0x400 PUSH2 0xF21 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0xFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x39B PUSH2 0xF26 JUMP JUMPDEST PUSH2 0x257 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x434 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 ADD CALLDATALOAD AND PUSH2 0xF2C JUMP JUMPDEST PUSH2 0x39B PUSH2 0x1005 JUMP JUMPDEST PUSH2 0x39B PUSH2 0x100B JUMP JUMPDEST PUSH2 0x39B PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x47F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x1011 JUMP JUMPDEST PUSH2 0x39B PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x4B2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x13CB JUMP JUMPDEST PUSH2 0x39B PUSH2 0x13DD JUMP JUMPDEST PUSH2 0x39B PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x4ED JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x13E3 JUMP JUMPDEST PUSH2 0x53D PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x520 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x13F5 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP3 DUP4 MSTORE PUSH1 0x20 DUP4 ADD SWAP2 SWAP1 SWAP2 MSTORE DUP1 MLOAD SWAP2 DUP3 SWAP1 SUB ADD SWAP1 RETURN JUMPDEST PUSH2 0x261 PUSH2 0x1892 JUMP JUMPDEST PUSH2 0x34E PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x574 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD AND SWAP1 PUSH1 0x20 ADD CALLDATALOAD PUSH2 0x18CB JUMP JUMPDEST PUSH2 0x39B PUSH2 0x18D8 JUMP JUMPDEST PUSH2 0x257 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x5B5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x18DE JUMP JUMPDEST PUSH2 0x36A PUSH2 0x1AD4 JUMP JUMPDEST PUSH2 0x36A PUSH2 0x1AF0 JUMP JUMPDEST PUSH2 0x257 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0xE0 DUP2 LT ISZERO PUSH2 0x5F8 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 DUP2 ADD CALLDATALOAD SWAP1 SWAP2 AND SWAP1 PUSH1 0x40 DUP2 ADD CALLDATALOAD SWAP1 PUSH1 0x60 DUP2 ADD CALLDATALOAD SWAP1 PUSH1 0xFF PUSH1 0x80 DUP3 ADD CALLDATALOAD AND SWAP1 PUSH1 0xA0 DUP2 ADD CALLDATALOAD SWAP1 PUSH1 0xC0 ADD CALLDATALOAD PUSH2 0x1B0C JUMP JUMPDEST PUSH2 0x39B PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x656 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 ADD CALLDATALOAD AND PUSH2 0x1DD8 JUMP JUMPDEST PUSH2 0x257 PUSH2 0x1DF5 JUMP JUMPDEST PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x6F4 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC SSTORE DUP5 ISZERO ISZERO DUP1 PUSH2 0x707 JUMPI POP PUSH1 0x0 DUP5 GT JUMPDEST PUSH2 0x75C JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x25 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2B2F PUSH1 0x25 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH1 0x0 DUP1 PUSH2 0x767 PUSH2 0xD90 JUMP JUMPDEST POP SWAP2 POP SWAP2 POP DUP2 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP8 LT DUP1 ISZERO PUSH2 0x79A JUMPI POP DUP1 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP7 LT JUMPDEST PUSH2 0x7EF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x21 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2B78 PUSH1 0x21 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH1 0x6 SLOAD PUSH1 0x7 SLOAD PUSH1 0x0 SWAP2 DUP3 SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP2 DUP3 AND SWAP2 SWAP1 DUP2 AND SWAP1 DUP10 AND DUP3 EQ DUP1 ISZERO SWAP1 PUSH2 0x854 JUMPI POP DUP1 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP10 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO JUMPDEST PUSH2 0x8BF JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x15 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20494E56414C49445F544F0000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST DUP11 ISZERO PUSH2 0x8D0 JUMPI PUSH2 0x8D0 DUP3 DUP11 DUP14 PUSH2 0x1FDB JUMP JUMPDEST DUP10 ISZERO PUSH2 0x8E1 JUMPI PUSH2 0x8E1 DUP2 DUP11 DUP13 PUSH2 0x1FDB JUMP JUMPDEST DUP7 ISZERO PUSH2 0x9C3 JUMPI DUP9 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0x10D1E85C CALLER DUP14 DUP14 DUP13 DUP13 PUSH1 0x40 MLOAD DUP7 PUSH4 0xFFFFFFFF AND PUSH1 0xE0 SHL DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP7 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 MSTORE PUSH1 0x20 ADD DUP6 DUP2 MSTORE PUSH1 0x20 ADD DUP5 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE DUP5 DUP5 DUP3 DUP2 DUP2 MSTORE PUSH1 0x20 ADD SWAP3 POP DUP1 DUP3 DUP5 CALLDATACOPY PUSH1 0x0 DUP2 DUP5 ADD MSTORE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND SWAP1 POP DUP1 DUP4 ADD SWAP3 POP POP POP SWAP7 POP POP POP POP POP POP POP PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x9AA JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x9BE JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0xA2F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0xA43 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0xA59 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP2 SWAP6 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0xACB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0xADF JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0xAF5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD SWAP3 POP PUSH1 0x0 SWAP2 POP POP PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND DUP11 SWAP1 SUB DUP4 GT PUSH2 0xB1F JUMPI PUSH1 0x0 PUSH2 0xB35 JUMP JUMPDEST DUP10 DUP6 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SUB DUP4 SUB JUMPDEST SWAP1 POP PUSH1 0x0 DUP10 DUP6 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SUB DUP4 GT PUSH2 0xB59 JUMPI PUSH1 0x0 PUSH2 0xB6F JUMP JUMPDEST DUP10 DUP6 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SUB DUP4 SUB JUMPDEST SWAP1 POP PUSH1 0x0 DUP3 GT DUP1 PUSH2 0xB80 JUMPI POP PUSH1 0x0 DUP2 GT JUMPDEST PUSH2 0xBD5 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x24 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2B54 PUSH1 0x24 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xC09 PUSH2 0xBEB DUP5 PUSH1 0x3 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH2 0xBFD DUP8 PUSH2 0x3E8 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0xC21 PUSH2 0xBEB DUP5 PUSH1 0x3 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 POP PUSH2 0xC59 PUSH3 0xF4240 PUSH2 0xC4D PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP12 DUP2 AND SWAP1 DUP12 AND PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH2 0xC69 DUP4 DUP4 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST LT ISZERO PUSH2 0xCD6 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0xC PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204B0000000000000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST POP POP PUSH2 0xCE4 DUP5 DUP5 DUP9 DUP9 PUSH2 0x22E0 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP4 DUP2 MSTORE PUSH1 0x20 DUP2 ADD DUP4 SWAP1 MSTORE DUP1 DUP3 ADD DUP14 SWAP1 MSTORE PUSH1 0x60 DUP2 ADD DUP13 SWAP1 MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP12 AND SWAP2 CALLER SWAP2 PUSH32 0xD78AD95FA46C994B6551D0DA85FC275FE613CE37657FB8D5E3D130840159D822 SWAP2 DUP2 SWAP1 SUB PUSH1 0x80 ADD SWAP1 LOG3 POP POP PUSH1 0x1 PUSH1 0xC SSTORE POP POP POP POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 PUSH1 0x40 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0xA DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x556E697377617020563200000000000000000000000000000000000000000000 DUP2 MSTORE POP DUP2 JUMP JUMPDEST PUSH1 0x8 SLOAD PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP3 AND SWAP3 PUSH15 0x10000000000000000000000000000 DUP4 DIV SWAP1 SWAP2 AND SWAP2 PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV PUSH4 0xFFFFFFFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xDF2 CALLER DUP5 DUP5 PUSH2 0x259C JUMP JUMPDEST POP PUSH1 0x1 JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x6 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x0 SLOAD DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE SWAP1 SWAP2 MSTORE DUP2 KECCAK256 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF EQ PUSH2 0xEE8 JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SLOAD PUSH2 0xEB6 SWAP1 DUP4 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SSTORE JUMPDEST PUSH2 0xEF3 DUP5 DUP5 DUP5 PUSH2 0x260B JUMP JUMPDEST POP PUSH1 0x1 SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH32 0x6E71EDAE12B1B97F4D1F60370FEF10105FA2FAAE0126114A169C64845D6126C9 DUP2 JUMP JUMPDEST PUSH1 0x12 DUP2 JUMP JUMPDEST PUSH1 0x3 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x5 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0xFB2 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20464F5242494444454E000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x6 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP4 DUP5 AND PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 SWAP2 DUP3 AND OR SWAP1 SWAP2 SSTORE PUSH1 0x7 DUP1 SLOAD SWAP3 SWAP1 SWAP4 AND SWAP2 AND OR SWAP1 SSTORE JUMP JUMPDEST PUSH1 0x9 SLOAD DUP2 JUMP JUMPDEST PUSH1 0xA SLOAD DUP2 JUMP JUMPDEST PUSH1 0x0 PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x1084 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC DUP2 SWAP1 SSTORE DUP1 PUSH2 0x1094 PUSH2 0xD90 JUMP JUMPDEST POP PUSH1 0x6 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP4 SWAP6 POP SWAP2 SWAP4 POP PUSH1 0x0 SWAP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP2 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x110E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1122 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1138 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x7 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP3 SWAP4 POP PUSH1 0x0 SWAP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP3 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x11B1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x11C5 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x11DB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD SWAP1 POP PUSH1 0x0 PUSH2 0x1201 DUP4 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x1225 DUP4 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x1233 DUP8 DUP8 PUSH2 0x26EC JUMP JUMPDEST PUSH1 0x0 SLOAD SWAP1 SWAP2 POP DUP1 PUSH2 0x1270 JUMPI PUSH2 0x125C PUSH2 0x3E8 PUSH2 0xBFD PUSH2 0x1257 DUP8 DUP8 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH2 0x2878 JUMP JUMPDEST SWAP9 POP PUSH2 0x126B PUSH1 0x0 PUSH2 0x3E8 PUSH2 0x28CA JUMP JUMPDEST PUSH2 0x12CD JUMP JUMPDEST PUSH2 0x12CA PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP10 AND PUSH2 0x1294 DUP7 DUP5 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST DUP2 PUSH2 0x129B JUMPI INVALID JUMPDEST DIV PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP10 AND PUSH2 0x12BD DUP7 DUP6 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST DUP2 PUSH2 0x12C4 JUMPI INVALID JUMPDEST DIV PUSH2 0x297A JUMP JUMPDEST SWAP9 POP JUMPDEST PUSH1 0x0 DUP10 GT PUSH2 0x1326 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x28 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2BC1 PUSH1 0x28 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH2 0x1330 DUP11 DUP11 PUSH2 0x28CA JUMP JUMPDEST PUSH2 0x133C DUP7 DUP7 DUP11 DUP11 PUSH2 0x22E0 JUMP JUMPDEST DUP2 ISZERO PUSH2 0x137E JUMPI PUSH1 0x8 SLOAD PUSH2 0x137A SWAP1 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP3 AND SWAP2 PUSH15 0x10000000000000000000000000000 SWAP1 DIV AND PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH1 0xB SSTORE JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP6 DUP2 MSTORE PUSH1 0x20 DUP2 ADD DUP6 SWAP1 MSTORE DUP2 MLOAD CALLER SWAP3 PUSH32 0x4C209B5FC8AD50758F13E2E1088BA56A560DFF690A1C6FEF26394F4C03821C4F SWAP3 DUP3 SWAP1 SUB ADD SWAP1 LOG2 POP POP PUSH1 0x1 PUSH1 0xC SSTORE POP SWAP5 SWAP7 SWAP6 POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH1 0xB SLOAD DUP2 JUMP JUMPDEST PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x1469 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC DUP2 SWAP1 SSTORE DUP1 PUSH2 0x1479 PUSH2 0xD90 JUMP JUMPDEST POP PUSH1 0x6 SLOAD PUSH1 0x7 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP5 SWAP7 POP SWAP3 SWAP5 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP2 DUP3 AND SWAP4 SWAP2 AND SWAP2 PUSH1 0x0 SWAP2 DUP5 SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x14FB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x150F JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1525 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP2 SWAP3 POP PUSH1 0x0 SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x1599 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x15AD JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x15C3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD ADDRESS PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 SLOAD SWAP2 SWAP3 POP PUSH2 0x15E2 DUP9 DUP9 PUSH2 0x26EC JUMP JUMPDEST PUSH1 0x0 SLOAD SWAP1 SWAP2 POP DUP1 PUSH2 0x15F9 DUP5 DUP8 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST DUP2 PUSH2 0x1600 JUMPI INVALID JUMPDEST DIV SWAP11 POP DUP1 PUSH2 0x1614 DUP5 DUP7 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST DUP2 PUSH2 0x161B JUMPI INVALID JUMPDEST DIV SWAP10 POP PUSH1 0x0 DUP12 GT DUP1 ISZERO PUSH2 0x162E JUMPI POP PUSH1 0x0 DUP11 GT JUMPDEST PUSH2 0x1683 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x28 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2B99 PUSH1 0x28 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH2 0x168D ADDRESS DUP5 PUSH2 0x2992 JUMP JUMPDEST PUSH2 0x1698 DUP8 DUP14 DUP14 PUSH2 0x1FDB JUMP JUMPDEST PUSH2 0x16A3 DUP7 DUP14 DUP13 PUSH2 0x1FDB JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP10 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x170F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1723 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1739 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP2 SWAP7 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x17AB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x17BF JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x17D5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD SWAP4 POP PUSH2 0x17E5 DUP6 DUP6 DUP12 DUP12 PUSH2 0x22E0 JUMP JUMPDEST DUP2 ISZERO PUSH2 0x1827 JUMPI PUSH1 0x8 SLOAD PUSH2 0x1823 SWAP1 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP3 AND SWAP2 PUSH15 0x10000000000000000000000000000 SWAP1 DIV AND PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH1 0xB SSTORE JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP13 DUP2 MSTORE PUSH1 0x20 DUP2 ADD DUP13 SWAP1 MSTORE DUP2 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP16 AND SWAP3 CALLER SWAP3 PUSH32 0xDCCD412F0B1252819CB1FD330B93224CA42612892BB3F4F789976E6D81936496 SWAP3 SWAP1 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP POP POP POP POP POP POP POP POP PUSH1 0x1 PUSH1 0xC DUP2 SWAP1 SSTORE POP SWAP2 POP SWAP2 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 PUSH1 0x40 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x6 DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x554E492D56320000000000000000000000000000000000000000000000000000 DUP2 MSTORE POP DUP2 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xDF2 CALLER DUP5 DUP5 PUSH2 0x260B JUMP JUMPDEST PUSH2 0x3E8 DUP2 JUMP JUMPDEST PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x194F JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC SSTORE PUSH1 0x6 SLOAD PUSH1 0x7 SLOAD PUSH1 0x8 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP5 DUP6 AND SWAP5 SWAP1 SWAP4 AND SWAP3 PUSH2 0x1A2B SWAP3 DUP6 SWAP3 DUP8 SWAP3 PUSH2 0x1A26 SWAP3 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP2 DUP6 SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x19EE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1A02 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1A18 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH2 0x1FDB JUMP JUMPDEST PUSH1 0x8 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH2 0x1ACA SWAP3 DUP5 SWAP3 DUP8 SWAP3 PUSH2 0x1A26 SWAP3 PUSH15 0x10000000000000000000000000000 SWAP1 DIV PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x19EE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP POP PUSH1 0x1 PUSH1 0xC SSTORE POP JUMP JUMPDEST PUSH1 0x5 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x7 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST TIMESTAMP DUP5 LT ISZERO PUSH2 0x1B7B JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x12 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20455850495245440000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x3 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP10 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 DUP1 SLOAD PUSH1 0x1 DUP1 DUP3 ADD SWAP1 SWAP3 SSTORE DUP3 MLOAD PUSH32 0x6E71EDAE12B1B97F4D1F60370FEF10105FA2FAAE0126114A169C64845D6126C9 DUP2 DUP7 ADD MSTORE DUP1 DUP5 ADD SWAP7 SWAP1 SWAP7 MSTORE SWAP6 DUP14 AND PUSH1 0x60 DUP7 ADD MSTORE PUSH1 0x80 DUP6 ADD DUP13 SWAP1 MSTORE PUSH1 0xA0 DUP6 ADD SWAP6 SWAP1 SWAP6 MSTORE PUSH1 0xC0 DUP1 DUP6 ADD DUP12 SWAP1 MSTORE DUP2 MLOAD DUP1 DUP7 SUB SWAP1 SWAP2 ADD DUP2 MSTORE PUSH1 0xE0 DUP6 ADD DUP3 MSTORE DUP1 MLOAD SWAP1 DUP4 ADD KECCAK256 PUSH32 0x1901000000000000000000000000000000000000000000000000000000000000 PUSH2 0x100 DUP7 ADD MSTORE PUSH2 0x102 DUP6 ADD SWAP7 SWAP1 SWAP7 MSTORE PUSH2 0x122 DUP1 DUP6 ADD SWAP7 SWAP1 SWAP7 MSTORE DUP1 MLOAD DUP1 DUP6 SUB SWAP1 SWAP7 ADD DUP7 MSTORE PUSH2 0x142 DUP5 ADD DUP1 DUP3 MSTORE DUP7 MLOAD SWAP7 DUP4 ADD SWAP7 SWAP1 SWAP7 KECCAK256 SWAP6 DUP4 SWAP1 MSTORE PUSH2 0x162 DUP5 ADD DUP1 DUP3 MSTORE DUP7 SWAP1 MSTORE PUSH1 0xFF DUP10 AND PUSH2 0x182 DUP6 ADD MSTORE PUSH2 0x1A2 DUP5 ADD DUP9 SWAP1 MSTORE PUSH2 0x1C2 DUP5 ADD DUP8 SWAP1 MSTORE MLOAD SWAP2 SWAP4 SWAP3 PUSH2 0x1E2 DUP1 DUP3 ADD SWAP4 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP2 ADD SWAP3 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 DUP6 GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1CDC JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP PUSH1 0x40 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 ADD MLOAD SWAP2 POP POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 AND ISZERO DUP1 ISZERO SWAP1 PUSH2 0x1D57 JUMPI POP DUP9 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ JUMPDEST PUSH2 0x1DC2 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x1C PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20494E56414C49445F5349474E415455524500000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH2 0x1DCD DUP10 DUP10 DUP10 PUSH2 0x259C JUMP JUMPDEST POP POP POP POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x1E66 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC SSTORE PUSH1 0x6 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH2 0x1FD4 SWAP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x1EDD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1EF1 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1F07 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x7 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP3 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x1F7A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1F8E JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1FA4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x8 SLOAD PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP3 AND SWAP2 PUSH15 0x10000000000000000000000000000 SWAP1 DIV AND PUSH2 0x22E0 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xC SSTORE JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD DUP3 MSTORE PUSH1 0x19 DUP2 MSTORE PUSH32 0x7472616E7366657228616464726573732C75696E743235362900000000000000 PUSH1 0x20 SWAP2 DUP3 ADD MSTORE DUP2 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 DUP2 AND PUSH1 0x24 DUP4 ADD MSTORE PUSH1 0x44 DUP1 DUP4 ADD DUP7 SWAP1 MSTORE DUP5 MLOAD DUP1 DUP5 SUB SWAP1 SWAP2 ADD DUP2 MSTORE PUSH1 0x64 SWAP1 SWAP3 ADD DUP5 MSTORE SWAP2 DUP2 ADD DUP1 MLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xA9059CBB00000000000000000000000000000000000000000000000000000000 OR DUP2 MSTORE SWAP3 MLOAD DUP2 MLOAD PUSH1 0x0 SWAP5 PUSH1 0x60 SWAP5 DUP10 AND SWAP4 SWAP3 SWAP2 DUP3 SWAP2 SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x20E1 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x20A4 JUMP JUMPDEST PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB DUP1 NOT DUP3 MLOAD AND DUP2 DUP5 MLOAD AND DUP1 DUP3 OR DUP6 MSTORE POP POP POP POP POP POP SWAP1 POP ADD SWAP2 POP POP PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP7 GAS CALL SWAP2 POP POP RETURNDATASIZE DUP1 PUSH1 0x0 DUP2 EQ PUSH2 0x2143 JUMPI PUSH1 0x40 MLOAD SWAP2 POP PUSH1 0x1F NOT PUSH1 0x3F RETURNDATASIZE ADD AND DUP3 ADD PUSH1 0x40 MSTORE RETURNDATASIZE DUP3 MSTORE RETURNDATASIZE PUSH1 0x0 PUSH1 0x20 DUP5 ADD RETURNDATACOPY PUSH2 0x2148 JUMP JUMPDEST PUSH1 0x60 SWAP2 POP JUMPDEST POP SWAP2 POP SWAP2 POP DUP2 DUP1 ISZERO PUSH2 0x2176 JUMPI POP DUP1 MLOAD ISZERO DUP1 PUSH2 0x2176 JUMPI POP DUP1 DUP1 PUSH1 0x20 ADD SWAP1 MLOAD PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x2173 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD JUMPDEST PUSH2 0x21E1 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x1A PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A205452414E534645525F4641494C4544000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 ISZERO DUP1 PUSH2 0x2203 JUMPI POP POP DUP1 DUP3 MUL DUP3 DUP3 DUP3 DUP2 PUSH2 0x2200 JUMPI INVALID JUMPDEST DIV EQ JUMPDEST PUSH2 0xDF6 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x64732D6D6174682D6D756C2D6F766572666C6F77000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST DUP1 DUP3 SUB DUP3 DUP2 GT ISZERO PUSH2 0xDF6 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x15 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x64732D6D6174682D7375622D756E646572666C6F770000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 GT DUP1 ISZERO SWAP1 PUSH2 0x230C JUMPI POP PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 GT ISZERO JUMPDEST PUSH2 0x2377 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x13 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204F564552464C4F5700000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x8 SLOAD PUSH4 0xFFFFFFFF TIMESTAMP DUP2 AND SWAP2 PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV DUP2 AND DUP3 SUB SWAP1 DUP2 AND ISZERO DUP1 ISZERO SWAP1 PUSH2 0x23C7 JUMPI POP PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND ISZERO ISZERO JUMPDEST DUP1 ISZERO PUSH2 0x23E2 JUMPI POP PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND ISZERO ISZERO JUMPDEST ISZERO PUSH2 0x2492 JUMPI DUP1 PUSH4 0xFFFFFFFF AND PUSH2 0x2425 DUP6 PUSH2 0x23FB DUP7 PUSH2 0x2A57 JUMP JUMPDEST PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x2A7B AND JUMP JUMPDEST PUSH1 0x9 DUP1 SLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP3 SWAP1 SWAP3 AND SWAP3 SWAP1 SWAP3 MUL ADD SWAP1 SSTORE PUSH4 0xFFFFFFFF DUP2 AND PUSH2 0x2465 DUP5 PUSH2 0x23FB DUP8 PUSH2 0x2A57 JUMP JUMPDEST PUSH1 0xA DUP1 SLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP3 SWAP1 SWAP3 AND SWAP3 SWAP1 SWAP3 MUL ADD SWAP1 SSTORE JUMPDEST PUSH1 0x8 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000 AND PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 DUP2 AND SWAP2 SWAP1 SWAP2 OR PUSH32 0xFFFFFFFF0000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH15 0x10000000000000000000000000000 DUP9 DUP4 AND DUP2 MUL SWAP2 SWAP1 SWAP2 OR PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH4 0xFFFFFFFF DUP8 AND MUL OR SWAP3 DUP4 SWAP1 SSTORE PUSH1 0x40 DUP1 MLOAD DUP5 DUP5 AND DUP2 MSTORE SWAP2 SWAP1 SWAP4 DIV SWAP1 SWAP2 AND PUSH1 0x20 DUP3 ADD MSTORE DUP2 MLOAD PUSH32 0x1C411E9A96E071241C2F21F7726B17AE89E3CAB4C78BE50E062B03A9FFFBBAD1 SWAP3 SWAP2 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG1 POP POP POP POP POP POP JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP5 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 DUP8 AND DUP1 DUP5 MSTORE SWAP5 DUP3 MSTORE SWAP2 DUP3 SWAP1 KECCAK256 DUP6 SWAP1 SSTORE DUP2 MLOAD DUP6 DUP2 MSTORE SWAP2 MLOAD PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP3 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP POP POP JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH2 0x2641 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 DUP1 DUP3 KECCAK256 SWAP4 SWAP1 SWAP4 SSTORE SWAP1 DUP5 AND DUP2 MSTORE KECCAK256 SLOAD PUSH2 0x2683 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x2ABC AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP5 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP2 DUP3 SWAP1 KECCAK256 SWAP5 SWAP1 SWAP5 SSTORE DUP1 MLOAD DUP6 DUP2 MSTORE SWAP1 MLOAD SWAP2 SWAP4 SWAP3 DUP8 AND SWAP3 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP3 SWAP2 DUP3 SWAP1 SUB ADD SWAP1 LOG3 POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x5 PUSH1 0x0 SWAP1 SLOAD SWAP1 PUSH2 0x100 EXP SWAP1 DIV PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0x17E7E58 PUSH1 0x40 MLOAD DUP2 PUSH4 0xFFFFFFFF AND PUSH1 0xE0 SHL DUP2 MSTORE PUSH1 0x4 ADD PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x2757 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x276B JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x2781 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0xB SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 AND ISZERO DUP1 ISZERO SWAP5 POP SWAP2 SWAP3 POP SWAP1 PUSH2 0x2864 JUMPI DUP1 ISZERO PUSH2 0x285F JUMPI PUSH1 0x0 PUSH2 0x27D8 PUSH2 0x1257 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 DUP2 AND SWAP1 DUP9 AND PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x27E5 DUP4 PUSH2 0x2878 JUMP JUMPDEST SWAP1 POP DUP1 DUP3 GT ISZERO PUSH2 0x285C JUMPI PUSH1 0x0 PUSH2 0x2813 PUSH2 0x2804 DUP5 DUP5 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH1 0x0 SLOAD SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x2838 DUP4 PUSH2 0x282C DUP7 PUSH1 0x5 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x2ABC AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 DUP2 DUP4 DUP2 PUSH2 0x2845 JUMPI INVALID JUMPDEST DIV SWAP1 POP DUP1 ISZERO PUSH2 0x2858 JUMPI PUSH2 0x2858 DUP8 DUP3 PUSH2 0x28CA JUMP JUMPDEST POP POP POP JUMPDEST POP POP JUMPDEST PUSH2 0x2870 JUMP JUMPDEST DUP1 ISZERO PUSH2 0x2870 JUMPI PUSH1 0x0 PUSH1 0xB SSTORE JUMPDEST POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x3 DUP3 GT ISZERO PUSH2 0x28BB JUMPI POP DUP1 PUSH1 0x1 PUSH1 0x2 DUP3 DIV ADD JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x28B5 JUMPI DUP1 SWAP2 POP PUSH1 0x2 DUP2 DUP3 DUP6 DUP2 PUSH2 0x28A4 JUMPI INVALID JUMPDEST DIV ADD DUP2 PUSH2 0x28AD JUMPI INVALID JUMPDEST DIV SWAP1 POP PUSH2 0x288D JUMP JUMPDEST POP PUSH2 0x28C5 JUMP JUMPDEST DUP2 ISZERO PUSH2 0x28C5 JUMPI POP PUSH1 0x1 JUMPDEST SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH2 0x28DD SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x2ABC AND JUMP JUMPDEST PUSH1 0x0 SWAP1 DUP2 SSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH2 0x2915 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x2ABC AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 SWAP1 SWAP5 SSTORE DUP4 MLOAD DUP6 DUP2 MSTORE SWAP4 MLOAD SWAP3 SWAP4 SWAP2 SWAP3 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP3 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 DUP4 LT PUSH2 0x2989 JUMPI DUP2 PUSH2 0x298B JUMP JUMPDEST DUP3 JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH2 0x29C8 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 SWAP2 SWAP1 SWAP2 SSTORE SLOAD PUSH2 0x2A02 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH1 0x0 SWAP1 DUP2 SSTORE PUSH1 0x40 DUP1 MLOAD DUP4 DUP2 MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND SWAP2 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP2 SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 LOG3 POP POP JUMP JUMPDEST PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH15 0x10000000000000000000000000000 MUL SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 AND PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND DUP2 PUSH2 0x2AB4 JUMPI INVALID JUMPDEST DIV SWAP4 SWAP3 POP POP POP JUMP JUMPDEST DUP1 DUP3 ADD DUP3 DUP2 LT ISZERO PUSH2 0xDF6 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x64732D6D6174682D6164642D6F766572666C6F77000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT INVALID SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x4F SSTORE SLOAD POP SSTORE SLOAD 0x5F COINBASE 0x4D 0x4F SSTORE 0x4E SLOAD SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x49 0x4E POP SSTORE SLOAD 0x5F COINBASE 0x4D 0x4F SSTORE 0x4E SLOAD SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x4C 0x49 MLOAD SSTORE 0x49 DIFFICULTY 0x49 SLOAD MSIZE SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x4C 0x49 MLOAD SSTORE 0x49 DIFFICULTY 0x49 SLOAD MSIZE 0x5F TIMESTAMP SSTORE MSTORE 0x4E GASLIMIT DIFFICULTY SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x4C 0x49 MLOAD SSTORE 0x49 DIFFICULTY 0x49 SLOAD MSIZE 0x5F 0x4D 0x49 0x4E SLOAD GASLIMIT DIFFICULTY LOG2 PUSH6 0x627A7A723158 KECCAK256 PUSH30 0xCA18479E58487606BF70C79E44D8DEE62353C9EE6D01F9A9D70885B8765F 0x22 PUSH5 0x736F6C6343 STOP SDIV LT STOP ORIGIN GASLIMIT 0x49 POP CALLDATACOPY BALANCE ORIGIN DIFFICULTY PUSH16 0x6D61696E28737472696E67206E616D65 0x2C PUSH20 0x7472696E672076657273696F6E2C75696E743235 CALLDATASIZE KECCAK256 PUSH4 0x6861696E 0x49 PUSH5 0x2C61646472 PUSH6 0x737320766572 PUSH10 0x6679696E67436F6E7472 PUSH2 0x6374 0x29 LOG2 PUSH6 0x627A7A723158 KECCAK256 0x27 PUSH1 0xF9 0x2D PUSH32 0xA1DB6F5AA16307BAD65DF4EBCC8550C4B1F03755AB8DFD830C178F64736F6C63 NUMBER STOP SDIV LT STOP ORIGIN ","sourceMap":"102:1764:1:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;102:1764:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;155:20;;;:::i;:::-;;;;;;;;;;;;;;;;;;;181:26;;;:::i;282:25::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;282:25:1;;:::i;496:94::-;;;:::i;:::-;;;;;;;;;;;;;;;;1698:166;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;1698:166:1;;;;:::i;:::-;;596:948;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;596:948:1;;;;;;;;;;;:::i;214:62::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;214:62:1;;;;;;;;;;;:::i;1550:142::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;1550:142:1;;;;:::i;155:20::-;;;;;;:::o;181:26::-;;;;;;:::o;282:25::-;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;282:25:1;:::o;496:94::-;568:8;:15;496:94;:::o;1698:166::-;1785:11;;;;1771:10;:25;1763:58;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1831:11;:26;;;;;;;;;;;;;;;1698:166::o;596:948::-;666:12;708:6;698:16;;:6;:16;;;;690:59;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;760:14;776;803:6;794:15;;:6;:15;;;:53;;832:6;840;794:53;;;813:6;821;794:53;759:88;;-1:-1:-1;759:88:1;-1:-1:-1;865:20:1;;;857:56;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;931:37;:15;;;966:1;931:15;;;:7;:15;;;;;;;;:23;;;;;;;;;;;;:37;923:72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1035:21;1059:32;;;;;;;;:::i;:::-;41:4:-1;34:5;30:16;25:3;21:26;14:5;7:41;87:2;83:7;78:2;73:3;69:12;65:26;61:2;54:38;1059:32:1;1035:56;;1101:12;1143:6;1151;1126:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;1126:32:1;;;1116:43;;;;;;1101:58;;1247:4;1236:8;1230:15;1225:2;1215:8;1211:17;1208:1;1200:52;1271:47;;;;;;:31;:47;;;;;;;;;;;;;;;;1192:60;;-1:-1:-1;1271:31:1;;;;;;:47;;;;;-1:-1:-1;;1271:47:1;;;;;;;;-1:-1:-1;1271:31:1;:47;;;5:2:-1;;;;30:1;27;20:12;5:2;1271:47:1;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;;;1328:15:1;;;;;;;;:7;:15;;;;;;;;:23;;;;;;;;;;;;:30;;;;;;;;;;;;;;1368:15;;;;;;:23;;;;;;;;:30;;;;;;;;1453:8;27:10:-1;;-1:-1;23:18;;45:23;;1453:19:1;;;;;;;;;;;;;;;;;;1521:15;;1487:50;;;;;;;;;;;;;;;;;;;;;;596:948;;;;;;;;:::o;214:62::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;1550:142::-;1625:11;;;;1611:10;:25;1603:58;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1671:5;:14;;;;;;;;;;;;;;;1550:142::o;102:1764::-;;;;;;;;:::o"}},"interface":[{"inputs":[{"internalType":"address","name":"_feeToSetter","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token0","type":"address"},{"indexed":true,"internalType":"address","name":"token1","type":"address"},{"indexed":false,"internalType":"address","name":"pair","type":"address"},{"indexed":false,"internalType":"uint256","name":"","type":"uint256"}],"name":"PairCreated","type":"event"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allPairs","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"allPairsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"name":"createPair","outputs":[{"internalType":"address","name":"pair","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"feeTo","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"feeToSetter","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"getPair","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_feeTo","type":"address"}],"name":"setFeeTo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_feeToSetter","type":"address"}],"name":"setFeeToSetter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]} \ No newline at end of file +{ + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_feeToSetter", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "pair", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "PairCreated", + "type": "event" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "allPairs", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "allPairsLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + } + ], + "name": "createPair", + "outputs": [ + { + "internalType": "address", + "name": "pair", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "feeTo", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "feeToSetter", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "getPair", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_feeTo", + "type": "address" + } + ], + "name": "setFeeTo", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_feeToSetter", + "type": "address" + } + ], + "name": "setFeeToSetter", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ], + "evm": { + "bytecode": { + "linkReferences": {}, + "object": "608060405234801561001057600080fd5b506040516136863803806136868339818101604052602081101561003357600080fd5b5051600180546001600160a01b0319166001600160a01b03909216919091179055613623806100636000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c8063a2e74af61161005b578063a2e74af6146100fd578063c9c6539614610132578063e6a439051461016d578063f46901ed146101a857610088565b8063017e7e581461008d578063094b7415146100be5780631e3dd18b146100c6578063574f2ba3146100e3575b600080fd5b6100956101db565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b6100956101f7565b610095600480360360208110156100dc57600080fd5b5035610213565b6100eb610247565b60408051918252519081900360200190f35b6101306004803603602081101561011357600080fd5b503573ffffffffffffffffffffffffffffffffffffffff1661024d565b005b6100956004803603604081101561014857600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135811691602001351661031a565b6100956004803603604081101561018357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135811691602001351661076d565b610130600480360360208110156101be57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166107a0565b60005473ffffffffffffffffffffffffffffffffffffffff1681565b60015473ffffffffffffffffffffffffffffffffffffffff1681565b6003818154811061022057fe5b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16905081565b60035490565b60015473ffffffffffffffffffffffffffffffffffffffff1633146102d357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f556e697377617056323a20464f5242494444454e000000000000000000000000604482015290519081900360640190fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60008173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156103b757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f556e697377617056323a204944454e544943414c5f4144445245535345530000604482015290519081900360640190fd5b6000808373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16106103f45783856103f7565b84845b909250905073ffffffffffffffffffffffffffffffffffffffff821661047e57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f556e697377617056323a205a45524f5f41444452455353000000000000000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff82811660009081526002602090815260408083208585168452909152902054161561051f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f556e697377617056323a20504149525f45584953545300000000000000000000604482015290519081900360640190fd5b6060604051806020016105319061086d565b6020820181038252601f19601f82011660405250905060008383604051602001808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b81526014018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140192505050604051602081830303815290604052805190602001209050808251602084016000f5604080517f485cc95500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152868116602483015291519297509087169163485cc9559160448082019260009290919082900301818387803b15801561065e57600080fd5b505af1158015610672573d6000803e3d6000fd5b5050505073ffffffffffffffffffffffffffffffffffffffff84811660008181526002602081815260408084208987168086529083528185208054978d167fffffffffffffffffffffffff000000000000000000000000000000000000000098891681179091559383528185208686528352818520805488168517905560038054600181018255958190527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b90950180549097168417909655925483519283529082015281517f0d3648bd0f6ba80134a33ba9275ac585d9d315f0ad8355cddefde31afa28d0e9929181900390910190a35050505092915050565b600260209081526000928352604080842090915290825290205473ffffffffffffffffffffffffffffffffffffffff1681565b60015473ffffffffffffffffffffffffffffffffffffffff16331461082657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f556e697377617056323a20464f5242494444454e000000000000000000000000604482015290519081900360640190fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b612d748061087b8339019056fe60806040526001600c5534801561001557600080fd5b506040514690806052612d228239604080519182900360520182208282018252600a8352692ab734b9bbb0b8102b1960b11b6020938401528151808301835260018152603160f81b908401528151808401919091527fbfcc8ef98ffbf7b6c3fec7bf5185b566b9863e35a9d83acd49ad6824b5969738818301527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6606082015260808101949094523060a0808601919091528151808603909101815260c09094019052825192019190912060035550600580546001600160a01b03191633179055612c1d806101056000396000f3fe608060405234801561001057600080fd5b50600436106101b95760003560e01c80636a627842116100f9578063ba9a7a5611610097578063d21220a711610071578063d21220a7146105da578063d505accf146105e2578063dd62ed3e14610640578063fff6cae91461067b576101b9565b8063ba9a7a5614610597578063bc25cf771461059f578063c45a0155146105d2576101b9565b80637ecebe00116100d35780637ecebe00146104d757806389afcb441461050a57806395d89b4114610556578063a9059cbb1461055e576101b9565b80636a6278421461046957806370a082311461049c5780637464fc3d146104cf576101b9565b806323b872dd116101665780633644e515116101405780633644e51514610416578063485cc9551461041e5780635909c0d5146104595780635a3d549314610461576101b9565b806323b872dd146103ad57806330adf81f146103f0578063313ce567146103f8576101b9565b8063095ea7b311610197578063095ea7b3146103155780630dfe16811461036257806318160ddd14610393576101b9565b8063022c0d9f146101be57806306fdde03146102595780630902f1ac146102d6575b600080fd5b610257600480360360808110156101d457600080fd5b81359160208101359173ffffffffffffffffffffffffffffffffffffffff604083013516919081019060808101606082013564010000000081111561021857600080fd5b82018360208201111561022a57600080fd5b8035906020019184600183028401116401000000008311171561024c57600080fd5b509092509050610683565b005b610261610d57565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561029b578181015183820152602001610283565b50505050905090810190601f1680156102c85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102de610d90565b604080516dffffffffffffffffffffffffffff948516815292909316602083015263ffffffff168183015290519081900360600190f35b61034e6004803603604081101561032b57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610de5565b604080519115158252519081900360200190f35b61036a610dfc565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b61039b610e18565b60408051918252519081900360200190f35b61034e600480360360608110156103c357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160208101359091169060400135610e1e565b61039b610efd565b610400610f21565b6040805160ff9092168252519081900360200190f35b61039b610f26565b6102576004803603604081101561043457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020013516610f2c565b61039b611005565b61039b61100b565b61039b6004803603602081101561047f57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16611011565b61039b600480360360208110156104b257600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166113cb565b61039b6113dd565b61039b600480360360208110156104ed57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166113e3565b61053d6004803603602081101561052057600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166113f5565b6040805192835260208301919091528051918290030190f35b610261611892565b61034e6004803603604081101561057457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81351690602001356118cb565b61039b6118d8565b610257600480360360208110156105b557600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166118de565b61036a611ad4565b61036a611af0565b610257600480360360e08110156105f857600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160208101359091169060408101359060608101359060ff6080820135169060a08101359060c00135611b0c565b61039b6004803603604081101561065657600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020013516611dd8565b610257611df5565b600c546001146106f457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c55841515806107075750600084115b61075c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180612b2f6025913960400191505060405180910390fd5b600080610767610d90565b5091509150816dffffffffffffffffffffffffffff168710801561079a5750806dffffffffffffffffffffffffffff1686105b6107ef576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180612b786021913960400191505060405180910390fd5b600654600754600091829173ffffffffffffffffffffffffffffffffffffffff91821691908116908916821480159061085457508073ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614155b6108bf57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f556e697377617056323a20494e56414c49445f544f0000000000000000000000604482015290519081900360640190fd5b8a156108d0576108d0828a8d611fdb565b89156108e1576108e1818a8c611fdb565b86156109c3578873ffffffffffffffffffffffffffffffffffffffff166310d1e85c338d8d8c8c6040518663ffffffff1660e01b8152600401808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509650505050505050600060405180830381600087803b1580156109aa57600080fd5b505af11580156109be573d6000803e3d6000fd5b505050505b604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff8416916370a08231916024808301926020929190829003018186803b158015610a2f57600080fd5b505afa158015610a43573d6000803e3d6000fd5b505050506040513d6020811015610a5957600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905191955073ffffffffffffffffffffffffffffffffffffffff8316916370a0823191602480820192602092909190829003018186803b158015610acb57600080fd5b505afa158015610adf573d6000803e3d6000fd5b505050506040513d6020811015610af557600080fd5b5051925060009150506dffffffffffffffffffffffffffff85168a90038311610b1f576000610b35565b89856dffffffffffffffffffffffffffff160383035b9050600089856dffffffffffffffffffffffffffff16038311610b59576000610b6f565b89856dffffffffffffffffffffffffffff160383035b90506000821180610b805750600081115b610bd5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526024815260200180612b546024913960400191505060405180910390fd5b6000610c09610beb84600363ffffffff6121e816565b610bfd876103e863ffffffff6121e816565b9063ffffffff61226e16565b90506000610c21610beb84600363ffffffff6121e816565b9050610c59620f4240610c4d6dffffffffffffffffffffffffffff8b8116908b1663ffffffff6121e816565b9063ffffffff6121e816565b610c69838363ffffffff6121e816565b1015610cd657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f556e697377617056323a204b0000000000000000000000000000000000000000604482015290519081900360640190fd5b5050610ce4848488886122e0565b60408051838152602081018390528082018d9052606081018c9052905173ffffffffffffffffffffffffffffffffffffffff8b169133917fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d8229181900360800190a350506001600c55505050505050505050565b6040518060400160405280600a81526020017f556e69737761702056320000000000000000000000000000000000000000000081525081565b6008546dffffffffffffffffffffffffffff808216926e0100000000000000000000000000008304909116917c0100000000000000000000000000000000000000000000000000000000900463ffffffff1690565b6000610df233848461259c565b5060015b92915050565b60065473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b73ffffffffffffffffffffffffffffffffffffffff831660009081526002602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14610ee85773ffffffffffffffffffffffffffffffffffffffff84166000908152600260209081526040808320338452909152902054610eb6908363ffffffff61226e16565b73ffffffffffffffffffffffffffffffffffffffff851660009081526002602090815260408083203384529091529020555b610ef384848461260b565b5060019392505050565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b601281565b60035481565b60055473ffffffffffffffffffffffffffffffffffffffff163314610fb257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f556e697377617056323a20464f5242494444454e000000000000000000000000604482015290519081900360640190fd5b6006805473ffffffffffffffffffffffffffffffffffffffff9384167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560078054929093169116179055565b60095481565b600a5481565b6000600c5460011461108457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c81905580611094610d90565b50600654604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905193955091935060009273ffffffffffffffffffffffffffffffffffffffff909116916370a08231916024808301926020929190829003018186803b15801561110e57600080fd5b505afa158015611122573d6000803e3d6000fd5b505050506040513d602081101561113857600080fd5b5051600754604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905192935060009273ffffffffffffffffffffffffffffffffffffffff909216916370a0823191602480820192602092909190829003018186803b1580156111b157600080fd5b505afa1580156111c5573d6000803e3d6000fd5b505050506040513d60208110156111db57600080fd5b505190506000611201836dffffffffffffffffffffffffffff871663ffffffff61226e16565b90506000611225836dffffffffffffffffffffffffffff871663ffffffff61226e16565b9050600061123387876126ec565b600054909150806112705761125c6103e8610bfd611257878763ffffffff6121e816565b612878565b985061126b60006103e86128ca565b6112cd565b6112ca6dffffffffffffffffffffffffffff8916611294868463ffffffff6121e816565b8161129b57fe5b046dffffffffffffffffffffffffffff89166112bd868563ffffffff6121e816565b816112c457fe5b0461297a565b98505b60008911611326576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180612bc16028913960400191505060405180910390fd5b6113308a8a6128ca565b61133c86868a8a6122e0565b811561137e5760085461137a906dffffffffffffffffffffffffffff808216916e01000000000000000000000000000090041663ffffffff6121e816565b600b555b6040805185815260208101859052815133927f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f928290030190a250506001600c5550949695505050505050565b60016020526000908152604090205481565b600b5481565b60046020526000908152604090205481565b600080600c5460011461146957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c81905580611479610d90565b50600654600754604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905194965092945073ffffffffffffffffffffffffffffffffffffffff9182169391169160009184916370a08231916024808301926020929190829003018186803b1580156114fb57600080fd5b505afa15801561150f573d6000803e3d6000fd5b505050506040513d602081101561152557600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905191925060009173ffffffffffffffffffffffffffffffffffffffff8516916370a08231916024808301926020929190829003018186803b15801561159957600080fd5b505afa1580156115ad573d6000803e3d6000fd5b505050506040513d60208110156115c357600080fd5b5051306000908152600160205260408120549192506115e288886126ec565b600054909150806115f9848763ffffffff6121e816565b8161160057fe5b049a5080611614848663ffffffff6121e816565b8161161b57fe5b04995060008b11801561162e575060008a115b611683576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180612b996028913960400191505060405180910390fd5b61168d3084612992565b611698878d8d611fdb565b6116a3868d8c611fdb565b604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff8916916370a08231916024808301926020929190829003018186803b15801561170f57600080fd5b505afa158015611723573d6000803e3d6000fd5b505050506040513d602081101561173957600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905191965073ffffffffffffffffffffffffffffffffffffffff8816916370a0823191602480820192602092909190829003018186803b1580156117ab57600080fd5b505afa1580156117bf573d6000803e3d6000fd5b505050506040513d60208110156117d557600080fd5b505193506117e585858b8b6122e0565b811561182757600854611823906dffffffffffffffffffffffffffff808216916e01000000000000000000000000000090041663ffffffff6121e816565b600b555b604080518c8152602081018c9052815173ffffffffffffffffffffffffffffffffffffffff8f169233927fdccd412f0b1252819cb1fd330b93224ca42612892bb3f4f789976e6d81936496929081900390910190a35050505050505050506001600c81905550915091565b6040518060400160405280600681526020017f554e492d5632000000000000000000000000000000000000000000000000000081525081565b6000610df233848461260b565b6103e881565b600c5460011461194f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c55600654600754600854604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff9485169490931692611a2b9285928792611a26926dffffffffffffffffffffffffffff169185916370a0823191602480820192602092909190829003018186803b1580156119ee57600080fd5b505afa158015611a02573d6000803e3d6000fd5b505050506040513d6020811015611a1857600080fd5b50519063ffffffff61226e16565b611fdb565b600854604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051611aca9284928792611a26926e01000000000000000000000000000090046dffffffffffffffffffffffffffff169173ffffffffffffffffffffffffffffffffffffffff8616916370a0823191602480820192602092909190829003018186803b1580156119ee57600080fd5b50506001600c5550565b60055473ffffffffffffffffffffffffffffffffffffffff1681565b60075473ffffffffffffffffffffffffffffffffffffffff1681565b42841015611b7b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f556e697377617056323a20455850495245440000000000000000000000000000604482015290519081900360640190fd5b60035473ffffffffffffffffffffffffffffffffffffffff80891660008181526004602090815260408083208054600180820190925582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98186015280840196909652958d166060860152608085018c905260a085019590955260c08085018b90528151808603909101815260e0850182528051908301207f19010000000000000000000000000000000000000000000000000000000000006101008601526101028501969096526101228085019690965280518085039096018652610142840180825286519683019690962095839052610162840180825286905260ff89166101828501526101a284018890526101c28401879052519193926101e2808201937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081019281900390910190855afa158015611cdc573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590611d5757508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b611dc257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f556e697377617056323a20494e56414c49445f5349474e415455524500000000604482015290519081900360640190fd5b611dcd89898961259c565b505050505050505050565b600260209081526000928352604080842090915290825290205481565b600c54600114611e6657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c55600654604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051611fd49273ffffffffffffffffffffffffffffffffffffffff16916370a08231916024808301926020929190829003018186803b158015611edd57600080fd5b505afa158015611ef1573d6000803e3d6000fd5b505050506040513d6020811015611f0757600080fd5b5051600754604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff909216916370a0823191602480820192602092909190829003018186803b158015611f7a57600080fd5b505afa158015611f8e573d6000803e3d6000fd5b505050506040513d6020811015611fa457600080fd5b50516008546dffffffffffffffffffffffffffff808216916e0100000000000000000000000000009004166122e0565b6001600c55565b604080518082018252601981527f7472616e7366657228616464726573732c75696e743235362900000000000000602091820152815173ffffffffffffffffffffffffffffffffffffffff85811660248301526044808301869052845180840390910181526064909201845291810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001781529251815160009460609489169392918291908083835b602083106120e157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016120a4565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612143576040519150601f19603f3d011682016040523d82523d6000602084013e612148565b606091505b5091509150818015612176575080511580612176575080806020019051602081101561217357600080fd5b50515b6121e157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f556e697377617056323a205452414e534645525f4641494c4544000000000000604482015290519081900360640190fd5b5050505050565b60008115806122035750508082028282828161220057fe5b04145b610df657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f64732d6d6174682d6d756c2d6f766572666c6f77000000000000000000000000604482015290519081900360640190fd5b80820382811115610df657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f64732d6d6174682d7375622d756e646572666c6f770000000000000000000000604482015290519081900360640190fd5b6dffffffffffffffffffffffffffff841180159061230c57506dffffffffffffffffffffffffffff8311155b61237757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f556e697377617056323a204f564552464c4f5700000000000000000000000000604482015290519081900360640190fd5b60085463ffffffff428116917c0100000000000000000000000000000000000000000000000000000000900481168203908116158015906123c757506dffffffffffffffffffffffffffff841615155b80156123e257506dffffffffffffffffffffffffffff831615155b15612492578063ffffffff16612425856123fb86612a57565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169063ffffffff612a7b16565b600980547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff929092169290920201905563ffffffff8116612465846123fb87612a57565b600a80547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff92909216929092020190555b600880547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000166dffffffffffffffffffffffffffff888116919091177fffffffff0000000000000000000000000000ffffffffffffffffffffffffffff166e0100000000000000000000000000008883168102919091177bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c010000000000000000000000000000000000000000000000000000000063ffffffff871602179283905560408051848416815291909304909116602082015281517f1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1929181900390910190a1505050505050565b73ffffffffffffffffffffffffffffffffffffffff808416600081815260026020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260016020526040902054612641908263ffffffff61226e16565b73ffffffffffffffffffffffffffffffffffffffff8085166000908152600160205260408082209390935590841681522054612683908263ffffffff612abc16565b73ffffffffffffffffffffffffffffffffffffffff80841660008181526001602090815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600080600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663017e7e586040518163ffffffff1660e01b815260040160206040518083038186803b15801561275757600080fd5b505afa15801561276b573d6000803e3d6000fd5b505050506040513d602081101561278157600080fd5b5051600b5473ffffffffffffffffffffffffffffffffffffffff821615801594509192509061286457801561285f5760006127d86112576dffffffffffffffffffffffffffff88811690881663ffffffff6121e816565b905060006127e583612878565b90508082111561285c576000612813612804848463ffffffff61226e16565b6000549063ffffffff6121e816565b905060006128388361282c86600563ffffffff6121e816565b9063ffffffff612abc16565b9050600081838161284557fe5b04905080156128585761285887826128ca565b5050505b50505b612870565b8015612870576000600b555b505092915050565b600060038211156128bb575080600160028204015b818110156128b5578091506002818285816128a457fe5b0401816128ad57fe5b04905061288d565b506128c5565b81156128c5575060015b919050565b6000546128dd908263ffffffff612abc16565b600090815573ffffffffffffffffffffffffffffffffffffffff8316815260016020526040902054612915908263ffffffff612abc16565b73ffffffffffffffffffffffffffffffffffffffff831660008181526001602090815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6000818310612989578161298b565b825b9392505050565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600160205260409020546129c8908263ffffffff61226e16565b73ffffffffffffffffffffffffffffffffffffffff831660009081526001602052604081209190915554612a02908263ffffffff61226e16565b600090815560408051838152905173ffffffffffffffffffffffffffffffffffffffff8516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef919081900360200190a35050565b6dffffffffffffffffffffffffffff166e0100000000000000000000000000000290565b60006dffffffffffffffffffffffffffff82167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff841681612ab457fe5b049392505050565b80820182811015610df657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f64732d6d6174682d6164642d6f766572666c6f77000000000000000000000000604482015290519081900360640190fdfe556e697377617056323a20494e53554646494349454e545f4f55545055545f414d4f554e54556e697377617056323a20494e53554646494349454e545f494e5055545f414d4f554e54556e697377617056323a20494e53554646494349454e545f4c4951554944495459556e697377617056323a20494e53554646494349454e545f4c49515549444954595f4255524e4544556e697377617056323a20494e53554646494349454e545f4c49515549444954595f4d494e544544a265627a7a723158207dca18479e58487606bf70c79e44d8dee62353c9ee6d01f9a9d70885b8765f2264736f6c63430005100032454950373132446f6d61696e28737472696e67206e616d652c737472696e672076657273696f6e2c75696e7432353620636861696e49642c6164647265737320766572696679696e67436f6e747261637429a265627a7a723158202760f92d7fa1db6f5aa16307bad65df4ebcc8550c4b1f03755ab8dfd830c178f64736f6c63430005100032", + "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 MLOAD PUSH2 0x3686 CODESIZE SUB DUP1 PUSH2 0x3686 DUP4 CODECOPY DUP2 DUP2 ADD PUSH1 0x40 MSTORE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x33 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x1 DUP1 SLOAD PUSH1 0x1 PUSH1 0x1 PUSH1 0xA0 SHL SUB NOT AND PUSH1 0x1 PUSH1 0x1 PUSH1 0xA0 SHL SUB SWAP1 SWAP3 AND SWAP2 SWAP1 SWAP2 OR SWAP1 SSTORE PUSH2 0x3623 DUP1 PUSH2 0x63 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH2 0x88 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0xA2E74AF6 GT PUSH2 0x5B JUMPI DUP1 PUSH4 0xA2E74AF6 EQ PUSH2 0xFD JUMPI DUP1 PUSH4 0xC9C65396 EQ PUSH2 0x132 JUMPI DUP1 PUSH4 0xE6A43905 EQ PUSH2 0x16D JUMPI DUP1 PUSH4 0xF46901ED EQ PUSH2 0x1A8 JUMPI PUSH2 0x88 JUMP JUMPDEST DUP1 PUSH4 0x17E7E58 EQ PUSH2 0x8D JUMPI DUP1 PUSH4 0x94B7415 EQ PUSH2 0xBE JUMPI DUP1 PUSH4 0x1E3DD18B EQ PUSH2 0xC6 JUMPI DUP1 PUSH4 0x574F2BA3 EQ PUSH2 0xE3 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x95 PUSH2 0x1DB JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x95 PUSH2 0x1F7 JUMP JUMPDEST PUSH2 0x95 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0xDC JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH2 0x213 JUMP JUMPDEST PUSH2 0xEB PUSH2 0x247 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x130 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x113 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x24D JUMP JUMPDEST STOP JUMPDEST PUSH2 0x95 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x148 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 ADD CALLDATALOAD AND PUSH2 0x31A JUMP JUMPDEST PUSH2 0x95 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x183 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 ADD CALLDATALOAD AND PUSH2 0x76D JUMP JUMPDEST PUSH2 0x130 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1BE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x7A0 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x3 DUP2 DUP2 SLOAD DUP2 LT PUSH2 0x220 JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 SWAP1 SWAP2 KECCAK256 ADD SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 POP DUP2 JUMP JUMPDEST PUSH1 0x3 SLOAD SWAP1 JUMP JUMPDEST PUSH1 0x1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x2D3 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20464F5242494444454E000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x1 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP3 SWAP1 SWAP3 AND SWAP2 SWAP1 SWAP2 OR SWAP1 SSTORE JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO PUSH2 0x3B7 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x1E PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204944454E544943414C5F4144445245535345530000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 DUP1 DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND LT PUSH2 0x3F4 JUMPI DUP4 DUP6 PUSH2 0x3F7 JUMP JUMPDEST DUP5 DUP5 JUMPDEST SWAP1 SWAP3 POP SWAP1 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 AND PUSH2 0x47E JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x17 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A205A45524F5F41444452455353000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 DUP6 DUP6 AND DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SLOAD AND ISZERO PUSH2 0x51F JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x16 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20504149525F45584953545300000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x60 PUSH1 0x40 MLOAD DUP1 PUSH1 0x20 ADD PUSH2 0x531 SWAP1 PUSH2 0x86D JUMP JUMPDEST PUSH1 0x20 DUP3 ADD DUP2 SUB DUP3 MSTORE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND PUSH1 0x40 MSTORE POP SWAP1 POP PUSH1 0x0 DUP4 DUP4 PUSH1 0x40 MLOAD PUSH1 0x20 ADD DUP1 DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH1 0x60 SHL DUP2 MSTORE PUSH1 0x14 ADD DUP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH1 0x60 SHL DUP2 MSTORE PUSH1 0x14 ADD SWAP3 POP POP POP PUSH1 0x40 MLOAD PUSH1 0x20 DUP2 DUP4 SUB SUB DUP2 MSTORE SWAP1 PUSH1 0x40 MSTORE DUP1 MLOAD SWAP1 PUSH1 0x20 ADD KECCAK256 SWAP1 POP DUP1 DUP3 MLOAD PUSH1 0x20 DUP5 ADD PUSH1 0x0 CREATE2 PUSH1 0x40 DUP1 MLOAD PUSH32 0x485CC95500000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 DUP2 AND PUSH1 0x4 DUP4 ADD MSTORE DUP7 DUP2 AND PUSH1 0x24 DUP4 ADD MSTORE SWAP2 MLOAD SWAP3 SWAP8 POP SWAP1 DUP8 AND SWAP2 PUSH4 0x485CC955 SWAP2 PUSH1 0x44 DUP1 DUP3 ADD SWAP3 PUSH1 0x0 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP4 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x65E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x672 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 DUP2 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 DUP2 DUP2 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 DUP10 DUP8 AND DUP1 DUP7 MSTORE SWAP1 DUP4 MSTORE DUP2 DUP6 KECCAK256 DUP1 SLOAD SWAP8 DUP14 AND PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 SWAP9 DUP10 AND DUP2 OR SWAP1 SWAP2 SSTORE SWAP4 DUP4 MSTORE DUP2 DUP6 KECCAK256 DUP7 DUP7 MSTORE DUP4 MSTORE DUP2 DUP6 KECCAK256 DUP1 SLOAD DUP9 AND DUP6 OR SWAP1 SSTORE PUSH1 0x3 DUP1 SLOAD PUSH1 0x1 DUP2 ADD DUP3 SSTORE SWAP6 DUP2 SWAP1 MSTORE PUSH32 0xC2575A0E9E593C00F959F8C92F12DB2869C3395A3B0502D05E2516446F71F85B SWAP1 SWAP6 ADD DUP1 SLOAD SWAP1 SWAP8 AND DUP5 OR SWAP1 SWAP7 SSTORE SWAP3 SLOAD DUP4 MLOAD SWAP3 DUP4 MSTORE SWAP1 DUP3 ADD MSTORE DUP2 MLOAD PUSH32 0xD3648BD0F6BA80134A33BA9275AC585D9D315F0AD8355CDDEFDE31AFA28D0E9 SWAP3 SWAP2 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x826 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20464F5242494444454E000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP3 SWAP1 SWAP3 AND SWAP2 SWAP1 SWAP2 OR SWAP1 SSTORE JUMP JUMPDEST PUSH2 0x2D74 DUP1 PUSH2 0x87B DUP4 CODECOPY ADD SWAP1 JUMP INVALID PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x1 PUSH1 0xC SSTORE CALLVALUE DUP1 ISZERO PUSH2 0x15 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 MLOAD CHAINID SWAP1 DUP1 PUSH1 0x52 PUSH2 0x2D22 DUP3 CODECOPY PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 SWAP1 SUB PUSH1 0x52 ADD DUP3 KECCAK256 DUP3 DUP3 ADD DUP3 MSTORE PUSH1 0xA DUP4 MSTORE PUSH10 0x2AB734B9BBB0B8102B19 PUSH1 0xB1 SHL PUSH1 0x20 SWAP4 DUP5 ADD MSTORE DUP2 MLOAD DUP1 DUP4 ADD DUP4 MSTORE PUSH1 0x1 DUP2 MSTORE PUSH1 0x31 PUSH1 0xF8 SHL SWAP1 DUP5 ADD MSTORE DUP2 MLOAD DUP1 DUP5 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH32 0xBFCC8EF98FFBF7B6C3FEC7BF5185B566B9863E35A9D83ACD49AD6824B5969738 DUP2 DUP4 ADD MSTORE PUSH32 0xC89EFDAA54C0F20C7ADF612882DF0950F5A951637E0307CDCB4C672F298B8BC6 PUSH1 0x60 DUP3 ADD MSTORE PUSH1 0x80 DUP2 ADD SWAP5 SWAP1 SWAP5 MSTORE ADDRESS PUSH1 0xA0 DUP1 DUP7 ADD SWAP2 SWAP1 SWAP2 MSTORE DUP2 MLOAD DUP1 DUP7 SUB SWAP1 SWAP2 ADD DUP2 MSTORE PUSH1 0xC0 SWAP1 SWAP5 ADD SWAP1 MSTORE DUP3 MLOAD SWAP3 ADD SWAP2 SWAP1 SWAP2 KECCAK256 PUSH1 0x3 SSTORE POP PUSH1 0x5 DUP1 SLOAD PUSH1 0x1 PUSH1 0x1 PUSH1 0xA0 SHL SUB NOT AND CALLER OR SWAP1 SSTORE PUSH2 0x2C1D DUP1 PUSH2 0x105 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH2 0x1B9 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x6A627842 GT PUSH2 0xF9 JUMPI DUP1 PUSH4 0xBA9A7A56 GT PUSH2 0x97 JUMPI DUP1 PUSH4 0xD21220A7 GT PUSH2 0x71 JUMPI DUP1 PUSH4 0xD21220A7 EQ PUSH2 0x5DA JUMPI DUP1 PUSH4 0xD505ACCF EQ PUSH2 0x5E2 JUMPI DUP1 PUSH4 0xDD62ED3E EQ PUSH2 0x640 JUMPI DUP1 PUSH4 0xFFF6CAE9 EQ PUSH2 0x67B JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0xBA9A7A56 EQ PUSH2 0x597 JUMPI DUP1 PUSH4 0xBC25CF77 EQ PUSH2 0x59F JUMPI DUP1 PUSH4 0xC45A0155 EQ PUSH2 0x5D2 JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x7ECEBE00 GT PUSH2 0xD3 JUMPI DUP1 PUSH4 0x7ECEBE00 EQ PUSH2 0x4D7 JUMPI DUP1 PUSH4 0x89AFCB44 EQ PUSH2 0x50A JUMPI DUP1 PUSH4 0x95D89B41 EQ PUSH2 0x556 JUMPI DUP1 PUSH4 0xA9059CBB EQ PUSH2 0x55E JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x6A627842 EQ PUSH2 0x469 JUMPI DUP1 PUSH4 0x70A08231 EQ PUSH2 0x49C JUMPI DUP1 PUSH4 0x7464FC3D EQ PUSH2 0x4CF JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x23B872DD GT PUSH2 0x166 JUMPI DUP1 PUSH4 0x3644E515 GT PUSH2 0x140 JUMPI DUP1 PUSH4 0x3644E515 EQ PUSH2 0x416 JUMPI DUP1 PUSH4 0x485CC955 EQ PUSH2 0x41E JUMPI DUP1 PUSH4 0x5909C0D5 EQ PUSH2 0x459 JUMPI DUP1 PUSH4 0x5A3D5493 EQ PUSH2 0x461 JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x23B872DD EQ PUSH2 0x3AD JUMPI DUP1 PUSH4 0x30ADF81F EQ PUSH2 0x3F0 JUMPI DUP1 PUSH4 0x313CE567 EQ PUSH2 0x3F8 JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x95EA7B3 GT PUSH2 0x197 JUMPI DUP1 PUSH4 0x95EA7B3 EQ PUSH2 0x315 JUMPI DUP1 PUSH4 0xDFE1681 EQ PUSH2 0x362 JUMPI DUP1 PUSH4 0x18160DDD EQ PUSH2 0x393 JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x22C0D9F EQ PUSH2 0x1BE JUMPI DUP1 PUSH4 0x6FDDE03 EQ PUSH2 0x259 JUMPI DUP1 PUSH4 0x902F1AC EQ PUSH2 0x2D6 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x257 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x80 DUP2 LT ISZERO PUSH2 0x1D4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD SWAP2 PUSH1 0x20 DUP2 ADD CALLDATALOAD SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x40 DUP4 ADD CALLDATALOAD AND SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH1 0x80 DUP2 ADD PUSH1 0x60 DUP3 ADD CALLDATALOAD PUSH5 0x100000000 DUP2 GT ISZERO PUSH2 0x218 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP3 ADD DUP4 PUSH1 0x20 DUP3 ADD GT ISZERO PUSH2 0x22A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP2 DUP5 PUSH1 0x1 DUP4 MUL DUP5 ADD GT PUSH5 0x100000000 DUP4 GT OR ISZERO PUSH2 0x24C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP SWAP1 SWAP3 POP SWAP1 POP PUSH2 0x683 JUMP JUMPDEST STOP JUMPDEST PUSH2 0x261 PUSH2 0xD57 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 ADD SWAP1 DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x29B JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x283 JUMP JUMPDEST POP POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x2C8 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH2 0x2DE PUSH2 0xD90 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP5 DUP6 AND DUP2 MSTORE SWAP3 SWAP1 SWAP4 AND PUSH1 0x20 DUP4 ADD MSTORE PUSH4 0xFFFFFFFF AND DUP2 DUP4 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x60 ADD SWAP1 RETURN JUMPDEST PUSH2 0x34E PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x32B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD AND SWAP1 PUSH1 0x20 ADD CALLDATALOAD PUSH2 0xDE5 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x36A PUSH2 0xDFC JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x39B PUSH2 0xE18 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x34E PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x60 DUP2 LT ISZERO PUSH2 0x3C3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 DUP2 ADD CALLDATALOAD SWAP1 SWAP2 AND SWAP1 PUSH1 0x40 ADD CALLDATALOAD PUSH2 0xE1E JUMP JUMPDEST PUSH2 0x39B PUSH2 0xEFD JUMP JUMPDEST PUSH2 0x400 PUSH2 0xF21 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0xFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x39B PUSH2 0xF26 JUMP JUMPDEST PUSH2 0x257 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x434 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 ADD CALLDATALOAD AND PUSH2 0xF2C JUMP JUMPDEST PUSH2 0x39B PUSH2 0x1005 JUMP JUMPDEST PUSH2 0x39B PUSH2 0x100B JUMP JUMPDEST PUSH2 0x39B PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x47F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x1011 JUMP JUMPDEST PUSH2 0x39B PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x4B2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x13CB JUMP JUMPDEST PUSH2 0x39B PUSH2 0x13DD JUMP JUMPDEST PUSH2 0x39B PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x4ED JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x13E3 JUMP JUMPDEST PUSH2 0x53D PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x520 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x13F5 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP3 DUP4 MSTORE PUSH1 0x20 DUP4 ADD SWAP2 SWAP1 SWAP2 MSTORE DUP1 MLOAD SWAP2 DUP3 SWAP1 SUB ADD SWAP1 RETURN JUMPDEST PUSH2 0x261 PUSH2 0x1892 JUMP JUMPDEST PUSH2 0x34E PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x574 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD AND SWAP1 PUSH1 0x20 ADD CALLDATALOAD PUSH2 0x18CB JUMP JUMPDEST PUSH2 0x39B PUSH2 0x18D8 JUMP JUMPDEST PUSH2 0x257 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x5B5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x18DE JUMP JUMPDEST PUSH2 0x36A PUSH2 0x1AD4 JUMP JUMPDEST PUSH2 0x36A PUSH2 0x1AF0 JUMP JUMPDEST PUSH2 0x257 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0xE0 DUP2 LT ISZERO PUSH2 0x5F8 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 DUP2 ADD CALLDATALOAD SWAP1 SWAP2 AND SWAP1 PUSH1 0x40 DUP2 ADD CALLDATALOAD SWAP1 PUSH1 0x60 DUP2 ADD CALLDATALOAD SWAP1 PUSH1 0xFF PUSH1 0x80 DUP3 ADD CALLDATALOAD AND SWAP1 PUSH1 0xA0 DUP2 ADD CALLDATALOAD SWAP1 PUSH1 0xC0 ADD CALLDATALOAD PUSH2 0x1B0C JUMP JUMPDEST PUSH2 0x39B PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x656 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 ADD CALLDATALOAD AND PUSH2 0x1DD8 JUMP JUMPDEST PUSH2 0x257 PUSH2 0x1DF5 JUMP JUMPDEST PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x6F4 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC SSTORE DUP5 ISZERO ISZERO DUP1 PUSH2 0x707 JUMPI POP PUSH1 0x0 DUP5 GT JUMPDEST PUSH2 0x75C JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x25 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2B2F PUSH1 0x25 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH1 0x0 DUP1 PUSH2 0x767 PUSH2 0xD90 JUMP JUMPDEST POP SWAP2 POP SWAP2 POP DUP2 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP8 LT DUP1 ISZERO PUSH2 0x79A JUMPI POP DUP1 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP7 LT JUMPDEST PUSH2 0x7EF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x21 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2B78 PUSH1 0x21 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH1 0x6 SLOAD PUSH1 0x7 SLOAD PUSH1 0x0 SWAP2 DUP3 SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP2 DUP3 AND SWAP2 SWAP1 DUP2 AND SWAP1 DUP10 AND DUP3 EQ DUP1 ISZERO SWAP1 PUSH2 0x854 JUMPI POP DUP1 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP10 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO JUMPDEST PUSH2 0x8BF JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x15 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20494E56414C49445F544F0000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST DUP11 ISZERO PUSH2 0x8D0 JUMPI PUSH2 0x8D0 DUP3 DUP11 DUP14 PUSH2 0x1FDB JUMP JUMPDEST DUP10 ISZERO PUSH2 0x8E1 JUMPI PUSH2 0x8E1 DUP2 DUP11 DUP13 PUSH2 0x1FDB JUMP JUMPDEST DUP7 ISZERO PUSH2 0x9C3 JUMPI DUP9 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0x10D1E85C CALLER DUP14 DUP14 DUP13 DUP13 PUSH1 0x40 MLOAD DUP7 PUSH4 0xFFFFFFFF AND PUSH1 0xE0 SHL DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP7 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 MSTORE PUSH1 0x20 ADD DUP6 DUP2 MSTORE PUSH1 0x20 ADD DUP5 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE DUP5 DUP5 DUP3 DUP2 DUP2 MSTORE PUSH1 0x20 ADD SWAP3 POP DUP1 DUP3 DUP5 CALLDATACOPY PUSH1 0x0 DUP2 DUP5 ADD MSTORE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND SWAP1 POP DUP1 DUP4 ADD SWAP3 POP POP POP SWAP7 POP POP POP POP POP POP POP PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x9AA JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x9BE JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0xA2F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0xA43 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0xA59 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP2 SWAP6 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0xACB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0xADF JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0xAF5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD SWAP3 POP PUSH1 0x0 SWAP2 POP POP PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND DUP11 SWAP1 SUB DUP4 GT PUSH2 0xB1F JUMPI PUSH1 0x0 PUSH2 0xB35 JUMP JUMPDEST DUP10 DUP6 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SUB DUP4 SUB JUMPDEST SWAP1 POP PUSH1 0x0 DUP10 DUP6 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SUB DUP4 GT PUSH2 0xB59 JUMPI PUSH1 0x0 PUSH2 0xB6F JUMP JUMPDEST DUP10 DUP6 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SUB DUP4 SUB JUMPDEST SWAP1 POP PUSH1 0x0 DUP3 GT DUP1 PUSH2 0xB80 JUMPI POP PUSH1 0x0 DUP2 GT JUMPDEST PUSH2 0xBD5 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x24 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2B54 PUSH1 0x24 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xC09 PUSH2 0xBEB DUP5 PUSH1 0x3 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH2 0xBFD DUP8 PUSH2 0x3E8 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0xC21 PUSH2 0xBEB DUP5 PUSH1 0x3 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 POP PUSH2 0xC59 PUSH3 0xF4240 PUSH2 0xC4D PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP12 DUP2 AND SWAP1 DUP12 AND PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH2 0xC69 DUP4 DUP4 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST LT ISZERO PUSH2 0xCD6 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0xC PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204B0000000000000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST POP POP PUSH2 0xCE4 DUP5 DUP5 DUP9 DUP9 PUSH2 0x22E0 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP4 DUP2 MSTORE PUSH1 0x20 DUP2 ADD DUP4 SWAP1 MSTORE DUP1 DUP3 ADD DUP14 SWAP1 MSTORE PUSH1 0x60 DUP2 ADD DUP13 SWAP1 MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP12 AND SWAP2 CALLER SWAP2 PUSH32 0xD78AD95FA46C994B6551D0DA85FC275FE613CE37657FB8D5E3D130840159D822 SWAP2 DUP2 SWAP1 SUB PUSH1 0x80 ADD SWAP1 LOG3 POP POP PUSH1 0x1 PUSH1 0xC SSTORE POP POP POP POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 PUSH1 0x40 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0xA DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x556E697377617020563200000000000000000000000000000000000000000000 DUP2 MSTORE POP DUP2 JUMP JUMPDEST PUSH1 0x8 SLOAD PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP3 AND SWAP3 PUSH15 0x10000000000000000000000000000 DUP4 DIV SWAP1 SWAP2 AND SWAP2 PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV PUSH4 0xFFFFFFFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xDF2 CALLER DUP5 DUP5 PUSH2 0x259C JUMP JUMPDEST POP PUSH1 0x1 JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x6 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x0 SLOAD DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE SWAP1 SWAP2 MSTORE DUP2 KECCAK256 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF EQ PUSH2 0xEE8 JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SLOAD PUSH2 0xEB6 SWAP1 DUP4 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SSTORE JUMPDEST PUSH2 0xEF3 DUP5 DUP5 DUP5 PUSH2 0x260B JUMP JUMPDEST POP PUSH1 0x1 SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH32 0x6E71EDAE12B1B97F4D1F60370FEF10105FA2FAAE0126114A169C64845D6126C9 DUP2 JUMP JUMPDEST PUSH1 0x12 DUP2 JUMP JUMPDEST PUSH1 0x3 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x5 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0xFB2 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20464F5242494444454E000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x6 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP4 DUP5 AND PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 SWAP2 DUP3 AND OR SWAP1 SWAP2 SSTORE PUSH1 0x7 DUP1 SLOAD SWAP3 SWAP1 SWAP4 AND SWAP2 AND OR SWAP1 SSTORE JUMP JUMPDEST PUSH1 0x9 SLOAD DUP2 JUMP JUMPDEST PUSH1 0xA SLOAD DUP2 JUMP JUMPDEST PUSH1 0x0 PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x1084 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC DUP2 SWAP1 SSTORE DUP1 PUSH2 0x1094 PUSH2 0xD90 JUMP JUMPDEST POP PUSH1 0x6 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP4 SWAP6 POP SWAP2 SWAP4 POP PUSH1 0x0 SWAP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP2 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x110E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1122 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1138 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x7 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP3 SWAP4 POP PUSH1 0x0 SWAP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP3 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x11B1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x11C5 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x11DB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD SWAP1 POP PUSH1 0x0 PUSH2 0x1201 DUP4 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x1225 DUP4 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x1233 DUP8 DUP8 PUSH2 0x26EC JUMP JUMPDEST PUSH1 0x0 SLOAD SWAP1 SWAP2 POP DUP1 PUSH2 0x1270 JUMPI PUSH2 0x125C PUSH2 0x3E8 PUSH2 0xBFD PUSH2 0x1257 DUP8 DUP8 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH2 0x2878 JUMP JUMPDEST SWAP9 POP PUSH2 0x126B PUSH1 0x0 PUSH2 0x3E8 PUSH2 0x28CA JUMP JUMPDEST PUSH2 0x12CD JUMP JUMPDEST PUSH2 0x12CA PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP10 AND PUSH2 0x1294 DUP7 DUP5 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST DUP2 PUSH2 0x129B JUMPI INVALID JUMPDEST DIV PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP10 AND PUSH2 0x12BD DUP7 DUP6 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST DUP2 PUSH2 0x12C4 JUMPI INVALID JUMPDEST DIV PUSH2 0x297A JUMP JUMPDEST SWAP9 POP JUMPDEST PUSH1 0x0 DUP10 GT PUSH2 0x1326 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x28 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2BC1 PUSH1 0x28 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH2 0x1330 DUP11 DUP11 PUSH2 0x28CA JUMP JUMPDEST PUSH2 0x133C DUP7 DUP7 DUP11 DUP11 PUSH2 0x22E0 JUMP JUMPDEST DUP2 ISZERO PUSH2 0x137E JUMPI PUSH1 0x8 SLOAD PUSH2 0x137A SWAP1 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP3 AND SWAP2 PUSH15 0x10000000000000000000000000000 SWAP1 DIV AND PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH1 0xB SSTORE JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP6 DUP2 MSTORE PUSH1 0x20 DUP2 ADD DUP6 SWAP1 MSTORE DUP2 MLOAD CALLER SWAP3 PUSH32 0x4C209B5FC8AD50758F13E2E1088BA56A560DFF690A1C6FEF26394F4C03821C4F SWAP3 DUP3 SWAP1 SUB ADD SWAP1 LOG2 POP POP PUSH1 0x1 PUSH1 0xC SSTORE POP SWAP5 SWAP7 SWAP6 POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH1 0xB SLOAD DUP2 JUMP JUMPDEST PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x1469 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC DUP2 SWAP1 SSTORE DUP1 PUSH2 0x1479 PUSH2 0xD90 JUMP JUMPDEST POP PUSH1 0x6 SLOAD PUSH1 0x7 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP5 SWAP7 POP SWAP3 SWAP5 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP2 DUP3 AND SWAP4 SWAP2 AND SWAP2 PUSH1 0x0 SWAP2 DUP5 SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x14FB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x150F JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1525 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP2 SWAP3 POP PUSH1 0x0 SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x1599 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x15AD JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x15C3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD ADDRESS PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 SLOAD SWAP2 SWAP3 POP PUSH2 0x15E2 DUP9 DUP9 PUSH2 0x26EC JUMP JUMPDEST PUSH1 0x0 SLOAD SWAP1 SWAP2 POP DUP1 PUSH2 0x15F9 DUP5 DUP8 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST DUP2 PUSH2 0x1600 JUMPI INVALID JUMPDEST DIV SWAP11 POP DUP1 PUSH2 0x1614 DUP5 DUP7 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST DUP2 PUSH2 0x161B JUMPI INVALID JUMPDEST DIV SWAP10 POP PUSH1 0x0 DUP12 GT DUP1 ISZERO PUSH2 0x162E JUMPI POP PUSH1 0x0 DUP11 GT JUMPDEST PUSH2 0x1683 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x28 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2B99 PUSH1 0x28 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH2 0x168D ADDRESS DUP5 PUSH2 0x2992 JUMP JUMPDEST PUSH2 0x1698 DUP8 DUP14 DUP14 PUSH2 0x1FDB JUMP JUMPDEST PUSH2 0x16A3 DUP7 DUP14 DUP13 PUSH2 0x1FDB JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP10 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x170F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1723 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1739 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP2 SWAP7 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x17AB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x17BF JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x17D5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD SWAP4 POP PUSH2 0x17E5 DUP6 DUP6 DUP12 DUP12 PUSH2 0x22E0 JUMP JUMPDEST DUP2 ISZERO PUSH2 0x1827 JUMPI PUSH1 0x8 SLOAD PUSH2 0x1823 SWAP1 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP3 AND SWAP2 PUSH15 0x10000000000000000000000000000 SWAP1 DIV AND PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH1 0xB SSTORE JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP13 DUP2 MSTORE PUSH1 0x20 DUP2 ADD DUP13 SWAP1 MSTORE DUP2 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP16 AND SWAP3 CALLER SWAP3 PUSH32 0xDCCD412F0B1252819CB1FD330B93224CA42612892BB3F4F789976E6D81936496 SWAP3 SWAP1 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP POP POP POP POP POP POP POP POP PUSH1 0x1 PUSH1 0xC DUP2 SWAP1 SSTORE POP SWAP2 POP SWAP2 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 PUSH1 0x40 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x6 DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x554E492D56320000000000000000000000000000000000000000000000000000 DUP2 MSTORE POP DUP2 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xDF2 CALLER DUP5 DUP5 PUSH2 0x260B JUMP JUMPDEST PUSH2 0x3E8 DUP2 JUMP JUMPDEST PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x194F JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC SSTORE PUSH1 0x6 SLOAD PUSH1 0x7 SLOAD PUSH1 0x8 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP5 DUP6 AND SWAP5 SWAP1 SWAP4 AND SWAP3 PUSH2 0x1A2B SWAP3 DUP6 SWAP3 DUP8 SWAP3 PUSH2 0x1A26 SWAP3 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP2 DUP6 SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x19EE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1A02 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1A18 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH2 0x1FDB JUMP JUMPDEST PUSH1 0x8 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH2 0x1ACA SWAP3 DUP5 SWAP3 DUP8 SWAP3 PUSH2 0x1A26 SWAP3 PUSH15 0x10000000000000000000000000000 SWAP1 DIV PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x19EE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP POP PUSH1 0x1 PUSH1 0xC SSTORE POP JUMP JUMPDEST PUSH1 0x5 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x7 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST TIMESTAMP DUP5 LT ISZERO PUSH2 0x1B7B JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x12 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20455850495245440000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x3 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP10 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 DUP1 SLOAD PUSH1 0x1 DUP1 DUP3 ADD SWAP1 SWAP3 SSTORE DUP3 MLOAD PUSH32 0x6E71EDAE12B1B97F4D1F60370FEF10105FA2FAAE0126114A169C64845D6126C9 DUP2 DUP7 ADD MSTORE DUP1 DUP5 ADD SWAP7 SWAP1 SWAP7 MSTORE SWAP6 DUP14 AND PUSH1 0x60 DUP7 ADD MSTORE PUSH1 0x80 DUP6 ADD DUP13 SWAP1 MSTORE PUSH1 0xA0 DUP6 ADD SWAP6 SWAP1 SWAP6 MSTORE PUSH1 0xC0 DUP1 DUP6 ADD DUP12 SWAP1 MSTORE DUP2 MLOAD DUP1 DUP7 SUB SWAP1 SWAP2 ADD DUP2 MSTORE PUSH1 0xE0 DUP6 ADD DUP3 MSTORE DUP1 MLOAD SWAP1 DUP4 ADD KECCAK256 PUSH32 0x1901000000000000000000000000000000000000000000000000000000000000 PUSH2 0x100 DUP7 ADD MSTORE PUSH2 0x102 DUP6 ADD SWAP7 SWAP1 SWAP7 MSTORE PUSH2 0x122 DUP1 DUP6 ADD SWAP7 SWAP1 SWAP7 MSTORE DUP1 MLOAD DUP1 DUP6 SUB SWAP1 SWAP7 ADD DUP7 MSTORE PUSH2 0x142 DUP5 ADD DUP1 DUP3 MSTORE DUP7 MLOAD SWAP7 DUP4 ADD SWAP7 SWAP1 SWAP7 KECCAK256 SWAP6 DUP4 SWAP1 MSTORE PUSH2 0x162 DUP5 ADD DUP1 DUP3 MSTORE DUP7 SWAP1 MSTORE PUSH1 0xFF DUP10 AND PUSH2 0x182 DUP6 ADD MSTORE PUSH2 0x1A2 DUP5 ADD DUP9 SWAP1 MSTORE PUSH2 0x1C2 DUP5 ADD DUP8 SWAP1 MSTORE MLOAD SWAP2 SWAP4 SWAP3 PUSH2 0x1E2 DUP1 DUP3 ADD SWAP4 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP2 ADD SWAP3 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 DUP6 GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1CDC JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP PUSH1 0x40 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 ADD MLOAD SWAP2 POP POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 AND ISZERO DUP1 ISZERO SWAP1 PUSH2 0x1D57 JUMPI POP DUP9 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ JUMPDEST PUSH2 0x1DC2 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x1C PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20494E56414C49445F5349474E415455524500000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH2 0x1DCD DUP10 DUP10 DUP10 PUSH2 0x259C JUMP JUMPDEST POP POP POP POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x1E66 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC SSTORE PUSH1 0x6 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH2 0x1FD4 SWAP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x1EDD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1EF1 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1F07 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x7 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP3 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x1F7A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1F8E JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1FA4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x8 SLOAD PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP3 AND SWAP2 PUSH15 0x10000000000000000000000000000 SWAP1 DIV AND PUSH2 0x22E0 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xC SSTORE JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD DUP3 MSTORE PUSH1 0x19 DUP2 MSTORE PUSH32 0x7472616E7366657228616464726573732C75696E743235362900000000000000 PUSH1 0x20 SWAP2 DUP3 ADD MSTORE DUP2 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 DUP2 AND PUSH1 0x24 DUP4 ADD MSTORE PUSH1 0x44 DUP1 DUP4 ADD DUP7 SWAP1 MSTORE DUP5 MLOAD DUP1 DUP5 SUB SWAP1 SWAP2 ADD DUP2 MSTORE PUSH1 0x64 SWAP1 SWAP3 ADD DUP5 MSTORE SWAP2 DUP2 ADD DUP1 MLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xA9059CBB00000000000000000000000000000000000000000000000000000000 OR DUP2 MSTORE SWAP3 MLOAD DUP2 MLOAD PUSH1 0x0 SWAP5 PUSH1 0x60 SWAP5 DUP10 AND SWAP4 SWAP3 SWAP2 DUP3 SWAP2 SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x20E1 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x20A4 JUMP JUMPDEST PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB DUP1 NOT DUP3 MLOAD AND DUP2 DUP5 MLOAD AND DUP1 DUP3 OR DUP6 MSTORE POP POP POP POP POP POP SWAP1 POP ADD SWAP2 POP POP PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP7 GAS CALL SWAP2 POP POP RETURNDATASIZE DUP1 PUSH1 0x0 DUP2 EQ PUSH2 0x2143 JUMPI PUSH1 0x40 MLOAD SWAP2 POP PUSH1 0x1F NOT PUSH1 0x3F RETURNDATASIZE ADD AND DUP3 ADD PUSH1 0x40 MSTORE RETURNDATASIZE DUP3 MSTORE RETURNDATASIZE PUSH1 0x0 PUSH1 0x20 DUP5 ADD RETURNDATACOPY PUSH2 0x2148 JUMP JUMPDEST PUSH1 0x60 SWAP2 POP JUMPDEST POP SWAP2 POP SWAP2 POP DUP2 DUP1 ISZERO PUSH2 0x2176 JUMPI POP DUP1 MLOAD ISZERO DUP1 PUSH2 0x2176 JUMPI POP DUP1 DUP1 PUSH1 0x20 ADD SWAP1 MLOAD PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x2173 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD JUMPDEST PUSH2 0x21E1 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x1A PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A205452414E534645525F4641494C4544000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 ISZERO DUP1 PUSH2 0x2203 JUMPI POP POP DUP1 DUP3 MUL DUP3 DUP3 DUP3 DUP2 PUSH2 0x2200 JUMPI INVALID JUMPDEST DIV EQ JUMPDEST PUSH2 0xDF6 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x64732D6D6174682D6D756C2D6F766572666C6F77000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST DUP1 DUP3 SUB DUP3 DUP2 GT ISZERO PUSH2 0xDF6 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x15 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x64732D6D6174682D7375622D756E646572666C6F770000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 GT DUP1 ISZERO SWAP1 PUSH2 0x230C JUMPI POP PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 GT ISZERO JUMPDEST PUSH2 0x2377 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x13 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204F564552464C4F5700000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x8 SLOAD PUSH4 0xFFFFFFFF TIMESTAMP DUP2 AND SWAP2 PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV DUP2 AND DUP3 SUB SWAP1 DUP2 AND ISZERO DUP1 ISZERO SWAP1 PUSH2 0x23C7 JUMPI POP PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND ISZERO ISZERO JUMPDEST DUP1 ISZERO PUSH2 0x23E2 JUMPI POP PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND ISZERO ISZERO JUMPDEST ISZERO PUSH2 0x2492 JUMPI DUP1 PUSH4 0xFFFFFFFF AND PUSH2 0x2425 DUP6 PUSH2 0x23FB DUP7 PUSH2 0x2A57 JUMP JUMPDEST PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x2A7B AND JUMP JUMPDEST PUSH1 0x9 DUP1 SLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP3 SWAP1 SWAP3 AND SWAP3 SWAP1 SWAP3 MUL ADD SWAP1 SSTORE PUSH4 0xFFFFFFFF DUP2 AND PUSH2 0x2465 DUP5 PUSH2 0x23FB DUP8 PUSH2 0x2A57 JUMP JUMPDEST PUSH1 0xA DUP1 SLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP3 SWAP1 SWAP3 AND SWAP3 SWAP1 SWAP3 MUL ADD SWAP1 SSTORE JUMPDEST PUSH1 0x8 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000 AND PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 DUP2 AND SWAP2 SWAP1 SWAP2 OR PUSH32 0xFFFFFFFF0000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH15 0x10000000000000000000000000000 DUP9 DUP4 AND DUP2 MUL SWAP2 SWAP1 SWAP2 OR PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH4 0xFFFFFFFF DUP8 AND MUL OR SWAP3 DUP4 SWAP1 SSTORE PUSH1 0x40 DUP1 MLOAD DUP5 DUP5 AND DUP2 MSTORE SWAP2 SWAP1 SWAP4 DIV SWAP1 SWAP2 AND PUSH1 0x20 DUP3 ADD MSTORE DUP2 MLOAD PUSH32 0x1C411E9A96E071241C2F21F7726B17AE89E3CAB4C78BE50E062B03A9FFFBBAD1 SWAP3 SWAP2 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG1 POP POP POP POP POP POP JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP5 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 DUP8 AND DUP1 DUP5 MSTORE SWAP5 DUP3 MSTORE SWAP2 DUP3 SWAP1 KECCAK256 DUP6 SWAP1 SSTORE DUP2 MLOAD DUP6 DUP2 MSTORE SWAP2 MLOAD PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP3 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP POP POP JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH2 0x2641 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 DUP1 DUP3 KECCAK256 SWAP4 SWAP1 SWAP4 SSTORE SWAP1 DUP5 AND DUP2 MSTORE KECCAK256 SLOAD PUSH2 0x2683 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x2ABC AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP5 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP2 DUP3 SWAP1 KECCAK256 SWAP5 SWAP1 SWAP5 SSTORE DUP1 MLOAD DUP6 DUP2 MSTORE SWAP1 MLOAD SWAP2 SWAP4 SWAP3 DUP8 AND SWAP3 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP3 SWAP2 DUP3 SWAP1 SUB ADD SWAP1 LOG3 POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x5 PUSH1 0x0 SWAP1 SLOAD SWAP1 PUSH2 0x100 EXP SWAP1 DIV PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0x17E7E58 PUSH1 0x40 MLOAD DUP2 PUSH4 0xFFFFFFFF AND PUSH1 0xE0 SHL DUP2 MSTORE PUSH1 0x4 ADD PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x2757 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x276B JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x2781 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0xB SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 AND ISZERO DUP1 ISZERO SWAP5 POP SWAP2 SWAP3 POP SWAP1 PUSH2 0x2864 JUMPI DUP1 ISZERO PUSH2 0x285F JUMPI PUSH1 0x0 PUSH2 0x27D8 PUSH2 0x1257 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 DUP2 AND SWAP1 DUP9 AND PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x27E5 DUP4 PUSH2 0x2878 JUMP JUMPDEST SWAP1 POP DUP1 DUP3 GT ISZERO PUSH2 0x285C JUMPI PUSH1 0x0 PUSH2 0x2813 PUSH2 0x2804 DUP5 DUP5 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH1 0x0 SLOAD SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x2838 DUP4 PUSH2 0x282C DUP7 PUSH1 0x5 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x2ABC AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 DUP2 DUP4 DUP2 PUSH2 0x2845 JUMPI INVALID JUMPDEST DIV SWAP1 POP DUP1 ISZERO PUSH2 0x2858 JUMPI PUSH2 0x2858 DUP8 DUP3 PUSH2 0x28CA JUMP JUMPDEST POP POP POP JUMPDEST POP POP JUMPDEST PUSH2 0x2870 JUMP JUMPDEST DUP1 ISZERO PUSH2 0x2870 JUMPI PUSH1 0x0 PUSH1 0xB SSTORE JUMPDEST POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x3 DUP3 GT ISZERO PUSH2 0x28BB JUMPI POP DUP1 PUSH1 0x1 PUSH1 0x2 DUP3 DIV ADD JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x28B5 JUMPI DUP1 SWAP2 POP PUSH1 0x2 DUP2 DUP3 DUP6 DUP2 PUSH2 0x28A4 JUMPI INVALID JUMPDEST DIV ADD DUP2 PUSH2 0x28AD JUMPI INVALID JUMPDEST DIV SWAP1 POP PUSH2 0x288D JUMP JUMPDEST POP PUSH2 0x28C5 JUMP JUMPDEST DUP2 ISZERO PUSH2 0x28C5 JUMPI POP PUSH1 0x1 JUMPDEST SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH2 0x28DD SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x2ABC AND JUMP JUMPDEST PUSH1 0x0 SWAP1 DUP2 SSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH2 0x2915 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x2ABC AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 SWAP1 SWAP5 SSTORE DUP4 MLOAD DUP6 DUP2 MSTORE SWAP4 MLOAD SWAP3 SWAP4 SWAP2 SWAP3 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP3 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 DUP4 LT PUSH2 0x2989 JUMPI DUP2 PUSH2 0x298B JUMP JUMPDEST DUP3 JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH2 0x29C8 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 SWAP2 SWAP1 SWAP2 SSTORE SLOAD PUSH2 0x2A02 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH1 0x0 SWAP1 DUP2 SSTORE PUSH1 0x40 DUP1 MLOAD DUP4 DUP2 MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND SWAP2 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP2 SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 LOG3 POP POP JUMP JUMPDEST PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH15 0x10000000000000000000000000000 MUL SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 AND PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND DUP2 PUSH2 0x2AB4 JUMPI INVALID JUMPDEST DIV SWAP4 SWAP3 POP POP POP JUMP JUMPDEST DUP1 DUP3 ADD DUP3 DUP2 LT ISZERO PUSH2 0xDF6 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x64732D6D6174682D6164642D6F766572666C6F77000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT INVALID SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x4F SSTORE SLOAD POP SSTORE SLOAD 0x5F COINBASE 0x4D 0x4F SSTORE 0x4E SLOAD SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x49 0x4E POP SSTORE SLOAD 0x5F COINBASE 0x4D 0x4F SSTORE 0x4E SLOAD SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x4C 0x49 MLOAD SSTORE 0x49 DIFFICULTY 0x49 SLOAD MSIZE SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x4C 0x49 MLOAD SSTORE 0x49 DIFFICULTY 0x49 SLOAD MSIZE 0x5F TIMESTAMP SSTORE MSTORE 0x4E GASLIMIT DIFFICULTY SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x4C 0x49 MLOAD SSTORE 0x49 DIFFICULTY 0x49 SLOAD MSIZE 0x5F 0x4D 0x49 0x4E SLOAD GASLIMIT DIFFICULTY LOG2 PUSH6 0x627A7A723158 KECCAK256 PUSH30 0xCA18479E58487606BF70C79E44D8DEE62353C9EE6D01F9A9D70885B8765F 0x22 PUSH5 0x736F6C6343 STOP SDIV LT STOP ORIGIN GASLIMIT 0x49 POP CALLDATACOPY BALANCE ORIGIN DIFFICULTY PUSH16 0x6D61696E28737472696E67206E616D65 0x2C PUSH20 0x7472696E672076657273696F6E2C75696E743235 CALLDATASIZE KECCAK256 PUSH4 0x6861696E 0x49 PUSH5 0x2C61646472 PUSH6 0x737320766572 PUSH10 0x6679696E67436F6E7472 PUSH2 0x6374 0x29 LOG2 PUSH6 0x627A7A723158 KECCAK256 0x27 PUSH1 0xF9 0x2D PUSH32 0xA1DB6F5AA16307BAD65DF4EBCC8550C4B1F03755AB8DFD830C178F64736F6C63 NUMBER STOP SDIV LT STOP ORIGIN ", + "sourceMap": "102:1764:1:-;;;406:84;8:9:-1;5:2;;;30:1;27;20:12;5:2;406:84:1;;;;;;;;;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;406:84:1;457:11;:26;;-1:-1:-1;;;;;;457:26:1;-1:-1:-1;;;;;457:26:1;;;;;;;;;102:1764;;;-1:-1:-1;102:1764:1;;" + }, + "deployedBytecode": { + "linkReferences": {}, + "object": "608060405234801561001057600080fd5b50600436106100885760003560e01c8063a2e74af61161005b578063a2e74af6146100fd578063c9c6539614610132578063e6a439051461016d578063f46901ed146101a857610088565b8063017e7e581461008d578063094b7415146100be5780631e3dd18b146100c6578063574f2ba3146100e3575b600080fd5b6100956101db565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b6100956101f7565b610095600480360360208110156100dc57600080fd5b5035610213565b6100eb610247565b60408051918252519081900360200190f35b6101306004803603602081101561011357600080fd5b503573ffffffffffffffffffffffffffffffffffffffff1661024d565b005b6100956004803603604081101561014857600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135811691602001351661031a565b6100956004803603604081101561018357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135811691602001351661076d565b610130600480360360208110156101be57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166107a0565b60005473ffffffffffffffffffffffffffffffffffffffff1681565b60015473ffffffffffffffffffffffffffffffffffffffff1681565b6003818154811061022057fe5b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16905081565b60035490565b60015473ffffffffffffffffffffffffffffffffffffffff1633146102d357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f556e697377617056323a20464f5242494444454e000000000000000000000000604482015290519081900360640190fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60008173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156103b757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f556e697377617056323a204944454e544943414c5f4144445245535345530000604482015290519081900360640190fd5b6000808373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16106103f45783856103f7565b84845b909250905073ffffffffffffffffffffffffffffffffffffffff821661047e57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f556e697377617056323a205a45524f5f41444452455353000000000000000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff82811660009081526002602090815260408083208585168452909152902054161561051f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f556e697377617056323a20504149525f45584953545300000000000000000000604482015290519081900360640190fd5b6060604051806020016105319061086d565b6020820181038252601f19601f82011660405250905060008383604051602001808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b81526014018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140192505050604051602081830303815290604052805190602001209050808251602084016000f5604080517f485cc95500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152868116602483015291519297509087169163485cc9559160448082019260009290919082900301818387803b15801561065e57600080fd5b505af1158015610672573d6000803e3d6000fd5b5050505073ffffffffffffffffffffffffffffffffffffffff84811660008181526002602081815260408084208987168086529083528185208054978d167fffffffffffffffffffffffff000000000000000000000000000000000000000098891681179091559383528185208686528352818520805488168517905560038054600181018255958190527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b90950180549097168417909655925483519283529082015281517f0d3648bd0f6ba80134a33ba9275ac585d9d315f0ad8355cddefde31afa28d0e9929181900390910190a35050505092915050565b600260209081526000928352604080842090915290825290205473ffffffffffffffffffffffffffffffffffffffff1681565b60015473ffffffffffffffffffffffffffffffffffffffff16331461082657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f556e697377617056323a20464f5242494444454e000000000000000000000000604482015290519081900360640190fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b612d748061087b8339019056fe60806040526001600c5534801561001557600080fd5b506040514690806052612d228239604080519182900360520182208282018252600a8352692ab734b9bbb0b8102b1960b11b6020938401528151808301835260018152603160f81b908401528151808401919091527fbfcc8ef98ffbf7b6c3fec7bf5185b566b9863e35a9d83acd49ad6824b5969738818301527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6606082015260808101949094523060a0808601919091528151808603909101815260c09094019052825192019190912060035550600580546001600160a01b03191633179055612c1d806101056000396000f3fe608060405234801561001057600080fd5b50600436106101b95760003560e01c80636a627842116100f9578063ba9a7a5611610097578063d21220a711610071578063d21220a7146105da578063d505accf146105e2578063dd62ed3e14610640578063fff6cae91461067b576101b9565b8063ba9a7a5614610597578063bc25cf771461059f578063c45a0155146105d2576101b9565b80637ecebe00116100d35780637ecebe00146104d757806389afcb441461050a57806395d89b4114610556578063a9059cbb1461055e576101b9565b80636a6278421461046957806370a082311461049c5780637464fc3d146104cf576101b9565b806323b872dd116101665780633644e515116101405780633644e51514610416578063485cc9551461041e5780635909c0d5146104595780635a3d549314610461576101b9565b806323b872dd146103ad57806330adf81f146103f0578063313ce567146103f8576101b9565b8063095ea7b311610197578063095ea7b3146103155780630dfe16811461036257806318160ddd14610393576101b9565b8063022c0d9f146101be57806306fdde03146102595780630902f1ac146102d6575b600080fd5b610257600480360360808110156101d457600080fd5b81359160208101359173ffffffffffffffffffffffffffffffffffffffff604083013516919081019060808101606082013564010000000081111561021857600080fd5b82018360208201111561022a57600080fd5b8035906020019184600183028401116401000000008311171561024c57600080fd5b509092509050610683565b005b610261610d57565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561029b578181015183820152602001610283565b50505050905090810190601f1680156102c85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102de610d90565b604080516dffffffffffffffffffffffffffff948516815292909316602083015263ffffffff168183015290519081900360600190f35b61034e6004803603604081101561032b57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610de5565b604080519115158252519081900360200190f35b61036a610dfc565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b61039b610e18565b60408051918252519081900360200190f35b61034e600480360360608110156103c357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160208101359091169060400135610e1e565b61039b610efd565b610400610f21565b6040805160ff9092168252519081900360200190f35b61039b610f26565b6102576004803603604081101561043457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020013516610f2c565b61039b611005565b61039b61100b565b61039b6004803603602081101561047f57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16611011565b61039b600480360360208110156104b257600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166113cb565b61039b6113dd565b61039b600480360360208110156104ed57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166113e3565b61053d6004803603602081101561052057600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166113f5565b6040805192835260208301919091528051918290030190f35b610261611892565b61034e6004803603604081101561057457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81351690602001356118cb565b61039b6118d8565b610257600480360360208110156105b557600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166118de565b61036a611ad4565b61036a611af0565b610257600480360360e08110156105f857600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160208101359091169060408101359060608101359060ff6080820135169060a08101359060c00135611b0c565b61039b6004803603604081101561065657600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020013516611dd8565b610257611df5565b600c546001146106f457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c55841515806107075750600084115b61075c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180612b2f6025913960400191505060405180910390fd5b600080610767610d90565b5091509150816dffffffffffffffffffffffffffff168710801561079a5750806dffffffffffffffffffffffffffff1686105b6107ef576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180612b786021913960400191505060405180910390fd5b600654600754600091829173ffffffffffffffffffffffffffffffffffffffff91821691908116908916821480159061085457508073ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614155b6108bf57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f556e697377617056323a20494e56414c49445f544f0000000000000000000000604482015290519081900360640190fd5b8a156108d0576108d0828a8d611fdb565b89156108e1576108e1818a8c611fdb565b86156109c3578873ffffffffffffffffffffffffffffffffffffffff166310d1e85c338d8d8c8c6040518663ffffffff1660e01b8152600401808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509650505050505050600060405180830381600087803b1580156109aa57600080fd5b505af11580156109be573d6000803e3d6000fd5b505050505b604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff8416916370a08231916024808301926020929190829003018186803b158015610a2f57600080fd5b505afa158015610a43573d6000803e3d6000fd5b505050506040513d6020811015610a5957600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905191955073ffffffffffffffffffffffffffffffffffffffff8316916370a0823191602480820192602092909190829003018186803b158015610acb57600080fd5b505afa158015610adf573d6000803e3d6000fd5b505050506040513d6020811015610af557600080fd5b5051925060009150506dffffffffffffffffffffffffffff85168a90038311610b1f576000610b35565b89856dffffffffffffffffffffffffffff160383035b9050600089856dffffffffffffffffffffffffffff16038311610b59576000610b6f565b89856dffffffffffffffffffffffffffff160383035b90506000821180610b805750600081115b610bd5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526024815260200180612b546024913960400191505060405180910390fd5b6000610c09610beb84600363ffffffff6121e816565b610bfd876103e863ffffffff6121e816565b9063ffffffff61226e16565b90506000610c21610beb84600363ffffffff6121e816565b9050610c59620f4240610c4d6dffffffffffffffffffffffffffff8b8116908b1663ffffffff6121e816565b9063ffffffff6121e816565b610c69838363ffffffff6121e816565b1015610cd657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f556e697377617056323a204b0000000000000000000000000000000000000000604482015290519081900360640190fd5b5050610ce4848488886122e0565b60408051838152602081018390528082018d9052606081018c9052905173ffffffffffffffffffffffffffffffffffffffff8b169133917fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d8229181900360800190a350506001600c55505050505050505050565b6040518060400160405280600a81526020017f556e69737761702056320000000000000000000000000000000000000000000081525081565b6008546dffffffffffffffffffffffffffff808216926e0100000000000000000000000000008304909116917c0100000000000000000000000000000000000000000000000000000000900463ffffffff1690565b6000610df233848461259c565b5060015b92915050565b60065473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b73ffffffffffffffffffffffffffffffffffffffff831660009081526002602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14610ee85773ffffffffffffffffffffffffffffffffffffffff84166000908152600260209081526040808320338452909152902054610eb6908363ffffffff61226e16565b73ffffffffffffffffffffffffffffffffffffffff851660009081526002602090815260408083203384529091529020555b610ef384848461260b565b5060019392505050565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b601281565b60035481565b60055473ffffffffffffffffffffffffffffffffffffffff163314610fb257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f556e697377617056323a20464f5242494444454e000000000000000000000000604482015290519081900360640190fd5b6006805473ffffffffffffffffffffffffffffffffffffffff9384167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560078054929093169116179055565b60095481565b600a5481565b6000600c5460011461108457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c81905580611094610d90565b50600654604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905193955091935060009273ffffffffffffffffffffffffffffffffffffffff909116916370a08231916024808301926020929190829003018186803b15801561110e57600080fd5b505afa158015611122573d6000803e3d6000fd5b505050506040513d602081101561113857600080fd5b5051600754604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905192935060009273ffffffffffffffffffffffffffffffffffffffff909216916370a0823191602480820192602092909190829003018186803b1580156111b157600080fd5b505afa1580156111c5573d6000803e3d6000fd5b505050506040513d60208110156111db57600080fd5b505190506000611201836dffffffffffffffffffffffffffff871663ffffffff61226e16565b90506000611225836dffffffffffffffffffffffffffff871663ffffffff61226e16565b9050600061123387876126ec565b600054909150806112705761125c6103e8610bfd611257878763ffffffff6121e816565b612878565b985061126b60006103e86128ca565b6112cd565b6112ca6dffffffffffffffffffffffffffff8916611294868463ffffffff6121e816565b8161129b57fe5b046dffffffffffffffffffffffffffff89166112bd868563ffffffff6121e816565b816112c457fe5b0461297a565b98505b60008911611326576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180612bc16028913960400191505060405180910390fd5b6113308a8a6128ca565b61133c86868a8a6122e0565b811561137e5760085461137a906dffffffffffffffffffffffffffff808216916e01000000000000000000000000000090041663ffffffff6121e816565b600b555b6040805185815260208101859052815133927f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f928290030190a250506001600c5550949695505050505050565b60016020526000908152604090205481565b600b5481565b60046020526000908152604090205481565b600080600c5460011461146957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c81905580611479610d90565b50600654600754604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905194965092945073ffffffffffffffffffffffffffffffffffffffff9182169391169160009184916370a08231916024808301926020929190829003018186803b1580156114fb57600080fd5b505afa15801561150f573d6000803e3d6000fd5b505050506040513d602081101561152557600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905191925060009173ffffffffffffffffffffffffffffffffffffffff8516916370a08231916024808301926020929190829003018186803b15801561159957600080fd5b505afa1580156115ad573d6000803e3d6000fd5b505050506040513d60208110156115c357600080fd5b5051306000908152600160205260408120549192506115e288886126ec565b600054909150806115f9848763ffffffff6121e816565b8161160057fe5b049a5080611614848663ffffffff6121e816565b8161161b57fe5b04995060008b11801561162e575060008a115b611683576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180612b996028913960400191505060405180910390fd5b61168d3084612992565b611698878d8d611fdb565b6116a3868d8c611fdb565b604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff8916916370a08231916024808301926020929190829003018186803b15801561170f57600080fd5b505afa158015611723573d6000803e3d6000fd5b505050506040513d602081101561173957600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905191965073ffffffffffffffffffffffffffffffffffffffff8816916370a0823191602480820192602092909190829003018186803b1580156117ab57600080fd5b505afa1580156117bf573d6000803e3d6000fd5b505050506040513d60208110156117d557600080fd5b505193506117e585858b8b6122e0565b811561182757600854611823906dffffffffffffffffffffffffffff808216916e01000000000000000000000000000090041663ffffffff6121e816565b600b555b604080518c8152602081018c9052815173ffffffffffffffffffffffffffffffffffffffff8f169233927fdccd412f0b1252819cb1fd330b93224ca42612892bb3f4f789976e6d81936496929081900390910190a35050505050505050506001600c81905550915091565b6040518060400160405280600681526020017f554e492d5632000000000000000000000000000000000000000000000000000081525081565b6000610df233848461260b565b6103e881565b600c5460011461194f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c55600654600754600854604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff9485169490931692611a2b9285928792611a26926dffffffffffffffffffffffffffff169185916370a0823191602480820192602092909190829003018186803b1580156119ee57600080fd5b505afa158015611a02573d6000803e3d6000fd5b505050506040513d6020811015611a1857600080fd5b50519063ffffffff61226e16565b611fdb565b600854604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051611aca9284928792611a26926e01000000000000000000000000000090046dffffffffffffffffffffffffffff169173ffffffffffffffffffffffffffffffffffffffff8616916370a0823191602480820192602092909190829003018186803b1580156119ee57600080fd5b50506001600c5550565b60055473ffffffffffffffffffffffffffffffffffffffff1681565b60075473ffffffffffffffffffffffffffffffffffffffff1681565b42841015611b7b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f556e697377617056323a20455850495245440000000000000000000000000000604482015290519081900360640190fd5b60035473ffffffffffffffffffffffffffffffffffffffff80891660008181526004602090815260408083208054600180820190925582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98186015280840196909652958d166060860152608085018c905260a085019590955260c08085018b90528151808603909101815260e0850182528051908301207f19010000000000000000000000000000000000000000000000000000000000006101008601526101028501969096526101228085019690965280518085039096018652610142840180825286519683019690962095839052610162840180825286905260ff89166101828501526101a284018890526101c28401879052519193926101e2808201937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081019281900390910190855afa158015611cdc573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590611d5757508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b611dc257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f556e697377617056323a20494e56414c49445f5349474e415455524500000000604482015290519081900360640190fd5b611dcd89898961259c565b505050505050505050565b600260209081526000928352604080842090915290825290205481565b600c54600114611e6657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c55600654604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051611fd49273ffffffffffffffffffffffffffffffffffffffff16916370a08231916024808301926020929190829003018186803b158015611edd57600080fd5b505afa158015611ef1573d6000803e3d6000fd5b505050506040513d6020811015611f0757600080fd5b5051600754604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff909216916370a0823191602480820192602092909190829003018186803b158015611f7a57600080fd5b505afa158015611f8e573d6000803e3d6000fd5b505050506040513d6020811015611fa457600080fd5b50516008546dffffffffffffffffffffffffffff808216916e0100000000000000000000000000009004166122e0565b6001600c55565b604080518082018252601981527f7472616e7366657228616464726573732c75696e743235362900000000000000602091820152815173ffffffffffffffffffffffffffffffffffffffff85811660248301526044808301869052845180840390910181526064909201845291810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001781529251815160009460609489169392918291908083835b602083106120e157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016120a4565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612143576040519150601f19603f3d011682016040523d82523d6000602084013e612148565b606091505b5091509150818015612176575080511580612176575080806020019051602081101561217357600080fd5b50515b6121e157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f556e697377617056323a205452414e534645525f4641494c4544000000000000604482015290519081900360640190fd5b5050505050565b60008115806122035750508082028282828161220057fe5b04145b610df657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f64732d6d6174682d6d756c2d6f766572666c6f77000000000000000000000000604482015290519081900360640190fd5b80820382811115610df657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f64732d6d6174682d7375622d756e646572666c6f770000000000000000000000604482015290519081900360640190fd5b6dffffffffffffffffffffffffffff841180159061230c57506dffffffffffffffffffffffffffff8311155b61237757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f556e697377617056323a204f564552464c4f5700000000000000000000000000604482015290519081900360640190fd5b60085463ffffffff428116917c0100000000000000000000000000000000000000000000000000000000900481168203908116158015906123c757506dffffffffffffffffffffffffffff841615155b80156123e257506dffffffffffffffffffffffffffff831615155b15612492578063ffffffff16612425856123fb86612a57565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169063ffffffff612a7b16565b600980547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff929092169290920201905563ffffffff8116612465846123fb87612a57565b600a80547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff92909216929092020190555b600880547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000166dffffffffffffffffffffffffffff888116919091177fffffffff0000000000000000000000000000ffffffffffffffffffffffffffff166e0100000000000000000000000000008883168102919091177bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c010000000000000000000000000000000000000000000000000000000063ffffffff871602179283905560408051848416815291909304909116602082015281517f1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1929181900390910190a1505050505050565b73ffffffffffffffffffffffffffffffffffffffff808416600081815260026020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260016020526040902054612641908263ffffffff61226e16565b73ffffffffffffffffffffffffffffffffffffffff8085166000908152600160205260408082209390935590841681522054612683908263ffffffff612abc16565b73ffffffffffffffffffffffffffffffffffffffff80841660008181526001602090815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600080600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663017e7e586040518163ffffffff1660e01b815260040160206040518083038186803b15801561275757600080fd5b505afa15801561276b573d6000803e3d6000fd5b505050506040513d602081101561278157600080fd5b5051600b5473ffffffffffffffffffffffffffffffffffffffff821615801594509192509061286457801561285f5760006127d86112576dffffffffffffffffffffffffffff88811690881663ffffffff6121e816565b905060006127e583612878565b90508082111561285c576000612813612804848463ffffffff61226e16565b6000549063ffffffff6121e816565b905060006128388361282c86600563ffffffff6121e816565b9063ffffffff612abc16565b9050600081838161284557fe5b04905080156128585761285887826128ca565b5050505b50505b612870565b8015612870576000600b555b505092915050565b600060038211156128bb575080600160028204015b818110156128b5578091506002818285816128a457fe5b0401816128ad57fe5b04905061288d565b506128c5565b81156128c5575060015b919050565b6000546128dd908263ffffffff612abc16565b600090815573ffffffffffffffffffffffffffffffffffffffff8316815260016020526040902054612915908263ffffffff612abc16565b73ffffffffffffffffffffffffffffffffffffffff831660008181526001602090815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6000818310612989578161298b565b825b9392505050565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600160205260409020546129c8908263ffffffff61226e16565b73ffffffffffffffffffffffffffffffffffffffff831660009081526001602052604081209190915554612a02908263ffffffff61226e16565b600090815560408051838152905173ffffffffffffffffffffffffffffffffffffffff8516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef919081900360200190a35050565b6dffffffffffffffffffffffffffff166e0100000000000000000000000000000290565b60006dffffffffffffffffffffffffffff82167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff841681612ab457fe5b049392505050565b80820182811015610df657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f64732d6d6174682d6164642d6f766572666c6f77000000000000000000000000604482015290519081900360640190fdfe556e697377617056323a20494e53554646494349454e545f4f55545055545f414d4f554e54556e697377617056323a20494e53554646494349454e545f494e5055545f414d4f554e54556e697377617056323a20494e53554646494349454e545f4c4951554944495459556e697377617056323a20494e53554646494349454e545f4c49515549444954595f4255524e4544556e697377617056323a20494e53554646494349454e545f4c49515549444954595f4d494e544544a265627a7a723158207dca18479e58487606bf70c79e44d8dee62353c9ee6d01f9a9d70885b8765f2264736f6c63430005100032454950373132446f6d61696e28737472696e67206e616d652c737472696e672076657273696f6e2c75696e7432353620636861696e49642c6164647265737320766572696679696e67436f6e747261637429a265627a7a723158202760f92d7fa1db6f5aa16307bad65df4ebcc8550c4b1f03755ab8dfd830c178f64736f6c63430005100032", + "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH2 0x88 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0xA2E74AF6 GT PUSH2 0x5B JUMPI DUP1 PUSH4 0xA2E74AF6 EQ PUSH2 0xFD JUMPI DUP1 PUSH4 0xC9C65396 EQ PUSH2 0x132 JUMPI DUP1 PUSH4 0xE6A43905 EQ PUSH2 0x16D JUMPI DUP1 PUSH4 0xF46901ED EQ PUSH2 0x1A8 JUMPI PUSH2 0x88 JUMP JUMPDEST DUP1 PUSH4 0x17E7E58 EQ PUSH2 0x8D JUMPI DUP1 PUSH4 0x94B7415 EQ PUSH2 0xBE JUMPI DUP1 PUSH4 0x1E3DD18B EQ PUSH2 0xC6 JUMPI DUP1 PUSH4 0x574F2BA3 EQ PUSH2 0xE3 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x95 PUSH2 0x1DB JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x95 PUSH2 0x1F7 JUMP JUMPDEST PUSH2 0x95 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0xDC JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH2 0x213 JUMP JUMPDEST PUSH2 0xEB PUSH2 0x247 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x130 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x113 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x24D JUMP JUMPDEST STOP JUMPDEST PUSH2 0x95 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x148 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 ADD CALLDATALOAD AND PUSH2 0x31A JUMP JUMPDEST PUSH2 0x95 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x183 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 ADD CALLDATALOAD AND PUSH2 0x76D JUMP JUMPDEST PUSH2 0x130 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1BE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x7A0 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x3 DUP2 DUP2 SLOAD DUP2 LT PUSH2 0x220 JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 SWAP1 SWAP2 KECCAK256 ADD SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 POP DUP2 JUMP JUMPDEST PUSH1 0x3 SLOAD SWAP1 JUMP JUMPDEST PUSH1 0x1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x2D3 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20464F5242494444454E000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x1 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP3 SWAP1 SWAP3 AND SWAP2 SWAP1 SWAP2 OR SWAP1 SSTORE JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO PUSH2 0x3B7 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x1E PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204944454E544943414C5F4144445245535345530000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 DUP1 DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND LT PUSH2 0x3F4 JUMPI DUP4 DUP6 PUSH2 0x3F7 JUMP JUMPDEST DUP5 DUP5 JUMPDEST SWAP1 SWAP3 POP SWAP1 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 AND PUSH2 0x47E JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x17 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A205A45524F5F41444452455353000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 DUP6 DUP6 AND DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SLOAD AND ISZERO PUSH2 0x51F JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x16 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20504149525F45584953545300000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x60 PUSH1 0x40 MLOAD DUP1 PUSH1 0x20 ADD PUSH2 0x531 SWAP1 PUSH2 0x86D JUMP JUMPDEST PUSH1 0x20 DUP3 ADD DUP2 SUB DUP3 MSTORE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND PUSH1 0x40 MSTORE POP SWAP1 POP PUSH1 0x0 DUP4 DUP4 PUSH1 0x40 MLOAD PUSH1 0x20 ADD DUP1 DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH1 0x60 SHL DUP2 MSTORE PUSH1 0x14 ADD DUP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH1 0x60 SHL DUP2 MSTORE PUSH1 0x14 ADD SWAP3 POP POP POP PUSH1 0x40 MLOAD PUSH1 0x20 DUP2 DUP4 SUB SUB DUP2 MSTORE SWAP1 PUSH1 0x40 MSTORE DUP1 MLOAD SWAP1 PUSH1 0x20 ADD KECCAK256 SWAP1 POP DUP1 DUP3 MLOAD PUSH1 0x20 DUP5 ADD PUSH1 0x0 CREATE2 PUSH1 0x40 DUP1 MLOAD PUSH32 0x485CC95500000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 DUP2 AND PUSH1 0x4 DUP4 ADD MSTORE DUP7 DUP2 AND PUSH1 0x24 DUP4 ADD MSTORE SWAP2 MLOAD SWAP3 SWAP8 POP SWAP1 DUP8 AND SWAP2 PUSH4 0x485CC955 SWAP2 PUSH1 0x44 DUP1 DUP3 ADD SWAP3 PUSH1 0x0 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP4 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x65E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x672 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 DUP2 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 DUP2 DUP2 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 DUP10 DUP8 AND DUP1 DUP7 MSTORE SWAP1 DUP4 MSTORE DUP2 DUP6 KECCAK256 DUP1 SLOAD SWAP8 DUP14 AND PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 SWAP9 DUP10 AND DUP2 OR SWAP1 SWAP2 SSTORE SWAP4 DUP4 MSTORE DUP2 DUP6 KECCAK256 DUP7 DUP7 MSTORE DUP4 MSTORE DUP2 DUP6 KECCAK256 DUP1 SLOAD DUP9 AND DUP6 OR SWAP1 SSTORE PUSH1 0x3 DUP1 SLOAD PUSH1 0x1 DUP2 ADD DUP3 SSTORE SWAP6 DUP2 SWAP1 MSTORE PUSH32 0xC2575A0E9E593C00F959F8C92F12DB2869C3395A3B0502D05E2516446F71F85B SWAP1 SWAP6 ADD DUP1 SLOAD SWAP1 SWAP8 AND DUP5 OR SWAP1 SWAP7 SSTORE SWAP3 SLOAD DUP4 MLOAD SWAP3 DUP4 MSTORE SWAP1 DUP3 ADD MSTORE DUP2 MLOAD PUSH32 0xD3648BD0F6BA80134A33BA9275AC585D9D315F0AD8355CDDEFDE31AFA28D0E9 SWAP3 SWAP2 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x826 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20464F5242494444454E000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP3 SWAP1 SWAP3 AND SWAP2 SWAP1 SWAP2 OR SWAP1 SSTORE JUMP JUMPDEST PUSH2 0x2D74 DUP1 PUSH2 0x87B DUP4 CODECOPY ADD SWAP1 JUMP INVALID PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x1 PUSH1 0xC SSTORE CALLVALUE DUP1 ISZERO PUSH2 0x15 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 MLOAD CHAINID SWAP1 DUP1 PUSH1 0x52 PUSH2 0x2D22 DUP3 CODECOPY PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 SWAP1 SUB PUSH1 0x52 ADD DUP3 KECCAK256 DUP3 DUP3 ADD DUP3 MSTORE PUSH1 0xA DUP4 MSTORE PUSH10 0x2AB734B9BBB0B8102B19 PUSH1 0xB1 SHL PUSH1 0x20 SWAP4 DUP5 ADD MSTORE DUP2 MLOAD DUP1 DUP4 ADD DUP4 MSTORE PUSH1 0x1 DUP2 MSTORE PUSH1 0x31 PUSH1 0xF8 SHL SWAP1 DUP5 ADD MSTORE DUP2 MLOAD DUP1 DUP5 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH32 0xBFCC8EF98FFBF7B6C3FEC7BF5185B566B9863E35A9D83ACD49AD6824B5969738 DUP2 DUP4 ADD MSTORE PUSH32 0xC89EFDAA54C0F20C7ADF612882DF0950F5A951637E0307CDCB4C672F298B8BC6 PUSH1 0x60 DUP3 ADD MSTORE PUSH1 0x80 DUP2 ADD SWAP5 SWAP1 SWAP5 MSTORE ADDRESS PUSH1 0xA0 DUP1 DUP7 ADD SWAP2 SWAP1 SWAP2 MSTORE DUP2 MLOAD DUP1 DUP7 SUB SWAP1 SWAP2 ADD DUP2 MSTORE PUSH1 0xC0 SWAP1 SWAP5 ADD SWAP1 MSTORE DUP3 MLOAD SWAP3 ADD SWAP2 SWAP1 SWAP2 KECCAK256 PUSH1 0x3 SSTORE POP PUSH1 0x5 DUP1 SLOAD PUSH1 0x1 PUSH1 0x1 PUSH1 0xA0 SHL SUB NOT AND CALLER OR SWAP1 SSTORE PUSH2 0x2C1D DUP1 PUSH2 0x105 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH2 0x1B9 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x6A627842 GT PUSH2 0xF9 JUMPI DUP1 PUSH4 0xBA9A7A56 GT PUSH2 0x97 JUMPI DUP1 PUSH4 0xD21220A7 GT PUSH2 0x71 JUMPI DUP1 PUSH4 0xD21220A7 EQ PUSH2 0x5DA JUMPI DUP1 PUSH4 0xD505ACCF EQ PUSH2 0x5E2 JUMPI DUP1 PUSH4 0xDD62ED3E EQ PUSH2 0x640 JUMPI DUP1 PUSH4 0xFFF6CAE9 EQ PUSH2 0x67B JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0xBA9A7A56 EQ PUSH2 0x597 JUMPI DUP1 PUSH4 0xBC25CF77 EQ PUSH2 0x59F JUMPI DUP1 PUSH4 0xC45A0155 EQ PUSH2 0x5D2 JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x7ECEBE00 GT PUSH2 0xD3 JUMPI DUP1 PUSH4 0x7ECEBE00 EQ PUSH2 0x4D7 JUMPI DUP1 PUSH4 0x89AFCB44 EQ PUSH2 0x50A JUMPI DUP1 PUSH4 0x95D89B41 EQ PUSH2 0x556 JUMPI DUP1 PUSH4 0xA9059CBB EQ PUSH2 0x55E JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x6A627842 EQ PUSH2 0x469 JUMPI DUP1 PUSH4 0x70A08231 EQ PUSH2 0x49C JUMPI DUP1 PUSH4 0x7464FC3D EQ PUSH2 0x4CF JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x23B872DD GT PUSH2 0x166 JUMPI DUP1 PUSH4 0x3644E515 GT PUSH2 0x140 JUMPI DUP1 PUSH4 0x3644E515 EQ PUSH2 0x416 JUMPI DUP1 PUSH4 0x485CC955 EQ PUSH2 0x41E JUMPI DUP1 PUSH4 0x5909C0D5 EQ PUSH2 0x459 JUMPI DUP1 PUSH4 0x5A3D5493 EQ PUSH2 0x461 JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x23B872DD EQ PUSH2 0x3AD JUMPI DUP1 PUSH4 0x30ADF81F EQ PUSH2 0x3F0 JUMPI DUP1 PUSH4 0x313CE567 EQ PUSH2 0x3F8 JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x95EA7B3 GT PUSH2 0x197 JUMPI DUP1 PUSH4 0x95EA7B3 EQ PUSH2 0x315 JUMPI DUP1 PUSH4 0xDFE1681 EQ PUSH2 0x362 JUMPI DUP1 PUSH4 0x18160DDD EQ PUSH2 0x393 JUMPI PUSH2 0x1B9 JUMP JUMPDEST DUP1 PUSH4 0x22C0D9F EQ PUSH2 0x1BE JUMPI DUP1 PUSH4 0x6FDDE03 EQ PUSH2 0x259 JUMPI DUP1 PUSH4 0x902F1AC EQ PUSH2 0x2D6 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x257 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x80 DUP2 LT ISZERO PUSH2 0x1D4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD SWAP2 PUSH1 0x20 DUP2 ADD CALLDATALOAD SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x40 DUP4 ADD CALLDATALOAD AND SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH1 0x80 DUP2 ADD PUSH1 0x60 DUP3 ADD CALLDATALOAD PUSH5 0x100000000 DUP2 GT ISZERO PUSH2 0x218 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP3 ADD DUP4 PUSH1 0x20 DUP3 ADD GT ISZERO PUSH2 0x22A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP2 DUP5 PUSH1 0x1 DUP4 MUL DUP5 ADD GT PUSH5 0x100000000 DUP4 GT OR ISZERO PUSH2 0x24C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP SWAP1 SWAP3 POP SWAP1 POP PUSH2 0x683 JUMP JUMPDEST STOP JUMPDEST PUSH2 0x261 PUSH2 0xD57 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 ADD SWAP1 DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x29B JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x283 JUMP JUMPDEST POP POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x2C8 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH2 0x2DE PUSH2 0xD90 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP5 DUP6 AND DUP2 MSTORE SWAP3 SWAP1 SWAP4 AND PUSH1 0x20 DUP4 ADD MSTORE PUSH4 0xFFFFFFFF AND DUP2 DUP4 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x60 ADD SWAP1 RETURN JUMPDEST PUSH2 0x34E PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x32B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD AND SWAP1 PUSH1 0x20 ADD CALLDATALOAD PUSH2 0xDE5 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x36A PUSH2 0xDFC JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x39B PUSH2 0xE18 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x34E PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x60 DUP2 LT ISZERO PUSH2 0x3C3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 DUP2 ADD CALLDATALOAD SWAP1 SWAP2 AND SWAP1 PUSH1 0x40 ADD CALLDATALOAD PUSH2 0xE1E JUMP JUMPDEST PUSH2 0x39B PUSH2 0xEFD JUMP JUMPDEST PUSH2 0x400 PUSH2 0xF21 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0xFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH2 0x39B PUSH2 0xF26 JUMP JUMPDEST PUSH2 0x257 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x434 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 ADD CALLDATALOAD AND PUSH2 0xF2C JUMP JUMPDEST PUSH2 0x39B PUSH2 0x1005 JUMP JUMPDEST PUSH2 0x39B PUSH2 0x100B JUMP JUMPDEST PUSH2 0x39B PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x47F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x1011 JUMP JUMPDEST PUSH2 0x39B PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x4B2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x13CB JUMP JUMPDEST PUSH2 0x39B PUSH2 0x13DD JUMP JUMPDEST PUSH2 0x39B PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x4ED JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x13E3 JUMP JUMPDEST PUSH2 0x53D PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x520 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x13F5 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP3 DUP4 MSTORE PUSH1 0x20 DUP4 ADD SWAP2 SWAP1 SWAP2 MSTORE DUP1 MLOAD SWAP2 DUP3 SWAP1 SUB ADD SWAP1 RETURN JUMPDEST PUSH2 0x261 PUSH2 0x1892 JUMP JUMPDEST PUSH2 0x34E PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x574 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD AND SWAP1 PUSH1 0x20 ADD CALLDATALOAD PUSH2 0x18CB JUMP JUMPDEST PUSH2 0x39B PUSH2 0x18D8 JUMP JUMPDEST PUSH2 0x257 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x5B5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x18DE JUMP JUMPDEST PUSH2 0x36A PUSH2 0x1AD4 JUMP JUMPDEST PUSH2 0x36A PUSH2 0x1AF0 JUMP JUMPDEST PUSH2 0x257 PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0xE0 DUP2 LT ISZERO PUSH2 0x5F8 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 DUP2 ADD CALLDATALOAD SWAP1 SWAP2 AND SWAP1 PUSH1 0x40 DUP2 ADD CALLDATALOAD SWAP1 PUSH1 0x60 DUP2 ADD CALLDATALOAD SWAP1 PUSH1 0xFF PUSH1 0x80 DUP3 ADD CALLDATALOAD AND SWAP1 PUSH1 0xA0 DUP2 ADD CALLDATALOAD SWAP1 PUSH1 0xC0 ADD CALLDATALOAD PUSH2 0x1B0C JUMP JUMPDEST PUSH2 0x39B PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT ISZERO PUSH2 0x656 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 CALLDATALOAD DUP2 AND SWAP2 PUSH1 0x20 ADD CALLDATALOAD AND PUSH2 0x1DD8 JUMP JUMPDEST PUSH2 0x257 PUSH2 0x1DF5 JUMP JUMPDEST PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x6F4 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC SSTORE DUP5 ISZERO ISZERO DUP1 PUSH2 0x707 JUMPI POP PUSH1 0x0 DUP5 GT JUMPDEST PUSH2 0x75C JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x25 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2B2F PUSH1 0x25 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH1 0x0 DUP1 PUSH2 0x767 PUSH2 0xD90 JUMP JUMPDEST POP SWAP2 POP SWAP2 POP DUP2 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP8 LT DUP1 ISZERO PUSH2 0x79A JUMPI POP DUP1 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP7 LT JUMPDEST PUSH2 0x7EF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x21 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2B78 PUSH1 0x21 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH1 0x6 SLOAD PUSH1 0x7 SLOAD PUSH1 0x0 SWAP2 DUP3 SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP2 DUP3 AND SWAP2 SWAP1 DUP2 AND SWAP1 DUP10 AND DUP3 EQ DUP1 ISZERO SWAP1 PUSH2 0x854 JUMPI POP DUP1 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP10 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO JUMPDEST PUSH2 0x8BF JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x15 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20494E56414C49445F544F0000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST DUP11 ISZERO PUSH2 0x8D0 JUMPI PUSH2 0x8D0 DUP3 DUP11 DUP14 PUSH2 0x1FDB JUMP JUMPDEST DUP10 ISZERO PUSH2 0x8E1 JUMPI PUSH2 0x8E1 DUP2 DUP11 DUP13 PUSH2 0x1FDB JUMP JUMPDEST DUP7 ISZERO PUSH2 0x9C3 JUMPI DUP9 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0x10D1E85C CALLER DUP14 DUP14 DUP13 DUP13 PUSH1 0x40 MLOAD DUP7 PUSH4 0xFFFFFFFF AND PUSH1 0xE0 SHL DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP7 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 MSTORE PUSH1 0x20 ADD DUP6 DUP2 MSTORE PUSH1 0x20 ADD DUP5 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE DUP5 DUP5 DUP3 DUP2 DUP2 MSTORE PUSH1 0x20 ADD SWAP3 POP DUP1 DUP3 DUP5 CALLDATACOPY PUSH1 0x0 DUP2 DUP5 ADD MSTORE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND SWAP1 POP DUP1 DUP4 ADD SWAP3 POP POP POP SWAP7 POP POP POP POP POP POP POP PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x9AA JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x9BE JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0xA2F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0xA43 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0xA59 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP2 SWAP6 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0xACB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0xADF JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0xAF5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD SWAP3 POP PUSH1 0x0 SWAP2 POP POP PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND DUP11 SWAP1 SUB DUP4 GT PUSH2 0xB1F JUMPI PUSH1 0x0 PUSH2 0xB35 JUMP JUMPDEST DUP10 DUP6 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SUB DUP4 SUB JUMPDEST SWAP1 POP PUSH1 0x0 DUP10 DUP6 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SUB DUP4 GT PUSH2 0xB59 JUMPI PUSH1 0x0 PUSH2 0xB6F JUMP JUMPDEST DUP10 DUP6 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SUB DUP4 SUB JUMPDEST SWAP1 POP PUSH1 0x0 DUP3 GT DUP1 PUSH2 0xB80 JUMPI POP PUSH1 0x0 DUP2 GT JUMPDEST PUSH2 0xBD5 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x24 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2B54 PUSH1 0x24 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xC09 PUSH2 0xBEB DUP5 PUSH1 0x3 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH2 0xBFD DUP8 PUSH2 0x3E8 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0xC21 PUSH2 0xBEB DUP5 PUSH1 0x3 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 POP PUSH2 0xC59 PUSH3 0xF4240 PUSH2 0xC4D PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP12 DUP2 AND SWAP1 DUP12 AND PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH2 0xC69 DUP4 DUP4 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST LT ISZERO PUSH2 0xCD6 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0xC PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204B0000000000000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST POP POP PUSH2 0xCE4 DUP5 DUP5 DUP9 DUP9 PUSH2 0x22E0 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP4 DUP2 MSTORE PUSH1 0x20 DUP2 ADD DUP4 SWAP1 MSTORE DUP1 DUP3 ADD DUP14 SWAP1 MSTORE PUSH1 0x60 DUP2 ADD DUP13 SWAP1 MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP12 AND SWAP2 CALLER SWAP2 PUSH32 0xD78AD95FA46C994B6551D0DA85FC275FE613CE37657FB8D5E3D130840159D822 SWAP2 DUP2 SWAP1 SUB PUSH1 0x80 ADD SWAP1 LOG3 POP POP PUSH1 0x1 PUSH1 0xC SSTORE POP POP POP POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 PUSH1 0x40 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0xA DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x556E697377617020563200000000000000000000000000000000000000000000 DUP2 MSTORE POP DUP2 JUMP JUMPDEST PUSH1 0x8 SLOAD PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP3 AND SWAP3 PUSH15 0x10000000000000000000000000000 DUP4 DIV SWAP1 SWAP2 AND SWAP2 PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV PUSH4 0xFFFFFFFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xDF2 CALLER DUP5 DUP5 PUSH2 0x259C JUMP JUMPDEST POP PUSH1 0x1 JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x6 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x0 SLOAD DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE SWAP1 SWAP2 MSTORE DUP2 KECCAK256 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF EQ PUSH2 0xEE8 JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SLOAD PUSH2 0xEB6 SWAP1 DUP4 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SSTORE JUMPDEST PUSH2 0xEF3 DUP5 DUP5 DUP5 PUSH2 0x260B JUMP JUMPDEST POP PUSH1 0x1 SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH32 0x6E71EDAE12B1B97F4D1F60370FEF10105FA2FAAE0126114A169C64845D6126C9 DUP2 JUMP JUMPDEST PUSH1 0x12 DUP2 JUMP JUMPDEST PUSH1 0x3 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x5 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0xFB2 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20464F5242494444454E000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x6 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP4 DUP5 AND PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 SWAP2 DUP3 AND OR SWAP1 SWAP2 SSTORE PUSH1 0x7 DUP1 SLOAD SWAP3 SWAP1 SWAP4 AND SWAP2 AND OR SWAP1 SSTORE JUMP JUMPDEST PUSH1 0x9 SLOAD DUP2 JUMP JUMPDEST PUSH1 0xA SLOAD DUP2 JUMP JUMPDEST PUSH1 0x0 PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x1084 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC DUP2 SWAP1 SSTORE DUP1 PUSH2 0x1094 PUSH2 0xD90 JUMP JUMPDEST POP PUSH1 0x6 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP4 SWAP6 POP SWAP2 SWAP4 POP PUSH1 0x0 SWAP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP2 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x110E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1122 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1138 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x7 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP3 SWAP4 POP PUSH1 0x0 SWAP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP3 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x11B1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x11C5 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x11DB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD SWAP1 POP PUSH1 0x0 PUSH2 0x1201 DUP4 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x1225 DUP4 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x1233 DUP8 DUP8 PUSH2 0x26EC JUMP JUMPDEST PUSH1 0x0 SLOAD SWAP1 SWAP2 POP DUP1 PUSH2 0x1270 JUMPI PUSH2 0x125C PUSH2 0x3E8 PUSH2 0xBFD PUSH2 0x1257 DUP8 DUP8 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH2 0x2878 JUMP JUMPDEST SWAP9 POP PUSH2 0x126B PUSH1 0x0 PUSH2 0x3E8 PUSH2 0x28CA JUMP JUMPDEST PUSH2 0x12CD JUMP JUMPDEST PUSH2 0x12CA PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP10 AND PUSH2 0x1294 DUP7 DUP5 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST DUP2 PUSH2 0x129B JUMPI INVALID JUMPDEST DIV PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP10 AND PUSH2 0x12BD DUP7 DUP6 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST DUP2 PUSH2 0x12C4 JUMPI INVALID JUMPDEST DIV PUSH2 0x297A JUMP JUMPDEST SWAP9 POP JUMPDEST PUSH1 0x0 DUP10 GT PUSH2 0x1326 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x28 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2BC1 PUSH1 0x28 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH2 0x1330 DUP11 DUP11 PUSH2 0x28CA JUMP JUMPDEST PUSH2 0x133C DUP7 DUP7 DUP11 DUP11 PUSH2 0x22E0 JUMP JUMPDEST DUP2 ISZERO PUSH2 0x137E JUMPI PUSH1 0x8 SLOAD PUSH2 0x137A SWAP1 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP3 AND SWAP2 PUSH15 0x10000000000000000000000000000 SWAP1 DIV AND PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH1 0xB SSTORE JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP6 DUP2 MSTORE PUSH1 0x20 DUP2 ADD DUP6 SWAP1 MSTORE DUP2 MLOAD CALLER SWAP3 PUSH32 0x4C209B5FC8AD50758F13E2E1088BA56A560DFF690A1C6FEF26394F4C03821C4F SWAP3 DUP3 SWAP1 SUB ADD SWAP1 LOG2 POP POP PUSH1 0x1 PUSH1 0xC SSTORE POP SWAP5 SWAP7 SWAP6 POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH1 0xB SLOAD DUP2 JUMP JUMPDEST PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x1469 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC DUP2 SWAP1 SSTORE DUP1 PUSH2 0x1479 PUSH2 0xD90 JUMP JUMPDEST POP PUSH1 0x6 SLOAD PUSH1 0x7 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP5 SWAP7 POP SWAP3 SWAP5 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP2 DUP3 AND SWAP4 SWAP2 AND SWAP2 PUSH1 0x0 SWAP2 DUP5 SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x14FB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x150F JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1525 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP2 SWAP3 POP PUSH1 0x0 SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x1599 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x15AD JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x15C3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD ADDRESS PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 SLOAD SWAP2 SWAP3 POP PUSH2 0x15E2 DUP9 DUP9 PUSH2 0x26EC JUMP JUMPDEST PUSH1 0x0 SLOAD SWAP1 SWAP2 POP DUP1 PUSH2 0x15F9 DUP5 DUP8 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST DUP2 PUSH2 0x1600 JUMPI INVALID JUMPDEST DIV SWAP11 POP DUP1 PUSH2 0x1614 DUP5 DUP7 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST DUP2 PUSH2 0x161B JUMPI INVALID JUMPDEST DIV SWAP10 POP PUSH1 0x0 DUP12 GT DUP1 ISZERO PUSH2 0x162E JUMPI POP PUSH1 0x0 DUP11 GT JUMPDEST PUSH2 0x1683 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE PUSH1 0x28 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH2 0x2B99 PUSH1 0x28 SWAP2 CODECOPY PUSH1 0x40 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH2 0x168D ADDRESS DUP5 PUSH2 0x2992 JUMP JUMPDEST PUSH2 0x1698 DUP8 DUP14 DUP14 PUSH2 0x1FDB JUMP JUMPDEST PUSH2 0x16A3 DUP7 DUP14 DUP13 PUSH2 0x1FDB JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP10 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x170F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1723 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1739 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD SWAP2 SWAP7 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x17AB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x17BF JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x17D5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD SWAP4 POP PUSH2 0x17E5 DUP6 DUP6 DUP12 DUP12 PUSH2 0x22E0 JUMP JUMPDEST DUP2 ISZERO PUSH2 0x1827 JUMPI PUSH1 0x8 SLOAD PUSH2 0x1823 SWAP1 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP3 AND SWAP2 PUSH15 0x10000000000000000000000000000 SWAP1 DIV AND PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST PUSH1 0xB SSTORE JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP13 DUP2 MSTORE PUSH1 0x20 DUP2 ADD DUP13 SWAP1 MSTORE DUP2 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP16 AND SWAP3 CALLER SWAP3 PUSH32 0xDCCD412F0B1252819CB1FD330B93224CA42612892BB3F4F789976E6D81936496 SWAP3 SWAP1 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP POP POP POP POP POP POP POP POP PUSH1 0x1 PUSH1 0xC DUP2 SWAP1 SSTORE POP SWAP2 POP SWAP2 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 PUSH1 0x40 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x6 DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x554E492D56320000000000000000000000000000000000000000000000000000 DUP2 MSTORE POP DUP2 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xDF2 CALLER DUP5 DUP5 PUSH2 0x260B JUMP JUMPDEST PUSH2 0x3E8 DUP2 JUMP JUMPDEST PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x194F JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC SSTORE PUSH1 0x6 SLOAD PUSH1 0x7 SLOAD PUSH1 0x8 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP5 DUP6 AND SWAP5 SWAP1 SWAP4 AND SWAP3 PUSH2 0x1A2B SWAP3 DUP6 SWAP3 DUP8 SWAP3 PUSH2 0x1A26 SWAP3 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP2 DUP6 SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x19EE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1A02 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1A18 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH2 0x1FDB JUMP JUMPDEST PUSH1 0x8 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH2 0x1ACA SWAP3 DUP5 SWAP3 DUP8 SWAP3 PUSH2 0x1A26 SWAP3 PUSH15 0x10000000000000000000000000000 SWAP1 DIV PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x19EE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP POP PUSH1 0x1 PUSH1 0xC SSTORE POP JUMP JUMPDEST PUSH1 0x5 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x7 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST TIMESTAMP DUP5 LT ISZERO PUSH2 0x1B7B JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x12 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20455850495245440000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x3 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP10 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 DUP1 SLOAD PUSH1 0x1 DUP1 DUP3 ADD SWAP1 SWAP3 SSTORE DUP3 MLOAD PUSH32 0x6E71EDAE12B1B97F4D1F60370FEF10105FA2FAAE0126114A169C64845D6126C9 DUP2 DUP7 ADD MSTORE DUP1 DUP5 ADD SWAP7 SWAP1 SWAP7 MSTORE SWAP6 DUP14 AND PUSH1 0x60 DUP7 ADD MSTORE PUSH1 0x80 DUP6 ADD DUP13 SWAP1 MSTORE PUSH1 0xA0 DUP6 ADD SWAP6 SWAP1 SWAP6 MSTORE PUSH1 0xC0 DUP1 DUP6 ADD DUP12 SWAP1 MSTORE DUP2 MLOAD DUP1 DUP7 SUB SWAP1 SWAP2 ADD DUP2 MSTORE PUSH1 0xE0 DUP6 ADD DUP3 MSTORE DUP1 MLOAD SWAP1 DUP4 ADD KECCAK256 PUSH32 0x1901000000000000000000000000000000000000000000000000000000000000 PUSH2 0x100 DUP7 ADD MSTORE PUSH2 0x102 DUP6 ADD SWAP7 SWAP1 SWAP7 MSTORE PUSH2 0x122 DUP1 DUP6 ADD SWAP7 SWAP1 SWAP7 MSTORE DUP1 MLOAD DUP1 DUP6 SUB SWAP1 SWAP7 ADD DUP7 MSTORE PUSH2 0x142 DUP5 ADD DUP1 DUP3 MSTORE DUP7 MLOAD SWAP7 DUP4 ADD SWAP7 SWAP1 SWAP7 KECCAK256 SWAP6 DUP4 SWAP1 MSTORE PUSH2 0x162 DUP5 ADD DUP1 DUP3 MSTORE DUP7 SWAP1 MSTORE PUSH1 0xFF DUP10 AND PUSH2 0x182 DUP6 ADD MSTORE PUSH2 0x1A2 DUP5 ADD DUP9 SWAP1 MSTORE PUSH2 0x1C2 DUP5 ADD DUP8 SWAP1 MSTORE MLOAD SWAP2 SWAP4 SWAP3 PUSH2 0x1E2 DUP1 DUP3 ADD SWAP4 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP2 ADD SWAP3 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 DUP6 GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1CDC JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP PUSH1 0x40 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 ADD MLOAD SWAP2 POP POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 AND ISZERO DUP1 ISZERO SWAP1 PUSH2 0x1D57 JUMPI POP DUP9 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ JUMPDEST PUSH2 0x1DC2 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x1C PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A20494E56414C49445F5349474E415455524500000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH2 0x1DCD DUP10 DUP10 DUP10 PUSH2 0x259C JUMP JUMPDEST POP POP POP POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH1 0xC SLOAD PUSH1 0x1 EQ PUSH2 0x1E66 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x11 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204C4F434B4544000000000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x0 PUSH1 0xC SSTORE PUSH1 0x6 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH2 0x1FD4 SWAP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP4 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x1EDD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1EF1 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1F07 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x7 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE ADDRESS PUSH1 0x4 DUP3 ADD MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP3 AND SWAP2 PUSH4 0x70A08231 SWAP2 PUSH1 0x24 DUP1 DUP3 ADD SWAP3 PUSH1 0x20 SWAP3 SWAP1 SWAP2 SWAP1 DUP3 SWAP1 SUB ADD DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x1F7A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x1F8E JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x1FA4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0x8 SLOAD PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP3 AND SWAP2 PUSH15 0x10000000000000000000000000000 SWAP1 DIV AND PUSH2 0x22E0 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xC SSTORE JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD DUP3 MSTORE PUSH1 0x19 DUP2 MSTORE PUSH32 0x7472616E7366657228616464726573732C75696E743235362900000000000000 PUSH1 0x20 SWAP2 DUP3 ADD MSTORE DUP2 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 DUP2 AND PUSH1 0x24 DUP4 ADD MSTORE PUSH1 0x44 DUP1 DUP4 ADD DUP7 SWAP1 MSTORE DUP5 MLOAD DUP1 DUP5 SUB SWAP1 SWAP2 ADD DUP2 MSTORE PUSH1 0x64 SWAP1 SWAP3 ADD DUP5 MSTORE SWAP2 DUP2 ADD DUP1 MLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xA9059CBB00000000000000000000000000000000000000000000000000000000 OR DUP2 MSTORE SWAP3 MLOAD DUP2 MLOAD PUSH1 0x0 SWAP5 PUSH1 0x60 SWAP5 DUP10 AND SWAP4 SWAP3 SWAP2 DUP3 SWAP2 SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x20E1 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x20A4 JUMP JUMPDEST PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB DUP1 NOT DUP3 MLOAD AND DUP2 DUP5 MLOAD AND DUP1 DUP3 OR DUP6 MSTORE POP POP POP POP POP POP SWAP1 POP ADD SWAP2 POP POP PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP7 GAS CALL SWAP2 POP POP RETURNDATASIZE DUP1 PUSH1 0x0 DUP2 EQ PUSH2 0x2143 JUMPI PUSH1 0x40 MLOAD SWAP2 POP PUSH1 0x1F NOT PUSH1 0x3F RETURNDATASIZE ADD AND DUP3 ADD PUSH1 0x40 MSTORE RETURNDATASIZE DUP3 MSTORE RETURNDATASIZE PUSH1 0x0 PUSH1 0x20 DUP5 ADD RETURNDATACOPY PUSH2 0x2148 JUMP JUMPDEST PUSH1 0x60 SWAP2 POP JUMPDEST POP SWAP2 POP SWAP2 POP DUP2 DUP1 ISZERO PUSH2 0x2176 JUMPI POP DUP1 MLOAD ISZERO DUP1 PUSH2 0x2176 JUMPI POP DUP1 DUP1 PUSH1 0x20 ADD SWAP1 MLOAD PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x2173 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD JUMPDEST PUSH2 0x21E1 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x1A PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A205452414E534645525F4641494C4544000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 ISZERO DUP1 PUSH2 0x2203 JUMPI POP POP DUP1 DUP3 MUL DUP3 DUP3 DUP3 DUP2 PUSH2 0x2200 JUMPI INVALID JUMPDEST DIV EQ JUMPDEST PUSH2 0xDF6 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x64732D6D6174682D6D756C2D6F766572666C6F77000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST DUP1 DUP3 SUB DUP3 DUP2 GT ISZERO PUSH2 0xDF6 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x15 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x64732D6D6174682D7375622D756E646572666C6F770000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 GT DUP1 ISZERO SWAP1 PUSH2 0x230C JUMPI POP PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 GT ISZERO JUMPDEST PUSH2 0x2377 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x13 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x556E697377617056323A204F564552464C4F5700000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT JUMPDEST PUSH1 0x8 SLOAD PUSH4 0xFFFFFFFF TIMESTAMP DUP2 AND SWAP2 PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV DUP2 AND DUP3 SUB SWAP1 DUP2 AND ISZERO DUP1 ISZERO SWAP1 PUSH2 0x23C7 JUMPI POP PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND ISZERO ISZERO JUMPDEST DUP1 ISZERO PUSH2 0x23E2 JUMPI POP PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND ISZERO ISZERO JUMPDEST ISZERO PUSH2 0x2492 JUMPI DUP1 PUSH4 0xFFFFFFFF AND PUSH2 0x2425 DUP6 PUSH2 0x23FB DUP7 PUSH2 0x2A57 JUMP JUMPDEST PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x2A7B AND JUMP JUMPDEST PUSH1 0x9 DUP1 SLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP3 SWAP1 SWAP3 AND SWAP3 SWAP1 SWAP3 MUL ADD SWAP1 SSTORE PUSH4 0xFFFFFFFF DUP2 AND PUSH2 0x2465 DUP5 PUSH2 0x23FB DUP8 PUSH2 0x2A57 JUMP JUMPDEST PUSH1 0xA DUP1 SLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP3 SWAP1 SWAP3 AND SWAP3 SWAP1 SWAP3 MUL ADD SWAP1 SSTORE JUMPDEST PUSH1 0x8 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000 AND PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 DUP2 AND SWAP2 SWAP1 SWAP2 OR PUSH32 0xFFFFFFFF0000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH15 0x10000000000000000000000000000 DUP9 DUP4 AND DUP2 MUL SWAP2 SWAP1 SWAP2 OR PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH4 0xFFFFFFFF DUP8 AND MUL OR SWAP3 DUP4 SWAP1 SSTORE PUSH1 0x40 DUP1 MLOAD DUP5 DUP5 AND DUP2 MSTORE SWAP2 SWAP1 SWAP4 DIV SWAP1 SWAP2 AND PUSH1 0x20 DUP3 ADD MSTORE DUP2 MLOAD PUSH32 0x1C411E9A96E071241C2F21F7726B17AE89E3CAB4C78BE50E062B03A9FFFBBAD1 SWAP3 SWAP2 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG1 POP POP POP POP POP POP JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP5 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 DUP8 AND DUP1 DUP5 MSTORE SWAP5 DUP3 MSTORE SWAP2 DUP3 SWAP1 KECCAK256 DUP6 SWAP1 SSTORE DUP2 MLOAD DUP6 DUP2 MSTORE SWAP2 MLOAD PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP3 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP POP POP JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH2 0x2641 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 DUP1 DUP3 KECCAK256 SWAP4 SWAP1 SWAP4 SSTORE SWAP1 DUP5 AND DUP2 MSTORE KECCAK256 SLOAD PUSH2 0x2683 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x2ABC AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP5 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP2 DUP3 SWAP1 KECCAK256 SWAP5 SWAP1 SWAP5 SSTORE DUP1 MLOAD DUP6 DUP2 MSTORE SWAP1 MLOAD SWAP2 SWAP4 SWAP3 DUP8 AND SWAP3 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP3 SWAP2 DUP3 SWAP1 SUB ADD SWAP1 LOG3 POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x5 PUSH1 0x0 SWAP1 SLOAD SWAP1 PUSH2 0x100 EXP SWAP1 DIV PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0x17E7E58 PUSH1 0x40 MLOAD DUP2 PUSH4 0xFFFFFFFF AND PUSH1 0xE0 SHL DUP2 MSTORE PUSH1 0x4 ADD PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x2757 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH2 0x276B JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x20 DUP2 LT ISZERO PUSH2 0x2781 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP MLOAD PUSH1 0xB SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 AND ISZERO DUP1 ISZERO SWAP5 POP SWAP2 SWAP3 POP SWAP1 PUSH2 0x2864 JUMPI DUP1 ISZERO PUSH2 0x285F JUMPI PUSH1 0x0 PUSH2 0x27D8 PUSH2 0x1257 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 DUP2 AND SWAP1 DUP9 AND PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x27E5 DUP4 PUSH2 0x2878 JUMP JUMPDEST SWAP1 POP DUP1 DUP3 GT ISZERO PUSH2 0x285C JUMPI PUSH1 0x0 PUSH2 0x2813 PUSH2 0x2804 DUP5 DUP5 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH1 0x0 SLOAD SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x2838 DUP4 PUSH2 0x282C DUP7 PUSH1 0x5 PUSH4 0xFFFFFFFF PUSH2 0x21E8 AND JUMP JUMPDEST SWAP1 PUSH4 0xFFFFFFFF PUSH2 0x2ABC AND JUMP JUMPDEST SWAP1 POP PUSH1 0x0 DUP2 DUP4 DUP2 PUSH2 0x2845 JUMPI INVALID JUMPDEST DIV SWAP1 POP DUP1 ISZERO PUSH2 0x2858 JUMPI PUSH2 0x2858 DUP8 DUP3 PUSH2 0x28CA JUMP JUMPDEST POP POP POP JUMPDEST POP POP JUMPDEST PUSH2 0x2870 JUMP JUMPDEST DUP1 ISZERO PUSH2 0x2870 JUMPI PUSH1 0x0 PUSH1 0xB SSTORE JUMPDEST POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x3 DUP3 GT ISZERO PUSH2 0x28BB JUMPI POP DUP1 PUSH1 0x1 PUSH1 0x2 DUP3 DIV ADD JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x28B5 JUMPI DUP1 SWAP2 POP PUSH1 0x2 DUP2 DUP3 DUP6 DUP2 PUSH2 0x28A4 JUMPI INVALID JUMPDEST DIV ADD DUP2 PUSH2 0x28AD JUMPI INVALID JUMPDEST DIV SWAP1 POP PUSH2 0x288D JUMP JUMPDEST POP PUSH2 0x28C5 JUMP JUMPDEST DUP2 ISZERO PUSH2 0x28C5 JUMPI POP PUSH1 0x1 JUMPDEST SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH2 0x28DD SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x2ABC AND JUMP JUMPDEST PUSH1 0x0 SWAP1 DUP2 SSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH2 0x2915 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x2ABC AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 SWAP1 SWAP5 SSTORE DUP4 MLOAD DUP6 DUP2 MSTORE SWAP4 MLOAD SWAP3 SWAP4 SWAP2 SWAP3 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP3 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 DUP4 LT PUSH2 0x2989 JUMPI DUP2 PUSH2 0x298B JUMP JUMPDEST DUP3 JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH2 0x29C8 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 SWAP2 SWAP1 SWAP2 SSTORE SLOAD PUSH2 0x2A02 SWAP1 DUP3 PUSH4 0xFFFFFFFF PUSH2 0x226E AND JUMP JUMPDEST PUSH1 0x0 SWAP1 DUP2 SSTORE PUSH1 0x40 DUP1 MLOAD DUP4 DUP2 MSTORE SWAP1 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND SWAP2 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP2 SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 LOG3 POP POP JUMP JUMPDEST PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH15 0x10000000000000000000000000000 MUL SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH14 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP3 AND PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND DUP2 PUSH2 0x2AB4 JUMPI INVALID JUMPDEST DIV SWAP4 SWAP3 POP POP POP JUMP JUMPDEST DUP1 DUP3 ADD DUP3 DUP2 LT ISZERO PUSH2 0xDF6 JUMPI PUSH1 0x40 DUP1 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x20 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x14 PUSH1 0x24 DUP3 ADD MSTORE PUSH32 0x64732D6D6174682D6164642D6F766572666C6F77000000000000000000000000 PUSH1 0x44 DUP3 ADD MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x64 ADD SWAP1 REVERT INVALID SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x4F SSTORE SLOAD POP SSTORE SLOAD 0x5F COINBASE 0x4D 0x4F SSTORE 0x4E SLOAD SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x49 0x4E POP SSTORE SLOAD 0x5F COINBASE 0x4D 0x4F SSTORE 0x4E SLOAD SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x4C 0x49 MLOAD SSTORE 0x49 DIFFICULTY 0x49 SLOAD MSIZE SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x4C 0x49 MLOAD SSTORE 0x49 DIFFICULTY 0x49 SLOAD MSIZE 0x5F TIMESTAMP SSTORE MSTORE 0x4E GASLIMIT DIFFICULTY SSTORE PUSH15 0x697377617056323A20494E53554646 0x49 NUMBER 0x49 GASLIMIT 0x4E SLOAD 0x5F 0x4C 0x49 MLOAD SSTORE 0x49 DIFFICULTY 0x49 SLOAD MSIZE 0x5F 0x4D 0x49 0x4E SLOAD GASLIMIT DIFFICULTY LOG2 PUSH6 0x627A7A723158 KECCAK256 PUSH30 0xCA18479E58487606BF70C79E44D8DEE62353C9EE6D01F9A9D70885B8765F 0x22 PUSH5 0x736F6C6343 STOP SDIV LT STOP ORIGIN GASLIMIT 0x49 POP CALLDATACOPY BALANCE ORIGIN DIFFICULTY PUSH16 0x6D61696E28737472696E67206E616D65 0x2C PUSH20 0x7472696E672076657273696F6E2C75696E743235 CALLDATASIZE KECCAK256 PUSH4 0x6861696E 0x49 PUSH5 0x2C61646472 PUSH6 0x737320766572 PUSH10 0x6679696E67436F6E7472 PUSH2 0x6374 0x29 LOG2 PUSH6 0x627A7A723158 KECCAK256 0x27 PUSH1 0xF9 0x2D PUSH32 0xA1DB6F5AA16307BAD65DF4EBCC8550C4B1F03755AB8DFD830C178F64736F6C63 NUMBER STOP SDIV LT STOP ORIGIN ", + "sourceMap": "102:1764:1:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;102:1764:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;155:20;;;:::i;:::-;;;;;;;;;;;;;;;;;;;181:26;;;:::i;282:25::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;282:25:1;;:::i;496:94::-;;;:::i;:::-;;;;;;;;;;;;;;;;1698:166;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;1698:166:1;;;;:::i;:::-;;596:948;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;596:948:1;;;;;;;;;;;:::i;214:62::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;214:62:1;;;;;;;;;;;:::i;1550:142::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;1550:142:1;;;;:::i;155:20::-;;;;;;:::o;181:26::-;;;;;;:::o;282:25::-;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;282:25:1;:::o;496:94::-;568:8;:15;496:94;:::o;1698:166::-;1785:11;;;;1771:10;:25;1763:58;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1831:11;:26;;;;;;;;;;;;;;;1698:166::o;596:948::-;666:12;708:6;698:16;;:6;:16;;;;690:59;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;760:14;776;803:6;794:15;;:6;:15;;;:53;;832:6;840;794:53;;;813:6;821;794:53;759:88;;-1:-1:-1;759:88:1;-1:-1:-1;865:20:1;;;857:56;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;931:37;:15;;;966:1;931:15;;;:7;:15;;;;;;;;:23;;;;;;;;;;;;:37;923:72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1035:21;1059:32;;;;;;;;:::i;:::-;41:4:-1;34:5;30:16;25:3;21:26;14:5;7:41;87:2;83:7;78:2;73:3;69:12;65:26;61:2;54:38;1059:32:1;1035:56;;1101:12;1143:6;1151;1126:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;1126:32:1;;;1116:43;;;;;;1101:58;;1247:4;1236:8;1230:15;1225:2;1215:8;1211:17;1208:1;1200:52;1271:47;;;;;;:31;:47;;;;;;;;;;;;;;;;1192:60;;-1:-1:-1;1271:31:1;;;;;;:47;;;;;-1:-1:-1;;1271:47:1;;;;;;;;-1:-1:-1;1271:31:1;:47;;;5:2:-1;;;;30:1;27;20:12;5:2;1271:47:1;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;;;1328:15:1;;;;;;;;:7;:15;;;;;;;;:23;;;;;;;;;;;;:30;;;;;;;;;;;;;;1368:15;;;;;;:23;;;;;;;;:30;;;;;;;;1453:8;27:10:-1;;-1:-1;23:18;;45:23;;1453:19:1;;;;;;;;;;;;;;;;;;1521:15;;1487:50;;;;;;;;;;;;;;;;;;;;;;596:948;;;;;;;;:::o;214:62::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;1550:142::-;1625:11;;;;1611:10;:25;1603:58;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1671:5;:14;;;;;;;;;;;;;;;1550:142::o;102:1764::-;;;;;;;;:::o" + } + }, + "interface": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_feeToSetter", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "pair", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "PairCreated", + "type": "event" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "allPairs", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "allPairsLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + } + ], + "name": "createPair", + "outputs": [ + { + "internalType": "address", + "name": "pair", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "feeTo", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "feeToSetter", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "getPair", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_feeTo", + "type": "address" + } + ], + "name": "setFeeTo", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_feeToSetter", + "type": "address" + } + ], + "name": "setFeeToSetter", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ] +} \ No newline at end of file diff --git a/crates/zkcast/Cargo.toml b/crates/zkcast/Cargo.toml new file mode 100644 index 000000000..547413173 --- /dev/null +++ b/crates/zkcast/Cargo.toml @@ -0,0 +1,90 @@ +[package] +name = "zkcast" +description = "Command-line tool for performing Ethereum RPC calls" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[[bin]] +name = "zkcast" +path = "bin/main.rs" + +[build-dependencies] +vergen = { version = "8", default-features = false, features = ["build", "git", "git2"] } + +[dependencies] +# lib +foundry-utils.workspace = true +foundry-evm.workspace = true +foundry-config.workspace = true +foundry-common.workspace = true + +ethers-etherscan.workspace = true +ethers-core.workspace = true +ethers-providers.workspace = true +chrono.workspace = true +evm-disassembler = "0.2" +eyre.workspace = true +futures = "0.3" +hex.workspace = true +rayon = "1" +serde.workspace = true +serde_json.workspace = true + +# zksync +zksync-web3-rs = {git = "https://github.com/lambdaclass/zksync-web3-rs.git", rev = "70327ae5413c517bd4d27502507cdd96ee40cd22"} + +# commit 2a5ab4cf7db0625b406cdf9023604654ce56af46 +ansi_term = "0.12.1" +anyhow = {version = "1.0.70"} +dirs = {version = "5.0.0"} +url = "2.3.1" + +ethabi = "18.0.0" +uint = "0.9.0" + +# aws +rusoto_core = { version = "0.48", default-features = false } +rusoto_kms = { version = "0.48", default-features = false } + +# bin +# foundry internal +foundry-cli.workspace = true + +# eth +ethers = { workspace = true, features = ["rustls", "solc-sha2-asm"] } +eth-keystore = "0.5" + +clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } +clap_complete = "4" +clap_complete_fig = "4" +comfy-table = "7" +dunce = "1" +indicatif = "0.17" +itertools.workspace = true +regex = { version = "1", default-features = false } +rpassword = "7" +semver = "1" +tempfile = "3" +tokio = { version = "1", features = ["macros", "signal"] } +tracing.workspace = true +yansi = "0.5" + +[dev-dependencies] +foundry-test-utils.workspace = true +async-trait = "0.1" +criterion = "0.5" + +[features] +default = ["rustls"] +rustls = ["foundry-cli/rustls"] +openssl = ["foundry-cli/openssl"] + +[[bench]] +name = "vanity" +harness = false diff --git a/crates/zkcast/README.md b/crates/zkcast/README.md new file mode 100644 index 000000000..32de3fd74 --- /dev/null +++ b/crates/zkcast/README.md @@ -0,0 +1,5 @@ +# `cast` + +Cast is a command-line tool for performing Ethereum RPC calls. You can make smart contract calls, send transactions, or retrieve any type of chain data - all from your command-line! + +For more information, see the [📖 Foundry Book (Cast Guide)](https://book.getfoundry.sh/cast/index.html). diff --git a/crates/zkcast/benches/vanity.rs b/crates/zkcast/benches/vanity.rs new file mode 100644 index 000000000..4311b5283 --- /dev/null +++ b/crates/zkcast/benches/vanity.rs @@ -0,0 +1,51 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use rayon::prelude::*; +use std::{hint::black_box, time::Duration}; + +#[path = "../bin/cmd/wallet/mod.rs"] +#[allow(unused)] +mod wallet; +use wallet::vanity::*; + +/// Benches `cast wallet vanity` +/// +/// Left or right matchers, with or without nonce do not change the outcome. +/// +/// Regex matchers get optimised away even with a black_box. +fn vanity(c: &mut Criterion) { + let mut g = c.benchmark_group("vanity"); + + g.sample_size(500); + g.noise_threshold(0.04); + g.measurement_time(Duration::from_secs(30)); + + g.bench_function("wallet generator", |b| b.iter(|| black_box(generate_wallet()))); + + // 1 + + g.sample_size(100); + g.noise_threshold(0.02); + + g.bench_function("match 1", |b| { + let m = LeftHexMatcher { left: vec![0] }; + let matcher = create_matcher(m); + b.iter(|| wallet_generator().find_any(|x| black_box(matcher(x)))) + }); + + // 2 + + g.sample_size(10); + g.noise_threshold(0.01); + g.measurement_time(Duration::from_secs(60)); + + g.bench_function("match 2", |b| { + let m = LeftHexMatcher { left: vec![0, 0] }; + let matcher = create_matcher(m); + b.iter(|| wallet_generator().find_any(|x| black_box(matcher(x)))) + }); + + g.finish(); +} + +criterion_group!(vanity_benches, vanity); +criterion_main!(vanity_benches); diff --git a/crates/zkcast/bin/cmd/access_list.rs b/crates/zkcast/bin/cmd/access_list.rs new file mode 100644 index 000000000..519f058d8 --- /dev/null +++ b/crates/zkcast/bin/cmd/access_list.rs @@ -0,0 +1,115 @@ +use clap::Parser; +use ethers::{ + providers::Middleware, + types::{BlockId, NameOrAddress}, +}; +use eyre::{Result, WrapErr}; +use foundry_cli::{ + opts::{EthereumOpts, TransactionOpts}, + utils, +}; +use foundry_config::{Chain, Config}; +use std::str::FromStr; +use zkcast::{Cast, TxBuilder}; + +/// CLI arguments for `cast access-list`. +#[derive(Debug, Parser)] +pub struct AccessListArgs { + /// The destination of the transaction. + #[clap( + value_name = "TO", + value_parser = NameOrAddress::from_str + )] + to: Option, + + /// The signature of the function to call. + #[clap(value_name = "SIG")] + sig: Option, + + /// The arguments of the function to call. + #[clap(value_name = "ARGS")] + args: Vec, + + /// The data for the transaction. + #[clap( + long, + value_name = "DATA", + value_parser = foundry_common::clap_helpers::strip_0x_prefix, + conflicts_with_all = &["sig", "args"] + )] + data: Option, + + /// The block height to query at. + /// + /// Can also be the tags earliest, finalized, safe, latest, or pending. + #[clap(long, short = 'B')] + block: Option, + + /// Print the access list as JSON. + #[clap(long, short, help_heading = "Display options")] + json: bool, + + #[clap(flatten)] + tx: TransactionOpts, + + #[clap(flatten)] + eth: EthereumOpts, +} + +impl AccessListArgs { + pub async fn run(self) -> Result<()> { + let AccessListArgs { to, sig, args, data, tx, eth, block, json: to_json } = self; + + let config = Config::from(ð); + let provider = utils::get_provider(&config)?; + let chain = utils::get_chain(config.chain_id, &provider).await?; + let sender = eth.wallet.sender().await; + + access_list(&provider, sender, to, sig, args, data, tx, chain, block, to_json).await?; + Ok(()) + } +} + +#[allow(clippy::too_many_arguments)] +async fn access_list, T: Into>( + provider: M, + from: F, + to: Option, + sig: Option, + args: Vec, + data: Option, + tx: TransactionOpts, + chain: Chain, + block: Option, + to_json: bool, +) -> Result<()> +where + M::Error: 'static, +{ + let mut builder = TxBuilder::new(&provider, from, to, chain, tx.legacy).await?; + builder + .gas(tx.gas_limit) + .gas_price(tx.gas_price) + .priority_gas_price(tx.priority_gas_price) + .nonce(tx.nonce); + + builder.value(tx.value); + + if let Some(sig) = sig { + builder.set_args(sig.as_str(), args).await?; + } + if let Some(data) = data { + // Note: `sig+args` and `data` are mutually exclusive + builder.set_data(hex::decode(data).wrap_err("Expected hex encoded function data")?); + } + + let builder_output = builder.peek(); + + let cast = Cast::new(&provider); + + let access_list: String = cast.access_list(builder_output, block, to_json).await?; + + println!("{}", access_list); + + Ok(()) +} diff --git a/crates/zkcast/bin/cmd/bind.rs b/crates/zkcast/bin/cmd/bind.rs new file mode 100644 index 000000000..16794711b --- /dev/null +++ b/crates/zkcast/bin/cmd/bind.rs @@ -0,0 +1,107 @@ +use clap::{Parser, ValueHint}; +use ethers::prelude::{errors::EtherscanError, Abigen, Client, MultiAbigen}; +use eyre::Result; +use foundry_cli::opts::EtherscanOpts; +use foundry_config::Config; +use std::path::{Path, PathBuf}; + +static DEFAULT_CRATE_NAME: &str = "foundry-contracts"; +static DEFAULT_CRATE_VERSION: &str = "0.0.1"; + +/// CLI arguments for `cast bind`. +#[derive(Debug, Clone, Parser)] +pub struct BindArgs { + /// The contract address, or the path to an ABI Directory + /// + /// If an address is specified, then the ABI is fetched from Etherscan. + path_or_address: String, + + /// Path to where bindings will be stored + #[clap( + short, + long, + value_hint = ValueHint::DirPath, + value_name = "PATH" + )] + pub output_dir: Option, + + /// The name of the Rust crate to generate. + /// + /// This should be a valid crates.io crate name. However, this is currently not validated by + /// this command. + #[clap( + long, + default_value = DEFAULT_CRATE_NAME, + value_name = "NAME" + )] + crate_name: String, + + /// The version of the Rust crate to generate. + /// + /// This should be a standard semver version string. However, it is not currently validated by + /// this command. + #[clap( + long, + default_value = DEFAULT_CRATE_VERSION, + value_name = "VERSION" + )] + crate_version: String, + + /// Generate bindings as separate files. + #[clap(long)] + separate_files: bool, + + #[clap(flatten)] + etherscan: EtherscanOpts, +} + +impl BindArgs { + pub async fn run(self) -> Result<()> { + let path = Path::new(&self.path_or_address); + let multi = if path.exists() { + MultiAbigen::from_json_files(path) + } else { + self.abigen_etherscan().await + }?; + + println!("Generating bindings for {} contracts", multi.len()); + let bindings = multi.build()?; + + let out = self + .output_dir + .clone() + .unwrap_or_else(|| std::env::current_dir().unwrap().join("bindings")); + bindings.write_to_crate(self.crate_name, self.crate_version, out, !self.separate_files)?; + Ok(()) + } + + async fn abigen_etherscan(&self) -> Result { + let config = Config::from(&self.etherscan); + + let chain = config.chain_id.unwrap_or_default(); + let api_key = config.get_etherscan_api_key(Some(chain)).unwrap_or_default(); + let chain = chain.named()?; + + let client = Client::new(chain, api_key)?; + let address = self.path_or_address.parse()?; + let source = match client.contract_source_code(address).await { + Ok(source) => source, + Err(EtherscanError::InvalidApiKey) => { + eyre::bail!("Invalid Etherscan API key. Did you set it correctly? You may be using an API key for another Etherscan API chain (e.g. Etherscan API key for Polygonscan).") + } + Err(EtherscanError::ContractCodeNotVerified(address)) => { + eyre::bail!("Contract source code at {:?} on {} not verified. Maybe you have selected the wrong chain?", address, chain) + } + Err(err) => { + eyre::bail!(err) + } + }; + let abigens = source + .items + .into_iter() + .map(|item| Abigen::new(item.contract_name, item.abi).unwrap()) + .collect::>(); + + Ok(MultiAbigen::from_abigens(abigens)) + } +} diff --git a/crates/zkcast/bin/cmd/call.rs b/crates/zkcast/bin/cmd/call.rs new file mode 100644 index 000000000..4cd16e0ac --- /dev/null +++ b/crates/zkcast/bin/cmd/call.rs @@ -0,0 +1,294 @@ +use clap::Parser; +use ethers::{ + solc::EvmVersion, + types::{BlockId, NameOrAddress, U256}, +}; +use eyre::{Result, WrapErr}; +use foundry_cli::{ + opts::{EthereumOpts, TransactionOpts}, + utils::{self, handle_traces, parse_ether_value, TraceResult}, +}; +use foundry_common::runtime_client::RuntimeClient; +use foundry_config::{find_project_root_path, Config}; +use foundry_evm::{executor::opts::EvmOpts, trace::TracingExecutor}; +use foundry_utils::types::ToAlloy; +use std::str::FromStr; +use zkcast::{Cast, TxBuilder}; + +type Provider = ethers::providers::Provider; + +/// CLI arguments for `cast call`. +#[derive(Debug, Parser)] +pub struct CallArgs { + /// The destination of the transaction. + #[clap(value_parser = NameOrAddress::from_str)] + to: Option, + + /// The signature of the function to call. + sig: Option, + + /// The arguments of the function to call. + args: Vec, + + /// Data for the transaction. + #[clap( + long, + value_parser = foundry_common::clap_helpers::strip_0x_prefix, + conflicts_with_all = &["sig", "args"] + )] + data: Option, + + /// Forks the remote rpc, executes the transaction locally and prints a trace + #[clap(long, default_value_t = false)] + trace: bool, + + /// Can only be used with "--trace" + /// + /// opens an interactive debugger + #[clap(long, requires = "trace")] + debug: bool, + + /// Can only be used with "--trace" + /// + /// prints a more verbose trace + #[clap(long, requires = "trace")] + verbose: bool, + + /// Can only be used with "--trace" + /// Labels to apply to the traces. + /// + /// Format: `address:label` + #[clap(long, requires = "trace")] + labels: Vec, + + /// Can only be used with "--trace" + /// + /// The EVM Version to use. + #[clap(long, requires = "trace")] + evm_version: Option, + + /// The block height to query at. + /// + /// Can also be the tags earliest, finalized, safe, latest, or pending. + #[clap(long, short)] + block: Option, + + #[clap(subcommand)] + command: Option, + + #[clap(flatten)] + tx: TransactionOpts, + + #[clap(flatten)] + eth: EthereumOpts, +} + +#[derive(Debug, Parser)] +pub enum CallSubcommands { + /// ignores the address field and simulates creating a contract + #[clap(name = "--create")] + Create { + /// Bytecode of contract. + code: String, + + /// The signature of the constructor. + sig: Option, + + /// The arguments of the constructor. + args: Vec, + + /// Ether to send in the transaction. + /// + /// Either specified in wei, or as a string with a unit type. + /// + /// Examples: 1ether, 10gwei, 0.01ether + #[clap(long, value_parser = parse_ether_value)] + value: Option, + }, +} + +impl CallArgs { + pub async fn run(self) -> Result<()> { + let CallArgs { + to, + sig, + args, + data, + tx, + eth, + command, + block, + trace, + evm_version, + debug, + verbose, + labels, + } = self; + + let config = Config::from(ð); + let provider = utils::get_provider(&config)?; + let chain = utils::get_chain(config.chain_id, &provider).await?; + let sender = eth.wallet.sender().await; + + let mut builder: TxBuilder<'_, Provider> = + TxBuilder::new(&provider, sender, to, chain, tx.legacy).await?; + + builder + .gas(tx.gas_limit) + .etherscan_api_key(config.get_etherscan_api_key(Some(chain))) + .gas_price(tx.gas_price) + .priority_gas_price(tx.priority_gas_price) + .nonce(tx.nonce); + + match command { + Some(CallSubcommands::Create { code, sig, args, value }) => { + if trace { + let figment = Config::figment_with_root(find_project_root_path(None).unwrap()) + .merge(eth.rpc); + + let evm_opts = figment.extract::()?; + + let (env, fork, chain) = + TracingExecutor::get_fork_material(&config, evm_opts).await?; + + let mut executor = + foundry_evm::trace::TracingExecutor::new(env, fork, evm_version, debug) + .await; + + let trace = match executor.deploy( + sender.to_alloy(), + code.into_bytes().into(), + value.unwrap_or(U256::zero()).to_alloy(), + None, + ) { + Ok(deploy_result) => TraceResult::from(deploy_result), + Err(evm_err) => TraceResult::try_from(evm_err)?, + }; + + handle_traces(trace, &config, chain, labels, verbose, debug).await?; + + return Ok(()) + } + + // fill the builder after the conditional so we dont move values + fill_create(&mut builder, value, code, sig, args).await?; + } + _ => { + // fill first here because we need to use the builder in the conditional + fill_tx(&mut builder, tx.value, sig, args, data).await?; + + if trace { + let figment = Config::figment_with_root(find_project_root_path(None).unwrap()) + .merge(eth.rpc); + + let evm_opts = figment.extract::()?; + + let (env, fork, chain) = + TracingExecutor::get_fork_material(&config, evm_opts).await?; + + let mut executor = + foundry_evm::trace::TracingExecutor::new(env, fork, evm_version, debug) + .await; + + let (tx, _) = builder.build(); + + let trace = TraceResult::from(executor.call_raw_committing( + sender.to_alloy(), + tx.to_addr().copied().expect("an address to be here").to_alloy(), + tx.data().cloned().unwrap_or_default().to_vec().into(), + tx.value().copied().unwrap_or_default().to_alloy(), + )?); + + handle_traces(trace, &config, chain, labels, verbose, debug).await?; + + return Ok(()) + } + } + }; + + let builder_output: ( + ethers::types::transaction::eip2718::TypedTransaction, + Option, + ) = builder.build(); + + println!("{}", Cast::new(provider).call(builder_output, block).await?); + + Ok(()) + } +} + +/// fills the builder from create arg +async fn fill_create( + builder: &mut TxBuilder<'_, Provider>, + value: Option, + code: String, + sig: Option, + args: Vec, +) -> Result<()> { + builder.value(value); + + let mut data = hex::decode(code)?; + + if let Some(s) = sig { + let (mut sigdata, _func) = builder.create_args(&s, args).await?; + data.append(&mut sigdata); + } + + builder.set_data(data); + + Ok(()) +} + +/// fills the builder from args +async fn fill_tx( + builder: &mut TxBuilder<'_, Provider>, + value: Option, + sig: Option, + args: Vec, + data: Option, +) -> Result<()> { + builder.value(value); + + if let Some(sig) = sig { + builder.set_args(sig.as_str(), args).await?; + } + + if let Some(data) = data { + // Note: `sig+args` and `data` are mutually exclusive + builder.set_data(hex::decode(data).wrap_err("Expected hex encoded function data")?); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use ethers::types::Address; + + #[test] + fn can_parse_call_data() { + let data = hex::encode("hello"); + let args: CallArgs = + CallArgs::parse_from(["foundry-cli", "--data", format!("0x{data}").as_str()]); + assert_eq!(args.data, Some(data.clone())); + + let args: CallArgs = CallArgs::parse_from(["foundry-cli", "--data", data.as_str()]); + assert_eq!(args.data, Some(data)); + } + + #[test] + fn call_sig_and_data_exclusive() { + let data = hex::encode("hello"); + let to = Address::zero(); + let args = CallArgs::try_parse_from([ + "foundry-cli", + format!("{to:?}").as_str(), + "signature", + "--data", + format!("0x{data}").as_str(), + ]); + + assert!(args.is_err()); + } +} diff --git a/crates/zkcast/bin/cmd/create2.rs b/crates/zkcast/bin/cmd/create2.rs new file mode 100644 index 000000000..e436dfa8f --- /dev/null +++ b/crates/zkcast/bin/cmd/create2.rs @@ -0,0 +1,314 @@ +use clap::Parser; +use ethers::{ + core::rand::thread_rng, + types::{Address, Bytes, H256, U256}, + utils::{get_create2_address_from_hash, keccak256}, +}; +use eyre::{Result, WrapErr}; +use rayon::prelude::*; +use regex::RegexSetBuilder; +use std::{str::FromStr, time::Instant}; +use zkcast::SimpleCast; + +/// CLI arguments for `cast create2`. +#[derive(Debug, Clone, Parser)] +pub struct Create2Args { + /// Prefix for the contract address. + #[clap( + long, + short, + required_unless_present_any = &["ends_with", "matching"], + value_name = "HEX" + )] + starts_with: Option, + + /// Suffix for the contract address. + #[clap(long, short, value_name = "HEX")] + ends_with: Option, + + /// Sequence that the address has to match. + #[clap(long, short, value_name = "HEX")] + matching: Option, + + /// Case sensitive matching. + #[clap(short, long)] + case_sensitive: bool, + + /// Address of the contract deployer. + #[clap( + short, + long, + default_value = "0x4e59b44847b379578588920ca78fbf26c0b4956c", + value_name = "ADDRESS" + )] + deployer: Address, + + /// Init code of the contract to be deployed. + #[clap(short, long, value_name = "HEX", default_value = "")] + init_code: String, + + /// Init code hash of the contract to be deployed. + #[clap(alias = "ch", long, value_name = "HASH")] + init_code_hash: Option, +} + +#[allow(dead_code)] +pub struct Create2Output { + pub address: Address, + pub salt: U256, +} + +impl Create2Args { + pub fn run(self) -> Result { + let Create2Args { + starts_with, + ends_with, + matching, + case_sensitive, + deployer, + init_code, + init_code_hash, + } = self; + + let mut regexs = vec![]; + + if let Some(matches) = matching { + if starts_with.is_some() || ends_with.is_some() { + eyre::bail!("Either use --matching or --starts/ends-with"); + } + + let matches = matches.trim_start_matches("0x"); + + if matches.len() != 40 { + eyre::bail!("Please provide a 40 characters long sequence for matching"); + } + + hex::decode(matches.replace('X', "0")).wrap_err("invalid matching hex provided")?; + // replacing X placeholders by . to match any character at these positions + + regexs.push(matches.replace('X', ".")); + } + + if let Some(prefix) = starts_with { + regexs.push(format!( + r"^{}", + get_regex_hex_string(prefix).wrap_err("invalid prefix hex provided")? + )); + } + if let Some(suffix) = ends_with { + regexs.push(format!( + r"{}$", + get_regex_hex_string(suffix).wrap_err("invalid prefix hex provided")? + )) + } + + debug_assert!( + regexs.iter().map(|p| p.len() - 1).sum::() <= 40, + "vanity patterns length exceeded. cannot be more than 40 characters", + ); + + let regex = RegexSetBuilder::new(regexs).case_insensitive(!case_sensitive).build()?; + + let init_code_hash = if let Some(init_code_hash) = init_code_hash { + let mut a: [u8; 32] = [0; 32]; + let init_code_hash_bytes = hex::decode(init_code_hash)?; + assert!(init_code_hash_bytes.len() == 32, "init code hash should be 32 bytes long"); + a.copy_from_slice(&init_code_hash_bytes); + a + } else { + keccak256(hex::decode(init_code)?) + }; + + println!("Starting to generate deterministic contract address..."); + let timer = Instant::now(); + let (salt, addr) = std::iter::repeat(()) + .par_bridge() + .map(|_| { + let salt = H256::random_using(&mut thread_rng()); + let salt = Bytes::from(salt.to_fixed_bytes()); + + let addr = SimpleCast::to_checksum_address(&get_create2_address_from_hash( + deployer, + salt.clone(), + init_code_hash, + )); + + (salt, addr) + }) + .find_any(move |(_, addr)| { + let addr = addr.to_string(); + let addr = addr.strip_prefix("0x").unwrap(); + regex.matches(addr).into_iter().count() == regex.patterns().len() + }) + .unwrap(); + + let salt = U256::from(salt.to_vec().as_slice()); + let address = Address::from_str(&addr).unwrap(); + + println!( + "Successfully found contract address in {} seconds.\nAddress: {}\nSalt: {}", + timer.elapsed().as_secs(), + addr, + salt + ); + + Ok(Create2Output { address, salt }) + } +} + +fn get_regex_hex_string(s: String) -> Result { + let s = s.strip_prefix("0x").unwrap_or(&s); + let pad_width = s.len() + s.len() % 2; + hex::decode(format!("{s:0) -> Address { + // let init_code_hash = keccak256(init_code); + get_create2_address(deployer, salt.encode(), init_code) + } + + fn verify_create2_hash(deployer: Address, salt: U256, init_code_hash: Vec) -> Address { + get_create2_address_from_hash(deployer, salt.encode(), init_code_hash) + } +} diff --git a/crates/zkcast/bin/cmd/estimate.rs b/crates/zkcast/bin/cmd/estimate.rs new file mode 100644 index 000000000..09f9395e7 --- /dev/null +++ b/crates/zkcast/bin/cmd/estimate.rs @@ -0,0 +1,126 @@ +use clap::Parser; +use ethers::types::{NameOrAddress, U256}; +use eyre::Result; +use foundry_cli::{ + opts::{EtherscanOpts, RpcOpts}, + utils::{self, parse_ether_value}, +}; +use foundry_config::{figment::Figment, Config}; +use std::str::FromStr; +use zkcast::{Cast, TxBuilder}; + +/// CLI arguments for `cast estimate`. +#[derive(Debug, Parser)] +pub struct EstimateArgs { + /// The destination of the transaction. + #[clap(value_parser = NameOrAddress::from_str)] + to: Option, + + /// The signature of the function to call. + sig: Option, + + /// The arguments of the function to call. + args: Vec, + + /// The sender account. + #[clap( + short, + long, + value_parser = NameOrAddress::from_str, + default_value = "0x0000000000000000000000000000000000000000", + env = "ETH_FROM", + )] + from: NameOrAddress, + + /// Ether to send in the transaction. + /// + /// Either specified in wei, or as a string with a unit type: + /// + /// Examples: 1ether, 10gwei, 0.01ether + #[clap(long, value_parser = parse_ether_value)] + value: Option, + + #[clap(flatten)] + rpc: RpcOpts, + + #[clap(flatten)] + etherscan: EtherscanOpts, + + #[clap(subcommand)] + command: Option, +} + +#[derive(Debug, Parser)] +pub enum EstimateSubcommands { + /// Estimate gas cost to deploy a smart contract + #[clap(name = "--create")] + Create { + /// The bytecode of contract + code: String, + + /// The signature of the constructor + sig: Option, + + /// Constructor arguments + args: Vec, + + /// Ether to send in the transaction + /// + /// Either specified in wei, or as a string with a unit type: + /// + /// Examples: 1ether, 10gwei, 0.01ether + #[clap(long, value_parser = parse_ether_value)] + value: Option, + }, +} + +impl EstimateArgs { + pub async fn run(self) -> Result<()> { + let EstimateArgs { from, to, sig, args, value, rpc, etherscan, command } = self; + + let figment = Figment::from(Config::figment()).merge(etherscan).merge(rpc); + let config = Config::from_provider(figment); + + let provider = utils::get_provider(&config)?; + let chain = utils::get_chain(config.chain_id, &provider).await?; + let api_key = config.get_etherscan_api_key(Some(chain)); + + let mut builder = TxBuilder::new(&provider, from, to, chain, false).await?; + builder.etherscan_api_key(api_key); + + match command { + Some(EstimateSubcommands::Create { code, sig, args, value }) => { + builder.value(value); + + let mut data = hex::decode(code)?; + + if let Some(s) = sig { + let (mut sigdata, _func) = builder.create_args(&s, args).await?; + data.append(&mut sigdata); + } + + builder.set_data(data); + } + _ => { + let sig = sig.ok_or_else(|| eyre::eyre!("Function signature must be provided."))?; + builder.value(value).set_args(sig.as_str(), args).await?; + } + }; + + let builder_output = builder.peek(); + let gas = Cast::new(&provider).estimate(builder_output).await?; + println!("{gas}"); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_estimate_value() { + let args: EstimateArgs = EstimateArgs::parse_from(["foundry-cli", "--value", "100"]); + assert!(args.value.is_some()); + } +} diff --git a/crates/zkcast/bin/cmd/find_block.rs b/crates/zkcast/bin/cmd/find_block.rs new file mode 100644 index 000000000..b6ce10410 --- /dev/null +++ b/crates/zkcast/bin/cmd/find_block.rs @@ -0,0 +1,84 @@ +use clap::Parser; +use ethers::prelude::*; +use eyre::Result; +use foundry_cli::{opts::RpcOpts, utils}; +use foundry_config::Config; +use futures::join; +use zkcast::Cast; + +/// CLI arguments for `cast find-block`. +#[derive(Debug, Clone, Parser)] +pub struct FindBlockArgs { + /// The UNIX timestamp to search for, in seconds. + timestamp: u64, + + #[clap(flatten)] + rpc: RpcOpts, +} + +impl FindBlockArgs { + pub async fn run(self) -> Result<()> { + let FindBlockArgs { timestamp, rpc } = self; + + let ts_target = U256::from(timestamp); + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + + let last_block_num = provider.get_block_number().await?; + let cast_provider = Cast::new(provider); + + let res = join!(cast_provider.timestamp(last_block_num), cast_provider.timestamp(1)); + let ts_block_latest = res.0?; + let ts_block_1 = res.1?; + + let block_num = if ts_block_latest < ts_target { + // If the most recent block's timestamp is below the target, return it + last_block_num + } else if ts_block_1 > ts_target { + // If the target timestamp is below block 1's timestamp, return that + U64::from(1_u64) + } else { + // Otherwise, find the block that is closest to the timestamp + let mut low_block = U64::from(1_u64); // block 0 has a timestamp of 0: https://github.com/ethereum/go-ethereum/issues/17042#issuecomment-559414137 + let mut high_block = last_block_num; + let mut matching_block: Option = None; + while high_block > low_block && matching_block.is_none() { + // Get timestamp of middle block (this approach approach to avoids overflow) + let high_minus_low_over_2 = high_block + .checked_sub(low_block) + .ok_or_else(|| eyre::eyre!("unexpected underflow")) + .unwrap() + .checked_div(U64::from(2_u64)) + .unwrap(); + let mid_block = high_block.checked_sub(high_minus_low_over_2).unwrap(); + let ts_mid_block = cast_provider.timestamp(mid_block).await?; + + // Check if we've found a match or should keep searching + if ts_mid_block == ts_target { + matching_block = Some(mid_block) + } else if high_block.checked_sub(low_block).unwrap() == U64::from(1_u64) { + // The target timestamp is in between these blocks. This rounds to the + // highest block if timestamp is equidistant between blocks + let res = join!( + cast_provider.timestamp(high_block), + cast_provider.timestamp(low_block) + ); + let ts_high = res.0.unwrap(); + let ts_low = res.1.unwrap(); + let high_diff = ts_high.checked_sub(ts_target).unwrap(); + let low_diff = ts_target.checked_sub(ts_low).unwrap(); + let is_low = low_diff < high_diff; + matching_block = if is_low { Some(low_block) } else { Some(high_block) } + } else if ts_mid_block < ts_target { + low_block = mid_block; + } else { + high_block = mid_block; + } + } + matching_block.unwrap_or(low_block) + }; + println!("{block_num}"); + + Ok(()) + } +} diff --git a/crates/zkcast/bin/cmd/interface.rs b/crates/zkcast/bin/cmd/interface.rs new file mode 100644 index 000000000..629bf8f78 --- /dev/null +++ b/crates/zkcast/bin/cmd/interface.rs @@ -0,0 +1,88 @@ +use clap::Parser; +use eyre::Result; +use foundry_cli::opts::EtherscanOpts; +use foundry_common::fs; +use foundry_config::Config; +use std::path::{Path, PathBuf}; +use zkcast::{AbiPath, SimpleCast}; + +/// CLI arguments for `cast interface`. +#[derive(Debug, Clone, Parser)] +pub struct InterfaceArgs { + /// The contract address, or the path to an ABI file. + /// + /// If an address is specified, then the ABI is fetched from Etherscan. + path_or_address: String, + + /// The name to use for the generated interface. + #[clap(long, short)] + name: Option, + + /// Solidity pragma version. + #[clap(long, short, default_value = "^0.8.10", value_name = "VERSION")] + pragma: String, + + /// The path to the output file. + /// + /// If not specified, the interface will be output to stdout. + #[clap( + short, + long, + value_hint = clap::ValueHint::FilePath, + value_name = "PATH", + )] + output: Option, + + /// If specified, the interface will be output as JSON rather than Solidity. + #[clap(long, short)] + json: bool, + + #[clap(flatten)] + etherscan: EtherscanOpts, +} + +impl InterfaceArgs { + pub async fn run(self) -> Result<()> { + let InterfaceArgs { + path_or_address, + name, + pragma, + output: output_location, + etherscan, + json, + } = self; + let config = Config::from(ðerscan); + let chain = config.chain_id.unwrap_or_default(); + let source = if Path::new(&path_or_address).exists() { + AbiPath::Local { path: path_or_address, name } + } else { + let api_key = config.get_etherscan_api_key(Some(chain)).unwrap_or_default(); + let chain = chain.named()?; + AbiPath::Etherscan { chain, api_key, address: path_or_address.parse()? } + }; + let interfaces = SimpleCast::generate_interface(source).await?; + + // put it all together + let res = if json { + interfaces.into_iter().map(|iface| iface.json_abi).collect::>().join("\n") + } else { + let pragma = format!("pragma solidity {pragma};"); + let interfaces = interfaces + .iter() + .map(|iface| iface.source.to_string()) + .collect::>() + .join("\n"); + format!("{pragma}\n\n{interfaces}") + }; + + // print or write to file + if let Some(loc) = output_location { + fs::create_dir_all(loc.parent().unwrap())?; + fs::write(&loc, res)?; + println!("Saved interface at {}", loc.display()); + } else { + println!("{res}"); + } + Ok(()) + } +} diff --git a/crates/zkcast/bin/cmd/logs.rs b/crates/zkcast/bin/cmd/logs.rs new file mode 100644 index 000000000..a4bf60407 --- /dev/null +++ b/crates/zkcast/bin/cmd/logs.rs @@ -0,0 +1,413 @@ +use std::{io, str::FromStr}; + +use clap::Parser; +use ethers::{providers::Middleware, types::NameOrAddress}; +use ethers_core::{ + abi::{Address, Event, RawTopicFilter, Topic, TopicFilter}, + types::{BlockId, BlockNumber, Filter, FilterBlockOption, ValueOrArray, H256}, +}; +use eyre::Result; +use foundry_cli::{opts::EthereumOpts, utils}; +use foundry_common::abi::{get_event, parse_tokens}; +use foundry_config::Config; +use itertools::Itertools; +use zkcast::Cast; + +/// CLI arguments for `cast logs`. +#[derive(Debug, Parser)] +pub struct LogsArgs { + /// The block height to start query at. + /// + /// Can also be the tags earliest, finalized, safe, latest, or pending. + #[clap(long)] + from_block: Option, + + /// The block height to stop query at. + /// + /// Can also be the tags earliest, finalized, safe, latest, or pending. + #[clap(long)] + to_block: Option, + + /// The contract address to filter on. + #[clap( + long, + value_parser = NameOrAddress::from_str + )] + address: Option, + + /// The signature of the event to filter logs by which will be converted to the first topic or + /// a topic to filter on. + #[clap(value_name = "SIG_OR_TOPIC")] + sig_or_topic: Option, + + /// If used with a signature, the indexed fields of the event to filter by. Otherwise, the + /// remaining topics of the filter. + #[clap(value_name = "TOPICS_OR_ARGS")] + topics_or_args: Vec, + + /// If the RPC type and endpoints supports `eth_subscribe` stream logs instead of printing and + /// exiting. Will continue until interrupted or TO_BLOCK is reached. + #[clap(long)] + subscribe: bool, + + /// Print the logs as JSON.s + #[clap(long, short, help_heading = "Display options")] + json: bool, + + #[clap(flatten)] + eth: EthereumOpts, +} + +impl LogsArgs { + pub async fn run(self) -> Result<()> { + let LogsArgs { + from_block, + to_block, + address, + sig_or_topic, + topics_or_args, + subscribe, + json, + eth, + } = self; + + let config = Config::from(ð); + let provider = utils::get_provider(&config)?; + + let cast = Cast::new(&provider); + + let address = match address { + Some(address) => { + let address = match address { + NameOrAddress::Name(name) => provider.resolve_name(&name).await?, + NameOrAddress::Address(address) => address, + }; + Some(address) + } + None => None, + }; + + let from_block = cast.convert_block_number(from_block).await?; + let to_block = cast.convert_block_number(to_block).await?; + + let filter = build_filter(from_block, to_block, address, sig_or_topic, topics_or_args)?; + + if !subscribe { + let logs = cast.filter_logs(filter, json).await?; + + println!("{}", logs); + + return Ok(()) + } + + let mut stdout = io::stdout(); + cast.subscribe(filter, &mut stdout, json).await?; + + Ok(()) + } +} + +/// Builds a Filter by first trying to parse the `sig_or_topic` as an event signature. If +/// successful, `topics_or_args` is parsed as indexed inputs and converted to topics. Otherwise, +/// `sig_or_topic` is prepended to `topics_or_args` and used as raw topics. +fn build_filter( + from_block: Option, + to_block: Option, + address: Option
, + sig_or_topic: Option, + topics_or_args: Vec, +) -> Result { + let block_option = FilterBlockOption::Range { from_block, to_block }; + let topic_filter = match sig_or_topic { + // Try and parse the signature as an event signature + Some(sig_or_topic) => match get_event(sig_or_topic.as_str()) { + Ok(event) => build_filter_event_sig(event, topics_or_args)?, + Err(_) => { + let topics = [vec![sig_or_topic], topics_or_args].concat(); + build_filter_topics(topics)? + } + }, + None => TopicFilter::default(), + }; + + // Convert from TopicFilter to Filter + let topics = + vec![topic_filter.topic0, topic_filter.topic1, topic_filter.topic2, topic_filter.topic3] + .into_iter() + .map(|topic| match topic { + Topic::Any => None, + Topic::This(topic) => Some(ValueOrArray::Value(Some(topic))), + _ => unreachable!(), + }) + .collect::>(); + + let filter = Filter { + block_option, + address: address.map(ValueOrArray::Value), + topics: [topics[0].clone(), topics[1].clone(), topics[2].clone(), topics[3].clone()], + }; + + Ok(filter) +} + +/// Creates a TopicFilter from the given event signature and arguments. +fn build_filter_event_sig(event: Event, args: Vec) -> Result { + let args = args.iter().map(|arg| arg.as_str()).collect::>(); + + // Match the args to indexed inputs. Enumerate so that the ordering can be restored + // when merging the inputs with arguments and without arguments + let (with_args, without_args): (Vec<_>, Vec<_>) = event + .inputs + .iter() + .zip(args) + .filter(|(input, _)| input.indexed) + .map(|(input, arg)| (&input.kind, arg)) + .enumerate() + .partition(|(_, (_, arg))| !arg.is_empty()); + + // Only parse the inputs with arguments + let indexed_tokens = + parse_tokens(with_args.clone().into_iter().map(|(_, p)| p).collect::>(), true)?; + + // Merge the inputs restoring the original ordering + let mut tokens = with_args + .into_iter() + .zip(indexed_tokens) + .map(|((i, _), t)| (i, Some(t))) + .chain(without_args.into_iter().map(|(i, _)| (i, None))) + .sorted_by(|(i1, _), (i2, _)| i1.cmp(i2)) + .map(|(_, token)| token) + .collect::>(); + + tokens.resize(3, None); + + let raw = RawTopicFilter { + topic0: tokens[0].clone().map_or(Topic::Any, Topic::This), + topic1: tokens[1].clone().map_or(Topic::Any, Topic::This), + topic2: tokens[2].clone().map_or(Topic::Any, Topic::This), + }; + + // Let filter do the hardwork of converting arguments to topics + Ok(event.filter(raw)?) +} + +/// Creates a TopicFilter from raw topic hashes. +fn build_filter_topics(topics: Vec) -> Result { + let mut topics = topics + .into_iter() + .map(|topic| if topic.is_empty() { Ok(None) } else { H256::from_str(&topic).map(Some) }) + .collect::, _>>()?; + + topics.resize(4, None); + + Ok(TopicFilter { + topic0: topics[0].map_or(Topic::Any, Topic::This), + topic1: topics[1].map_or(Topic::Any, Topic::This), + topic2: topics[2].map_or(Topic::Any, Topic::This), + topic3: topics[3].map_or(Topic::Any, Topic::This), + }) +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use super::*; + use ethers::types::H160; + use ethers_core::types::H256; + + const ADDRESS: &str = "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38"; + const TRANSFER_SIG: &str = "Transfer(address indexed,address indexed,uint256)"; + const TRANSFER_TOPIC: &str = + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"; + + #[test] + fn test_build_filter_basic() { + let from_block = Some(BlockNumber::from(1337)); + let to_block = Some(BlockNumber::Latest); + let address = Address::from_str(ADDRESS).ok(); + let expected = Filter { + block_option: FilterBlockOption::Range { from_block, to_block }, + address: Some(ValueOrArray::Value(address.unwrap())), + topics: [None, None, None, None], + }; + let filter = build_filter(from_block, to_block, address, None, vec![]).unwrap(); + assert_eq!(filter, expected) + } + + #[test] + fn test_build_filter_sig() { + let expected = Filter { + block_option: FilterBlockOption::Range { from_block: None, to_block: None }, + address: None, + topics: [Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), None, None, None], + }; + let filter = + build_filter(None, None, None, Some(TRANSFER_SIG.to_string()), vec![]).unwrap(); + assert_eq!(filter, expected) + } + + #[test] + fn test_build_filter_mismatch() { + let expected = Filter { + block_option: FilterBlockOption::Range { from_block: None, to_block: None }, + address: None, + topics: [Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), None, None, None], + }; + let filter = build_filter( + None, + None, + None, + Some("Swap(address indexed from, address indexed to, uint256 value)".to_string()), // Change signature, should result in error + vec![], + ) + .unwrap(); + assert_ne!(filter, expected) + } + + #[test] + fn test_build_filter_sig_with_arguments() { + let expected = Filter { + block_option: FilterBlockOption::Range { from_block: None, to_block: None }, + address: None, + topics: [ + Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), + Some(H160::from_str(ADDRESS).unwrap().into()), + None, + None, + ], + }; + let filter = build_filter( + None, + None, + None, + Some(TRANSFER_SIG.to_string()), + vec![ADDRESS.to_string()], + ) + .unwrap(); + assert_eq!(filter, expected) + } + + #[test] + fn test_build_filter_sig_with_skipped_arguments() { + let expected = Filter { + block_option: FilterBlockOption::Range { from_block: None, to_block: None }, + address: None, + topics: [ + Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), + None, + Some(H160::from_str(ADDRESS).unwrap().into()), + None, + ], + }; + let filter = build_filter( + None, + None, + None, + Some(TRANSFER_SIG.to_string()), + vec![String::new(), ADDRESS.to_string()], + ) + .unwrap(); + assert_eq!(filter, expected) + } + + #[test] + fn test_build_filter_with_topics() { + let expected = Filter { + block_option: FilterBlockOption::Range { from_block: None, to_block: None }, + address: None, + topics: [ + Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), + Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), + None, + None, + ], + }; + let filter = build_filter( + None, + None, + None, + Some(TRANSFER_TOPIC.to_string()), + vec![TRANSFER_TOPIC.to_string()], + ) + .unwrap(); + + assert_eq!(filter, expected) + } + + #[test] + fn test_build_filter_with_skipped_topic() { + let expected = Filter { + block_option: FilterBlockOption::Range { from_block: None, to_block: None }, + address: None, + topics: [ + Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), + None, + Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), + None, + ], + }; + let filter = build_filter( + None, + None, + None, + Some(TRANSFER_TOPIC.to_string()), + vec![String::new(), TRANSFER_TOPIC.to_string()], + ) + .unwrap(); + + assert_eq!(filter, expected) + } + + #[test] + fn test_build_filter_sig_with_mismatched_argument() { + let err = build_filter( + None, + None, + None, + Some(TRANSFER_SIG.to_string()), + vec!["1234".to_string()], + ) + .err() + .unwrap() + .to_string(); + + assert_eq!(err, "Failed to parse `1234`, expected value of type: address"); + } + + #[test] + fn test_build_filter_with_invalid_sig_or_topic() { + let err = build_filter(None, None, None, Some("asdasdasd".to_string()), vec![]) + .err() + .unwrap() + .to_string(); + + assert_eq!(err, "Invalid character 's' at position 1"); + } + + #[test] + fn test_build_filter_with_invalid_sig_or_topic_hex() { + let err = build_filter(None, None, None, Some(ADDRESS.to_string()), vec![]) + .err() + .unwrap() + .to_string(); + + assert_eq!(err, "Invalid input length"); + } + + #[test] + fn test_build_filter_with_invalid_topic() { + let err = build_filter( + None, + None, + None, + Some(TRANSFER_TOPIC.to_string()), + vec!["1234".to_string()], + ) + .err() + .unwrap() + .to_string(); + + assert_eq!(err, "Invalid input length"); + } +} diff --git a/crates/zkcast/bin/cmd/mod.rs b/crates/zkcast/bin/cmd/mod.rs new file mode 100644 index 000000000..e2102f786 --- /dev/null +++ b/crates/zkcast/bin/cmd/mod.rs @@ -0,0 +1,22 @@ +//! Subcommands for cast +//! +//! All subcommands should respect the `foundry_config::Config`. +//! If a subcommand accepts values that are supported by the `Config`, then the subcommand should +//! implement `figment::Provider` which allows the subcommand to override the config's defaults, see +//! [`foundry_config::Config`]. + +pub mod access_list; +pub mod bind; +pub mod call; +pub mod create2; +pub mod estimate; +pub mod find_block; +pub mod interface; +pub mod logs; +pub mod rpc; +pub mod run; +pub mod send; +pub mod storage; +pub mod wallet; +pub mod zk_deposit; +pub mod zk_send; diff --git a/crates/zkcast/bin/cmd/rpc.rs b/crates/zkcast/bin/cmd/rpc.rs new file mode 100644 index 000000000..c2cf1cddc --- /dev/null +++ b/crates/zkcast/bin/cmd/rpc.rs @@ -0,0 +1,63 @@ +use clap::Parser; +use eyre::Result; +use foundry_cli::{opts::RpcOpts, utils}; +use foundry_config::Config; +use itertools::Itertools; +use zkcast::Cast; + +/// CLI arguments for `cast rpc`. +#[derive(Debug, Clone, Parser)] +pub struct RpcArgs { + /// RPC method name + method: String, + + /// RPC parameters + /// + /// Interpreted as JSON: + /// + /// cast rpc eth_getBlockByNumber 0x123 false + /// => {"method": "eth_getBlockByNumber", "params": ["0x123", false] ... } + params: Vec, + + /// Send raw JSON parameters + /// + /// The first param will be interpreted as a raw JSON array of params. + /// If no params are given, stdin will be used. For example: + /// + /// cast rpc eth_getBlockByNumber '["0x123", false]' --raw + /// => {"method": "eth_getBlockByNumber", "params": ["0x123", false] ... } + #[clap(long, short = 'w')] + raw: bool, + + #[clap(flatten)] + rpc: RpcOpts, +} + +impl RpcArgs { + pub async fn run(self) -> Result<()> { + let RpcArgs { raw, method, params, rpc } = self; + + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + + let params = if raw { + if params.is_empty() { + serde_json::Deserializer::from_reader(std::io::stdin()) + .into_iter() + .next() + .transpose()? + .ok_or_else(|| eyre::format_err!("Empty JSON parameters"))? + } else { + value_or_string(params.into_iter().join(" ")) + } + } else { + serde_json::Value::Array(params.into_iter().map(value_or_string).collect()) + }; + println!("{}", Cast::new(provider).rpc(&method, params).await?); + Ok(()) + } +} + +fn value_or_string(value: String) -> serde_json::Value { + serde_json::from_str(&value).unwrap_or(serde_json::Value::String(value)) +} diff --git a/crates/zkcast/bin/cmd/run.rs b/crates/zkcast/bin/cmd/run.rs new file mode 100644 index 000000000..0d5833cdc --- /dev/null +++ b/crates/zkcast/bin/cmd/run.rs @@ -0,0 +1,212 @@ +use clap::Parser; +use ethers::{prelude::Middleware, solc::EvmVersion}; +use eyre::{Result, WrapErr}; +use foundry_cli::{ + init_progress, + opts::RpcOpts, + update_progress, utils, + utils::{handle_traces, TraceResult}, +}; +use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE}; +use foundry_config::{find_project_root_path, Config}; +use foundry_evm::{ + executor::{inspector::cheatcodes::util::configure_tx_env, opts::EvmOpts, EvmError}, + revm::primitives::U256 as rU256, + trace::TracingExecutor, +}; +use foundry_utils::types::ToAlloy; +use tracing::trace; + +/// CLI arguments for `cast run`. +#[derive(Debug, Clone, Parser)] +pub struct RunArgs { + /// The transaction hash. + tx_hash: String, + + /// Opens the transaction in the debugger. + #[clap(long, short)] + debug: bool, + + /// Print out opcode traces. + #[clap(long, short)] + trace_printer: bool, + + /// Executes the transaction only with the state from the previous block. + /// + /// May result in different results than the live execution! + #[clap(long, short)] + quick: bool, + + /// Prints the full address of the contract. + #[clap(long, short)] + verbose: bool, + + /// Label addresses in the trace. + /// + /// Example: 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045:vitalik.eth + #[clap(long, short)] + label: Vec, + + #[clap(flatten)] + rpc: RpcOpts, + + /// The evm version to use. + /// + /// Overrides the version specified in the config. + #[clap(long, short)] + evm_version: Option, + /// Sets the number of assumed available compute units per second for this provider + /// + /// default value: 330 + /// + /// See also, https://github.com/alchemyplatform/alchemy-docs/blob/master/documentation/compute-units.md#rate-limits-cups + #[clap(long, alias = "cups", value_name = "CUPS")] + pub compute_units_per_second: Option, + + /// Disables rate limiting for this node's provider. + /// + /// default value: false + /// + /// See also, https://github.com/alchemyplatform/alchemy-docs/blob/master/documentation/compute-units.md#rate-limits-cups + #[clap(long, value_name = "NO_RATE_LIMITS", visible_alias = "no-rpc-rate-limit")] + pub no_rate_limit: bool, +} + +impl RunArgs { + /// Executes the transaction by replaying it + /// + /// This replays the entire block the transaction was mined in unless `quick` is set to true + /// + /// Note: This executes the transaction(s) as is: Cheatcodes are disabled + pub async fn run(self) -> Result<()> { + let figment = + Config::figment_with_root(find_project_root_path(None).unwrap()).merge(self.rpc); + let evm_opts = figment.extract::()?; + let mut config = Config::from_provider(figment).sanitized(); + + let compute_units_per_second = + if self.no_rate_limit { Some(u64::MAX) } else { self.compute_units_per_second }; + + let provider = utils::get_provider_builder(&config)? + .compute_units_per_second_opt(compute_units_per_second) + .build()?; + + let tx_hash = self.tx_hash.parse().wrap_err("invalid tx hash")?; + let tx = provider + .get_transaction(tx_hash) + .await? + .ok_or_else(|| eyre::eyre!("tx not found: {:?}", tx_hash))?; + + // check if the tx is a system transaction + if is_known_system_sender(tx.from) || + tx.transaction_type.map(|ty| ty.as_u64()) == Some(SYSTEM_TRANSACTION_TYPE) + { + return Err(eyre::eyre!( + "{:?} is a system transaction.\nReplaying system transactions is currently not supported.", + tx.hash + )) + } + + let tx_block_number = tx + .block_number + .ok_or_else(|| eyre::eyre!("tx may still be pending: {:?}", tx_hash))? + .as_u64(); + + // we need to fork off the parent block + config.fork_block_number = Some(tx_block_number - 1); + + let (mut env, fork, chain) = TracingExecutor::get_fork_material(&config, evm_opts).await?; + + let mut executor = + TracingExecutor::new(env.clone(), fork, self.evm_version, self.debug).await; + + env.block.number = rU256::from(tx_block_number); + + let block = provider.get_block_with_txs(tx_block_number).await?; + if let Some(ref block) = block { + env.block.timestamp = block.timestamp.to_alloy(); + env.block.coinbase = block.author.unwrap_or_default().to_alloy(); + env.block.difficulty = block.difficulty.to_alloy(); + env.block.prevrandao = Some(block.mix_hash.map(|h| h.to_alloy()).unwrap_or_default()); + env.block.basefee = block.base_fee_per_gas.unwrap_or_default().to_alloy(); + env.block.gas_limit = block.gas_limit.to_alloy(); + } + + // Set the state to the moment right before the transaction + if !self.quick { + println!("Executing previous transactions from the block."); + + if let Some(block) = block { + let pb = init_progress!(block.transactions, "tx"); + pb.set_position(0); + + for (index, tx) in block.transactions.into_iter().enumerate() { + // System transactions such as on L2s don't contain any pricing info so we skip + // them otherwise this would cause reverts + if is_known_system_sender(tx.from) || + tx.transaction_type.map(|ty| ty.as_u64()) == + Some(SYSTEM_TRANSACTION_TYPE) + { + update_progress!(pb, index); + continue + } + if tx.hash.eq(&tx_hash) { + break + } + + configure_tx_env(&mut env, &tx); + + if let Some(to) = tx.to { + trace!(tx=?tx.hash,?to, "executing previous call transaction"); + executor.commit_tx_with_env(env.clone()).wrap_err_with(|| { + format!( + "Failed to execute transaction: {:?} in block {}", + tx.hash, env.block.number + ) + })?; + } else { + trace!(tx=?tx.hash, "executing previous create transaction"); + if let Err(error) = executor.deploy_with_env(env.clone(), None) { + match error { + // Reverted transactions should be skipped + EvmError::Execution(_) => (), + error => { + return Err(error).wrap_err_with(|| { + format!( + "Failed to deploy transaction: {:?} in block {}", + tx.hash, env.block.number + ) + }) + } + } + } + } + + update_progress!(pb, index); + } + } + } + + // Execute our transaction + let result = { + executor.set_trace_printer(self.trace_printer); + + configure_tx_env(&mut env, &tx); + + if let Some(to) = tx.to { + trace!(tx=?tx.hash,to=?to, "executing call transaction"); + TraceResult::from(executor.commit_tx_with_env(env)?) + } else { + trace!(tx=?tx.hash, "executing create transaction"); + match executor.deploy_with_env(env, None) { + Ok(res) => TraceResult::from(res), + Err(err) => TraceResult::try_from(err)?, + } + } + }; + + handle_traces(result, &config, chain, self.label, self.verbose, self.debug).await?; + + Ok(()) + } +} diff --git a/crates/zkcast/bin/cmd/send.rs b/crates/zkcast/bin/cmd/send.rs new file mode 100644 index 000000000..9d9c3a49f --- /dev/null +++ b/crates/zkcast/bin/cmd/send.rs @@ -0,0 +1,254 @@ +use clap::Parser; +use ethers::{ + prelude::MiddlewareBuilder, providers::Middleware, signers::Signer, types::NameOrAddress, +}; +use eyre::Result; +use foundry_cli::{ + opts::{EthereumOpts, TransactionOpts}, + utils, +}; +use foundry_common::cli_warn; +use foundry_config::{Chain, Config}; +use std::str::FromStr; +use zkcast::{Cast, TxBuilder}; + +/// CLI arguments for `cast send`. +#[derive(Debug, Parser)] +pub struct SendTxArgs { + /// The destination of the transaction. + /// + /// If not provided, you must use cast send --create. + #[clap(value_parser = NameOrAddress::from_str)] + to: Option, + + /// The signature of the function to call. + sig: Option, + + /// The arguments of the function to call. + args: Vec, + + /// Only print the transaction hash and exit immediately. + #[clap(name = "async", long = "async", alias = "cast-async", env = "CAST_ASYNC")] + cast_async: bool, + + /// The number of confirmations until the receipt is fetched. + #[clap(long, default_value = "1")] + confirmations: usize, + + /// Print the transaction receipt as JSON. + #[clap(long, short, help_heading = "Display options")] + json: bool, + + /// Reuse the latest nonce for the sender account. + #[clap(long, conflicts_with = "nonce")] + resend: bool, + + #[clap(subcommand)] + command: Option, + + /// Send via `eth_sendTransaction using the `--from` argument or $ETH_FROM as sender + #[clap(long, requires = "from")] + unlocked: bool, + + #[clap(flatten)] + tx: TransactionOpts, + + #[clap(flatten)] + eth: EthereumOpts, +} + +#[derive(Debug, Parser)] +pub enum SendTxSubcommands { + /// Use to deploy raw contract bytecode. + #[clap(name = "--create")] + Create { + /// The bytecode of the contract to deploy. + code: String, + + /// The signature of the function to call. + sig: Option, + + /// The arguments of the function to call. + args: Vec, + }, +} + +impl SendTxArgs { + pub async fn run(self) -> Result<()> { + let SendTxArgs { + eth, + to, + sig, + cast_async, + mut args, + mut tx, + confirmations, + json: to_json, + resend, + command, + unlocked, + } = self; + let config = Config::from(ð); + let provider = utils::get_provider(&config)?; + let chain = utils::get_chain(config.chain_id, &provider).await?; + let api_key = config.get_etherscan_api_key(Some(chain)); + let mut sig = sig.unwrap_or_default(); + + let code = if let Some(SendTxSubcommands::Create { + code, + sig: constructor_sig, + args: constructor_args, + }) = command + { + sig = constructor_sig.unwrap_or_default(); + args = constructor_args; + Some(code) + } else { + None + }; + + // Case 1: + // Default to sending via eth_sendTransaction if the --unlocked flag is passed. + // This should be the only way this RPC method is used as it requires a local node + // or remote RPC with unlocked accounts. + if unlocked { + // only check current chain id if it was specified in the config + if let Some(config_chain) = config.chain_id { + let current_chain_id = provider.get_chainid().await?.as_u64(); + let config_chain_id = config_chain.id(); + // switch chain if current chain id is not the same as the one specified in the + // config + if config_chain_id != current_chain_id { + cli_warn!("Switching to chain {}", config_chain); + provider + .request( + "wallet_switchEthereumChain", + [serde_json::json!({ + "chainId": format!("0x{:x}", config_chain_id), + })], + ) + .await?; + } + } + + if resend { + tx.nonce = Some(provider.get_transaction_count(config.sender, None).await?); + } + + cast_send( + provider, + config.sender, + to, + code, + (sig, args), + tx, + chain, + api_key, + cast_async, + confirmations, + to_json, + ) + .await + // Case 2: + // An option to use a local signer was provided. + // If we cannot successfully instantiate a local signer, then we will assume we don't have + // enough information to sign and we must bail. + } else { + // Retrieve the signer, and bail if it can't be constructed. + let signer = eth.wallet.signer(chain.id()).await?; + let from = signer.address(); + + // prevent misconfigured hwlib from sending a transaction that defies + // user-specified --from + if let Some(specified_from) = eth.wallet.from { + if specified_from != from { + eyre::bail!( + "\ +The specified sender via CLI/env vars does not match the sender configured via +the hardware wallet's HD Path. +Please use the `--hd-path ` parameter to specify the BIP32 Path which +corresponds to the sender, or let foundry automatically detect it by not specifying any sender address." + ) + } + } + + if resend { + tx.nonce = Some(provider.get_transaction_count(from, None).await?); + } + + let provider = provider.with_signer(signer); + + cast_send( + provider, + from, + to, + code, + (sig, args), + tx, + chain, + api_key, + cast_async, + confirmations, + to_json, + ) + .await + } + } +} + +#[allow(clippy::too_many_arguments)] +async fn cast_send, T: Into>( + provider: M, + from: F, + to: Option, + code: Option, + args: (String, Vec), + tx: TransactionOpts, + chain: Chain, + etherscan_api_key: Option, + cast_async: bool, + confs: usize, + to_json: bool, +) -> Result<()> +where + M::Error: 'static, +{ + let (sig, params) = args; + let params = if !sig.is_empty() { Some((&sig[..], params)) } else { None }; + let mut builder = TxBuilder::new(&provider, from, to, chain, tx.legacy).await?; + builder + .etherscan_api_key(etherscan_api_key) + .gas(tx.gas_limit) + .gas_price(tx.gas_price) + .priority_gas_price(tx.priority_gas_price) + .value(tx.value) + .nonce(tx.nonce); + + if let Some(code) = code { + let mut data = hex::decode(code)?; + + if let Some((sig, args)) = params { + let (mut sigdata, _) = builder.create_args(sig, args).await?; + data.append(&mut sigdata); + } + + builder.set_data(data); + } else { + builder.args(params).await?; + }; + let builder_output = builder.build(); + + let cast = Cast::new(provider); + + let pending_tx = cast.send(builder_output).await?; + let tx_hash = *pending_tx; + + if cast_async { + println!("{tx_hash:#x}"); + } else { + let receipt = cast.receipt(format!("{tx_hash:#x}"), None, confs, false, to_json).await?; + println!("{receipt}"); + } + + Ok(()) +} diff --git a/crates/zkcast/bin/cmd/storage.rs b/crates/zkcast/bin/cmd/storage.rs new file mode 100644 index 000000000..47a76ff23 --- /dev/null +++ b/crates/zkcast/bin/cmd/storage.rs @@ -0,0 +1,258 @@ +use crate::opts::parse_slot; +use clap::Parser; +use comfy_table::{presets::ASCII_MARKDOWN, Table}; +use ethers::{ + abi::ethabi::ethereum_types::BigEndianHash, etherscan::Client, prelude::*, + solc::artifacts::StorageLayout, +}; +use eyre::Result; +use foundry_cli::{ + opts::{CoreBuildArgs, EtherscanOpts, RpcOpts}, + utils, +}; +use foundry_common::{ + abi::find_source, + compile::{compile, etherscan_project, suppress_compile}, + RetryProvider, +}; +use foundry_config::{ + figment::{self, value::Dict, Metadata, Profile}, + impl_figment_convert_cast, Config, +}; +use futures::future::join_all; +use semver::Version; +use std::str::FromStr; +use zkcast::Cast; + +/// The minimum Solc version for outputting storage layouts. +/// +/// https://github.com/ethereum/solidity/blob/develop/Changelog.md#065-2020-04-06 +const MIN_SOLC: Version = Version::new(0, 6, 5); + +/// CLI arguments for `cast storage`. +#[derive(Debug, Clone, Parser)] +pub struct StorageArgs { + /// The contract address. + #[clap(value_parser = NameOrAddress::from_str)] + address: NameOrAddress, + + /// The storage slot number. + #[clap(value_parser = parse_slot)] + slot: Option, + + /// The block height to query at. + /// + /// Can also be the tags earliest, finalized, safe, latest, or pending. + #[clap(long, short)] + block: Option, + + #[clap(flatten)] + rpc: RpcOpts, + + #[clap(flatten)] + etherscan: EtherscanOpts, + + #[clap(flatten)] + build: CoreBuildArgs, +} + +impl_figment_convert_cast!(StorageArgs); + +impl figment::Provider for StorageArgs { + fn metadata(&self) -> Metadata { + Metadata::named("StorageArgs") + } + + fn data(&self) -> Result, figment::Error> { + let mut map = self.build.data()?; + let dict = map.get_mut(&Config::selected_profile()).unwrap(); + dict.extend(self.rpc.dict()); + dict.extend(self.etherscan.dict()); + Ok(map) + } +} + +impl StorageArgs { + pub async fn run(self) -> Result<()> { + let config = Config::from(&self); + + let Self { address, slot, block, build, .. } = self; + + let provider = utils::get_provider(&config)?; + let address = match address { + NameOrAddress::Name(name) => provider.resolve_name(&name).await?, + NameOrAddress::Address(address) => address, + }; + + // Slot was provided, perform a simple RPC call + if let Some(slot) = slot { + let cast = Cast::new(provider); + println!("{}", cast.storage(address, slot, block).await?); + return Ok(()) + } + + // No slot was provided + // Get deployed bytecode at given address + let address_code = provider.get_code(address, block).await?; + if address_code.is_empty() { + eyre::bail!("Provided address has no deployed code and thus no storage"); + } + + // Check if we're in a forge project and if we can find the address' code + let mut project = build.project()?; + if project.paths.has_input_files() { + // Find in artifacts and pretty print + add_storage_layout_output(&mut project); + let out = compile(&project, false, false)?; + let match_code = |artifact: &ConfigurableContractArtifact| -> Option { + let bytes = + artifact.deployed_bytecode.as_ref()?.bytecode.as_ref()?.object.as_bytes()?; + Some(bytes == &address_code) + }; + let artifact = + out.artifacts().find(|(_, artifact)| match_code(artifact).unwrap_or_default()); + if let Some((_, artifact)) = artifact { + return fetch_and_print_storage(provider, address, artifact, true).await + } + } + + // Not a forge project or artifact not found + // Get code from Etherscan + eprintln!("No matching artifacts found, fetching source code from Etherscan..."); + + if self.etherscan.key.is_none() { + eyre::bail!("You must provide an Etherscan API key if you're fetching a remote contract's storage."); + } + + let chain = utils::get_chain(config.chain_id, &provider).await?; + let api_key = config.get_etherscan_api_key(Some(chain)).unwrap_or_default(); + let client = Client::new(chain.named()?, api_key)?; + 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") + } + + let version = metadata.compiler_version()?; + let auto_detect = version < MIN_SOLC; + + // Create a new temp project + // TODO: Cache instead of using a temp directory: metadata from Etherscan won't change + let root = tempfile::tempdir()?; + let root_path = root.path(); + let mut project = etherscan_project(metadata, root_path)?; + add_storage_layout_output(&mut project); + project.auto_detect = auto_detect; + + // Compile + let mut out = suppress_compile(&project)?; + let artifact = { + let (_, mut artifact) = out + .artifacts() + .find(|(name, _)| name == &metadata.contract_name) + .ok_or_else(|| eyre::eyre!("Could not find artifact"))?; + + if is_storage_layout_empty(&artifact.storage_layout) && auto_detect { + // try recompiling with the minimum version + eprintln!("The requested contract was compiled with {version} while the minimum version for storage layouts is {MIN_SOLC} and as a result the output may be empty."); + let solc = Solc::find_or_install_svm_version(MIN_SOLC.to_string())?; + project.solc = solc; + project.auto_detect = false; + if let Ok(output) = suppress_compile(&project) { + out = output; + let (_, new_artifact) = out + .artifacts() + .find(|(name, _)| name == &metadata.contract_name) + .ok_or_else(|| eyre::eyre!("Could not find artifact"))?; + artifact = new_artifact; + } + } + + artifact + }; + + // Clear temp directory + root.close()?; + + fetch_and_print_storage(provider, address, artifact, true).await + } +} + +async fn fetch_and_print_storage( + provider: RetryProvider, + address: Address, + artifact: &ConfigurableContractArtifact, + pretty: bool, +) -> Result<()> { + if is_storage_layout_empty(&artifact.storage_layout) { + eprintln!("Storage layout is empty."); + Ok(()) + } else { + let layout = artifact.storage_layout.as_ref().unwrap().clone(); + let values = fetch_storage_values(provider, address, &layout).await?; + print_storage(layout, values, pretty) + } +} + +/// Overrides the `value` field in [StorageLayout] with the slot's value to avoid creating new data +/// structures. +async fn fetch_storage_values( + provider: RetryProvider, + address: Address, + layout: &StorageLayout, +) -> Result> { + // TODO: Batch request; handle array values + let futures: Vec<_> = layout + .storage + .iter() + .map(|slot| { + let slot_h256 = H256::from_uint(&U256::from_dec_str(&slot.slot)?); + Ok(provider.get_storage_at(address, slot_h256, None)) + }) + .collect::>()?; + + // TODO: Better format values according to their Solidity type + join_all(futures).await.into_iter().map(|value| Ok(format!("{}", value?.into_uint()))).collect() +} + +fn print_storage(layout: StorageLayout, values: Vec, pretty: bool) -> Result<()> { + if !pretty { + println!("{}", serde_json::to_string_pretty(&serde_json::to_value(layout)?)?); + return Ok(()) + } + + let mut table = Table::new(); + table.load_preset(ASCII_MARKDOWN); + table.set_header(vec!["Name", "Type", "Slot", "Offset", "Bytes", "Value", "Contract"]); + + for (slot, value) in layout.storage.into_iter().zip(values) { + let storage_type = layout.types.get(&slot.storage_type); + table.add_row(vec![ + slot.label, + storage_type.as_ref().map_or("?".to_string(), |t| t.label.clone()), + slot.slot, + slot.offset.to_string(), + storage_type.as_ref().map_or("?".to_string(), |t| t.number_of_bytes.clone()), + value, + slot.contract, + ]); + } + + println!("{table}"); + + Ok(()) +} + +fn add_storage_layout_output(project: &mut Project) { + project.artifacts.additional_values.storage_layout = true; + let output_selection = project.artifacts.output_selection(); + project.solc_config.settings.push_all(output_selection); +} + +fn is_storage_layout_empty(storage_layout: &Option) -> bool { + if let Some(ref s) = storage_layout { + s.storage.is_empty() + } else { + true + } +} diff --git a/crates/zkcast/bin/cmd/wallet/mod.rs b/crates/zkcast/bin/cmd/wallet/mod.rs new file mode 100644 index 000000000..98610c1d6 --- /dev/null +++ b/crates/zkcast/bin/cmd/wallet/mod.rs @@ -0,0 +1,350 @@ +use clap::Parser; +use ethers::{ + core::rand::thread_rng, + signers::{LocalWallet, Signer}, + types::{transaction::eip712::TypedData, Address, Signature}, +}; +use eyre::{Context, Result}; +use foundry_cli::opts::{RawWallet, Wallet}; +use foundry_common::fs; +use foundry_config::Config; +use std::path::Path; +use yansi::Paint; +use zkcast::SimpleCast; + +pub mod vanity; +use vanity::VanityArgs; + +/// CLI arguments for `cast wallet`. +#[derive(Debug, Parser)] +pub enum WalletSubcommands { + /// Create a new random keypair. + #[clap(visible_alias = "n")] + New { + /// If provided, then keypair will be written to an encrypted JSON keystore. + path: Option, + + /// Triggers a hidden password prompt for the JSON keystore. + /// + /// Deprecated: prompting for a hidden password is now the default. + #[clap(long, short, requires = "path", conflicts_with = "unsafe_password")] + password: bool, + + /// Password for the JSON keystore in cleartext. + /// + /// This is UNSAFE to use and we recommend using the --password. + #[clap(long, requires = "path", env = "CAST_PASSWORD", value_name = "PASSWORD")] + unsafe_password: Option, + }, + + /// Generate a vanity address. + #[clap(visible_alias = "va")] + Vanity(VanityArgs), + + /// Convert a private key to an address. + #[clap(visible_aliases = &["a", "addr"])] + Address { + /// If provided, the address will be derived from the specified private key. + #[clap( + value_name = "PRIVATE_KEY", + value_parser = foundry_common::clap_helpers::strip_0x_prefix, + )] + private_key_override: Option, + + #[clap(flatten)] + wallet: Wallet, + }, + + /// Sign a message or typed data. + #[clap(visible_alias = "s")] + Sign { + /// The message or typed data to sign. + /// + /// Messages starting with 0x are expected to be hex encoded, + /// which get decoded before being signed. + /// The message will be prefixed with the Ethereum Signed Message header and hashed before + /// signing. + /// + /// Typed data can be provided as a json string or a file name. + /// Use --data flag to denote the message is a string of typed data. + /// Use --data --from-file to denote the message is a file name containing typed data. + /// The data will be combined and hashed using the EIP712 specification before signing. + /// The data should be formatted as JSON. + message: String, + + /// If provided, the message will be treated as typed data. + #[clap(long)] + data: bool, + + /// If provided, the message will be treated as a file name containing typed data. Requires + /// --data. + #[clap(long, requires = "data")] + from_file: bool, + + #[clap(flatten)] + wallet: Wallet, + }, + + /// Verify the signature of a message. + #[clap(visible_alias = "v")] + Verify { + /// The original message. + message: String, + + /// The signature to verify. + signature: Signature, + + /// The address of the message signer. + #[clap(long, short)] + address: Address, + }, + /// Import a private key into an encrypted keystore. + #[clap(visible_alias = "i")] + Import { + /// The name for the account in the keystore. + #[clap(value_name = "ACCOUNT_NAME")] + account_name: String, + /// If provided, keystore will be saved here instead of the default keystores directory + /// (~/.foundry/keystores) + #[clap(long, short)] + keystore_dir: Option, + #[clap(flatten)] + raw_wallet_options: RawWallet, + }, + /// List all the accounts in the keystore default directory + #[clap(visible_alias = "ls")] + List, +} + +impl WalletSubcommands { + pub async fn run(self) -> Result<()> { + match self { + WalletSubcommands::New { path, unsafe_password, .. } => { + let mut rng = thread_rng(); + + if let Some(path) = path { + let path = dunce::canonicalize(path)?; + if !path.is_dir() { + // we require path to be an existing directory + eyre::bail!("`{}` is not a directory", path.display()); + } + + let password = if let Some(password) = unsafe_password { + password + } else { + // if no --unsafe-password was provided read via stdin + rpassword::prompt_password("Enter secret: ")? + }; + + let (wallet, uuid) = + LocalWallet::new_keystore(&path, &mut rng, password, None)?; + + println!("Created new encrypted keystore file: {}", path.join(uuid).display()); + println!("Address: {}", SimpleCast::to_checksum_address(&wallet.address())); + } else { + let wallet = LocalWallet::new(&mut rng); + println!("Successfully created new keypair."); + println!("Address: {}", SimpleCast::to_checksum_address(&wallet.address())); + println!("Private key: 0x{}", hex::encode(wallet.signer().to_bytes())); + } + } + WalletSubcommands::Vanity(cmd) => { + cmd.run()?; + } + WalletSubcommands::Address { wallet, private_key_override } => { + let wallet = private_key_override + .map(|pk| Wallet { + raw: RawWallet { private_key: Some(pk), ..Default::default() }, + ..Default::default() + }) + .unwrap_or(wallet) + .signer(0) + .await?; + let addr = wallet.address(); + println!("{}", SimpleCast::to_checksum_address(&addr)); + } + WalletSubcommands::Sign { message, data, from_file, wallet } => { + let wallet = wallet.signer(0).await?; + let sig = if data { + let typed_data: TypedData = if from_file { + // data is a file name, read json from file + foundry_common::fs::read_json_file(message.as_ref())? + } else { + // data is a json string + serde_json::from_str(&message)? + }; + wallet.sign_typed_data(&typed_data).await? + } else { + wallet.sign_message(Self::hex_str_to_bytes(&message)?).await? + }; + println!("0x{sig}"); + } + WalletSubcommands::Verify { message, signature, address } => { + match signature.verify(Self::hex_str_to_bytes(&message)?, address) { + Ok(_) => { + println!("Validation succeeded. Address {address} signed this message.") + } + Err(_) => { + println!("Validation failed. Address {address} did not sign this message.") + } + } + } + WalletSubcommands::Import { account_name, keystore_dir, raw_wallet_options } => { + // Set up keystore directory + let dir = if let Some(path) = keystore_dir { + Path::new(&path).to_path_buf() + } else { + Config::foundry_keystores_dir().ok_or_else(|| { + eyre::eyre!("Could not find the default keystore directory.") + })? + }; + + fs::create_dir_all(&dir)?; + + // check if account exists already + let keystore_path = Path::new(&dir).join(&account_name); + if keystore_path.exists() { + eyre::bail!("Keystore file already exists at {}", keystore_path.display()); + } + + // get wallet + let wallet: Wallet = raw_wallet_options.into(); + let wallet = wallet.try_resolve_local_wallet()?.ok_or_else(|| { + eyre::eyre!( + "\ +Did you set a private key or mnemonic? +Run `cast wallet import --help` and use the corresponding CLI +flag to set your key via: +--private-key, --mnemonic-path or --interactive." + ) + })?; + + let private_key = wallet.signer().to_bytes(); + let password = rpassword::prompt_password("Enter password: ")?; + + let mut rng = thread_rng(); + eth_keystore::encrypt_key( + &dir, + &mut rng, + private_key, + &password, + Some(&account_name), + )?; + let address = wallet.address(); + let success_message = format!( + "`{}` keystore was saved successfully. Address: {:?}", + &account_name, address, + ); + println!("{}", Paint::green(success_message)); + } + WalletSubcommands::List => { + let default_keystore_dir = Config::foundry_keystores_dir() + .ok_or_else(|| eyre::eyre!("Could not find the default keystore directory."))?; + // Create the keystore directory if it doesn't exist + fs::create_dir_all(&default_keystore_dir)?; + // List all files in keystore directory + let keystore_files: Result, eyre::Report> = + std::fs::read_dir(&default_keystore_dir) + .wrap_err("Failed to read the directory")? + .filter_map(|entry| match entry { + Ok(entry) => { + let path = entry.path(); + if path.is_file() && path.extension().is_none() { + Some(Ok(path)) + } else { + None + } + } + Err(e) => Some(Err(e.into())), + }) + .collect::, eyre::Report>>(); + // Print the names of the keystore files + match keystore_files { + Ok(files) => { + // Print the names of the keystore files + for file in files { + if let Some(file_name) = file.file_name() { + if let Some(name) = file_name.to_str() { + println!("{}", name); + } + } + } + } + Err(e) => return Err(e), + } + } + }; + + Ok(()) + } + + fn hex_str_to_bytes(s: &str) -> Result> { + Ok(match s.strip_prefix("0x") { + Some(data) => hex::decode(data).wrap_err("Could not decode 0x-prefixed string.")?, + None => s.as_bytes().to_vec(), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_parse_wallet_sign_message() { + let args = WalletSubcommands::parse_from(["foundry-cli", "sign", "deadbeef"]); + match args { + WalletSubcommands::Sign { message, data, from_file, .. } => { + assert_eq!(message, "deadbeef".to_string()); + assert!(!data); + assert!(!from_file); + } + _ => panic!("expected WalletSubcommands::Sign"), + } + } + + #[test] + fn can_parse_wallet_sign_hex_message() { + let args = WalletSubcommands::parse_from(["foundry-cli", "sign", "0xdeadbeef"]); + match args { + WalletSubcommands::Sign { message, data, from_file, .. } => { + assert_eq!(message, "0xdeadbeef".to_string()); + assert!(!data); + assert!(!from_file); + } + _ => panic!("expected WalletSubcommands::Sign"), + } + } + + #[test] + fn can_parse_wallet_sign_data() { + let args = WalletSubcommands::parse_from(["foundry-cli", "sign", "--data", "{ ... }"]); + match args { + WalletSubcommands::Sign { message, data, from_file, .. } => { + assert_eq!(message, "{ ... }".to_string()); + assert!(data); + assert!(!from_file); + } + _ => panic!("expected WalletSubcommands::Sign"), + } + } + + #[test] + fn can_parse_wallet_sign_data_file() { + let args = WalletSubcommands::parse_from([ + "foundry-cli", + "sign", + "--data", + "--from-file", + "tests/data/typed_data.json", + ]); + match args { + WalletSubcommands::Sign { message, data, from_file, .. } => { + assert_eq!(message, "tests/data/typed_data.json".to_string()); + assert!(data); + assert!(from_file); + } + _ => panic!("expected WalletSubcommands::Sign"), + } + } +} diff --git a/crates/zkcast/bin/cmd/wallet/vanity.rs b/crates/zkcast/bin/cmd/wallet/vanity.rs new file mode 100644 index 000000000..f047e2d0b --- /dev/null +++ b/crates/zkcast/bin/cmd/wallet/vanity.rs @@ -0,0 +1,339 @@ +use clap::{builder::TypedValueParser, Parser}; +use ethers::{ + core::{k256::ecdsa::SigningKey, rand::thread_rng}, + prelude::{LocalWallet, Signer}, + types::{H160, U256}, + utils::{get_contract_address, secret_key_to_address}, +}; +use eyre::Result; +use zkcast::SimpleCast; + +use rayon::iter::{self, ParallelIterator}; +use regex::Regex; +use std::time::Instant; + +/// Type alias for the result of [generate_wallet]. +pub type GeneratedWallet = (SigningKey, H160); + +/// CLI arguments for `cast wallet vanity`. +#[derive(Debug, Clone, Parser)] +pub struct VanityArgs { + /// Prefix for the vanity address. + #[clap( + long, + required_unless_present = "ends_with", + value_parser = HexAddressValidator, + value_name = "HEX" + )] + pub starts_with: Option, + + /// Suffix for the vanity address. + #[clap(long, value_parser = HexAddressValidator, value_name = "HEX")] + pub ends_with: Option, + + // 2^64-1 is max possible nonce per [eip-2681](https://eips.ethereum.org/EIPS/eip-2681). + /// Generate a vanity contract address created by the generated keypair with the specified + /// nonce. + #[clap(long)] + pub nonce: Option, +} + +impl VanityArgs { + pub fn run(self) -> Result { + let Self { starts_with, ends_with, nonce } = self; + let mut left_exact_hex = None; + let mut left_regex = None; + let mut right_exact_hex = None; + let mut right_regex = None; + + if let Some(prefix) = starts_with { + if let Ok(decoded) = hex::decode(prefix.as_bytes()) { + left_exact_hex = Some(decoded) + } else { + left_regex = Some(Regex::new(&format!(r"^{prefix}"))?); + } + } + + if let Some(suffix) = ends_with { + if let Ok(decoded) = hex::decode(suffix.as_bytes()) { + right_exact_hex = Some(decoded) + } else { + right_regex = Some(Regex::new(&format!(r"{suffix}$"))?); + } + } + + macro_rules! find_vanity { + ($m:ident, $nonce: ident) => { + if let Some(nonce) = $nonce { + find_vanity_address_with_nonce($m, nonce) + } else { + find_vanity_address($m) + } + }; + } + + println!("Starting to generate vanity address..."); + let timer = Instant::now(); + + let wallet = match (left_exact_hex, left_regex, right_exact_hex, right_regex) { + (Some(left), _, Some(right), _) => { + let matcher = HexMatcher { left, right }; + find_vanity!(matcher, nonce) + } + (Some(left), _, _, Some(right)) => { + let matcher = LeftExactRightRegexMatcher { left, right }; + find_vanity!(matcher, nonce) + } + (_, Some(left), _, Some(right)) => { + let matcher = RegexMatcher { left, right }; + find_vanity!(matcher, nonce) + } + (_, Some(left), Some(right), _) => { + let matcher = LeftRegexRightExactMatcher { left, right }; + find_vanity!(matcher, nonce) + } + (Some(left), None, None, None) => { + let matcher = LeftHexMatcher { left }; + find_vanity!(matcher, nonce) + } + (None, None, Some(right), None) => { + let matcher = RightHexMatcher { right }; + find_vanity!(matcher, nonce) + } + (None, Some(re), None, None) => { + let matcher = SingleRegexMatcher { re }; + find_vanity!(matcher, nonce) + } + (None, None, None, Some(re)) => { + let matcher = SingleRegexMatcher { re }; + find_vanity!(matcher, nonce) + } + _ => unreachable!(), + } + .expect("failed to generate vanity wallet"); + + println!( + "Successfully found vanity address in {} seconds.{}{}\nAddress: {}\nPrivate Key: 0x{}", + timer.elapsed().as_secs(), + if nonce.is_some() { "\nContract address: " } else { "" }, + if nonce.is_some() { + SimpleCast::to_checksum_address(&get_contract_address( + wallet.address(), + nonce.unwrap(), + )) + } else { + String::new() + }, + SimpleCast::to_checksum_address(&wallet.address()), + hex::encode(wallet.signer().to_bytes()), + ); + + Ok(wallet) + } +} + +/// Generates random wallets until `matcher` matches the wallet address, returning the wallet. +pub fn find_vanity_address(matcher: T) -> Option { + wallet_generator().find_any(create_matcher(matcher)).map(|(key, _)| key.into()) +} + +/// Generates random wallets until `matcher` matches the contract address created at `nonce`, +/// returning the wallet. +pub fn find_vanity_address_with_nonce( + matcher: T, + nonce: u64, +) -> Option { + let nonce: U256 = nonce.into(); + wallet_generator().find_any(create_nonce_matcher(matcher, nonce)).map(|(key, _)| key.into()) +} + +/// Creates a nonce matcher function, which takes a reference to a [GeneratedWallet] and returns +/// whether it found a match or not by using `matcher`. +#[inline] +pub fn create_matcher(matcher: T) -> impl Fn(&GeneratedWallet) -> bool { + move |(_, addr)| matcher.is_match(addr) +} + +/// Creates a nonce matcher function, which takes a reference to a [GeneratedWallet] and a nonce and +/// returns whether it found a match or not by using `matcher`. +#[inline] +pub fn create_nonce_matcher( + matcher: T, + nonce: U256, +) -> impl Fn(&GeneratedWallet) -> bool { + move |(_, addr)| { + let contract_addr = get_contract_address(*addr, nonce); + matcher.is_match(&contract_addr) + } +} + +/// Returns an infinite parallel iterator which yields a [GeneratedWallet]. +#[inline] +pub fn wallet_generator() -> iter::Map, fn(()) -> GeneratedWallet> { + iter::repeat(()).map(|_| generate_wallet()) +} + +/// Generates a random K-256 signing key and derives its Ethereum address. +pub fn generate_wallet() -> GeneratedWallet { + let key = SigningKey::random(&mut thread_rng()); + let address = secret_key_to_address(&key); + (key, address) +} + +/// A trait to match vanity addresses. +pub trait VanityMatcher: Send + Sync { + fn is_match(&self, addr: &H160) -> bool; +} + +/// Matches start and end hex. +pub struct HexMatcher { + pub left: Vec, + pub right: Vec, +} + +impl VanityMatcher for HexMatcher { + #[inline] + fn is_match(&self, addr: &H160) -> bool { + let bytes = addr.as_bytes(); + bytes.starts_with(&self.left) && bytes.ends_with(&self.right) + } +} + +/// Matches only start hex. +pub struct LeftHexMatcher { + pub left: Vec, +} + +impl VanityMatcher for LeftHexMatcher { + #[inline] + fn is_match(&self, addr: &H160) -> bool { + let bytes = addr.as_bytes(); + bytes.starts_with(&self.left) + } +} + +/// Matches only end hex. +pub struct RightHexMatcher { + pub right: Vec, +} + +impl VanityMatcher for RightHexMatcher { + #[inline] + fn is_match(&self, addr: &H160) -> bool { + let bytes = addr.as_bytes(); + bytes.ends_with(&self.right) + } +} + +/// Matches start hex and end regex. +pub struct LeftExactRightRegexMatcher { + pub left: Vec, + pub right: Regex, +} + +impl VanityMatcher for LeftExactRightRegexMatcher { + #[inline] + fn is_match(&self, addr: &H160) -> bool { + let bytes = addr.as_bytes(); + bytes.starts_with(&self.left) && self.right.is_match(&hex::encode(bytes)) + } +} + +/// Matches start regex and end hex. +pub struct LeftRegexRightExactMatcher { + pub left: Regex, + pub right: Vec, +} + +impl VanityMatcher for LeftRegexRightExactMatcher { + #[inline] + fn is_match(&self, addr: &H160) -> bool { + let bytes = addr.as_bytes(); + bytes.ends_with(&self.right) && self.left.is_match(&hex::encode(bytes)) + } +} + +/// Matches a single regex. +pub struct SingleRegexMatcher { + pub re: Regex, +} + +impl VanityMatcher for SingleRegexMatcher { + #[inline] + fn is_match(&self, addr: &H160) -> bool { + let addr = hex::encode(addr.as_ref()); + self.re.is_match(&addr) + } +} + +/// Matches start and end regex. +pub struct RegexMatcher { + pub left: Regex, + pub right: Regex, +} + +impl VanityMatcher for RegexMatcher { + #[inline] + fn is_match(&self, addr: &H160) -> bool { + let addr = hex::encode(addr.as_ref()); + self.left.is_match(&addr) && self.right.is_match(&addr) + } +} + +/// Parse 40 byte addresses +#[derive(Copy, Clone, Debug, Default)] +pub struct HexAddressValidator; + +impl TypedValueParser for HexAddressValidator { + type Value = String; + + fn parse_ref( + &self, + _cmd: &clap::Command, + _arg: Option<&clap::Arg>, + value: &std::ffi::OsStr, + ) -> Result { + if value.len() > 40 { + return Err(clap::Error::raw( + clap::error::ErrorKind::InvalidValue, + "vanity patterns length exceeded. cannot be more than 40 characters", + )) + } + let value = value.to_str().ok_or_else(|| { + clap::Error::raw(clap::error::ErrorKind::InvalidUtf8, "address must be valid utf8") + })?; + Ok(value.to_string()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn find_simple_vanity_start() { + let args: VanityArgs = VanityArgs::parse_from(["foundry-cli", "--starts-with", "00"]); + let wallet = args.run().unwrap(); + let addr = wallet.address(); + let addr = format!("{addr:x}"); + assert!(addr.starts_with("00")); + } + + #[test] + fn find_simple_vanity_start2() { + let args: VanityArgs = VanityArgs::parse_from(["foundry-cli", "--starts-with", "9"]); + let wallet = args.run().unwrap(); + let addr = wallet.address(); + let addr = format!("{addr:x}"); + assert!(addr.starts_with('9')); + } + + #[test] + fn find_simple_vanity_end() { + let args: VanityArgs = VanityArgs::parse_from(["foundry-cli", "--ends-with", "00"]); + let wallet = args.run().unwrap(); + let addr = wallet.address(); + let addr = format!("{addr:x}"); + assert!(addr.ends_with("00")); + } +} diff --git a/crates/zkcast/bin/cmd/zk_deposit.rs b/crates/zkcast/bin/cmd/zk_deposit.rs new file mode 100644 index 000000000..0b38553fd --- /dev/null +++ b/crates/zkcast/bin/cmd/zk_deposit.rs @@ -0,0 +1,265 @@ +use clap::Parser; +use ethers::types::NameOrAddress; +/// This module handles Bridging assets to ZkSync from Layer 1. +/// It defines the CLI arguments for the `cast zk-deposit` command and provides functionality +/// for depositing assets onto zkSync. +/// +/// The module contains the following components: +/// - `ZkDepositTxArgs`: Struct representing the command line arguments for the `cast +/// zk-deposit` command. It includes parameters such as the destination address, amount to +/// deposit, bridge address, operator tip, zkSync RPC endpoint, and token to bridge. +/// - `ZkDepositTxArgs` implementation: Defines methods for executing the deposit transaction +/// based on the provided command line arguments. +/// - Helper functions: +/// - `get_url_with_port`: Parses a URL string and attaches a default port if one is not +/// specified. +/// - `parse_decimal_u256`: Converts a string to a `U256` number. +use foundry_cli::opts::{TransactionOpts, Wallet}; +use foundry_common::zk_utils::{get_chain, get_private_key, get_rpc_url, get_url_with_port}; +use foundry_config::Chain; +use std::str::FromStr; +use zksync_web3_rs::{ + providers::Provider, + signers::{LocalWallet, Signer}, + types::{Address, H160, U256}, + DepositRequest, ZKSWallet, +}; + +/// Struct to represent the command line arguments for the `cast zk-deposit` command. +/// +/// `ZkDepositTxArgs` contains parameters to be passed via the command line for the `cast +/// zk-deposit` operation. These include the destination of the transaction, the amount to deposit, +/// an optional bridge address, an optional operator tip, the zkSync RPC endpoint, and the token to +/// bridge. +#[derive(Debug, Parser)] +pub struct ZkDepositTxArgs { + /// The destination address of the transaction. + /// This can be either a name or an address. + #[clap( + help = "The destination of the transaction.", + value_parser = NameOrAddress::from_str, + value_name = "TO" + )] + to: NameOrAddress, + + /// The amount of the token to deposit. + #[clap( + help = "Amount of token to deposit.", + value_name = "AMOUNT", + value_parser = parse_decimal_u256 + )] + amount: U256, + + /// An optional address of a custom bridge to call. + #[clap(help = "The address of a custom bridge to call.", value_name = "BRIDGE")] + bridge_address: Option
, + + /// An optional tip that the user can choose to pay in addition to the regular transaction fee. + #[clap( + help = "Optional fee that the user can choose to pay in addition to the regular transaction fee.", + value_name = "TIP" + )] + operator_tip: Option, + + /// Layer 2 gas limit. + #[clap(help = "Layer 2 gas limit", value_name = "L2_GAS_LIMIT")] + l2_gas_limit: Option, + + /// Set the gas per pubdata byte (Optional). + #[clap(help = "Set the gas per pubdata byte (Optional)", value_name = "GAS_PER_PUBDATA_BYTE")] + gas_per_pubdata_byte: Option, + + /// The zkSync RPC Layer 2 endpoint. + /// Can be provided via the env var L2_RPC_URL + /// or --l2-url from the command line. + /// + /// NOTE: For Deposits, L1_RPC_URL, or --l1-url should be set to the Layer 1 RPC URL + #[clap( + env = "L2_RPC_URL", + long, + short = 'z', + help = "The zkSync RPC endpoint.", + value_name = "L2URL" + )] + l2_url: String, + + /// An optional token to bridge. If not specified, ETH is assumed. + #[clap(long, help = "Token to bridge. Leave blank for ETH.", value_name = "TOKEN")] + token: Option
, + + /// Transaction options, such as gas price and gas limit. + #[clap(flatten)] + tx: TransactionOpts, + + /// Ethereum-specific options, such as the network and wallet. + /// We use the options directly, as we want to have a separate URL + #[clap( + env = "L1_RPC_URL", + long = "l1-rpc-url", + help = "The L1 RPC endpoint.", + value_name = "L1_URL" + )] + pub l1_url: Option, + + #[clap(long, env = "CHAIN", value_name = "CHAIN_NAME")] + pub chain: Option, + + #[clap(flatten)] + pub wallet: Wallet, +} + +impl ZkDepositTxArgs { + /// Executes the deposit transaction based on the provided command line arguments. + /// + /// This function first gets the private key from the Ethereum options and the chain. + /// Then, it creates a new wallet using the zkSync HTTP client and signer. + /// Finally, it deposits the specified amount to the target address and prints the transaction + /// hash. + /// + /// # Returns + /// + /// A `Result` which is: + /// - Ok: If the deposit transaction is successfully completed. + /// - Err: If an error occurred during the execution of the deposit transaction. + pub async fn run(self) -> eyre::Result<()> { + let private_key = get_private_key(&self.wallet.raw.private_key)?; + let l1_url = get_rpc_url(&self.l1_url)?; + let l2_url = get_url_with_port(&self.l2_url).expect("Invalid L2_RPC_URL"); + let chain: Chain = get_chain(self.chain)?; + let l1_provider = Provider::try_from(l1_url)?; + let l2_provider = Provider::try_from(l2_url)?; + let wallet = LocalWallet::from_str(&format!("{private_key:?}"))?.with_chain_id(chain); + let zk_wallet = + ZKSWallet::new(wallet, None, Some(l2_provider.clone()), Some(l1_provider.clone())) + .unwrap(); + + // TODO Support different tokens than ETH. + let deposit_request = DepositRequest::new(self.amount) + .to(self.get_to_address()) + .operator_tip(self.operator_tip.unwrap_or(0.into())) + .gas_price(self.tx.gas_price) + .gas_limit(self.tx.gas_limit) + .gas_per_pubdata_byte(self.gas_per_pubdata_byte) + .l2_gas_limit(self.l2_gas_limit); + + println!("Bridging assets...."); + let l1_receipt = zk_wallet.deposit(&deposit_request).await.unwrap(); + println!("Transaction Hash: {:#?}", l1_receipt.transaction_hash); + + Ok(()) + } + + /// Retrieves the 'to' address from the command line arguments. + /// + /// The 'to' address is expected to be a command line argument (`to`). + /// If it is not provided, the function will return an error. + /// + /// # Returns + /// + /// A `H160` which represents the 'to' address. + fn get_to_address(&self) -> H160 { + let to = self.to.as_address().expect("Please enter TO address."); + let deployed_contract = to.as_bytes(); + Address::from_slice(deployed_contract) + } +} + +/// Converts a string to a `U256` number. +/// +/// The function takes a string as input and attempts to parse it as a decimal `U256` number. +/// If the parsing fails, it returns an error. +/// +/// # Parameters +/// +/// - `s`: The string to parse. +/// +/// # Returns +/// +/// A `Result` which is: +/// - Ok: Contains the parsed `U256` number. +/// - Err: Contains a string error message indicating that the parsing failed. +fn parse_decimal_u256(s: &str) -> Result { + match U256::from_dec_str(s) { + Ok(value) => Ok(value), + Err(e) => Err(format!("Failed to parse decimal number: {}", e)), + } +} + +#[cfg(test)] +mod zk_deposit_tests { + use std::env; + + use super::*; + + #[tokio::test] + async fn test_deposit_to_signer_account() { + let amount = U256::from(1); + let private_key = "0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110"; + let l1_url = env::var("L1_RPC_URL").ok(); + if env::var("L2_RPC_URL").is_err() { + // TODO: this test requires a running L2 to pass. + return + } + let l2_url = env::var("L2_RPC_URL").unwrap(); + + let zk_wallet = { + let l1_provider = Provider::try_from(l1_url.unwrap()).unwrap(); + let l2_provider = Provider::try_from(l2_url.clone()).unwrap(); + + let wallet = LocalWallet::from_str(private_key).unwrap(); + ZKSWallet::new(wallet, None, Some(l2_provider), Some(l1_provider)).unwrap() + }; + + let l1_balance_before = zk_wallet.eth_balance().await.unwrap(); + let l2_balance_before = zk_wallet.era_balance().await.unwrap(); + + let zk_deposit_tx_args = { + let to = NameOrAddress::from_str("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049").unwrap(); + let bridge_address = None; + let operator_tip = None; + let token = None; // => Ether. + let tx = TransactionOpts { + gas_limit: None, + gas_price: None, + priority_gas_price: None, + value: None, + nonce: None, + legacy: false, + }; + let l1_url = env::var("L1_RPC_URL").ok(); + let chain = Some(Chain::Id(env::var("CHAIN").unwrap().parse().unwrap())); + let wallet: Wallet = Wallet::parse_from(["foundry-cli", "--private-key", private_key]); + + ZkDepositTxArgs { + to, + amount, + bridge_address, + operator_tip, + l2_url, + token, + tx, + l1_url, + chain, + wallet, + l2_gas_limit: None, + gas_per_pubdata_byte: None, + } + }; + + zk_deposit_tx_args.run().await.unwrap(); + + let l1_balance_after = zk_wallet.eth_balance().await.unwrap(); + let l2_balance_after = zk_wallet.era_balance().await.unwrap(); + println!("L1 balance after: {}", l1_balance_after); + println!("L2 balance after: {}", l2_balance_after); + + assert!( + l1_balance_after <= l1_balance_before - amount, + "Balance on L1 should be decreased" + ); + assert!( + l2_balance_after >= l2_balance_before + amount, + "Balance on L2 should be increased" + ); + } +} diff --git a/crates/zkcast/bin/cmd/zk_send.rs b/crates/zkcast/bin/cmd/zk_send.rs new file mode 100644 index 000000000..cbc572b0d --- /dev/null +++ b/crates/zkcast/bin/cmd/zk_send.rs @@ -0,0 +1,261 @@ +use clap::Parser; +use ethers::types::NameOrAddress; +/// This module handles transactions related to ZkSync. It provides functionality for sending +/// transactions and withdrawing from Layer 2 to Layer 1. The module also defines the +/// command-line arguments for the `cast zk-send` subcommand. +/// +/// The module consists of the following components: +/// - Helper functions for interacting with ZkSync and Ethereum: +/// - `get_url_with_port`: Retrieves the URL with port from the `zk_deposit` module. +/// - `get_chain`, `get_private_key`, `get_rpc_url`: Retrieves chain, private key, and RPC +/// URL from the `zk_utils` module. +/// - Struct `ZkSendTxArgs` representing the command-line arguments for the `cast zk-send` +/// subcommand: +/// - `to`: The destination of the transaction. Accepts address or name. +/// - `sig`: Signature of the function to call when interacting with a contract. +/// - `args`: Arguments for the function being called. +/// - `withdraw`: Flag indicating if the transaction is a Layer 2 to Layer 1 withdrawal. +/// - `token`: Token to bridge in case of withdrawal. Defaults to ETH if not provided. +/// - `amount`: Amount of token to bridge in case of withdrawal. +/// - `tx`: Transaction options such as gas price, nonce, etc. +/// - `eth`: Ethereum options such as sender's address, private key, etc. +/// - Implementation of the `ZkSendTxArgs` struct with methods: +/// - `run`: Executes the command-line arguments, loads the configuration, retrieves private +/// key and RPC URL, prepares and sends the transaction, and handles withdrawals. +/// - `print_receipt`: Prints the receipt of the transaction, including transaction hash, gas +/// used, effective gas price, block number, and deployed contract address. +/// - `get_signer`: Creates a signer from the private key and chain. +/// - `get_to_address`: Retrieves the recipient address of the transaction. +/// - Helper functions: +/// - `parse_decimal_u256`: Parses a decimal string into a `U256` number. +/// - `decode_hex`: Decodes a hexadecimal string into a byte vector. +/// +/// Usage: +/// The `ZkSendTxArgs` struct is used to define and parse command-line arguments for the `cast +/// zk-send` command. It provides the `run` method to execute the transaction and the +/// `print_receipt` method to print the transaction receipt. +/// +/// The `run` method processes the command-line arguments, loads the configuration, retrieves +/// the private key and RPC URL, prepares the transaction, and sends it. If the transaction is +/// a Layer 2 to Layer 1 withdrawal, it handles the withdrawal operation. The method returns an +/// `eyre::Result` indicating the success or failure of the transaction. +/// +/// The `print_receipt` method extracts relevant information from the transaction receipt and +/// prints it to the console. This includes the transaction hash, gas used, effective gas +/// price, block number, and deployed contract address, if applicable. +use foundry_cli::opts::{EthereumOpts, TransactionOpts}; +use foundry_common::zk_utils::{get_chain, get_private_key, get_rpc_url}; +use foundry_config::Config; +use std::str::FromStr; +use zksync_web3_rs::{ + providers::Provider, + signers::{LocalWallet, Signer}, + types::{Address, TransactionReceipt, H160, U256}, + zks_provider::ZKSProvider, + zks_utils::CONTRACT_DEPLOYER_ADDR, + ZKSWallet, +}; + +/// CLI arguments for the `cast zk-send` subcommand. +/// +/// This struct contains all the arguments and options that can be passed to the `zk-send` +/// subcommand. It has methods to run the subcommand and to print the receipt of the transaction. +#[derive(Debug, Parser)] +pub struct ZkSendTxArgs { + /// The destination of the transaction. + /// + /// This field can be populated using the value parser `parse_name_or_address`. + /// If not provided, the value is `None`. + #[clap( + help = "The destination of the transaction.", + value_parser = NameOrAddress::from_str, + value_name = "TO" + )] + to: Option, + + /// Signature of the function to call. + /// This is used when the transaction involves calling a function on a contract. + #[clap(help = "The signature of the function to call.", value_name = "SIG")] + sig: Option, + + /// Arguments for the function being called. + /// These are passed in order to the function specified by `sig`. + #[clap(help = "The arguments of the function to call.", value_name = "ARGS")] + args: Vec, + + /// Flag indicating whether the transaction is a Layer 2 to Layer 1 withdrawal. + #[clap( + long, + short, + help_heading = "Bridging options", + help = "For L2 -> L1 withdrawals.", + group = "bridging" + )] + withdraw: bool, + + /// Token to bridge in case of L2 to L1 withdrawal. + /// If left blank, it will be treated as ETH. + #[clap( + long, + help_heading = "Bridging options", + help = "Token to bridge. Leave blank for ETH.", + value_name = "TOKEN" + )] + token: Option, + + /// Amount of token to bridge in case of L2 to L1 withdrawal. + /// This is required when the `withdraw` flag is set. + #[clap( + long, + short, + help_heading = "Bridging options", + help = "Amount of token to bridge. Required value when bridging", + value_name = "AMOUNT", + requires = "bridging", + value_parser = parse_decimal_u256 + )] + amount: Option, + + /// Options for the transaction such as gas price, nonce etc. + #[clap(flatten)] + tx: TransactionOpts, + + /// Ethereum related options such as sender's address, private key, etc. + #[clap(flatten)] + eth: EthereumOpts, +} + +impl ZkSendTxArgs { + /// Executes the arguments passed through the CLI. + /// + /// This function processes all the arguments and options passed through the CLI. + /// It loads the configuration, retrieves the private key and RPC URL, prepares the transaction + /// and sends it. It also handles the withdraw functionality. + /// + /// # Returns + /// + /// An `eyre::Result` which is: + /// - Ok: If the transaction or withdraw operation is successful. + /// - Err: If any error occurs during the operation. + pub async fn run(self) -> eyre::Result<()> { + let private_key = get_private_key(&self.eth.wallet.raw.private_key)?; + let rpc_url = get_rpc_url(&self.eth.rpc.url)?; + let config = Config::from(&self.eth); + let chain = get_chain(config.chain_id)?; + let provider = Provider::try_from(rpc_url)?; + let to_address = self.get_to_address(); + let wallet = LocalWallet::from_str(&format!("{private_key:?}"))?.with_chain_id(chain); + let zk_wallet = ZKSWallet::new(wallet, None, Some(provider), None); + + // TODO Support different tokens than ETH. + if self.withdraw { + let amount = self + .amount + .expect("Amount was not provided. Use --amount flag (ex. --amount 1000000000 )"); + + match zk_wallet { + Ok(wallet) => { + println!("Bridging assets...."); + let tx_rcpt = wallet + .withdraw(amount, to_address) + .await? + .await? + .ok_or(eyre::eyre!("Error getting the receipt for withdraw"))?; + self.print_receipt(&tx_rcpt); + } + Err(e) => eyre::bail!("error wallet: {e:?}"), + }; + } else { + match zk_wallet { + Ok(wallet) => { + println!("Sending transaction...."); + let sig = self.sig.as_ref().expect("Error: Function Signature is empty"); + let params = (!sig.is_empty()).then_some((&sig[..], self.args.clone())); + + let rcpt = wallet + .get_era_provider()? + .send_eip712( + &wallet.l2_wallet, + to_address, + sig, + params.map(|(_, values)| values), + None, + ) + .await? + .await? + .ok_or(eyre::eyre!("Error getting the receipt for transaction"))?; + + self.print_receipt(&rcpt); + } + Err(e) => eyre::bail!("error wallet: {e:?}"), + }; + } + + Ok(()) + } + + /// Prints the receipt of the transaction. + /// + /// This function extracts the transaction hash, gas used, effective gas price, and block number + /// from the receipt and prints them. It also prints the address of the deployed contract, if + /// any. + /// + /// # Arguments + /// + /// * `rcpt` - A reference to the `TransactionReceipt`. + fn print_receipt(&self, rcpt: &TransactionReceipt) { + let gas_used = rcpt.gas_used.expect("Error retrieving gas used"); + let gas_price = rcpt.effective_gas_price.expect("Error retrieving gas price"); + let block_number = rcpt.block_number.expect("Error retrieving block number"); + + println!("+-------------------------------------------------+"); + println!("Transaction Hash: {:#?}", rcpt.transaction_hash); + println!("Gas used: {:#?}", gas_used); + println!("Effective gas price: {:#?}", gas_price); + println!("Block Number: {:#?}", block_number); + println!("+-------------------------------------------------+"); + + // This will display a deployed contract address if one was deployed via zksend + for log in &rcpt.logs { + if log.address == Address::from_str(CONTRACT_DEPLOYER_ADDR).unwrap() { + let deployed_address = log.topics.get(3).unwrap(); + let deployed_address = Address::from(*deployed_address); + println!("Deployed contract address: {:#?}", deployed_address); + println!("+-------------------------------------------------+"); + } + } + } + + // Gets the recipient address of the transaction. + /// + /// If the `to` field is `None`, it will panic with the message "Enter TO: Address". + /// + /// # Returns + /// + /// A `H160` object that represents the recipient's address. + fn get_to_address(&self) -> H160 { + let to = self.to.as_ref().expect("Enter TO: Address"); + let deployed_contract = to.as_address().expect("Invalid address").as_bytes(); + Address::from_slice(deployed_contract) + } +} + +/// Parses a decimal string into a U256 number. +/// +/// If the string cannot be parsed into a U256, an error message is returned. +/// +/// # Arguments +/// +/// * `s` - A string that represents a decimal number. +/// +/// # Returns +/// +/// A `Result` which is: +/// - Ok: Contains the parsed U256 number. +/// - Err: Contains an error message indicating that the parsing failed. +fn parse_decimal_u256(s: &str) -> Result { + match U256::from_dec_str(s) { + Ok(value) => Ok(value), + Err(e) => Err(format!("Failed to parse decimal number: {}", e)), + } +} diff --git a/crates/zkcast/bin/main.rs b/crates/zkcast/bin/main.rs new file mode 100644 index 000000000..f30773c3e --- /dev/null +++ b/crates/zkcast/bin/main.rs @@ -0,0 +1,505 @@ +use clap::{CommandFactory, Parser}; +use clap_complete::generate; +use ethers::{ + core::types::{BlockId, BlockNumber::Latest, H256}, + providers::Middleware, + types::Address, + utils::keccak256, +}; +use eyre::{Result, WrapErr}; +use foundry_cli::{handler, prompt, stdin, utils}; +use foundry_common::{ + abi::{format_tokens, get_event}, + fs, + selectors::{ + decode_calldata, decode_event_topic, decode_function_selector, import_selectors, + parse_signatures, pretty_calldata, ParsedSignatures, SelectorImportData, + }, +}; +use foundry_config::Config; +use std::time::Instant; +use zkcast::{Cast, SimpleCast}; + +pub mod cmd; +pub mod opts; + +use opts::{Opts, Subcommands, ToBaseArgs}; + +#[tokio::main] +async fn main() -> Result<()> { + utils::load_dotenv(); + handler::install()?; + utils::subscriber(); + utils::enable_paint(); + + let opts = Opts::parse(); + match opts.sub { + // Constants + Subcommands::MaxInt { r#type } => { + println!("{}", SimpleCast::max_int(&r#type)?); + } + Subcommands::MinInt { r#type } => { + println!("{}", SimpleCast::min_int(&r#type)?); + } + Subcommands::MaxUint { r#type } => { + println!("{}", SimpleCast::max_int(&r#type)?); + } + Subcommands::AddressZero => { + println!("{:?}", Address::zero()); + } + Subcommands::HashZero => { + println!("{:?}", H256::zero()); + } + + // Conversions & transformations + Subcommands::FromUtf8 { text } => { + let value = stdin::unwrap(text, false)?; + println!("{}", SimpleCast::from_utf8(&value)); + } + Subcommands::ToAscii { hexdata } => { + let value = stdin::unwrap(hexdata, false)?; + println!("{}", SimpleCast::to_ascii(&value)?); + } + Subcommands::FromFixedPoint { value, decimals } => { + let (value, decimals) = stdin::unwrap2(value, decimals)?; + println!("{}", SimpleCast::from_fixed_point(&value, &decimals)?); + } + Subcommands::ToFixedPoint { value, decimals } => { + let (value, decimals) = stdin::unwrap2(value, decimals)?; + println!("{}", SimpleCast::to_fixed_point(&value, &decimals)?); + } + Subcommands::ConcatHex { data } => { + if data.is_empty() { + let s = stdin::read(true)?; + println!("{}", SimpleCast::concat_hex(s.split_whitespace())) + } else { + println!("{}", SimpleCast::concat_hex(data)) + } + } + Subcommands::FromBin => { + let hex = stdin::read_bytes(false)?; + println!("{}", hex::encode_prefixed(hex)); + } + Subcommands::ToHexdata { input } => { + let value = stdin::unwrap_line(input)?; + let output = match value { + s if s.starts_with('@') => hex::encode(std::env::var(&s[1..])?), + s if s.starts_with('/') => hex::encode(fs::read(s)?), + s => s.split(':').map(|s| s.trim_start_matches("0x").to_lowercase()).collect(), + }; + println!("0x{output}"); + } + Subcommands::ToCheckSumAddress { address } => { + let value = stdin::unwrap_line(address)?; + println!("{}", SimpleCast::to_checksum_address(&value)); + } + Subcommands::ToUint256 { value } => { + let value = stdin::unwrap_line(value)?; + println!("{}", SimpleCast::to_uint256(&value)?); + } + Subcommands::ToInt256 { value } => { + let value = stdin::unwrap_line(value)?; + println!("{}", SimpleCast::to_int256(&value)?); + } + Subcommands::ToUnit { value, unit } => { + let value = stdin::unwrap_line(value)?; + println!("{}", SimpleCast::to_unit(&value, &unit)?); + } + Subcommands::FromWei { value, unit } => { + let value = stdin::unwrap_line(value)?; + println!("{}", SimpleCast::from_wei(&value, &unit)?); + } + Subcommands::ToWei { value, unit } => { + let value = stdin::unwrap_line(value)?; + println!("{}", SimpleCast::to_wei(&value, &unit)?); + } + Subcommands::FromRlp { value } => { + let value = stdin::unwrap_line(value)?; + println!("{}", SimpleCast::from_rlp(value)?); + } + Subcommands::ToRlp { value } => { + let value = stdin::unwrap_line(value)?; + println!("{}", SimpleCast::to_rlp(&value)?); + } + Subcommands::ToHex(ToBaseArgs { value, base_in }) => { + let value = stdin::unwrap_line(value)?; + println!("{}", SimpleCast::to_base(&value, base_in, "hex")?); + } + Subcommands::ToDec(ToBaseArgs { value, base_in }) => { + let value = stdin::unwrap_line(value)?; + println!("{}", SimpleCast::to_base(&value, base_in, "dec")?); + } + Subcommands::ToBase { base: ToBaseArgs { value, base_in }, base_out } => { + let (value, base_out) = stdin::unwrap2(value, base_out)?; + println!("{}", SimpleCast::to_base(&value, base_in, &base_out)?); + } + Subcommands::ToBytes32 { bytes } => { + let value = stdin::unwrap_line(bytes)?; + println!("{}", SimpleCast::to_bytes32(&value)?); + } + Subcommands::FormatBytes32String { string } => { + let value = stdin::unwrap_line(string)?; + println!("{}", SimpleCast::format_bytes32_string(&value)?); + } + Subcommands::ParseBytes32String { bytes } => { + let value = stdin::unwrap_line(bytes)?; + println!("{}", SimpleCast::parse_bytes32_string(&value)?); + } + Subcommands::ParseBytes32Address { bytes } => { + let value = stdin::unwrap_line(bytes)?; + println!("{}", SimpleCast::parse_bytes32_address(&value)?); + } + + // ABI encoding & decoding + Subcommands::AbiDecode { sig, calldata, input } => { + let tokens = SimpleCast::abi_decode(&sig, &calldata, input)?; + let tokens = format_tokens(&tokens); + tokens.for_each(|t| println!("{t}")); + } + Subcommands::AbiEncode { sig, args } => { + println!("{}", SimpleCast::abi_encode(&sig, &args)?); + } + Subcommands::CalldataDecode { sig, calldata } => { + let tokens = SimpleCast::calldata_decode(&sig, &calldata, true)?; + let tokens = format_tokens(&tokens); + tokens.for_each(|t| println!("{t}")); + } + Subcommands::CalldataEncode { sig, args } => { + println!("{}", SimpleCast::calldata_encode(sig, &args)?); + } + Subcommands::Interface(cmd) => cmd.run().await?, + Subcommands::Bind(cmd) => cmd.run().await?, + Subcommands::PrettyCalldata { calldata, offline } => { + let calldata = stdin::unwrap_line(calldata)?; + println!("{}", pretty_calldata(&calldata, offline).await?); + } + Subcommands::Sig { sig, optimize } => { + let sig = stdin::unwrap_line(sig).wrap_err("Failed to read signature")?; + if optimize.is_none() { + println!("{}", SimpleCast::get_selector(&sig, None)?.0); + } else { + println!("Starting to optimize signature..."); + let start_time = Instant::now(); + let (selector, signature) = SimpleCast::get_selector(&sig, optimize)?; + let elapsed_time = start_time.elapsed(); + println!("Successfully generated in {} seconds", elapsed_time.as_secs()); + println!("Selector: {}", selector); + println!("Optimized signature: {}", signature); + } + } + + // Blockchain & RPC queries + Subcommands::AccessList(cmd) => cmd.run().await?, + Subcommands::Age { block, rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + println!( + "{}", + Cast::new(provider).age(block.unwrap_or(BlockId::Number(Latest))).await? + ); + } + Subcommands::Balance { block, who, ether, rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + let value = Cast::new(provider).balance(who, block).await?; + if ether { + println!("{}", SimpleCast::from_wei(&value.to_string(), "eth")?); + } else { + println!("{value}"); + } + } + Subcommands::BaseFee { block, rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + println!( + "{}", + Cast::new(provider).base_fee(block.unwrap_or(BlockId::Number(Latest))).await? + ); + } + Subcommands::Block { block, full, field, json, rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + println!( + "{}", + Cast::new(provider) + .block(block.unwrap_or(BlockId::Number(Latest)), full, field, json) + .await? + ); + } + Subcommands::BlockNumber { rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + println!("{}", Cast::new(provider).block_number().await?); + } + Subcommands::Chain { rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + println!("{}", Cast::new(provider).chain().await?); + } + Subcommands::ChainId { rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + println!("{}", Cast::new(provider).chain_id().await?); + } + Subcommands::Client { rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + println!("{}", provider.client_version().await?); + } + Subcommands::Code { block, who, disassemble, rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + println!("{}", Cast::new(provider).code(who, block, disassemble).await?); + } + Subcommands::Codesize { block, who, rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + println!("{}", Cast::new(provider).codesize(who, block).await?); + } + Subcommands::ComputeAddress { address, nonce, rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + + let address: Address = stdin::unwrap_line(address)?.parse()?; + let computed = Cast::new(&provider).compute_address(address, nonce).await?; + println!("Computed Address: {}", SimpleCast::to_checksum_address(&computed)); + } + Subcommands::Disassemble { bytecode } => { + println!("{}", SimpleCast::disassemble(&bytecode)?); + } + Subcommands::FindBlock(cmd) => cmd.run().await?, + Subcommands::GasPrice { rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + println!("{}", Cast::new(provider).gas_price().await?); + } + Subcommands::Index { key_type, key, slot_number } => { + println!("{}", SimpleCast::index(&key_type, &key, &slot_number)?); + } + Subcommands::Implementation { block, who, rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + println!("{}", Cast::new(provider).implementation(who, block).await?); + } + Subcommands::Admin { block, who, rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + println!("{}", Cast::new(provider).admin(who, block).await?); + } + Subcommands::Nonce { block, who, rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + println!("{}", Cast::new(provider).nonce(who, block).await?); + } + Subcommands::Proof { address, slots, rpc, block } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + let value = provider.get_proof(address, slots, block).await?; + println!("{}", serde_json::to_string(&value)?); + } + Subcommands::Rpc(cmd) => cmd.run().await?, + Subcommands::Storage(cmd) => cmd.run().await?, + + // Calls & transactions + Subcommands::Call(cmd) => cmd.run().await?, + Subcommands::Estimate(cmd) => cmd.run().await?, + Subcommands::PublishTx { raw_tx, cast_async, rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + let cast = Cast::new(&provider); + let pending_tx = cast.publish(raw_tx).await?; + let tx_hash = *pending_tx; + + if cast_async { + println!("{tx_hash:#x}"); + } else { + let receipt = + pending_tx.await?.ok_or_else(|| eyre::eyre!("tx {tx_hash} not found"))?; + println!("{}", serde_json::json!(receipt)); + } + } + Subcommands::Receipt { tx_hash, field, json, cast_async, confirmations, rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + println!( + "{}", + Cast::new(provider) + .receipt(tx_hash, field, confirmations, cast_async, json) + .await? + ); + } + Subcommands::Run(cmd) => cmd.run().await?, + Subcommands::SendTx(cmd) => cmd.run().await?, + Subcommands::Tx { tx_hash, field, raw, json, rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + + // Can use either --raw or specify raw as a field + let raw = raw || field.as_ref().is_some_and(|f| f == "raw"); + + println!("{}", Cast::new(&provider).transaction(tx_hash, field, raw, json).await?) + } + Subcommands::ZkSendTx(cmd) => cmd.run().await?, + Subcommands::ZkDepositTx(cmd) => cmd.run().await?, + + // 4Byte + Subcommands::FourByte { selector } => { + let selector = stdin::unwrap_line(selector)?; + let sigs = decode_function_selector(&selector).await?; + if sigs.is_empty() { + eyre::bail!("No matching function signatures found for selector `{selector}`"); + } + for sig in sigs { + println!("{sig}"); + } + } + Subcommands::FourByteDecode { calldata } => { + let calldata = stdin::unwrap_line(calldata)?; + let sigs = decode_calldata(&calldata).await?; + sigs.iter().enumerate().for_each(|(i, sig)| println!("{}) \"{sig}\"", i + 1)); + + let sig = match sigs.len() { + 0 => eyre::bail!("No signatures found"), + 1 => sigs.get(0).unwrap(), + _ => { + let i: usize = prompt!("Select a function signature by number: ")?; + sigs.get(i - 1).ok_or_else(|| eyre::eyre!("Invalid signature index"))? + } + }; + + let tokens = SimpleCast::calldata_decode(sig, &calldata, true)?; + for token in format_tokens(&tokens) { + println!("{token}"); + } + } + Subcommands::FourByteEvent { topic } => { + let topic = stdin::unwrap_line(topic)?; + let sigs = decode_event_topic(&topic).await?; + if sigs.is_empty() { + eyre::bail!("No matching event signatures found for topic `{topic}`"); + } + for sig in sigs { + println!("{sig}"); + } + } + Subcommands::UploadSignature { signatures } => { + let signatures = stdin::unwrap_vec(signatures)?; + let ParsedSignatures { signatures, abis } = parse_signatures(signatures); + if !abis.is_empty() { + import_selectors(SelectorImportData::Abi(abis)).await?.describe(); + } + if !signatures.is_empty() { + import_selectors(SelectorImportData::Raw(signatures)).await?.describe(); + } + } + + // ENS + Subcommands::Namehash { name } => { + let name = stdin::unwrap_line(name)?; + println!("{}", SimpleCast::namehash(&name)?); + } + Subcommands::LookupAddress { who, rpc, verify } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + + let who = stdin::unwrap_line(who)?; + let name = provider.lookup_address(who).await?; + if verify { + let address = provider.resolve_name(&name).await?; + eyre::ensure!( + address == who, + "Forward lookup verification failed: got `{name:?}`, expected `{who:?}`" + ); + } + println!("{name}"); + } + Subcommands::ResolveName { who, rpc, verify } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + + let who = stdin::unwrap_line(who)?; + let address = provider.resolve_name(&who).await?; + if verify { + let name = provider.lookup_address(address).await?; + assert_eq!( + name, who, + "forward lookup verification failed. got {name}, expected {who}" + ); + } + println!("{}", SimpleCast::to_checksum_address(&address)); + } + + // Misc + Subcommands::Keccak { data } => { + let bytes = match data { + Some(data) => data.into_bytes(), + None => stdin::read_bytes(false)?, + }; + match String::from_utf8(bytes) { + Ok(s) => { + let s = SimpleCast::keccak(&s)?; + println!("{s}"); + } + Err(e) => { + let hash = keccak256(e.as_bytes()); + let s = hex::encode(hash); + println!("0x{s}"); + } + }; + } + Subcommands::SigEvent { event_string } => { + let event_string = stdin::unwrap_line(event_string)?; + let parsed_event = get_event(&event_string)?; + println!("{:?}", parsed_event.signature()); + } + Subcommands::LeftShift { value, bits, base_in, base_out } => { + println!("{}", SimpleCast::left_shift(&value, &bits, base_in, &base_out)?); + } + Subcommands::RightShift { value, bits, base_in, base_out } => { + println!("{}", SimpleCast::right_shift(&value, &bits, base_in, &base_out)?); + } + Subcommands::EtherscanSource { address, directory, etherscan } => { + let config = Config::from(ðerscan); + let chain = config.chain_id.unwrap_or_default(); + let api_key = config.get_etherscan_api_key(Some(chain)).unwrap_or_default(); + let chain = chain.named()?; + match directory { + Some(dir) => { + SimpleCast::expand_etherscan_source_to_directory(chain, address, api_key, dir) + .await? + } + None => { + println!("{}", SimpleCast::etherscan_source(chain, address, api_key).await?); + } + } + } + Subcommands::Create2(cmd) => { + cmd.run()?; + } + Subcommands::Wallet { command } => command.run().await?, + Subcommands::Completions { shell } => { + generate(shell, &mut Opts::command(), "cast", &mut std::io::stdout()) + } + Subcommands::GenerateFigSpec => clap_complete::generate( + clap_complete_fig::Fig, + &mut Opts::command(), + "cast", + &mut std::io::stdout(), + ), + Subcommands::Logs(cmd) => cmd.run().await?, + Subcommands::DecodeTransaction { tx } => { + let tx = stdin::unwrap_line(tx)?; + let (tx, sig) = SimpleCast::decode_raw_transaction(&tx)?; + + // Serialize tx, sig and constructed a merged json string + let mut tx = serde_json::to_value(&tx)?; + let tx_map = tx.as_object_mut().unwrap(); + serde_json::to_value(sig)?.as_object().unwrap().iter().for_each(|(k, v)| { + tx_map.entry(k).or_insert(v.clone()); + }); + + println!("{}", serde_json::to_string_pretty(&tx)?); + } + }; + Ok(()) +} diff --git a/crates/zkcast/bin/opts.rs b/crates/zkcast/bin/opts.rs new file mode 100644 index 000000000..8da1dc356 --- /dev/null +++ b/crates/zkcast/bin/opts.rs @@ -0,0 +1,988 @@ +use crate::cmd::{ + access_list::AccessListArgs, bind::BindArgs, call::CallArgs, create2::Create2Args, + estimate::EstimateArgs, find_block::FindBlockArgs, interface::InterfaceArgs, logs::LogsArgs, + rpc::RpcArgs, run::RunArgs, send::SendTxArgs, storage::StorageArgs, wallet::WalletSubcommands, + zk_deposit::ZkDepositTxArgs, zk_send::ZkSendTxArgs, +}; +use clap::{Parser, Subcommand, ValueHint}; +use ethers::{ + abi::ethabi::ethereum_types::BigEndianHash, + types::{serde_helpers::Numeric, Address, BlockId, NameOrAddress, H256, U256}, +}; +use eyre::Result; +use foundry_cli::{ + opts::{EtherscanOpts, RpcOpts}, + utils::parse_u256, +}; +use std::{path::PathBuf, str::FromStr}; + +const VERSION_MESSAGE: &str = concat!( + env!("CARGO_PKG_VERSION"), + " (", + env!("VERGEN_GIT_SHA"), + " ", + env!("VERGEN_BUILD_TIMESTAMP"), + ")" +); + +#[derive(Debug, Parser)] +#[clap(name = "cast", version = VERSION_MESSAGE)] +pub struct Opts { + #[clap(subcommand)] + pub sub: Subcommands, +} + +/// Perform Ethereum RPC calls from the comfort of your command line. +#[derive(Debug, Subcommand)] +#[clap( + after_help = "Find more information in the book: http://book.getfoundry.sh/reference/cast/cast.html", + next_display_order = None +)] +pub enum Subcommands { + /// Prints the maximum value of the given integer type. + #[clap(visible_aliases = &["--max-int", "maxi"])] + MaxInt { + /// The integer type to get the maximum value of. + #[clap(default_value = "int256")] + r#type: String, + }, + + /// Prints the minimum value of the given integer type. + #[clap(visible_aliases = &["--min-int", "mini"])] + MinInt { + /// The integer type to get the minimum value of. + #[clap(default_value = "int256")] + r#type: String, + }, + + /// Prints the maximum value of the given integer type. + #[clap(visible_aliases = &["--max-uint", "maxu"])] + MaxUint { + /// The unsigned integer type to get the maximum value of. + #[clap(default_value = "uint256")] + r#type: String, + }, + + /// Prints the zero address. + #[clap(visible_aliases = &["--address-zero", "az"])] + AddressZero, + + /// Prints the zero hash. + #[clap(visible_aliases = &["--hash-zero", "hz"])] + HashZero, + + /// Convert UTF8 text to hex. + #[clap( + visible_aliases = &[ + "--from-ascii", + "--from-utf8", + "from-ascii", + "fu", + "fa"] + )] + FromUtf8 { + /// The text to convert. + text: Option, + }, + + /// Concatenate hex strings. + #[clap(visible_aliases = &["--concat-hex", "ch"])] + ConcatHex { + /// The data to concatenate. + data: Vec, + }, + + /// "Convert binary data into hex data." + #[clap(visible_aliases = &["--from-bin", "from-binx", "fb"])] + FromBin, + + /// Normalize the input to lowercase, 0x-prefixed hex. + /// + /// The input can be: + /// - mixed case hex with or without 0x prefix + /// - 0x prefixed hex, concatenated with a ':' + /// - an absolute path to file + /// - @tag, where the tag is defined in an environment variable + #[clap(visible_aliases = &["--to-hexdata", "thd", "2hd"])] + ToHexdata { + /// The input to normalize. + input: Option, + }, + + /// Convert an address to a checksummed format (EIP-55). + #[clap( + visible_aliases = &["--to-checksum-address", + "--to-checksum", + "to-checksum", + "ta", + "2a"] + )] + ToCheckSumAddress { + /// The address to convert. + address: Option
, + }, + + /// Convert hex data to an ASCII string. + #[clap(visible_aliases = &["--to-ascii", "tas", "2as"])] + ToAscii { + /// The hex data to convert. + hexdata: Option, + }, + + /// Convert a fixed point number into an integer. + #[clap(visible_aliases = &["--from-fix", "ff"])] + FromFixedPoint { + /// The number of decimals to use. + decimals: Option, + + /// The value to convert. + #[clap(allow_hyphen_values = true)] + value: Option, + }, + + /// Right-pads hex data to 32 bytes. + #[clap(visible_aliases = &["--to-bytes32", "tb", "2b"])] + ToBytes32 { + /// The hex data to convert. + bytes: Option, + }, + + /// Convert an integer into a fixed point number. + #[clap(visible_aliases = &["--to-fix", "tf", "2f"])] + ToFixedPoint { + /// The number of decimals to use. + decimals: Option, + + /// The value to convert. + #[clap(allow_hyphen_values = true)] + value: Option, + }, + + /// Convert a number to a hex-encoded uint256. + #[clap(name = "to-uint256", visible_aliases = &["--to-uint256", "tu", "2u"])] + ToUint256 { + /// The value to convert. + value: Option, + }, + + /// Convert a number to a hex-encoded int256. + #[clap(name = "to-int256", visible_aliases = &["--to-int256", "ti", "2i"])] + ToInt256 { + /// The value to convert. + value: Option, + }, + + /// Perform a left shifting operation + #[clap(name = "shl")] + LeftShift { + /// The value to shift. + value: String, + + /// The number of bits to shift. + bits: String, + + /// The input base. + #[clap(long)] + base_in: Option, + + /// The output base. + #[clap(long, default_value = "16")] + base_out: String, + }, + + /// Perform a right shifting operation + #[clap(name = "shr")] + RightShift { + /// The value to shift. + value: String, + + /// The number of bits to shift. + bits: String, + + /// The input base, + #[clap(long)] + base_in: Option, + + /// The output base, + #[clap(long, default_value = "16")] + base_out: String, + }, + + /// Convert an ETH amount into another unit (ether, gwei or wei). + /// + /// Examples: + /// - 1ether wei + /// - "1 ether" wei + /// - 1ether + /// - 1 gwei + /// - 1gwei ether + #[clap(visible_aliases = &["--to-unit", "tun", "2un"])] + ToUnit { + /// The value to convert. + value: Option, + + /// The unit to convert to (ether, gwei, wei). + #[clap(default_value = "wei")] + unit: String, + }, + + /// Convert an ETH amount to wei. + /// + /// Consider using --to-unit. + #[clap(visible_aliases = &["--to-wei", "tw", "2w"])] + ToWei { + /// The value to convert. + #[clap(allow_hyphen_values = true)] + value: Option, + + /// The unit to convert from (ether, gwei, wei). + #[clap(default_value = "eth")] + unit: String, + }, + + /// Convert wei into an ETH amount. + /// + /// Consider using --to-unit. + #[clap(visible_aliases = &["--from-wei", "fw"])] + FromWei { + /// The value to convert. + #[clap(allow_hyphen_values = true)] + value: Option, + + /// The unit to convert from (ether, gwei, wei). + #[clap(default_value = "eth")] + unit: String, + }, + + /// RLP encodes hex data, or an array of hex data + #[clap(visible_aliases = &["--to-rlp"])] + ToRlp { + /// The value to convert. + value: Option, + }, + + /// Decodes RLP encoded data. + /// + /// Input must be hexadecimal. + #[clap(visible_aliases = &["--from-rlp"])] + FromRlp { + /// The value to convert. + value: Option, + }, + + /// Converts a number of one base to another + #[clap(visible_aliases = &["--to-hex", "th", "2h"])] + ToHex(ToBaseArgs), + + /// Converts a number of one base to decimal + #[clap(visible_aliases = &["--to-dec", "td", "2d"])] + ToDec(ToBaseArgs), + + /// Converts a number of one base to another + #[clap( + visible_aliases = &["--to-base", + "--to-radix", + "to-radix", + "tr", + "2r"] + )] + ToBase { + #[clap(flatten)] + base: ToBaseArgs, + + /// The output base. + #[clap(value_name = "BASE")] + base_out: Option, + }, + /// Create an access list for a transaction. + #[clap(visible_aliases = &["ac", "acl"])] + AccessList(AccessListArgs), + /// Get logs by signature or topic. + #[clap(visible_alias = "l")] + Logs(LogsArgs), + /// Get information about a block. + #[clap(visible_alias = "bl")] + Block { + /// The block height to query at. + /// + /// Can also be the tags earliest, finalized, safe, latest, or pending. + block: Option, + + /// If specified, only get the given field of the block. + #[clap(long, short)] + field: Option, + + #[clap(long, env = "CAST_FULL_BLOCK")] + full: bool, + + /// Print the block as JSON. + #[clap(long, short, help_heading = "Display options")] + json: bool, + + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Get the latest block number. + #[clap(visible_alias = "bn")] + BlockNumber { + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Perform a call on an account without publishing a transaction. + #[clap(visible_alias = "c")] + Call(CallArgs), + + /// ABI-encode a function with arguments. + #[clap(name = "calldata", visible_alias = "cd")] + CalldataEncode { + /// The function signature in the format `()()` + sig: String, + + /// The arguments to encode. + #[clap(allow_hyphen_values = true)] + args: Vec, + }, + + /// Get the symbolic name of the current chain. + Chain { + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Get the Ethereum chain ID. + #[clap(visible_aliases = &["ci", "cid"])] + ChainId { + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Get the current client version. + #[clap(visible_alias = "cl")] + Client { + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Compute the contract address from a given nonce and deployer address. + #[clap(visible_alias = "ca")] + ComputeAddress { + /// The deployer address. + address: Option, + + /// The nonce of the deployer address. + #[clap(long, value_parser = parse_u256)] + nonce: Option, + + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Disassembles hex encoded bytecode into individual / human readable opcodes + #[clap(visible_alias = "da")] + Disassemble { + /// The hex encoded bytecode. + bytecode: String, + }, + + /// Calculate the ENS namehash of a name. + #[clap(visible_aliases = &["na", "nh"])] + Namehash { name: Option }, + + /// Get information about a transaction. + #[clap(visible_alias = "t")] + Tx { + /// The transaction hash. + tx_hash: String, + + /// If specified, only get the given field of the transaction. If "raw", the RLP encoded + /// transaction will be printed. + field: Option, + + /// Print the raw RLP encoded transaction. + #[clap(long, conflicts_with = "field")] + raw: bool, + + /// Print as JSON. + #[clap(long, short, help_heading = "Display options")] + json: bool, + + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Get the transaction receipt for a transaction. + #[clap(visible_alias = "re")] + Receipt { + /// The transaction hash. + tx_hash: String, + + /// If specified, only get the given field of the transaction. + field: Option, + + /// The number of confirmations until the receipt is fetched + #[clap(long, default_value = "1")] + confirmations: usize, + + /// Exit immediately if the transaction was not found. + #[clap(long = "async", env = "CAST_ASYNC", name = "async", alias = "cast-async")] + cast_async: bool, + + /// Print as JSON. + #[clap(long, short, help_heading = "Display options")] + json: bool, + + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Sign and publish a transaction. + #[clap(name = "send", visible_alias = "s")] + SendTx(SendTxArgs), + + #[clap(name = "zk-send")] + #[clap(visible_aliases = ["zks", "zksend"])] + #[clap(about = "Sign and publish a zksync transaction.")] + ZkSendTx(ZkSendTxArgs), + + #[clap(name = "zk-deposit")] + #[clap(visible_aliases = ["zkd", "zkdeposit"])] + #[clap(about = "Bridge Assets from L1 to L2.")] + ZkDepositTx(ZkDepositTxArgs), + + /// Publish a raw transaction to the network. + #[clap(name = "publish", visible_alias = "p")] + PublishTx { + /// The raw transaction + raw_tx: String, + + /// Only print the transaction hash and exit immediately. + #[clap(long = "async", env = "CAST_ASYNC", name = "async", alias = "cast-async")] + cast_async: bool, + + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Estimate the gas cost of a transaction. + #[clap(visible_alias = "e")] + Estimate(EstimateArgs), + + /// Decode ABI-encoded input data. + /// + /// Similar to `abi-decode --input`, but function selector MUST be prefixed in `calldata` + /// string + #[clap(visible_aliases = &["--calldata-decode","cdd"])] + CalldataDecode { + /// The function signature in the format `()()`. + sig: String, + + /// The ABI-encoded calldata. + calldata: String, + }, + + /// Decode ABI-encoded input or output data. + /// + /// Defaults to decoding output data. To decode input data pass --input. + /// + /// When passing `--input`, function selector must NOT be prefixed in `calldata` string + #[clap(name = "abi-decode", visible_aliases = &["ad", "--abi-decode"])] + AbiDecode { + /// The function signature in the format `()()`. + sig: String, + + /// The ABI-encoded calldata. + calldata: String, + + /// Whether to decode the input or output data. + #[clap(long, short, help_heading = "Decode input data instead of output data")] + input: bool, + }, + + /// ABI encode the given function argument, excluding the selector. + #[clap(visible_alias = "ae")] + AbiEncode { + /// The function signature. + sig: String, + + /// The arguments of the function. + #[clap(allow_hyphen_values = true)] + args: Vec, + }, + + /// Compute the storage slot for an entry in a mapping. + #[clap(visible_alias = "in")] + Index { + /// The mapping key type. + key_type: String, + + /// The mapping key. + key: String, + + /// The storage slot of the mapping. + slot_number: String, + }, + + /// Fetch the EIP-1967 implementation account + #[clap(visible_alias = "impl")] + Implementation { + /// The block height to query at. + /// + /// Can also be the tags earliest, finalized, safe, latest, or pending. + #[clap(long, short = 'B')] + block: Option, + + /// The address to get the nonce for. + #[clap(value_parser = NameOrAddress::from_str)] + who: NameOrAddress, + + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Fetch the EIP-1967 admin account + #[clap(visible_alias = "adm")] + Admin { + /// The block height to query at. + /// + /// Can also be the tags earliest, finalized, safe, latest, or pending. + #[clap(long, short = 'B')] + block: Option, + + /// The address to get the nonce for. + #[clap(value_parser = NameOrAddress::from_str)] + who: NameOrAddress, + + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Get the function signatures for the given selector from https://openchain.xyz. + #[clap(name = "4byte", visible_aliases = &["4", "4b"])] + FourByte { + /// The function selector. + selector: Option, + }, + + /// Decode ABI-encoded calldata using https://openchain.xyz. + #[clap(name = "4byte-decode", visible_aliases = &["4d", "4bd"])] + FourByteDecode { + /// The ABI-encoded calldata. + calldata: Option, + }, + + /// Get the event signature for a given topic 0 from https://openchain.xyz. + #[clap(name = "4byte-event", visible_aliases = &["4e", "4be"])] + FourByteEvent { + /// Topic 0 + #[clap(value_name = "TOPIC_0")] + topic: Option, + }, + + /// Upload the given signatures to https://openchain.xyz. + /// + /// Example inputs: + /// - "transfer(address,uint256)" + /// - "function transfer(address,uint256)" + /// - "function transfer(address,uint256)" "event Transfer(address,address,uint256)" + /// - "./out/Contract.sol/Contract.json" + #[clap(visible_aliases = &["ups"])] + UploadSignature { + /// The signatures to upload. + /// + /// Prefix with 'function', 'event', or 'error'. Defaults to function if no prefix given. + /// Can also take paths to contract artifact JSON. + signatures: Vec, + }, + + /// Pretty print calldata. + /// + /// Tries to decode the calldata using https://openchain.xyz unless --offline is passed. + #[clap(visible_alias = "pc")] + PrettyCalldata { + /// The calldata. + calldata: Option, + + /// Skip the https://openchain.xyz lookup. + #[clap(long, short)] + offline: bool, + }, + + /// Get the timestamp of a block. + #[clap(visible_alias = "a")] + Age { + /// The block height to query at. + /// + /// Can also be the tags earliest, finalized, safe, latest, or pending. + block: Option, + + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Get the balance of an account in wei. + #[clap(visible_alias = "b")] + Balance { + /// The block height to query at. + /// + /// Can also be the tags earliest, finalized, safe, latest, or pending. + #[clap(long, short = 'B')] + block: Option, + + /// The account to query. + #[clap(value_parser = NameOrAddress::from_str)] + who: NameOrAddress, + + /// Format the balance in ether. + #[clap(long, short)] + ether: bool, + + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Get the basefee of a block. + #[clap(visible_aliases = &["ba", "fee", "basefee"])] + BaseFee { + /// The block height to query at. + /// + /// Can also be the tags earliest, finalized, safe, latest, or pending. + block: Option, + + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Get the runtime bytecode of a contract. + #[clap(visible_alias = "co")] + Code { + /// The block height to query at. + /// + /// Can also be the tags earliest, finalized, safe, latest, or pending. + #[clap(long, short = 'B')] + block: Option, + + /// The contract address. + #[clap(value_parser = NameOrAddress::from_str)] + who: NameOrAddress, + + /// Disassemble bytecodes into individual opcodes. + #[clap(long, short)] + disassemble: bool, + + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Get the runtime bytecode size of a contract. + #[clap(visible_alias = "cs")] + Codesize { + /// The block height to query at. + /// + /// Can also be the tags earliest, finalized, safe, latest, or pending. + #[clap(long, short = 'B')] + block: Option, + + /// The contract address. + #[clap(value_parser = NameOrAddress::from_str)] + who: NameOrAddress, + + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Get the current gas price. + #[clap(visible_alias = "g")] + GasPrice { + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Generate event signatures from event string. + #[clap(visible_alias = "se")] + SigEvent { + /// The event string. + event_string: Option, + }, + + /// Hash arbitrary data using Keccak-256. + #[clap(visible_alias = "k")] + Keccak { + /// The data to hash. + data: Option, + }, + + /// Perform an ENS lookup. + #[clap(visible_alias = "rn")] + ResolveName { + /// The name to lookup. + who: Option, + + /// Perform a reverse lookup to verify that the name is correct. + #[clap(long, short)] + verify: bool, + + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Perform an ENS reverse lookup. + #[clap(visible_alias = "la")] + LookupAddress { + /// The account to perform the lookup for. + who: Option
, + + /// Perform a normal lookup to verify that the address is correct. + #[clap(long, short)] + verify: bool, + + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Get the raw value of a contract's storage slot. + #[clap(visible_alias = "st")] + Storage(StorageArgs), + + /// Generate a storage proof for a given storage slot. + #[clap(visible_alias = "pr")] + Proof { + /// The contract address. + #[clap(value_parser = NameOrAddress::from_str)] + address: NameOrAddress, + + /// The storage slot numbers (hex or decimal). + #[clap(value_parser = parse_slot)] + slots: Vec, + + /// The block height to query at. + /// + /// Can also be the tags earliest, finalized, safe, latest, or pending. + #[clap(long, short = 'B')] + block: Option, + + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Get the nonce for an account. + #[clap(visible_alias = "n")] + Nonce { + /// The block height to query at. + /// + /// Can also be the tags earliest, finalized, safe, latest, or pending. + #[clap(long, short = 'B')] + block: Option, + + /// The address to get the nonce for. + #[clap(value_parser = NameOrAddress::from_str)] + who: NameOrAddress, + + #[clap(flatten)] + rpc: RpcOpts, + }, + + /// Get the source code of a contract from Etherscan. + #[clap(visible_aliases = &["et", "src"])] + EtherscanSource { + /// The contract's address. + address: String, + + /// The output directory to expand source tree into. + #[clap(short, value_hint = ValueHint::DirPath)] + directory: Option, + + #[clap(flatten)] + etherscan: EtherscanOpts, + }, + + /// Wallet management utilities. + #[clap(visible_alias = "w")] + Wallet { + #[clap(subcommand)] + command: WalletSubcommands, + }, + + /// Generate a Solidity interface from a given ABI. + /// + /// Currently does not support ABI encoder v2. + #[clap(visible_alias = "i")] + Interface(InterfaceArgs), + + /// Generate a rust binding from a given ABI. + #[clap(visible_alias = "bi")] + Bind(BindArgs), + + /// Get the selector for a function. + #[clap(visible_alias = "si")] + Sig { + /// The function signature, e.g. transfer(address,uint256). + sig: Option, + + /// Optimize signature to contain provided amount of leading zeroes in selector. + optimize: Option, + }, + + /// Generate a deterministic contract address using CREATE2. + #[clap(visible_alias = "c2")] + Create2(Create2Args), + + /// Get the block number closest to the provided timestamp. + #[clap(visible_alias = "f")] + FindBlock(FindBlockArgs), + + /// Generate shell completions script. + #[clap(visible_alias = "com")] + Completions { + #[clap(value_enum)] + shell: clap_complete::Shell, + }, + + /// Generate Fig autocompletion spec. + #[clap(visible_alias = "fig")] + GenerateFigSpec, + + /// Runs a published transaction in a local environment and prints the trace. + #[clap(visible_alias = "r")] + Run(RunArgs), + + /// Perform a raw JSON-RPC request. + #[clap(visible_alias = "rp")] + Rpc(RpcArgs), + + /// Formats a string into bytes32 encoding. + #[clap(name = "format-bytes32-string", visible_aliases = &["--format-bytes32-string"])] + FormatBytes32String { + /// The string to format. + string: Option, + }, + + /// Parses a string from bytes32 encoding. + #[clap(name = "parse-bytes32-string", visible_aliases = &["--parse-bytes32-string"])] + ParseBytes32String { + /// The string to parse. + bytes: Option, + }, + #[clap(name = "parse-bytes32-address", visible_aliases = &["--parse-bytes32-address"])] + #[clap(about = "Parses a checksummed address from bytes32 encoding.")] + ParseBytes32Address { + #[clap(value_name = "BYTES")] + bytes: Option, + }, + + /// Decodes a raw signed EIP 2718 typed transaction + #[clap(visible_alias = "dt")] + DecodeTransaction { tx: Option }, +} + +/// CLI arguments for `cast --to-base`. +#[derive(Debug, Parser)] +pub struct ToBaseArgs { + /// The value to convert. + #[clap(allow_hyphen_values = true)] + pub value: Option, + + /// The input base. + #[clap(long, short = 'i')] + pub base_in: Option, +} + +pub fn parse_slot(s: &str) -> Result { + Numeric::from_str(s) + .map_err(|e| eyre::eyre!("Could not parse slot number: {e}")) + .map(|n| H256::from_uint(&n.into())) +} + +#[cfg(test)] +mod tests { + use super::*; + use ethers::types::BlockNumber; + use zkcast::SimpleCast; + + #[test] + fn parse_call_data() { + let args: Opts = Opts::parse_from([ + "foundry-cli", + "calldata", + "f()", + "5c9d55b78febcc2061715ba4f57ecf8ea2711f2c", + "2", + ]); + match args.sub { + Subcommands::CalldataEncode { args, .. } => { + assert_eq!( + args, + vec!["5c9d55b78febcc2061715ba4f57ecf8ea2711f2c".to_string(), "2".to_string()] + ) + } + _ => unreachable!(), + }; + } + + // + #[test] + fn parse_signature() { + let args: Opts = Opts::parse_from([ + "foundry-cli", + "sig", + "__$_$__$$$$$__$$_$$$_$$__$$___$$(address,address,uint256)", + ]); + match args.sub { + Subcommands::Sig { sig, .. } => { + let sig = sig.unwrap(); + assert_eq!( + sig, + "__$_$__$$$$$__$$_$$$_$$__$$___$$(address,address,uint256)".to_string() + ); + + let selector = SimpleCast::get_selector(&sig, None).unwrap(); + assert_eq!(selector.0, "0x23b872dd".to_string()); + } + _ => unreachable!(), + }; + } + + #[test] + fn parse_block_ids() { + struct TestCase { + input: String, + expect: BlockId, + } + + let test_cases = [ + TestCase { + input: "0".to_string(), + expect: BlockId::Number(BlockNumber::Number(0u64.into())), + }, + TestCase { + input: "0x56462c47c03df160f66819f0a79ea07def1569f8aac0fe91bb3a081159b61b4a" + .to_string(), + expect: BlockId::Hash( + "0x56462c47c03df160f66819f0a79ea07def1569f8aac0fe91bb3a081159b61b4a" + .parse() + .unwrap(), + ), + }, + TestCase { input: "latest".to_string(), expect: BlockId::Number(BlockNumber::Latest) }, + TestCase { + input: "earliest".to_string(), + expect: BlockId::Number(BlockNumber::Earliest), + }, + TestCase { + input: "pending".to_string(), + expect: BlockId::Number(BlockNumber::Pending), + }, + TestCase { input: "safe".to_string(), expect: BlockId::Number(BlockNumber::Safe) }, + TestCase { + input: "finalized".to_string(), + expect: BlockId::Number(BlockNumber::Finalized), + }, + ]; + + for test in test_cases { + let result: BlockId = test.input.parse().unwrap(); + assert_eq!(result, test.expect); + } + } +} diff --git a/crates/zkcast/build.rs b/crates/zkcast/build.rs new file mode 100644 index 000000000..c2f550fb6 --- /dev/null +++ b/crates/zkcast/build.rs @@ -0,0 +1,3 @@ +fn main() { + vergen::EmitBuilder::builder().build_timestamp().git_sha(true).emit().unwrap(); +} diff --git a/crates/zkcast/src/base.rs b/crates/zkcast/src/base.rs new file mode 100644 index 000000000..016d7402a --- /dev/null +++ b/crates/zkcast/src/base.rs @@ -0,0 +1,800 @@ +use ethers_core::{ + abi::ethereum_types::FromStrRadixErrKind, + types::{Sign, I256, U256}, + utils::ParseUnits, +}; +use eyre::Result; +use std::{ + convert::{Infallible, TryFrom, TryInto}, + fmt::{Binary, Debug, Display, Formatter, LowerHex, Octal, Result as FmtResult, UpperHex}, + iter::FromIterator, + num::IntErrorKind, + str::FromStr, +}; + +/* -------------------------------------------- Base -------------------------------------------- */ + +/// Represents a number's [radix] or base. Currently it supports the same bases that [std::fmt] +/// supports. +/// +/// [radix]: https://en.wikipedia.org/wiki/Radix +#[repr(u32)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum Base { + Binary = 2, + Octal = 8, + #[default] + Decimal = 10, + Hexadecimal = 16, +} + +impl Display for Base { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + Display::fmt(&(*self as u32), f) + } +} + +impl FromStr for Base { + type Err = eyre::Report; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "2" | "b" | "bin" | "binary" => Ok(Self::Binary), + "8" | "o" | "oct" | "octal" => Ok(Self::Octal), + "10" | "d" | "dec" | "decimal" => Ok(Self::Decimal), + "16" | "h" | "hex" | "hexadecimal" => Ok(Self::Hexadecimal), + s => Err(eyre::eyre!( + r#"Invalid base "{}". Possible values: +2, b, bin, binary +8, o, oct, octal +10, d, dec, decimal +16, h, hex, hexadecimal"#, + s + )), + } + } +} + +impl TryFrom for Base { + type Error = eyre::Report; + + fn try_from(s: String) -> Result { + Self::from_str(&s) + } +} + +impl TryFrom for Base { + type Error = eyre::Report; + + fn try_from(n: u32) -> Result { + match n { + 2 => Ok(Self::Binary), + 8 => Ok(Self::Octal), + 10 => Ok(Self::Decimal), + 16 => Ok(Self::Hexadecimal), + n => Err(eyre::eyre!("Invalid base \"{}\". Possible values: 2, 8, 10, 16", n)), + } + } +} + +impl TryFrom for Base { + type Error = eyre::Report; + + fn try_from(n: I256) -> Result { + Self::try_from(n.low_u32()) + } +} + +impl TryFrom for Base { + type Error = eyre::Report; + + fn try_from(n: U256) -> Result { + Self::try_from(n.low_u32()) + } +} + +impl From for u32 { + fn from(b: Base) -> Self { + b as u32 + } +} + +impl From for I256 { + fn from(b: Base) -> Self { + Self::from(b as u32) + } +} + +impl From for U256 { + fn from(b: Base) -> Self { + Self::from(b as u32) + } +} + +impl From for String { + fn from(b: Base) -> Self { + b.to_string() + } +} + +impl Base { + pub fn unwrap_or_detect(base: Option, s: impl AsRef) -> Result { + match base { + Some(base) => base.try_into(), + None => Self::detect(s), + } + } + + /// Try parsing a number's base from a string. + pub fn detect(s: impl AsRef) -> Result { + let s = s.as_ref(); + match s { + // Ignore sign + _ if s.starts_with(['+', '-']) => Self::detect(&s[1..]), + // Verify binary and octal values with u128::from_str_radix as U256 does not support + // them; + // assume overflows are within u128::MAX and U256::MAX, we're not using the parsed value + // anyway; + // strip prefix when using u128::from_str_radix because it does not recognize it as + // valid. + _ if s.starts_with("0b") => match u128::from_str_radix(&s[2..], 2) { + Ok(_) => Ok(Self::Binary), + Err(e) => match e.kind() { + IntErrorKind::PosOverflow => Ok(Self::Binary), + _ => Err(eyre::eyre!("could not parse binary value: {}", e)), + }, + }, + _ if s.starts_with("0o") => match u128::from_str_radix(&s[2..], 8) { + Ok(_) => Ok(Self::Octal), + Err(e) => match e.kind() { + IntErrorKind::PosOverflow => Ok(Self::Octal), + _ => Err(eyre::eyre!("could not parse octal value: {}", e)), + }, + }, + _ if s.starts_with("0x") => match U256::from_str_radix(s, 16) { + Ok(_) => Ok(Self::Hexadecimal), + Err(e) => match e.kind() { + FromStrRadixErrKind::InvalidLength => { + Err(eyre::eyre!("number must be less than U256::MAX ({})", U256::MAX)) + } + _ => Err(eyre::eyre!("could not parse hexadecimal value: {}", e)), + }, + }, + // No prefix => first try parsing as decimal + _ => match U256::from_str_radix(s, 10) { + Ok(_) => { + // Can be both, ambiguous but default to Decimal + + // Err(eyre::eyre!("Could not autodetect base: input could be decimal or + // hexadecimal. Please prepend with 0x if the input is hexadecimal, or specify a + // --base-in parameter.")) + Ok(Self::Decimal) + } + Err(_) => match U256::from_str_radix(s, 16) { + Ok(_) => Ok(Self::Hexadecimal), + Err(e) => match e.kind() { + FromStrRadixErrKind::InvalidLength => { + Err(eyre::eyre!("number must be less than U256::MAX ({})", U256::MAX)) + } + _ => Err(eyre::eyre!( + "could not autodetect base as neither decimal or hexadecimal: {}", + e + )), + }, + }, + }, + } + } + + /// Returns the Rust standard prefix for a base + pub const fn prefix(&self) -> &str { + match self { + Base::Binary => "0b", + Base::Octal => "0o", + Base::Hexadecimal => "0x", + _ => "", + } + } +} + +/* --------------------------------------- NumberWithBase --------------------------------------- */ + +/// Utility struct for parsing numbers and formatting them into different [bases][Base]. +/// +/// # Example +/// +/// ``` +/// use cast::base::NumberWithBase; +/// use ethers_core::types::U256; +/// +/// let number: NumberWithBase = U256::from(12345).into(); +/// assert_eq!(number.format(), "12345"); +/// +/// // Debug uses number.base() to determine which base to format to, which defaults to Base::Decimal +/// assert_eq!(format!("{:?}", number), "12345"); +/// +/// // Display uses Base::Decimal +/// assert_eq!(format!("{}", number), "12345"); +/// +/// // The alternate formatter ("#") prepends the base's prefix +/// assert_eq!(format!("{:x}", number), "3039"); +/// assert_eq!(format!("{:#x}", number), "0x3039"); +/// +/// assert_eq!(format!("{:b}", number), "11000000111001"); +/// assert_eq!(format!("{:#b}", number), "0b11000000111001"); +/// +/// assert_eq!(format!("{:o}", number), "30071"); +/// assert_eq!(format!("{:#o}", number), "0o30071"); +/// ``` +#[derive(Clone, Copy)] +pub struct NumberWithBase { + /// The number. + number: U256, + /// Whether the number is positive or zero. + is_nonnegative: bool, + /// The base to format to. + base: Base, +} + +impl std::ops::Deref for NumberWithBase { + type Target = U256; + + fn deref(&self) -> &Self::Target { + &self.number + } +} + +// Format using self.base +impl Debug for NumberWithBase { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + let prefix = self.base.prefix(); + if self.number.is_zero() { + f.pad_integral(true, prefix, "0") + } else { + // Add sign only for decimal + let is_nonnegative = match self.base { + Base::Decimal => self.is_nonnegative, + _ => true, + }; + f.pad_integral(is_nonnegative, prefix, &self.format()) + } + } +} + +impl Binary for NumberWithBase { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + Debug::fmt(&self.with_base(Base::Binary), f) + } +} + +impl Octal for NumberWithBase { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + Debug::fmt(&self.with_base(Base::Octal), f) + } +} + +impl Display for NumberWithBase { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + Debug::fmt(&self.with_base(Base::Decimal), f) + } +} + +impl LowerHex for NumberWithBase { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + Debug::fmt(&self.with_base(Base::Hexadecimal), f) + } +} + +impl UpperHex for NumberWithBase { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + let n = format!("{:X}", self.number); + f.pad_integral(true, Base::Hexadecimal.prefix(), &n) + } +} + +impl FromStr for NumberWithBase { + type Err = eyre::Report; + + fn from_str(s: &str) -> Result { + Self::parse_int(s, None) + } +} + +impl From for NumberWithBase { + fn from(number: I256) -> Self { + // both is_positive and is_negative return false for 0 + Self::new(number.into_raw(), !number.is_negative(), Base::default()) + } +} + +impl From for NumberWithBase { + fn from(value: ParseUnits) -> Self { + match value { + ParseUnits::U256(val) => val.into(), + ParseUnits::I256(val) => val.into(), + } + } +} + +impl From for NumberWithBase { + fn from(number: U256) -> Self { + Self::new(number, true, Base::default()) + } +} + +impl From for I256 { + fn from(n: NumberWithBase) -> Self { + I256::from_raw(n.number) + } +} + +impl From for U256 { + fn from(n: NumberWithBase) -> Self { + n.number + } +} + +impl From for String { + /// Formats the number into the specified base. See [NumberWithBase::format]. + /// + /// [NumberWithBase::format]: NumberWithBase + fn from(n: NumberWithBase) -> Self { + n.format() + } +} + +impl NumberWithBase { + pub fn new(number: impl Into, is_nonnegative: bool, base: Base) -> Self { + Self { number: number.into(), is_nonnegative, base } + } + + /// Creates a copy of the number with the provided base. + pub fn with_base(&self, base: Base) -> Self { + Self { number: self.number, is_nonnegative: self.is_nonnegative, base } + } + + /// Parses a string slice into a signed integer. If base is None then it tries to determine base + /// from the prefix, otherwise defaults to Decimal. + pub fn parse_int(s: &str, base: Option) -> Result { + let base = Base::unwrap_or_detect(base, s)?; + let (number, is_nonnegative) = Self::_parse_int(s, base)?; + Ok(Self { number, is_nonnegative, base }) + } + + /// Parses a string slice into an unsigned integer. If base is None then it tries to determine + /// base from the prefix, otherwise defaults to Decimal. + pub fn parse_uint(s: &str, base: Option) -> Result { + let base = Base::unwrap_or_detect(base, s)?; + let number = Self::_parse_uint(s, base)?; + Ok(Self { number, is_nonnegative: true, base }) + } + + /// Returns a copy of the underlying number as an unsigned integer. If the value is negative + /// then the two's complement of its absolute value will be returned. + pub fn number(&self) -> U256 { + self.number + } + + /// Returns whether the underlying number is positive or zero. + pub fn is_nonnegative(&self) -> bool { + self.is_nonnegative + } + + /// Returns the underlying base. Defaults to [Decimal][Base]. + pub fn base(&self) -> Base { + self.base + } + + /// Returns the Rust standard prefix for the base. + pub const fn prefix(&self) -> &str { + self.base.prefix() + } + + /// Sets the number's base to format to. + pub fn set_base(&mut self, base: Base) -> &mut Self { + self.base = base; + self + } + + /// Formats the number into the specified base. + /// + /// **Note**: this method only formats the number into the base, without adding any prefixes, + /// signs or padding. Refer to the [std::fmt] module documentation on how to format this + /// number with the aforementioned properties. + pub fn format(&self) -> String { + match self.base { + // Binary and Octal traits are not implemented for primitive-types types, so we're using + // a custom formatter + Base::Binary | Base::Octal => self.format_radix(), + Base::Decimal => { + if self.is_nonnegative { + self.number.to_string() + } else { + let s = I256::from_raw(self.number).to_string(); + s.strip_prefix('-').unwrap_or(&s).to_string() + } + } + Base::Hexadecimal => format!("{:x}", self.number), + } + } + + /// Constructs a String from every digit of the number using [std::char::from_digit]. + /// + /// Modified from: https://stackoverflow.com/a/50278316 + fn format_radix(&self) -> String { + let mut x = self.number; + let radix = self.base as u32; + let r = U256::from(radix); + + let mut buf = ['\0'; 256]; + let mut i = 255; + loop { + let m = (x % r).low_u64() as u32; + // radix is always less than 37 so from_digit cannot panic + // m is always in the radix's range so unwrap cannot panic + buf[i] = char::from_digit(m, radix).unwrap(); + x /= r; + if x.is_zero() { + break + } + i -= 1; + } + String::from_iter(&buf[i..]) + } + + fn _parse_int(s: &str, base: Base) -> Result<(U256, bool)> { + let (s, sign) = get_sign(s); + let mut n = Self::_parse_uint(s, base)?; + + let is_neg = matches!(sign, Sign::Negative); + if is_neg { + n = (!n).overflowing_add(U256::one()).0; + } + + Ok((n, !is_neg)) + } + + fn _parse_uint(s: &str, base: Base) -> Result { + // TODO: Parse from binary or octal str into U256, requires a parser + U256::from_str_radix(s, base as u32).map_err(|e| match e.kind() { + FromStrRadixErrKind::UnsupportedRadix => { + eyre::eyre!("numbers in base {} are currently not supported as input", base) + } + _ => eyre::eyre!(e), + }) + } +} + +/* ------------------------------------------- ToBase ------------------------------------------- */ + +/// Facilitates formatting an integer into a [Base]. +pub trait ToBase { + type Err; + + /// Formats self into a base, specifying whether to add the base prefix or not. + /// + /// Tries converting `self` into a [NumberWithBase] and then formats into the provided base by + /// using the [Debug] implementation. + /// + /// # Example + /// + /// ``` + /// use cast::base::{Base, ToBase}; + /// use ethers_core::types::U256; + /// + /// // Any type that implements ToBase + /// let number = U256::from(12345); + /// assert_eq!(number.to_base(Base::Decimal, false).unwrap(), "12345"); + /// assert_eq!(number.to_base(Base::Hexadecimal, false).unwrap(), "3039"); + /// assert_eq!(number.to_base(Base::Hexadecimal, true).unwrap(), "0x3039"); + /// assert_eq!(number.to_base(Base::Binary, true).unwrap(), "0b11000000111001"); + /// assert_eq!(number.to_base(Base::Octal, true).unwrap(), "0o30071"); + /// ``` + fn to_base(&self, base: Base, add_prefix: bool) -> Result; +} + +impl ToBase for NumberWithBase { + type Err = Infallible; + + fn to_base(&self, base: Base, add_prefix: bool) -> Result { + let n = self.with_base(base); + if add_prefix { + Ok(format!("{n:#?}")) + } else { + Ok(format!("{n:?}")) + } + } +} + +impl ToBase for I256 { + type Err = Infallible; + + fn to_base(&self, base: Base, add_prefix: bool) -> Result { + let n = NumberWithBase::from(*self).with_base(base); + if add_prefix { + Ok(format!("{n:#?}")) + } else { + Ok(format!("{n:?}")) + } + } +} + +impl ToBase for U256 { + type Err = Infallible; + + fn to_base(&self, base: Base, add_prefix: bool) -> Result { + let n = NumberWithBase::from(*self).with_base(base); + if add_prefix { + Ok(format!("{n:#?}")) + } else { + Ok(format!("{n:?}")) + } + } +} + +impl ToBase for String { + type Err = eyre::Report; + + fn to_base(&self, base: Base, add_prefix: bool) -> Result { + str::to_base(self, base, add_prefix) + } +} + +impl ToBase for str { + type Err = eyre::Report; + + fn to_base(&self, base: Base, add_prefix: bool) -> Result { + let n = NumberWithBase::from_str(self)?.with_base(base); + if add_prefix { + Ok(format!("{n:#?}")) + } else { + Ok(format!("{n:?}")) + } + } +} + +fn get_sign(s: &str) -> (&str, Sign) { + match s.as_bytes().first() { + Some(b'+') => (&s[1..], Sign::Positive), + Some(b'-') => (&s[1..], Sign::Negative), + _ => (s, Sign::Positive), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use Base::*; + + const POS_NUM: [i128; 44] = [ + 1, + 2, + 3, + 5, + 7, + 8, + 10, + 11, + 13, + 16, + 17, + 19, + 23, + 29, + 31, + 32, + 37, + 41, + 43, + 47, + 53, + 59, + 61, + 64, + 67, + 71, + 73, + 79, + 83, + 89, + 97, + 100, + 128, + 200, + 333, + 500, + 666, + 1000, + 6666, + 10000, + i16::MAX as i128, + i32::MAX as i128, + i64::MAX as i128, + i128::MAX, + ]; + + const NEG_NUM: [i128; 44] = [ + -1, + -2, + -3, + -5, + -7, + -8, + -10, + -11, + -13, + -16, + -17, + -19, + -23, + -29, + -31, + -32, + -37, + -41, + -43, + -47, + -53, + -59, + -61, + -64, + -67, + -71, + -73, + -79, + -83, + -89, + -97, + -100, + -128, + -200, + -333, + -500, + -666, + -1000, + -6666, + -10000, + i16::MIN as i128, + i32::MIN as i128, + i64::MIN as i128, + i128::MIN, + ]; + + #[test] + fn test_defaults() { + let def: Base = Default::default(); + assert!(matches!(def, Decimal)); + + let n: NumberWithBase = U256::zero().into(); + assert!(matches!(n.base, Decimal)); + let n: NumberWithBase = I256::zero().into(); + assert!(matches!(n.base, Decimal)); + } + + #[test] + fn can_parse_base() { + assert_eq!("2".parse::().unwrap(), Binary); + assert_eq!("b".parse::().unwrap(), Binary); + assert_eq!("bin".parse::().unwrap(), Binary); + assert_eq!("binary".parse::().unwrap(), Binary); + + assert_eq!("8".parse::().unwrap(), Octal); + assert_eq!("o".parse::().unwrap(), Octal); + assert_eq!("oct".parse::().unwrap(), Octal); + assert_eq!("octal".parse::().unwrap(), Octal); + + assert_eq!("10".parse::().unwrap(), Decimal); + assert_eq!("d".parse::().unwrap(), Decimal); + assert_eq!("dec".parse::().unwrap(), Decimal); + assert_eq!("decimal".parse::().unwrap(), Decimal); + + assert_eq!("16".parse::().unwrap(), Hexadecimal); + assert_eq!("h".parse::().unwrap(), Hexadecimal); + assert_eq!("hex".parse::().unwrap(), Hexadecimal); + assert_eq!("hexadecimal".parse::().unwrap(), Hexadecimal); + } + + #[test] + fn can_detect_base() { + assert_eq!(Base::detect("0b100").unwrap(), Binary); + assert_eq!(Base::detect("0o100").unwrap(), Octal); + assert_eq!(Base::detect("100").unwrap(), Decimal); + assert_eq!(Base::detect("0x100").unwrap(), Hexadecimal); + + assert_eq!(Base::detect("0123456789abcdef").unwrap(), Hexadecimal); + + let _ = Base::detect("0b234abc").unwrap_err(); + let _ = Base::detect("0o89cba").unwrap_err(); + let _ = Base::detect("0123456789abcdefg").unwrap_err(); + let _ = Base::detect("0x123abclpmk").unwrap_err(); + let _ = Base::detect("hello world").unwrap_err(); + } + + #[test] + fn test_format_pos() { + let expected_2: Vec<_> = POS_NUM.iter().map(|n| format!("{n:b}")).collect(); + let expected_8: Vec<_> = POS_NUM.iter().map(|n| format!("{n:o}")).collect(); + let expected_10: Vec<_> = POS_NUM.iter().map(|n| format!("{n:}")).collect(); + let expected_l16: Vec<_> = POS_NUM.iter().map(|n| format!("{n:x}")).collect(); + let expected_u16: Vec<_> = POS_NUM.iter().map(|n| format!("{n:X}")).collect(); + + for (i, n) in POS_NUM.into_iter().enumerate() { + let mut num: NumberWithBase = I256::from(n).into(); + + assert_eq!(num.set_base(Binary).format(), expected_2[i]); + assert_eq!(num.set_base(Octal).format(), expected_8[i]); + assert_eq!(num.set_base(Decimal).format(), expected_10[i]); + assert_eq!(num.set_base(Hexadecimal).format(), expected_l16[i]); + assert_eq!(num.set_base(Hexadecimal).format().to_uppercase(), expected_u16[i]); + } + } + + // TODO: test for octal + #[test] + fn test_format_neg() { + // underlying is 256 bits so we have to pad left manually + + let expected_2: Vec<_> = NEG_NUM.iter().map(|n| format!("{n:1>256b}")).collect(); + // let expected_8: Vec<_> = NEG_NUM.iter().map(|n| format!("1{:7>85o}", n)).collect(); + // Sign not included, see NumberWithBase::format + let expected_10: Vec<_> = + NEG_NUM.iter().map(|n| format!("{n:}").trim_matches('-').to_string()).collect(); + let expected_l16: Vec<_> = NEG_NUM.iter().map(|n| format!("{n:f>64x}")).collect(); + let expected_u16: Vec<_> = NEG_NUM.iter().map(|n| format!("{n:F>64X}")).collect(); + + for (i, n) in NEG_NUM.into_iter().enumerate() { + let mut num: NumberWithBase = I256::from(n).into(); + + assert_eq!(num.set_base(Binary).format(), expected_2[i]); + // assert_eq!(num.set_base(Octal).format(), expected_8[i]); + assert_eq!(num.set_base(Decimal).format(), expected_10[i]); + assert_eq!(num.set_base(Hexadecimal).format(), expected_l16[i]); + assert_eq!(num.set_base(Hexadecimal).format().to_uppercase(), expected_u16[i]); + } + } + + #[test] + fn test_fmt_macro() { + let nums: Vec<_> = + POS_NUM.into_iter().map(|n| NumberWithBase::from(I256::from(n))).collect(); + + let actual_2: Vec<_> = nums.iter().map(|n| format!("{n:b}")).collect(); + let actual_2_alt: Vec<_> = nums.iter().map(|n| format!("{n:#b}")).collect(); + let actual_8: Vec<_> = nums.iter().map(|n| format!("{n:o}")).collect(); + let actual_8_alt: Vec<_> = nums.iter().map(|n| format!("{n:#o}")).collect(); + let actual_10: Vec<_> = nums.iter().map(|n| format!("{n:}")).collect(); + let actual_10_alt: Vec<_> = nums.iter().map(|n| format!("{n:#}")).collect(); + let actual_l16: Vec<_> = nums.iter().map(|n| format!("{n:x}")).collect(); + let actual_l16_alt: Vec<_> = nums.iter().map(|n| format!("{n:#x}")).collect(); + let actual_u16: Vec<_> = nums.iter().map(|n| format!("{n:X}")).collect(); + let actual_u16_alt: Vec<_> = nums.iter().map(|n| format!("{n:#X}")).collect(); + + let expected_2: Vec<_> = POS_NUM.iter().map(|n| format!("{n:b}")).collect(); + let expected_2_alt: Vec<_> = POS_NUM.iter().map(|n| format!("{n:#b}")).collect(); + let expected_8: Vec<_> = POS_NUM.iter().map(|n| format!("{n:o}")).collect(); + let expected_8_alt: Vec<_> = POS_NUM.iter().map(|n| format!("{n:#o}")).collect(); + let expected_10: Vec<_> = POS_NUM.iter().map(|n| format!("{n:}")).collect(); + let expected_10_alt: Vec<_> = POS_NUM.iter().map(|n| format!("{n:#}")).collect(); + let expected_l16: Vec<_> = POS_NUM.iter().map(|n| format!("{n:x}")).collect(); + let expected_l16_alt: Vec<_> = POS_NUM.iter().map(|n| format!("{n:#x}")).collect(); + let expected_u16: Vec<_> = POS_NUM.iter().map(|n| format!("{n:X}")).collect(); + let expected_u16_alt: Vec<_> = POS_NUM.iter().map(|n| format!("{n:#X}")).collect(); + + for (i, _) in POS_NUM.iter().enumerate() { + assert_eq!(actual_2[i], expected_2[i]); + assert_eq!(actual_2_alt[i], expected_2_alt[i]); + + assert_eq!(actual_8[i], expected_8[i]); + assert_eq!(actual_8_alt[i], expected_8_alt[i]); + + assert_eq!(actual_10[i], expected_10[i]); + assert_eq!(actual_10_alt[i], expected_10_alt[i]); + + assert_eq!(actual_l16[i], expected_l16[i]); + assert_eq!(actual_l16_alt[i], expected_l16_alt[i]); + + assert_eq!(actual_u16[i], expected_u16[i]); + assert_eq!(actual_u16_alt[i], expected_u16_alt[i]); + } + } +} diff --git a/crates/zkcast/src/errors.rs b/crates/zkcast/src/errors.rs new file mode 100644 index 000000000..235ec4d7c --- /dev/null +++ b/crates/zkcast/src/errors.rs @@ -0,0 +1,34 @@ +//! Errors for this crate + +use foundry_config::Chain; +use std::fmt; + +/// An error thrown when resolving a function via signature failed +#[derive(Debug, Clone)] +pub enum FunctionSignatureError { + MissingSignature, + MissingEtherscan { sig: String }, + UnknownChain(Chain), + MissingToAddress, +} + +impl fmt::Display for FunctionSignatureError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + FunctionSignatureError::MissingSignature => { + writeln!(f, "Function signature must be set") + } + FunctionSignatureError::MissingEtherscan { sig } => { + writeln!(f, "Failed to determine function signature for `{sig}`")?; + writeln!(f, "To lookup a function signature of a deployed contract by name, a valid ETHERSCAN_API_KEY must be set.")?; + write!(f, "\tOr did you mean:\t {sig}()") + } + FunctionSignatureError::UnknownChain(chain) => { + write!(f, "Resolving via etherscan requires a known chain. Unknown chain: {chain}") + } + FunctionSignatureError::MissingToAddress => f.write_str("Target address must be set"), + } + } +} + +impl std::error::Error for FunctionSignatureError {} diff --git a/crates/zkcast/src/lib.rs b/crates/zkcast/src/lib.rs new file mode 100644 index 000000000..659886678 --- /dev/null +++ b/crates/zkcast/src/lib.rs @@ -0,0 +1,2100 @@ +use crate::rlp_converter::Item; +use base::{Base, NumberWithBase, ToBase}; +use chrono::NaiveDateTime; +use ethers_core::{ + abi::{ + token::{LenientTokenizer, Tokenizer}, + Function, HumanReadableParser, ParamType, RawAbi, Token, + }, + types::{transaction::eip2718::TypedTransaction, Chain, *}, + utils::{ + format_bytes32_string, format_units, get_contract_address, keccak256, parse_bytes32_string, + parse_units, rlp, Units, + }, +}; +use ethers_etherscan::{errors::EtherscanError, Client}; +use ethers_providers::{Middleware, PendingTransaction, PubsubClient}; +use evm_disassembler::{disassemble_bytes, disassemble_str, format_operations}; +use eyre::{Context, Result}; +use foundry_common::{abi::encode_args, fmt::*, TransactionReceiptWithRevertReason}; +pub use foundry_evm::*; +use futures::{future::Either, FutureExt, StreamExt}; +use rayon::prelude::*; +pub use rusoto_core::{ + credential::ChainProvider as AwsChainProvider, region::Region as AwsRegion, + request::HttpClient as AwsHttpClient, Client as AwsClient, +}; +pub use rusoto_kms::KmsClient; +use std::{ + io, + path::PathBuf, + str::FromStr, + sync::atomic::{AtomicBool, Ordering}, +}; +use tokio::signal::ctrl_c; +pub use tx::TxBuilder; +use tx::{TxBuilderOutput, TxBuilderPeekOutput}; + +pub mod base; +pub mod errors; +mod rlp_converter; +mod tx; + +// TODO: CastContract with common contract initializers? Same for CastProviders? + +pub struct Cast { + provider: M, +} + +impl Cast +where + M::Error: 'static, +{ + /// Creates a new Cast instance from the provided client + /// + /// # Example + /// + /// ``` + /// use cast::Cast; + /// use ethers_providers::{Provider, Http}; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let cast = Cast::new(provider); + /// # Ok(()) + /// # } + /// ``` + pub fn new(provider: M) -> Self { + Self { provider } + } + + /// Makes a read-only call to the specified address + /// + /// # Example + /// + /// ```no_run + /// use cast::{Cast, TxBuilder}; + /// use ethers_core::types::{Address, Chain}; + /// use ethers_providers::{Provider, Http}; + /// use std::{str::FromStr, convert::TryFrom}; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let to = Address::from_str("0xB3C95ff08316fb2F2e3E52Ee82F8e7b605Aa1304")?; + /// let sig = "function greeting(uint256 i) public returns (string)"; + /// let args = vec!["5".to_owned()]; + /// let mut builder = TxBuilder::new(&provider, Address::zero(), Some(to), Chain::Mainnet, false).await?; + /// builder + /// .set_args(sig, args).await?; + /// let builder_output = builder.build(); + /// let cast = Cast::new(provider); + /// let data = cast.call(builder_output, None).await?; + /// println!("{}", data); + /// # Ok(()) + /// # } + /// ``` + pub async fn call<'a>( + &self, + builder_output: TxBuilderOutput, + block: Option, + ) -> Result { + let (tx, func) = builder_output; + let res = self.provider.call(&tx, block).await?; + + let mut decoded = vec![]; + + if let Some(func) = func { + // decode args into tokens + decoded = match func.decode_output(res.as_ref()) { + Ok(decoded) => decoded, + Err(err) => { + // ensure the address is a contract + if res.is_empty() { + // check that the recipient is a contract that can be called + if let Some(NameOrAddress::Address(addr)) = tx.to() { + let code = self.provider.get_code(*addr, block).await?; + if code.is_empty() { + eyre::bail!("Contract {:?} does not exist", addr) + } + } + } + return Err(err).wrap_err( + "could not decode output. did you specify the wrong function return data type perhaps?" + ); + } + }; + } + // handle case when return type is not specified + Ok(if decoded.is_empty() { + format!("{res}\n") + } else { + // seth compatible user-friendly return type conversions + decoded + .iter() + .map(TokenDisplay) + .map(|token| token.to_string()) + .collect::>() + .join("\n") + }) + } + + /// Generates an access list for the specified transaction + /// + /// # Example + /// + /// ```no_run + /// use cast::{Cast, TxBuilder}; + /// use ethers_core::types::{Address, Chain}; + /// use ethers_providers::{Provider, Http}; + /// use std::{str::FromStr, convert::TryFrom}; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let to = Address::from_str("0xB3C95ff08316fb2F2e3E52Ee82F8e7b605Aa1304")?; + /// let sig = "greeting(uint256)(string)"; + /// let args = vec!["5".to_owned()]; + /// let mut builder = TxBuilder::new(&provider, Address::zero(), Some(to), Chain::Mainnet, false).await?; + /// builder + /// .set_args(sig, args).await?; + /// let builder_output = builder.peek(); + /// let cast = Cast::new(&provider); + /// let access_list = cast.access_list(builder_output, None, false).await?; + /// println!("{}", access_list); + /// # Ok(()) + /// # } + /// ``` + pub async fn access_list( + &self, + builder_output: TxBuilderPeekOutput<'_>, + block: Option, + to_json: bool, + ) -> Result { + let (tx, _) = builder_output; + let access_list = self.provider.create_access_list(tx, block).await?; + let res = if to_json { + serde_json::to_string(&access_list)? + } else { + let mut s = + vec![format!("gas used: {}", access_list.gas_used), "access list:".to_string()]; + for al in access_list.access_list.0 { + s.push(format!("- address: {}", SimpleCast::to_checksum_address(&al.address))); + if !al.storage_keys.is_empty() { + s.push(" keys:".to_string()); + for key in al.storage_keys { + s.push(format!(" {key:?}")); + } + } + } + s.join("\n") + }; + + Ok(res) + } + + pub async fn balance + Send + Sync>( + &self, + who: T, + block: Option, + ) -> Result { + Ok(self.provider.get_balance(who, block).await?) + } + + /// Sends a transaction to the specified address + /// + /// # Example + /// + /// ```no_run + /// use cast::{Cast, TxBuilder}; + /// use ethers_core::types::{Address, Chain, U256}; + /// use ethers_providers::{Provider, Http}; + /// use std::{str::FromStr, convert::TryFrom}; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let from = "vitalik.eth"; + /// let to = Address::from_str("0xB3C95ff08316fb2F2e3E52Ee82F8e7b605Aa1304")?; + /// let sig = "greet(string)()"; + /// let args = vec!["hello".to_owned()]; + /// let gas = U256::from_str("200000").unwrap(); + /// let value = U256::from_str("1").unwrap(); + /// let nonce = U256::from_str("1").unwrap(); + /// let mut builder = TxBuilder::new(&provider, Address::zero(), Some(to), Chain::Mainnet, false).await?; + /// builder + /// .set_args(sig, args).await? + /// .set_gas(gas) + /// .set_value(value) + /// .set_nonce(nonce); + /// let builder_output = builder.build(); + /// let cast = Cast::new(provider); + /// let data = cast.send(builder_output).await?; + /// println!("{}", *data); + /// # Ok(()) + /// # } + /// ``` + pub async fn send<'a>( + &self, + builder_output: TxBuilderOutput, + ) -> Result> { + let (tx, _) = builder_output; + let res = self.provider.send_transaction(tx, None).await?; + + Ok::<_, eyre::Error>(res) + } + + /// Publishes a raw transaction to the network + /// + /// # Example + /// + /// ```no_run + /// use cast::Cast; + /// use ethers_providers::{Provider, Http}; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let cast = Cast::new(provider); + /// let res = cast.publish("0x1234".to_string()).await?; + /// println!("{:?}", res); + /// # Ok(()) + /// # } + /// ``` + pub async fn publish(&self, mut raw_tx: String) -> Result> { + raw_tx = match raw_tx.strip_prefix("0x") { + Some(s) => s.to_string(), + None => raw_tx, + }; + let tx = Bytes::from(hex::decode(raw_tx)?); + let res = self.provider.send_raw_transaction(tx).await?; + + Ok::<_, eyre::Error>(res) + } + + /// Estimates the gas cost of a transaction + /// + /// # Example + /// + /// ```no_run + /// use cast::{Cast, TxBuilder}; + /// use ethers_core::types::{Address, Chain, U256}; + /// use ethers_providers::{Provider, Http}; + /// use std::{str::FromStr, convert::TryFrom}; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let from = "vitalik.eth"; + /// let to = Address::from_str("0xB3C95ff08316fb2F2e3E52Ee82F8e7b605Aa1304")?; + /// let sig = "greet(string)()"; + /// let args = vec!["5".to_owned()]; + /// let value = U256::from_str("1").unwrap(); + /// let mut builder = TxBuilder::new(&provider, from, Some(to), Chain::Mainnet, false).await?; + /// builder + /// .set_value(value) + /// .set_args(sig, args).await?; + /// let builder_output = builder.peek(); + /// let cast = Cast::new(&provider); + /// let data = cast.estimate(builder_output).await?; + /// println!("{}", data); + /// # Ok(()) + /// # } + /// ``` + pub async fn estimate(&self, builder_output: TxBuilderPeekOutput<'_>) -> Result { + let (tx, _) = builder_output; + + let res = self.provider.estimate_gas(tx, None).await?; + + Ok::<_, eyre::Error>(res) + } + + /// # Example + /// + /// ```no_run + /// use cast::Cast; + /// use ethers_providers::{Provider, Http}; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let cast = Cast::new(provider); + /// let block = cast.block(5, true, None, false).await?; + /// println!("{}", block); + /// # Ok(()) + /// # } + /// ``` + pub async fn block>( + &self, + block: T, + full: bool, + field: Option, + to_json: bool, + ) -> Result { + let block = block.into(); + let block = if full { + let block = self + .provider + .get_block_with_txs(block) + .await? + .ok_or_else(|| eyre::eyre!("block {:?} not found", block))?; + if let Some(ref field) = field { + get_pretty_block_attr(&block, field) + .unwrap_or_else(|| format!("{field} is not a valid block field")) + } else if to_json { + serde_json::to_value(&block).unwrap().to_string() + } else { + block.pretty() + } + } else { + let block = self + .provider + .get_block(block) + .await? + .ok_or_else(|| eyre::eyre!("block {:?} not found", block))?; + + if let Some(ref field) = field { + if field == "transactions" { + "use --full to view transactions".to_string() + } else { + get_pretty_block_attr(&block, field) + .unwrap_or_else(|| format!("{field} is not a valid block field")) + } + } else if to_json { + serde_json::to_value(&block).unwrap().to_string() + } else { + block.pretty() + } + }; + + Ok(block) + } + + async fn block_field_as_num>(&self, block: T, field: String) -> Result { + let block = block.into(); + let block_field = Cast::block( + self, + block, + false, + // Select only select field + Some(field), + false, + ) + .await?; + + let ret = if block_field.starts_with("0x") { + U256::from_str_radix(strip_0x(&block_field), 16).expect("Unable to convert hex to U256") + } else { + U256::from_str_radix(&block_field, 10).expect("Unable to convert decimal to U256") + }; + Ok(ret) + } + + pub async fn base_fee>(&self, block: T) -> Result { + Cast::block_field_as_num(self, block, String::from("baseFeePerGas")).await + } + + pub async fn age>(&self, block: T) -> Result { + let timestamp_str = + Cast::block_field_as_num(self, block, String::from("timestamp")).await?.to_string(); + let datetime = + NaiveDateTime::from_timestamp_opt(timestamp_str.parse::().unwrap(), 0).unwrap(); + Ok(datetime.format("%a %b %e %H:%M:%S %Y").to_string()) + } + + pub async fn timestamp>(&self, block: T) -> Result { + Cast::block_field_as_num(self, block, "timestamp".to_string()).await + } + + pub async fn chain(&self) -> Result<&str> { + let genesis_hash = Cast::block( + self, + 0, + false, + // Select only block hash + Some(String::from("hash")), + false, + ) + .await?; + + Ok(match &genesis_hash[..] { + "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" => { + match &(Cast::block(self, 1920000, false, Some("hash".to_string()), false).await?)[..] + { + "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" => { + "etclive" + } + _ => "ethlive", + } + } + "0xa3c565fc15c7478862d50ccd6561e3c06b24cc509bf388941c25ea985ce32cb9" => "kovan", + "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d" => "ropsten", + "0x7ca38a1916c42007829c55e69d3e9a73265554b586a499015373241b8a3fa48b" => { + "optimism-mainnet" + } + "0xc1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1" => { + "optimism-goerli" + } + "0x02adc9b449ff5f2467b8c674ece7ff9b21319d76c4ad62a67a70d552655927e5" => { + "optimism-kovan" + } + "0x7ee576b35482195fc49205cec9af72ce14f003b9ae69f6ba0faef4514be8b442" => { + "arbitrum-mainnet" + } + "0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303" => "morden", + "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177" => "rinkeby", + "0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a" => "goerli", + "0x14c2283285a88fe5fce9bf5c573ab03d6616695d717b12a127188bcacfc743c4" => "kotti", + "0xa9c28ce2141b56c474f1dc504bee9b01eb1bd7d1a507580d5519d4437a97de1b" => "polygon", + "0x7b66506a9ebdbf30d32b43c5f15a3b1216269a1ec3a75aa3182b86176a2b1ca7" => { + "polygon-mumbai" + } + "0x4f1dd23188aab3a76b463e4af801b52b1248ef073c648cbdc4c9333d3da79756" => "gnosis", + "0xada44fd8d2ecab8b08f256af07ad3e777f17fb434f8f8e678b312f576212ba9a" => "chiado", + "0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34" => "bsctest", + "0x0d21840abff46b96c84b2ac9e10e4f5cdaeb5693cb665db62a2f3b02d2d57b5b" => "bsc", + "0x31ced5b9beb7f8782b014660da0cb18cc409f121f408186886e1ca3e8eeca96b" => { + match &(Cast::block(self, 1, false, Some(String::from("hash")), false).await?)[..] { + "0x738639479dc82d199365626f90caa82f7eafcfe9ed354b456fb3d294597ceb53" => { + "avalanche-fuji" + } + _ => "avalanche", + } + } + _ => "unknown", + }) + } + + pub async fn chain_id(&self) -> Result { + Ok(self.provider.get_chainid().await?) + } + + pub async fn block_number(&self) -> Result { + Ok(self.provider.get_block_number().await?) + } + + pub async fn gas_price(&self) -> Result { + Ok(self.provider.get_gas_price().await?) + } + + /// # Example + /// + /// ```no_run + /// use cast::Cast; + /// use ethers_providers::{Provider, Http}; + /// use ethers_core::types::Address; + /// use std::{str::FromStr, convert::TryFrom}; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let cast = Cast::new(provider); + /// let addr = Address::from_str("0x7eD52863829AB99354F3a0503A622e82AcD5F7d3")?; + /// let nonce = cast.nonce(addr, None).await?; + /// println!("{}", nonce); + /// # Ok(()) + /// # } + /// ``` + pub async fn nonce + Send + Sync>( + &self, + who: T, + block: Option, + ) -> Result { + Ok(self.provider.get_transaction_count(who, block).await?) + } + + /// # Example + /// + /// ```no_run + /// use cast::Cast; + /// use ethers_providers::{Provider, Http}; + /// use ethers_core::types::Address; + /// use std::{str::FromStr, convert::TryFrom}; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let cast = Cast::new(provider); + /// let addr = Address::from_str("0x7eD52863829AB99354F3a0503A622e82AcD5F7d3")?; + /// let implementation = cast.implementation(addr, None).await?; + /// println!("{}", implementation); + /// # Ok(()) + /// # } + /// ``` + pub async fn implementation + Send + Sync>( + &self, + who: T, + block: Option, + ) -> Result { + let slot = + H256::from_str("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc")?; + let value = self.provider.get_storage_at(who, slot, block).await?; + let addr: H160 = value.into(); + Ok(format!("{addr:?}")) + } + + /// # Example + /// + /// ```no_run + /// use cast::Cast; + /// use ethers_providers::{Provider, Http}; + /// use ethers_core::types::Address; + /// use std::{str::FromStr, convert::TryFrom}; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let cast = Cast::new(provider); + /// let addr = Address::from_str("0x7eD52863829AB99354F3a0503A622e82AcD5F7d3")?; + /// let admin = cast.admin(addr, None).await?; + /// println!("{}", admin); + /// # Ok(()) + /// # } + /// ``` + pub async fn admin + Send + Sync>( + &self, + who: T, + block: Option, + ) -> Result { + let slot = + H256::from_str("0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103")?; + let value = self.provider.get_storage_at(who, slot, block).await?; + let addr: H160 = value.into(); + Ok(format!("{addr:?}")) + } + + /// # Example + /// + /// ```no_run + /// use cast::Cast; + /// use ethers_providers::{Provider, Http}; + /// use ethers_core::types::Address; + /// use std::{str::FromStr, convert::TryFrom}; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let cast = Cast::new(provider); + /// let addr = Address::from_str("0x7eD52863829AB99354F3a0503A622e82AcD5F7d3")?; + /// let nonce = cast.nonce(addr, None).await? + 5; + /// let computed_address = cast.compute_address(addr, Some(nonce)).await?; + /// println!("Computed address for address {} with nonce {}: {}", addr, nonce, computed_address); + /// let computed_address_no_nonce = cast.compute_address(addr, None).await?; + /// println!("Computed address for address {} with nonce {}: {}", addr, nonce, computed_address_no_nonce); + /// # Ok(()) + /// # } + /// ``` + pub async fn compute_address + Copy + Send + Sync>( + &self, + address: T, + nonce: Option, + ) -> Result
{ + let unpacked = if let Some(n) = nonce { + n + } else { + self.provider.get_transaction_count(address.into(), None).await? + }; + + Ok(get_contract_address(address, unpacked)) + } + + /// # Example + /// + /// ```no_run + /// use cast::Cast; + /// use ethers_providers::{Provider, Http}; + /// use ethers_core::types::Address; + /// use std::{str::FromStr, convert::TryFrom}; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let cast = Cast::new(provider); + /// let addr = Address::from_str("0x00000000219ab540356cbb839cbe05303d7705fa")?; + /// let code = cast.code(addr, None, false).await?; + /// println!("{}", code); + /// # Ok(()) + /// # } + /// ``` + pub async fn code + Send + Sync>( + &self, + who: T, + block: Option, + disassemble: bool, + ) -> Result { + if disassemble { + let code = self.provider.get_code(who, block).await?.to_vec(); + Ok(format_operations(disassemble_bytes(code)?)?) + } else { + Ok(format!("{}", self.provider.get_code(who, block).await?)) + } + } + + /// Example + /// + /// ```no_run + /// use cast::Cast; + /// use ethers_providers::{Provider, Http}; + /// use ethers_core::types::Address; + /// use std::{str::FromStr, convert::TryFrom}; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let cast = Cast::new(provider); + /// let addr = Address::from_str("0x00000000219ab540356cbb839cbe05303d7705fa")?; + /// let codesize = cast.codesize(addr, None).await?; + /// println!("{}", codesize); + /// # Ok(()) + /// # } + /// ``` + pub async fn codesize + Send + Sync>( + &self, + who: T, + block: Option, + ) -> Result { + let code = self.provider.get_code(who, block).await?.to_vec(); + Ok(format!("{}", code.len())) + } + + /// # Example + /// + /// ```no_run + /// use cast::Cast; + /// use ethers_providers::{Provider, Http}; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let cast = Cast::new(provider); + /// let tx_hash = "0xf8d1713ea15a81482958fb7ddf884baee8d3bcc478c5f2f604e008dc788ee4fc"; + /// let tx = cast.transaction(tx_hash.to_string(), None, false, false).await?; + /// println!("{}", tx); + /// # Ok(()) + /// # } + /// ``` + pub async fn transaction( + &self, + tx_hash: String, + field: Option, + raw: bool, + to_json: bool, + ) -> Result { + let tx_hash = H256::from_str(&tx_hash).wrap_err("invalid tx hash")?; + let tx = self + .provider + .get_transaction(tx_hash) + .await? + .ok_or_else(|| eyre::eyre!("tx not found: {:?}", tx_hash))?; + + Ok(if raw { + format!("0x{}", hex::encode(tx.rlp())) + } else if let Some(field) = field { + get_pretty_tx_attr(&tx, field.as_str()) + .ok_or_else(|| eyre::eyre!("invalid tx field: {}", field.to_string()))? + } else if to_json { + // to_value first to sort json object keys + serde_json::to_value(&tx)?.to_string() + } else { + tx.pretty() + }) + } + + /// # Example + /// + /// ```no_run + /// use cast::Cast; + /// use ethers_providers::{Provider, Http}; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let cast = Cast::new(provider); + /// let tx_hash = "0xf8d1713ea15a81482958fb7ddf884baee8d3bcc478c5f2f604e008dc788ee4fc"; + /// let receipt = cast.receipt(tx_hash.to_string(), None, 1, false, false).await?; + /// println!("{}", receipt); + /// # Ok(()) + /// # } + /// ``` + pub async fn receipt( + &self, + tx_hash: String, + field: Option, + confs: usize, + cast_async: bool, + to_json: bool, + ) -> Result { + let tx_hash = H256::from_str(&tx_hash).wrap_err("invalid tx hash")?; + + let mut receipt: TransactionReceiptWithRevertReason = + match self.provider.get_transaction_receipt(tx_hash).await? { + Some(r) => r, + None => { + // if the async flag is provided, immediately exit if no tx is found, otherwise + // try to poll for it + if cast_async { + eyre::bail!("tx not found: {:?}", tx_hash) + } else { + let tx = PendingTransaction::new(tx_hash, self.provider.provider()); + tx.confirmations(confs).await?.ok_or_else(|| { + eyre::eyre!( + "tx not found, might have been dropped from mempool: {:?}", + tx_hash + ) + })? + } + } + } + .into(); + + // Allow to fail silently + let _ = receipt.update_revert_reason(&self.provider).await; + + Ok(if let Some(ref field) = field { + get_pretty_tx_receipt_attr(&receipt, field) + .ok_or_else(|| eyre::eyre!("invalid receipt field: {}", field))? + } else if to_json { + // to_value first to sort json object keys + serde_json::to_value(&receipt)?.to_string() + } else { + receipt.pretty() + }) + } + + /// Perform a raw JSON-RPC request + /// + /// # Example + /// + /// ```no_run + /// use cast::Cast; + /// use ethers_providers::{Provider, Http}; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let cast = Cast::new(provider); + /// let result = cast.rpc("eth_getBalance", &["0xc94770007dda54cF92009BFF0dE90c06F603a09f", "latest"]) + /// .await?; + /// println!("{}", result); + /// # Ok(()) + /// # } + /// ``` + pub async fn rpc(&self, method: &str, params: T) -> Result + where + T: std::fmt::Debug + serde::Serialize + Send + Sync, + { + let res = self.provider.provider().request::(method, params).await?; + Ok(serde_json::to_string(&res)?) + } + + /// Returns the slot + /// + /// # Example + /// + /// ```no_run + /// use cast::Cast; + /// use ethers_providers::{Provider, Http}; + /// use ethers_core::types::{Address, H256}; + /// use std::{str::FromStr, convert::TryFrom}; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let cast = Cast::new(provider); + /// let addr = Address::from_str("0x00000000006c3852cbEf3e08E8dF289169EdE581")?; + /// let slot = H256::zero(); + /// let storage = cast.storage(addr, slot, None).await?; + /// println!("{}", storage); + /// # Ok(()) + /// # } + /// ``` + pub async fn storage + Send + Sync>( + &self, + from: T, + slot: H256, + block: Option, + ) -> Result { + Ok(format!("{:?}", self.provider.get_storage_at(from, slot, block).await?)) + } + + pub async fn filter_logs(&self, filter: Filter, to_json: bool) -> Result { + let logs = self.provider.get_logs(&filter).await?; + + let res = if to_json { + serde_json::to_string(&logs)? + } else { + let mut s = vec![]; + for log in logs { + let pretty = log + .pretty() + .replacen('\n', "- ", 1) // Remove empty first line + .replace('\n', "\n "); // Indent + s.push(pretty); + } + s.join("\n") + }; + Ok(res) + } + + /// Converts a block identifier into a block number. + /// + /// If the block identifier is a block number, then this function returns the block number. If + /// the block identifier is a block hash, then this function returns the block number of + /// that block hash. If the block identifier is `None`, then this function returns `None`. + /// + /// # Example + /// + /// ```no_run + /// use cast::Cast; + /// use ethers_providers::{Provider, Http}; + /// use ethers_core::types::{BlockId, BlockNumber}; + /// use std::convert::TryFrom; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let cast = Cast::new(provider); + /// + /// let block_number = cast.convert_block_number(Some(BlockId::Number(BlockNumber::from(5)))).await?; + /// assert_eq!(block_number, Some(BlockNumber::from(5))); + /// + /// let block_number = cast.convert_block_number(Some(BlockId::Hash("0x1234".parse().unwrap()))).await?; + /// assert_eq!(block_number, Some(BlockNumber::from(1234))); + /// + /// let block_number = cast.convert_block_number(None).await?; + /// assert_eq!(block_number, None); + /// # Ok(()) + /// # } + /// ``` + pub async fn convert_block_number( + &self, + block: Option, + ) -> Result, eyre::Error> { + match block { + Some(block) => match block { + BlockId::Number(block_number) => Ok(Some(block_number)), + BlockId::Hash(hash) => { + let block = self.provider.get_block(hash).await?; + Ok(block.map(|block| block.number.unwrap()).map(BlockNumber::from)) + } + }, + None => Ok(None), + } + } + + /// Sets up a subscription to the given filter and writes the logs to the given output. + /// + /// # Example + /// + /// ```no_run + /// use cast::Cast; + /// use ethers_core::abi::Address; + /// use ethers_providers::{Provider, Ws}; + /// use ethers_core::types::Filter; + /// use std::{str::FromStr, convert::TryFrom}; + /// use std::io; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = Provider::new(Ws::connect("wss://localhost:8545").await?); + /// let cast = Cast::new(provider); + /// + /// let filter = Filter::new().address(Address::from_str("0x00000000006c3852cbEf3e08E8dF289169EdE581")?); + /// let mut output = io::stdout(); + /// cast.subscribe(filter, &mut output, false).await?; + /// # Ok(()) + /// # } + /// ``` + pub async fn subscribe( + &self, + filter: Filter, + output: &mut dyn io::Write, + to_json: bool, + ) -> Result<()> + where + ::Provider: PubsubClient, + { + // Initialize the subscription stream for logs + let mut subscription = self.provider.subscribe_logs(&filter).await?; + + // Check if a to_block is specified, if so, subscribe to blocks + let mut block_subscription = if filter.get_to_block().is_some() { + Some(self.provider.subscribe_blocks().await?) + } else { + None + }; + + let to_block_number = filter.get_to_block(); + + // If output should be JSON, start with an opening bracket + if to_json { + write!(output, "[")?; + } + + let mut first = true; + + loop { + tokio::select! { + // If block subscription is present, listen to it to avoid blocking indefinitely past the desired to_block + block = if let Some(bs) = &mut block_subscription { + Either::Left(bs.next().fuse()) + } else { + Either::Right(futures::future::pending()) + } => { + if let (Some(block), Some(to_block)) = (block, to_block_number) { + if block.number.map_or(false, |bn| bn > to_block) { + break; + } + } + }, + // Process incoming log + log = subscription.next() => { + if to_json { + if !first { + write!(output, ",")?; + } + first = false; + let log_str = serde_json::to_string(&log).unwrap(); + write!(output, "{}", log_str)?; + } else { + let log_str = log.pretty() + .replacen('\n', "- ", 1) // Remove empty first line + .replace('\n', "\n "); // Indent + writeln!(output, "{}", log_str)?; + } + }, + // Break on cancel signal, to allow for closing JSON bracket + _ = ctrl_c() => { + break; + }, + else => break, + } + } + + // If output was JSON, end with a closing bracket + if to_json { + write!(output, "]")?; + } + + Ok(()) + } +} + +pub struct InterfaceSource { + pub name: String, + pub json_abi: String, + pub source: String, +} + +// Local is a path to the directory containing the ABI files +// In case of etherscan, ABI is fetched from the address on the chain +pub enum AbiPath { + Local { path: String, name: Option }, + Etherscan { address: Address, chain: Chain, api_key: String }, +} + +pub struct SimpleCast; + +impl SimpleCast { + /// Returns the maximum value of the given integer type + /// + /// # Example + /// + /// ``` + /// # use cast::SimpleCast; + /// # use ethers_core::types::{I256, U256}; + /// assert_eq!(SimpleCast::max_int("uint256")?, format!("{}", U256::MAX)); + /// assert_eq!(SimpleCast::max_int("int256")?, format!("{}", I256::MAX)); + /// assert_eq!(SimpleCast::max_int("int32")?, format!("{}", i32::MAX)); + /// # Ok::<(), eyre::Report>(()) + /// ``` + pub fn max_int(s: &str) -> Result { + Self::max_min_int::(s) + } + + /// Returns the maximum value of the given integer type + /// + /// # Example + /// + /// ``` + /// # use cast::SimpleCast; + /// # use ethers_core::types::{I256, U256}; + /// assert_eq!(SimpleCast::min_int("uint256")?, "0"); + /// assert_eq!(SimpleCast::min_int("int256")?, format!("{}", I256::MIN)); + /// assert_eq!(SimpleCast::min_int("int32")?, format!("{}", i32::MIN)); + /// # Ok::<(), eyre::Report>(()) + /// ``` + pub fn min_int(s: &str) -> Result { + Self::max_min_int::(s) + } + + fn max_min_int(s: &str) -> Result { + let ty = HumanReadableParser::parse_type(s) + .wrap_err("Invalid type, expected `(u)int`")?; + match ty { + ParamType::Int(n) => { + let mask = U256::one() << U256::from(n - 1); + let max = (U256::MAX & mask) - 1; + if MAX { + Ok(max.to_string()) + } else { + let min = I256::from_raw(max).wrapping_neg() + I256::minus_one(); + Ok(min.to_string()) + } + } + ParamType::Uint(n) => { + if MAX { + let mut max = U256::MAX; + if n < 255 { + max &= U256::one() << U256::from(n); + } + Ok(max.to_string()) + } else { + Ok("0".to_string()) + } + } + _ => Err(eyre::eyre!("Type is not int/uint: {s}")), + } + } + + /// Converts UTF-8 text input to hex + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::from_utf8("yo"), "0x796f"); + /// assert_eq!(Cast::from_utf8("Hello, World!"), "0x48656c6c6f2c20576f726c6421"); + /// assert_eq!(Cast::from_utf8("TurboDappTools"), "0x547572626f44617070546f6f6c73"); + /// + /// Ok(()) + /// } + /// ``` + pub fn from_utf8(s: &str) -> String { + hex::encode_prefixed(s) + } + + /// Converts hex data into text data + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::to_ascii("0x796f")?, "yo"); + /// assert_eq!(Cast::to_ascii("48656c6c6f2c20576f726c6421")?, "Hello, World!"); + /// assert_eq!(Cast::to_ascii("0x547572626f44617070546f6f6c73")?, "TurboDappTools"); + /// + /// Ok(()) + /// } + /// ``` + pub fn to_ascii(hex: &str) -> Result { + let bytes = hex::decode(hex)?; + if !bytes.iter().all(u8::is_ascii) { + return Err(eyre::eyre!("Invalid ASCII bytes")) + } + Ok(String::from_utf8(bytes).unwrap()) + } + + /// Converts fixed point number into specified number of decimals + /// ``` + /// use cast::SimpleCast as Cast; + /// use ethers_core::types::U256; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::from_fixed_point("10", "0")?, "10"); + /// assert_eq!(Cast::from_fixed_point("1.0", "1")?, "10"); + /// assert_eq!(Cast::from_fixed_point("0.10", "2")?, "10"); + /// assert_eq!(Cast::from_fixed_point("0.010", "3")?, "10"); + /// + /// Ok(()) + /// } + /// ``` + pub fn from_fixed_point(value: &str, decimals: &str) -> Result { + // first try u32 as Units assumes a string can only be "ether", "gwei"... and not a number + let units = match decimals.parse::() { + Ok(d) => Units::Other(d), + Err(_) => Units::try_from(decimals)?, + }; + let n: NumberWithBase = parse_units(value, units.as_num())?.into(); + Ok(format!("{n}")) + } + + /// Converts integers with specified decimals into fixed point numbers + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// use ethers_core::types::U256; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::to_fixed_point("10", "0")?, "10."); + /// assert_eq!(Cast::to_fixed_point("10", "1")?, "1.0"); + /// assert_eq!(Cast::to_fixed_point("10", "2")?, "0.10"); + /// assert_eq!(Cast::to_fixed_point("10", "3")?, "0.010"); + /// + /// assert_eq!(Cast::to_fixed_point("-10", "0")?, "-10."); + /// assert_eq!(Cast::to_fixed_point("-10", "1")?, "-1.0"); + /// assert_eq!(Cast::to_fixed_point("-10", "2")?, "-0.10"); + /// assert_eq!(Cast::to_fixed_point("-10", "3")?, "-0.010"); + /// + /// Ok(()) + /// } + /// ``` + pub fn to_fixed_point(value: &str, decimals: &str) -> Result { + let (sign, mut value, value_len) = { + let number = NumberWithBase::parse_int(value, None)?; + let sign = if number.is_nonnegative() { "" } else { "-" }; + let value = format!("{number:#}"); + let value_stripped = value.strip_prefix('-').unwrap_or(&value).to_string(); + let value_len = value_stripped.len(); + (sign, value_stripped, value_len) + }; + let decimals = NumberWithBase::parse_uint(decimals, None)?.number().low_u64() as usize; + + let value = if decimals >= value_len { + // Add "0." and pad with 0s + format!("0.{value:0>decimals$}") + } else { + // Insert decimal at -idx (i.e 1 => decimal idx = -1) + value.insert(value_len - decimals, '.'); + value + }; + + Ok(format!("{sign}{value}")) + } + + /// Concatencates hex strings + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::concat_hex(["0x00", "0x01"]), "0x0001"); + /// assert_eq!(Cast::concat_hex(["1", "2"]), "0x12"); + /// + /// Ok(()) + /// } + /// ``` + pub fn concat_hex>(values: impl IntoIterator) -> String { + let mut out = String::new(); + for s in values { + let s = s.as_ref(); + out.push_str(s.strip_prefix("0x").unwrap_or(s)) + } + format!("0x{out}") + } + + /// Converts an Ethereum address to its checksum format + /// according to [EIP-55](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md) + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// use ethers_core::types::Address; + /// use std::str::FromStr; + /// + /// # fn main() -> eyre::Result<()> { + /// let addr = Address::from_str("0xb7e390864a90b7b923c9f9310c6f98aafe43f707")?; + /// let addr = Cast::to_checksum_address(&addr); + /// assert_eq!(addr, "0xB7e390864a90b7b923C9f9310C6F98aafE43F707"); + /// + /// # Ok(()) + /// # } + /// ``` + pub fn to_checksum_address(address: &Address) -> String { + ethers_core::utils::to_checksum(address, None) + } + + /// Converts a number into uint256 hex string with 0x prefix + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::to_uint256("100")?, "0x0000000000000000000000000000000000000000000000000000000000000064"); + /// assert_eq!(Cast::to_uint256("192038293923")?, "0x0000000000000000000000000000000000000000000000000000002cb65fd1a3"); + /// assert_eq!( + /// Cast::to_uint256("115792089237316195423570985008687907853269984665640564039457584007913129639935")?, + /// "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + /// ); + /// + /// Ok(()) + /// } + /// ``` + pub fn to_uint256(value: &str) -> Result { + let n = NumberWithBase::parse_uint(value, None)?; + Ok(format!("{n:#066x}")) + } + + /// Converts a number into int256 hex string with 0x prefix + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::to_int256("0")?, "0x0000000000000000000000000000000000000000000000000000000000000000"); + /// assert_eq!(Cast::to_int256("100")?, "0x0000000000000000000000000000000000000000000000000000000000000064"); + /// assert_eq!(Cast::to_int256("-100")?, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c"); + /// assert_eq!(Cast::to_int256("192038293923")?, "0x0000000000000000000000000000000000000000000000000000002cb65fd1a3"); + /// assert_eq!(Cast::to_int256("-192038293923")?, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffd349a02e5d"); + /// assert_eq!( + /// Cast::to_int256("57896044618658097711785492504343953926634992332820282019728792003956564819967")?, + /// "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + /// ); + /// assert_eq!( + /// Cast::to_int256("-57896044618658097711785492504343953926634992332820282019728792003956564819968")?, + /// "0x8000000000000000000000000000000000000000000000000000000000000000" + /// ); + /// + /// Ok(()) + /// } + /// ``` + pub fn to_int256(value: &str) -> Result { + let n = NumberWithBase::parse_int(value, None)?; + Ok(format!("{n:#066x}")) + } + + /// Converts an eth amount into a specified unit + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::to_unit("1 wei", "wei")?, "1"); + /// assert_eq!(Cast::to_unit("1", "wei")?, "1"); + /// assert_eq!(Cast::to_unit("1ether", "wei")?, "1000000000000000000"); + /// assert_eq!(Cast::to_unit("100 gwei", "gwei")?, "100"); + /// + /// Ok(()) + /// } + /// ``` + pub fn to_unit(value: &str, unit: &str) -> Result { + let value = U256::from(LenientTokenizer::tokenize_uint(value)?); + + Ok(match unit { + "eth" | "ether" => ethers_core::utils::format_units(value, 18)? + .trim_end_matches(".000000000000000000") + .to_string(), + "milli" | "milliether" => ethers_core::utils::format_units(value, 15)? + .trim_end_matches(".000000000000000") + .to_string(), + "micro" | "microether" => ethers_core::utils::format_units(value, 12)? + .trim_end_matches(".000000000000") + .to_string(), + "gwei" | "nano" | "nanoether" => ethers_core::utils::format_units(value, 9)? + .trim_end_matches(".000000000") + .to_string(), + "mwei" | "mega" | "megaether" => { + ethers_core::utils::format_units(value, 6)?.trim_end_matches(".000000").to_string() + } + "kwei" | "kilo" | "kiloether" => { + ethers_core::utils::format_units(value, 3)?.trim_end_matches(".000").to_string() + } + "wei" => ethers_core::utils::format_units(value, 0)?.trim_end_matches(".0").to_string(), + _ => eyre::bail!("invalid unit: \"{}\"", unit), + }) + } + + /// Converts wei into an eth amount + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::from_wei("1", "gwei")?, "0.000000001"); + /// assert_eq!(Cast::from_wei("12340000005", "gwei")?, "12.340000005"); + /// assert_eq!(Cast::from_wei("10", "ether")?, "0.000000000000000010"); + /// assert_eq!(Cast::from_wei("100", "eth")?, "0.000000000000000100"); + /// assert_eq!(Cast::from_wei("17", "")?, "0.000000000000000017"); + /// + /// Ok(()) + /// } + /// ``` + pub fn from_wei(value: &str, unit: &str) -> Result { + let value = NumberWithBase::parse_int(value, None)?.number(); + + Ok(match unit { + "gwei" => format_units(value, 9), + _ => format_units(value, 18), + }?) + } + + /// Converts an eth amount into wei + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::to_wei("1", "")?, "1000000000000000000"); + /// assert_eq!(Cast::to_wei("100", "gwei")?, "100000000000"); + /// assert_eq!(Cast::to_wei("100", "eth")?, "100000000000000000000"); + /// assert_eq!(Cast::to_wei("1000", "ether")?, "1000000000000000000000"); + /// + /// Ok(()) + /// } + /// ``` + pub fn to_wei(value: &str, unit: &str) -> Result { + let wei = match unit { + "gwei" => parse_units(value, 9), + _ => parse_units(value, 18), + }?; + Ok(wei.to_string()) + } + + /// Decodes rlp encoded list with hex data + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::from_rlp("0xc0".to_string()).unwrap(), "[]"); + /// assert_eq!(Cast::from_rlp("0x0f".to_string()).unwrap(), "\"0x0f\""); + /// assert_eq!(Cast::from_rlp("0x33".to_string()).unwrap(), "\"0x33\""); + /// assert_eq!(Cast::from_rlp("0xc161".to_string()).unwrap(), "[\"0x61\"]"); + /// assert_eq!(Cast::from_rlp("0xc26162".to_string()).unwrap(), "[\"0x61\",\"0x62\"]"); + /// Ok(()) + /// } + /// ``` + pub fn from_rlp(value: impl AsRef) -> Result { + let bytes = hex::decode(value.as_ref()).wrap_err("Could not decode hex")?; + let item = rlp::decode::(&bytes).wrap_err("Could not decode rlp")?; + Ok(item.to_string()) + } + + /// Encodes hex data or list of hex data to hexadecimal rlp + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::to_rlp("[]").unwrap(),"0xc0".to_string()); + /// assert_eq!(Cast::to_rlp("0x22").unwrap(),"0x22".to_string()); + /// assert_eq!(Cast::to_rlp("[\"0x61\"]",).unwrap(), "0xc161".to_string()); + /// assert_eq!(Cast::to_rlp( "[\"0xf1\",\"f2\"]").unwrap(), "0xc481f181f2".to_string()); + /// Ok(()) + /// } + /// ``` + pub fn to_rlp(value: &str) -> Result { + let val = serde_json::from_str(value) + .unwrap_or_else(|_| serde_json::Value::String(value.to_string())); + let item = Item::value_to_item(&val)?; + Ok(format!("0x{}", hex::encode(rlp::encode(&item)))) + } + + /// Converts a number of one base to another + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// use ethers_core::types::{I256, U256}; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::to_base("100", Some("10".to_string()), "16")?, "0x64"); + /// assert_eq!(Cast::to_base("100", Some("10".to_string()), "oct")?, "0o144"); + /// assert_eq!(Cast::to_base("100", Some("10".to_string()), "binary")?, "0b1100100"); + /// + /// assert_eq!(Cast::to_base("0xffffffffffffffff", None, "10")?, u64::MAX.to_string()); + /// assert_eq!(Cast::to_base("0xffffffffffffffffffffffffffffffff", None, "dec")?, u128::MAX.to_string()); + /// // U256::MAX overflows as internally it is being parsed as I256 + /// assert_eq!(Cast::to_base("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", None, "decimal")?, I256::MAX.to_string()); + /// + /// Ok(()) + /// } + /// ``` + pub fn to_base(value: &str, base_in: Option, base_out: &str) -> Result { + let base_in = Base::unwrap_or_detect(base_in, value)?; + let base_out: Base = base_out.parse()?; + if base_in == base_out { + return Ok(value.to_string()) + } + + let mut n = NumberWithBase::parse_int(value, Some(base_in.to_string()))?; + n.set_base(base_out); + + // Use Debug fmt + Ok(format!("{n:#?}")) + } + + /// Converts hexdata into bytes32 value + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// # fn main() -> eyre::Result<()> { + /// let bytes = Cast::to_bytes32("1234")?; + /// assert_eq!(bytes, "0x1234000000000000000000000000000000000000000000000000000000000000"); + /// + /// let bytes = Cast::to_bytes32("0x1234")?; + /// assert_eq!(bytes, "0x1234000000000000000000000000000000000000000000000000000000000000"); + /// + /// let err = Cast::to_bytes32("0x123400000000000000000000000000000000000000000000000000000000000011").unwrap_err(); + /// assert_eq!(err.to_string(), "string >32 bytes"); + /// + /// # Ok(()) + /// # } + pub fn to_bytes32(s: &str) -> Result { + let s = strip_0x(s); + if s.len() > 64 { + eyre::bail!("string >32 bytes"); + } + + let padded = format!("{s:0<64}"); + // need to use the Debug implementation + Ok(format!("{:?}", H256::from_str(&padded)?)) + } + + /// Encodes string into bytes32 value + pub fn format_bytes32_string(s: &str) -> Result { + let formatted = format_bytes32_string(s)?; + Ok(hex::encode_prefixed(formatted)) + } + + /// Decodes string from bytes32 value + pub fn parse_bytes32_string(s: &str) -> Result { + let bytes = hex::decode(s)?; + if bytes.len() != 32 { + eyre::bail!("expected 64 byte hex-string, got {}", strip_0x(s)); + } + + let mut buffer = [0u8; 32]; + buffer.copy_from_slice(&bytes); + + Ok(parse_bytes32_string(&buffer)?.to_owned()) + } + + /// Decodes checksummed address from bytes32 value + pub fn parse_bytes32_address(s: &str) -> Result { + let s = strip_0x(s); + if s.len() != 64 { + eyre::bail!("expected 64 byte hex-string, got {s}"); + } + + let s = if let Some(stripped) = s.strip_prefix("000000000000000000000000") { + stripped + } else { + return Err(eyre::eyre!("Not convertible to address, there are non-zero bytes")) + }; + + let lowercase_address_string = format!("0x{s}"); + let lowercase_address = Address::from_str(&lowercase_address_string)?; + + Ok(ethers_core::utils::to_checksum(&lowercase_address, None)) + } + + /// Decodes abi-encoded hex input or output + /// + /// When `input=true`, `calldata` string MUST not be prefixed with function selector + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// // Passing `input = false` will decode the data as the output type. + /// // The input data types and the full function sig are ignored, i.e. + /// // you could also pass `balanceOf()(uint256)` and it'd still work. + /// let data = "0x0000000000000000000000000000000000000000000000000000000000000001"; + /// let sig = "balanceOf(address, uint256)(uint256)"; + /// let decoded = Cast::abi_decode(sig, data, false)?[0].to_string(); + /// assert_eq!(decoded, "1"); + /// + /// // Passing `input = true` will decode the data with the input function signature. + /// // We exclude the "prefixed" function selector from the data field (the first 4 bytes). + /// let data = "0x0000000000000000000000008dbd1b711dc621e1404633da156fcc779e1c6f3e000000000000000000000000d9f3c9cc99548bf3b44a43e0a2d07399eb918adc000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000"; + /// let sig = "safeTransferFrom(address, address, uint256, uint256, bytes)"; + /// let decoded = Cast::abi_decode(sig, data, true)?; + /// let decoded = decoded.iter().map(ToString::to_string).collect::>(); + /// assert_eq!( + /// decoded, + /// vec!["8dbd1b711dc621e1404633da156fcc779e1c6f3e", "d9f3c9cc99548bf3b44a43e0a2d07399eb918adc", "2a", "1", ""] + /// ); + /// + /// + /// # Ok(()) + /// } + /// ``` + pub fn abi_decode(sig: &str, calldata: &str, input: bool) -> Result> { + foundry_common::abi::abi_decode(sig, calldata, input, false) + } + + /// Decodes calldata-encoded hex input or output + /// + /// Similar to `abi_decode`, but `calldata` string MUST be prefixed with function selector + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// // Passing `input = false` will decode the data as the output type. + /// // The input data types and the full function sig are ignored, i.e. + /// // you could also pass `balanceOf()(uint256)` and it'd still work. + /// let data = "0x0000000000000000000000000000000000000000000000000000000000000001"; + /// let sig = "balanceOf(address, uint256)(uint256)"; + /// let decoded = Cast::calldata_decode(sig, data, false)?[0].to_string(); + /// assert_eq!(decoded, "1"); + /// + /// // Passing `input = true` will decode the data with the input function signature. + /// let data = "0xf242432a0000000000000000000000008dbd1b711dc621e1404633da156fcc779e1c6f3e000000000000000000000000d9f3c9cc99548bf3b44a43e0a2d07399eb918adc000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000"; + /// let sig = "safeTransferFrom(address, address, uint256, uint256, bytes)"; + /// let decoded = Cast::calldata_decode(sig, data, true)?; + /// let decoded = decoded.iter().map(ToString::to_string).collect::>(); + /// assert_eq!( + /// decoded, + /// vec!["8dbd1b711dc621e1404633da156fcc779e1c6f3e", "d9f3c9cc99548bf3b44a43e0a2d07399eb918adc", "2a", "1", ""] + /// ); + /// + /// + /// # Ok(()) + /// } + /// ``` + pub fn calldata_decode(sig: &str, calldata: &str, input: bool) -> Result> { + foundry_common::abi::abi_decode(sig, calldata, input, true) + } + + /// Performs ABI encoding based off of the function signature. Does not include + /// the function selector in the result. + /// + /// # Example + /// + /// ``` + /// # use cast::SimpleCast as Cast; + /// + /// # fn main() -> eyre::Result<()> { + /// assert_eq!( + /// "0x0000000000000000000000000000000000000000000000000000000000000001", + /// Cast::abi_encode("f(uint a)", &["1"]).unwrap().as_str() + /// ); + /// assert_eq!( + /// "0x0000000000000000000000000000000000000000000000000000000000000001", + /// Cast::abi_encode("constructor(uint a)", &["1"]).unwrap().as_str() + /// ); + /// # Ok(()) + /// # } + /// ``` + pub fn abi_encode(sig: &str, args: &[impl AsRef]) -> Result { + let func = match HumanReadableParser::parse_function(sig) { + Ok(func) => func, + Err(err) => { + if let Ok(constructor) = HumanReadableParser::parse_constructor(sig) { + #[allow(deprecated)] + Function { + name: "constructor".to_string(), + inputs: constructor.inputs, + outputs: vec![], + constant: None, + state_mutability: Default::default(), + } + } else { + // we return the `Function` parse error as this case is more likely + eyre::bail!("Could not process human-readable ABI. Please check if you've left the parenthesis unclosed or if some type is incomplete.\nError:\n{}", err) + // return Err(err.into()).wrap_err("Could not process human-readable ABI. Please + // check if you've left the parenthesis unclosed or if some type is + // incomplete.") + } + } + }; + let calldata = match encode_args(&func, args) { + Ok(res) => hex::encode(res), + Err(e) => eyre::bail!("Could not ABI encode the function and arguments. Did you pass in the right types?\nError\n{}", e), + }; + let encoded = &calldata[8..]; + Ok(format!("0x{encoded}")) + } + + /// Performs ABI encoding to produce the hexadecimal calldata with the given arguments. + /// + /// # Example + /// + /// ``` + /// # use cast::SimpleCast as Cast; + /// + /// # fn main() -> eyre::Result<()> { + /// assert_eq!( + /// "0xb3de648b0000000000000000000000000000000000000000000000000000000000000001", + /// Cast::calldata_encode("f(uint a)", &["1"]).unwrap().as_str() + /// ); + /// # Ok(()) + /// # } + /// ``` + pub fn calldata_encode(sig: impl AsRef, args: &[impl AsRef]) -> Result { + let func = HumanReadableParser::parse_function(sig.as_ref())?; + let calldata = encode_args(&func, args)?; + Ok(hex::encode_prefixed(calldata)) + } + + /// Generates an interface in solidity from either a local file ABI or a verified contract on + /// Etherscan. It returns a vector of [`InterfaceSource`] structs that contain the source of the + /// interface and their name. + /// ```no_run + /// use cast::SimpleCast as Cast; + /// use cast::AbiPath; + /// # async fn foo() -> eyre::Result<()> { + /// let path = AbiPath::Local { + /// path: "utils/testdata/interfaceTestABI.json".to_owned(), + /// name: None, + /// }; + /// let interfaces= Cast::generate_interface(path).await?; + /// println!("interface {} {{\n {}\n}}", interfaces[0].name, interfaces[0].source); + /// # Ok(()) + /// # } + /// ``` + pub async fn generate_interface(address_or_path: AbiPath) -> Result> { + let (contract_abis, contract_names): (Vec, Vec) = match address_or_path { + AbiPath::Local { path, name } => { + let file = std::fs::read_to_string(path).wrap_err("unable to read abi file")?; + let mut json: serde_json::Value = serde_json::from_str(&file)?; + let json = if !json["abi"].is_null() { json["abi"].take() } else { json }; + let abi: RawAbi = + serde_json::from_value(json).wrap_err("unable to parse json ABI from file")?; + + (vec![abi], vec![name.unwrap_or_else(|| "Interface".to_owned())]) + } + AbiPath::Etherscan { address, chain, api_key } => { + let client = Client::new(chain, api_key)?; + + // get the source + let source = match client.contract_source_code(address).await { + Ok(source) => source, + Err(EtherscanError::InvalidApiKey) => { + eyre::bail!("Invalid Etherscan API key. Did you set it correctly? You may be using an API key for another Etherscan API chain (e.g. Etherscan API key for Polygonscan).") + } + Err(EtherscanError::ContractCodeNotVerified(address)) => { + eyre::bail!("Contract source code at {:?} on {} not verified. Maybe you have selected the wrong chain?", address, chain) + } + Err(err) => { + eyre::bail!(err) + } + }; + + let names = source + .items + .iter() + .map(|item| item.contract_name.clone()) + .collect::>(); + + let abis = source.raw_abis()?; + + (abis, names) + } + }; + contract_abis + .iter() + .zip(contract_names) + .map(|(contract_abi, name)| { + let source = foundry_utils::abi::abi_to_solidity(contract_abi, &name)?; + Ok(InterfaceSource { + name, + json_abi: serde_json::to_string_pretty(contract_abi)?, + source, + }) + }) + .collect::>>() + } + + /// Prints the slot number for the specified mapping type and input data + /// Uses abi_encode to pad the data to 32 bytes. + /// For value types v, slot number of v is keccak256(concat(h(v) , p)) where h is the padding + /// function and p is slot number of the mapping. + /// + /// # Example + /// + /// ``` + /// # use cast::SimpleCast as Cast; + /// + /// # fn main() -> eyre::Result<()> { + /// + /// assert_eq!(Cast::index("address", "0xD0074F4E6490ae3f888d1d4f7E3E43326bD3f0f5" ,"2").unwrap().as_str(),"0x9525a448a9000053a4d151336329d6563b7e80b24f8e628e95527f218e8ab5fb"); + /// assert_eq!(Cast::index("uint256","42" ,"6").unwrap().as_str(),"0xfc808b0f31a1e6b9cf25ff6289feae9b51017b392cc8e25620a94a38dcdafcc1"); + /// # Ok(()) + /// # } + /// ``` + pub fn index(from_type: &str, from_value: &str, slot_number: &str) -> Result { + let sig = format!("x({from_type},uint256)"); + let encoded = Self::abi_encode(&sig, &[from_value, slot_number])?; + let location: String = Self::keccak(&encoded)?; + Ok(location) + } + + /// Converts ENS names to their namehash representation + /// [Namehash reference](https://docs.ens.domains/contract-api-reference/name-processing#hashing-names) + /// [namehash-rust reference](https://github.com/InstateDev/namehash-rust/blob/master/src/lib.rs) + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::namehash("")?, "0x0000000000000000000000000000000000000000000000000000000000000000"); + /// assert_eq!(Cast::namehash("eth")?, "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"); + /// assert_eq!(Cast::namehash("foo.eth")?, "0xde9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f"); + /// assert_eq!(Cast::namehash("sub.foo.eth")?, "0x500d86f9e663479e5aaa6e99276e55fc139c597211ee47d17e1e92da16a83402"); + /// + /// Ok(()) + /// } + /// ``` + pub fn namehash(ens: &str) -> Result { + let mut node = vec![0u8; 32]; + + if !ens.is_empty() { + let ens_lower = ens.to_lowercase(); + let mut labels: Vec<&str> = ens_lower.split('.').collect(); + labels.reverse(); + + for label in labels { + let mut label_hash = keccak256(label.as_bytes()); + node.append(&mut label_hash.to_vec()); + + label_hash = keccak256(node.as_slice()); + node = label_hash.to_vec(); + } + } + + Ok(hex::encode_prefixed(node)) + } + + /// Keccak-256 hashes arbitrary data + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::keccak("foo")?, "0x41b1a0649752af1b28b3dc29a1556eee781e4a4c3a1f7f53f90fa834de098c4d"); + /// assert_eq!(Cast::keccak("123abc")?, "0xb1f1c74a1ba56f07a892ea1110a39349d40f66ca01d245e704621033cb7046a4"); + /// assert_eq!(Cast::keccak("0x12")?, "0x5fa2358263196dbbf23d1ca7a509451f7a2f64c15837bfbb81298b1e3e24e4fa"); + /// assert_eq!(Cast::keccak("12")?, "0x7f8b6b088b6d74c2852fc86c796dca07b44eed6fb3daf5e6b59f7c364db14528"); + /// + /// Ok(()) + /// } + /// ``` + pub fn keccak(data: &str) -> Result { + let hash = match data.as_bytes() { + // 0x prefix => read as hex data + [b'0', b'x', rest @ ..] => keccak256(hex::decode(rest)?), + // No 0x prefix => read as text + _ => keccak256(data), + }; + + Ok(format!("{:?}", H256(hash))) + } + + /// Performs the left shift operation (<<) on a number + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::left_shift("16", "10", Some("10".to_string()), "hex")?, "0x4000"); + /// assert_eq!(Cast::left_shift("255", "16", Some("dec".to_string()), "hex")?, "0xff0000"); + /// assert_eq!(Cast::left_shift("0xff", "16", None, "hex")?, "0xff0000"); + /// + /// Ok(()) + /// } + /// ``` + pub fn left_shift( + value: &str, + bits: &str, + base_in: Option, + base_out: &str, + ) -> Result { + let base_out: Base = base_out.parse()?; + let value = NumberWithBase::parse_uint(value, base_in)?; + let bits = NumberWithBase::parse_uint(bits, None)?; + + let res = value.number() << bits.number(); + + Ok(res.to_base(base_out, true)?) + } + + /// Performs the right shift operation (>>) on a number + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::right_shift("0x4000", "10", None, "dec")?, "16"); + /// assert_eq!(Cast::right_shift("16711680", "16", Some("10".to_string()), "hex")?, "0xff"); + /// assert_eq!(Cast::right_shift("0xff0000", "16", None, "hex")?, "0xff"); + /// + /// Ok(()) + /// } + /// ``` + pub fn right_shift( + value: &str, + bits: &str, + base_in: Option, + base_out: &str, + ) -> Result { + let base_out: Base = base_out.parse()?; + let value = NumberWithBase::parse_uint(value, base_in)?; + let bits = NumberWithBase::parse_uint(bits, None)?; + + let res = value.number() >> bits.number(); + + Ok(res.to_base(base_out, true)?) + } + + /// Fetches source code of verified contracts from etherscan. + /// + /// # Example + /// + /// ``` + /// # use cast::SimpleCast as Cast; + /// # use ethers_core::types::Chain; + /// + /// # async fn foo() -> eyre::Result<()> { + /// assert_eq!( + /// "/* + /// - Bytecode Verification performed was compared on second iteration - + /// This file is part of the DAO.....", + /// Cast::etherscan_source(Chain::Mainnet, "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413".to_string(), "".to_string()).await.unwrap().as_str() + /// ); + /// # Ok(()) + /// # } + /// ``` + pub async fn etherscan_source( + chain: Chain, + contract_address: String, + etherscan_api_key: String, + ) -> Result { + let client = Client::new(chain, etherscan_api_key)?; + let metadata = client.contract_source_code(contract_address.parse()?).await?; + Ok(metadata.source_code()) + } + + /// Fetches the source code of verified contracts from etherscan and expands the resulting + /// files to a directory for easy perusal. + /// + /// # Example + /// + /// ``` + /// # use cast::SimpleCast as Cast; + /// # use ethers_core::types::Chain; + /// # use std::path::PathBuf; + /// + /// # async fn expand() -> eyre::Result<()> { + /// Cast::expand_etherscan_source_to_directory(Chain::Mainnet, "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413".to_string(), "".to_string(), PathBuf::from("output_dir")).await?; + /// # Ok(()) + /// # } + /// ``` + pub async fn expand_etherscan_source_to_directory( + chain: Chain, + contract_address: String, + etherscan_api_key: String, + output_directory: PathBuf, + ) -> eyre::Result<()> { + let client = Client::new(chain, etherscan_api_key)?; + let meta = client.contract_source_code(contract_address.parse()?).await?; + let source_tree = meta.source_tree(); + source_tree.write_to(&output_directory)?; + Ok(()) + } + + /// Disassembles hex encoded bytecode into individual / human readable opcodes + /// + /// # Example + /// + /// ```no_run + /// use cast::SimpleCast as Cast; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let bytecode = "0x608060405260043610603f57600035"; + /// let opcodes = Cast::disassemble(bytecode)?; + /// println!("{}", opcodes); + /// # Ok(()) + /// # } + /// ``` + pub fn disassemble(bytecode: &str) -> Result { + format_operations(disassemble_str(bytecode)?) + } + + /// Gets the selector for a given function signature + /// Optimizes if the `optimize` parameter is set to a number of leading zeroes + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// assert_eq!(Cast::get_selector("foo(address,uint256)", None)?.0, String::from("0xbd0d639f")); + /// + /// Ok(()) + /// } + /// ``` + pub fn get_selector(signature: &str, optimize: Option) -> Result<(String, String)> { + let optimize = optimize.unwrap_or(0); + if optimize > 4 { + eyre::bail!("Number of leading zeroes must not be greater than 4"); + } + if optimize == 0 { + let selector = HumanReadableParser::parse_function(signature)?.short_signature(); + return Ok((hex::encode_prefixed(selector), String::from(signature))) + } + let Some((name, params)) = signature.split_once('(') else { + eyre::bail!("Invalid signature"); + }; + + let num_threads = std::thread::available_parallelism().map_or(1, |n| n.get()); + let found = AtomicBool::new(false); + + let result: Option<(u32, String, String)> = + (0..num_threads).into_par_iter().find_map_any(|i| { + let nonce_start = i as u32; + let nonce_step = num_threads as u32; + + let mut nonce = nonce_start; + while nonce < u32::MAX && !found.load(Ordering::Relaxed) { + let input = format!("{}{}({}", name, nonce, params); + let hash = keccak256(input.as_bytes()); + let selector = &hash[..4]; + + if selector.iter().take_while(|&&byte| byte == 0).count() == optimize { + found.store(true, Ordering::Relaxed); + return Some((nonce, hex::encode_prefixed(selector), input)) + } + + nonce += nonce_step; + } + None + }); + + match result { + Some((_nonce, selector, signature)) => Ok((selector, signature)), + None => eyre::bail!("No selector found"), + } + } + + /// Decodes a raw EIP2718 transaction payload + /// Returns details about the typed transaction and ECSDA signature components + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// fn main() -> eyre::Result<()> { + /// let tx = "0x02f8f582a86a82058d8459682f008508351050808303fd84948e42f2f4101563bf679975178e880fd87d3efd4e80b884659ac74b00000000000000000000000080f0c1c49891dcfdd40b6e0f960f84e6042bcb6f000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e00000000000000000000000000000000000000000000000000000000007ff4e20000000000000000000000000000000000000000000000000000000000000064c001a05d429597befe2835396206781b199122f2e8297327ed4a05483339e7a8b2022aa04c23a7f70fb29dda1b4ee342fb10a625e9b8ddc6a603fb4e170d4f6f37700cb8"; + /// let (tx, sig) = Cast::decode_raw_transaction(&tx)?; + /// + /// Ok(()) + /// } + pub fn decode_raw_transaction(tx: &str) -> Result<(TypedTransaction, Signature)> { + let tx_hex = hex::decode(strip_0x(tx))?; + let tx_rlp = rlp::Rlp::new(tx_hex.as_slice()); + Ok(TypedTransaction::decode_signed(&tx_rlp)?) + } +} + +fn strip_0x(s: &str) -> &str { + s.strip_prefix("0x").unwrap_or(s) +} + +#[cfg(test)] +mod tests { + use super::SimpleCast as Cast; + + #[test] + fn calldata_uint() { + assert_eq!( + "0xb3de648b0000000000000000000000000000000000000000000000000000000000000001", + Cast::calldata_encode("f(uint a)", &["1"]).unwrap().as_str() + ); + } + + // + #[test] + fn calldata_array() { + assert_eq!( + "0xcde2baba0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000", + Cast::calldata_encode("propose(string[])", &["[\"\"]"]).unwrap().as_str() + ); + } + + #[test] + fn calldata_bool() { + assert_eq!( + "0x6fae94120000000000000000000000000000000000000000000000000000000000000000", + Cast::calldata_encode("bar(bool)", &["false"]).unwrap().as_str() + ); + } + + #[test] + fn abi_decode() { + let data = "0x0000000000000000000000000000000000000000000000000000000000000001"; + let sig = "balanceOf(address, uint256)(uint256)"; + assert_eq!("1", Cast::abi_decode(sig, data, false).unwrap()[0].to_string()); + + let data = "0x0000000000000000000000008dbd1b711dc621e1404633da156fcc779e1c6f3e000000000000000000000000d9f3c9cc99548bf3b44a43e0a2d07399eb918adc000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000"; + let sig = "safeTransferFrom(address,address,uint256,uint256,bytes)"; + let decoded = Cast::abi_decode(sig, data, true).unwrap(); + let decoded = decoded.iter().map(ToString::to_string).collect::>(); + assert_eq!( + decoded, + vec![ + "8dbd1b711dc621e1404633da156fcc779e1c6f3e", + "d9f3c9cc99548bf3b44a43e0a2d07399eb918adc", + "2a", + "1", + "" + ] + ); + } + + #[test] + fn calldata_decode() { + let data = "0x0000000000000000000000000000000000000000000000000000000000000001"; + let sig = "balanceOf(address, uint256)(uint256)"; + let decoded = Cast::calldata_decode(sig, data, false).unwrap()[0].to_string(); + assert_eq!(decoded, "1"); + + // Passing `input = true` will decode the data with the input function signature. + // We exclude the "prefixed" function selector from the data field (the first 4 bytes). + let data = "0xf242432a0000000000000000000000008dbd1b711dc621e1404633da156fcc779e1c6f3e000000000000000000000000d9f3c9cc99548bf3b44a43e0a2d07399eb918adc000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000"; + let sig = "safeTransferFrom(address, address, uint256, uint256, bytes)"; + let decoded = Cast::calldata_decode(sig, data, true).unwrap(); + let decoded = decoded.iter().map(ToString::to_string).collect::>(); + assert_eq!( + decoded, + vec![ + "8dbd1b711dc621e1404633da156fcc779e1c6f3e", + "d9f3c9cc99548bf3b44a43e0a2d07399eb918adc", + "2a", + "1", + "" + ] + ); + } + + #[test] + fn concat_hex() { + assert_eq!(Cast::concat_hex(["0x00", "0x01"]), "0x0001"); + assert_eq!(Cast::concat_hex(["1", "2"]), "0x12"); + } + + #[test] + fn from_rlp() { + let rlp = "0xf8b1a02b5df5f0757397573e8ff34a8b987b21680357de1f6c8d10273aa528a851eaca8080a02838ac1d2d2721ba883169179b48480b2ba4f43d70fcf806956746bd9e83f90380a0e46fff283b0ab96a32a7cc375cecc3ed7b6303a43d64e0a12eceb0bc6bd8754980a01d818c1c414c665a9c9a0e0c0ef1ef87cacb380b8c1f6223cb2a68a4b2d023f5808080a0236e8f61ecde6abfebc6c529441f782f62469d8a2cc47b7aace2c136bd3b1ff08080808080"; + let item = Cast::from_rlp(rlp).unwrap(); + assert_eq!( + item, + r#"["0x2b5df5f0757397573e8ff34a8b987b21680357de1f6c8d10273aa528a851eaca","0x","0x","0x2838ac1d2d2721ba883169179b48480b2ba4f43d70fcf806956746bd9e83f903","0x","0xe46fff283b0ab96a32a7cc375cecc3ed7b6303a43d64e0a12eceb0bc6bd87549","0x","0x1d818c1c414c665a9c9a0e0c0ef1ef87cacb380b8c1f6223cb2a68a4b2d023f5","0x","0x","0x","0x236e8f61ecde6abfebc6c529441f782f62469d8a2cc47b7aace2c136bd3b1ff0","0x","0x","0x","0x","0x"]"# + ) + } +} diff --git a/crates/zkcast/src/rlp_converter.rs b/crates/zkcast/src/rlp_converter.rs new file mode 100644 index 000000000..0ca8597d4 --- /dev/null +++ b/crates/zkcast/src/rlp_converter.rs @@ -0,0 +1,172 @@ +use ethers_core::utils::rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; +use serde_json::Value; +use std::fmt::{Debug, Display, Formatter, Write}; + +/// Arbitrary nested data +/// Item::Array(vec![]); is equivalent to [] +/// Item::Array(vec![Item::Data(vec![])]); is equivalent to [""] or [null] +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum Item { + Data(Vec), + Array(Vec), +} + +impl Encodable for Item { + fn rlp_append(&self, s: &mut RlpStream) { + match self { + Item::Array(arr) => { + s.begin_unbounded_list(); + for item in arr { + s.append(item); + } + s.finalize_unbounded_list(); + } + Item::Data(data) => { + s.append(data); + } + } + } +} + +impl Decodable for Item { + fn decode(rlp: &Rlp) -> Result { + if rlp.is_data() { + return Ok(Item::Data(Vec::from(rlp.data()?))) + } + Ok(Item::Array(rlp.as_list()?)) + } +} + +impl Item { + pub(crate) fn value_to_item(value: &Value) -> eyre::Result { + return match value { + Value::Null => Ok(Item::Data(vec![])), + Value::Bool(_) => { + eyre::bail!("RLP input should not contain booleans") + } + // If a value is passed without quotes we cast it to string + Value::Number(n) => Ok(Item::value_to_item(&Value::String(n.to_string()))?), + Value::String(s) => Ok(Item::Data(hex::decode(s).expect("Could not decode hex"))), + Value::Array(values) => values.iter().map(Item::value_to_item).collect(), + Value::Object(_) => { + eyre::bail!("RLP input can not contain objects") + } + } + } +} + +impl FromIterator for Item { + fn from_iter>(iter: T) -> Self { + Item::Array(iter.into_iter().collect()) + } +} + +// Display as hex values +impl Display for Item { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Item::Data(dat) => { + write!(f, "\"0x{}\"", hex::encode(dat))?; + } + Item::Array(arr) => { + write!(f, "[")?; + let mut iter = arr.iter().peekable(); + while let Some(item) = iter.next() { + write!(f, "{item}")?; + if iter.peek().is_some() { + f.write_char(',')?; + } + } + write!(f, "]")?; + } + }; + Ok(()) + } +} + +#[cfg(test)] +mod test { + use crate::rlp_converter::Item; + use ethers_core::utils::{rlp, rlp::DecoderError}; + use serde_json::Result as JsonResult; + + // https://en.wikipedia.org/wiki/Set-theoretic_definition_of_natural_numbers + fn array_von_neuman() -> Item { + Item::Array(vec![ + Item::Array(vec![]), + Item::Array(vec![Item::Array(vec![])]), + Item::Array(vec![Item::Array(vec![]), Item::Array(vec![Item::Array(vec![])])]), + ]) + } + + #[test] + fn encode_decode_test() -> Result<(), DecoderError> { + let parameters = vec![ + (1, b"\xc0".to_vec(), Item::Array(vec![])), + (2, b"\xc1\x80".to_vec(), Item::Array(vec![Item::Data(vec![])])), + (3, b"\xc4\x83dog".to_vec(), Item::Array(vec![Item::Data(vec![0x64, 0x6f, 0x67])])), + ( + 4, + b"\xc5\xc4\x83dog".to_vec(), + Item::Array(vec![Item::Array(vec![Item::Data(vec![0x64, 0x6f, 0x67])])]), + ), + ( + 5, + b"\xc8\x83dog\x83cat".to_vec(), + Item::Array(vec![ + Item::Data(vec![0x64, 0x6f, 0x67]), + Item::Data(vec![0x63, 0x61, 0x74]), + ]), + ), + (6, b"\xc7\xc0\xc1\xc0\xc3\xc0\xc1\xc0".to_vec(), array_von_neuman()), + ( + 7, + b"\xcd\x83\x6c\x6f\x6c\xc3\xc2\xc1\xc0\xc4\x83\x6f\x6c\x6f".to_vec(), + Item::Array(vec![ + Item::Data(vec![b'\x6c', b'\x6f', b'\x6c']), + Item::Array(vec![Item::Array(vec![Item::Array(vec![Item::Array(vec![])])])]), + Item::Array(vec![Item::Data(vec![b'\x6f', b'\x6c', b'\x6f'])]), + ]), + ), + ]; + for params in parameters { + let encoded = rlp::encode::(¶ms.2); + assert_eq!(rlp::decode::(&encoded)?, params.2); + let decoded = rlp::decode::(¶ms.1); + assert_eq!(rlp::encode::(&decoded?), params.1); + println!("case {} validated", params.0) + } + + Ok(()) + } + + #[test] + fn deserialize_from_str_test_hex() -> JsonResult<()> { + let parameters = vec![ + (1, "[\"\"]", Item::Array(vec![Item::Data(vec![])])), + (2, "[\"0x646f67\"]", Item::Array(vec![Item::Data(vec![0x64, 0x6f, 0x67])])), + ( + 3, + "[[\"646f67\"]]", + Item::Array(vec![Item::Array(vec![Item::Data(vec![0x64, 0x6f, 0x67])])]), + ), + ( + 4, + "[\"646f67\",\"0x636174\"]", + Item::Array(vec![ + Item::Data(vec![0x64, 0x6f, 0x67]), + Item::Data(vec![0x63, 0x61, 0x74]), + ]), + ), + (6, "[[],[[]],[[],[[]]]]", array_von_neuman()), + ]; + for params in parameters { + let val = serde_json::from_str(params.1)?; + let item = Item::value_to_item(&val).unwrap(); + assert_eq!(item, params.2); + println!("case {} validated", params.0); + } + + Ok(()) + } +} diff --git a/crates/zkcast/src/tx.rs b/crates/zkcast/src/tx.rs new file mode 100644 index 000000000..2de54f1e4 --- /dev/null +++ b/crates/zkcast/src/tx.rs @@ -0,0 +1,391 @@ +use crate::errors::FunctionSignatureError; +use ethers_core::{ + abi::Function, + types::{ + transaction::eip2718::TypedTransaction, Eip1559TransactionRequest, NameOrAddress, + TransactionRequest, H160, U256, + }, +}; +use ethers_providers::Middleware; +use eyre::{eyre, Result}; +use foundry_common::abi::{encode_args, get_func, get_func_etherscan}; +use foundry_config::Chain; +use futures::future::join_all; + +pub struct TxBuilder<'a, M: Middleware> { + to: Option, + chain: Chain, + tx: TypedTransaction, + func: Option, + etherscan_api_key: Option, + provider: &'a M, +} + +pub type TxBuilderOutput = (TypedTransaction, Option); +pub type TxBuilderPeekOutput<'a> = (&'a TypedTransaction, &'a Option); + +/// Transaction builder +/// ``` +/// async fn foo() -> eyre::Result<()> { +/// use ethers_core::types::{Chain, U256}; +/// use cast::TxBuilder; +/// let provider = ethers_providers::test_provider::MAINNET.provider(); +/// let mut builder = TxBuilder::new(&provider, "a.eth", Some("b.eth"), Chain::Mainnet, false).await?; +/// builder +/// .gas(Some(U256::from(1))); +/// let (tx, _) = builder.build(); +/// Ok(()) +/// } +/// ``` +impl<'a, M: Middleware> TxBuilder<'a, M> { + /// Create a new TxBuilder + /// `provider` - provider to use + /// `from` - 'from' field. Could be an ENS name + /// `to` - `to`. Could be a ENS + /// `chain` - chain to construct the tx for + /// `legacy` - use type 1 transaction + pub async fn new, T: Into>( + provider: &'a M, + from: F, + to: Option, + chain: impl Into, + legacy: bool, + ) -> Result> { + let chain = chain.into(); + let from_addr = resolve_ens(provider, from).await?; + + let mut tx: TypedTransaction = if chain.is_legacy() || legacy { + TransactionRequest::new().from(from_addr).chain_id(chain.id()).into() + } else { + Eip1559TransactionRequest::new().from(from_addr).chain_id(chain.id()).into() + }; + + let to_addr = if let Some(to) = to { + let addr = + resolve_ens(provider, foundry_utils::resolve_addr(to, chain.try_into().ok())?) + .await?; + tx.set_to(addr); + Some(addr) + } else { + None + }; + Ok(Self { to: to_addr, chain, tx, func: None, etherscan_api_key: None, provider }) + } + + /// Set gas for tx + pub fn set_gas(&mut self, v: U256) -> &mut Self { + self.tx.set_gas(v); + self + } + + /// Set gas for tx, if `v` is not None + pub fn gas(&mut self, v: Option) -> &mut Self { + if let Some(value) = v { + self.set_gas(value); + } + self + } + + /// Set gas price + pub fn set_gas_price(&mut self, v: U256) -> &mut Self { + self.tx.set_gas_price(v); + self + } + + /// Set gas price, if `v` is not None + pub fn gas_price(&mut self, v: Option) -> &mut Self { + if let Some(value) = v { + self.set_gas_price(value); + } + self + } + + /// Set priority gas price + pub fn set_priority_gas_price(&mut self, v: U256) -> &mut Self { + if let TypedTransaction::Eip1559(tx) = &mut self.tx { + tx.max_priority_fee_per_gas = Some(v) + } + self + } + + /// Set priority gas price, if `v` is not None + pub fn priority_gas_price(&mut self, v: Option) -> &mut Self { + if let Some(value) = v { + self.set_priority_gas_price(value); + } + self + } + + /// Set value + pub fn set_value(&mut self, v: U256) -> &mut Self { + self.tx.set_value(v); + self + } + + /// Set value, if `v` is not None + pub fn value(&mut self, v: Option) -> &mut Self { + if let Some(value) = v { + self.set_value(value); + } + self + } + + /// Set nonce + pub fn set_nonce(&mut self, v: U256) -> &mut Self { + self.tx.set_nonce(v); + self + } + + /// Set nonce, if `v` is not None + pub fn nonce(&mut self, v: Option) -> &mut Self { + if let Some(value) = v { + self.set_nonce(value); + } + self + } + + /// Set etherscan API key. Used to look up function signature buy name + pub fn set_etherscan_api_key(&mut self, v: String) -> &mut Self { + self.etherscan_api_key = Some(v); + self + } + + /// Set etherscan API key, if `v` is not None + pub fn etherscan_api_key(&mut self, v: Option) -> &mut Self { + if let Some(value) = v { + self.set_etherscan_api_key(value); + } + self + } + + pub fn set_data(&mut self, v: Vec) -> &mut Self { + self.tx.set_data(v.into()); + self + } + + pub async fn create_args( + &mut self, + sig: &str, + args: Vec, + ) -> Result<(Vec, Function)> { + if sig.trim().is_empty() { + return Err(FunctionSignatureError::MissingSignature.into()) + } + + let args = resolve_name_args(&args, self.provider).await; + + let func = if sig.contains('(') { + // a regular function signature with parentheses + get_func(sig)? + } else if sig.starts_with("0x") { + // if only calldata is provided, returning a dummy function + get_func("x()")? + } else { + let chain = self + .chain + .try_into() + .map_err(|_| FunctionSignatureError::UnknownChain(self.chain))?; + get_func_etherscan( + sig, + self.to.ok_or(FunctionSignatureError::MissingToAddress)?, + &args, + chain, + self.etherscan_api_key.as_ref().ok_or_else(|| { + FunctionSignatureError::MissingEtherscan { sig: sig.to_string() } + })?, + ) + .await? + }; + + if sig.starts_with("0x") { + Ok((hex::decode(sig)?, func)) + } else { + Ok((encode_args(&func, &args)?, func)) + } + } + + /// Set function arguments + /// `sig` can be: + /// * a fragment (`do(uint32,string)`) + /// * selector + abi-encoded calldata + /// (`0xcdba2fd40000000000000000000000000000000000000000000000000000000000007a69`) + /// * only function name (`do`) - in this case, etherscan lookup is performed on `tx.to`'s + /// contract + pub async fn set_args( + &mut self, + sig: &str, + args: Vec, + ) -> Result<&mut TxBuilder<'a, M>> { + let (data, func) = self.create_args(sig, args).await?; + self.tx.set_data(data.into()); + self.func = Some(func); + Ok(self) + } + + /// Set function arguments, if `value` is not None + pub async fn args( + &mut self, + value: Option<(&str, Vec)>, + ) -> Result<&mut TxBuilder<'a, M>> { + if let Some((sig, args)) = value { + return self.set_args(sig, args).await + } + Ok(self) + } + + /// Consuming build: returns typed transaction and optional function call + pub fn build(self) -> TxBuilderOutput { + (self.tx, self.func) + } + + /// Non-consuming build: peek into the tx content + pub fn peek(&self) -> TxBuilderPeekOutput { + (&self.tx, &self.func) + } +} + +async fn resolve_ens>(provider: &M, addr: T) -> Result { + let from_addr = match addr.into() { + NameOrAddress::Name(ref ens_name) => provider.resolve_name(ens_name).await, + NameOrAddress::Address(addr) => Ok(addr), + } + .map_err(|x| eyre!("Failed to resolve ENS name: {x}"))?; + Ok(from_addr) +} + +async fn resolve_name_args(args: &[String], provider: &M) -> Vec { + join_all(args.iter().map(|arg| async { + if arg.contains('.') { + let addr = provider.resolve_name(arg).await; + match addr { + Ok(addr) => format!("{addr:?}"), + Err(_) => arg.to_string(), + } + } else { + arg.to_string() + } + })) + .await +} + +#[cfg(test)] +mod tests { + use crate::TxBuilder; + use async_trait::async_trait; + use ethers_core::types::{ + transaction::eip2718::TypedTransaction, Address, Chain, NameOrAddress, H160, U256, + }; + use ethers_providers::{JsonRpcClient, Middleware, ProviderError}; + use serde::{de::DeserializeOwned, Serialize}; + use std::str::FromStr; + + const ADDR_1: &str = "0000000000000000000000000000000000000001"; + const ADDR_2: &str = "0000000000000000000000000000000000000002"; + + #[derive(Debug)] + struct MyProvider {} + + #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] + #[cfg_attr(not(target_arch = "wasm32"), async_trait)] + impl JsonRpcClient for MyProvider { + type Error = ProviderError; + + async fn request( + &self, + _method: &str, + _params: T, + ) -> Result { + Err(ProviderError::CustomError("There is no request".to_string())) + } + } + #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] + #[cfg_attr(not(target_arch = "wasm32"), async_trait)] + impl Middleware for MyProvider { + type Error = ProviderError; + type Provider = MyProvider; + type Inner = MyProvider; + + fn inner(&self) -> &Self::Inner { + self + } + + async fn resolve_name(&self, ens_name: &str) -> Result { + match ens_name { + "a.eth" => Ok(H160::from_str(ADDR_1).unwrap()), + "b.eth" => Ok(H160::from_str(ADDR_2).unwrap()), + _ => unreachable!("don't know how to resolve {ens_name}"), + } + } + } + #[tokio::test(flavor = "multi_thread")] + async fn builder_new_non_legacy() -> eyre::Result<()> { + let provider = MyProvider {}; + let builder = + TxBuilder::new(&provider, "a.eth", Some("b.eth"), Chain::Mainnet, false).await?; + let (tx, args) = builder.build(); + assert_eq!(*tx.from().unwrap(), H160::from_str(ADDR_1).unwrap()); + assert_eq!(*tx.to().unwrap(), NameOrAddress::Address(H160::from_str(ADDR_2).unwrap())); + assert_eq!(args, None); + + match tx { + TypedTransaction::Eip1559(_) => {} + _ => { + panic!("Wrong tx type"); + } + } + Ok(()) + } + + #[tokio::test(flavor = "multi_thread")] + async fn builder_new_legacy() -> eyre::Result<()> { + let provider = MyProvider {}; + let builder = + TxBuilder::new(&provider, "a.eth", Some("b.eth"), Chain::Mainnet, true).await?; + // don't check anything other than the tx type - the rest is covered in the non-legacy case + let (tx, _) = builder.build(); + match tx { + TypedTransaction::Legacy(_) => {} + _ => { + panic!("Wrong tx type"); + } + } + Ok(()) + } + + #[tokio::test(flavor = "multi_thread")] + async fn builder_fields() -> eyre::Result<()> { + let provider = MyProvider {}; + let mut builder = + TxBuilder::new(&provider, "a.eth", Some("b.eth"), Chain::Mainnet, false).await.unwrap(); + builder + .gas(Some(U256::from(12u32))) + .gas_price(Some(U256::from(34u32))) + .value(Some(U256::from(56u32))) + .nonce(Some(U256::from(78u32))); + + builder.etherscan_api_key(Some(String::from("what a lovely day"))); // not testing for this :-/ + let (tx, _) = builder.build(); + + assert_eq!(tx.gas().unwrap().as_u32(), 12); + assert_eq!(tx.gas_price().unwrap().as_u32(), 34); + assert_eq!(tx.value().unwrap().as_u32(), 56); + assert_eq!(tx.nonce().unwrap().as_u32(), 78); + assert_eq!(tx.chain_id().unwrap().as_u32(), 1); + Ok(()) + } + + #[tokio::test(flavor = "multi_thread")] + async fn builder_args() -> eyre::Result<()> { + let provider = MyProvider {}; + let mut builder = + TxBuilder::new(&provider, "a.eth", Some("b.eth"), Chain::Mainnet, false).await.unwrap(); + builder.args(Some(("what_a_day(int)", vec![String::from("31337")]))).await?; + let (_, function_maybe) = builder.build(); + + assert_ne!(function_maybe, None); + let function = function_maybe.unwrap(); + assert_eq!(function.name, String::from("what_a_day")); + // could test function.inputs() but that should be covered by utils's unit test + Ok(()) + } +} diff --git a/crates/zkcast/tests/cli/main.rs b/crates/zkcast/tests/cli/main.rs new file mode 100644 index 000000000..cbc999079 --- /dev/null +++ b/crates/zkcast/tests/cli/main.rs @@ -0,0 +1,512 @@ +//! Contains various tests for checking cast commands + +use foundry_test_utils::{ + casttest, + util::{OutputExt, TestCommand, TestProject}, +}; +use foundry_utils::rpc::{next_http_rpc_endpoint, next_ws_rpc_endpoint}; +use std::{io::Write, path::Path}; + +// tests `--help` is printed to std out +casttest!(print_help, |_: TestProject, mut cmd: TestCommand| { + cmd.arg("--help"); + cmd.assert_non_empty_stdout(); +}); + +// tests that the `cast block` command works correctly +casttest!(latest_block, |_: TestProject, mut cmd: TestCommand| { + let eth_rpc_url = next_http_rpc_endpoint(); + + // Call `cast find-block` + cmd.args(["block", "latest", "--rpc-url", eth_rpc_url.as_str()]); + let output = cmd.stdout_lossy(); + assert!(output.contains("transactions:")); + assert!(output.contains("gasUsed")); + + // + cmd.cast_fuse().args(["block", "15007840", "-f", "hash", "--rpc-url", eth_rpc_url.as_str()]); + let output = cmd.stdout_lossy(); + assert_eq!(output.trim(), "0x950091817a57e22b6c1f3b951a15f52d41ac89b299cc8f9c89bb6d185f80c415") +}); + +// tests that the `cast find-block` command works correctly +casttest!(finds_block, |_: TestProject, mut cmd: TestCommand| { + // Construct args + let timestamp = "1647843609".to_string(); + let eth_rpc_url = next_http_rpc_endpoint(); + + // Call `cast find-block` + cmd.args(["find-block", "--rpc-url", eth_rpc_url.as_str(), ×tamp]); + let output = cmd.stdout_lossy(); + println!("{output}"); + + // Expect successful block query + // Query: 1647843609, Mar 21 2022 06:20:09 UTC + // Output block: https://etherscan.io/block/14428082 + // Output block time: Mar 21 2022 06:20:09 UTC + assert!(output.contains("14428082"), "{}", output); +}); + +// tests that we can create a new wallet with keystore +casttest!(new_wallet_keystore_with_password, |_: TestProject, mut cmd: TestCommand| { + cmd.args(["wallet", "new", ".", "--unsafe-password", "test"]); + let out = cmd.stdout_lossy(); + assert!(out.contains("Created new encrypted keystore file")); + assert!(out.contains("Address")); +}); + +// tests that we can get the address of a keystore file +casttest!(wallet_address_keystore_with_password_file, |_: TestProject, mut cmd: TestCommand| { + let keystore_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/keystore"); + + cmd.args([ + "wallet", + "address", + "--keystore", + keystore_dir + .join("UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2") + .to_str() + .unwrap(), + "--password-file", + keystore_dir.join("password-ec554").to_str().unwrap(), + ]); + let out = cmd.stdout_lossy(); + assert!(out.contains("0xeC554aeAFE75601AaAb43Bd4621A22284dB566C2")); +}); + +// tests that `cast wallet sign message` outputs the expected signature +casttest!(cast_wallet_sign_message_utf8_data, |_: TestProject, mut cmd: TestCommand| { + cmd.args([ + "wallet", + "sign", + "--private-key", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "test", + ]); + let output = cmd.stdout_lossy(); + assert_eq!(output.trim(), "0xfe28833983d6faa0715c7e8c3873c725ddab6fa5bf84d40e780676e463e6bea20fc6aea97dc273a98eb26b0914e224c8dd5c615ceaab69ddddcf9b0ae3de0e371c"); +}); + +// tests that `cast wallet sign message` outputs the expected signature, given a 0x-prefixed data +casttest!(cast_wallet_sign_message_hex_data, |_: TestProject, mut cmd: TestCommand| { + cmd.args([ + "wallet", + "sign", + "--private-key", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + ]); + let output = cmd.stdout_lossy(); + assert_eq!(output.trim(), "0x23a42ca5616ee730ff3735890c32fc7b9491a9f633faca9434797f2c845f5abf4d9ba23bd7edb8577acebaa3644dc5a4995296db420522bb40060f1693c33c9b1c"); +}); + +// tests that `cast wallet sign typed-data` outputs the expected signature, given a JSON string +casttest!(cast_wallet_sign_typed_data_string, |_: TestProject, mut cmd: TestCommand| { + cmd.args([ + "wallet", + "sign", + "--private-key", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "--data", + "{\"types\": {\"EIP712Domain\": [{\"name\": \"name\",\"type\": \"string\"},{\"name\": \"version\",\"type\": \"string\"},{\"name\": \"chainId\",\"type\": \"uint256\"},{\"name\": \"verifyingContract\",\"type\": \"address\"}],\"Message\": [{\"name\": \"data\",\"type\": \"string\"}]},\"primaryType\": \"Message\",\"domain\": {\"name\": \"example.metamask.io\",\"version\": \"1\",\"chainId\": \"1\",\"verifyingContract\": \"0x0000000000000000000000000000000000000000\"},\"message\": {\"data\": \"Hello!\"}}", + ]); + let output = cmd.stdout_lossy(); + assert_eq!(output.trim(), "0x06c18bdc8163219fddc9afaf5a0550e381326474bb757c86dc32317040cf384e07a2c72ce66c1a0626b6750ca9b6c035bf6f03e7ed67ae2d1134171e9085c0b51b"); +}); + +// tests that `cast wallet sign typed-data` outputs the expected signature, given a JSON file +casttest!(cast_wallet_sign_typed_data_file, |_: TestProject, mut cmd: TestCommand| { + cmd.args([ + "wallet", + "sign", + "--private-key", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "--data", + "--from-file", + Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/sign_typed_data.json") + .into_os_string() + .into_string() + .unwrap() + .as_str(), + ]); + let output = cmd.stdout_lossy(); + assert_eq!(output.trim(), "0x06c18bdc8163219fddc9afaf5a0550e381326474bb757c86dc32317040cf384e07a2c72ce66c1a0626b6750ca9b6c035bf6f03e7ed67ae2d1134171e9085c0b51b"); +}); + +// tests that `cast estimate` is working correctly. +casttest!(estimate_function_gas, |_: TestProject, mut cmd: TestCommand| { + let eth_rpc_url = next_http_rpc_endpoint(); + cmd.args([ + "estimate", + "vitalik.eth", + "--value", + "100", + "deposit()", + "--rpc-url", + eth_rpc_url.as_str(), + ]); + let out: u32 = cmd.stdout_lossy().trim().parse().unwrap(); + // ensure we get a positive non-error value for gas estimate + assert!(out.ge(&0)); +}); + +// tests that `cast estimate --create` is working correctly. +casttest!(estimate_contract_deploy_gas, |_: TestProject, mut cmd: TestCommand| { + let eth_rpc_url = next_http_rpc_endpoint(); + // sample contract code bytecode. Wouldn't run but is valid bytecode that the estimate method + // accepts and could be deployed. + cmd.args([ + "estimate", + "--rpc-url", + eth_rpc_url.as_str(), + "--create", + "0000", + "ERC20(uint256,string,string)", + "100", + "Test", + "TST", + ]); + + let gas: u32 = cmd.stdout_lossy().trim().parse().unwrap(); + // ensure we get a positive non-error value for gas estimate + assert!(gas > 0); +}); + +// tests that the `cast upload-signatures` command works correctly +casttest!(upload_signatures, |_: TestProject, mut cmd: TestCommand| { + // test no prefix is accepted as function + cmd.args(["upload-signature", "transfer(address,uint256)"]); + let output = cmd.stdout_lossy(); + + assert!(output.contains("Function transfer(address,uint256): 0xa9059cbb"), "{}", output); + + // test event prefix + cmd.args(["upload-signature", "event Transfer(address,uint256)"]); + let output = cmd.stdout_lossy(); + + assert!(output.contains("Event Transfer(address,uint256): 0x69ca02dd4edd7bf0a4abb9ed3b7af3f14778db5d61921c7dc7cd545266326de2"), "{}", output); + + // test multiple sigs + cmd.args([ + "upload-signature", + "event Transfer(address,uint256)", + "transfer(address,uint256)", + "approve(address,uint256)", + ]); + let output = cmd.stdout_lossy(); + + assert!(output.contains("Event Transfer(address,uint256): 0x69ca02dd4edd7bf0a4abb9ed3b7af3f14778db5d61921c7dc7cd545266326de2"), "{}", output); + assert!(output.contains("Function transfer(address,uint256): 0xa9059cbb"), "{}", output); + assert!(output.contains("Function approve(address,uint256): 0x095ea7b3"), "{}", output); + + // test abi + cmd.args([ + "upload-signature", + "event Transfer(address,uint256)", + "transfer(address,uint256)", + Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/ERC20Artifact.json") + .into_os_string() + .into_string() + .unwrap() + .as_str(), + ]); + let output = cmd.stdout_lossy(); + + assert!(output.contains("Event Transfer(address,uint256): 0x69ca02dd4edd7bf0a4abb9ed3b7af3f14778db5d61921c7dc7cd545266326de2"), "{}", output); + assert!(output.contains("Function transfer(address,uint256): 0xa9059cbb"), "{}", output); + assert!(output.contains("Function approve(address,uint256): 0x095ea7b3"), "{}", output); + assert!(output.contains("Function decimals(): 0x313ce567"), "{}", output); + assert!(output.contains("Function allowance(address,address): 0xdd62ed3e"), "{}", output); +}); + +// tests that the `cast to-rlp` and `cast from-rlp` commands work correctly +casttest!(cast_rlp, |_: TestProject, mut cmd: TestCommand| { + cmd.args(["--to-rlp", "[\"0xaa\", [[\"bb\"]], \"0xcc\"]"]); + let out = cmd.stdout_lossy(); + assert!(out.contains("0xc881aac3c281bb81cc"), "{}", out); + + cmd.cast_fuse(); + cmd.args(["--from-rlp", "0xcbc58455556666c0c0c2c1c0"]); + let out = cmd.stdout_lossy(); + assert!(out.contains("[[\"0x55556666\"],[],[],[[[]]]]"), "{}", out); +}); + +// test for cast_rpc without arguments +casttest!(cast_rpc_no_args, |_: TestProject, mut cmd: TestCommand| { + let eth_rpc_url = next_http_rpc_endpoint(); + + // Call `cast rpc eth_chainId` + cmd.args(["rpc", "--rpc-url", eth_rpc_url.as_str(), "eth_chainId"]); + let output = cmd.stdout_lossy(); + assert_eq!(output.trim_end(), r#""0x1""#); +}); + +// test for cast_rpc without arguments using websocket +casttest!(cast_ws_rpc_no_args, |_: TestProject, mut cmd: TestCommand| { + let eth_rpc_url = next_ws_rpc_endpoint(); + + // Call `cast rpc eth_chainId` + cmd.args(["rpc", "--rpc-url", eth_rpc_url.as_str(), "eth_chainId"]); + let output = cmd.stdout_lossy(); + assert_eq!(output.trim_end(), r#""0x1""#); +}); + +// test for cast_rpc with arguments +casttest!(cast_rpc_with_args, |_: TestProject, mut cmd: TestCommand| { + let eth_rpc_url = next_http_rpc_endpoint(); + + // Call `cast rpc eth_getBlockByNumber 0x123 false` + cmd.args(["rpc", "--rpc-url", eth_rpc_url.as_str(), "eth_getBlockByNumber", "0x123", "false"]); + let output = cmd.stdout_lossy(); + assert!(output.contains(r#""number":"0x123""#), "{}", output); +}); + +// test for cast_rpc with raw params +casttest!(cast_rpc_raw_params, |_: TestProject, mut cmd: TestCommand| { + let eth_rpc_url = next_http_rpc_endpoint(); + + // Call `cast rpc eth_getBlockByNumber --raw '["0x123", false]'` + cmd.args([ + "rpc", + "--rpc-url", + eth_rpc_url.as_str(), + "eth_getBlockByNumber", + "--raw", + r#"["0x123", false]"#, + ]); + let output = cmd.stdout_lossy(); + assert!(output.contains(r#""number":"0x123""#), "{}", output); +}); + +// test for cast_rpc with direct params +casttest!(cast_rpc_raw_params_stdin, |_: TestProject, mut cmd: TestCommand| { + let eth_rpc_url = next_http_rpc_endpoint(); + + // Call `echo "\n[\n\"0x123\",\nfalse\n]\n" | cast rpc eth_getBlockByNumber --raw + cmd.args(["rpc", "--rpc-url", eth_rpc_url.as_str(), "eth_getBlockByNumber", "--raw"]).stdin( + |mut stdin| { + stdin.write_all(b"\n[\n\"0x123\",\nfalse\n]\n").unwrap(); + }, + ); + let output = cmd.stdout_lossy(); + assert!(output.contains(r#""number":"0x123""#), "{}", output); +}); + +// checks `cast calldata` can handle arrays +casttest!(calldata_array, |_: TestProject, mut cmd: TestCommand| { + cmd.args(["calldata", "propose(string[])", "[\"\"]"]); + let out = cmd.stdout_lossy(); + assert_eq!(out.trim(),"0xcde2baba0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" + ); +}); + +// +casttest!(cast_run_succeeds, |_: TestProject, mut cmd: TestCommand| { + let rpc = next_http_rpc_endpoint(); + cmd.args([ + "run", + "-v", + "0x2d951c5c95d374263ca99ad9c20c9797fc714330a8037429a3aa4c83d456f845", + "--quick", + "--rpc-url", + rpc.as_str(), + ]); + let output = cmd.stdout_lossy(); + assert!(output.contains("Transaction successfully executed")); + assert!(!output.contains("Revert")); +}); + +// tests that `cast --to-base` commands are working correctly. +casttest!(cast_to_base, |_: TestProject, mut cmd: TestCommand| { + let values = [ + "1", + "100", + "100000", + "115792089237316195423570985008687907853269984665640564039457584007913129639935", + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "-1", + "-100", + "-100000", + "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + ]; + for value in values { + for subcmd in ["--to-base", "--to-hex", "--to-dec"] { + if subcmd == "--to-base" { + for base in ["bin", "oct", "dec", "hex"] { + cmd.cast_fuse().args([subcmd, value, base]); + assert!(!cmd.stdout_lossy().trim().is_empty()); + } + } else { + cmd.cast_fuse().args([subcmd, value]); + assert!(!cmd.stdout_lossy().trim().is_empty()); + } + } + } +}); + +// tests that revert reason is only present if transaction has reverted. +casttest!(cast_receipt_revert_reason, |_: TestProject, mut cmd: TestCommand| { + let rpc = next_http_rpc_endpoint(); + + // + cmd.cast_fuse().args([ + "receipt", + "0x44f2aaa351460c074f2cb1e5a9e28cbc7d83f33e425101d2de14331c7b7ec31e", + "--rpc-url", + rpc.as_str(), + ]); + let output = cmd.stdout_lossy(); + assert!(!output.contains("revertReason")); + + // + cmd.cast_fuse().args([ + "receipt", + "0x0e07d8b53ed3d91314c80e53cf25bcde02084939395845cbb625b029d568135c", + "--rpc-url", + rpc.as_str(), + ]); + let output = cmd.stdout_lossy(); + assert!(output.contains("revertReason")); + assert!(output.contains("Transaction too old")); +}); + +// tests that `cast --parse-bytes32-address` command is working correctly. +casttest!(parse_bytes32_address, |_: TestProject, mut cmd: TestCommand| { + cmd.args([ + "--parse-bytes32-address", + "0x000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045", + ]); + let output = cmd.stdout_lossy(); + assert_eq!(output.trim(), "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045") +}); + +casttest!(cast_access_list, |_: TestProject, mut cmd: TestCommand| { + let rpc = next_http_rpc_endpoint(); + cmd.args([ + "access-list", + "0xbb2b8038a1640196fbe3e38816f3e67cba72d940", + "skim(address)", + "0xbb2b8038a1640196fbe3e38816f3e67cba72d940", + "--rpc-url", + rpc.as_str(), + "--gas-limit", // need to set this for alchemy.io to avoid "intrinsic gas too low" error + "100000", + ]); + + let output = cmd.stdout_lossy(); + assert!(output.contains("address: 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599")); + assert!(output.contains("0x0d2a19d3ac39dc6cc6fd07423195495e18679bd8c7dd610aa1db7cd784a683a8")); + assert!(output.contains("0x7fba2702a7d6e85ac783a88eacdc48e51310443458071f6db9ac66f8ca7068b8")); +}); + +casttest!(cast_logs_topics, |_: TestProject, mut cmd: TestCommand| { + let rpc = next_http_rpc_endpoint(); + cmd.args([ + "logs", + "--rpc-url", + rpc.as_str(), + "--from-block", + "12421181", + "--to-block", + "12421182", + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x000000000000000000000000ab5801a7d398351b8be11c439e05c5b3259aec9b", + ]); + + cmd.unchecked_output().stdout_matches_path( + Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/cast_logs.stdout"), + ); +}); + +casttest!(cast_logs_topic_2, |_: TestProject, mut cmd: TestCommand| { + let rpc = next_http_rpc_endpoint(); + cmd.args([ + "logs", + "--rpc-url", + rpc.as_str(), + "--from-block", + "12421181", + "--to-block", + "12421182", + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "", + "0x00000000000000000000000068a99f89e475a078645f4bac491360afe255dff1", /* Filter on the + * `to` address */ + ]); + + cmd.unchecked_output().stdout_matches_path( + Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/cast_logs.stdout"), + ); +}); + +casttest!(cast_logs_sig, |_: TestProject, mut cmd: TestCommand| { + let rpc = next_http_rpc_endpoint(); + cmd.args([ + "logs", + "--rpc-url", + rpc.as_str(), + "--from-block", + "12421181", + "--to-block", + "12421182", + "Transfer(address indexed from, address indexed to, uint256 value)", + "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B", + ]); + + cmd.unchecked_output().stdout_matches_path( + Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/cast_logs.stdout"), + ); +}); + +casttest!(cast_logs_sig_2, |_: TestProject, mut cmd: TestCommand| { + let rpc = next_http_rpc_endpoint(); + cmd.args([ + "logs", + "--rpc-url", + rpc.as_str(), + "--from-block", + "12421181", + "--to-block", + "12421182", + "Transfer(address indexed from, address indexed to, uint256 value)", + "", + "0x68A99f89E475a078645f4BAC491360aFe255Dff1", + ]); + + cmd.unchecked_output().stdout_matches_path( + Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/cast_logs.stdout"), + ); +}); + +// tests that the raw encoded transaction is returned +casttest!(cast_tx_raw, |_: TestProject, mut cmd: TestCommand| { + let rpc = next_http_rpc_endpoint(); + + // + cmd.cast_fuse().args([ + "tx", + "0x44f2aaa351460c074f2cb1e5a9e28cbc7d83f33e425101d2de14331c7b7ec31e", + "raw", + "--rpc-url", + rpc.as_str(), + ]); + let output = cmd.stdout_lossy(); + + // + assert_eq!( + output.trim(), + "0xf86d824c548502743b65088275309491da5bf3f8eb72724e6f50ec6c3d199c6355c59c87a0a73f33e9e4cc8025a0428518b1748a08bbeb2392ea055b418538944d30adfc2accbbfa8362a401d3a4a07d6093ab2580efd17c11b277de7664fce56e6953cae8e925bec3313399860470" + ); + + cmd.cast_fuse().args([ + "tx", + "0x44f2aaa351460c074f2cb1e5a9e28cbc7d83f33e425101d2de14331c7b7ec31e", + "--raw", + "--rpc-url", + rpc.as_str(), + ]); + let output2 = cmd.stdout_lossy(); + assert_eq!(output, output2); +}); diff --git a/crates/zkcast/tests/fixtures/ERC20Artifact.json b/crates/zkcast/tests/fixtures/ERC20Artifact.json new file mode 100644 index 000000000..4f4fd6e61 --- /dev/null +++ b/crates/zkcast/tests/fixtures/ERC20Artifact.json @@ -0,0 +1,385 @@ +{ + "abi": [ + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "nonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": { + "object": "0x60e06040523480156200001157600080fd5b5060405162000f7738038062000f7783398101604081905262000034916200029a565b82828282600090805190602001906200004f92919062000127565b5081516200006590600190602085019062000127565b5060ff81166080524660a0526200007b6200008b565b60c0525062000400945050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051620000bf91906200035c565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b82805462000135906200031f565b90600052602060002090601f016020900481019282620001595760008555620001a4565b82601f106200017457805160ff1916838001178555620001a4565b82800160010185558215620001a4579182015b82811115620001a457825182559160200191906001019062000187565b50620001b2929150620001b6565b5090565b5b80821115620001b25760008155600101620001b7565b634e487b7160e01b600052604160045260246000fd5b600082601f830112620001f557600080fd5b81516001600160401b0380821115620002125762000212620001cd565b604051601f8301601f19908116603f011681019082821181831017156200023d576200023d620001cd565b816040528381526020925086838588010111156200025a57600080fd5b600091505b838210156200027e57858201830151818301840152908201906200025f565b83821115620002905760008385830101525b9695505050505050565b600080600060608486031215620002b057600080fd5b83516001600160401b0380821115620002c857600080fd5b620002d687838801620001e3565b94506020860151915080821115620002ed57600080fd5b50620002fc86828701620001e3565b925050604084015160ff811681146200031457600080fd5b809150509250925092565b600181811c908216806200033457607f821691505b602082108114156200035657634e487b7160e01b600052602260045260246000fd5b50919050565b600080835481600182811c9150808316806200037957607f831692505b60208084108214156200039a57634e487b7160e01b86526022600452602486fd5b818015620003b15760018114620003c357620003f2565b60ff19861689528489019650620003f2565b60008a81526020902060005b86811015620003ea5781548b820152908501908301620003cf565b505084890196505b509498975050505050505050565b60805160a05160c051610b476200043060003960006104530152600061041e015260006101440152610b476000f3fe608060405234801561001057600080fd5b50600436106100cf5760003560e01c806340c10f191161008c57806395d89b411161006657806395d89b41146101d5578063a9059cbb146101dd578063d505accf146101f0578063dd62ed3e1461020357600080fd5b806340c10f191461018057806370a08231146101955780637ecebe00146101b557600080fd5b806306fdde03146100d4578063095ea7b3146100f257806318160ddd1461011557806323b872dd1461012c578063313ce5671461013f5780633644e51514610178575b600080fd5b6100dc61022e565b6040516100e99190610856565b60405180910390f35b6101056101003660046108c7565b6102bc565b60405190151581526020016100e9565b61011e60025481565b6040519081526020016100e9565b61010561013a3660046108f1565b610328565b6101667f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016100e9565b61011e61041a565b61019361018e3660046108c7565b610475565b005b61011e6101a336600461092d565b60036020526000908152604090205481565b61011e6101c336600461092d565b60056020526000908152604090205481565b6100dc610483565b6101056101eb3660046108c7565b610490565b6101936101fe36600461094f565b610508565b61011e6102113660046109c2565b600460209081526000928352604080842090915290825290205481565b6000805461023b906109f5565b80601f0160208091040260200160405190810160405280929190818152602001828054610267906109f5565b80156102b45780601f10610289576101008083540402835291602001916102b4565b820191906000526020600020905b81548152906001019060200180831161029757829003601f168201915b505050505081565b3360008181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906103179086815260200190565b60405180910390a350600192915050565b6001600160a01b038316600090815260046020908152604080832033845290915281205460001981146103845761035f8382610a46565b6001600160a01b03861660009081526004602090815260408083203384529091529020555b6001600160a01b038516600090815260036020526040812080548592906103ac908490610a46565b90915550506001600160a01b03808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906104079087815260200190565b60405180910390a3506001949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000046146104505761044b610751565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b61047f82826107eb565b5050565b6001805461023b906109f5565b336000908152600360205260408120805483919083906104b1908490610a46565b90915550506001600160a01b038316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906103179086815260200190565b4284101561055d5760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f4558504952454400000000000000000060448201526064015b60405180910390fd5b6000600161056961041a565b6001600160a01b038a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015610675573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906106ab5750876001600160a01b0316816001600160a01b0316145b6106e85760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610554565b6001600160a01b0390811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516107839190610a5d565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b80600260008282546107fd9190610af9565b90915550506001600160a01b0382166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b600060208083528351808285015260005b8181101561088357858101830151858201604001528201610867565b81811115610895576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b03811681146108c257600080fd5b919050565b600080604083850312156108da57600080fd5b6108e3836108ab565b946020939093013593505050565b60008060006060848603121561090657600080fd5b61090f846108ab565b925061091d602085016108ab565b9150604084013590509250925092565b60006020828403121561093f57600080fd5b610948826108ab565b9392505050565b600080600080600080600060e0888a03121561096a57600080fd5b610973886108ab565b9650610981602089016108ab565b95506040880135945060608801359350608088013560ff811681146109a557600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156109d557600080fd5b6109de836108ab565b91506109ec602084016108ab565b90509250929050565b600181811c90821680610a0957607f821691505b60208210811415610a2a57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b600082821015610a5857610a58610a30565b500390565b600080835481600182811c915080831680610a7957607f831692505b6020808410821415610a9957634e487b7160e01b86526022600452602486fd5b818015610aad5760018114610abe57610aeb565b60ff19861689528489019650610aeb565b60008a81526020902060005b86811015610ae35781548b820152908501908301610aca565b505084890196505b509498975050505050505050565b60008219821115610b0c57610b0c610a30565b50019056fea26469706673582212209ca55739676d094f8ef4b3b5cfcc08f600b805843c4c3027beb2b4159a02f63664736f6c634300080a0033", + "sourceMap": "113:230:22:-:0;;;148:102;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;224:4;230:6;238:8;2098:5:10;2091:4;:12;;;;;;;;;;;;:::i;:::-;-1:-1:-1;2113:16:10;;;;:6;;:16;;;;;:::i;:::-;-1:-1:-1;2139:20:10;;;;;2189:13;2170:32;;2239:24;:22;:24::i;:::-;2212:51;;-1:-1:-1;113:230:22;;-1:-1:-1;;;;;113:230:22;5507:446:10;5572:7;5669:95;5802:4;5786:22;;;;;;:::i;:::-;;;;;;;;;;5637:295;;;3635:25:23;;;;3676:18;;3669:34;;;;5830:14:10;3719:18:23;;;3712:34;5866:13:10;3762:18:23;;;3755:34;5909:4:10;3805:19:23;;;3798:61;3607:19;;5637:295:10;;;;;;;;;;;;5610:336;;;;;;5591:355;;5507:446;:::o;113:230:22:-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;113:230:22;;;-1:-1:-1;113:230:22;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;14:127:23;75:10;70:3;66:20;63:1;56:31;106:4;103:1;96:15;130:4;127:1;120:15;146:885;200:5;253:3;246:4;238:6;234:17;230:27;220:55;;271:1;268;261:12;220:55;294:13;;-1:-1:-1;;;;;356:10:23;;;353:36;;;369:18;;:::i;:::-;444:2;438:9;412:2;498:13;;-1:-1:-1;;494:22:23;;;518:2;490:31;486:40;474:53;;;542:18;;;562:22;;;539:46;536:72;;;588:18;;:::i;:::-;628:10;624:2;617:22;663:2;655:6;648:18;685:4;675:14;;730:3;725:2;720;712:6;708:15;704:24;701:33;698:53;;;747:1;744;737:12;698:53;769:1;760:10;;779:133;793:2;790:1;787:9;779:133;;;881:14;;;877:23;;871:30;850:14;;;846:23;;839:63;804:10;;;;779:133;;;930:2;927:1;924:9;921:80;;;989:1;984:2;979;971:6;967:15;963:24;956:35;921:80;1019:6;146:885;-1:-1:-1;;;;;;146:885:23:o;1036:712::-;1142:6;1150;1158;1211:2;1199:9;1190:7;1186:23;1182:32;1179:52;;;1227:1;1224;1217:12;1179:52;1254:16;;-1:-1:-1;;;;;1319:14:23;;;1316:34;;;1346:1;1343;1336:12;1316:34;1369:61;1422:7;1413:6;1402:9;1398:22;1369:61;:::i;:::-;1359:71;;1476:2;1465:9;1461:18;1455:25;1439:41;;1505:2;1495:8;1492:16;1489:36;;;1521:1;1518;1511:12;1489:36;;1544:63;1599:7;1588:8;1577:9;1573:24;1544:63;:::i;:::-;1534:73;;;1650:2;1639:9;1635:18;1629:25;1694:4;1687:5;1683:16;1676:5;1673:27;1663:55;;1714:1;1711;1704:12;1663:55;1737:5;1727:15;;;1036:712;;;;;:::o;1753:380::-;1832:1;1828:12;;;;1875;;;1896:61;;1950:4;1942:6;1938:17;1928:27;;1896:61;2003:2;1995:6;1992:14;1972:18;1969:38;1966:161;;;2049:10;2044:3;2040:20;2037:1;2030:31;2084:4;2081:1;2074:15;2112:4;2109:1;2102:15;1966:161;;1753:380;;;:::o;2267:1104::-;2397:3;2426:1;2459:6;2453:13;2489:3;2511:1;2539:9;2535:2;2531:18;2521:28;;2599:2;2588:9;2584:18;2621;2611:61;;2665:4;2657:6;2653:17;2643:27;;2611:61;2691:2;2739;2731:6;2728:14;2708:18;2705:38;2702:165;;;-1:-1:-1;;;2766:33:23;;2822:4;2819:1;2812:15;2852:4;2773:3;2840:17;2702:165;2883:18;2910:104;;;;3028:1;3023:323;;;;2876:470;;2910:104;-1:-1:-1;;2943:24:23;;2931:37;;2988:16;;;;-1:-1:-1;2910:104:23;;3023:323;2214:1;2207:14;;;2251:4;2238:18;;3121:1;3135:165;3149:6;3146:1;3143:13;3135:165;;;3227:14;;3214:11;;;3207:35;3270:16;;;;3164:10;;3135:165;;;3139:3;;3329:6;3324:3;3320:16;3313:23;;2876:470;-1:-1:-1;3362:3:23;;2267:1104;-1:-1:-1;;;;;;;;2267:1104:23:o;3376:489::-;113:230:22;;;;;;;;;;;;;;;;;;;;;;;;", + "linkReferences": {} + }, + "deployedBytecode": { + "object": "0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c806340c10f191161008c57806395d89b411161006657806395d89b41146101d5578063a9059cbb146101dd578063d505accf146101f0578063dd62ed3e1461020357600080fd5b806340c10f191461018057806370a08231146101955780637ecebe00146101b557600080fd5b806306fdde03146100d4578063095ea7b3146100f257806318160ddd1461011557806323b872dd1461012c578063313ce5671461013f5780633644e51514610178575b600080fd5b6100dc61022e565b6040516100e99190610856565b60405180910390f35b6101056101003660046108c7565b6102bc565b60405190151581526020016100e9565b61011e60025481565b6040519081526020016100e9565b61010561013a3660046108f1565b610328565b6101667f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016100e9565b61011e61041a565b61019361018e3660046108c7565b610475565b005b61011e6101a336600461092d565b60036020526000908152604090205481565b61011e6101c336600461092d565b60056020526000908152604090205481565b6100dc610483565b6101056101eb3660046108c7565b610490565b6101936101fe36600461094f565b610508565b61011e6102113660046109c2565b600460209081526000928352604080842090915290825290205481565b6000805461023b906109f5565b80601f0160208091040260200160405190810160405280929190818152602001828054610267906109f5565b80156102b45780601f10610289576101008083540402835291602001916102b4565b820191906000526020600020905b81548152906001019060200180831161029757829003601f168201915b505050505081565b3360008181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906103179086815260200190565b60405180910390a350600192915050565b6001600160a01b038316600090815260046020908152604080832033845290915281205460001981146103845761035f8382610a46565b6001600160a01b03861660009081526004602090815260408083203384529091529020555b6001600160a01b038516600090815260036020526040812080548592906103ac908490610a46565b90915550506001600160a01b03808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906104079087815260200190565b60405180910390a3506001949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000046146104505761044b610751565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b61047f82826107eb565b5050565b6001805461023b906109f5565b336000908152600360205260408120805483919083906104b1908490610a46565b90915550506001600160a01b038316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906103179086815260200190565b4284101561055d5760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f4558504952454400000000000000000060448201526064015b60405180910390fd5b6000600161056961041a565b6001600160a01b038a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015610675573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906106ab5750876001600160a01b0316816001600160a01b0316145b6106e85760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610554565b6001600160a01b0390811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516107839190610a5d565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b80600260008282546107fd9190610af9565b90915550506001600160a01b0382166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b600060208083528351808285015260005b8181101561088357858101830151858201604001528201610867565b81811115610895576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b03811681146108c257600080fd5b919050565b600080604083850312156108da57600080fd5b6108e3836108ab565b946020939093013593505050565b60008060006060848603121561090657600080fd5b61090f846108ab565b925061091d602085016108ab565b9150604084013590509250925092565b60006020828403121561093f57600080fd5b610948826108ab565b9392505050565b600080600080600080600060e0888a03121561096a57600080fd5b610973886108ab565b9650610981602089016108ab565b95506040880135945060608801359350608088013560ff811681146109a557600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156109d557600080fd5b6109de836108ab565b91506109ec602084016108ab565b90509250929050565b600181811c90821680610a0957607f821691505b60208210811415610a2a57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b600082821015610a5857610a58610a30565b500390565b600080835481600182811c915080831680610a7957607f831692505b6020808410821415610a9957634e487b7160e01b86526022600452602486fd5b818015610aad5760018114610abe57610aeb565b60ff19861689528489019650610aeb565b60008a81526020902060005b86811015610ae35781548b820152908501908301610aca565b505084890196505b509498975050505050505050565b60008219821115610b0c57610b0c610a30565b50019056fea26469706673582212209ca55739676d094f8ef4b3b5cfcc08f600b805843c4c3027beb2b4159a02f63664736f6c634300080a0033", + "sourceMap": "113:230:22:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1028:18:10;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;2458:211;;;;;;:::i;:::-;;:::i;:::-;;;1218:14:23;;1211:22;1193:41;;1181:2;1166:18;2458:211:10;1053:187:23;1301:26:10;;;;;;;;;1391:25:23;;;1379:2;1364:18;1301:26:10;1245:177:23;3054:592:10;;;;;;:::i;:::-;;:::i;1080:31::-;;;;;;;;1932:4:23;1920:17;;;1902:36;;1890:2;1875:18;1080:31:10;1760:184:23;5324:177:10;;;:::i;256:85:22:-;;;;;;:::i;:::-;;:::i;:::-;;1334:44:10;;;;;;:::i;:::-;;;;;;;;;;;;;;1748:41;;;;;;:::i;:::-;;;;;;;;;;;;;;1053:20;;;:::i;2675:373::-;;;;;;:::i;:::-;;:::i;3835:1483::-;;;;;;:::i;:::-;;:::i;1385:64::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;1028:18;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;2458:211::-;2558:10;2532:4;2548:21;;;:9;:21;;;;;;;;-1:-1:-1;;;;;2548:30:10;;;;;;;;;;:39;;;2603:37;2532:4;;2548:30;;2603:37;;;;2581:6;1391:25:23;;1379:2;1364:18;;1245:177;2603:37:10;;;;;;;;-1:-1:-1;2658:4:10;2458:211;;;;:::o;3054:592::-;-1:-1:-1;;;;;3206:15:10;;3172:4;3206:15;;;:9;:15;;;;;;;;3222:10;3206:27;;;;;;;;-1:-1:-1;;3284:28:10;;3280:80;;3344:16;3354:6;3344:7;:16;:::i;:::-;-1:-1:-1;;;;;3314:15:10;;;;;;:9;:15;;;;;;;;3330:10;3314:27;;;;;;;:46;3280:80;-1:-1:-1;;;;;3371:15:10;;;;;;:9;:15;;;;;:25;;3390:6;;3371:15;:25;;3390:6;;3371:25;:::i;:::-;;;;-1:-1:-1;;;;;;;3542:13:10;;;;;;;:9;:13;;;;;;;:23;;;;;;3591:26;3542:13;;3591:26;;;;;;;3559:6;1391:25:23;;1379:2;1364:18;;1245:177;3591:26:10;;;;;;;;-1:-1:-1;3635:4:10;;3054:592;-1:-1:-1;;;;3054:592:10:o;5324:177::-;5381:7;5424:16;5407:13;:33;:87;;5470:24;:22;:24::i;:::-;5400:94;;5324:177;:::o;5407:87::-;-1:-1:-1;5443:24:10;;5324:177::o;256:85:22:-;317:17;323:2;327:6;317:5;:17::i;:::-;256:85;;:::o;1053:20:10:-;;;;;;;:::i;2675:373::-;2771:10;2745:4;2761:21;;;:9;:21;;;;;:31;;2786:6;;2761:21;2745:4;;2761:31;;2786:6;;2761:31;:::i;:::-;;;;-1:-1:-1;;;;;;;2938:13:10;;;;;;:9;:13;;;;;;;:23;;;;;;2987:32;2996:10;;2987:32;;;;2955:6;1391:25:23;;1379:2;1364:18;;1245:177;3835:1483:10;4054:15;4042:8;:27;;4034:63;;;;-1:-1:-1;;;4034:63:10;;4134:2:23;4034:63:10;;;4116:21:23;4173:2;4153:18;;;4146:30;4212:25;4192:18;;;4185:53;4255:18;;4034:63:10;;;;;;;;;4262:24;4289:805;4425:18;:16;:18::i;:::-;-1:-1:-1;;;;;4870:13:10;;;;;;;:6;:13;;;;;;;;;:15;;;;;;;;4508:449;;4552:165;4508:449;;;4571:25:23;4650:18;;;4643:43;;;;4722:15;;;4702:18;;;4695:43;4754:18;;;4747:34;;;4797:19;;;4790:35;;;;4841:19;;;;4834:35;;;4508:449:10;;;;;;;;;;4543:19:23;;;4508:449:10;;;4469:514;;;;;;;;-1:-1:-1;;;4347:658:10;;;5138:27:23;5181:11;;;5174:27;;;;5217:12;;;5210:28;;;;5254:12;;4347:658:10;;;-1:-1:-1;;4347:658:10;;;;;;;;;4316:707;;4347:658;4316:707;;;;4289:805;;;;;;;;;5504:25:23;5577:4;5565:17;;5545:18;;;5538:45;5599:18;;;5592:34;;;5642:18;;;5635:34;;;5476:19;;4289:805:10;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4289:805:10;;-1:-1:-1;;4289:805:10;;;-1:-1:-1;;;;;;;5117:30:10;;;;;;:59;;;5171:5;-1:-1:-1;;;;;5151:25:10;:16;-1:-1:-1;;;;;5151:25:10;;5117:59;5109:86;;;;-1:-1:-1;;;5109:86:10;;5882:2:23;5109:86:10;;;5864:21:23;5921:2;5901:18;;;5894:30;-1:-1:-1;;;5940:18:23;;;5933:44;5994:18;;5109:86:10;5680:338:23;5109:86:10;-1:-1:-1;;;;;5210:27:10;;;;;;;:9;:27;;;;;;;;:36;;;;;;;;;;;;;:44;;;5280:31;1391:25:23;;;5210:36:10;;5280:31;;;;;1364:18:23;5280:31:10;;;;;;;3835:1483;;;;;;;:::o;5507:446::-;5572:7;5669:95;5802:4;5786:22;;;;;;:::i;:::-;;;;;;;;;;5637:295;;;7520:25:23;;;;7561:18;;7554:34;;;;5830:14:10;7604:18:23;;;7597:34;5866:13:10;7647:18:23;;;7640:34;5909:4:10;7690:19:23;;;7683:61;7492:19;;5637:295:10;;;;;;;;;;;;5610:336;;;;;;5591:355;;5507:446;:::o;6147:325::-;6232:6;6217:11;;:21;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;;;6384:13:10;;;;;;:9;:13;;;;;;;;:23;;;;;;6433:32;1391:25:23;;;6433:32:10;;1364:18:23;6433:32:10;;;;;;;6147:325;;:::o;14:597:23:-;126:4;155:2;184;173:9;166:21;216:6;210:13;259:6;254:2;243:9;239:18;232:34;284:1;294:140;308:6;305:1;302:13;294:140;;;403:14;;;399:23;;393:30;369:17;;;388:2;365:26;358:66;323:10;;294:140;;;452:6;449:1;446:13;443:91;;;522:1;517:2;508:6;497:9;493:22;489:31;482:42;443:91;-1:-1:-1;595:2:23;574:15;-1:-1:-1;;570:29:23;555:45;;;;602:2;551:54;;14:597;-1:-1:-1;;;14:597:23:o;616:173::-;684:20;;-1:-1:-1;;;;;733:31:23;;723:42;;713:70;;779:1;776;769:12;713:70;616:173;;;:::o;794:254::-;862:6;870;923:2;911:9;902:7;898:23;894:32;891:52;;;939:1;936;929:12;891:52;962:29;981:9;962:29;:::i;:::-;952:39;1038:2;1023:18;;;;1010:32;;-1:-1:-1;;;794:254:23:o;1427:328::-;1504:6;1512;1520;1573:2;1561:9;1552:7;1548:23;1544:32;1541:52;;;1589:1;1586;1579:12;1541:52;1612:29;1631:9;1612:29;:::i;:::-;1602:39;;1660:38;1694:2;1683:9;1679:18;1660:38;:::i;:::-;1650:48;;1745:2;1734:9;1730:18;1717:32;1707:42;;1427:328;;;;;:::o;2131:186::-;2190:6;2243:2;2231:9;2222:7;2218:23;2214:32;2211:52;;;2259:1;2256;2249:12;2211:52;2282:29;2301:9;2282:29;:::i;:::-;2272:39;2131:186;-1:-1:-1;;;2131:186:23:o;2322:693::-;2433:6;2441;2449;2457;2465;2473;2481;2534:3;2522:9;2513:7;2509:23;2505:33;2502:53;;;2551:1;2548;2541:12;2502:53;2574:29;2593:9;2574:29;:::i;:::-;2564:39;;2622:38;2656:2;2645:9;2641:18;2622:38;:::i;:::-;2612:48;;2707:2;2696:9;2692:18;2679:32;2669:42;;2758:2;2747:9;2743:18;2730:32;2720:42;;2812:3;2801:9;2797:19;2784:33;2857:4;2850:5;2846:16;2839:5;2836:27;2826:55;;2877:1;2874;2867:12;2826:55;2322:693;;;;-1:-1:-1;2322:693:23;;;;2900:5;2952:3;2937:19;;2924:33;;-1:-1:-1;3004:3:23;2989:19;;;2976:33;;2322:693;-1:-1:-1;;2322:693:23:o;3020:260::-;3088:6;3096;3149:2;3137:9;3128:7;3124:23;3120:32;3117:52;;;3165:1;3162;3155:12;3117:52;3188:29;3207:9;3188:29;:::i;:::-;3178:39;;3236:38;3270:2;3259:9;3255:18;3236:38;:::i;:::-;3226:48;;3020:260;;;;;:::o;3285:380::-;3364:1;3360:12;;;;3407;;;3428:61;;3482:4;3474:6;3470:17;3460:27;;3428:61;3535:2;3527:6;3524:14;3504:18;3501:38;3498:161;;;3581:10;3576:3;3572:20;3569:1;3562:31;3616:4;3613:1;3606:15;3644:4;3641:1;3634:15;3498:161;;3285:380;;;:::o;3670:127::-;3731:10;3726:3;3722:20;3719:1;3712:31;3762:4;3759:1;3752:15;3786:4;3783:1;3776:15;3802:125;3842:4;3870:1;3867;3864:8;3861:34;;;3875:18;;:::i;:::-;-1:-1:-1;3912:9:23;;3802:125::o;6152:1104::-;6282:3;6311:1;6344:6;6338:13;6374:3;6396:1;6424:9;6420:2;6416:18;6406:28;;6484:2;6473:9;6469:18;6506;6496:61;;6550:4;6542:6;6538:17;6528:27;;6496:61;6576:2;6624;6616:6;6613:14;6593:18;6590:38;6587:165;;;-1:-1:-1;;;6651:33:23;;6707:4;6704:1;6697:15;6737:4;6658:3;6725:17;6587:165;6768:18;6795:104;;;;6913:1;6908:323;;;;6761:470;;6795:104;-1:-1:-1;;6828:24:23;;6816:37;;6873:16;;;;-1:-1:-1;6795:104:23;;6908:323;6099:1;6092:14;;;6136:4;6123:18;;7006:1;7020:165;7034:6;7031:1;7028:13;7020:165;;;7112:14;;7099:11;;;7092:35;7155:16;;;;7049:10;;7020:165;;;7024:3;;7214:6;7209:3;7205:16;7198:23;;6761:470;-1:-1:-1;7247:3:23;;6152:1104;-1:-1:-1;;;;;;;;6152:1104:23:o;7755:128::-;7795:3;7826:1;7822:6;7819:1;7816:13;7813:39;;;7832:18;;:::i;:::-;-1:-1:-1;7868:9:23;;7755:128::o", + "linkReferences": {}, + "immutableReferences": { + "3499": [ + { + "start": 324, + "length": 32 + } + ], + "3513": [ + { + "start": 1054, + "length": 32 + } + ], + "3515": [ + { + "start": 1107, + "length": 32 + } + ] + } + }, + "methodIdentifiers": { + "DOMAIN_SEPARATOR()": "3644e515", + "allowance(address,address)": "dd62ed3e", + "approve(address,uint256)": "095ea7b3", + "balanceOf(address)": "70a08231", + "decimals()": "313ce567", + "mint(address,uint256)": "40c10f19", + "name()": "06fdde03", + "nonces(address)": "7ecebe00", + "permit(address,address,uint256,uint256,uint8,bytes32,bytes32)": "d505accf", + "symbol()": "95d89b41", + "totalSupply()": "18160ddd", + "transfer(address,uint256)": "a9059cbb", + "transferFrom(address,address,uint256)": "23b872dd" + } +} diff --git a/crates/zkcast/tests/fixtures/cast_logs.stdout b/crates/zkcast/tests/fixtures/cast_logs.stdout new file mode 100644 index 000000000..1729394f6 --- /dev/null +++ b/crates/zkcast/tests/fixtures/cast_logs.stdout @@ -0,0 +1,13 @@ +- address: 0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE + blockHash: 0x439b61565dacbc09a6d54378dff60d9b0400496d7a5a060cfdfdd899262f466c + blockNumber: 12421182 + data: 0x000000000000000000000000000000000000027fd7b375dda5ef932dac18d302 + logIndex: 15 + removed: false + topics: [ + 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef + 0x000000000000000000000000ab5801a7d398351b8be11c439e05c5b3259aec9b + 0x00000000000000000000000068a99f89e475a078645f4bac491360afe255dff1 + ] + transactionHash: 0xb65bcbb85c1633b0ab4e4886c3cd8eeaeb63edbb39cacdb9223fdcf4454fd2c7 + transactionIndex: 8 diff --git a/crates/zkcast/tests/fixtures/keystore/UTC--2022-10-30T06-51-20.130356000Z--560d246fcddc9ea98a8b032c9a2f474efb493c28 b/crates/zkcast/tests/fixtures/keystore/UTC--2022-10-30T06-51-20.130356000Z--560d246fcddc9ea98a8b032c9a2f474efb493c28 new file mode 100644 index 000000000..f17ecfa06 --- /dev/null +++ b/crates/zkcast/tests/fixtures/keystore/UTC--2022-10-30T06-51-20.130356000Z--560d246fcddc9ea98a8b032c9a2f474efb493c28 @@ -0,0 +1 @@ +{"address":"560d246fcddc9ea98a8b032c9a2f474efb493c28","crypto":{"cipher":"aes-128-ctr","ciphertext":"0b0012edfc7a1b22c7c616a78562807c363482490359ae23858c49d6a369b2ff","cipherparams":{"iv":"9d72960fe04dd987300e91c101c890b8"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"f7d24eae5e746700a1fdc0e0886801b0e7aff7e298f9409f74355a5387827181"},"mac":"981cae42a43ce975b9ff13d19a99ae755ad3f6125c94a4da517c50067ca87f07"},"id":"852dbcfc-726a-416e-9321-29f9b5d7e2de","version":3} \ No newline at end of file diff --git a/crates/zkcast/tests/fixtures/keystore/UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2 b/crates/zkcast/tests/fixtures/keystore/UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2 new file mode 100644 index 000000000..4f071d708 --- /dev/null +++ b/crates/zkcast/tests/fixtures/keystore/UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2 @@ -0,0 +1 @@ +{"address":"ec554aeafe75601aaab43bd4621a22284db566c2","crypto":{"cipher":"aes-128-ctr","ciphertext":"f4e11a2667d97f42ad820f2aa735cd557ff608f47e2738d763a834b889611e18","cipherparams":{"iv":"99c3e2f1c98ccac50cb19f0a148e02ee"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"81f3033ffcc9fec9939eaec246a4037d9bc0c47cfb33247c98132b7d477b7e64"},"mac":"13bb4eac9a8d8f72905bca0c6f947c440d990baa649b0697b2083f3ab57d2cc5"},"id":"279ce8c3-dd94-43a8-aa46-15c3a45adbd1","version":3} \ No newline at end of file diff --git a/crates/zkcast/tests/fixtures/keystore/password b/crates/zkcast/tests/fixtures/keystore/password new file mode 100644 index 000000000..7c4da6ce1 --- /dev/null +++ b/crates/zkcast/tests/fixtures/keystore/password @@ -0,0 +1 @@ +this is keystore password diff --git a/crates/zkcast/tests/fixtures/keystore/password-ec554 b/crates/zkcast/tests/fixtures/keystore/password-ec554 new file mode 100644 index 000000000..aaf01fa0c --- /dev/null +++ b/crates/zkcast/tests/fixtures/keystore/password-ec554 @@ -0,0 +1 @@ +keystorepassword \ No newline at end of file diff --git a/crates/zkcast/tests/fixtures/sign_typed_data.json b/crates/zkcast/tests/fixtures/sign_typed_data.json new file mode 100644 index 000000000..a6002810a --- /dev/null +++ b/crates/zkcast/tests/fixtures/sign_typed_data.json @@ -0,0 +1,38 @@ +{ + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Message": [ + { + "name": "data", + "type": "string" + } + ] + }, + "primaryType": "Message", + "domain": { + "name": "example.metamask.io", + "version": "1", + "chainId": "1", + "verifyingContract": "0x0000000000000000000000000000000000000000" + }, + "message": { + "data": "Hello!" + } +} \ No newline at end of file diff --git a/crates/zkforge/Cargo.toml b/crates/zkforge/Cargo.toml new file mode 100644 index 000000000..b67eada6f --- /dev/null +++ b/crates/zkforge/Cargo.toml @@ -0,0 +1,98 @@ +[package] +name = "zkforge" +description = "Fast and flexible Ethereum testing framework" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[[bin]] +name = "zkforge" +path = "bin/main.rs" + +[build-dependencies] +vergen = { version = "8", default-features = false, features = ["build", "git", "git2"] } + +[dependencies] +# lib +foundry-utils.workspace = true +foundry-common.workspace = true +foundry-config.workspace = true +foundry-evm.workspace = true +zkcast.workspace = true + +comfy-table = "7" +ethers = { git = "https://github.com/mm-zk/ethers-rs", tag = "main_public_artifact", features = ["solc-full"] } +eyre.workspace = true +proptest = "1" +rayon = "1" +serde.workspace = true +tracing.workspace = true +yansi = "0.5" + +# zksync +era_revm = { git = "https://github.com/matter-labs/era-revm.git", tag = "v0.0.1-alpha" } +zksync-web3-rs = {git = "https://github.com/lambdaclass/zksync-web3-rs.git", rev = "70327ae5413c517bd4d27502507cdd96ee40cd22"} +ansi_term = "0.12.1" +anyhow = {version = "1.0.70"} +dirs = {version = "5.0.0"} +url = "2.3.1" + +# bin +forge-fmt.workspace = true +forge-doc.workspace = true +foundry-cli.workspace = true +foundry-debugger.workspace = true + +alloy-primitives = { workspace = true, features = ["serde"] } + +async-trait = "0.1" +clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } +clap_complete = "4" +clap_complete_fig = "4" +dialoguer = { version = "0.11", default-features = false } +dunce = "1" +futures = "0.3" +hex.workspace = true +indicatif = "0.17" +itertools.workspace = true +once_cell = "1" +parking_lot = "0.12" +regex = { version = "1", default-features = false } +reqwest = { version = "0.11", default-features = false, features = ["json"] } +semver = "1" +serde_json.workspace = true +similar = { version = "2", features = ["inline"] } +solang-parser.workspace = true +strum = { version = "0.25", features = ["derive"] } +thiserror = "1" +tokio = { version = "1", features = ["time"] } +watchexec = "2" + +[dev-dependencies] +anvil.workspace = true +foundry-test-utils.workspace = true + +criterion = "0.5" +globset = "0.4" +path-slash = "0.2" +pretty_assertions = "1" +serial_test = "2" +svm = { package = "svm-rs", version = "0.3", default-features = false, features = ["rustls"] } +tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } + +[features] +default = ["rustls"] +rustls = ["foundry-cli/rustls", "reqwest/rustls-tls", "reqwest/rustls-tls-native-roots"] +openssl = ["foundry-cli/openssl", "reqwest/default-tls"] + +# feature for heavy (long-running) integration tests +heavy-integration-tests = [] + +[[bench]] +name = "test" +harness = false diff --git a/crates/zkforge/README.md b/crates/zkforge/README.md new file mode 100644 index 000000000..35cb196ce --- /dev/null +++ b/crates/zkforge/README.md @@ -0,0 +1,446 @@ +# `forge` + +Forge is a fast and flexible Ethereum testing framework, inspired by +[Dapp](https://github.com/dapphub/dapptools/tree/master/src/dapp). + +If you are looking into how to consume the software as an end user, check the +[CLI README](../cli/README.md). + +For more context on how the package works under the hood, look in the +[code docs](./src/lib.rs). + +**Need help with Forge? Read the [📖 Foundry Book (Forge Guide)][foundry-book-forge-guide] (WIP)!** + +[foundry-book-forge-guide]: https://book.getfoundry.sh/forge/ + +## Why? + +### Write your tests in Solidity to minimize context switching + +Writing tests in Javascript/Typescript while writing your smart contracts in +Solidity can be confusing. Forge lets you write your tests in Solidity, so you +can focus on what matters. + +```solidity +contract Foo { + uint256 public x = 1; + function set(uint256 _x) external { + x = _x; + } + + function double() external { + x = 2 * x; + } +} + +contract FooTest { + Foo foo; + + // The state of the contract gets reset before each + // test is run, with the `setUp()` function being called + // each time after deployment. + function setUp() public { + foo = new Foo(); + } + + // A simple unit test + function testDouble() public { + require(foo.x() == 1); + foo.double(); + require(foo.x() == 2); + } +} +``` + +### Fuzzing: Go beyond unit testing + +When testing smart contracts, fuzzing can uncover edge cases which would be hard +to manually detect with manual unit testing. We support fuzzing natively, where +any test function that takes >0 arguments will be fuzzed, using the +[proptest](https://docs.rs/proptest/1.0.0/proptest/) crate. + +An example of how a fuzzed test would look like can be seen below: + +```solidity +function testDoubleWithFuzzing(uint256 x) public { + foo.set(x); + require(foo.x() == x); + foo.double(); + require(foo.x() == 2 * x); +} +``` + +## Features + +- [ ] test + - [x] Simple unit tests + - [x] Gas costs + - [x] DappTools style test output + - [x] JSON test output + - [x] Matching on regex + - [x] DSTest-style assertions support + - [x] Fuzzing + - [ ] Symbolic execution + - [ ] Coverage + - [x] HEVM-style Solidity cheatcodes + - [ ] Structured tracing with abi decoding + - [ ] Per-line gas profiling + - [x] Forking mode + - [x] Automatic solc selection +- [x] build + - [x] Can read DappTools-style .sol.json artifacts + - [x] Manual remappings + - [x] Automatic remappings + - [x] Multiple compiler versions + - [x] Incremental compilation + - [ ] Can read Hardhat-style artifacts + - [ ] Can read Truffle-style artifacts +- [x] install +- [x] update +- [ ] debug +- [x] CLI Tracing with `RUST_LOG=forge=trace` + +### Gas Report + +Foundry will show you a comprehensive gas report about your contracts. It returns the `min`, `average`, `median` and, `max` gas cost for every function. + +It looks at **all** the tests that make a call to a given function and records the associated gas costs. For example, if something calls a function and it reverts, that's probably the `min` value. Another example is the `max` value that is generated usually during the first call of the function (as it has to initialise storage, variables, etc.) + +Usually, the `median` value is what your users will probably end up paying. `max` and `min` concern edge cases that you might want to explicitly test against, but users will probably never encounter. + +image + +### Cheat codes + +_The below is modified from +[Dapp's README](https://github.com/dapphub/dapptools/blob/master/src/hevm/README.md#cheat-codes)_ + +We allow modifying blockchain state with "cheat codes". These can be accessed by +calling into a contract at address `0x7109709ECfa91a80626fF3989D68f67F5b1DD12D`, +which implements the following methods: + +- `function warp(uint x) public` Sets the block timestamp to `x`. + +- `function difficulty(uint x) public` Sets the block difficulty to `x`. + +- `function roll(uint x) public` Sets the block number to `x`. + +- `function coinbase(address c) public` Sets the block coinbase to `c`. + +- `function store(address c, bytes32 loc, bytes32 val) public` Sets the slot + `loc` of contract `c` to `val`. + +- `function load(address c, bytes32 loc) public returns (bytes32 val)` Reads the + slot `loc` of contract `c`. + +- `function sign(uint sk, bytes32 digest) public returns (uint8 v, bytes32 r, bytes32 s)` + Signs the `digest` using the private key `sk`. Note that signatures produced + via `hevm.sign` will leak the private key. + +- `function addr(uint sk) public returns (address addr)` Derives an ethereum + address from the private key `sk`. Note that `hevm.addr(0)` will fail with + `BadCheatCode` as `0` is an invalid ECDSA private key. `sk` values above the + secp256k1 curve order, near the max uint256 value will also fail. + +- `function ffi(string[] calldata) external returns (bytes memory)` Executes the + arguments as a command in the system shell and returns stdout. Note that this + cheatcode means test authors can execute arbitrary code on user machines as + part of a call to `forge test`, for this reason all calls to `ffi` will fail + unless the `--ffi` flag is passed. + +- `function deal(address who, uint256 amount)`: Sets an account's balance + +- `function etch(address where, bytes memory what)`: Sets the contract code at + some address contract code + +- `function prank(address sender)`: Performs the next smart contract call as another address (prank just changes msg.sender. Tx still occurs as normal) + +- `function prank(address sender, address origin)`: Performs the next smart contract call setting both `msg.sender` and `tx.origin`. + +- `function startPrank(address sender)`: Performs smart contract calls as another address. The account impersonation lasts until the end of the transaction, or until `stopPrank` is called. + +- `function startPrank(address sender, address origin)`: Performs smart contract calls as another address, while also setting `tx.origin`. The account impersonation lasts until the end of the transaction, or until `stopPrank` is called. + +- `function stopPrank()`: Stop calling smart contracts with the address set at `startPrank` + +- `function expectRevert( expectedError)`: + Tells the evm to expect that the next call reverts with specified error bytes. Valid input types: `bytes`, and `bytes4`. Implicitly, strings get converted to bytes except when shorter than 4, in which case you will need to cast explicitly to `bytes`. +- `function expectEmit(bool,bool,bool,bool) external`: Expects the next emitted event. Params check topic 1, topic 2, topic 3 and data are the same. + +- `function expectEmit(bool,bool,bool,bool,address) external`: Expects the next emitted event. Params check topic 1, topic 2, topic 3 and data are the same. Also checks supplied address against address of originating contract. + +- `function getCode(string calldata) external returns (bytes memory)`: Fetches bytecode from a contract artifact. The parameter can either be in the form `ContractFile.sol` (if the filename and contract name are the same), `ContractFile.sol:ContractName`, or `./path/to/artifact.json`. + +- `function label(address addr, string calldata label) external`: Label an address in test traces. + +- `function assume(bool) external`: When fuzzing, generate new inputs if conditional not met + +- `function setNonce(address account, uint64 nonce) external`: Set nonce for an account, increment only. + +- `function getNonce(address account)`: Get nonce for an account. + +- `function chainId(uint x) public` Sets the block chainid to `x`. + +The below example uses the `warp` cheatcode to override the timestamp & `expectRevert` to expect a specific revert string: + +```solidity +interface Vm { + function warp(uint256 x) external; + function expectRevert(bytes calldata) external; +} + +contract Foo { + function bar(uint256 a) public returns (uint256) { + require(a < 100, "My expected revert string"); + return a; + } +} + +contract MyTest { + Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + function testWarp() public { + vm.warp(100); + require(block.timestamp == 100); + } + + function testBarExpectedRevert() public { + vm.expectRevert("My expected revert string"); + // This would fail *if* we didnt expect revert. Since we expect the revert, + // it doesn't, unless the revert string is wrong. + foo.bar(101); + } + + function testFailBar() public { + // this call would revert, causing this test to pass + foo.bar(101); + } +} +``` + +Below is another example using the `expectEmit` cheatcode to check events: + +```solidity +interface Vm { + function expectEmit(bool,bool,bool,bool) external; + function expectEmit(bool,bool,bool,bool,address) external; +} + +contract T is DSTest { + Vm vm = Vm(HEVM_ADDRESS); + event Transfer(address indexed from,address indexed to, uint256 amount); + function testExpectEmit() public { + ExpectEmit emitter = new ExpectEmit(); + // check topic 1, topic 2, and data are the same as the following emitted event + vm.expectEmit(true,true,false,true); + emit Transfer(address(this), address(1337), 1337); + emitter.t(); + } + + function testExpectEmitWithAddress() public { + ExpectEmit emitter = new ExpectEmit(); + // do the same as above and check emitting address + vm.expectEmit(true,true,false,true,address(emitter)); + emit Transfer(address(this), address(1337), 1337); + emitter.t(); + } +} + +contract ExpectEmit { + event Transfer(address indexed from,address indexed to, uint256 amount); + function t() public { + emit Transfer(msg.sender, address(1337), 1337); + } +} +``` + +A full interface for all cheatcodes is here: + +```solidity +interface Hevm { + // Set block.timestamp (newTimestamp) + function warp(uint256) external; + // Set block.height (newHeight) + function roll(uint256) external; + // Set block.basefee (newBasefee) + function fee(uint256) external; + // Set block.coinbase (who) + function coinbase(address) external; + // Loads a storage slot from an address (who, slot) + function load(address,bytes32) external returns (bytes32); + // Stores a value to an address' storage slot, (who, slot, value) + function store(address,bytes32,bytes32) external; + // Signs data, (privateKey, digest) => (v, r, s) + function sign(uint256,bytes32) external returns (uint8,bytes32,bytes32); + // Gets address for a given private key, (privateKey) => (address) + function addr(uint256) external returns (address); + // Performs a foreign function call via terminal, (stringInputs) => (result) + function ffi(string[] calldata) external returns (bytes memory); + // Sets the *next* call's msg.sender to be the input address + function prank(address) external; + // Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called + function startPrank(address) external; + // Sets the *next* call's msg.sender to be the input address, and the tx.origin to be the second input + function prank(address,address) external; + // Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called, and the tx.origin to be the second input + function startPrank(address,address) external; + // Resets subsequent calls' msg.sender to be `address(this)` + function stopPrank() external; + // Sets an address' balance, (who, newBalance) + function deal(address, uint256) external; + // Sets an address' code, (who, newCode) + function etch(address, bytes calldata) external; + // Expects an error on next call + function expectRevert() external; + function expectRevert(bytes calldata) external; + function expectRevert(bytes4) external; + // Record all storage reads and writes + function record() external; + // Gets all accessed reads and write slot from a recording session, for a given address + function accesses(address) external returns (bytes32[] memory reads, bytes32[] memory writes); + // 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) + function expectEmit(bool,bool,bool,bool) external; + // Mocks a call to an address, returning specified data. + // Calldata can either be strict or a partial match, e.g. if you only + // pass a Solidity selector to the expected calldata, then the entire Solidity + // function will be mocked. + function mockCall(address,bytes calldata,bytes calldata) external; + // Mocks a call to an address with a specific msg.value, returning specified data. + // Calldata match takes precedence over msg.value in case of ambiguity. + function mockCall(address,uint256,bytes calldata,bytes calldata) external; + // Reverts a call to an address with specified revert data. + function mockCallRevert(address, bytes calldata, bytes calldata) external; + // Reverts a call to an address with a specific msg.value, with specified revert data. + function mockCallRevert(address, uint256 msgValue, bytes calldata, bytes calldata) external; + // Clears all mocked and reverted mocked calls + function clearMockedCalls() external; + // Expect a call to an address with the specified calldata. + // Calldata can either be strict or a partial match + function expectCall(address, bytes calldata) external; + // Expect given number of calls to an address with the specified calldata. + // Calldata can either be strict or a partial match + function expectCall(address, bytes calldata, uint64) external; + // Expect a call to an address with the specified msg.value and calldata + function expectCall(address, uint256, bytes calldata) external; + // Expect a given number of calls to an address with the specified msg.value and calldata + function expectCall(address, uint256, bytes calldata, uint64) external; + // Expect a call to an address with the specified msg.value, gas, and calldata. + function expectCall(address, uint256, uint64, bytes calldata) external; + // Expect a given number of calls to an address with the specified msg.value, gas, and calldata. + function expectCall(address, uint256, uint64, bytes calldata, uint64) external; + // Expect a call to an address with the specified msg.value and calldata, and a *minimum* amount of gas. + function expectCallMinGas(address, uint256, uint64, bytes calldata) external; + // Expect a given number of calls to an address with the specified msg.value and calldata, and a *minimum* amount of gas. + function expectCallMinGas(address, uint256, uint64, bytes calldata, uint64) external; + + // Only allows memory writes to offsets [0x00, 0x60) ∪ [min, max) in the current subcontext. If any other + // memory is written to, the test will fail. + function expectSafeMemory(uint64, uint64) external; + // Only allows memory writes to offsets [0x00, 0x60) ∪ [min, max) in the next created subcontext. + // If any other memory is written to, the test will fail. + function expectSafeMemoryCall(uint64, uint64) external; + // Fetches the contract bytecode from its artifact file + function getCode(string calldata) external returns (bytes memory); + // Label an address in test traces + function label(address addr, string calldata label) external; + // When fuzzing, generate new inputs if conditional not met + function assume(bool) external; + // Set nonce for an account, increment only + function setNonce(address,uint64) external; + // Get nonce for an account + function getNonce(address) external returns(uint64); +} +``` + +### `console.log` + +We support the logging functionality from Hardhat's `console.log`. + +If you are on a hardhat project, `import hardhat/console.sol` should just work if you use `forge test --hh`. + +If no, there is an implementation contract [here](https://raw.githubusercontent.com/NomicFoundation/hardhat/master/packages/hardhat-core/console.sol). We currently recommend that you copy this contract, place it in your `test` folder, and import it into the contract where you wish to use `console.log`, though there should be more streamlined functionality soon. + +Usage follows the same format as [Hardhat](https://hardhat.org/hardhat-network/reference/#console-log): + +```solidity +import "./console.sol"; +... +console.log(someValue); + +``` + +Note: to make logs visible in `stdout`, you must use at least level 2 verbosity. + +```bash +$> forge test -vv +[PASS] test1() (gas: 7683) +... +Logs: + + ... +``` + +## Remappings + +If you are working in a repo with NPM-style imports, like + +``` +import "@openzeppelin/contracts/access/Ownable.sol"; +``` + +then you will need to create a `remappings.txt` file at the top level of your project directory, so that Forge knows where to find these dependencies. + +For example, if you have `@openzeppelin` imports, you would + +1. `forge install openzeppelin/openzeppelin-contracts` (this will add the repo to `lib/openzepplin-contracts`) +2. Create a remappings file: `touch remappings.txt` +3. Add this line to `remappings.txt` + +``` +@openzeppelin/=lib/openzeppelin-contracts/ +``` + +## Github Actions CI + +We recommend using the [Github Actions CI setup](https://book.getfoundry.sh/config/continous-integration.html) from the [📖 Foundry Book](https://book.getfoundry.sh/index.html). + +## Future Features + +### Dapptools feature parity + +Over the next months, we intend to add the following features which are +available in upstream dapptools: + +1. Stack Traces: Currently we do not provide any debug information when a call + fails. We intend to add a structured printer (something like + [this](https://twitter.com/gakonst/status/1434337110111182848) which will + show all the calls, logs and arguments passed across intermediate smart + contract calls, which should help with debugging. +1. [Invariant Tests](https://github.com/dapphub/dapptools/blob/master/src/dapp/README.md#invariant-testing) +1. [Interactive Debugger](https://github.com/dapphub/dapptools/blob/master/src/hevm/README.md#interactive-debugger-key-bindings) +1. [Code coverage](https://twitter.com/dapptools/status/1435973810545729536) +1. [Gas snapshots](https://github.com/dapphub/dapptools/pull/850/files) +1. [Symbolic EVM](https://fv.ethereum.org/2020/07/28/symbolic-hevm-release/) + +### Unique features? + +We also intend to add features which are not available in dapptools: + +1. Even faster tests with parallel EVM execution that produces state diffs + instead of modifying the state +1. Improved UX for assertions: + 1. Check revert error or reason on a Solidity call + 1. Check that an event was emitted with expected arguments +1. Support more EVM backends ([revm](https://github.com/bluealloy/revm/), geth's + evm, hevm etc.) & benchmark performance across them +1. Declarative deployment system based on a config file +1. Formatting & Linting (maybe powered by + [Solang](https://github.com/hyperledger-labs/solang)) + 1. `forge fmt`, an automatic code formatter according to standard rules (like + [`prettier-plugin-solidity`](https://github.com/prettier-solidity/prettier-plugin-solidity)) + 1. `forge lint`, a linter + static analyzer, like a combination of + [`solhint`](https://github.com/protofire/solhint) and + [slither](https://github.com/crytic/slither/) +1. Flamegraphs for gas profiling diff --git a/crates/zkforge/assets/.gitignoreTemplate b/crates/zkforge/assets/.gitignoreTemplate new file mode 100644 index 000000000..85198aaa5 --- /dev/null +++ b/crates/zkforge/assets/.gitignoreTemplate @@ -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/crates/zkforge/assets/CounterTemplate.s.sol b/crates/zkforge/assets/CounterTemplate.s.sol new file mode 100644 index 000000000..1a47b40b8 --- /dev/null +++ b/crates/zkforge/assets/CounterTemplate.s.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script, console2} from "forge-std/Script.sol"; + +contract CounterScript is Script { + function setUp() public {} + + function run() public { + vm.broadcast(); + } +} diff --git a/crates/zkforge/assets/CounterTemplate.sol b/crates/zkforge/assets/CounterTemplate.sol new file mode 100644 index 000000000..aded7997b --- /dev/null +++ b/crates/zkforge/assets/CounterTemplate.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/crates/zkforge/assets/CounterTemplate.t.sol b/crates/zkforge/assets/CounterTemplate.t.sol new file mode 100644 index 000000000..e9b9e6acf --- /dev/null +++ b/crates/zkforge/assets/CounterTemplate.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, console2} 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/zkforge/assets/README.md b/crates/zkforge/assets/README.md new file mode 100644 index 000000000..9265b4558 --- /dev/null +++ b/crates/zkforge/assets/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/crates/zkforge/assets/generated/TestTemplate.t.sol b/crates/zkforge/assets/generated/TestTemplate.t.sol new file mode 100644 index 000000000..468acba01 --- /dev/null +++ b/crates/zkforge/assets/generated/TestTemplate.t.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, console2} from "forge-std/Test.sol"; +import {{contract_name}} from "../src/{contract_name}.sol"; + +contract {contract_name}Test is Test { + {contract_name} public {instance_name}; + + function setUp() public { + {instance_name} = new {contract_name}(); + } +} \ No newline at end of file diff --git a/crates/zkforge/assets/workflowTemplate.yml b/crates/zkforge/assets/workflowTemplate.yml new file mode 100644 index 000000000..09880b1d7 --- /dev/null +++ b/crates/zkforge/assets/workflowTemplate.yml @@ -0,0 +1,34 @@ +name: test + +on: workflow_dispatch + +env: + FOUNDRY_PROFILE: ci + +jobs: + check: + strategy: + fail-fast: true + + name: Foundry project + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Run Forge build + run: | + forge --version + forge build --sizes + id: build + + - name: Run Forge tests + run: | + forge test -vvv + id: test diff --git a/crates/zkforge/benches/test.rs b/crates/zkforge/benches/test.rs new file mode 100644 index 000000000..7650077b1 --- /dev/null +++ b/crates/zkforge/benches/test.rs @@ -0,0 +1,24 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use foundry_test_utils::{util::setup_forge_remote, TestCommand, TestProject}; + +/// Returns a cloned and `forge built` `solmate` project +fn built_solmate() -> (TestProject, TestCommand) { + setup_forge_remote("transmissions11/solmate") +} + +fn forge_test_benchmark(c: &mut Criterion) { + let (prj, _) = built_solmate(); + + let mut group = c.benchmark_group("forge test"); + group.sample_size(10); + group.bench_function("solmate", |b| { + let mut cmd = prj.forge_command(); + cmd.arg("test"); + b.iter(|| { + cmd.ensure_execute_success().unwrap(); + }); + }); +} + +criterion_group!(benches, forge_test_benchmark); +criterion_main!(benches); diff --git a/crates/zkforge/bin/cmd/bind.rs b/crates/zkforge/bin/cmd/bind.rs new file mode 100644 index 000000000..1a88d2028 --- /dev/null +++ b/crates/zkforge/bin/cmd/bind.rs @@ -0,0 +1,213 @@ +use clap::{Parser, ValueHint}; +use ethers::contract::{Abigen, ContractFilter, ExcludeContracts, MultiAbigen, SelectContracts}; +use eyre::Result; +use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; +use foundry_common::{compile, fs::json_files}; +use foundry_config::impl_figment_convert; +use std::{ + fs, + path::{Path, PathBuf}, +}; + +impl_figment_convert!(BindArgs, build_args); + +const DEFAULT_CRATE_NAME: &str = "foundry-contracts"; +const DEFAULT_CRATE_VERSION: &str = "0.1.0"; + +/// CLI arguments for `forge bind`. +#[derive(Debug, Clone, Parser)] +pub struct BindArgs { + /// Path to where the contract artifacts are stored. + #[clap( + long = "bindings-path", + short, + value_hint = ValueHint::DirPath, + value_name = "PATH" + )] + pub bindings: Option, + + /// Create bindings only for contracts whose names match the specified filter(s) + #[clap(long)] + pub select: Vec, + + /// Create bindings only for contracts whose names do not match the specified filter(s) + #[clap(long, conflicts_with = "select")] + pub skip: Vec, + + /// Explicitly generate bindings for all contracts + /// + /// By default all contracts ending with `Test` or `Script` are excluded. + #[clap(long, conflicts_with_all = &["select", "skip"])] + pub select_all: bool, + + /// The name of the Rust crate to generate. + /// + /// This should be a valid crates.io crate name, + /// however, this is not currently validated by this command. + #[clap(long, default_value = DEFAULT_CRATE_NAME, value_name = "NAME")] + crate_name: String, + + /// The version of the Rust crate to generate. + /// + /// This should be a standard semver version string, + /// however, this is not currently validated by this command. + #[clap(long, default_value = DEFAULT_CRATE_VERSION, value_name = "VERSION")] + crate_version: String, + + /// Generate the bindings as a module instead of a crate. + #[clap(long)] + module: bool, + + /// Overwrite existing generated bindings. + /// + /// By default, the command will check that the bindings are correct, and then exit. If + /// --overwrite is passed, it will instead delete and overwrite the bindings. + #[clap(long)] + overwrite: bool, + + /// Generate bindings as a single file. + #[clap(long)] + single_file: bool, + + /// Skip Cargo.toml consistency checks. + #[clap(long)] + skip_cargo_toml: bool, + + /// Skips running forge build before generating binding + #[clap(long)] + skip_build: bool, + + #[clap(flatten)] + build_args: CoreBuildArgs, +} + +impl BindArgs { + /// Get the path to the root of the autogenerated crate + fn bindings_root(&self, artifacts: impl AsRef) -> PathBuf { + self.bindings.clone().unwrap_or_else(|| artifacts.as_ref().join("bindings")) + } + + /// `true` if the bindings root already exists + fn bindings_exist(&self, artifacts: impl AsRef) -> bool { + self.bindings_root(artifacts).is_dir() + } + + /// Returns the filter to use for `MultiAbigen` + fn get_filter(&self) -> ContractFilter { + if self.select_all { + return ContractFilter::All + } + if !self.select.is_empty() { + return SelectContracts::default().extend_regex(self.select.clone()).into() + } + if !self.skip.is_empty() { + return ExcludeContracts::default().extend_regex(self.skip.clone()).into() + } + // This excludes all Test/Script and forge-std contracts + ExcludeContracts::default() + .extend_pattern([ + ".*Test.*", + ".*Script", + "console[2]?", + "CommonBase", + "Components", + "[Ss]td(Chains|Math|Error|Json|Utils|Cheats|Style|Invariant|Assertions|Storage(Safe)?)", + "[Vv]m.*", + ]) + .extend_names(["IMulticall3"]) + .into() + } + + /// Instantiate the multi-abigen + fn get_multi(&self, artifacts: impl AsRef) -> Result { + let abigens = json_files(artifacts.as_ref()) + .into_iter() + .filter_map(|path| { + // we don't want `.metadata.json files + let stem = path.file_stem()?; + if stem.to_str()?.ends_with(".metadata") { + None + } else { + Some(path) + } + }) + .map(|x| { + Abigen::from_file(x)? + .add_derive("serde::Serialize")? + .add_derive("serde::Deserialize") + }) + .collect::, _>>()?; + let multi = MultiAbigen::from_abigens(abigens).with_filter(self.get_filter()); + + eyre::ensure!( + !multi.is_empty(), + r#" +No contract artifacts found. Hint: Have you built your contracts yet? `forge bind` does not currently invoke `forge build`, although this is planned for future versions. + "# + ); + Ok(multi) + } + + /// Check that the existing bindings match the expected abigen output + fn check_existing_bindings(&self, artifacts: impl AsRef) -> Result<()> { + let bindings = self.get_multi(&artifacts)?.build()?; + println!("Checking bindings for {} contracts.", bindings.len()); + if !self.module { + bindings.ensure_consistent_crate( + &self.crate_name, + &self.crate_version, + self.bindings_root(&artifacts), + self.single_file, + !self.skip_cargo_toml, + )?; + } else { + bindings.ensure_consistent_module(self.bindings_root(&artifacts), self.single_file)?; + } + println!("OK."); + Ok(()) + } + + /// Generate the bindings + fn generate_bindings(&self, artifacts: impl AsRef) -> Result<()> { + let bindings = self.get_multi(&artifacts)?.build()?; + println!("Generating bindings for {} contracts", bindings.len()); + if !self.module { + bindings.dependencies([r#"serde = "1""#]).write_to_crate( + &self.crate_name, + &self.crate_version, + self.bindings_root(&artifacts), + self.single_file, + )?; + } else { + bindings.write_to_module(self.bindings_root(&artifacts), self.single_file)?; + } + Ok(()) + } + + pub fn run(self) -> Result<()> { + if !self.skip_build { + // run `forge build` + let project = self.build_args.project()?; + compile::compile(&project, false, false)?; + } + + let artifacts = self.try_load_config_emit_warnings()?.out; + + if !self.overwrite && self.bindings_exist(&artifacts) { + println!("Bindings found. Checking for consistency."); + return self.check_existing_bindings(&artifacts) + } + + if self.overwrite && self.bindings_exist(&artifacts) { + fs::remove_dir_all(self.bindings_root(&artifacts))?; + } + + self.generate_bindings(&artifacts)?; + + println!( + "Bindings have been output to {}", + self.bindings_root(&artifacts).to_str().unwrap() + ); + Ok(()) + } +} diff --git a/crates/zkforge/bin/cmd/build.rs b/crates/zkforge/bin/cmd/build.rs new file mode 100644 index 000000000..638d7f732 --- /dev/null +++ b/crates/zkforge/bin/cmd/build.rs @@ -0,0 +1,164 @@ +use super::{install, watch::WatchArgs}; +use clap::Parser; +use ethers::solc::{Project, ProjectCompileOutput}; +use eyre::Result; +use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; +use foundry_common::{ + compile, + compile::{ProjectCompiler, SkipBuildFilter}, +}; +use foundry_config::{ + figment::{ + self, + error::Kind::InvalidType, + value::{Dict, Map, Value}, + Metadata, Profile, Provider, + }, + Config, +}; +use serde::Serialize; +use watchexec::config::{InitConfig, RuntimeConfig}; + +foundry_config::merge_impl_figment_convert!(BuildArgs, args); + +/// CLI arguments for `forge build`. +/// +/// CLI arguments take the highest precedence in the Config/Figment hierarchy. +/// In order to override them in the foundry `Config` they need to be merged into an existing +/// `figment::Provider`, like `foundry_config::Config` is. +/// +/// # Example +/// +/// ``` +/// use foundry_cli::cmd::forge::build::BuildArgs; +/// use foundry_config::Config; +/// # fn t(args: BuildArgs) { +/// let config = Config::from(&args); +/// # } +/// ``` +/// +/// `BuildArgs` implements `figment::Provider` in which all config related fields are serialized and +/// then merged into an existing `Config`, effectively overwriting them. +/// +/// Some arguments are marked as `#[serde(skip)]` and require manual processing in +/// `figment::Provider` implementation +#[derive(Debug, Clone, Parser, Serialize, Default)] +#[clap(next_help_heading = "Build options", about = None, long_about = None)] // override doc +pub struct BuildArgs { + /// Print compiled contract names. + #[clap(long)] + #[serde(skip)] + pub names: bool, + + /// Print compiled contract sizes. + #[clap(long)] + #[serde(skip)] + pub sizes: bool, + + /// Skip building files whose names contain the given filter. + /// + /// `test` and `script` are aliases for `.t.sol` and `.s.sol`. + #[clap(long, num_args(1..))] + #[serde(skip)] + pub skip: Option>, + + #[clap(flatten)] + #[serde(flatten)] + pub args: CoreBuildArgs, + + #[clap(flatten)] + #[serde(skip)] + pub watch: WatchArgs, +} + +impl BuildArgs { + pub fn run(self) -> Result { + let mut config = self.try_load_config_emit_warnings()?; + let mut project = config.project()?; + + if install::install_missing_dependencies(&mut config, self.args.silent) && + config.auto_detect_remappings + { + // need to re-configure here to also catch additional remappings + config = self.load_config(); + project = config.project()?; + } + + let filters = self.skip.unwrap_or_default(); + + if self.args.silent { + compile::suppress_compile_with_filter(&project, filters) + } else { + let compiler = ProjectCompiler::with_filter(self.names, self.sizes, filters); + compiler.compile(&project) + } + } + + /// Returns the `Project` for the current workspace + /// + /// This loads the `foundry_config::Config` for the current workspace (see + /// [`utils::find_project_root_path`] and merges the cli `BuildArgs` into it before returning + /// [`foundry_config::Config::project()`] + pub fn project(&self) -> Result { + self.args.project() + } + + /// Returns whether `BuildArgs` was configured with `--watch` + pub fn is_watch(&self) -> bool { + self.watch.watch.is_some() + } + + /// Returns the [`watchexec::InitConfig`] and [`watchexec::RuntimeConfig`] necessary to + /// bootstrap a new [`watchexe::Watchexec`] loop. + pub(crate) fn _watchexec_config(&self) -> Result<(InitConfig, RuntimeConfig)> { + // use the path arguments or if none where provided the `src` dir + self.watch.watchexec_config(|| { + let config = Config::from(self); + vec![config.src, config.test, config.script] + }) + } +} + +// Make this args a `figment::Provider` so that it can be merged into the `Config` +impl Provider for BuildArgs { + fn metadata(&self) -> Metadata { + Metadata::named("Build Args Provider") + } + + fn data(&self) -> Result, figment::Error> { + let value = Value::serialize(self)?; + let error = InvalidType(value.to_actual(), "map".into()); + let mut dict = value.into_dict().ok_or(error)?; + + if self.names { + dict.insert("names".to_string(), true.into()); + } + + if self.sizes { + dict.insert("sizes".to_string(), true.into()); + } + + Ok(Map::from([(Config::selected_profile(), dict)])) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_parse_build_filters() { + let args: BuildArgs = BuildArgs::parse_from(["foundry-cli", "--skip", "tests"]); + assert_eq!(args.skip, Some(vec![SkipBuildFilter::Tests])); + + let args: BuildArgs = BuildArgs::parse_from(["foundry-cli", "--skip", "scripts"]); + assert_eq!(args.skip, Some(vec![SkipBuildFilter::Scripts])); + + let args: BuildArgs = + BuildArgs::parse_from(["foundry-cli", "--skip", "tests", "--skip", "scripts"]); + assert_eq!(args.skip, Some(vec![SkipBuildFilter::Tests, SkipBuildFilter::Scripts])); + + let args: BuildArgs = BuildArgs::parse_from(["foundry-cli", "--skip", "tests", "scripts"]); + assert_eq!(args.skip, Some(vec![SkipBuildFilter::Tests, SkipBuildFilter::Scripts])); + } +} diff --git a/crates/zkforge/bin/cmd/cache.rs b/crates/zkforge/bin/cmd/cache.rs new file mode 100644 index 000000000..e70a2c66f --- /dev/null +++ b/crates/zkforge/bin/cmd/cache.rs @@ -0,0 +1,192 @@ +use cache::Cache; +use clap::{ + builder::{PossibleValuesParser, TypedValueParser}, + Arg, Command, Parser, Subcommand, +}; +use ethers::prelude::Chain; +use eyre::Result; +use foundry_config::{cache, Chain as FoundryConfigChain, Config}; +use std::{ffi::OsStr, str::FromStr}; +use strum::VariantNames; + +/// CLI arguments for `forge cache`. +#[derive(Debug, Parser)] +pub struct CacheArgs { + #[clap(subcommand)] + pub sub: CacheSubcommands, +} + +#[derive(Debug, Subcommand)] +pub enum CacheSubcommands { + /// Cleans cached data from the global foundry directory. + Clean(CleanArgs), + + /// Shows cached data from the global foundry directory. + Ls(LsArgs), +} + +/// CLI arguments for `forge clean`. +#[derive(Debug, Parser)] +#[clap(group = clap::ArgGroup::new("etherscan-blocks").multiple(false))] +pub struct CleanArgs { + /// The chains to clean the cache for. + /// + /// Can also be "all" to clean all chains. + #[clap( + env = "CHAIN", + default_value = "all", + value_parser = ChainOrAllValueParser::default(), + )] + chains: Vec, + + /// The blocks to clean the cache for. + #[clap( + short, + long, + num_args(1..), + use_value_delimiter(true), + value_delimiter(','), + group = "etherscan-blocks" + )] + blocks: Vec, + + /// Whether to clean the Etherscan cache. + #[clap(long, group = "etherscan-blocks")] + etherscan: bool, +} + +impl CleanArgs { + pub fn run(self) -> Result<()> { + let CleanArgs { chains, blocks, etherscan } = self; + + for chain_or_all in chains { + match chain_or_all { + ChainOrAll::Chain(chain) => clean_chain_cache(chain, blocks.to_vec(), etherscan)?, + ChainOrAll::All => { + if etherscan { + Config::clean_foundry_etherscan_cache()?; + } else { + Config::clean_foundry_cache()? + } + } + } + } + + Ok(()) + } +} + +#[derive(Debug, Parser)] +pub struct LsArgs { + /// The chains to list the cache for. + /// + /// Can also be "all" to list all chains. + #[clap( + env = "CHAIN", + default_value = "all", + value_parser = ChainOrAllValueParser::default(), + )] + chains: Vec, +} + +impl LsArgs { + pub fn run(self) -> Result<()> { + let LsArgs { chains } = self; + let mut cache = Cache::default(); + for chain_or_all in chains { + match chain_or_all { + ChainOrAll::Chain(chain) => { + cache.chains.push(Config::list_foundry_chain_cache(chain.into())?) + } + ChainOrAll::All => cache = Config::list_foundry_cache()?, + } + } + print!("{cache}"); + Ok(()) + } +} + +#[derive(Debug, Clone)] +pub enum ChainOrAll { + Chain(Chain), + All, +} + +impl FromStr for ChainOrAll { + type Err = String; + + fn from_str(s: &str) -> Result { + if let Ok(chain) = ethers::prelude::Chain::from_str(s) { + Ok(ChainOrAll::Chain(chain)) + } else if s == "all" { + Ok(ChainOrAll::All) + } else { + Err(format!("Expected known chain or all, found: {s}")) + } + } +} + +fn clean_chain_cache( + chain: impl Into, + blocks: Vec, + etherscan: bool, +) -> Result<()> { + let chain = chain.into(); + if blocks.is_empty() { + Config::clean_foundry_etherscan_chain_cache(chain)?; + if etherscan { + return Ok(()) + } + Config::clean_foundry_chain_cache(chain)?; + } else { + for block in blocks { + Config::clean_foundry_block_cache(chain, block)?; + } + } + Ok(()) +} + +/// The value parser for `ChainOrAll` +#[derive(Clone, Debug)] +pub struct ChainOrAllValueParser { + inner: PossibleValuesParser, +} + +impl Default for ChainOrAllValueParser { + fn default() -> Self { + ChainOrAllValueParser { inner: possible_chains() } + } +} + +impl TypedValueParser for ChainOrAllValueParser { + type Value = ChainOrAll; + + fn parse_ref( + &self, + cmd: &Command, + arg: Option<&Arg>, + value: &OsStr, + ) -> Result { + self.inner.parse_ref(cmd, arg, value)?.parse::().map_err(|_| { + clap::Error::raw( + clap::error::ErrorKind::InvalidValue, + "chain argument did not match any possible chain variant", + ) + }) + } +} + +fn possible_chains() -> PossibleValuesParser { + Some(&"all").into_iter().chain(Chain::VARIANTS).into() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_parse_cache_ls() { + let args: CacheArgs = CacheArgs::parse_from(["cache", "ls"]); + assert!(matches!(args.sub, CacheSubcommands::Ls(_))); + } +} diff --git a/crates/zkforge/bin/cmd/config.rs b/crates/zkforge/bin/cmd/config.rs new file mode 100644 index 000000000..a8e33cdba --- /dev/null +++ b/crates/zkforge/bin/cmd/config.rs @@ -0,0 +1,60 @@ +use super::build::BuildArgs; +use clap::Parser; +use eyre::Result; +use foundry_cli::utils::LoadConfig; +use foundry_common::{evm::EvmArgs, term::cli_warn}; +use foundry_config::fix::fix_tomls; + +foundry_config::impl_figment_convert!(ConfigArgs, opts, evm_opts); + +/// CLI arguments for `forge config`. +#[derive(Debug, Clone, Parser)] +pub struct ConfigArgs { + /// Print only a basic set of the currently set config values. + #[clap(long)] + basic: bool, + + /// Print currently set config values as JSON. + #[clap(long)] + json: bool, + + /// Attempt to fix any configuration warnings. + #[clap(long)] + fix: bool, + + // support nested build arguments + #[clap(flatten)] + opts: BuildArgs, + + #[clap(flatten)] + evm_opts: EvmArgs, +} + +impl ConfigArgs { + pub fn run(self) -> Result<()> { + if self.fix { + for warning in fix_tomls() { + cli_warn!("{warning}"); + } + return Ok(()) + } + + let config = self.try_load_config_unsanitized_emit_warnings()?; + + let s = if self.basic { + let config = config.into_basic(); + if self.json { + serde_json::to_string_pretty(&config)? + } else { + config.to_string_pretty()? + } + } else if self.json { + serde_json::to_string_pretty(&config)? + } else { + config.to_string_pretty()? + }; + + println!("{s}"); + Ok(()) + } +} diff --git a/crates/zkforge/bin/cmd/coverage.rs b/crates/zkforge/bin/cmd/coverage.rs new file mode 100644 index 000000000..c8ddbe961 --- /dev/null +++ b/crates/zkforge/bin/cmd/coverage.rs @@ -0,0 +1,405 @@ +use super::{install, test::FilterArgs}; +use alloy_primitives::{Address, Bytes, U256}; +use clap::{Parser, ValueEnum, ValueHint}; +use ethers::{ + prelude::{ + artifacts::{Ast, CompactBytecode, CompactDeployedBytecode}, + Artifact, Project, ProjectCompileOutput, + }, + solc::{artifacts::contract::CompactContractBytecode, sourcemap::SourceMap}, +}; +use eyre::{Context, Result}; +use foundry_cli::{ + opts::CoreBuildArgs, + p_println, + utils::{LoadConfig, STATIC_FUZZ_SEED}, +}; +use foundry_common::{compile::ProjectCompiler, evm::EvmArgs, fs}; +use foundry_config::{Config, SolcReq}; +use foundry_utils::types::ToEthers; +use semver::Version; +use std::{collections::HashMap, path::PathBuf, sync::mpsc::channel}; +use tracing::trace; +use yansi::Paint; +use zkforge::{ + coverage::{ + analysis::SourceAnalyzer, anchors::find_anchors, ContractId, CoverageReport, + CoverageReporter, DebugReporter, ItemAnchor, LcovReporter, SummaryReporter, + }, + executor::{inspector::CheatsConfig, opts::EvmOpts}, + result::SuiteResult, + revm::primitives::SpecId, + utils::{build_ic_pc_map, ICPCMap}, + MultiContractRunnerBuilder, TestOptions, +}; + +/// A map, keyed by contract ID, to a tuple of the deployment source map and the runtime source map. +type SourceMaps = HashMap; + +// Loads project's figment and merges the build cli arguments into it +foundry_config::impl_figment_convert!(CoverageArgs, opts, evm_opts); + +/// CLI arguments for `forge coverage`. +#[derive(Debug, Clone, Parser)] +pub struct CoverageArgs { + /// The report type to use for coverage. + /// + /// This flag can be used multiple times. + #[clap(long, value_enum, default_value = "summary")] + report: Vec, + + /// Enable viaIR with minimum optimization + /// + /// This can fix most of the "stack too deep" errors while resulting a + /// relatively accurate source map. + #[clap(long)] + ir_minimum: bool, + + /// The path to output the report. + /// + /// If not specified, the report will be stored in the root of the project. + #[clap( + long, + short, + value_hint = ValueHint::FilePath, + value_name = "PATH" + )] + report_file: Option, + + #[clap(flatten)] + filter: FilterArgs, + + #[clap(flatten)] + evm_opts: EvmArgs, + + #[clap(flatten)] + opts: CoreBuildArgs, +} + +impl CoverageArgs { + pub async fn run(self) -> Result<()> { + let (mut config, evm_opts) = self.load_config_and_evm_opts_emit_warnings()?; + + // install missing dependencies + if install::install_missing_dependencies(&mut config, self.build_args().silent) && + config.auto_detect_remappings + { + // need to re-configure here to also catch additional remappings + config = self.load_config(); + } + + // Set fuzz seed so coverage reports are deterministic + config.fuzz.seed = Some(U256::from_be_bytes(STATIC_FUZZ_SEED).to_ethers()); + + let (project, output) = self.build(&config)?; + p_println!(!self.opts.silent => "Analysing contracts..."); + let report = self.prepare(&config, output.clone())?; + + p_println!(!self.opts.silent => "Running tests..."); + self.collect(project, output, report, config, evm_opts).await + } + + /// Builds the project. + fn build(&self, config: &Config) -> Result<(Project, ProjectCompileOutput)> { + // Set up the project + let project = { + let mut project = config.ephemeral_no_artifacts_project()?; + + if self.ir_minimum { + // TODO: How to detect solc version if the user does not specify a solc version in + // config case1: specify local installed solc ? + // case2: multiple solc versions used and auto_detect_solc == true + if let Some(SolcReq::Version(version)) = &config.solc { + if *version < Version::new(0, 8, 13) { + return Err(eyre::eyre!( + "viaIR with minimum optimization is only available in Solidity 0.8.13 and above." + )); + } + } + + // print warning message + p_println!(!self.opts.silent => "{}", + Paint::yellow( + concat!( + "Warning! \"--ir-minimum\" flag enables viaIR with minimum optimization, which can result in inaccurate source mappings.\n", + "Only use this flag as a workaround if you are experiencing \"stack too deep\" errors.\n", + "Note that \"viaIR\" is only available in Solidity 0.8.13 and above.\n", + "See more:\n", + "https://github.com/foundry-rs/foundry/issues/3357\n" + ))); + + // Enable viaIR with minimum optimization + // https://github.com/ethereum/solidity/issues/12533#issuecomment-1013073350 + // And also in new releases of solidity: + // https://github.com/ethereum/solidity/issues/13972#issuecomment-1628632202 + project.solc_config.settings = + project.solc_config.settings.with_via_ir_minimum_optimization() + } else { + project.solc_config.settings.optimizer.disable(); + project.solc_config.settings.optimizer.runs = None; + project.solc_config.settings.optimizer.details = None; + project.solc_config.settings.via_ir = None; + } + + project + }; + + let output = ProjectCompiler::default() + .compile(&project)? + .with_stripped_file_prefixes(project.root()); + + Ok((project, output)) + } + + /// Builds the coverage report. + #[tracing::instrument(name = "prepare coverage", skip_all)] + fn prepare(&self, config: &Config, output: ProjectCompileOutput) -> Result { + let project_paths = config.project_paths(); + + // Extract artifacts + let (artifacts, sources) = output.into_artifacts_with_sources(); + let mut report = CoverageReport::default(); + + // Collect ASTs and sources + let mut versioned_asts: HashMap> = HashMap::new(); + let mut versioned_sources: HashMap> = HashMap::new(); + for (path, mut source_file, version) in sources.into_sources_with_version() { + // Filter out dependencies + if project_paths.has_library_ancestor(std::path::Path::new(&path)) { + continue + } + + if let Some(ast) = source_file.ast.take() { + versioned_asts + .entry(version.clone()) + .or_default() + .insert(source_file.id as usize, ast); + + let file = project_paths.root.join(&path); + trace!(root=?project_paths.root, ?file, "reading source file"); + + versioned_sources.entry(version.clone()).or_default().insert( + source_file.id as usize, + fs::read_to_string(&file) + .wrap_err("Could not read source code for analysis")?, + ); + report.add_source(version, source_file.id as usize, path); + } + } + + // Get source maps and bytecodes + let (source_maps, bytecodes): (SourceMaps, HashMap) = artifacts + .into_iter() + .map(|(id, artifact)| (id, CompactContractBytecode::from(artifact))) + .filter_map(|(id, artifact)| { + let contract_id = ContractId { + version: id.version.clone(), + source_id: *report + .get_source_id(id.version, id.source.to_string_lossy().to_string())?, + contract_name: id.name, + }; + let source_maps = ( + contract_id.clone(), + ( + artifact.get_source_map()?.ok()?, + artifact + .get_deployed_bytecode() + .as_ref()? + .bytecode + .as_ref()? + .source_map()? + .ok()?, + ), + ); + let bytecodes = ( + contract_id, + ( + artifact + .get_bytecode() + .and_then(|bytecode| dummy_link_bytecode(bytecode.into_owned()))?, + artifact.get_deployed_bytecode().and_then(|bytecode| { + dummy_link_deployed_bytecode(bytecode.into_owned()) + })?, + ), + ); + + Some((source_maps, bytecodes)) + }) + .unzip(); + + // Build IC -> PC mappings + // + // The source maps are indexed by *instruction counters*, which are the indexes of + // instructions in the bytecode *minus any push bytes*. + // + // Since our coverage inspector collects hit data using program counters, the anchors also + // need to be based on program counters. + // TODO: Index by contract ID + let ic_pc_maps: HashMap = bytecodes + .iter() + .map(|(id, bytecodes)| { + // TODO: Creation bytecode as well + ( + id.clone(), + ( + build_ic_pc_map(SpecId::LATEST, bytecodes.0.as_ref()), + build_ic_pc_map(SpecId::LATEST, bytecodes.1.as_ref()), + ), + ) + }) + .collect(); + + // Add coverage items + for (version, asts) in versioned_asts.into_iter() { + let source_analysis = SourceAnalyzer::new( + version.clone(), + asts, + versioned_sources.remove(&version).ok_or_else(|| { + eyre::eyre!( + "File tree is missing source code, cannot perform coverage analysis" + ) + })?, + )? + .analyze()?; + let anchors: HashMap> = source_analysis + .contract_items + .iter() + .filter_map(|(contract_id, item_ids)| { + // TODO: Creation source map/bytecode as well + Some(( + contract_id.clone(), + find_anchors( + &bytecodes.get(contract_id)?.1, + &source_maps.get(contract_id)?.1, + &ic_pc_maps.get(contract_id)?.1, + item_ids, + &source_analysis.items, + ), + )) + }) + .collect(); + report.add_items(version, source_analysis.items); + report.add_anchors(anchors); + } + + Ok(report) + } + + /// Runs tests, collects coverage data and generates the final report. + async fn collect( + self, + project: Project, + output: ProjectCompileOutput, + mut report: CoverageReport, + config: Config, + evm_opts: EvmOpts, + ) -> Result<()> { + let root = project.paths.root; + + // Build the contract runner + let env = evm_opts.evm_env().await?; + let mut runner = MultiContractRunnerBuilder::default() + .initial_balance(evm_opts.initial_balance) + .evm_spec(SpecId::LATEST) + .sender(evm_opts.sender) + .with_fork(evm_opts.get_fork(&config, env.clone())) + .with_cheats_config(CheatsConfig::new(&config, &evm_opts)) + .with_test_options(TestOptions { fuzz: config.fuzz, ..Default::default() }) + .set_coverage(true) + .build(root.clone(), output, env, evm_opts)?; + + // Run tests + let known_contracts = runner.known_contracts.clone(); + let filter = self.filter; + let (tx, rx) = channel::<(String, SuiteResult)>(); + let handle = + tokio::task::spawn( + async move { runner.test(filter, Some(tx), Default::default()).await }, + ); + + // Add hit data to the coverage report + for (artifact_id, hits) in rx + .into_iter() + .flat_map(|(_, suite)| suite.test_results.into_values()) + .filter_map(|mut result| result.coverage.take()) + .flat_map(|hit_maps| { + hit_maps.0.into_values().filter_map(|map| { + Some((known_contracts.find_by_code(map.bytecode.as_ref())?.0, map)) + }) + }) + { + // TODO: Note down failing tests + if let Some(source_id) = report.get_source_id( + artifact_id.version.clone(), + artifact_id.source.to_string_lossy().to_string(), + ) { + let source_id = *source_id; + // TODO: Distinguish between creation/runtime in a smart way + report.add_hit_map( + &ContractId { + version: artifact_id.version.clone(), + source_id, + contract_name: artifact_id.name.clone(), + }, + &hits, + ); + } + } + + // Reattach the thread + let _ = handle.await; + + // Output final report + for report_kind in self.report { + match report_kind { + CoverageReportKind::Summary => SummaryReporter::default().report(&report), + CoverageReportKind::Lcov => { + if let Some(report_file) = self.report_file { + return LcovReporter::new(&mut fs::create_file(root.join(report_file))?) + .report(&report) + } else { + return LcovReporter::new(&mut fs::create_file(root.join("lcov.info"))?) + .report(&report) + } + } + CoverageReportKind::Debug => DebugReporter.report(&report), + }?; + } + Ok(()) + } + + /// Returns the flattened [`CoreBuildArgs`] + pub fn build_args(&self) -> &CoreBuildArgs { + &self.opts + } +} + +// TODO: HTML +#[derive(Debug, Clone, ValueEnum)] +pub enum CoverageReportKind { + Summary, + Lcov, + Debug, +} + +/// Helper function that will link references in unlinked bytecode to the 0 address. +/// +/// This is needed in order to analyze the bytecode for contracts that use libraries. +fn dummy_link_bytecode(mut obj: CompactBytecode) -> Option { + let link_references = obj.link_references.clone(); + for (file, libraries) in link_references { + for library in libraries.keys() { + obj.link(&file, library, Address::ZERO.to_ethers()); + } + } + + obj.object.resolve(); + obj.object.into_bytes().map(|o| o.0.into()) +} + +/// Helper function that will link references in unlinked bytecode to the 0 address. +/// +/// This is needed in order to analyze the bytecode for contracts that use libraries. +fn dummy_link_deployed_bytecode(obj: CompactDeployedBytecode) -> Option { + obj.bytecode.and_then(dummy_link_bytecode) +} diff --git a/crates/zkforge/bin/cmd/create.rs b/crates/zkforge/bin/cmd/create.rs new file mode 100644 index 000000000..f561bbefb --- /dev/null +++ b/crates/zkforge/bin/cmd/create.rs @@ -0,0 +1,373 @@ +use super::{retry::RetryArgs, verify}; +use clap::{Parser, ValueHint}; +use ethers::{ + abi::{Abi, Constructor, Token}, + prelude::{artifacts::BytecodeObject, ContractFactory, Middleware, MiddlewareBuilder}, + solc::{info::ContractInfo, utils::canonicalized}, + types::{transaction::eip2718::TypedTransaction, Chain}, +}; +use eyre::{Context, Result}; +use foundry_cli::{ + opts::{CoreBuildArgs, EthereumOpts, EtherscanOpts, TransactionOpts}, + utils::{self, read_constructor_args_file, remove_contract, LoadConfig}, +}; +use foundry_common::{abi::parse_tokens, compile, estimate_eip1559_fees}; +use foundry_utils::types::ToAlloy; +use serde_json::json; +use std::{path::PathBuf, sync::Arc}; + +/// CLI arguments for `forge create`. +#[derive(Debug, Clone, Parser)] +pub struct CreateArgs { + /// The contract identifier in the form `:`. + contract: ContractInfo, + + /// The constructor arguments. + #[clap( + long, + num_args(1..), + conflicts_with = "constructor_args_path", + value_name = "ARGS", + )] + constructor_args: Vec, + + /// The path to a file containing the constructor arguments. + #[clap( + long, + value_hint = ValueHint::FilePath, + value_name = "PATH", + )] + constructor_args_path: Option, + + /// Print the deployment information as JSON. + #[clap(long, help_heading = "Display options")] + json: bool, + + /// Verify contract after creation. + #[clap(long)] + verify: bool, + + /// Send via `eth_sendTransaction` using the `--from` argument or `$ETH_FROM` as sender + #[clap(long, requires = "from")] + unlocked: bool, + + #[clap(flatten)] + opts: CoreBuildArgs, + + #[clap(flatten)] + tx: TransactionOpts, + + #[clap(flatten)] + eth: EthereumOpts, + + #[clap(flatten)] + pub verifier: verify::VerifierArgs, + + #[clap(flatten)] + retry: RetryArgs, +} + +impl CreateArgs { + /// Executes the command to create a contract + pub async fn _run(mut self) -> Result<()> { + // Find Project & Compile + let project = self.opts.project()?; + let mut output = if self.json || self.opts.silent { + // Suppress compile stdout messages when printing json output or when silent + compile::suppress_compile(&project) + } else { + compile::compile(&project, false, false) + }?; + + if let Some(ref mut path) = self.contract.path { + // paths are absolute in the project's output + *path = canonicalized(project.root().join(&path)).to_string_lossy().to_string(); + } + + let (abi, bin, _) = remove_contract(&mut output, &self.contract)?; + + let bin = match bin.object { + BytecodeObject::Bytecode(_) => bin.object, + _ => { + let link_refs = bin + .link_references + .iter() + .flat_map(|(path, names)| { + names.keys().map(move |name| format!("\t{name}: {path}")) + }) + .collect::>() + .join("\n"); + eyre::bail!("Dynamic linking not supported in `create` command - deploy the following library contracts first, then provide the address to link at compile time\n{}", link_refs) + } + }; + + // Add arguments to constructor + let config = self.eth.try_load_config_emit_warnings()?; + let provider = utils::get_provider(&config)?; + let params = match abi.constructor { + Some(ref v) => { + let constructor_args = + if let Some(ref constructor_args_path) = self.constructor_args_path { + read_constructor_args_file(constructor_args_path.to_path_buf())? + } else { + self.constructor_args.clone() + }; + self._parse_constructor_args(v, &constructor_args)? + } + None => vec![], + }; + + // respect chain, if set explicitly via cmd args + let chain_id = if let Some(chain_id) = self._chain_id() { + chain_id + } else { + provider.get_chainid().await?.as_u64() + }; + if self.unlocked { + // Deploy with unlocked account + let sender = self.eth.wallet.from.expect("required"); + let provider = provider.with_sender(sender); + self._deploy(abi, bin, params, provider, chain_id).await + } else { + // Deploy with signer + let signer = self.eth.wallet.signer(chain_id).await?; + let provider = provider.with_signer(signer); + self._deploy(abi, bin, params, provider, chain_id).await + } + } + + /// Returns the provided chain id, if any. + fn _chain_id(&self) -> Option { + self.eth.etherscan.chain.map(|chain| chain.id()) + } + + /// Ensures the verify command can be executed. + /// + /// This is supposed to check any things that might go wrong when preparing a verify request + /// before the contract is deployed. This should prevent situations where a contract is deployed + /// successfully, but we fail to prepare a verify request which would require manual + /// verification. + async fn _verify_preflight_check( + &self, + constructor_args: Option, + chain: u64, + ) -> Result<()> { + // NOTE: this does not represent the same `VerifyArgs` that would be sent after deployment, + // since we don't know the address yet. + let verify = verify::VerifyArgs { + address: Default::default(), + contract: self.contract.clone(), + compiler_version: None, + constructor_args, + constructor_args_path: None, + num_of_optimizations: None, + etherscan: EtherscanOpts { + key: self.eth.etherscan.key.clone(), + chain: Some(chain.into()), + }, + flatten: false, + force: false, + watch: true, + retry: self.retry, + libraries: vec![], + root: None, + verifier: self.verifier.clone(), + show_standard_json_input: false, + }; + verify.verification_provider()?.preflight_check(verify).await?; + Ok(()) + } + + /// Deploys the contract + async fn _deploy( + self, + abi: Abi, + bin: BytecodeObject, + args: Vec, + provider: M, + chain: u64, + ) -> Result<()> { + let deployer_address = + provider.default_sender().expect("no sender address set for provider"); + let bin = bin.into_bytes().unwrap_or_else(|| { + panic!("no bytecode found in bin object for {}", self.contract.name) + }); + let provider = Arc::new(provider); + let factory = ContractFactory::new(abi.clone(), bin.clone(), provider.clone()); + + let is_args_empty = args.is_empty(); + let deployer = + factory.deploy_tokens(args.clone()).context("Failed to deploy contract").map_err(|e| { + if is_args_empty { + e.wrap_err("No arguments provided for contract constructor. Consider --constructor-args or --constructor-args-path") + } else { + e + } + })?; + let is_legacy = self.tx.legacy || + Chain::try_from(chain).map(|x| Chain::is_legacy(&x)).unwrap_or_default(); + let mut deployer = if is_legacy { deployer.legacy() } else { deployer }; + + // set tx value if specified + if let Some(value) = self.tx.value { + deployer.tx.set_value(value); + } + + // fill tx first because if you target a lower gas than current base, eth_estimateGas + // will fail and create will fail + provider.fill_transaction(&mut deployer.tx, None).await?; + + // the max + let mut priority_fee = self.tx.priority_gas_price; + + // set gas price if specified + if let Some(gas_price) = self.tx.gas_price { + deployer.tx.set_gas_price(gas_price); + } else if !is_legacy { + // estimate EIP1559 fees + let (max_fee, max_priority_fee) = estimate_eip1559_fees(&provider, Some(chain)) + .await + .wrap_err("Failed to estimate EIP1559 fees. This chain might not support EIP1559, try adding --legacy to your command.")?; + deployer.tx.set_gas_price(max_fee); + if priority_fee.is_none() { + priority_fee = Some(max_priority_fee); + } + } + + // set gas limit if specified + if let Some(gas_limit) = self.tx.gas_limit { + deployer.tx.set_gas(gas_limit); + } + + // set nonce if specified + if let Some(nonce) = self.tx.nonce { + deployer.tx.set_nonce(nonce); + } + + // set priority fee if specified + if let Some(priority_fee) = priority_fee { + if is_legacy { + eyre::bail!("there is no priority fee for legacy txs"); + } + deployer.tx = match deployer.tx { + TypedTransaction::Eip1559(eip1559_tx_request) => TypedTransaction::Eip1559( + eip1559_tx_request.max_priority_fee_per_gas(priority_fee), + ), + _ => deployer.tx, + }; + } + + // Before we actually deploy the contract we try check if the verify settings are valid + let mut constructor_args = None; + if self.verify { + if !args.is_empty() { + // we're passing an empty vec to the `encode_input` of the constructor because we + // only need the constructor arguments and the encoded input is + // `code + args` + let code = Vec::new(); + let encoded_args = abi + .constructor() + .ok_or_else(|| eyre::eyre!("could not find constructor"))? + .encode_input(code, &args)?; + constructor_args = Some(hex::encode(encoded_args)); + } + + self._verify_preflight_check(constructor_args.clone(), chain).await?; + } + + // Deploy the actual contract + let (deployed_contract, receipt) = deployer.send_with_receipt().await?; + + let address = deployed_contract.address().to_alloy(); + if self.json { + let output = json!({ + "deployer": deployer_address.to_alloy().to_string(), + "deployedTo": address.to_string(), + "transactionHash": receipt.transaction_hash + }); + println!("{output}"); + } else { + println!("Deployer: {}", deployer_address.to_alloy()); + println!("Deployed to: {address}"); + println!("Transaction hash: {:?}", receipt.transaction_hash); + }; + + if !self.verify { + return Ok(()) + } + + println!("Starting contract verification..."); + + let num_of_optimizations = + if self.opts.compiler.optimize { self.opts.compiler.optimizer_runs } else { None }; + let verify = verify::VerifyArgs { + address, + contract: self.contract, + compiler_version: None, + constructor_args, + constructor_args_path: None, + num_of_optimizations, + etherscan: EtherscanOpts { key: self.eth.etherscan.key, chain: Some(chain.into()) }, + flatten: false, + force: false, + watch: true, + retry: self.retry, + libraries: vec![], + root: None, + verifier: self.verifier, + show_standard_json_input: false, + }; + println!("Waiting for {} to detect contract deployment...", verify.verifier.verifier); + verify.run().await + } + + fn _parse_constructor_args( + &self, + constructor: &Constructor, + constructor_args: &[String], + ) -> Result> { + let params = constructor + .inputs + .iter() + .zip(constructor_args) + .map(|(input, arg)| (&input.kind, arg.as_str())) + .collect::>(); + + parse_tokens(params, true) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_parse_create() { + let args: CreateArgs = CreateArgs::parse_from([ + "foundry-cli", + "src/Domains.sol:Domains", + "--verify", + "--retries", + "10", + "--delay", + "30", + ]); + assert_eq!(args.retry.retries, 10); + assert_eq!(args.retry.delay, 30); + } + #[test] + fn can_parse_chain_id() { + let args: CreateArgs = CreateArgs::parse_from([ + "foundry-cli", + "src/Domains.sol:Domains", + "--verify", + "--retries", + "10", + "--delay", + "30", + "--chain-id", + "9999", + ]); + assert_eq!(args._chain_id(), Some(9999)); + } +} diff --git a/crates/zkforge/bin/cmd/debug.rs b/crates/zkforge/bin/cmd/debug.rs new file mode 100644 index 000000000..bdcea1c0d --- /dev/null +++ b/crates/zkforge/bin/cmd/debug.rs @@ -0,0 +1,58 @@ +use super::{build::BuildArgs, retry::RETRY_VERIFY_ON_CREATE, script::ScriptArgs}; +use clap::{Parser, ValueHint}; +use foundry_cli::opts::CoreBuildArgs; +use foundry_common::evm::EvmArgs; +use std::path::PathBuf; + +// Loads project's figment and merges the build cli arguments into it +foundry_config::impl_figment_convert!(DebugArgs, opts, evm_opts); + +/// CLI arguments for `forge debug`. +#[derive(Debug, Clone, Parser)] +pub struct DebugArgs { + /// The contract you want to run. Either the file path or contract name. + /// + /// If multiple contracts exist in the same file you must specify the target contract with + /// --target-contract. + #[clap(value_hint = ValueHint::FilePath)] + pub path: PathBuf, + + /// Arguments to pass to the script function. + pub args: Vec, + + /// The name of the contract you want to run. + #[clap(long, visible_alias = "tc", value_name = "CONTRACT_NAME")] + pub target_contract: Option, + + /// The signature of the function you want to call in the contract, or raw calldata. + #[clap(long, short, default_value = "run()", value_name = "SIGNATURE")] + pub sig: String, + + /// Open the script in the debugger. + #[clap(long)] + pub debug: bool, + + #[clap(flatten)] + pub opts: CoreBuildArgs, + + #[clap(flatten)] + pub evm_opts: EvmArgs, +} + +impl DebugArgs { + pub async fn run(self) -> eyre::Result<()> { + let script = ScriptArgs { + path: self.path.to_str().expect("Invalid path string.").to_string(), + args: self.args, + target_contract: self.target_contract, + sig: self.sig, + gas_estimate_multiplier: 130, + opts: BuildArgs { args: self.opts, ..Default::default() }, + evm_opts: self.evm_opts, + debug: true, + retry: RETRY_VERIFY_ON_CREATE, + ..Default::default() + }; + script.run_script().await + } +} diff --git a/crates/zkforge/bin/cmd/doc.rs b/crates/zkforge/bin/cmd/doc.rs new file mode 100644 index 000000000..e30afa21f --- /dev/null +++ b/crates/zkforge/bin/cmd/doc.rs @@ -0,0 +1,107 @@ +use clap::{Parser, ValueHint}; +use eyre::Result; +use forge_doc::{ContractInheritance, Deployments, DocBuilder, GitSource, Inheritdoc, Server}; +use foundry_cli::opts::GH_REPO_PREFIX_REGEX; +use foundry_config::{find_project_root_path, load_config_with_root}; +use std::{path::PathBuf, process::Command}; + +#[derive(Debug, Clone, Parser)] +pub struct DocArgs { + /// The project's root path. + /// + /// By default root of the Git repository, if in one, + /// or the current working directory. + #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + pub root: Option, + + /// The doc's output path. + /// + /// By default, it is the `docs/` in project root. + #[clap( + long, + short, + value_hint = ValueHint::DirPath, + value_name = "PATH", + )] + out: Option, + + /// Build the `mdbook` from generated files. + #[clap(long, short)] + build: bool, + + /// Serve the documentation. + #[clap(long, short)] + serve: bool, + + /// Hostname for serving documentation. + #[clap(long, requires = "serve")] + hostname: Option, + + /// Port for serving documentation. + #[clap(long, short, requires = "serve")] + port: Option, + + /// The relative path to the `hardhat-deploy` or `forge-deploy` artifact directory. Leave blank + /// for default. + #[clap(long)] + deployments: Option>, +} + +impl DocArgs { + pub fn run(self) -> Result<()> { + let root = self.root.clone().unwrap_or(find_project_root_path(None)?); + let config = load_config_with_root(Some(root.clone())); + + let mut doc_config = config.doc.clone(); + if let Some(out) = self.out { + doc_config.out = out; + } + if doc_config.repository.is_none() { + // Attempt to read repo from git + if let Ok(output) = Command::new("git").args(["remote", "get-url", "origin"]).output() { + if !output.stdout.is_empty() { + let remote = String::from_utf8(output.stdout)?.trim().to_owned(); + if let Some(captures) = GH_REPO_PREFIX_REGEX.captures(&remote) { + let brand = captures.name("brand").unwrap().as_str(); + let tld = captures.name("tld").unwrap().as_str(); + let project = GH_REPO_PREFIX_REGEX.replace(&remote, ""); + doc_config.repository = Some(format!( + "https://{brand}.{tld}/{}", + project.trim_end_matches(".git") + )); + } + } + } + } + + let commit = foundry_cli::utils::Git::new(&root).commit_hash(false, "HEAD").ok(); + + let mut builder = DocBuilder::new(root.clone(), config.project_paths().sources) + .with_should_build(self.build) + .with_config(doc_config.clone()) + .with_fmt(config.fmt) + .with_preprocessor(ContractInheritance::default()) + .with_preprocessor(Inheritdoc::default()) + .with_preprocessor(GitSource { + root: root.clone(), + commit, + repository: doc_config.repository.clone(), + }); + + // If deployment docgen is enabled, add the [Deployments] preprocessor + if let Some(deployments) = self.deployments { + builder = builder.with_preprocessor(Deployments { root, deployments }); + } + + builder.build()?; + + if self.serve { + Server::new(doc_config.out) + .with_hostname(self.hostname.unwrap_or("localhost".to_owned())) + .with_port(self.port.unwrap_or(3000)) + .serve()?; + } + + Ok(()) + } +} diff --git a/crates/zkforge/bin/cmd/flatten.rs b/crates/zkforge/bin/cmd/flatten.rs new file mode 100644 index 000000000..2f7508776 --- /dev/null +++ b/crates/zkforge/bin/cmd/flatten.rs @@ -0,0 +1,74 @@ +use clap::{Parser, ValueHint}; +use eyre::Result; +use foundry_cli::{ + opts::{CoreBuildArgs, ProjectPathsArgs}, + utils::LoadConfig, +}; +use foundry_common::fs; +use std::path::PathBuf; + +/// CLI arguments for `forge flatten`. +#[derive(Debug, Clone, Parser)] +pub struct FlattenArgs { + /// The path to the contract to flatten. + #[clap(value_hint = ValueHint::FilePath, value_name = "PATH")] + pub target_path: PathBuf, + + /// The path to output the flattened contract. + /// + /// If not specified, the flattened contract will be output to stdout. + #[clap( + long, + short, + value_hint = ValueHint::FilePath, + value_name = "PATH", + )] + pub output: Option, + + #[clap(flatten)] + project_paths: ProjectPathsArgs, +} + +impl FlattenArgs { + pub fn run(self) -> Result<()> { + let FlattenArgs { target_path, output, project_paths } = self; + + // flatten is a subset of `BuildArgs` so we can reuse that to get the config + let build_args = CoreBuildArgs { + project_paths, + out_path: Default::default(), + compiler: Default::default(), + ignored_error_codes: vec![], + deny_warnings: false, + no_auto_detect: false, + use_solc: None, + offline: false, + force: false, + libraries: vec![], + via_ir: false, + revert_strings: None, + silent: false, + build_info: false, + build_info_path: None, + }; + + let config = build_args.try_load_config_emit_warnings()?; + + let paths = config.project_paths(); + let target_path = dunce::canonicalize(target_path)?; + let flattened = paths + .flatten(&target_path) + .map_err(|err| eyre::Error::msg(format!("Failed to flatten the file: {err}")))?; + + match output { + Some(output) => { + fs::create_dir_all(output.parent().unwrap())?; + fs::write(&output, flattened)?; + println!("Flattened file written at {}", output.display()); + } + None => println!("{flattened}"), + }; + + Ok(()) + } +} diff --git a/crates/zkforge/bin/cmd/fmt.rs b/crates/zkforge/bin/cmd/fmt.rs new file mode 100644 index 000000000..bf8334d70 --- /dev/null +++ b/crates/zkforge/bin/cmd/fmt.rs @@ -0,0 +1,250 @@ +use clap::{Parser, ValueHint}; +use eyre::Result; +use forge_fmt::{format, parse, print_diagnostics_report}; +use foundry_cli::utils::{FoundryPathExt, LoadConfig}; +use foundry_common::{fs, term::cli_warn}; +use foundry_config::impl_figment_convert_basic; +use foundry_utils::glob::expand_globs; +use rayon::prelude::*; +use similar::{ChangeTag, TextDiff}; +use std::{ + fmt::{self, Write}, + io, + io::{Read, Write as _}, + path::{Path, PathBuf}, +}; +use tracing::log::warn; +use yansi::Color; + +/// CLI arguments for `forge fmt`. +#[derive(Debug, Clone, Parser)] +pub struct FmtArgs { + /// Path to the file, directory or '-' to read from stdin. + #[clap(value_hint = ValueHint::FilePath, value_name = "PATH", num_args(1..))] + paths: Vec, + + /// The project's root path. + /// + /// By default root of the Git repository, if in one, + /// or the current working directory. + #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + root: Option, + + /// Run in 'check' mode. + /// + /// Exits with 0 if input is formatted correctly. + /// Exits with 1 if formatting is required. + #[clap(long)] + check: bool, + + /// In 'check' and stdin modes, outputs raw formatted code instead of the diff. + #[clap(long, short)] + raw: bool, +} + +impl_figment_convert_basic!(FmtArgs); + +// === impl FmtArgs === + +impl FmtArgs { + pub fn run(self) -> Result<()> { + let config = self.try_load_config_emit_warnings()?; + + // Expand ignore globs and canonicalize from the get go + let ignored = expand_globs(&config.__root.0, config.fmt.ignore.iter())? + .iter() + .flat_map(foundry_utils::path::canonicalize_path) + .collect::>(); + + let cwd = std::env::current_dir()?; + let input = match &self.paths[..] { + [] => { + // Retrieve the project paths, and filter out the ignored ones. + let project_paths: Vec = config + .project_paths() + .input_files_iter() + .filter(|p| !(ignored.contains(p) || ignored.contains(&cwd.join(p)))) + .collect(); + Input::Paths(project_paths) + } + [one] if one == Path::new("-") => { + let mut s = String::new(); + io::stdin().read_to_string(&mut s).expect("Failed to read from stdin"); + Input::Stdin(s) + } + paths => { + let mut inputs = Vec::with_capacity(paths.len()); + for path in paths { + if !ignored.is_empty() && + ((path.is_absolute() && ignored.contains(path)) || + ignored.contains(&cwd.join(path))) + { + continue + } + + if path.is_dir() { + inputs.extend(ethers::solc::utils::source_files_iter(path)); + } else if path.is_sol() { + inputs.push(path.to_path_buf()); + } else { + warn!("Cannot process path {}", path.display()); + } + } + Input::Paths(inputs) + } + }; + + let format = |source: String, path: Option<&Path>| -> Result<_> { + let name = match path { + Some(path) => { + path.strip_prefix(&config.__root.0).unwrap_or(path).display().to_string() + } + None => "stdin".to_string(), + }; + + let parsed = parse(&source).map_err(|diagnostics| { + let _ = print_diagnostics_report(&source, path, diagnostics); + eyre::eyre!("Failed to parse Solidity code for {name}. Leaving source unchanged.") + })?; + + if !parsed.invalid_inline_config_items.is_empty() { + for (loc, warning) in &parsed.invalid_inline_config_items { + let mut lines = source[..loc.start().min(source.len())].split('\n'); + let col = lines.next_back().unwrap().len() + 1; + let row = lines.count() + 1; + cli_warn!("[{}:{}:{}] {}", name, row, col, warning); + } + } + + let mut output = String::new(); + format(&mut output, parsed, config.fmt.clone()).unwrap(); + + solang_parser::parse(&output, 0).map_err(|diags| { + eyre::eyre!( + "Failed to construct valid Solidity code for {name}. Leaving source unchanged.\n\ + Debug info: {diags:?}" + ) + })?; + + if self.check || path.is_none() { + if self.raw { + print!("{output}"); + } + + let diff = TextDiff::from_lines(&source, &output); + if diff.ratio() < 1.0 { + return Ok(Some(format_diff_summary(&name, &diff))) + } + } else if let Some(path) = path { + fs::write(path, output)?; + } + Ok(None) + }; + + let diffs = match input { + Input::Stdin(source) => format(source, None).map(|diff| vec![diff]), + Input::Paths(paths) => { + if paths.is_empty() { + cli_warn!( + "Nothing to format.\n\ + HINT: If you are working outside of the project, \ + try providing paths to your source files: `forge fmt `" + ); + return Ok(()) + } + paths + .par_iter() + .map(|path| { + let source = fs::read_to_string(path)?; + format(source, Some(path)) + }) + .collect() + } + }?; + + let mut diffs = diffs.iter().flatten(); + if let Some(first) = diffs.next() { + // This branch is only reachable with stdin or --check + + if !self.raw { + let mut stdout = io::stdout().lock(); + let first = std::iter::once(first); + for (i, diff) in first.chain(diffs).enumerate() { + if i > 0 { + let _ = stdout.write_all(b"\n"); + } + let _ = stdout.write_all(diff.as_bytes()); + } + } + + if self.check { + std::process::exit(1); + } + } + + Ok(()) + } +} + +struct Line(Option); + +#[derive(Debug)] +enum Input { + Stdin(String), + Paths(Vec), +} + +impl fmt::Display for Line { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.0 { + None => f.write_str(" "), + Some(idx) => write!(f, "{:<4}", idx + 1), + } + } +} + +fn format_diff_summary<'a, 'b, 'r>(name: &str, diff: &'r TextDiff<'a, 'b, '_, str>) -> String +where + 'r: 'a + 'b, +{ + let cap = 128; + let mut diff_summary = String::with_capacity(cap); + + let _ = writeln!(diff_summary, "Diff in {name}:"); + for (j, group) in diff.grouped_ops(3).into_iter().enumerate() { + if j > 0 { + let s = + "--------------------------------------------------------------------------------"; + diff_summary.push_str(s); + } + for op in group { + for change in diff.iter_inline_changes(&op) { + let dimmed = Color::Default.style().dimmed(); + let (sign, s) = match change.tag() { + ChangeTag::Delete => ("-", Color::Red.style()), + ChangeTag::Insert => ("+", Color::Green.style()), + ChangeTag::Equal => (" ", dimmed), + }; + + let _ = write!( + diff_summary, + "{}{} |{}", + dimmed.paint(Line(change.old_index())), + dimmed.paint(Line(change.new_index())), + s.bold().paint(sign), + ); + + for (emphasized, value) in change.iter_strings_lossy() { + let s = if emphasized { s.underline().bg(Color::Black) } else { s }; + let _ = write!(diff_summary, "{}", s.paint(value)); + } + + if change.missing_newline() { + diff_summary.push('\n'); + } + } + } + } + + diff_summary +} diff --git a/crates/zkforge/bin/cmd/fourbyte.rs b/crates/zkforge/bin/cmd/fourbyte.rs new file mode 100644 index 000000000..86a889d6f --- /dev/null +++ b/crates/zkforge/bin/cmd/fourbyte.rs @@ -0,0 +1,93 @@ +use clap::Parser; +use ethers::prelude::artifacts::output_selection::ContractOutputSelection; +use eyre::Result; +use foundry_cli::{ + opts::{CompilerArgs, CoreBuildArgs, ProjectPathsArgs}, + utils::FoundryPathExt, +}; +use foundry_common::{ + compile, + selectors::{import_selectors, SelectorImportData}, + shell, +}; +use yansi::Paint; + +/// CLI arguments for `forge upload-selectors`. +#[derive(Debug, Clone, Parser)] +pub struct UploadSelectorsArgs { + /// The name of the contract to upload selectors for. + #[clap(required_unless_present = "all")] + pub contract: Option, + + /// Upload selectors for all contracts in the project. + #[clap(long, required_unless_present = "contract")] + pub all: bool, + + #[clap(flatten)] + pub project_paths: ProjectPathsArgs, +} + +impl UploadSelectorsArgs { + /// Builds a contract and uploads the ABI to selector database + pub async fn run(self) -> Result<()> { + shell::println(Paint::yellow("Warning! This command is deprecated and will be removed in v1, use `forge selectors upload` instead"))?; + + let UploadSelectorsArgs { contract, all, project_paths } = self; + + let build_args = CoreBuildArgs { + project_paths: project_paths.clone(), + compiler: CompilerArgs { + extra_output: vec![ContractOutputSelection::Abi], + ..Default::default() + }, + ..Default::default() + }; + + let project = build_args.project()?; + let outcome = compile::suppress_compile(&project)?; + let artifacts = if all { + outcome + .into_artifacts_with_files() + .filter(|(file, _, _)| { + let is_sources_path = + file.starts_with(&project.paths.sources.to_string_lossy().to_string()); + let is_test = file.is_sol_test(); + + is_sources_path && !is_test + }) + .map(|(_, contract, artifact)| (contract, artifact)) + .collect() + } else { + let contract = contract.unwrap(); + let found_artifact = outcome.find_first(&contract); + let artifact = found_artifact + .ok_or_else(|| { + eyre::eyre!("Could not find artifact `{contract}` in the compiled artifacts") + })? + .clone(); + vec![(contract, artifact)] + }; + + let mut artifacts = artifacts.into_iter().peekable(); + while let Some((contract, artifact)) = artifacts.next() { + let abi = artifact.abi.ok_or(eyre::eyre!("Unable to fetch abi"))?; + if abi.abi.functions.is_empty() && + abi.abi.events.is_empty() && + abi.abi.errors.is_empty() + { + continue + } + + println!("Uploading selectors for {contract}..."); + + // upload abi to selector database + import_selectors(SelectorImportData::Abi(vec![abi])).await?.describe(); + + if artifacts.peek().is_some() { + println!() + } + } + + Ok(()) + } +} diff --git a/crates/zkforge/bin/cmd/geiger/error.rs b/crates/zkforge/bin/cmd/geiger/error.rs new file mode 100644 index 000000000..77c6374ea --- /dev/null +++ b/crates/zkforge/bin/cmd/geiger/error.rs @@ -0,0 +1,12 @@ +use foundry_common::errors::FsPathError; +use solang_parser::diagnostics::Diagnostic; +use std::path::PathBuf; + +/// Possible errors when scanning a solidity file +#[derive(Debug, thiserror::Error)] +pub enum ScanFileError { + #[error(transparent)] + Io(#[from] FsPathError), + #[error("Failed to parse {1:?}: {0:?}")] + ParseSol(Vec, PathBuf), +} diff --git a/crates/zkforge/bin/cmd/geiger/find.rs b/crates/zkforge/bin/cmd/geiger/find.rs new file mode 100644 index 000000000..315381b55 --- /dev/null +++ b/crates/zkforge/bin/cmd/geiger/find.rs @@ -0,0 +1,168 @@ +use super::{error::ScanFileError, visitor::CheatcodeVisitor}; +use eyre::Result; +use forge_fmt::{offset_to_line_column, parse, Visitable}; +use foundry_common::fs; +use solang_parser::{diagnostics::Diagnostic, pt::Loc}; +use std::{ + fmt, + path::{Path, PathBuf}, +}; +use yansi::Paint; + +/// Scan a single file for `unsafe` cheatcode usage. +pub fn find_cheatcodes_in_file(path: &Path) -> Result { + let contents = fs::read_to_string(path)?; + let cheatcodes = find_cheatcodes_in_string(&contents) + .map_err(|diagnostic| ScanFileError::ParseSol(diagnostic, path.to_path_buf()))?; + Ok(SolFileMetrics { contents, cheatcodes, file: path.to_path_buf() }) +} + +/// Scan a string for unsafe cheatcodes. +pub fn find_cheatcodes_in_string(src: &str) -> Result> { + let mut parsed = parse(src)?; + let mut visitor = CheatcodeVisitor::default(); + parsed.pt.visit(&mut visitor).unwrap(); + Ok(visitor.cheatcodes) +} + +/// Scan result for a single Solidity file. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct SolFileMetrics { + /// The Solidity file + pub file: PathBuf, + + /// The file's contents. + pub contents: String, + + /// The unsafe cheatcodes found. + pub cheatcodes: UnsafeCheatcodes, +} + +/// Formats the metrics for a single file using [`fmt::Display`]. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct SolFileMetricsPrinter<'a, 'b> { + pub metrics: &'a SolFileMetrics, + pub root: &'b Path, +} + +impl<'a, 'b> fmt::Display for SolFileMetricsPrinter<'a, 'b> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let SolFileMetricsPrinter { metrics, root } = *self; + + let file = metrics.file.strip_prefix(root).unwrap_or(&metrics.file); + + macro_rules! print_unsafe_fn { + ($($name:literal => $field:ident),*) => {$( + let $field = &metrics.cheatcodes.$field[..]; + if !$field.is_empty() { + writeln!(f, " {} {}", Paint::red(metrics.cheatcodes.$field.len()), Paint::red($name))?; + + for &loc in $field { + let content = &metrics.contents[loc.range()]; + let (line, col) = offset_to_line_column(&metrics.contents, loc.start()); + let pos = format!(" --> {}:{}:{}", file.display(), line, col); + writeln!(f,"{}", Paint::red(pos))?; + for line in content.lines() { + writeln!(f, " {}", Paint::red(line))?; + } + } + } + )*}; + } + + if !metrics.cheatcodes.is_empty() { + writeln!( + f, + "{} {}", + Paint::red(metrics.cheatcodes.len()), + Paint::red(file.display()) + )?; + print_unsafe_fn!( + "ffi" => ffi, + "readFile" => read_file, + "readLine" => read_line, + "writeFile" => write_file, + "writeLine" => write_line, + "removeFile" => remove_file, + "closeFile" => close_file, + "setEnv" => set_env, + "deriveKey" => derive_key + ); + } else { + writeln!(f, "0 {}", file.display())? + } + + Ok(()) + } +} + +/// Unsafe usage metrics collection. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct UnsafeCheatcodes { + pub ffi: Vec, + pub read_file: Vec, + pub read_line: Vec, + pub write_file: Vec, + pub write_line: Vec, + pub remove_file: Vec, + pub close_file: Vec, + pub set_env: Vec, + pub derive_key: Vec, +} + +impl UnsafeCheatcodes { + /// Whether there are any unsafe calls. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// The total number of unsafe calls. + pub fn len(&self) -> usize { + self.ffi.len() + + self.read_file.len() + + self.read_line.len() + + self.write_file.len() + + self.write_line.len() + + self.close_file.len() + + self.set_env.len() + + self.derive_key.len() + + self.remove_file.len() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_find_calls() { + let s = r" + contract A is Test { + function do_ffi() public { + string[] memory inputs = new string[](1); + vm.ffi(inputs); + } + } + "; + + let count = find_cheatcodes_in_string(s).unwrap(); + assert_eq!(count.ffi.len(), 1); + assert!(!count.is_empty()); + } + + #[test] + fn can_find_call_in_assignment() { + let s = r" + contract A is Test { + function do_ffi() public { + string[] memory inputs = new string[](1); + bytes stuff = vm.ffi(inputs); + } + } + "; + + let count = find_cheatcodes_in_string(s).unwrap(); + assert_eq!(count.ffi.len(), 1); + assert!(!count.is_empty()); + } +} diff --git a/crates/zkforge/bin/cmd/geiger/mod.rs b/crates/zkforge/bin/cmd/geiger/mod.rs new file mode 100644 index 000000000..6756a5921 --- /dev/null +++ b/crates/zkforge/bin/cmd/geiger/mod.rs @@ -0,0 +1,119 @@ +use clap::{Parser, ValueHint}; +use ethers::solc::Graph; +use eyre::{Result, WrapErr}; +use foundry_cli::utils::LoadConfig; +use foundry_config::{impl_figment_convert_basic, Config}; +use itertools::Itertools; +use rayon::prelude::*; +use std::path::PathBuf; +use yansi::Paint; + +mod error; + +mod find; +use find::{find_cheatcodes_in_file, SolFileMetricsPrinter}; + +mod visitor; + +/// CLI arguments for `forge geiger`. +#[derive(Debug, Clone, Parser)] +pub struct GeigerArgs { + /// Paths to files or directories to detect. + #[clap( + conflicts_with = "root", + value_hint = ValueHint::FilePath, + value_name = "PATH", + num_args(1..), + )] + paths: Vec, + + /// The project's root path. + /// + /// By default root of the Git repository, if in one, + /// or the current working directory. + #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + root: Option, + + /// Run in "check" mode. + /// + /// The exit code of the program will be the number of unsafe cheatcodes found. + #[clap(long)] + pub check: bool, + + /// Globs to ignore. + #[clap( + long, + value_hint = ValueHint::FilePath, + value_name = "PATH", + num_args(1..), + )] + ignore: Vec, + + /// Print a report of all files, even if no unsafe functions are found. + #[clap(long)] + full: bool, +} + +impl_figment_convert_basic!(GeigerArgs); + +impl GeigerArgs { + pub fn sources(&self, config: &Config) -> Result> { + let cwd = std::env::current_dir()?; + + let mut sources: Vec = { + if self.paths.is_empty() { + Graph::resolve(&config.project_paths())?.files().keys().cloned().collect() + } else { + self.paths + .iter() + .flat_map(|path| foundry_common::fs::files_with_ext(path, "sol")) + .unique() + .collect() + } + }; + + sources.retain(|path| { + let abs_path = if path.is_absolute() { path.clone() } else { cwd.join(path) }; + !self.ignore.iter().any(|ignore| { + if ignore.is_absolute() { + abs_path.starts_with(ignore) + } else { + abs_path.starts_with(cwd.join(ignore)) + } + }) + }); + + Ok(sources) + } + + pub fn run(self) -> Result { + let config = self.try_load_config_emit_warnings()?; + let sources = self.sources(&config).wrap_err("Failed to resolve files")?; + + if config.ffi { + eprintln!("{}\n", Paint::red("ffi enabled")); + } + + let root = config.__root.0; + + let sum = sources + .par_iter() + .map(|file| match find_cheatcodes_in_file(file) { + Ok(metrics) => { + let len = metrics.cheatcodes.len(); + let printer = SolFileMetricsPrinter { metrics: &metrics, root: &root }; + if self.full || len == 0 { + eprint!("{printer}"); + } + len + } + Err(err) => { + eprintln!("{err}"); + 0 + } + }) + .sum(); + + Ok(sum) + } +} diff --git a/crates/zkforge/bin/cmd/geiger/visitor.rs b/crates/zkforge/bin/cmd/geiger/visitor.rs new file mode 100644 index 000000000..703130890 --- /dev/null +++ b/crates/zkforge/bin/cmd/geiger/visitor.rs @@ -0,0 +1,333 @@ +use super::find::UnsafeCheatcodes; +use eyre::Result; +use forge_fmt::{Visitable, Visitor}; +use solang_parser::pt::{ + ContractDefinition, Expression, FunctionDefinition, IdentifierPath, Loc, Parameter, SourceUnit, + Statement, TypeDefinition, VariableDeclaration, VariableDefinition, +}; +use std::convert::Infallible; + +/// a [`forge_fmt::Visitor` that scans for invocations of cheatcodes +#[derive(Default)] +pub struct CheatcodeVisitor { + pub cheatcodes: UnsafeCheatcodes, +} + +impl Visitor for CheatcodeVisitor { + type Error = Infallible; + + fn visit_source_unit(&mut self, source_unit: &mut SourceUnit) -> Result<(), Self::Error> { + source_unit.0.visit(self) + } + + fn visit_contract(&mut self, contract: &mut ContractDefinition) -> Result<(), Self::Error> { + contract.base.visit(self)?; + contract.parts.visit(self) + } + + fn visit_block( + &mut self, + _loc: Loc, + _unchecked: bool, + statements: &mut Vec, + ) -> Result<(), Self::Error> { + statements.visit(self) + } + + fn visit_expr(&mut self, _loc: Loc, expr: &mut Expression) -> Result<(), Self::Error> { + match expr { + Expression::PostIncrement(_, expr) => { + expr.visit(self)?; + } + Expression::PostDecrement(_, expr) => { + expr.visit(self)?; + } + Expression::New(_, expr) => { + expr.visit(self)?; + } + Expression::ArraySubscript(_, expr1, expr2) => { + expr1.visit(self)?; + expr2.visit(self)?; + } + Expression::ArraySlice(_, expr1, expr2, expr3) => { + expr1.visit(self)?; + expr2.visit(self)?; + expr3.visit(self)?; + } + Expression::Parenthesis(_, expr) => { + expr.visit(self)?; + } + Expression::MemberAccess(_, expr, _) => { + expr.visit(self)?; + } + Expression::FunctionCall(loc, lhs, rhs) => { + // all cheatcodes are accessd via .cheatcode + if let Expression::MemberAccess(_, expr, identifier) = &**lhs { + if let Expression::Variable(_) = &**expr { + match identifier.name.as_str() { + "ffi" => self.cheatcodes.ffi.push(*loc), + "readFile" => self.cheatcodes.read_file.push(*loc), + "writeFile" => self.cheatcodes.write_file.push(*loc), + "readLine" => self.cheatcodes.read_line.push(*loc), + "writeLine" => self.cheatcodes.write_line.push(*loc), + "closeFile" => self.cheatcodes.close_file.push(*loc), + "removeFile" => self.cheatcodes.remove_file.push(*loc), + "setEnv" => self.cheatcodes.set_env.push(*loc), + "deriveKey" => self.cheatcodes.derive_key.push(*loc), + _ => {} + } + } + } + rhs.visit(self)?; + } + Expression::FunctionCallBlock(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::NamedFunctionCall(_, lhs, rhs) => { + lhs.visit(self)?; + for arg in rhs.iter_mut() { + arg.expr.visit(self)?; + } + } + Expression::Not(_, expr) => { + expr.visit(self)?; + } + Expression::BitwiseNot(_, expr) => { + expr.visit(self)?; + } + Expression::Delete(_, expr) => { + expr.visit(self)?; + } + Expression::PreIncrement(_, expr) => { + expr.visit(self)?; + } + Expression::PreDecrement(_, expr) => { + expr.visit(self)?; + } + Expression::UnaryPlus(_, expr) => { + expr.visit(self)?; + } + Expression::Negate(_, expr) => { + expr.visit(self)?; + } + Expression::Power(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::Multiply(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::Divide(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::Modulo(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::Add(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::Subtract(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::ShiftLeft(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::ShiftRight(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::BitwiseAnd(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::BitwiseXor(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::BitwiseOr(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::Less(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::More(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::LessEqual(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::MoreEqual(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::Equal(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::NotEqual(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::And(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::Or(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::ConditionalOperator(_, llhs, lhs, rhs) => { + llhs.visit(self)?; + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::Assign(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::AssignOr(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::AssignAnd(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::AssignXor(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::AssignShiftLeft(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::AssignShiftRight(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::AssignAdd(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::AssignSubtract(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::AssignMultiply(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::AssignDivide(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::AssignModulo(_, lhs, rhs) => { + lhs.visit(self)?; + rhs.visit(self)?; + } + Expression::List(_, param) => { + for (_, param) in param.iter_mut() { + param.visit(self)?; + } + } + _ => {} + } + + Ok(()) + } + + fn visit_emit(&mut self, _: Loc, expr: &mut Expression) -> Result<(), Self::Error> { + expr.visit(self) + } + + fn visit_var_definition(&mut self, var: &mut VariableDefinition) -> Result<(), Self::Error> { + var.ty.visit(self)?; + var.initializer.visit(self) + } + + fn visit_var_definition_stmt( + &mut self, + _: Loc, + declaration: &mut VariableDeclaration, + expr: &mut Option, + ) -> Result<(), Self::Error> { + declaration.visit(self)?; + expr.visit(self) + } + + fn visit_var_declaration(&mut self, var: &mut VariableDeclaration) -> Result<(), Self::Error> { + var.ty.visit(self) + } + + fn visit_revert( + &mut self, + _: Loc, + _error: &mut Option, + args: &mut Vec, + ) -> Result<(), Self::Error> { + args.visit(self) + } + + fn visit_if( + &mut self, + _loc: Loc, + cond: &mut Expression, + if_branch: &mut Box, + else_branch: &mut Option>, + _is_frst_stmt: bool, + ) -> Result<(), Self::Error> { + cond.visit(self)?; + if_branch.visit(self)?; + else_branch.visit(self) + } + + fn visit_while( + &mut self, + _loc: Loc, + cond: &mut Expression, + body: &mut Statement, + ) -> Result<(), Self::Error> { + cond.visit(self)?; + body.visit(self) + } + + fn visit_for( + &mut self, + _loc: Loc, + init: &mut Option>, + cond: &mut Option>, + update: &mut Option>, + body: &mut Option>, + ) -> Result<(), Self::Error> { + init.visit(self)?; + cond.visit(self)?; + update.visit(self)?; + body.visit(self) + } + + fn visit_function(&mut self, func: &mut FunctionDefinition) -> Result<(), Self::Error> { + if let Some(ref mut body) = func.body { + body.visit(self)?; + } + Ok(()) + } + + fn visit_parameter(&mut self, parameter: &mut Parameter) -> Result<(), Self::Error> { + parameter.ty.visit(self) + } + + fn visit_type_definition(&mut self, def: &mut TypeDefinition) -> Result<(), Self::Error> { + def.ty.visit(self) + } +} diff --git a/crates/zkforge/bin/cmd/generate/mod.rs b/crates/zkforge/bin/cmd/generate/mod.rs new file mode 100644 index 000000000..9e25d6532 --- /dev/null +++ b/crates/zkforge/bin/cmd/generate/mod.rs @@ -0,0 +1,70 @@ +use clap::{Parser, Subcommand}; +use eyre::Result; +use foundry_common::fs; +use std::path::Path; +use yansi::Paint; + +/// CLI arguments for `forge generate`. +#[derive(Debug, Parser)] +pub struct GenerateArgs { + #[clap(subcommand)] + pub sub: GenerateSubcommands, +} + +#[derive(Debug, Subcommand)] +pub enum GenerateSubcommands { + /// Scaffolds test file for given contract. + Test(GenerateTestArgs), +} + +#[derive(Debug, Parser)] +pub struct GenerateTestArgs { + /// Contract name for test generation. + #[clap(long, short, value_name = "CONTRACT_NAME")] + pub contract_name: String, +} + +impl GenerateTestArgs { + pub fn run(self) -> Result<()> { + let contract_name = format_identifier(&self.contract_name, true); + let instance_name = format_identifier(&self.contract_name, false); + + // Create the test file content. + let test_content = include_str!("../../../assets/generated/TestTemplate.t.sol"); + let test_content = test_content + .replace("{contract_name}", &contract_name) + .replace("{instance_name}", &instance_name); + + // Create the test directory if it doesn't exist. + fs::create_dir_all("test")?; + + // Define the test file path + let test_file_path = Path::new("test").join(format!("{}.t.sol", contract_name)); + + // Write the test content to the test file. + fs::write(&test_file_path, test_content)?; + + println!("{} test file: {}", Paint::green("Generated"), test_file_path.to_str().unwrap()); + Ok(()) + } +} + +/// Utility function to convert an identifier to pascal or camel case. +fn format_identifier(input: &str, is_pascal_case: bool) -> String { + let mut result = String::new(); + let mut capitalize_next = is_pascal_case; + + for word in input.split_whitespace() { + if !word.is_empty() { + let (first, rest) = word.split_at(1); + let formatted_word = if capitalize_next { + format!("{}{}", first.to_uppercase(), rest) + } else { + format!("{}{}", first.to_lowercase(), rest) + }; + capitalize_next = true; + result.push_str(&formatted_word); + } + } + result +} diff --git a/crates/zkforge/bin/cmd/init.rs b/crates/zkforge/bin/cmd/init.rs new file mode 100644 index 000000000..4d8c6c715 --- /dev/null +++ b/crates/zkforge/bin/cmd/init.rs @@ -0,0 +1,246 @@ +use super::install::DependencyInstallOpts; +use clap::{Parser, ValueHint}; +use ethers::solc::remappings::Remapping; +use eyre::Result; +use foundry_cli::{p_println, utils::Git}; +use foundry_common::fs; +use foundry_config::Config; +use std::path::{Path, PathBuf}; +use yansi::Paint; + +/// CLI arguments for `forge init`. +#[derive(Debug, Clone, Parser)] +pub struct InitArgs { + /// The root directory of the new project. + #[clap(value_hint = ValueHint::DirPath, default_value = ".", value_name = "PATH")] + root: PathBuf, + + /// The template to start from. + #[clap(long, short)] + template: Option, + + /// Branch argument that can only be used with template option. + /// If not specified, the default branch is used. + #[clap(long, short, requires = "template")] + branch: Option, + + /// Do not install dependencies from the network. + #[clap(long, conflicts_with = "template", visible_alias = "no-deps")] + offline: bool, + + /// Create the project even if the specified root directory is not empty. + #[clap(long, conflicts_with = "template")] + force: bool, + + /// Create a .vscode/settings.json file with Solidity settings, and generate a remappings.txt + /// file. + #[clap(long, conflicts_with = "template")] + vscode: bool, + + #[clap(flatten)] + opts: DependencyInstallOpts, +} + +impl InitArgs { + pub fn run(self) -> Result<()> { + let InitArgs { root, template, branch, opts, offline, force, vscode } = self; + let DependencyInstallOpts { shallow, no_git, no_commit, quiet } = opts; + + // create the root dir if it does not exist + if !root.exists() { + fs::create_dir_all(&root)?; + } + let root = dunce::canonicalize(root)?; + let git = Git::new(&root).quiet(quiet).shallow(shallow); + + // if a template is provided, then this command initializes a git repo, + // fetches the template repo, and resets the git history to the head of the fetched + // repo with no other history + if let Some(template) = template { + let template = if template.contains("://") { + template + } else { + "https://github.com/".to_string() + &template + }; + p_println!(!quiet => "Initializing {} from {}...", root.display(), template); + // initialize the git repository + git.init()?; + + // fetch the template - always fetch shallow for templates since git history will be + // collapsed. gitmodules will be initialized after the template is fetched + Git::fetch(true, &template, branch)?; + // reset git history to the head of the template + // first get the commit hash that was fetched + let commit_hash = git.commit_hash(true, "FETCH_HEAD")?; + // format a commit message for the new repo + let commit_msg = format!("chore: init from {template} at {commit_hash}"); + // get the hash of the FETCH_HEAD with the new commit message + let new_commit_hash = git.commit_tree("FETCH_HEAD^{tree}", Some(commit_msg))?; + // reset head of this repo to be the head of the template repo + git.reset(true, new_commit_hash)?; + + // if shallow, just initialize submodules + if shallow { + git.submodule_init()?; + } else { + // if not shallow, initialize and clone submodules (without fetching latest) + git.submodule_update(false, false, true, None::)?; + } + } else { + // if target is not empty + if root.read_dir().map_or(false, |mut i| i.next().is_some()) { + if !force { + eyre::bail!( + "Cannot run `init` on a non-empty directory.\n\ + Run with the `--force` flag to initialize regardless." + ); + } + + p_println!(!quiet => "Target directory is not empty, but `--force` was specified"); + } + + // ensure git status is clean before generating anything + if !no_git && !no_commit && !force && git.is_in_repo()? { + git.ensure_clean()?; + } + + p_println!(!quiet => "Initializing {}...", root.display()); + + // make the dirs + let src = root.join("src"); + fs::create_dir_all(&src)?; + + let test = root.join("test"); + fs::create_dir_all(&test)?; + + let script = root.join("script"); + fs::create_dir_all(&script)?; + + // write the contract file + let contract_path = src.join("Counter.sol"); + fs::write(contract_path, include_str!("../../assets/CounterTemplate.sol"))?; + // write the tests + let contract_path = test.join("Counter.t.sol"); + fs::write(contract_path, include_str!("../../assets/CounterTemplate.t.sol"))?; + // write the script + let contract_path = script.join("Counter.s.sol"); + fs::write(contract_path, include_str!("../../assets/CounterTemplate.s.sol"))?; + // Write the default README file + let readme_path = root.join("README.md"); + fs::write(readme_path, include_str!("../../assets/README.md"))?; + + // write foundry.toml, if it doesn't exist already + let dest = root.join(Config::FILE_NAME); + let mut config = Config::load_with_root(&root); + if !dest.exists() { + fs::write(dest, config.clone().into_basic().to_string_pretty()?)?; + } + let git = self.opts.git(&config); + + // set up the repo + if !no_git { + init_git_repo(git, no_commit)?; + } + + // install forge-std + if !offline { + if root.join("lib/forge-std").exists() { + p_println!(!quiet => "\"lib/forge-std\" already exists, skipping install...."); + self.opts.install(&mut config, vec![])?; + } else { + let dep = "https://github.com/foundry-rs/forge-std".parse()?; + self.opts.install(&mut config, vec![dep])?; + } + } + + // init vscode settings + if vscode { + init_vscode(&root)?; + } + } + + p_println!(!quiet => " {} forge project", Paint::green("Initialized")); + Ok(()) + } +} + +/// Returns the commit hash of the project if it exists +pub fn get_commit_hash(root: &Path) -> Option { + Git::new(root).commit_hash(true, "HEAD").ok() +} + +/// Initialises `root` as a git repository, if it isn't one already. +/// +/// Creates `.gitignore` and `.github/workflows/test.yml`, if they don't exist already. +/// +/// Commits everything in `root` if `no_commit` is false. +fn init_git_repo(git: Git<'_>, no_commit: bool) -> Result<()> { + // git init + if !git.is_in_repo()? { + git.init()?; + } + + // .gitignore + let gitignore = git.root.join(".gitignore"); + if !gitignore.exists() { + fs::write(gitignore, include_str!("../../assets/.gitignoreTemplate"))?; + } + + // github workflow + let workflow = git.root.join(".github/workflows/test.yml"); + if !workflow.exists() { + fs::create_dir_all(workflow.parent().unwrap())?; + fs::write(workflow, include_str!("../../assets/workflowTemplate.yml"))?; + } + + // commit everything + if !no_commit { + git.add(Some("--all"))?; + git.commit("chore: forge init")?; + } + + Ok(()) +} + +/// initializes the `.vscode/settings.json` file +fn init_vscode(root: &Path) -> Result<()> { + let remappings_file = root.join("remappings.txt"); + if !remappings_file.exists() { + let mut remappings = Remapping::find_many(root.join("lib")) + .into_iter() + .map(|r| r.into_relative(root).to_relative_remapping().to_string()) + .collect::>(); + if !remappings.is_empty() { + remappings.sort(); + let content = remappings.join("\n"); + fs::write(remappings_file, content)?; + } + } + + let vscode_dir = root.join(".vscode"); + let settings_file = vscode_dir.join("settings.json"); + let mut settings = if !vscode_dir.is_dir() { + fs::create_dir_all(&vscode_dir)?; + serde_json::json!({}) + } else if settings_file.exists() { + ethers::solc::utils::read_json_file(&settings_file)? + } else { + serde_json::json!({}) + }; + + let obj = settings.as_object_mut().expect("Expected settings object"); + // insert [vscode-solidity settings](https://github.com/juanfranblanco/vscode-solidity) + let src_key = "solidity.packageDefaultDependenciesContractsDirectory"; + if !obj.contains_key(src_key) { + obj.insert(src_key.to_string(), serde_json::Value::String("src".to_string())); + } + let lib_key = "solidity.packageDefaultDependenciesDirectory"; + if !obj.contains_key(lib_key) { + obj.insert(lib_key.to_string(), serde_json::Value::String("lib".to_string())); + } + + let content = serde_json::to_string_pretty(&settings)?; + fs::write(settings_file, content)?; + + Ok(()) +} diff --git a/crates/zkforge/bin/cmd/inspect.rs b/crates/zkforge/bin/cmd/inspect.rs new file mode 100644 index 000000000..135906757 --- /dev/null +++ b/crates/zkforge/bin/cmd/inspect.rs @@ -0,0 +1,441 @@ +use clap::Parser; +use comfy_table::{presets::ASCII_MARKDOWN, Table}; +use ethers::{ + abi::RawAbi, + prelude::{ + artifacts::output_selection::{ + BytecodeOutputSelection, ContractOutputSelection, DeployedBytecodeOutputSelection, + EvmOutputSelection, EwasmOutputSelection, + }, + info::ContractInfo, + }, + solc::{ + artifacts::{LosslessAbi, StorageLayout}, + utils::canonicalize, + }, +}; +use eyre::Result; +use foundry_cli::opts::{CompilerArgs, CoreBuildArgs}; +use foundry_common::compile; +use serde_json::{to_value, Value}; +use std::fmt; +use tracing::trace; + +/// CLI arguments for `forge inspect`. +#[derive(Debug, Clone, Parser)] +pub struct InspectArgs { + /// The identifier of the contract to inspect in the form `(:)?`. + pub contract: ContractInfo, + + /// The contract artifact field to inspect. + #[clap(value_enum)] + pub field: ContractArtifactField, + + /// Pretty print the selected field, if supported. + #[clap(long)] + pub pretty: bool, + + /// All build arguments are supported + #[clap(flatten)] + build: CoreBuildArgs, +} + +impl InspectArgs { + pub fn run(self) -> Result<()> { + let InspectArgs { mut contract, field, build, pretty } = self; + + trace!(target: "forge", ?field, ?contract, "running forge inspect"); + + // Map field to ContractOutputSelection + let mut cos = build.compiler.extra_output; + if !field.is_default() && !cos.iter().any(|selected| field.eq(selected)) { + cos.push(field.into()); + } + + // Run Optimized? + let optimized = if let ContractArtifactField::AssemblyOptimized = field { + true + } else { + build.compiler.optimize + }; + + // Build modified Args + let modified_build_args = CoreBuildArgs { + compiler: CompilerArgs { extra_output: cos, optimize: optimized, ..build.compiler }, + ..build + }; + + // Build the project + let project = modified_build_args.project()?; + let outcome = if let Some(ref mut contract_path) = contract.path { + let target_path = canonicalize(&*contract_path)?; + *contract_path = target_path.to_string_lossy().to_string(); + compile::compile_files(&project, vec![target_path], true) + } else { + compile::suppress_compile(&project) + }?; + + // Find the artifact + let found_artifact = outcome.find_contract(&contract); + + trace!(target: "forge", artifact=?found_artifact, input=?contract, "Found contract"); + + // Unwrap the inner artifact + let artifact = found_artifact.ok_or_else(|| { + eyre::eyre!("Could not find artifact `{contract}` in the compiled artifacts") + })?; + + // Match on ContractArtifactFields and Pretty Print + match field { + ContractArtifactField::Abi => { + let abi = artifact + .abi + .as_ref() + .ok_or_else(|| eyre::eyre!("Failed to fetch lossless ABI"))?; + print_abi(abi, pretty)?; + } + ContractArtifactField::Bytecode => { + let tval: Value = to_value(&artifact.bytecode)?; + println!( + "{}", + tval.get("object").unwrap_or(&tval).as_str().ok_or_else(|| eyre::eyre!( + "Failed to extract artifact bytecode as a string" + ))? + ); + } + ContractArtifactField::DeployedBytecode => { + let tval: Value = to_value(&artifact.deployed_bytecode)?; + println!( + "{}", + tval.get("object").unwrap_or(&tval).as_str().ok_or_else(|| eyre::eyre!( + "Failed to extract artifact deployed bytecode as a string" + ))? + ); + } + ContractArtifactField::Assembly | ContractArtifactField::AssemblyOptimized => { + println!( + "{}", + to_value(&artifact.assembly)?.as_str().ok_or_else(|| eyre::eyre!( + "Failed to extract artifact assembly as a string" + ))? + ); + } + ContractArtifactField::MethodIdentifiers => { + println!( + "{}", + serde_json::to_string_pretty(&to_value(&artifact.method_identifiers)?)? + ); + } + ContractArtifactField::GasEstimates => { + println!("{}", serde_json::to_string_pretty(&to_value(&artifact.gas_estimates)?)?); + } + ContractArtifactField::StorageLayout => { + print_storage_layout(&artifact.storage_layout, pretty)?; + } + ContractArtifactField::DevDoc => { + println!("{}", serde_json::to_string_pretty(&to_value(&artifact.devdoc)?)?); + } + ContractArtifactField::Ir => { + println!( + "{}", + to_value(&artifact.ir)? + .as_str() + .ok_or_else(|| eyre::eyre!("Failed to extract artifact ir as a string"))? + ); + } + ContractArtifactField::IrOptimized => { + println!( + "{}", + to_value(&artifact.ir_optimized)?.as_str().ok_or_else(|| eyre::eyre!( + "Failed to extract artifact optimized ir as a string" + ))? + ); + } + ContractArtifactField::Metadata => { + println!("{}", serde_json::to_string_pretty(&to_value(&artifact.metadata)?)?); + } + ContractArtifactField::UserDoc => { + println!("{}", serde_json::to_string_pretty(&to_value(&artifact.userdoc)?)?); + } + ContractArtifactField::Ewasm => { + println!( + "{}", + to_value(&artifact.ewasm)?.as_str().ok_or_else(|| eyre::eyre!( + "Failed to extract artifact ewasm as a string" + ))? + ); + } + ContractArtifactField::Errors => { + let mut out = serde_json::Map::new(); + if let Some(LosslessAbi { abi, .. }) = &artifact.abi { + // Print the signature of all errors + for er in abi.errors.iter().flat_map(|(_, errors)| errors) { + let types = + er.inputs.iter().map(|p| p.kind.to_string()).collect::>(); + let sig = format!("{:x}", er.signature()); + let sig_trimmed = &sig[0..8]; + out.insert( + format!("{}({})", er.name, types.join(",")), + sig_trimmed.to_string().into(), + ); + } + } + println!("{}", serde_json::to_string_pretty(&out)?); + } + ContractArtifactField::Events => { + let mut out = serde_json::Map::new(); + if let Some(LosslessAbi { abi, .. }) = &artifact.abi { + // print the signature of all events including anonymous + for ev in abi.events.iter().flat_map(|(_, events)| events) { + let types = + ev.inputs.iter().map(|p| p.kind.to_string()).collect::>(); + out.insert( + format!("{}({})", ev.name, types.join(",")), + format!("{:?}", ev.signature()).into(), + ); + } + } + println!("{}", serde_json::to_string_pretty(&out)?); + } + }; + + Ok(()) + } +} + +pub fn print_abi(abi: &LosslessAbi, pretty: bool) -> Result<()> { + let abi_json = to_value(abi)?; + if !pretty { + println!("{}", serde_json::to_string_pretty(&abi_json)?); + return Ok(()) + } + + let abi_json: RawAbi = serde_json::from_value(abi_json)?; + let source = foundry_utils::abi::abi_to_solidity(&abi_json, "")?; + println!("{}", source); + + Ok(()) +} + +pub fn print_storage_layout(storage_layout: &Option, pretty: bool) -> Result<()> { + if storage_layout.is_none() { + eyre::bail!("Could not get storage layout") + } + + let storage_layout = storage_layout.as_ref().unwrap(); + + if !pretty { + println!("{}", serde_json::to_string_pretty(&to_value(storage_layout)?)?); + return Ok(()) + } + + let mut table = Table::new(); + table.load_preset(ASCII_MARKDOWN); + table.set_header(vec!["Name", "Type", "Slot", "Offset", "Bytes", "Contract"]); + + for slot in &storage_layout.storage { + let storage_type = storage_layout.types.get(&slot.storage_type); + table.add_row(vec![ + slot.label.clone(), + storage_type.as_ref().map_or("?".to_string(), |t| t.label.clone()), + slot.slot.clone(), + slot.offset.to_string(), + storage_type.as_ref().map_or("?".to_string(), |t| t.number_of_bytes.clone()), + slot.contract.clone(), + ]); + } + + println!("{table}"); + + Ok(()) +} + +/// Contract level output selection +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum ContractArtifactField { + Abi, + Bytecode, + DeployedBytecode, + Assembly, + AssemblyOptimized, + MethodIdentifiers, + GasEstimates, + StorageLayout, + DevDoc, + Ir, + IrOptimized, + Metadata, + UserDoc, + Ewasm, + Errors, + Events, +} + +macro_rules! impl_value_enum { + (enum $name:ident { $($field:ident => $main:literal $(| $alias:literal)*),+ $(,)? }) => { + impl $name { + /// All the variants of this enum. + pub const ALL: &'static [Self] = &[$(Self::$field),+]; + + /// Returns the string representation of `self`. + #[inline] + pub const fn as_str(&self) -> &'static str { + match self { + $( + Self::$field => $main, + )+ + } + } + + /// Returns all the aliases of `self`. + #[inline] + pub const fn aliases(&self) -> &'static [&'static str] { + match self { + $( + Self::$field => &[$($alias),*], + )+ + } + } + } + + impl ::clap::ValueEnum for $name { + #[inline] + fn value_variants<'a>() -> &'a [Self] { + Self::ALL + } + + #[inline] + fn to_possible_value(&self) -> Option<::clap::builder::PossibleValue> { + Some(::clap::builder::PossibleValue::new(Self::as_str(self)).aliases(Self::aliases(self))) + } + + #[inline] + fn from_str(input: &str, ignore_case: bool) -> Result { + let _ = ignore_case; + ::from_str(input) + } + } + + impl ::std::str::FromStr for $name { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + $( + $main $(| $alias)* => Ok(Self::$field), + )+ + _ => Err(format!(concat!("Invalid ", stringify!($name), " value: {}"), s)), + } + } + } + }; +} + +impl_value_enum! { + enum ContractArtifactField { + Abi => "abi", + Bytecode => "bytecode" | "bytes" | "b", + DeployedBytecode => "deployedBytecode" | "deployed_bytecode" | "deployed-bytecode" + | "deployed" | "deployedbytecode", + Assembly => "assembly" | "asm", + AssemblyOptimized => "assemblyOptimized" | "asmOptimized" | "assemblyoptimized" + | "assembly_optimized" | "asmopt" | "assembly-optimized" + | "asmo" | "asm-optimized" | "asmoptimized" | "asm_optimized", + MethodIdentifiers => "methodIdentifiers" | "methodidentifiers" | "methods" + | "method_identifiers" | "method-identifiers" | "mi", + GasEstimates => "gasEstimates" | "gas" | "gas_estimates" | "gas-estimates" + | "gasestimates", + StorageLayout => "storageLayout" | "storage_layout" | "storage-layout" + | "storagelayout" | "storage", + DevDoc => "devdoc" | "dev-doc" | "devDoc", + Ir => "ir" | "iR" | "IR", + IrOptimized => "irOptimized" | "ir-optimized" | "iroptimized" | "iro" | "iropt", + Metadata => "metadata" | "meta", + UserDoc => "userdoc" | "userDoc" | "user-doc", + Ewasm => "ewasm" | "e-wasm", + Errors => "errors" | "er", + Events => "events" | "ev", + } +} + +impl From for ContractOutputSelection { + fn from(field: ContractArtifactField) -> Self { + type Caf = ContractArtifactField; + match field { + Caf::Abi => Self::Abi, + Caf::Bytecode => Self::Evm(EvmOutputSelection::ByteCode(BytecodeOutputSelection::All)), + Caf::DeployedBytecode => Self::Evm(EvmOutputSelection::DeployedByteCode( + DeployedBytecodeOutputSelection::All, + )), + Caf::Assembly | Caf::AssemblyOptimized => Self::Evm(EvmOutputSelection::Assembly), + Caf::MethodIdentifiers => Self::Evm(EvmOutputSelection::MethodIdentifiers), + Caf::GasEstimates => Self::Evm(EvmOutputSelection::GasEstimates), + Caf::StorageLayout => Self::StorageLayout, + Caf::DevDoc => Self::DevDoc, + Caf::Ir => Self::Ir, + Caf::IrOptimized => Self::IrOptimized, + Caf::Metadata => Self::Metadata, + Caf::UserDoc => Self::UserDoc, + Caf::Ewasm => Self::Ewasm(EwasmOutputSelection::All), + Caf::Errors => Self::Abi, + Caf::Events => Self::Abi, + } + } +} + +impl PartialEq for ContractArtifactField { + fn eq(&self, other: &ContractOutputSelection) -> bool { + type Cos = ContractOutputSelection; + type Eos = EvmOutputSelection; + matches!( + (self, other), + (Self::Abi | Self::Events, Cos::Abi) | + (Self::Errors, Cos::Abi) | + (Self::Bytecode, Cos::Evm(Eos::ByteCode(_))) | + (Self::DeployedBytecode, Cos::Evm(Eos::DeployedByteCode(_))) | + (Self::Assembly | Self::AssemblyOptimized, Cos::Evm(Eos::Assembly)) | + (Self::MethodIdentifiers, Cos::Evm(Eos::MethodIdentifiers)) | + (Self::GasEstimates, Cos::Evm(Eos::GasEstimates)) | + (Self::StorageLayout, Cos::StorageLayout) | + (Self::DevDoc, Cos::DevDoc) | + (Self::Ir, Cos::Ir) | + (Self::IrOptimized, Cos::IrOptimized) | + (Self::Metadata, Cos::Metadata) | + (Self::UserDoc, Cos::UserDoc) | + (Self::Ewasm, Cos::Ewasm(_)) + ) + } +} + +impl fmt::Display for ContractArtifactField { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +impl ContractArtifactField { + /// Returns true if this field is generated by default. + pub const fn is_default(&self) -> bool { + matches!(self, Self::Bytecode | Self::DeployedBytecode) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn contract_output_selection() { + for &field in ContractArtifactField::ALL { + let selection: ContractOutputSelection = field.into(); + assert_eq!(field, selection); + + let s = field.as_str(); + assert_eq!(s, field.to_string()); + assert_eq!(s.parse::().unwrap(), field); + for alias in field.aliases() { + assert_eq!(alias.parse::().unwrap(), field); + } + } + } +} diff --git a/crates/zkforge/bin/cmd/install.rs b/crates/zkforge/bin/cmd/install.rs new file mode 100644 index 000000000..d5568b823 --- /dev/null +++ b/crates/zkforge/bin/cmd/install.rs @@ -0,0 +1,513 @@ +use clap::{Parser, ValueHint}; +use eyre::{Context, Result}; +use foundry_cli::{ + opts::Dependency, + p_println, prompt, + utils::{CommandUtils, Git, LoadConfig}, +}; +use foundry_common::fs; +use foundry_config::{impl_figment_convert_basic, Config}; +use once_cell::sync::Lazy; +use regex::Regex; +use semver::Version; +use std::{ + io::IsTerminal, + path::{Path, PathBuf}, + str, +}; +use tracing::{trace, warn}; +use yansi::Paint; + +static DEPENDENCY_VERSION_TAG_REGEX: Lazy = + Lazy::new(|| Regex::new(r"^v?\d+(\.\d+)*$").unwrap()); + +/// CLI arguments for `forge install`. +#[derive(Debug, Clone, Parser)] +#[clap(override_usage = "forge install [OPTIONS] [DEPENDENCIES]... + forge install [OPTIONS] /@... + forge install [OPTIONS] =/@... + forge install [OPTIONS] ...")] +pub struct InstallArgs { + /// The dependencies to install. + /// + /// A dependency can be a raw URL, or the path to a GitHub repository. + /// + /// Additionally, a ref can be provided by adding @ to the dependency path. + /// + /// A ref can be: + /// - A branch: master + /// - A tag: v1.2.3 + /// - A commit: 8e8128 + /// + /// Target installation directory can be added via `=` suffix. + /// The dependency will installed to `lib/`. + dependencies: Vec, + + /// The project's root path. + /// + /// By default root of the Git repository, if in one, + /// or the current working directory. + #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + pub root: Option, + + #[clap(flatten)] + opts: DependencyInstallOpts, +} + +impl_figment_convert_basic!(InstallArgs); + +impl InstallArgs { + pub fn run(self) -> Result<()> { + let mut config = self.try_load_config_emit_warnings()?; + self.opts.install(&mut config, self.dependencies) + } +} + +#[derive(Debug, Clone, Default, Copy, Parser)] +pub struct DependencyInstallOpts { + /// Perform shallow clones instead of deep ones. + /// + /// Improves performance and reduces disk usage, but prevents switching branches or tags. + #[clap(long)] + pub shallow: bool, + + /// Install without adding the dependency as a submodule. + #[clap(long)] + pub no_git: bool, + + /// Do not create a commit. + #[clap(long)] + pub no_commit: bool, + + /// Do not print any messages. + #[clap(short, long)] + pub quiet: bool, +} + +impl DependencyInstallOpts { + pub fn git(self, config: &Config) -> Git<'_> { + Git::from_config(config).quiet(self.quiet).shallow(self.shallow) + } + + /// Installs all missing dependencies. + /// + /// See also [`Self::install`]. + /// + /// Returns true if any dependency was installed. + pub fn install_missing_dependencies(mut self, config: &mut Config) -> bool { + let DependencyInstallOpts { quiet, .. } = self; + let lib = config.install_lib_dir(); + if self.git(config).has_missing_dependencies(Some(lib)).unwrap_or(false) { + // The extra newline is needed, otherwise the compiler output will overwrite the message + p_println!(!quiet => "Missing dependencies found. Installing now...\n"); + self.no_commit = true; + if self.install(config, Vec::new()).is_err() && !quiet { + eprintln!( + "{}", + Paint::yellow( + "Your project has missing dependencies that could not be installed." + ) + ) + } + true + } else { + false + } + } + + /// Installs all dependencies + pub fn install(self, config: &mut Config, dependencies: Vec) -> Result<()> { + let DependencyInstallOpts { no_git, no_commit, quiet, .. } = self; + + let git = self.git(config); + + let install_lib_dir = config.install_lib_dir(); + let libs = git.root.join(install_lib_dir); + + if dependencies.is_empty() && !self.no_git { + p_println!(!self.quiet => "Updating dependencies in {}", libs.display()); + git.submodule_update(false, false, false, Some(&libs))?; + } + fs::create_dir_all(&libs)?; + + let installer = Installer { git, no_commit }; + for dep in dependencies { + let path = libs.join(dep.name()); + let rel_path = path + .strip_prefix(git.root) + .wrap_err("Library directory is not relative to the repository root")?; + p_println!(!quiet => "Installing {} in {} (url: {:?}, tag: {:?})", dep.name, path.display(), dep.url, dep.tag); + + // this tracks the actual installed tag + let installed_tag; + if no_git { + installed_tag = installer.install_as_folder(&dep, &path)?; + } else { + if !no_commit { + git.ensure_clean()?; + } + installed_tag = installer.install_as_submodule(&dep, &path)?; + + // Pin branch to submodule if branch is used + if let Some(branch) = &installed_tag { + // First, check if this tag has a branch + if git.has_branch(branch)? { + // always work with relative paths when directly modifying submodules + git.cmd() + .args(["submodule", "set-branch", "-b", branch]) + .arg(rel_path) + .exec()?; + } + + // update .gitmodules which is at the root of the repo, + // not necessarily at the root of the current Foundry project + let root = Git::root_of(git.root)?; + git.root(&root).add(Some(".gitmodules"))?; + } + + // commit the installation + if !no_commit { + let mut msg = String::with_capacity(128); + msg.push_str("forge install: "); + msg.push_str(dep.name()); + if let Some(tag) = &installed_tag { + msg.push_str("\n\n"); + msg.push_str(tag); + } + git.commit(&msg)?; + } + } + + if !quiet { + let mut msg = format!(" {} {}", Paint::green("Installed"), dep.name); + if let Some(tag) = dep.tag.or(installed_tag) { + msg.push(' '); + msg.push_str(tag.as_str()); + } + println!("{msg}"); + } + } + + // update `libs` in config if not included yet + if !config.libs.iter().any(|p| p == install_lib_dir) { + config.libs.push(install_lib_dir.to_path_buf()); + config.update_libs()?; + } + Ok(()) + } +} + +pub fn install_missing_dependencies(config: &mut Config, quiet: bool) -> bool { + DependencyInstallOpts { quiet, ..Default::default() }.install_missing_dependencies(config) +} + +#[derive(Clone, Copy, Debug)] +struct Installer<'a> { + git: Git<'a>, + no_commit: bool, +} + +impl Installer<'_> { + /// Installs the dependency as an ordinary folder instead of a submodule + fn install_as_folder(self, dep: &Dependency, path: &Path) -> Result> { + let url = dep.require_url()?; + Git::clone(dep.tag.is_none(), url, Some(&path))?; + let mut dep = dep.clone(); + + if dep.tag.is_none() { + // try to find latest semver release tag + dep.tag = self.last_tag(path); + } + + // checkout the tag if necessary + self.git_checkout(&dep, path, false)?; + + // remove git artifacts + fs::remove_dir_all(path.join(".git"))?; + + Ok(dep.tag) + } + + /// Installs the dependency as new submodule. + /// + /// This will add the git submodule to the given dir, initialize it and checkout the tag if + /// provided or try to find the latest semver, release tag. + fn install_as_submodule(self, dep: &Dependency, path: &Path) -> Result> { + // install the dep + self.git_submodule(dep, path)?; + + let mut dep = dep.clone(); + if dep.tag.is_none() { + // try to find latest semver release tag + dep.tag = self.last_tag(path); + } + + // checkout the tag if necessary + self.git_checkout(&dep, path, true)?; + + if !self.no_commit { + self.git.add(Some(path))?; + } + + Ok(dep.tag) + } + + fn last_tag(self, path: &Path) -> Option { + if self.git.shallow { + None + } else { + self.git_semver_tags(path).ok().and_then(|mut tags| tags.pop()).map(|(tag, _)| tag) + } + } + + /// Returns all semver git tags sorted in ascending order + fn git_semver_tags(self, path: &Path) -> Result> { + let out = self.git.root(path).tag()?; + let mut tags = Vec::new(); + // tags are commonly prefixed which would make them not semver: v1.2.3 is not a semantic + // version + let common_prefixes = &["v-", "v", "release-", "release"]; + for tag in out.lines() { + let mut maybe_semver = tag; + for &prefix in common_prefixes { + if let Some(rem) = tag.strip_prefix(prefix) { + maybe_semver = rem; + break + } + } + match Version::parse(maybe_semver) { + Ok(v) => { + // ignore if additional metadata, like rc, beta, etc... + if v.build.is_empty() && v.pre.is_empty() { + tags.push((tag.to_string(), v)); + } + } + Err(err) => { + warn!(?err, ?maybe_semver, "No semver tag"); + } + } + } + + tags.sort_by(|(_, a), (_, b)| a.cmp(b)); + + Ok(tags) + } + + /// Install the given dependency as git submodule in `target_dir`. + fn git_submodule(self, dep: &Dependency, path: &Path) -> Result<()> { + let url = dep.require_url()?; + + // make path relative to the git root, already checked above + let path = path.strip_prefix(self.git.root).unwrap(); + + trace!(?dep, url, ?path, "installing git submodule"); + self.git.submodule_add(true, url, path)?; + + trace!("updating submodule recursively"); + self.git.submodule_update(false, false, false, Some(path)) + } + + fn git_checkout(self, dep: &Dependency, path: &Path, recurse: bool) -> Result { + // no need to checkout if there is no tag + let Some(mut tag) = dep.tag.clone() else { return Ok(String::new()) }; + + let mut is_branch = false; + // only try to match tag if current terminal is a tty + if std::io::stdout().is_terminal() { + if tag.is_empty() { + tag = self.match_tag(&tag, path)?; + } else if let Some(branch) = self.match_branch(&tag, path)? { + trace!(?tag, ?branch, "selecting branch for given tag"); + tag = branch; + is_branch = true; + } + } + let url = dep.url.as_ref().unwrap(); + + let res = self.git.root(path).checkout(recurse, &tag); + if let Err(mut e) = res { + // remove dependency on failed checkout + fs::remove_dir_all(path)?; + if e.to_string().contains("did not match any file(s) known to git") { + e = eyre::eyre!("Tag: \"{tag}\" not found for repo \"{url}\"!") + } + return Err(e) + } + + if is_branch { + Ok(tag) + } else { + Ok(String::new()) + } + } + + /// disambiguate tag if it is a version tag + fn match_tag(self, tag: &str, path: &Path) -> Result { + // only try to match if it looks like a version tag + if !DEPENDENCY_VERSION_TAG_REGEX.is_match(tag) { + return Ok(tag.into()) + } + + // generate candidate list by filtering `git tag` output, valid ones are those "starting + // with" the user-provided tag (ignoring the starting 'v'), for example, if the user + // specifies 1.5, then v1.5.2 is a valid candidate, but v3.1.5 is not + let trimmed_tag = tag.trim_start_matches('v').to_string(); + let output = self.git.root(path).tag()?; + let mut candidates: Vec = output + .trim() + .lines() + .filter(|x| x.trim_start_matches('v').starts_with(&trimmed_tag)) + .map(|x| x.to_string()) + .rev() + .collect(); + + // no match found, fall back to the user-provided tag + if candidates.is_empty() { + return Ok(tag.into()) + } + + // have exact match + for candidate in candidates.iter() { + if candidate == tag { + return Ok(tag.into()) + } + } + + // only one candidate, ask whether the user wants to accept or not + if candidates.len() == 1 { + let matched_tag = &candidates[0]; + let input = prompt!( + "Found a similar version tag: {matched_tag}, do you want to use this instead? [Y/n] " + )?; + return if match_yn(input) { Ok(matched_tag.clone()) } else { Ok(tag.into()) } + } + + // multiple candidates, ask the user to choose one or skip + candidates.insert(0, String::from("SKIP AND USE ORIGINAL TAG")); + println!("There are multiple matching tags:"); + for (i, candidate) in candidates.iter().enumerate() { + println!("[{i}] {candidate}"); + } + + let n_candidates = candidates.len(); + loop { + let input: String = + prompt!("Please select a tag (0-{}, default: 1): ", n_candidates - 1)?; + let s = input.trim(); + // default selection, return first candidate + let n = if s.is_empty() { Ok(1) } else { s.parse() }; + // match user input, 0 indicates skipping and use original tag + match n { + Ok(0) => return Ok(tag.into()), + Ok(i) if (1..=n_candidates).contains(&i) => { + let c = &candidates[i]; + println!("[{i}] {c} selected"); + return Ok(c.clone()) + } + _ => continue, + } + } + } + + fn match_branch(self, tag: &str, path: &Path) -> Result> { + // fetch remote branches and check for tag + let output = self.git.root(path).cmd().args(["branch", "-r"]).get_stdout_lossy()?; + + let mut candidates = output + .lines() + .map(|x| x.trim().trim_start_matches("origin/")) + .filter(|x| x.starts_with(tag)) + .map(ToString::to_string) + .rev() + .collect::>(); + + trace!(?candidates, ?tag, "found branch candidates"); + + // no match found, fall back to the user-provided tag + if candidates.is_empty() { + return Ok(None) + } + + // have exact match + for candidate in candidates.iter() { + if candidate == tag { + return Ok(Some(tag.to_string())) + } + } + + // only one candidate, ask whether the user wants to accept or not + if candidates.len() == 1 { + let matched_tag = &candidates[0]; + let input = prompt!( + "Found a similar branch: {matched_tag}, do you want to use this instead? [Y/n] " + )?; + return if match_yn(input) { Ok(Some(matched_tag.clone())) } else { Ok(None) } + } + + // multiple candidates, ask the user to choose one or skip + candidates.insert(0, format!("{tag} (original branch)")); + println!("There are multiple matching branches:"); + for (i, candidate) in candidates.iter().enumerate() { + println!("[{i}] {candidate}"); + } + + let n_candidates = candidates.len(); + let input: String = prompt!( + "Please select a tag (0-{}, default: 1, Press to cancel): ", + n_candidates - 1 + )?; + let input = input.trim(); + + // default selection, return None + if input.is_empty() { + println!("Canceled branch matching"); + return Ok(None) + } + + // match user input, 0 indicates skipping and use original tag + match input.parse::() { + Ok(0) => Ok(Some(tag.into())), + Ok(i) if (1..=n_candidates).contains(&i) => { + let c = &candidates[i]; + println!("[{i}] {c} selected"); + Ok(Some(c.clone())) + } + _ => Ok(None), + } + } +} + +/// Matches on the result of a prompt for yes/no. +/// +/// Defaults to true. +fn match_yn(input: String) -> bool { + let s = input.trim().to_lowercase(); + matches!(s.as_str(), "" | "y" | "yes") +} + +#[cfg(test)] +mod tests { + use super::*; + use foundry_test_utils::tempfile::tempdir; + + #[test] + fn get_oz_tags() { + let tmp = tempdir().unwrap(); + let git = Git::new(tmp.path()); + let installer = Installer { git, no_commit: true }; + + git.init().unwrap(); + + let dep: Dependency = "openzeppelin/openzeppelin-contracts".parse().unwrap(); + let libs = tmp.path().join("libs"); + fs::create_dir(&libs).unwrap(); + let submodule = libs.join("openzeppelin-contracts"); + installer.git_submodule(&dep, &submodule).unwrap(); + assert!(submodule.exists()); + + let tags = installer.git_semver_tags(&submodule).unwrap(); + assert!(!tags.is_empty()); + let v480: Version = "4.8.0".parse().unwrap(); + assert!(tags.iter().any(|(_, v)| v == &v480)); + } +} diff --git a/crates/zkforge/bin/cmd/mod.rs b/crates/zkforge/bin/cmd/mod.rs new file mode 100644 index 000000000..ecbd08855 --- /dev/null +++ b/crates/zkforge/bin/cmd/mod.rs @@ -0,0 +1,72 @@ +//! Subcommands for forge +//! +//! All subcommands should respect the `foundry_config::Config`. +//! If a subcommand accepts values that are supported by the `Config`, then the subcommand should +//! implement `figment::Provider` which allows the subcommand to override the config's defaults, see +//! [`foundry_config::Config`]. +//! +//! See [`BuildArgs`] for a reference implementation. +//! And [`DebugArgs`] for how to merge `Providers`. +//! +//! # Example +//! +//! create a `clap` subcommand into a `figment::Provider` and integrate it in the +//! `foundry_config::Config`: +//! +//! ``` +//! use clap::Parser; +//! use forge::executor::opts::EvmOpts; +//! use foundry_cli::cmd::forge::build::BuildArgs; +//! use foundry_common::evm::EvmArgs; +//! use foundry_config::{figment::Figment, *}; +//! +//! // A new clap subcommand that accepts both `EvmArgs` and `BuildArgs` +//! #[derive(Debug, Clone, Parser)] +//! pub struct MyArgs { +//! #[clap(flatten)] +//! evm_opts: EvmArgs, +//! #[clap(flatten)] +//! opts: BuildArgs, +//! } +//! +//! // add `Figment` and `Config` converters +//! foundry_config::impl_figment_convert!(MyArgs, opts, evm_opts); +//! let args = MyArgs::parse_from(["build"]); +//! +//! let figment: Figment = From::from(&args); +//! let evm_opts = figment.extract::().unwrap(); +//! +//! let config: Config = From::from(&args); +//! ``` + +pub mod bind; +pub mod build; +pub mod cache; +pub mod config; +pub mod coverage; +pub mod create; +pub mod debug; +pub mod doc; +pub mod flatten; +pub mod fmt; +pub mod fourbyte; +pub mod geiger; +pub mod generate; +pub mod init; +pub mod inspect; +pub mod install; +pub mod remappings; +pub mod remove; +pub mod retry; +pub mod script; +pub mod selectors; +pub mod snapshot; +pub mod test; +pub mod tree; +pub mod update; +pub mod verify; +pub mod watch; +pub mod zk_build; +pub mod zk_create; +pub mod zk_solc; +pub mod zksolc_manager; diff --git a/crates/zkforge/bin/cmd/remappings.rs b/crates/zkforge/bin/cmd/remappings.rs new file mode 100644 index 000000000..c3d30cf73 --- /dev/null +++ b/crates/zkforge/bin/cmd/remappings.rs @@ -0,0 +1,58 @@ +use clap::{Parser, ValueHint}; +use ethers::solc::remappings::RelativeRemapping; +use eyre::Result; +use foundry_cli::utils::LoadConfig; +use foundry_config::impl_figment_convert_basic; +use foundry_evm::HashMap; +use std::path::PathBuf; + +/// CLI arguments for `forge remappings`. +#[derive(Debug, Clone, Parser)] +pub struct RemappingArgs { + /// The project's root path. + /// + /// By default root of the Git repository, if in one, + /// or the current working directory. + #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + root: Option, + /// Pretty-print the remappings, grouping each of them by context. + #[clap(long)] + pretty: bool, +} +impl_figment_convert_basic!(RemappingArgs); + +impl RemappingArgs { + // TODO: Do people use `forge remappings >> file`? + pub fn run(self) -> Result<()> { + let config = self.try_load_config_emit_warnings()?; + + if self.pretty { + let groups = config.remappings.into_iter().fold( + HashMap::new(), + |mut groups: HashMap, Vec>, remapping| { + groups.entry(remapping.context.clone()).or_default().push(remapping); + groups + }, + ); + for (group, remappings) in groups.into_iter() { + if let Some(group) = group { + println!("Context: {group}"); + } else { + println!("Global:"); + } + + for mut remapping in remappings.into_iter() { + remapping.context = None; // avoid writing context twice + println!("- {remapping}"); + } + println!(); + } + } else { + for remapping in config.remappings.into_iter() { + println!("{remapping}"); + } + } + + Ok(()) + } +} diff --git a/crates/zkforge/bin/cmd/remove.rs b/crates/zkforge/bin/cmd/remove.rs new file mode 100644 index 000000000..f5deb00b2 --- /dev/null +++ b/crates/zkforge/bin/cmd/remove.rs @@ -0,0 +1,46 @@ +use clap::{Parser, ValueHint}; +use eyre::Result; +use foundry_cli::{ + opts::Dependency, + utils::{Git, LoadConfig}, +}; +use foundry_config::impl_figment_convert_basic; +use std::path::PathBuf; + +/// CLI arguments for `forge remove`. +#[derive(Debug, Clone, Parser)] +pub struct RemoveArgs { + /// The dependencies you want to remove. + dependencies: Vec, + + /// The project's root path. + /// + /// By default root of the Git repository, if in one, + /// or the current working directory. + #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + root: Option, + + /// Override the up-to-date check. + #[clap(short, long)] + force: bool, +} +impl_figment_convert_basic!(RemoveArgs); + +impl RemoveArgs { + pub fn run(self) -> Result<()> { + let config = self.try_load_config_emit_warnings()?; + let (root, paths) = super::update::dependencies_paths(&self.dependencies, &config)?; + let git_modules = root.join(".git/modules"); + + // remove all the dependencies by invoking `git rm` only once with all the paths + Git::new(&root).rm(self.force, &paths)?; + + // remove all the dependencies from .git/modules + for (Dependency { name, url, tag, .. }, path) in self.dependencies.iter().zip(&paths) { + println!("Removing '{name}' in {}, (url: {url:?}, tag: {tag:?})", path.display()); + std::fs::remove_dir_all(git_modules.join(path))?; + } + + Ok(()) + } +} diff --git a/crates/zkforge/bin/cmd/retry.rs b/crates/zkforge/bin/cmd/retry.rs new file mode 100644 index 000000000..0a8db8f1f --- /dev/null +++ b/crates/zkforge/bin/cmd/retry.rs @@ -0,0 +1,61 @@ +use clap::{builder::RangedU64ValueParser, Parser}; +use foundry_utils::Retry; + +/// Retry config used when waiting for verification +pub const RETRY_CHECK_ON_VERIFY: RetryArgs = RetryArgs { retries: 8, delay: 15 }; + +/// Retry config used when waiting for a created contract +pub const RETRY_VERIFY_ON_CREATE: RetryArgs = RetryArgs { retries: 15, delay: 5 }; + +/// Retry arguments for contract verification. +#[derive(Debug, Clone, Copy, Parser)] +#[clap(about = "Allows to use retry arguments for contract verification")] // override doc +pub struct RetryArgs { + /// Number of attempts for retrying verification. + #[clap( + long, + value_parser = RangedU64ValueParser::::new().range(1..=10), + default_value = "5", + )] + pub retries: u32, + + /// Optional delay to apply inbetween verification attempts, in seconds. + #[clap( + long, + value_parser = RangedU64ValueParser::::new().range(0..=30), + default_value = "5", + )] + pub delay: u32, +} + +impl Default for RetryArgs { + fn default() -> Self { + RETRY_VERIFY_ON_CREATE + } +} + +impl From for Retry { + fn from(r: RetryArgs) -> Self { + Retry::new(r.retries, Some(r.delay)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_cli() { + let args = RetryArgs::parse_from(["foundry-cli", "--retries", "10"]); + assert_eq!(args.retries, 10); + assert_eq!(args.delay, 5); + + let args = RetryArgs::parse_from(["foundry-cli", "--delay", "10"]); + assert_eq!(args.retries, 5); + assert_eq!(args.delay, 10); + + let args = RetryArgs::parse_from(["foundry-cli", "--retries", "10", "--delay", "10"]); + assert_eq!(args.retries, 10); + assert_eq!(args.delay, 10); + } +} diff --git a/crates/zkforge/bin/cmd/script/artifacts.rs b/crates/zkforge/bin/cmd/script/artifacts.rs new file mode 100644 index 000000000..b546cef2e --- /dev/null +++ b/crates/zkforge/bin/cmd/script/artifacts.rs @@ -0,0 +1,9 @@ +use ethers::abi::Abi; + +/// Bundles info of an artifact +pub struct ArtifactInfo<'a> { + pub contract_name: String, + pub contract_id: String, + pub abi: &'a Abi, + pub code: &'a Vec, +} diff --git a/crates/zkforge/bin/cmd/script/broadcast.rs b/crates/zkforge/bin/cmd/script/broadcast.rs new file mode 100644 index 000000000..c493ecb74 --- /dev/null +++ b/crates/zkforge/bin/cmd/script/broadcast.rs @@ -0,0 +1,679 @@ +use super::{ + multi::MultiChainSequence, providers::ProvidersManager, receipts::clear_pendings, + sequence::ScriptSequence, transaction::TransactionWithMetadata, verify::VerifyBundle, *, +}; +use ethers::{ + prelude::{Provider, Signer, TxHash}, + providers::{JsonRpcClient, Middleware}, + utils::format_units, +}; +use eyre::{bail, ContextCompat, Result, WrapErr}; +use foundry_cli::{ + init_progress, + opts::WalletSigner, + update_progress, + utils::{has_batch_support, has_different_gas_calc}, +}; +use foundry_common::{estimate_eip1559_fees, shell, try_get_http_provider, RetryProvider}; +use futures::StreamExt; +use std::{cmp::min, collections::HashSet, ops::Mul, sync::Arc}; +use tracing::trace; + +impl ScriptArgs { + /// Sends the transactions which haven't been broadcasted yet. + pub async fn send_transactions( + &self, + deployment_sequence: &mut ScriptSequence, + fork_url: &str, + script_wallets: &[LocalWallet], + ) -> Result<()> { + let provider = Arc::new(try_get_http_provider(fork_url)?); + let already_broadcasted = deployment_sequence.receipts.len(); + + if already_broadcasted < deployment_sequence.transactions.len() { + let required_addresses: HashSet
= deployment_sequence + .typed_transactions() + .into_iter() + .skip(already_broadcasted) + .map(|(_, tx)| (*tx.from().expect("No sender for onchain transaction!")).to_alloy()) + .collect(); + + let (send_kind, chain) = if self.unlocked { + let chain = provider.get_chainid().await?; + let mut senders = HashSet::from([self + .evm_opts + .sender + .map(|sender| sender.to_alloy()) + .wrap_err("--sender must be set with --unlocked")?]); + // also take all additional senders that where set manually via broadcast + senders.extend( + deployment_sequence + .typed_transactions() + .iter() + .filter_map(|(_, tx)| tx.from().copied().map(|addr| addr.to_alloy())), + ); + (SendTransactionsKind::Unlocked(senders), chain.as_u64()) + } else { + let local_wallets = self + .wallets + .find_all( + provider.clone(), + required_addresses.into_iter().map(|addr| addr.to_ethers()).collect(), + script_wallets, + ) + .await?; + let chain = local_wallets.values().last().wrap_err("Error accessing local wallet when trying to send onchain transaction, did you set a private key, mnemonic or keystore?")?.chain_id(); + ( + SendTransactionsKind::Raw( + local_wallets.into_iter().map(|m| (m.0.to_alloy(), m.1)).collect(), + ), + chain, + ) + }; + + // We only wait for a transaction receipt before sending the next transaction, if there + // is more than one signer. There would be no way of assuring their order + // otherwise. Or if the chain does not support batched transactions (eg. Arbitrum). + let sequential_broadcast = + send_kind.signers_count() != 1 || self.slow || !has_batch_support(chain); + + // Make a one-time gas price estimation + let (gas_price, eip1559_fees) = { + match deployment_sequence.transactions.front().unwrap().typed_tx() { + TypedTransaction::Legacy(_) | TypedTransaction::Eip2930(_) => { + (provider.get_gas_price().await.ok(), None) + } + TypedTransaction::Eip1559(_) => { + let fees = estimate_eip1559_fees(&provider, Some(chain)) + .await + .wrap_err("Failed to estimate EIP1559 fees. This chain might not support EIP1559, try adding --legacy to your command.")?; + + (None, Some(fees)) + } + } + }; + + // Iterate through transactions, matching the `from` field with the associated + // wallet. Then send the transaction. Panics if we find a unknown `from` + let sequence = deployment_sequence + .transactions + .iter() + .skip(already_broadcasted) + .map(|tx_with_metadata| { + let tx = tx_with_metadata.typed_tx(); + let from = (*tx.from().expect("No sender for onchain transaction!")).to_alloy(); + + let kind = send_kind.for_sender(&from)?; + let is_fixed_gas_limit = tx_with_metadata.is_fixed_gas_limit; + + let mut tx = tx.clone(); + + tx.set_chain_id(chain); + + if let Some(gas_price) = self.with_gas_price { + tx.set_gas_price(gas_price.to_ethers()); + } else { + // fill gas price + match tx { + TypedTransaction::Eip2930(_) | TypedTransaction::Legacy(_) => { + tx.set_gas_price(gas_price.expect("Could not get gas_price.")); + } + TypedTransaction::Eip1559(ref mut inner) => { + let eip1559_fees = + eip1559_fees.expect("Could not get eip1559 fee estimation."); + if let Some(priority_gas_price) = self.priority_gas_price { + inner.max_priority_fee_per_gas = + Some(priority_gas_price.to_ethers()); + } else { + inner.max_priority_fee_per_gas = Some(eip1559_fees.1); + } + inner.max_fee_per_gas = Some(eip1559_fees.0); + } + } + } + + Ok((tx, kind, is_fixed_gas_limit)) + }) + .collect::>>()?; + + let pb = init_progress!(deployment_sequence.transactions, "txes"); + + // We send transactions and wait for receipts in batches of 100, since some networks + // cannot handle more than that. + let batch_size = 100; + let mut index = 0; + + for (batch_number, batch) in sequence.chunks(batch_size).map(|f| f.to_vec()).enumerate() + { + let mut pending_transactions = vec![]; + + shell::println(format!( + "##\nSending transactions [{} - {}].", + batch_number * batch_size, + batch_number * batch_size + min(batch_size, batch.len()) - 1 + ))?; + for (tx, kind, is_fixed_gas_limit) in batch.into_iter() { + let tx_hash = self.send_transaction( + provider.clone(), + tx, + kind, + sequential_broadcast, + fork_url, + is_fixed_gas_limit, + ); + + if sequential_broadcast { + let tx_hash = tx_hash.await?; + deployment_sequence.add_pending(index, tx_hash.to_alloy()); + + update_progress!(pb, (index + already_broadcasted)); + index += 1; + + clear_pendings( + provider.clone(), + deployment_sequence, + Some(vec![tx_hash.to_alloy()]), + ) + .await?; + } else { + pending_transactions.push(tx_hash); + } + } + + if !pending_transactions.is_empty() { + let mut buffer = futures::stream::iter(pending_transactions).buffered(7); + + while let Some(tx_hash) = buffer.next().await { + let tx_hash = tx_hash?; + deployment_sequence.add_pending(index, tx_hash.to_alloy()); + + update_progress!(pb, (index + already_broadcasted)); + index += 1; + } + + // Checkpoint save + deployment_sequence.save()?; + + if !sequential_broadcast { + shell::println("##\nWaiting for receipts.")?; + clear_pendings(provider.clone(), deployment_sequence, None).await?; + } + } + + // Checkpoint save + deployment_sequence.save()?; + } + } + + shell::println("\n\n==========================")?; + shell::println("\nONCHAIN EXECUTION COMPLETE & SUCCESSFUL.")?; + + let (total_gas, total_gas_price, total_paid) = deployment_sequence.receipts.iter().fold( + (U256::ZERO, U256::ZERO, U256::ZERO), + |acc, receipt| { + let gas_used = receipt.gas_used.unwrap_or_default().to_alloy(); + let gas_price = receipt.effective_gas_price.unwrap_or_default().to_alloy(); + (acc.0 + gas_used, acc.1 + gas_price, acc.2 + gas_used.mul(gas_price)) + }, + ); + let paid = format_units(total_paid.to_ethers(), 18).unwrap_or_else(|_| "N/A".to_string()); + let avg_gas_price = + format_units(total_gas_price.to_ethers() / deployment_sequence.receipts.len(), 9) + .unwrap_or_else(|_| "N/A".to_string()); + shell::println(format!( + "Total Paid: {} ETH ({} gas * avg {} gwei)", + paid.trim_end_matches('0'), + total_gas, + avg_gas_price.trim_end_matches('0').trim_end_matches('.') + ))?; + + Ok(()) + } + + async fn send_transaction( + &self, + provider: Arc, + mut tx: TypedTransaction, + kind: SendTransactionKind<'_>, + sequential_broadcast: bool, + fork_url: &str, + is_fixed_gas_limit: bool, + ) -> Result { + let from = tx.from().expect("no sender"); + + if sequential_broadcast { + let nonce = foundry_utils::next_nonce((*from).to_alloy(), fork_url, None) + .await + .map_err(|_| eyre::eyre!("Not able to query the EOA nonce."))?; + + let tx_nonce = tx.nonce().expect("no nonce"); + if let Ok(tx_nonce) = u64::try_from(tx_nonce.to_alloy()) { + if nonce != tx_nonce { + bail!("EOA nonce changed unexpectedly while sending transactions. Expected {tx_nonce} got {nonce} from provider.") + } + } + } + + match kind { + SendTransactionKind::Unlocked(addr) => { + tracing::debug!("sending transaction from unlocked account {:?}: {:?}", addr, tx); + + // Chains which use `eth_estimateGas` are being sent sequentially and require their + // gas to be re-estimated right before broadcasting. + if !is_fixed_gas_limit && + (has_different_gas_calc(provider.get_chainid().await?.as_u64()) || + self.skip_simulation) + { + self.estimate_gas(&mut tx, &provider).await?; + } + + // Submit the transaction + let pending = provider.send_transaction(tx, None).await?; + + Ok(pending.tx_hash()) + } + SendTransactionKind::Raw(signer) => self.broadcast(provider, signer, tx).await, + } + } + + /// Executes the created transactions, and if no error has occurred, broadcasts + /// them. + pub async fn handle_broadcastable_transactions( + &self, + mut result: ScriptResult, + libraries: Libraries, + decoder: &CallTraceDecoder, + mut script_config: ScriptConfig, + verify: VerifyBundle, + ) -> Result<()> { + if let Some(txs) = result.transactions.take() { + script_config.collect_rpcs(&txs); + script_config.check_multi_chain_constraints(&libraries)?; + script_config.check_shanghai_support().await?; + + if !script_config.missing_rpc { + trace!(target: "script", "creating deployments"); + + let mut deployments = self + .create_script_sequences( + txs, + &result, + &mut script_config, + decoder, + &verify.known_contracts, + ) + .await?; + + if script_config.has_multiple_rpcs() { + trace!(target: "script", "broadcasting multi chain deployment"); + + let multi = MultiChainSequence::new( + deployments.clone(), + &self.sig, + script_config.target_contract(), + &script_config.config.broadcast, + self.broadcast, + )?; + + if self.broadcast { + self.multi_chain_deployment( + multi, + libraries, + &script_config.config, + result.script_wallets, + verify, + ) + .await?; + } + } else if self.broadcast { + self.single_deployment( + deployments.first_mut().expect("to be set."), + script_config, + libraries, + result, + verify, + ) + .await?; + } + + if !self.broadcast { + shell::println("\nSIMULATION COMPLETE. To broadcast these transactions, add --broadcast and wallet configuration(s) to the previous command. See forge script --help for more.")?; + } + } else { + shell::println("\nIf you wish to simulate on-chain transactions pass a RPC URL.")?; + } + } + Ok(()) + } + + /// Broadcasts a single chain script. + async fn single_deployment( + &self, + deployment_sequence: &mut ScriptSequence, + script_config: ScriptConfig, + libraries: Libraries, + result: ScriptResult, + verify: VerifyBundle, + ) -> Result<()> { + trace!(target: "script", "broadcasting single chain deployment"); + + let rpc = script_config.total_rpcs.into_iter().next().expect("exists; qed"); + + deployment_sequence.add_libraries(libraries); + + self.send_transactions(deployment_sequence, &rpc, &result.script_wallets).await?; + + if self.verify { + return deployment_sequence.verify_contracts(&script_config.config, verify).await + } + Ok(()) + } + + /// Given the collected transactions it creates a list of [`ScriptSequence`]. List length will + /// be higher than 1, if we're dealing with a multi chain deployment. + /// + /// If `--skip-simulation` is not passed, it will make an onchain simulation of the transactions + /// before adding them to [`ScriptSequence`]. + async fn create_script_sequences( + &self, + txs: BroadcastableTransactions, + script_result: &ScriptResult, + script_config: &mut ScriptConfig, + decoder: &CallTraceDecoder, + known_contracts: &ContractsByArtifact, + ) -> Result> { + if !txs.is_empty() { + let gas_filled_txs = self + .fills_transactions_with_gas(txs, script_config, decoder, known_contracts) + .await?; + + let returns = self.get_returns(&*script_config, &script_result.returned)?; + + return self + .bundle_transactions( + gas_filled_txs, + &script_config.target_contract().clone(), + &mut script_config.config, + returns, + ) + .await + } else if self.broadcast { + eyre::bail!("No onchain transactions generated in script"); + } + + Ok(vec![]) + } + + /// Takes the collected transactions and executes them locally before converting them to + /// [`TransactionWithMetadata`] with the appropriate gas execution estimation. If + /// `--skip-simulation` is passed, then it will skip the execution. + async fn fills_transactions_with_gas( + &self, + txs: BroadcastableTransactions, + script_config: &ScriptConfig, + decoder: &CallTraceDecoder, + known_contracts: &ContractsByArtifact, + ) -> Result> { + let gas_filled_txs = if self.skip_simulation { + shell::println("\nSKIPPING ON CHAIN SIMULATION.")?; + txs.into_iter() + .map(|btx| { + let mut tx = TransactionWithMetadata::from_typed_transaction(btx.transaction); + tx.rpc = btx.rpc; + tx + }) + .collect() + } else { + self.onchain_simulation( + txs, + script_config, + decoder, + known_contracts, + ) + .await + .wrap_err("\nTransaction failed when running the on-chain simulation. Check the trace above for more information.")? + }; + Ok(gas_filled_txs) + } + + /// Returns all transactions of the [`TransactionWithMetadata`] type in a list of + /// [`ScriptSequence`]. List length will be higher than 1, if we're dealing with a multi + /// chain deployment. + /// + /// Each transaction will be added with the correct transaction type and gas estimation. + async fn bundle_transactions( + &self, + transactions: VecDeque, + target: &ArtifactId, + config: &mut Config, + returns: HashMap, + ) -> Result> { + // User might be using both "in-code" forks and `--fork-url`. + let last_rpc = &transactions.back().expect("exists; qed").rpc; + let is_multi_deployment = transactions.iter().any(|tx| &tx.rpc != last_rpc); + + let mut total_gas_per_rpc: HashMap = HashMap::new(); + + // Batches sequence of transactions from different rpcs. + let mut new_sequence = VecDeque::new(); + let mut manager = ProvidersManager::default(); + let mut deployments = vec![]; + + // Config is used to initialize the sequence chain, so we need to change when handling a new + // sequence. This makes sure we don't lose the original value. + let original_config_chain = config.chain_id; + + // Peeking is used to check if the next rpc url is different. If so, it creates a + // [`ScriptSequence`] from all the collected transactions up to this point. + let mut txes_iter = transactions.into_iter().peekable(); + + while let Some(mut tx) = txes_iter.next() { + let tx_rpc = match tx.rpc.clone() { + Some(rpc) => rpc, + None => { + let rpc = self.evm_opts.ensure_fork_url()?.clone(); + // Fills the RPC inside the transaction, if missing one. + tx.rpc = Some(rpc.clone()); + rpc + } + }; + + let provider_info = manager.get_or_init_provider(&tx_rpc, self.legacy).await?; + + // Handles chain specific requirements. + tx.change_type(provider_info.is_legacy); + tx.transaction.set_chain_id(provider_info.chain); + + if !self.skip_simulation { + let typed_tx = tx.typed_tx_mut(); + + if has_different_gas_calc(provider_info.chain) { + trace!("estimating with different gas calculation"); + let gas = *typed_tx.gas().expect("gas is set by simulation."); + + // We are trying to show the user an estimation of the total gas usage. + // + // However, some transactions might depend on previous ones. For + // example, tx1 might deploy a contract that tx2 uses. That + // will result in the following `estimate_gas` call to fail, + // since tx1 hasn't been broadcasted yet. + // + // Not exiting here will not be a problem when actually broadcasting, because + // for chains where `has_different_gas_calc` returns true, + // we await each transaction before broadcasting the next + // one. + if let Err(err) = self.estimate_gas(typed_tx, &provider_info.provider).await { + trace!("gas estimation failed: {err}"); + + // Restore gas value, since `estimate_gas` will remove it. + typed_tx.set_gas(gas); + } + } + + let total_gas = total_gas_per_rpc.entry(tx_rpc.clone()).or_insert(U256::ZERO); + *total_gas += (*typed_tx.gas().expect("gas is set")).to_alloy(); + } + + new_sequence.push_back(tx); + // We only create a [`ScriptSequence`] object when we collect all the rpc related + // transactions. + if let Some(next_tx) = txes_iter.peek() { + if next_tx.rpc == Some(tx_rpc) { + continue + } + } + + config.chain_id = Some(provider_info.chain.into()); + let sequence = ScriptSequence::new( + new_sequence, + returns.clone(), + &self.sig, + target, + config, + self.broadcast, + is_multi_deployment, + )?; + + deployments.push(sequence); + + new_sequence = VecDeque::new(); + } + + // Restore previous config chain. + config.chain_id = original_config_chain; + + if !self.skip_simulation { + // Present gas information on a per RPC basis. + for (rpc, total_gas) in total_gas_per_rpc { + let provider_info = manager.get(&rpc).expect("provider is set."); + + // We don't store it in the transactions, since we want the most updated value. + // Right before broadcasting. + let per_gas = if let Some(gas_price) = self.with_gas_price { + gas_price + } else { + provider_info.gas_price()? + }; + + shell::println("\n==========================")?; + shell::println(format!("\nChain {}", provider_info.chain))?; + + shell::println(format!( + "\nEstimated gas price: {} gwei", + format_units(per_gas.to_ethers(), 9) + .unwrap_or_else(|_| "[Could not calculate]".to_string()) + .trim_end_matches('0') + .trim_end_matches('.') + ))?; + shell::println(format!("\nEstimated total gas used for script: {total_gas}"))?; + shell::println(format!( + "\nEstimated amount required: {} ETH", + format_units(total_gas.saturating_mul(per_gas).to_ethers(), 18) + .unwrap_or_else(|_| "[Could not calculate]".to_string()) + .trim_end_matches('0') + ))?; + shell::println("\n==========================")?; + } + } + Ok(deployments) + } + + /// Uses the signer to submit a transaction to the network. If it fails, it tries to retrieve + /// the transaction hash that can be used on a later run with `--resume`. + async fn broadcast( + &self, + provider: Arc, + signer: &WalletSigner, + mut legacy_or_1559: TypedTransaction, + ) -> Result { + tracing::debug!("sending transaction: {:?}", legacy_or_1559); + + // Chains which use `eth_estimateGas` are being sent sequentially and require their gas + // to be re-estimated right before broadcasting. + if has_different_gas_calc(signer.chain_id()) || self.skip_simulation { + // if already set, some RPC endpoints might simply return the gas value that is + // already set in the request and omit the estimate altogether, so + // we remove it here + let _ = legacy_or_1559.gas_mut().take(); + + self.estimate_gas(&mut legacy_or_1559, &provider).await?; + } + + // Signing manually so we skip `fill_transaction` and its `eth_createAccessList` + // request. + let signature = signer + .sign_transaction(&legacy_or_1559) + .await + .wrap_err("Failed to sign transaction")?; + + // Submit the raw transaction + let pending = provider.send_raw_transaction(legacy_or_1559.rlp_signed(&signature)).await?; + + Ok(pending.tx_hash()) + } + + async fn estimate_gas(&self, tx: &mut TypedTransaction, provider: &Provider) -> Result<()> + where + T: JsonRpcClient, + { + // if already set, some RPC endpoints might simply return the gas value that is already + // set in the request and omit the estimate altogether, so we remove it here + let _ = tx.gas_mut().take(); + + tx.set_gas( + provider + .estimate_gas(tx, None) + .await + .wrap_err_with(|| format!("Failed to estimate gas for tx: {:?}", tx.sighash()))? * + self.gas_estimate_multiplier / + 100, + ); + Ok(()) + } +} + +/// How to send a single transaction +#[derive(Clone)] +enum SendTransactionKind<'a> { + Unlocked(Address), + Raw(&'a WalletSigner), +} + +/// Represents how to send _all_ transactions +enum SendTransactionsKind { + /// Send via `eth_sendTransaction` and rely on the `from` address being unlocked. + Unlocked(HashSet
), + /// Send a signed transaction via `eth_sendRawTransaction` + Raw(HashMap), +} + +impl SendTransactionsKind { + /// Returns the [`SendTransactionKind`] for the given address + /// + /// Returns an error if no matching signer is found or the address is not unlocked + fn for_sender(&self, addr: &Address) -> Result> { + match self { + SendTransactionsKind::Unlocked(unlocked) => { + if !unlocked.contains(addr) { + bail!("Sender address {:?} is not unlocked", addr) + } + Ok(SendTransactionKind::Unlocked(*addr)) + } + SendTransactionsKind::Raw(wallets) => { + if let Some(wallet) = wallets.get(addr) { + Ok(SendTransactionKind::Raw(wallet)) + } else { + bail!("No matching signer for {:?} found", addr) + } + } + } + } + + /// How many signers are set + fn signers_count(&self) -> usize { + match self { + SendTransactionsKind::Unlocked(addr) => addr.len(), + SendTransactionsKind::Raw(signers) => signers.len(), + } + } +} diff --git a/crates/zkforge/bin/cmd/script/build.rs b/crates/zkforge/bin/cmd/script/build.rs new file mode 100644 index 000000000..c0a176213 --- /dev/null +++ b/crates/zkforge/bin/cmd/script/build.rs @@ -0,0 +1,289 @@ +use super::*; +use alloy_primitives::{Address, Bytes}; +use ethers::{ + prelude::{ + artifacts::Libraries, cache::SolFilesCache, ArtifactId, Project, ProjectCompileOutput, + }, + solc::{ + artifacts::{CompactContractBytecode, ContractBytecode, ContractBytecodeSome}, + contracts::ArtifactContracts, + info::ContractInfo, + }, +}; +use eyre::{Context, ContextCompat, Result}; +use foundry_cli::utils::get_cached_entry_by_name; +use foundry_common::{ + compact_to_contract, + compile::{self, ContractSources}, +}; +use foundry_utils::{PostLinkInput, ResolvedDependency}; +use std::{collections::BTreeMap, fs, str::FromStr}; +use tracing::{trace, warn}; + +impl ScriptArgs { + /// Compiles the file or project and the verify metadata. + pub fn compile(&mut self, script_config: &mut ScriptConfig) -> Result { + trace!(target: "script", "compiling script"); + + self.build(script_config) + } + + /// Compiles the file with auto-detection and compiler params. + pub fn build(&mut self, script_config: &mut ScriptConfig) -> Result { + let (project, output) = self.get_project_and_output(script_config)?; + let output = output.with_stripped_file_prefixes(project.root()); + + let mut sources: ContractSources = Default::default(); + + let contracts = output + .into_artifacts() + .map(|(id, artifact)| -> Result<_> { + // Sources are only required for the debugger, but it *might* mean that there's + // something wrong with the build and/or artifacts. + if let Some(source) = artifact.source_file() { + let abs_path = source + .ast + .ok_or(eyre::eyre!("Source from artifact has no AST."))? + .absolute_path; + let source_code = fs::read_to_string(abs_path)?; + let contract = artifact.clone().into_contract_bytecode(); + let source_contract = compact_to_contract(contract)?; + sources + .0 + .entry(id.clone().name) + .or_default() + .insert(source.id, (source_code, source_contract)); + } else { + warn!("source not found for artifact={:?}", id); + } + Ok((id, artifact)) + }) + .collect::>()?; + + let mut output = self.link( + project, + contracts, + script_config.config.parsed_libraries()?, + script_config.evm_opts.sender, + script_config.sender_nonce, + )?; + + output.sources = sources; + script_config.target_contract = Some(output.target.clone()); + + Ok(output) + } + + pub fn link( + &self, + project: Project, + contracts: ArtifactContracts, + libraries_addresses: Libraries, + sender: Address, + nonce: u64, + ) -> Result { + let mut run_dependencies = vec![]; + let mut contract = CompactContractBytecode::default(); + let mut highlevel_known_contracts = BTreeMap::new(); + + let mut target_fname = dunce::canonicalize(&self.path) + .wrap_err("Couldn't convert contract path to absolute path.")? + .strip_prefix(project.root()) + .wrap_err("Couldn't strip project root from contract path.")? + .to_str() + .wrap_err("Bad path to string.")? + .to_string(); + + let no_target_name = if let Some(target_name) = &self.target_contract { + target_fname = target_fname + ":" + target_name; + false + } else { + true + }; + + let mut extra_info = ExtraLinkingInfo { + no_target_name, + target_fname: target_fname.clone(), + contract: &mut contract, + dependencies: &mut run_dependencies, + matched: false, + target_id: None, + }; + + // link_with_nonce_or_address expects absolute paths + let mut libs = libraries_addresses.clone(); + for (file, libraries) in libraries_addresses.libs.iter() { + if file.is_relative() { + let mut absolute_path = project.root().clone(); + absolute_path.push(file); + libs.libs.insert(absolute_path, libraries.clone()); + } + } + + foundry_utils::link_with_nonce_or_address( + contracts.clone(), + &mut highlevel_known_contracts, + libs, + sender, + nonce, + &mut extra_info, + |post_link_input| { + let PostLinkInput { + contract, + known_contracts: highlevel_known_contracts, + id, + extra, + dependencies, + } = post_link_input; + + fn unique_deps(deps: Vec) -> Vec<(String, Bytes)> { + let mut filtered = Vec::new(); + let mut seen = HashSet::new(); + for dep in deps { + if !seen.insert(dep.id.clone()) { + continue + } + filtered.push((dep.id, dep.bytecode)); + } + + filtered + } + + // if it's the target contract, grab the info + if extra.no_target_name { + if id.source == std::path::PathBuf::from(&extra.target_fname) { + if extra.matched { + eyre::bail!("Multiple contracts in the target path. Please specify the contract name with `--tc ContractName`") + } + *extra.dependencies = unique_deps(dependencies); + *extra.contract = contract.clone(); + extra.matched = true; + extra.target_id = Some(id.clone()); + } + } else { + let (path, name) = extra + .target_fname + .rsplit_once(':') + .expect("The target specifier is malformed."); + let path = std::path::Path::new(path); + if path == id.source && name == id.name { + *extra.dependencies = unique_deps(dependencies); + *extra.contract = contract.clone(); + extra.matched = true; + extra.target_id = Some(id.clone()); + } + } + + if let Ok(tc) = ContractBytecode::from(contract).try_into() { + highlevel_known_contracts.insert(id, tc); + } + + Ok(()) + }, + project.root(), + )?; + + let target = extra_info + .target_id + .ok_or_else(|| eyre::eyre!("Could not find target contract: {}", target_fname))?; + + let (new_libraries, predeploy_libraries): (Vec<_>, Vec<_>) = + run_dependencies.into_iter().unzip(); + + // Merge with user provided libraries + let mut new_libraries = Libraries::parse(&new_libraries)?; + for (file, libraries) in libraries_addresses.libs.into_iter() { + new_libraries.libs.entry(file).or_default().extend(libraries) + } + + Ok(BuildOutput { + target, + contract, + known_contracts: contracts, + highlevel_known_contracts: ArtifactContracts(highlevel_known_contracts), + predeploy_libraries, + sources: Default::default(), + project, + libraries: new_libraries, + }) + } + + pub fn get_project_and_output( + &mut self, + script_config: &ScriptConfig, + ) -> Result<(Project, ProjectCompileOutput)> { + let project = script_config.config.project()?; + + let filters = self.opts.skip.clone().unwrap_or_default(); + // We received a valid file path. + // If this file does not exist, `dunce::canonicalize` will + // result in an error and it will be handled below. + if let Ok(target_contract) = dunce::canonicalize(&self.path) { + let output = compile::compile_target_with_filter( + &target_contract, + &project, + self.opts.args.silent, + self.verify, + filters, + )?; + return Ok((project, output)) + } + + if !project.paths.has_input_files() { + eyre::bail!("The project doesn't have any input files. Make sure the `script` directory is configured properly in foundry.toml. Otherwise, provide the path to the file.") + } + + let contract = ContractInfo::from_str(&self.path)?; + self.target_contract = Some(contract.name.clone()); + + // We received `contract_path:contract_name` + if let Some(path) = contract.path { + let path = + dunce::canonicalize(path).wrap_err("Could not canonicalize the target path")?; + let output = compile::compile_target_with_filter( + &path, + &project, + self.opts.args.silent, + self.verify, + filters, + )?; + self.path = path.to_string_lossy().to_string(); + return Ok((project, output)) + } + + // We received `contract_name`, and need to find its file path. + let output = if self.opts.args.silent { + compile::suppress_compile(&project) + } else { + compile::compile(&project, false, false) + }?; + let cache = + SolFilesCache::read_joined(&project.paths).wrap_err("Could not open compiler cache")?; + + let (path, _) = get_cached_entry_by_name(&cache, &contract.name) + .wrap_err("Could not find target contract in cache")?; + self.path = path.to_string_lossy().to_string(); + + Ok((project, output)) + } +} + +struct ExtraLinkingInfo<'a> { + no_target_name: bool, + target_fname: String, + contract: &'a mut CompactContractBytecode, + dependencies: &'a mut Vec<(String, Bytes)>, + matched: bool, + target_id: Option, +} + +pub struct BuildOutput { + pub project: Project, + pub target: ArtifactId, + pub contract: CompactContractBytecode, + pub known_contracts: ArtifactContracts, + pub highlevel_known_contracts: ArtifactContracts, + pub libraries: Libraries, + pub predeploy_libraries: Vec, + pub sources: ContractSources, +} diff --git a/crates/zkforge/bin/cmd/script/cmd.rs b/crates/zkforge/bin/cmd/script/cmd.rs new file mode 100644 index 000000000..77cd60e78 --- /dev/null +++ b/crates/zkforge/bin/cmd/script/cmd.rs @@ -0,0 +1,360 @@ +use super::{multi::MultiChainSequence, sequence::ScriptSequence, verify::VerifyBundle, *}; +use alloy_primitives::Bytes; +use ethers::{ + prelude::{Middleware, Signer}, + types::transaction::eip2718::TypedTransaction, +}; +use eyre::Result; +use foundry_cli::utils::LoadConfig; +use foundry_common::{contracts::flatten_contracts, try_get_http_provider}; +use foundry_debugger::DebuggerArgs; +use foundry_utils::types::ToAlloy; +use std::sync::Arc; +use tracing::trace; + +/// Helper alias type for the collection of data changed due to the new sender. +type NewSenderChanges = (CallTraceDecoder, Libraries, ArtifactContracts); + +impl ScriptArgs { + /// Executes the script + pub async fn run_script(mut self) -> Result<()> { + trace!(target: "script", "executing script command"); + + let (config, evm_opts) = self.load_config_and_evm_opts_emit_warnings()?; + let mut script_config = ScriptConfig { + // dapptools compatibility + sender_nonce: 1, + config, + evm_opts, + debug: self.debug, + ..Default::default() + }; + + self.maybe_load_private_key(&mut script_config)?; + + if let Some(ref fork_url) = script_config.evm_opts.fork_url { + // when forking, override the sender's nonce to the onchain value + script_config.sender_nonce = + foundry_utils::next_nonce(script_config.evm_opts.sender, fork_url, None).await? + } else { + // if not forking, then ignore any pre-deployed library addresses + script_config.config.libraries = Default::default(); + } + + let build_output = self.compile(&mut script_config)?; + + let mut verify = VerifyBundle::new( + &build_output.project, + &script_config.config, + flatten_contracts(&build_output.highlevel_known_contracts, false), + self.retry, + self.verifier.clone(), + ); + + let BuildOutput { + project, + contract, + mut highlevel_known_contracts, + predeploy_libraries, + known_contracts: default_known_contracts, + sources, + mut libraries, + .. + } = build_output; + + // Execute once with default sender. + let sender = script_config.evm_opts.sender; + + // We need to execute the script even if just resuming, in case we need to collect private + // keys from the execution. + let mut result = + self.execute(&mut script_config, contract, sender, &predeploy_libraries).await?; + + if self.resume || (self.verify && !self.broadcast) { + return self + .resume_deployment( + script_config, + project, + default_known_contracts, + libraries, + result, + verify, + ) + .await + } + + let known_contracts = flatten_contracts(&highlevel_known_contracts, true); + let mut decoder = self.decode_traces(&script_config, &mut result, &known_contracts)?; + + if self.debug { + let debugger = DebuggerArgs { + debug: result.debug.clone().unwrap_or_default(), + decoder: &decoder, + sources, + breakpoints: result.breakpoints.clone(), + }; + debugger.run()?; + } + + if let Some((new_traces, updated_libraries, updated_contracts)) = self + .maybe_prepare_libraries( + &mut script_config, + project, + default_known_contracts, + predeploy_libraries, + &mut result, + ) + .await? + { + decoder = new_traces; + highlevel_known_contracts = updated_contracts; + libraries = updated_libraries; + } + + if self.json { + self.show_json(&script_config, &result)?; + } else { + self.show_traces(&script_config, &decoder, &mut result).await?; + } + + verify.known_contracts = flatten_contracts(&highlevel_known_contracts, false); + self.check_contract_sizes(&result, &highlevel_known_contracts)?; + + self.handle_broadcastable_transactions(result, libraries, &decoder, script_config, verify) + .await + } + + // In case there are libraries to be deployed, it makes sure that these are added to the list of + // broadcastable transactions with the appropriate sender. + async fn maybe_prepare_libraries( + &mut self, + script_config: &mut ScriptConfig, + project: Project, + default_known_contracts: ArtifactContracts, + predeploy_libraries: Vec, + result: &mut ScriptResult, + ) -> Result> { + if let Some(new_sender) = self.maybe_new_sender( + &script_config.evm_opts, + result.transactions.as_ref(), + &predeploy_libraries, + )? { + // We have a new sender, so we need to relink all the predeployed libraries. + let (libraries, highlevel_known_contracts) = self + .rerun_with_new_deployer( + project, + script_config, + new_sender, + result, + default_known_contracts, + ) + .await?; + + // redo traces for the new addresses + let new_traces = self.decode_traces( + &*script_config, + result, + &flatten_contracts(&highlevel_known_contracts, true), + )?; + + return Ok(Some((new_traces, libraries, highlevel_known_contracts))) + } + + // Add predeploy libraries to the list of broadcastable transactions. + let mut lib_deploy = self.create_deploy_transactions( + script_config.evm_opts.sender, + script_config.sender_nonce, + &predeploy_libraries, + &script_config.evm_opts.fork_url, + ); + + if let Some(txs) = &mut result.transactions { + for tx in txs.iter() { + lib_deploy.push_back(BroadcastableTransaction { + rpc: tx.rpc.clone(), + transaction: TypedTransaction::Legacy(tx.transaction.clone().into()), + }); + } + *txs = lib_deploy; + } + + Ok(None) + } + + /// Resumes the deployment and/or verification of the script. + async fn resume_deployment( + &mut self, + script_config: ScriptConfig, + project: Project, + default_known_contracts: ArtifactContracts, + libraries: Libraries, + result: ScriptResult, + verify: VerifyBundle, + ) -> Result<()> { + if self.multi { + return self + .multi_chain_deployment( + MultiChainSequence::load( + &script_config.config.broadcast, + &self.sig, + script_config.target_contract(), + )?, + libraries, + &script_config.config, + result.script_wallets, + verify, + ) + .await + } + self.resume_single_deployment( + script_config, + project, + default_known_contracts, + result, + verify, + ) + .await + .map_err(|err| { + eyre::eyre!("{err}\n\nIf you were trying to resume or verify a multi chain deployment, add `--multi` to your command invocation.") + }) + } + + /// Resumes the deployment and/or verification of a single RPC script. + async fn resume_single_deployment( + &mut self, + script_config: ScriptConfig, + project: Project, + default_known_contracts: ArtifactContracts, + result: ScriptResult, + mut verify: VerifyBundle, + ) -> Result<()> { + trace!(target: "script", "resuming single deployment"); + + let fork_url = script_config + .evm_opts + .fork_url + .as_deref() + .ok_or_else(|| eyre::eyre!("Missing `--fork-url` field."))?; + let provider = Arc::new(try_get_http_provider(fork_url)?); + + let chain = provider.get_chainid().await?.as_u64(); + verify.set_chain(&script_config.config, chain.into()); + + let broadcasted = self.broadcast || self.resume; + let mut deployment_sequence = match ScriptSequence::load( + &script_config.config, + &self.sig, + script_config.target_contract(), + chain, + broadcasted, + ) { + Ok(seq) => seq, + // If the script was simulated, but there was no attempt to broadcast yet, + // try to read the script sequence from the `dry-run/` folder + Err(_) if broadcasted => ScriptSequence::load( + &script_config.config, + &self.sig, + script_config.target_contract(), + chain, + false, + )?, + Err(err) => eyre::bail!(err), + }; + + receipts::wait_for_pending(provider, &mut deployment_sequence).await?; + + if self.resume { + self.send_transactions(&mut deployment_sequence, fork_url, &result.script_wallets) + .await?; + } + + if self.verify { + // We might have predeployed libraries from the broadcasting, so we need to + // relink the contracts with them, since their mapping is + // not included in the solc cache files. + let BuildOutput { highlevel_known_contracts, .. } = self.link( + project, + default_known_contracts, + Libraries::parse(&deployment_sequence.libraries)?, + script_config.config.sender.to_alloy(), // irrelevant, since we're not creating any + 0, // irrelevant, since we're not creating any + )?; + + verify.known_contracts = flatten_contracts(&highlevel_known_contracts, false); + + deployment_sequence.verify_contracts(&script_config.config, verify).await?; + } + + Ok(()) + } + + /// Reruns the execution with a new sender and relinks the libraries accordingly + async fn rerun_with_new_deployer( + &mut self, + project: Project, + script_config: &mut ScriptConfig, + new_sender: Address, + first_run_result: &mut ScriptResult, + default_known_contracts: ArtifactContracts, + ) -> Result<(Libraries, ArtifactContracts)> { + // if we had a new sender that requires relinking, we need to + // get the nonce mainnet for accurate addresses for predeploy libs + let nonce = foundry_utils::next_nonce( + new_sender, + script_config.evm_opts.fork_url.as_ref().ok_or_else(|| { + eyre::eyre!("You must provide an RPC URL (see --fork-url) when broadcasting.") + })?, + None, + ) + .await?; + script_config.sender_nonce = nonce; + + let BuildOutput { + libraries, contract, highlevel_known_contracts, predeploy_libraries, .. + } = self.link( + project, + default_known_contracts, + script_config.config.parsed_libraries()?, + new_sender, + nonce, + )?; + + let mut txs = self.create_deploy_transactions( + new_sender, + nonce, + &predeploy_libraries, + &script_config.evm_opts.fork_url, + ); + + let result = + self.execute(script_config, contract, new_sender, &predeploy_libraries).await?; + + if let Some(new_txs) = &result.transactions { + for new_tx in new_txs.iter() { + txs.push_back(BroadcastableTransaction { + rpc: new_tx.rpc.clone(), + transaction: TypedTransaction::Legacy(new_tx.transaction.clone().into()), + }); + } + } + + *first_run_result = result; + first_run_result.transactions = Some(txs); + + Ok((libraries, highlevel_known_contracts)) + } + + /// In case the user has loaded *only* one private-key, we can assume that he's using it as the + /// `--sender` + fn maybe_load_private_key(&mut self, script_config: &mut ScriptConfig) -> Result<()> { + if let Some(ref private_key) = self.wallets.private_key { + self.wallets.private_keys = Some(vec![private_key.clone()]); + } + if let Some(wallets) = self.wallets.private_keys()? { + if wallets.len() == 1 { + script_config.evm_opts.sender = wallets.get(0).unwrap().address().to_alloy() + } + } + Ok(()) + } +} diff --git a/crates/zkforge/bin/cmd/script/executor.rs b/crates/zkforge/bin/cmd/script/executor.rs new file mode 100644 index 000000000..9eaec211a --- /dev/null +++ b/crates/zkforge/bin/cmd/script/executor.rs @@ -0,0 +1,333 @@ +use super::{ + artifacts::ArtifactInfo, + runner::SimulationStage, + transaction::{AdditionalContract, TransactionWithMetadata}, + *, +}; +use alloy_primitives::{Address, Bytes, U256}; +use ethers::{ + solc::artifacts::CompactContractBytecode, types::transaction::eip2718::TypedTransaction, +}; +use eyre::Result; +use foundry_cli::utils::{ensure_clean_constructor, needs_setup}; +use foundry_common::{shell, RpcUrl}; +use foundry_utils::types::ToEthers; +use futures::future::join_all; +use parking_lot::RwLock; +use std::{collections::VecDeque, sync::Arc}; +use tracing::trace; +use zkforge::{ + executor::{ + inspector::{cheatcodes::util::BroadcastableTransactions, CheatsConfig}, + Backend, ExecutorBuilder, + }, + revm::primitives::{SpecId, U256 as rU256}, + trace::{CallTraceDecoder, Traces}, + CallKind, +}; + +/// Helper alias type for the processed result of a runner onchain simulation. +type RunnerResult = (Option, Traces); + +impl ScriptArgs { + /// Locally deploys and executes the contract method that will collect all broadcastable + /// transactions. + pub async fn execute( + &self, + script_config: &mut ScriptConfig, + contract: CompactContractBytecode, + sender: Address, + predeploy_libraries: &[Bytes], + ) -> Result { + trace!(target: "script", "start executing script"); + + let CompactContractBytecode { abi, bytecode, .. } = contract; + + let abi = abi.ok_or_else(|| eyre::eyre!("no ABI found for contract"))?; + let bytecode = bytecode + .ok_or_else(|| eyre::eyre!("no bytecode found for contract"))? + .object + .into_bytes() + .ok_or_else(|| { + eyre::eyre!("expected fully linked bytecode, found unlinked bytecode") + })?; + + ensure_clean_constructor(&abi)?; + + let predeploy_libraries = + predeploy_libraries.iter().map(|b| b.0.clone().into()).collect::>(); + let mut runner = self.prepare_runner(script_config, sender, SimulationStage::Local).await; + let (address, mut result) = runner.setup( + &predeploy_libraries, + bytecode.0.into(), + needs_setup(&abi), + script_config.sender_nonce, + self.broadcast, + script_config.evm_opts.fork_url.is_none(), + )?; + + let (func, calldata) = self.get_method_and_calldata(&abi)?; + script_config.called_function = Some(func); + + // Only call the method if `setUp()` succeeded. + if result.success { + let script_result = runner.script(address, calldata.0.into())?; + + result.success &= script_result.success; + result.gas_used = script_result.gas_used; + result.logs.extend(script_result.logs); + result.traces.extend(script_result.traces); + result.debug = script_result.debug; + result.labeled_addresses.extend(script_result.labeled_addresses); + result.returned = script_result.returned; + result.script_wallets.extend(script_result.script_wallets); + result.breakpoints = script_result.breakpoints; + + match (&mut result.transactions, script_result.transactions) { + (Some(txs), Some(new_txs)) => { + txs.extend(new_txs); + } + (None, Some(new_txs)) => { + result.transactions = Some(new_txs); + } + _ => {} + } + } + + Ok(result) + } + + /// Simulates onchain state by executing a list of transactions locally and persisting their + /// state. Returns the transactions and any CREATE2 contract address created. + pub async fn onchain_simulation( + &self, + transactions: BroadcastableTransactions, + script_config: &ScriptConfig, + decoder: &CallTraceDecoder, + contracts: &ContractsByArtifact, + ) -> Result> { + trace!(target: "script", "executing onchain simulation"); + + let runners = Arc::new( + self.build_runners(script_config) + .await + .into_iter() + .map(|(rpc, runner)| (rpc, Arc::new(RwLock::new(runner)))) + .collect::>(), + ); + + if script_config.evm_opts.verbosity > 3 { + println!("=========================="); + println!("Simulated On-chain Traces:\n"); + } + + let address_to_abi: BTreeMap = decoder + .contracts + .iter() + .filter_map(|(addr, contract_id)| { + let contract_name = get_contract_name(contract_id); + if let Ok(Some((_, (abi, code)))) = + contracts.find_by_name_or_identifier(contract_name) + { + let info = ArtifactInfo { + contract_name: contract_name.to_string(), + contract_id: contract_id.to_string(), + abi, + code, + }; + return Some(((*addr).to_alloy(), info)) + } + None + }) + .collect(); + + let mut final_txs = VecDeque::new(); + + // Executes all transactions from the different forks concurrently. + let futs = + transactions + .into_iter() + .map(|transaction| async { + let mut runner = runners + .get(transaction.rpc.as_ref().expect("to have been filled already.")) + .expect("to have been built.") + .write(); + + if let TypedTransaction::Legacy(mut tx) = transaction.transaction { + let result = runner + .simulate( + tx.from.expect( + "Transaction doesn't have a `from` address at execution time", + ).to_alloy(), + tx.to.clone(), + tx.data.clone().map(|b| b.0.into()), + tx.value.map(|v| v.to_alloy()), + ) + .expect("Internal EVM error"); + + if !result.success || result.traces.is_empty() { + return Ok((None, result.traces)) + } + + let created_contracts = result + .traces + .iter() + .flat_map(|(_, traces)| { + traces.arena.iter().filter_map(|node| { + if matches!(node.kind(), CallKind::Create | CallKind::Create2) { + return Some(AdditionalContract { + opcode: node.kind(), + address: node.trace.address.to_alloy(), + init_code: node.trace.data.to_raw(), + }) + } + None + }) + }) + .collect(); + + // Simulate mining the transaction if the user passes `--slow`. + if self.slow { + runner.executor.env.block.number += rU256::from(1); + } + + let is_fixed_gas_limit = tx.gas.is_some(); + // If tx.gas is already set that means it was specified in script + if !is_fixed_gas_limit { + // We inflate the gas used by the user specified percentage + tx.gas = Some( + U256::from(result.gas_used * self.gas_estimate_multiplier / 100) + .to_ethers(), + ); + } else { + println!("Gas limit was set in script to {:}", tx.gas.unwrap()); + } + + let tx = TransactionWithMetadata::new( + tx.into(), + transaction.rpc, + &result, + &address_to_abi, + decoder, + created_contracts, + is_fixed_gas_limit, + )?; + + Ok((Some(tx), result.traces)) + } else { + unreachable!() + } + }) + .collect::>(); + + let mut abort = false; + for res in join_all(futs).await { + // type hint + let res: Result = res; + + let (tx, mut traces) = res?; + + // Transaction will be `None`, if execution didn't pass. + if tx.is_none() || script_config.evm_opts.verbosity > 3 { + // Identify all contracts created during the call. + if traces.is_empty() { + eyre::bail!( + "Forge script requires tracing enabled to collect created contracts." + ) + } + + for (_kind, trace) in &mut traces { + decoder.decode(trace).await; + println!("{trace}"); + } + } + + if let Some(tx) = tx { + final_txs.push_back(tx); + } else { + abort = true; + } + } + + if abort { + eyre::bail!("Simulated execution failed.") + } + + Ok(final_txs) + } + + /// Build the multiple runners from different forks. + async fn build_runners(&self, script_config: &ScriptConfig) -> HashMap { + let sender = script_config.evm_opts.sender; + + if !shell::verbosity().is_silent() { + eprintln!("\n## Setting up ({}) EVMs.", script_config.total_rpcs.len()); + } + + let futs = script_config + .total_rpcs + .iter() + .map(|rpc| async { + let mut script_config = script_config.clone(); + script_config.evm_opts.fork_url = Some(rpc.clone()); + + ( + rpc.clone(), + self.prepare_runner(&mut script_config, sender, SimulationStage::OnChain).await, + ) + }) + .collect::>(); + + join_all(futs).await.into_iter().collect() + } + + /// Creates the Runner that drives script execution + async fn prepare_runner( + &self, + script_config: &mut ScriptConfig, + sender: Address, + stage: SimulationStage, + ) -> ScriptRunner { + trace!("preparing script runner"); + let env = + script_config.evm_opts.evm_env().await.expect("Could not instantiate fork environment"); + + // The db backend that serves all the data. + let db = match &script_config.evm_opts.fork_url { + Some(url) => match script_config.backends.get(url) { + Some(db) => db.clone(), + None => { + let backend = Backend::spawn( + script_config.evm_opts.get_fork(&script_config.config, env.clone()), + ) + .await; + script_config.backends.insert(url.clone(), backend); + script_config.backends.get(url).unwrap().clone() + } + }, + None => { + // It's only really `None`, when we don't pass any `--fork-url`. And if so, there is + // no need to cache it, since there won't be any onchain simulation that we'd need + // to cache the backend for. + Backend::spawn(script_config.evm_opts.get_fork(&script_config.config, env.clone())) + .await + } + }; + + // We need to enable tracing to decode contract names: local or external. + let mut builder = ExecutorBuilder::new() + .inspectors(|stack| stack.trace(true)) + .spec(SpecId::LATEST) + .gas_limit(script_config.evm_opts.gas_limit()); + + if let SimulationStage::Local = stage { + builder = builder.inspectors(|stack| { + stack.debug(self.debug).cheatcodes( + CheatsConfig::new(&script_config.config, &script_config.evm_opts).into(), + ) + }); + } + + ScriptRunner::new(builder.build(env, db), script_config.evm_opts.initial_balance, sender) + } +} diff --git a/crates/zkforge/bin/cmd/script/mod.rs b/crates/zkforge/bin/cmd/script/mod.rs new file mode 100644 index 000000000..205a1da4d --- /dev/null +++ b/crates/zkforge/bin/cmd/script/mod.rs @@ -0,0 +1,993 @@ +use self::{build::BuildOutput, runner::ScriptRunner}; +use super::{build::BuildArgs, retry::RetryArgs}; +use alloy_primitives::{Address, Bytes, U256}; +use clap::{Parser, ValueHint}; +use dialoguer::Confirm; +use ethers::{ + abi::{Abi, Function, HumanReadableParser}, + prelude::{ + artifacts::{ContractBytecodeSome, Libraries}, + ArtifactId, Project, + }, + providers::{Http, Middleware}, + signers::LocalWallet, + solc::contracts::ArtifactContracts, + types::{ + transaction::eip2718::TypedTransaction, Chain, Log, NameOrAddress, TransactionRequest, + }, +}; +use eyre::{ContextCompat, Result, WrapErr}; +use foundry_cli::opts::MultiWallet; +use foundry_common::{ + abi::{encode_args, format_token}, + contracts::get_contract_name, + errors::UnlinkedByteCode, + evm::{Breakpoints, EvmArgs}, + shell, ContractsByArtifact, RpcUrl, CONTRACT_MAX_SIZE, SELECTOR_LEN, +}; +use foundry_config::{ + figment, + figment::{ + value::{Dict, Map}, + Metadata, Profile, Provider, + }, + Config, +}; +use foundry_evm::{ + decode, + executor::inspector::{ + cheatcodes::{util::BroadcastableTransactions, BroadcastableTransaction}, + DEFAULT_CREATE2_DEPLOYER, + }, +}; +use foundry_utils::types::{ToAlloy, ToEthers}; +use futures::future; +use serde::{Deserialize, Serialize}; +use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; +use yansi::Paint; +use zkforge::{ + debug::DebugArena, + decode::decode_console_logs, + executor::{opts::EvmOpts, Backend}, + trace::{ + identifier::{EtherscanIdentifier, LocalTraceIdentifier, SignaturesIdentifier}, + CallTraceDecoder, CallTraceDecoderBuilder, RawOrDecodedCall, RawOrDecodedReturnData, + TraceKind, Traces, + }, + CallKind, +}; + +mod artifacts; +mod broadcast; +mod build; +mod cmd; +mod executor; +mod multi; +mod providers; +mod receipts; +mod runner; +mod sequence; +pub mod transaction; +mod verify; + +pub use transaction::TransactionWithMetadata; + +/// List of Chains that support Shanghai. +static SHANGHAI_ENABLED_CHAINS: &[Chain] = &[ + // Ethereum Mainnet + Chain::Mainnet, + // Goerli + Chain::Goerli, + // Sepolia + Chain::Sepolia, +]; + +// Loads project's figment and merges the build cli arguments into it +foundry_config::merge_impl_figment_convert!(ScriptArgs, opts, evm_opts); + +/// CLI arguments for `forge script`. +#[derive(Debug, Clone, Parser, Default)] +pub struct ScriptArgs { + /// The contract you want to run. Either the file path or contract name. + /// + /// If multiple contracts exist in the same file you must specify the target contract with + /// --target-contract. + #[clap(value_hint = ValueHint::FilePath)] + pub path: String, + + /// Arguments to pass to the script function. + pub args: Vec, + + /// The name of the contract you want to run. + #[clap(long, visible_alias = "tc", value_name = "CONTRACT_NAME")] + pub target_contract: Option, + + /// The signature of the function you want to call in the contract, or raw calldata. + #[clap( + long, + short, + default_value = "run()", + value_parser = foundry_common::clap_helpers::strip_0x_prefix + )] + pub sig: String, + + /// Max priority fee per gas for EIP1559 transactions. + #[clap( + long, + env = "ETH_PRIORITY_GAS_PRICE", + value_parser = foundry_cli::utils::alloy_parse_ether_value, + value_name = "PRICE" + )] + pub priority_gas_price: Option, + + /// Use legacy transactions instead of EIP1559 ones. + /// + /// This is auto-enabled for common networks without EIP1559. + #[clap(long)] + pub legacy: bool, + + /// Broadcasts the transactions. + #[clap(long)] + pub broadcast: bool, + + /// Skips on-chain simulation. + #[clap(long)] + pub skip_simulation: bool, + + /// Relative percentage to multiply gas estimates by. + #[clap(long, short, default_value = "130")] + pub gas_estimate_multiplier: u64, + + /// Send via `eth_sendTransaction` using the `--from` argument or `$ETH_FROM` as sender + #[clap( + long, + requires = "sender", + conflicts_with_all = &["private_key", "private_keys", "froms", "ledger", "trezor", "aws"], + )] + pub unlocked: bool, + + /// Resumes submitting transactions that failed or timed-out previously. + /// + /// It DOES NOT simulate the script again and it expects nonces to have remained the same. + /// + /// Example: If transaction N has a nonce of 22, then the account should have a nonce of 22, + /// otherwise it fails. + #[clap(long)] + pub resume: bool, + + /// If present, --resume or --verify will be assumed to be a multi chain deployment. + #[clap(long)] + pub multi: bool, + + /// Open the script in the debugger. + /// + /// Takes precedence over broadcast. + #[clap(long)] + pub debug: bool, + + /// Makes sure a transaction is sent, + /// only after its previous one has been confirmed and succeeded. + #[clap(long)] + pub slow: bool, + + /// Disables interactive prompts that might appear when deploying big contracts. + /// + /// For more info on the contract size limit, see EIP-170: + #[clap(long)] + pub non_interactive: bool, + + /// The Etherscan (or equivalent) API key + #[clap(long, env = "ETHERSCAN_API_KEY", value_name = "KEY")] + pub etherscan_api_key: Option, + + /// Verifies all the contracts found in the receipts of a script, if any. + #[clap(long)] + pub verify: bool, + + /// Output results in JSON format. + #[clap(long)] + pub json: bool, + + /// Gas price for legacy transactions, or max fee per gas for EIP1559 transactions. + #[clap( + long, + env = "ETH_GAS_PRICE", + value_parser = foundry_cli::utils::alloy_parse_ether_value, + value_name = "PRICE", + )] + pub with_gas_price: Option, + + #[clap(flatten)] + pub opts: BuildArgs, + + #[clap(flatten)] + pub wallets: MultiWallet, + + #[clap(flatten)] + pub evm_opts: EvmArgs, + + #[clap(flatten)] + pub verifier: super::verify::VerifierArgs, + + #[clap(flatten)] + pub retry: RetryArgs, +} + +// === impl ScriptArgs === + +impl ScriptArgs { + pub fn decode_traces( + &self, + script_config: &ScriptConfig, + result: &mut ScriptResult, + known_contracts: &ContractsByArtifact, + ) -> Result { + let verbosity = script_config.evm_opts.verbosity; + let mut etherscan_identifier = EtherscanIdentifier::new( + &script_config.config, + script_config.evm_opts.get_remote_chain_id(), + )?; + + let mut local_identifier = LocalTraceIdentifier::new(known_contracts); + let mut decoder = CallTraceDecoderBuilder::new() + .with_labels( + result.labeled_addresses.iter().map(|(a, s)| ((*a).to_ethers(), s.clone())), + ) + .with_verbosity(verbosity) + .with_signature_identifier(SignaturesIdentifier::new( + Config::foundry_cache_dir(), + script_config.config.offline, + )?) + .build(); + + // Decoding traces using etherscan is costly as we run into rate limits, + // causing scripts to run for a very long time unnecessarily. + // Therefore, we only try and use etherscan if the user has provided an API key. + let should_use_etherscan_traces = script_config.config.etherscan_api_key.is_some(); + + for (_, trace) in &mut result.traces { + decoder.identify(trace, &mut local_identifier); + if should_use_etherscan_traces { + decoder.identify(trace, &mut etherscan_identifier); + } + } + Ok(decoder) + } + + pub fn get_returns( + &self, + script_config: &ScriptConfig, + returned: &Bytes, + ) -> Result> { + let func = script_config.called_function.as_ref().expect("There should be a function."); + let mut returns = HashMap::new(); + + match func.decode_output(returned) { + Ok(decoded) => { + for (index, (token, output)) in decoded.iter().zip(&func.outputs).enumerate() { + let internal_type = output.internal_type.as_deref().unwrap_or("unknown"); + + let label = if !output.name.is_empty() { + output.name.to_string() + } else { + index.to_string() + }; + + returns.insert( + label, + NestedValue { + internal_type: internal_type.to_string(), + value: format_token(token), + }, + ); + } + } + Err(_) => { + shell::println(format!("{returned:?}"))?; + } + } + + Ok(returns) + } + + pub async fn show_traces( + &self, + script_config: &ScriptConfig, + decoder: &CallTraceDecoder, + result: &mut ScriptResult, + ) -> Result<()> { + let verbosity = script_config.evm_opts.verbosity; + let func = script_config.called_function.as_ref().expect("There should be a function."); + + if !result.success || verbosity > 3 { + if result.traces.is_empty() { + eyre::bail!("Unexpected error: No traces despite verbosity level. Please report this as a bug: https://github.com/foundry-rs/foundry/issues/new?assignees=&labels=T-bug&template=BUG-FORM.yml"); + } + + shell::println("Traces:")?; + for (kind, trace) in &mut result.traces { + let should_include = match kind { + TraceKind::Setup => verbosity >= 5, + TraceKind::Execution => verbosity > 3, + _ => false, + } || !result.success; + + if should_include { + decoder.decode(trace).await; + shell::println(format!("{trace}"))?; + } + } + shell::println(String::new())?; + } + + if result.success { + shell::println(format!("{}", Paint::green("Script ran successfully.")))?; + } + + if script_config.evm_opts.fork_url.is_none() { + shell::println(format!("Gas used: {}", result.gas_used))?; + } + + if result.success && !result.returned.is_empty() { + shell::println("\n== Return ==")?; + match func.decode_output(&result.returned) { + Ok(decoded) => { + for (index, (token, output)) in decoded.iter().zip(&func.outputs).enumerate() { + let internal_type = output.internal_type.as_deref().unwrap_or("unknown"); + + let label = if !output.name.is_empty() { + output.name.to_string() + } else { + index.to_string() + }; + shell::println(format!( + "{}: {internal_type} {}", + label.trim_end(), + format_token(token) + ))?; + } + } + Err(_) => { + shell::println(format!("{:x?}", (&result.returned)))?; + } + } + } + + let console_logs = decode_console_logs(&result.logs); + if !console_logs.is_empty() { + shell::println("\n== Logs ==")?; + for log in console_logs { + shell::println(format!(" {log}"))?; + } + } + + if !result.success { + let revert_msg = decode::decode_revert(&result.returned[..], None, None) + .map(|err| format!("{err}\n")) + .unwrap_or_else(|_| "Script failed.\n".to_string()); + + eyre::bail!("{}", Paint::red(revert_msg)); + } + + Ok(()) + } + + pub fn show_json(&self, script_config: &ScriptConfig, result: &ScriptResult) -> Result<()> { + let returns = self.get_returns(script_config, &result.returned)?; + + let console_logs = decode_console_logs(&result.logs); + let output = JsonResult { logs: console_logs, gas_used: result.gas_used, returns }; + let j = serde_json::to_string(&output)?; + shell::println(j)?; + + Ok(()) + } + + /// It finds the deployer from the running script and uses it to predeploy libraries. + /// + /// If there are multiple candidate addresses, it skips everything and lets `--sender` deploy + /// them instead. + fn maybe_new_sender( + &self, + evm_opts: &EvmOpts, + transactions: Option<&BroadcastableTransactions>, + predeploy_libraries: &[Bytes], + ) -> Result> { + let mut new_sender = None; + + if let Some(txs) = transactions { + // If the user passed a `--sender` don't check anything. + if !predeploy_libraries.is_empty() && self.evm_opts.sender.is_none() { + for tx in txs.iter() { + match &tx.transaction { + TypedTransaction::Legacy(tx) => { + if tx.to.is_none() { + let sender = tx.from.expect("no sender").to_alloy(); + if let Some(ns) = new_sender { + if sender != ns { + shell::println("You have more than one deployer who could predeploy libraries. Using `--sender` instead.")?; + return Ok(None) + } + } else if sender != evm_opts.sender { + new_sender = Some(sender); + } + } + } + _ => unreachable!(), + } + } + } + } + Ok(new_sender) + } + + /// Helper for building the transactions for any libraries that need to be deployed ahead of + /// linking + fn create_deploy_transactions( + &self, + from: Address, + nonce: u64, + data: &[Bytes], + fork_url: &Option, + ) -> BroadcastableTransactions { + data.iter() + .enumerate() + .map(|(i, bytes)| BroadcastableTransaction { + rpc: fork_url.clone(), + transaction: TypedTransaction::Legacy(TransactionRequest { + from: Some(from.to_ethers()), + data: Some(bytes.clone().0.into()), + nonce: Some(ethers::types::U256::from(nonce + i as u64)), + ..Default::default() + }), + }) + .collect() + } + + /// Returns the Function and calldata based on the signature + /// + /// If the `sig` is a valid human-readable function we find the corresponding function in the + /// `abi` If the `sig` is valid hex, we assume it's calldata and try to find the + /// corresponding function by matching the selector, first 4 bytes in the calldata. + /// + /// Note: We assume that the `sig` is already stripped of its prefix, See [`ScriptArgs`] + pub fn get_method_and_calldata(&self, abi: &Abi) -> Result<(Function, Bytes)> { + let (func, data) = if let Ok(func) = HumanReadableParser::parse_function(&self.sig) { + ( + abi.functions() + .find(|&abi_func| abi_func.short_signature() == func.short_signature()) + .wrap_err(format!( + "Function `{}` is not implemented in your script.", + self.sig + ))?, + encode_args(&func, &self.args)?.into(), + ) + } else { + let decoded = hex::decode(&self.sig).wrap_err("Invalid hex calldata")?; + let selector = &decoded[..SELECTOR_LEN]; + ( + abi.functions().find(|&func| selector == &func.short_signature()[..]).ok_or_else( + || { + eyre::eyre!( + "Function selector `{}` not found in the ABI", + hex::encode(selector) + ) + }, + )?, + decoded.into(), + ) + }; + + Ok((func.clone(), data)) + } + + /// Checks if the transaction is a deployment with either a size above the `CONTRACT_MAX_SIZE` + /// or specified `code_size_limit`. + /// + /// If `self.broadcast` is enabled, it asks confirmation of the user. Otherwise, it just warns + /// the user. + fn check_contract_sizes( + &self, + result: &ScriptResult, + known_contracts: &BTreeMap, + ) -> Result<()> { + // (name, &init, &deployed)[] + let mut bytecodes: Vec<(String, &[u8], &[u8])> = vec![]; + + // From artifacts + for (artifact, bytecode) in known_contracts.iter() { + if bytecode.bytecode.object.is_unlinked() { + return Err(UnlinkedByteCode::Bytecode(artifact.identifier()).into()) + } + let init_code = bytecode.bytecode.object.as_bytes().unwrap(); + // Ignore abstract contracts + if let Some(ref deployed_code) = bytecode.deployed_bytecode.bytecode { + if deployed_code.object.is_unlinked() { + return Err(UnlinkedByteCode::DeployedBytecode(artifact.identifier()).into()) + } + let deployed_code = deployed_code.object.as_bytes().unwrap(); + bytecodes.push((artifact.name.clone(), init_code, deployed_code)); + } + } + + // From traces + let create_nodes = result.traces.iter().flat_map(|(_, traces)| { + traces + .arena + .iter() + .filter(|node| matches!(node.kind(), CallKind::Create | CallKind::Create2)) + }); + let mut unknown_c = 0usize; + for node in create_nodes { + // Calldata == init code + if let RawOrDecodedCall::Raw(ref init_code) = node.trace.data { + // Output is the runtime code + if let RawOrDecodedReturnData::Raw(ref deployed_code) = node.trace.output { + // Only push if it was not present already + if !bytecodes.iter().any(|(_, b, _)| *b == init_code.as_ref()) { + bytecodes.push((format!("Unknown{unknown_c}"), init_code, deployed_code)); + unknown_c += 1; + } + continue + } + } + // Both should be raw and not decoded since it's just bytecode + eyre::bail!("Create node returned decoded data: {:?}", node); + } + + let mut prompt_user = false; + let max_size = match self.evm_opts.env.code_size_limit { + Some(size) => size, + None => CONTRACT_MAX_SIZE, + }; + + for (data, to) in result.transactions.iter().flat_map(|txes| { + txes.iter().filter_map(|tx| { + tx.transaction + .data() + .filter(|data| data.len() > max_size) + .map(|data| (data, tx.transaction.to())) + }) + }) { + let mut offset = 0; + + // Find if it's a CREATE or CREATE2. Otherwise, skip transaction. + if let Some(NameOrAddress::Address(to)) = to { + if to.to_alloy() == DEFAULT_CREATE2_DEPLOYER { + // Size of the salt prefix. + offset = 32; + } + } else if to.is_some() { + continue + } + + // Find artifact with a deployment code same as the data. + if let Some((name, _, deployed_code)) = + bytecodes.iter().find(|(_, init_code, _)| *init_code == &data[offset..]) + { + let deployment_size = deployed_code.len(); + + if deployment_size > max_size { + prompt_user = self.broadcast; + shell::println(format!( + "{}", + Paint::red(format!( + "`{name}` is above the contract size limit ({deployment_size} > {max_size})." + )) + ))?; + } + } + } + + // Only prompt if we're broadcasting and we've not disabled interactivity. + if prompt_user && + !self.non_interactive && + !Confirm::new().with_prompt("Do you wish to continue?".to_string()).interact()? + { + eyre::bail!("User canceled the script."); + } + + Ok(()) + } +} + +impl Provider for ScriptArgs { + fn metadata(&self) -> Metadata { + Metadata::named("Script Args Provider") + } + + fn data(&self) -> Result, figment::Error> { + let mut dict = Dict::default(); + if let Some(ref etherscan_api_key) = self.etherscan_api_key { + dict.insert( + "etherscan_api_key".to_string(), + figment::value::Value::from(etherscan_api_key.to_string()), + ); + } + Ok(Map::from([(Config::selected_profile(), dict)])) + } +} + +#[derive(Default)] +pub struct ScriptResult { + pub success: bool, + pub logs: Vec, + pub traces: Traces, + pub debug: Option>, + pub gas_used: u64, + pub labeled_addresses: BTreeMap, + pub transactions: Option, + pub returned: Bytes, + pub address: Option
, + pub script_wallets: Vec, + pub breakpoints: Breakpoints, +} + +#[derive(Serialize, Deserialize)] +pub struct JsonResult { + pub logs: Vec, + pub gas_used: u64, + pub returns: HashMap, +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct NestedValue { + pub internal_type: String, + pub value: String, +} + +#[derive(Clone, Debug, Default)] +pub struct ScriptConfig { + pub config: Config, + pub evm_opts: EvmOpts, + pub sender_nonce: u64, + /// Maps a rpc url to a backend + pub backends: HashMap, + /// Script target contract + pub target_contract: Option, + /// Function called by the script + pub called_function: Option, + /// Unique list of rpc urls present + pub total_rpcs: HashSet, + /// If true, one of the transactions did not have a rpc + pub missing_rpc: bool, + /// Should return some debug information + pub debug: bool, +} + +impl ScriptConfig { + fn collect_rpcs(&mut self, txs: &BroadcastableTransactions) { + self.missing_rpc = txs.iter().any(|tx| tx.rpc.is_none()); + + self.total_rpcs + .extend(txs.iter().filter_map(|tx| tx.rpc.as_ref().cloned()).collect::>()); + + if let Some(rpc) = &self.evm_opts.fork_url { + self.total_rpcs.insert(rpc.clone()); + } + } + + fn has_multiple_rpcs(&self) -> bool { + self.total_rpcs.len() > 1 + } + + /// Certain features are disabled for multi chain deployments, and if tried, will return + /// error. [library support] + fn check_multi_chain_constraints(&self, libraries: &Libraries) -> Result<()> { + if self.has_multiple_rpcs() || (self.missing_rpc && !self.total_rpcs.is_empty()) { + shell::eprintln(format!( + "{}", + Paint::yellow( + "Multi chain deployment is still under development. Use with caution." + ) + ))?; + if !libraries.libs.is_empty() { + eyre::bail!( + "Multi chain deployment does not support library linking at the moment." + ) + } + } + Ok(()) + } + + /// Returns the script target contract + fn target_contract(&self) -> &ArtifactId { + self.target_contract.as_ref().expect("should exist after building") + } + + /// Checks if the RPCs used point to chains that support EIP-3855. + /// If not, warns the user. + async fn check_shanghai_support(&self) -> Result<()> { + let chain_ids = self.total_rpcs.iter().map(|rpc| async move { + if let Ok(provider) = ethers::providers::Provider::::try_from(rpc) { + match provider.get_chainid().await { + Ok(chain_id) => match TryInto::::try_into(chain_id) { + Ok(chain) => return Some((SHANGHAI_ENABLED_CHAINS.contains(&chain), chain)), + Err(_) => return None, + }, + Err(_) => return None, + } + } + None + }); + + let chain_ids: Vec<_> = future::join_all(chain_ids).await.into_iter().flatten().collect(); + + let chain_id_unsupported = chain_ids.iter().any(|(supported, _)| !supported); + + // At least one chain ID is unsupported, therefore we print the message. + if chain_id_unsupported { + let msg = format!( + r#" +EIP-3855 is not supported in one or more of the RPCs used. +Unsupported Chain IDs: {}. +Contracts deployed with a Solidity version equal or higher than 0.8.20 might not work properly. +For more information, please see https://eips.ethereum.org/EIPS/eip-3855"#, + chain_ids + .iter() + .filter(|(supported, _)| !supported) + .map(|(_, chain)| format!("{}", *chain as u64)) + .collect::>() + .join(", ") + ); + shell::println(Paint::yellow(msg))?; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use foundry_cli::utils::LoadConfig; + use foundry_config::UnresolvedEnvVarError; + use foundry_test_utils::tempfile::tempdir; + use std::fs; + + #[test] + fn can_parse_sig() { + let args: ScriptArgs = ScriptArgs::parse_from([ + "foundry-cli", + "Contract.sol", + "--sig", + "0x522bb704000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfFFb92266", + ]); + assert_eq!( + args.sig, + "522bb704000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfFFb92266" + ); + } + + #[test] + fn can_parse_unlocked() { + let args: ScriptArgs = ScriptArgs::parse_from([ + "foundry-cli", + "Contract.sol", + "--sender", + "0x4e59b44847b379578588920ca78fbf26c0b4956c", + "--unlocked", + ]); + assert!(args.unlocked); + + let key = U256::ZERO; + let args = ScriptArgs::try_parse_from([ + "foundry-cli", + "Contract.sol", + "--sender", + "0x4e59b44847b379578588920ca78fbf26c0b4956c", + "--unlocked", + "--private-key", + key.to_string().as_str(), + ]); + assert!(args.is_err()); + } + + #[test] + fn can_merge_script_config() { + let args: ScriptArgs = ScriptArgs::parse_from([ + "foundry-cli", + "Contract.sol", + "--etherscan-api-key", + "goerli", + ]); + let config = args.load_config(); + assert_eq!(config.etherscan_api_key, Some("goerli".to_string())); + } + + #[test] + fn can_parse_verifier_url() { + let args: ScriptArgs = ScriptArgs::parse_from([ + "foundry-cli", + "script", + "script/Test.s.sol:TestScript", + "--fork-url", + "http://localhost:8545", + "--verifier-url", + "http://localhost:3000/api/verify", + "--etherscan-api-key", + "blacksmith", + "--broadcast", + "--verify", + "-vvvv", + ]); + assert_eq!( + args.verifier.verifier_url, + Some("http://localhost:3000/api/verify".to_string()) + ); + } + + #[test] + fn can_extract_code_size_limit() { + let args: ScriptArgs = ScriptArgs::parse_from([ + "foundry-cli", + "script", + "script/Test.s.sol:TestScript", + "--fork-url", + "http://localhost:8545", + "--broadcast", + "--code-size-limit", + "50000", + ]); + assert_eq!(args.evm_opts.env.code_size_limit, Some(50000)); + } + + #[test] + fn can_extract_script_etherscan_key() { + let temp = tempdir().unwrap(); + let root = temp.path(); + + let config = r#" + [profile.default] + etherscan_api_key = "mumbai" + + [etherscan] + mumbai = { key = "https://etherscan-mumbai.com/" } + "#; + + let toml_file = root.join(Config::FILE_NAME); + fs::write(toml_file, config).unwrap(); + let args: ScriptArgs = ScriptArgs::parse_from([ + "foundry-cli", + "Contract.sol", + "--etherscan-api-key", + "mumbai", + "--root", + root.as_os_str().to_str().unwrap(), + ]); + + let config = args.load_config(); + let mumbai = config.get_etherscan_api_key(Some(ethers::types::Chain::PolygonMumbai)); + assert_eq!(mumbai, Some("https://etherscan-mumbai.com/".to_string())); + } + + #[test] + fn can_extract_script_rpc_alias() { + let temp = tempdir().unwrap(); + let root = temp.path(); + + let config = r#" + [profile.default] + + [rpc_endpoints] + polygonMumbai = "https://polygon-mumbai.g.alchemy.com/v2/${_CAN_EXTRACT_RPC_ALIAS}" + "#; + + let toml_file = root.join(Config::FILE_NAME); + fs::write(toml_file, config).unwrap(); + let args: ScriptArgs = ScriptArgs::parse_from([ + "foundry-cli", + "DeployV1", + "--rpc-url", + "polygonMumbai", + "--root", + root.as_os_str().to_str().unwrap(), + ]); + + let err = args.load_config_and_evm_opts().unwrap_err(); + + assert!(err.downcast::().is_ok()); + + std::env::set_var("_CAN_EXTRACT_RPC_ALIAS", "123456"); + let (config, evm_opts) = args.load_config_and_evm_opts().unwrap(); + assert_eq!(config.eth_rpc_url, Some("polygonMumbai".to_string())); + assert_eq!( + evm_opts.fork_url, + Some("https://polygon-mumbai.g.alchemy.com/v2/123456".to_string()) + ); + } + + #[test] + fn can_extract_script_rpc_and_etherscan_alias() { + let temp = tempdir().unwrap(); + let root = temp.path(); + + let config = r#" + [profile.default] + + [rpc_endpoints] + mumbai = "https://polygon-mumbai.g.alchemy.com/v2/${_EXTRACT_RPC_ALIAS}" + + [etherscan] + mumbai = { key = "${_POLYSCAN_API_KEY}", chain = 80001, url = "https://api-testnet.polygonscan.com/" } + "#; + + let toml_file = root.join(Config::FILE_NAME); + fs::write(toml_file, config).unwrap(); + let args: ScriptArgs = ScriptArgs::parse_from([ + "foundry-cli", + "DeployV1", + "--rpc-url", + "mumbai", + "--etherscan-api-key", + "mumbai", + "--root", + root.as_os_str().to_str().unwrap(), + ]); + let err = args.load_config_and_evm_opts().unwrap_err(); + + assert!(err.downcast::().is_ok()); + + std::env::set_var("_EXTRACT_RPC_ALIAS", "123456"); + std::env::set_var("_POLYSCAN_API_KEY", "polygonkey"); + let (config, evm_opts) = args.load_config_and_evm_opts().unwrap(); + assert_eq!(config.eth_rpc_url, Some("mumbai".to_string())); + assert_eq!( + evm_opts.fork_url, + Some("https://polygon-mumbai.g.alchemy.com/v2/123456".to_string()) + ); + let etherscan = config.get_etherscan_api_key(Some(80001u64)); + assert_eq!(etherscan, Some("polygonkey".to_string())); + let etherscan = config.get_etherscan_api_key(Option::::None); + assert_eq!(etherscan, Some("polygonkey".to_string())); + } + + #[test] + fn can_extract_script_rpc_and_sole_etherscan_alias() { + let temp = tempdir().unwrap(); + let root = temp.path(); + + let config = r#" + [profile.default] + + [rpc_endpoints] + mumbai = "https://polygon-mumbai.g.alchemy.com/v2/${_SOLE_EXTRACT_RPC_ALIAS}" + + [etherscan] + mumbai = { key = "${_SOLE_POLYSCAN_API_KEY}" } + "#; + + let toml_file = root.join(Config::FILE_NAME); + fs::write(toml_file, config).unwrap(); + let args: ScriptArgs = ScriptArgs::parse_from([ + "foundry-cli", + "DeployV1", + "--rpc-url", + "mumbai", + "--root", + root.as_os_str().to_str().unwrap(), + ]); + let err = args.load_config_and_evm_opts().unwrap_err(); + + assert!(err.downcast::().is_ok()); + + std::env::set_var("_SOLE_EXTRACT_RPC_ALIAS", "123456"); + std::env::set_var("_SOLE_POLYSCAN_API_KEY", "polygonkey"); + let (config, evm_opts) = args.load_config_and_evm_opts().unwrap(); + assert_eq!( + evm_opts.fork_url, + Some("https://polygon-mumbai.g.alchemy.com/v2/123456".to_string()) + ); + let etherscan = config.get_etherscan_api_key(Some(80001u64)); + assert_eq!(etherscan, Some("polygonkey".to_string())); + let etherscan = config.get_etherscan_api_key(Option::::None); + assert_eq!(etherscan, Some("polygonkey".to_string())); + } + + // + #[test] + fn test_5923() { + let args: ScriptArgs = + ScriptArgs::parse_from(["foundry-cli", "DeployV1", "--priority-gas-price", "100"]); + assert!(args.priority_gas_price.is_some()); + } +} diff --git a/crates/zkforge/bin/cmd/script/multi.rs b/crates/zkforge/bin/cmd/script/multi.rs new file mode 100644 index 000000000..04862b838 --- /dev/null +++ b/crates/zkforge/bin/cmd/script/multi.rs @@ -0,0 +1,182 @@ +use super::{ + receipts, + sequence::{ScriptSequence, DRY_RUN_DIR}, + verify::VerifyBundle, + ScriptArgs, +}; +use ethers::{ + prelude::{artifacts::Libraries, ArtifactId}, + signers::LocalWallet, +}; +use eyre::{ContextCompat, Result, WrapErr}; +use foundry_cli::utils::now; +use foundry_common::{fs, get_http_provider}; +use foundry_config::Config; +use futures::future::join_all; +use serde::{Deserialize, Serialize}; +use std::{ + io::{BufWriter, Write}, + path::{Path, PathBuf}, + sync::Arc, +}; +use tracing::log::trace; + +/// Holds the sequences of multiple chain deployments. +#[derive(Deserialize, Serialize, Clone, Default)] +pub struct MultiChainSequence { + pub deployments: Vec, + pub path: PathBuf, + pub timestamp: u64, +} + +impl Drop for MultiChainSequence { + fn drop(&mut self) { + self.deployments.iter_mut().for_each(|sequence| sequence.sort_receipts()); + self.save().expect("could not save multi deployment sequence"); + } +} + +impl MultiChainSequence { + pub fn new( + deployments: Vec, + sig: &str, + target: &ArtifactId, + log_folder: &Path, + broadcasted: bool, + ) -> Result { + let path = + MultiChainSequence::get_path(&log_folder.join("multi"), sig, target, broadcasted)?; + + Ok(MultiChainSequence { deployments, path, timestamp: now().as_secs() }) + } + + /// Saves to ./broadcast/multi/contract_filename[-timestamp]/sig.json + pub fn get_path( + out: &Path, + sig: &str, + target: &ArtifactId, + broadcasted: bool, + ) -> Result { + let mut out = out.to_path_buf(); + + if !broadcasted { + out.push(DRY_RUN_DIR); + } + + let target_fname = target + .source + .file_name() + .wrap_err_with(|| format!("No filename for {:?}", target.source))? + .to_string_lossy(); + out.push(format!("{target_fname}-latest")); + + fs::create_dir_all(&out)?; + + let filename = sig + .split_once('(') + .wrap_err_with(|| format!("Failed to compute file name: Signature {sig} is invalid."))? + .0; + out.push(format!("{filename}.json")); + + Ok(out) + } + + /// Loads the sequences for the multi chain deployment. + pub fn load(log_folder: &Path, sig: &str, target: &ArtifactId) -> Result { + let path = MultiChainSequence::get_path(&log_folder.join("multi"), sig, target, true)?; + ethers::solc::utils::read_json_file(path).wrap_err("Multi-chain deployment not found.") + } + + /// Saves the transactions as file if it's a standalone deployment. + pub fn save(&mut self) -> Result<()> { + self.timestamp = now().as_secs(); + + //../Contract-latest/run.json + let mut writer = BufWriter::new(fs::create_file(&self.path)?); + serde_json::to_writer_pretty(&mut writer, &self)?; + writer.flush()?; + + //../Contract-[timestamp]/run.json + let path = self.path.to_string_lossy(); + let file = PathBuf::from(&path.replace("-latest", &format!("-{}", self.timestamp))); + fs::create_dir_all(file.parent().unwrap())?; + fs::copy(&self.path, &file)?; + + println!("\nTransactions saved to: {}\n", self.path.display()); + + Ok(()) + } +} + +impl ScriptArgs { + /// Given a [`MultiChainSequence`] with multiple sequences of different chains, it executes them + /// all in parallel. Supports `--resume` and `--verify`. + pub async fn multi_chain_deployment( + &self, + mut deployments: MultiChainSequence, + libraries: Libraries, + config: &Config, + script_wallets: Vec, + verify: VerifyBundle, + ) -> Result<()> { + if !libraries.is_empty() { + eyre::bail!("Libraries are currently not supported on multi deployment setups."); + } + + if self.resume { + trace!(target: "script", "resuming multi chain deployment"); + + let futs = deployments + .deployments + .iter_mut() + .map(|sequence| async move { + let provider = Arc::new(get_http_provider( + sequence.typed_transactions().first().unwrap().0.clone(), + )); + receipts::wait_for_pending(provider, sequence).await + }) + .collect::>(); + + let errors = + join_all(futs).await.into_iter().filter(|res| res.is_err()).collect::>(); + + if !errors.is_empty() { + return Err(eyre::eyre!("{errors:?}")) + } + } + + trace!(target: "script", "broadcasting multi chain deployments"); + + let futs = deployments + .deployments + .iter_mut() + .map(|sequence| async { + match self + .send_transactions( + sequence, + &sequence.typed_transactions().first().unwrap().0.clone(), + &script_wallets, + ) + .await + { + Ok(_) => { + if self.verify { + return sequence.verify_contracts(config, verify.clone()).await + } + Ok(()) + } + Err(err) => Err(err), + } + }) + .collect::>(); + + let errors = + join_all(futs).await.into_iter().filter(|res| res.is_err()).collect::>(); + + if !errors.is_empty() { + return Err(eyre::eyre!("{errors:?}")) + } + + Ok(()) + } +} diff --git a/crates/zkforge/bin/cmd/script/providers.rs b/crates/zkforge/bin/cmd/script/providers.rs new file mode 100644 index 000000000..6031a76fe --- /dev/null +++ b/crates/zkforge/bin/cmd/script/providers.rs @@ -0,0 +1,101 @@ +use alloy_primitives::U256; +use ethers::prelude::{Middleware, Provider}; +use eyre::{Result, WrapErr}; +use foundry_common::{get_http_provider, runtime_client::RuntimeClient, RpcUrl}; +use foundry_config::Chain; +use foundry_utils::types::ToAlloy; +use std::{ + collections::{hash_map::Entry, HashMap}, + ops::Deref, + sync::Arc, +}; + +/// Contains a map of RPC urls to single instances of [`ProviderInfo`]. +#[derive(Default)] +pub struct ProvidersManager { + pub inner: HashMap, +} + +impl ProvidersManager { + /// Get or initialize the RPC provider. + pub async fn get_or_init_provider( + &mut self, + rpc: &str, + is_legacy: bool, + ) -> Result<&ProviderInfo> { + Ok(match self.inner.entry(rpc.to_string()) { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => { + let info = ProviderInfo::new(rpc, is_legacy).await?; + entry.insert(info) + } + }) + } +} + +impl Deref for ProvidersManager { + type Target = HashMap; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +/// Holds related metadata to each provider RPC. +#[derive(Debug)] +pub struct ProviderInfo { + pub provider: Arc>, + pub chain: u64, + pub gas_price: GasPrice, + pub is_legacy: bool, +} + +/// Represents the outcome of a gas price request +#[derive(Debug)] +pub enum GasPrice { + Legacy(Result), + EIP1559(Result<(U256, U256)>), +} + +impl ProviderInfo { + pub async fn new(rpc: &str, mut is_legacy: bool) -> Result { + let provider = Arc::new(get_http_provider(rpc)); + let chain = provider.get_chainid().await?.as_u64(); + + if let Chain::Named(chain) = Chain::from(chain) { + is_legacy |= chain.is_legacy(); + }; + + let gas_price = if is_legacy { + GasPrice::Legacy( + provider + .get_gas_price() + .await + .wrap_err("Failed to get legacy gas price") + .map(|p| p.to_alloy()), + ) + } else { + GasPrice::EIP1559( + provider + .estimate_eip1559_fees(None) + .await + .wrap_err("Failed to get EIP-1559 fees") + .map(|p| (p.0.to_alloy(), p.1.to_alloy())), + ) + }; + + Ok(ProviderInfo { provider, chain, gas_price, is_legacy }) + } + + /// Returns the gas price to use + pub fn gas_price(&self) -> Result { + let res = match &self.gas_price { + GasPrice::Legacy(res) => res.as_ref(), + GasPrice::EIP1559(res) => res.as_ref().map(|res| &res.0), + }; + match res { + Ok(val) => Ok(*val), + Err(err) => Err(eyre::eyre!("{}", err)), + } + } +} diff --git a/crates/zkforge/bin/cmd/script/receipts.rs b/crates/zkforge/bin/cmd/script/receipts.rs new file mode 100644 index 000000000..f09329c35 --- /dev/null +++ b/crates/zkforge/bin/cmd/script/receipts.rs @@ -0,0 +1,153 @@ +use super::sequence::ScriptSequence; +use alloy_primitives::TxHash; +use ethers::{prelude::PendingTransaction, providers::Middleware, types::TransactionReceipt}; +use eyre::Result; +use foundry_cli::{init_progress, update_progress, utils::print_receipt}; +use foundry_common::RetryProvider; +use foundry_utils::types::{ToAlloy, ToEthers}; +use futures::StreamExt; +use std::sync::Arc; +use tracing::{trace, warn}; + +/// Convenience enum for internal signalling of transaction status +enum TxStatus { + Dropped, + Success(TransactionReceipt), + Revert(TransactionReceipt), +} + +impl From for TxStatus { + fn from(receipt: TransactionReceipt) -> Self { + let status = receipt.status.expect("receipt is from an ancient, pre-EIP658 block"); + if status.is_zero() { + TxStatus::Revert(receipt) + } else { + TxStatus::Success(receipt) + } + } +} + +/// Gets the receipts of previously pending transactions, or removes them from +/// the deploy sequence's pending vector +pub async fn wait_for_pending( + provider: Arc, + deployment_sequence: &mut ScriptSequence, +) -> Result<()> { + if deployment_sequence.pending.is_empty() { + return Ok(()) + } + println!("##\nChecking previously pending transactions."); + clear_pendings(provider, deployment_sequence, None).await +} + +/// Traverses a set of pendings and either finds receipts, or clears them from +/// the deployment sequnce. +/// +/// If no `tx_hashes` are provided, then `deployment_sequence.pending` will be +/// used. For each `tx_hash`, we check if it has confirmed. If it has +/// confirmed, we push the receipt (if successful) or push an error (if +/// revert). If the transaction has not confirmed, but can be found in the +/// node's mempool, we wait for its receipt to be available. If the transaction +/// has not confirmed, and cannot be found in the mempool, we remove it from +/// the `deploy_sequence.pending` vector so that it will be rebroadcast in +/// later steps. +pub async fn clear_pendings( + provider: Arc, + deployment_sequence: &mut ScriptSequence, + tx_hashes: Option>, +) -> Result<()> { + let to_query = tx_hashes.unwrap_or_else(|| deployment_sequence.pending.clone()); + + let count = deployment_sequence.pending.len(); + + trace!("Checking status of {count} pending transactions"); + + let futs = to_query.iter().copied().map(|tx| check_tx_status(&provider, tx)); + let mut tasks = futures::stream::iter(futs).buffer_unordered(10); + + let mut errors: Vec = vec![]; + let mut receipts = Vec::::with_capacity(count); + + // set up progress bar + let mut pos = 0; + let pb = init_progress!(deployment_sequence.pending, "receipts"); + pb.set_position(pos); + + while let Some((tx_hash, result)) = tasks.next().await { + match result { + Err(err) => { + errors.push(format!("Failure on receiving a receipt for {tx_hash:?}:\n{err}")) + } + Ok(TxStatus::Dropped) => { + // We want to remove it from pending so it will be re-broadcast. + deployment_sequence.remove_pending(tx_hash); + errors.push(format!("Transaction dropped from the mempool: {tx_hash:?}")); + } + Ok(TxStatus::Success(receipt)) => { + trace!(tx_hash = ?tx_hash, "received tx receipt"); + deployment_sequence.remove_pending(receipt.transaction_hash.to_alloy()); + receipts.push(receipt); + } + Ok(TxStatus::Revert(receipt)) => { + // consider: + // if this is not removed from pending, then the script becomes + // un-resumable. Is this desirable on reverts? + warn!(tx_hash = ?tx_hash, "Transaction Failure"); + deployment_sequence.remove_pending(receipt.transaction_hash.to_alloy()); + errors.push(format!("Transaction Failure: {:?}", receipt.transaction_hash)); + } + } + // update the progress bar + update_progress!(pb, pos); + pos += 1; + } + + // sort receipts by blocks asc and index + receipts.sort_unstable(); + + // print all receipts + for receipt in receipts { + print_receipt(deployment_sequence.chain.into(), &receipt); + deployment_sequence.add_receipt(receipt); + } + + // print any erros + if !errors.is_empty() { + let mut error_msg = errors.join("\n"); + if !deployment_sequence.pending.is_empty() { + error_msg += "\n\n Add `--resume` to your command to try and continue broadcasting + the transactions." + } + eyre::bail!(error_msg); + } + + Ok(()) +} + +/// Checks the status of a txhash by first polling for a receipt, then for +/// mempool inclusion. Returns the tx hash, and a status +async fn check_tx_status( + provider: &RetryProvider, + hash: TxHash, +) -> (TxHash, Result) { + // We use the inner future so that we can use ? operator in the future, but + // still neatly return the tuple + let result = async move { + // First check if there's a receipt + let receipt_opt = provider.get_transaction_receipt(hash.to_ethers()).await?; + if let Some(receipt) = receipt_opt { + return Ok(receipt.into()) + } + + // If the tx is present in the mempool, run the pending tx future, and + // assume the next drop is really really real + let pending_res = PendingTransaction::new(hash.to_ethers(), provider).await?; + match pending_res { + Some(receipt) => Ok(receipt.into()), + None => Ok(TxStatus::Dropped), + } + } + .await; + + (hash, result) +} diff --git a/crates/zkforge/bin/cmd/script/runner.rs b/crates/zkforge/bin/cmd/script/runner.rs new file mode 100644 index 000000000..e2b89889a --- /dev/null +++ b/crates/zkforge/bin/cmd/script/runner.rs @@ -0,0 +1,373 @@ +use super::*; +use alloy_primitives::{Address, Bytes, U256}; +use ethers::types::NameOrAddress; +use eyre::Result; +use tracing::log::trace; +use zkforge::{ + executor::{CallResult, DeployResult, EvmError, ExecutionErr, Executor, RawCallResult}, + revm::interpreter::{return_ok, InstructionResult}, + trace::{TraceKind, Traces}, + CALLER, +}; + +/// Represents which simulation stage is the script execution at. +pub enum SimulationStage { + Local, + OnChain, +} + +/// Drives script execution +#[derive(Debug)] +pub struct ScriptRunner { + pub executor: Executor, + pub initial_balance: U256, + pub sender: Address, +} + +impl ScriptRunner { + pub fn new(executor: Executor, initial_balance: U256, sender: Address) -> Self { + Self { executor, initial_balance, sender } + } + + /// Deploys the libraries and broadcast contract. Calls setUp method if requested. + pub fn setup( + &mut self, + libraries: &[Bytes], + code: Bytes, + setup: bool, + sender_nonce: u64, + is_broadcast: bool, + need_create2_deployer: bool, + ) -> Result<(Address, ScriptResult)> { + trace!(target: "script", "executing setUP()"); + + if !is_broadcast { + if self.sender == Config::DEFAULT_SENDER.to_alloy() { + // We max out their balance so that they can deploy and make calls. + self.executor.set_balance(self.sender, U256::MAX)?; + } + + if need_create2_deployer { + self.executor.deploy_create2_deployer()?; + } + } + + self.executor.set_nonce(self.sender, sender_nonce)?; + + // We max out their balance so that they can deploy and make calls. + self.executor.set_balance(CALLER, U256::MAX)?; + + // Deploy libraries + let mut traces: Traces = libraries + .iter() + .filter_map(|code| { + let DeployResult { traces, .. } = self + .executor + .deploy(self.sender, code.0.clone().into(), U256::ZERO, None) + .expect("couldn't deploy library"); + + traces + }) + .map(|traces| (TraceKind::Deployment, traces)) + .collect(); + + // Deploy an instance of the contract + let DeployResult { + address, + mut logs, + traces: constructor_traces, + debug: constructor_debug, + .. + } = self + .executor + .deploy(CALLER, code.0.into(), U256::ZERO, None) + .map_err(|err| eyre::eyre!("Failed to deploy script:\n{}", err))?; + + traces.extend(constructor_traces.map(|traces| (TraceKind::Deployment, traces))); + self.executor.set_balance(address, self.initial_balance)?; + + // Optionally call the `setUp` function + let (success, gas_used, labeled_addresses, transactions, debug, script_wallets) = if !setup + { + self.executor.backend.set_test_contract(address); + ( + true, + 0, + Default::default(), + None, + vec![constructor_debug].into_iter().collect(), + vec![], + ) + } else { + match self.executor.setup(Some(self.sender), address) { + Ok(CallResult { + reverted, + traces: setup_traces, + labels, + logs: setup_logs, + debug, + gas_used, + transactions, + script_wallets, + .. + }) => { + traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces))); + logs.extend_from_slice(&setup_logs); + + self.maybe_correct_nonce(sender_nonce, libraries.len())?; + + ( + !reverted, + gas_used, + labels, + transactions, + vec![constructor_debug, debug].into_iter().collect(), + script_wallets, + ) + } + Err(EvmError::Execution(err)) => { + let ExecutionErr { + reverted, + traces: setup_traces, + labels, + logs: setup_logs, + debug, + gas_used, + transactions, + script_wallets, + .. + } = *err; + traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces))); + logs.extend_from_slice(&setup_logs); + + self.maybe_correct_nonce(sender_nonce, libraries.len())?; + + ( + !reverted, + gas_used, + labels, + transactions, + vec![constructor_debug, debug].into_iter().collect(), + script_wallets, + ) + } + Err(e) => return Err(e.into()), + } + }; + + Ok(( + address, + ScriptResult { + returned: Bytes::new(), + success, + gas_used, + labeled_addresses: labeled_addresses + .into_iter() + .map(|l| (l.0, l.1)) + .collect::>(), + transactions, + logs, + traces, + debug, + address: None, + script_wallets, + ..Default::default() + }, + )) + } + + /// We call the `setUp()` function with self.sender, and if there haven't been + /// any broadcasts, then the EVM cheatcode module hasn't corrected the nonce. + /// So we have to. + fn maybe_correct_nonce( + &mut self, + sender_initial_nonce: u64, + libraries_len: usize, + ) -> Result<()> { + if let Some(cheatcodes) = &self.executor.inspector.cheatcodes { + if !cheatcodes.corrected_nonce { + self.executor + .set_nonce(self.sender, sender_initial_nonce + libraries_len as u64)?; + } + self.executor.inspector.cheatcodes.as_mut().unwrap().corrected_nonce = false; + } + Ok(()) + } + + /// Executes the method that will collect all broadcastable transactions. + pub fn script(&mut self, address: Address, calldata: Bytes) -> Result { + self.call(self.sender, address, calldata, U256::ZERO, false) + } + + /// Runs a broadcastable transaction locally and persists its state. + pub fn simulate( + &mut self, + from: Address, + to: Option, + calldata: Option, + value: Option, + ) -> Result { + if let Some(NameOrAddress::Address(to)) = to { + self.call( + from, + to.to_alloy(), + calldata.unwrap_or_default(), + value.unwrap_or(U256::ZERO), + true, + ) + } else if to.is_none() { + let (address, gas_used, logs, traces, debug) = match self.executor.deploy( + from, + calldata.expect("No data for create transaction").0.into(), + value.unwrap_or(U256::ZERO), + None, + ) { + Ok(DeployResult { address, gas_used, logs, traces, debug, .. }) => { + (address, gas_used, logs, traces, debug) + } + Err(EvmError::Execution(err)) => { + let ExecutionErr { reason, traces, gas_used, logs, debug, .. } = *err; + println!("{}", Paint::red(format!("\nFailed with `{reason}`:\n"))); + + (Address::ZERO, gas_used, logs, traces, debug) + } + Err(e) => eyre::bail!("Failed deploying contract: {e:?}"), + }; + + Ok(ScriptResult { + returned: Bytes::new(), + success: address != Address::ZERO, + gas_used, + logs, + traces: traces + .map(|mut traces| { + // Manually adjust gas for the trace to add back the stipend/real used gas + traces.arena[0].trace.gas_cost = gas_used; + vec![(TraceKind::Execution, traces)] + }) + .unwrap_or_default(), + debug: vec![debug].into_iter().collect(), + address: Some(address), + ..Default::default() + }) + } else { + eyre::bail!("ENS not supported."); + } + } + + /// Executes the call + /// + /// This will commit the changes if `commit` is true. + /// + /// This will return _estimated_ gas instead of the precise gas the call would consume, so it + /// can be used as `gas_limit`. + fn call( + &mut self, + from: Address, + to: Address, + calldata: Bytes, + value: U256, + commit: bool, + ) -> Result { + let mut res = self.executor.call_raw(from, to, calldata.0.clone().into(), value)?; + let mut gas_used = res.gas_used; + + // We should only need to calculate realistic gas costs when preparing to broadcast + // something. This happens during the onchain simulation stage, where we commit each + // collected transactions. + // + // Otherwise don't re-execute, or some usecases might be broken: https://github.com/foundry-rs/foundry/issues/3921 + if commit { + gas_used = self.search_optimal_gas_usage(&res, from, to, &calldata, value)?; + res = self.executor.call_raw_committing(from, to, calldata.0.into(), value)?; + } + + let RawCallResult { + result, + reverted, + logs, + traces, + labels, + debug, + transactions, + script_wallets, + .. + } = res; + let breakpoints = res.cheatcodes.map(|cheats| cheats.breakpoints).unwrap_or_default(); + + Ok(ScriptResult { + returned: result, + success: !reverted, + gas_used, + logs, + traces: traces + .map(|mut traces| { + // Manually adjust gas for the trace to add back the stipend/real used gas + traces.arena[0].trace.gas_cost = gas_used; + vec![(TraceKind::Execution, traces)] + }) + .unwrap_or_default(), + debug: vec![debug].into_iter().collect(), + labeled_addresses: labels, + transactions, + address: None, + script_wallets, + breakpoints, + }) + } + + /// The executor will return the _exact_ gas value this transaction consumed, setting this value + /// as gas limit will result in `OutOfGas` so to come up with a better estimate we search over a + /// possible range we pick a higher gas limit 3x of a succeeded call should be safe. + /// + /// This might result in executing the same script multiple times. Depending on the user's goal, + /// it might be problematic when using `ffi`. + fn search_optimal_gas_usage( + &mut self, + res: &RawCallResult, + from: Address, + to: Address, + calldata: &Bytes, + value: U256, + ) -> Result { + let mut gas_used = res.gas_used; + if matches!(res.exit_reason, return_ok!()) { + // store the current gas limit and reset it later + let init_gas_limit = self.executor.env.tx.gas_limit; + + let mut highest_gas_limit = gas_used * 3; + let mut lowest_gas_limit = gas_used; + let mut last_highest_gas_limit = highest_gas_limit; + while (highest_gas_limit - lowest_gas_limit) > 1 { + let mid_gas_limit = (highest_gas_limit + lowest_gas_limit) / 2; + self.executor.env.tx.gas_limit = mid_gas_limit; + let res = self.executor.call_raw(from, to, calldata.0.clone().into(), value)?; + match res.exit_reason { + InstructionResult::Revert | + InstructionResult::OutOfGas | + InstructionResult::OutOfFund => { + lowest_gas_limit = mid_gas_limit; + } + _ => { + highest_gas_limit = mid_gas_limit; + // if last two successful estimations only vary by 10%, we consider this to + // sufficiently accurate + const ACCURACY: u64 = 10; + if (last_highest_gas_limit - highest_gas_limit) * ACCURACY / + last_highest_gas_limit < + 1 + { + // update the gas + gas_used = highest_gas_limit; + break + } + last_highest_gas_limit = highest_gas_limit; + } + } + } + // reset gas limit in the + self.executor.env.tx.gas_limit = init_gas_limit; + } + Ok(gas_used) + } +} diff --git a/crates/zkforge/bin/cmd/script/sequence.rs b/crates/zkforge/bin/cmd/script/sequence.rs new file mode 100644 index 000000000..90b7f75f4 --- /dev/null +++ b/crates/zkforge/bin/cmd/script/sequence.rs @@ -0,0 +1,398 @@ +use super::NestedValue; +use crate::cmd::{ + init::get_commit_hash, + script::{ + transaction::{wrapper, AdditionalContract, TransactionWithMetadata}, + verify::VerifyBundle, + }, + verify::provider::VerificationProviderType, +}; +use alloy_primitives::{Address, TxHash}; +use ethers::{ + prelude::{artifacts::Libraries, ArtifactId, TransactionReceipt}, + types::transaction::eip2718::TypedTransaction, +}; +use eyre::{ContextCompat, Result, WrapErr}; +use foundry_cli::utils::now; +use foundry_common::{fs, shell, SELECTOR_LEN}; +use foundry_config::Config; +use foundry_utils::types::{ToAlloy, ToEthers}; +use serde::{Deserialize, Serialize}; +use std::{ + collections::{HashMap, VecDeque}, + io::{BufWriter, Write}, + path::{Path, PathBuf}, +}; +use tracing::trace; +use yansi::Paint; + +pub const DRY_RUN_DIR: &str = "dry-run"; + +/// Helper that saves the transactions sequence and its state on which transactions have been +/// broadcasted +#[derive(Deserialize, Serialize, Clone, Default)] +pub struct ScriptSequence { + pub transactions: VecDeque, + #[serde(serialize_with = "wrapper::serialize_receipts")] + pub receipts: Vec, + pub libraries: Vec, + pub pending: Vec, + #[serde(skip)] + pub path: PathBuf, + #[serde(skip)] + pub sensitive_path: PathBuf, + pub returns: HashMap, + pub timestamp: u64, + pub chain: u64, + /// If `True`, the sequence belongs to a `MultiChainSequence` and won't save to disk as usual. + pub multi: bool, + pub commit: Option, +} + +/// Sensitive values from the transactions in a script sequence +#[derive(Deserialize, Serialize, Clone, Default)] +pub struct SensitiveTransactionMetadata { + pub rpc: Option, +} + +/// Sensitive info from the script sequence which is saved into the cache folder +#[derive(Deserialize, Serialize, Clone, Default)] +pub struct SensitiveScriptSequence { + pub transactions: VecDeque, +} + +impl From<&mut ScriptSequence> for SensitiveScriptSequence { + fn from(sequence: &mut ScriptSequence) -> Self { + SensitiveScriptSequence { + transactions: sequence + .transactions + .iter() + .map(|tx| SensitiveTransactionMetadata { rpc: tx.rpc.clone() }) + .collect(), + } + } +} + +impl ScriptSequence { + pub fn new( + transactions: VecDeque, + returns: HashMap, + sig: &str, + target: &ArtifactId, + config: &Config, + broadcasted: bool, + is_multi: bool, + ) -> Result { + let chain = config.chain_id.unwrap_or_default().id(); + + let (path, sensitive_path) = ScriptSequence::get_paths( + &config.broadcast, + &config.cache_path, + sig, + target, + chain, + broadcasted && !is_multi, + )?; + + let commit = get_commit_hash(&config.__root.0); + + Ok(ScriptSequence { + transactions, + returns, + receipts: vec![], + pending: vec![], + path, + sensitive_path, + timestamp: now().as_secs(), + libraries: vec![], + chain, + multi: is_multi, + commit, + }) + } + + /// Loads The sequence for the corresponding json file + pub fn load( + config: &Config, + sig: &str, + target: &ArtifactId, + chain_id: u64, + broadcasted: bool, + ) -> Result { + let (path, sensitive_path) = ScriptSequence::get_paths( + &config.broadcast, + &config.cache_path, + sig, + target, + chain_id, + broadcasted, + )?; + + let mut script_sequence: Self = ethers::solc::utils::read_json_file(&path) + .wrap_err(format!("Deployment not found for chain `{chain_id}`."))?; + + let sensitive_script_sequence: SensitiveScriptSequence = + ethers::solc::utils::read_json_file(&sensitive_path).wrap_err(format!( + "Deployment's sensitive details not found for chain `{chain_id}`." + ))?; + + script_sequence + .transactions + .iter_mut() + .enumerate() + .for_each(|(i, tx)| tx.rpc = sensitive_script_sequence.transactions[i].rpc.clone()); + + script_sequence.path = path; + script_sequence.sensitive_path = sensitive_path; + + Ok(script_sequence) + } + + /// Saves the transactions as file if it's a standalone deployment. + pub fn save(&mut self) -> Result<()> { + if self.multi || self.transactions.is_empty() { + return Ok(()) + } + + self.timestamp = now().as_secs(); + let ts_name = format!("run-{}.json", self.timestamp); + + let sensitive_script_sequence: SensitiveScriptSequence = self.into(); + + // broadcast folder writes + //../run-latest.json + let mut writer = BufWriter::new(fs::create_file(&self.path)?); + serde_json::to_writer_pretty(&mut writer, &self)?; + writer.flush()?; + //../run-[timestamp].json + fs::copy(&self.path, self.path.with_file_name(&ts_name))?; + + // cache folder writes + //../run-latest.json + let mut writer = BufWriter::new(fs::create_file(&self.sensitive_path)?); + serde_json::to_writer_pretty(&mut writer, &sensitive_script_sequence)?; + writer.flush()?; + //../run-[timestamp].json + fs::copy(&self.sensitive_path, self.sensitive_path.with_file_name(&ts_name))?; + + shell::println(format!("\nTransactions saved to: {}\n", self.path.display()))?; + shell::println(format!("Sensitive values saved to: {}\n", self.sensitive_path.display()))?; + + Ok(()) + } + + pub fn add_receipt(&mut self, receipt: TransactionReceipt) { + self.receipts.push(receipt); + } + + /// Sorts all receipts with ascending transaction index + pub fn sort_receipts(&mut self) { + self.receipts.sort_unstable() + } + + pub fn add_pending(&mut self, index: usize, tx_hash: TxHash) { + if !self.pending.contains(&tx_hash) { + self.transactions[index].hash = Some(tx_hash); + self.pending.push(tx_hash); + } + } + + pub fn remove_pending(&mut self, tx_hash: TxHash) { + self.pending.retain(|element| element != &tx_hash); + } + + pub fn add_libraries(&mut self, libraries: Libraries) { + self.libraries = libraries + .libs + .iter() + .flat_map(|(file, libs)| { + libs.iter() + .map(|(name, address)| format!("{}:{name}:{address}", file.to_string_lossy())) + }) + .collect(); + } + + /// Gets paths in the formats + /// ./broadcast/[contract_filename]/[chain_id]/[sig]-[timestamp].json and + /// ./cache/[contract_filename]/[chain_id]/[sig]-[timestamp].json + pub fn get_paths( + broadcast: &Path, + cache: &Path, + sig: &str, + target: &ArtifactId, + chain_id: u64, + broadcasted: bool, + ) -> Result<(PathBuf, PathBuf)> { + let mut broadcast = broadcast.to_path_buf(); + let mut cache = cache.to_path_buf(); + let mut common = PathBuf::new(); + + let target_fname = target.source.file_name().wrap_err("No filename.")?; + common.push(target_fname); + common.push(chain_id.to_string()); + if !broadcasted { + common.push(DRY_RUN_DIR); + } + + broadcast.push(common.clone()); + cache.push(common); + + fs::create_dir_all(&broadcast)?; + fs::create_dir_all(&cache)?; + + // TODO: ideally we want the name of the function here if sig is calldata + let filename = sig_to_file_name(sig); + + broadcast.push(format!("{filename}-latest.json")); + cache.push(format!("{filename}-latest.json")); + + Ok((broadcast, cache)) + } + + /// Given the broadcast log, it matches transactions with receipts, and tries to verify any + /// created contract on etherscan. + pub async fn verify_contracts( + &mut self, + config: &Config, + mut verify: VerifyBundle, + ) -> Result<()> { + trace!(target: "script", "verifying {} contracts [{}]", verify.known_contracts.len(), self.chain); + + verify.set_chain(config, self.chain.into()); + + if verify.etherscan.key.is_some() || + verify.verifier.verifier != VerificationProviderType::Etherscan + { + trace!(target: "script", "prepare future verifications"); + + let mut future_verifications = Vec::with_capacity(self.receipts.len()); + let mut unverifiable_contracts = vec![]; + + // Make sure the receipts have the right order first. + self.sort_receipts(); + + for (receipt, tx) in self.receipts.iter_mut().zip(self.transactions.iter()) { + // create2 hash offset + let mut offset = 0; + + if tx.is_create2() { + receipt.contract_address = tx.contract_address.map(|a| a.to_ethers()); + offset = 32; + } + + // Verify contract created directly from the transaction + if let (Some(address), Some(data)) = + (receipt.contract_address.map(|h| h.to_alloy()), tx.typed_tx().data()) + { + match verify.get_verify_args(address, offset, &data.0, &self.libraries) { + Some(verify) => future_verifications.push(verify.run()), + None => unverifiable_contracts.push(address), + }; + } + + // Verify potential contracts created during the transaction execution + for AdditionalContract { address, init_code, .. } in &tx.additional_contracts { + match verify.get_verify_args(*address, 0, init_code, &self.libraries) { + Some(verify) => future_verifications.push(verify.run()), + None => unverifiable_contracts.push(*address), + }; + } + } + + trace!(target: "script", "collected {} verification jobs and {} unverifiable contracts", future_verifications.len(), unverifiable_contracts.len()); + + self.check_unverified(unverifiable_contracts, verify); + + let num_verifications = future_verifications.len(); + println!("##\nStart verification for ({num_verifications}) contracts",); + for verification in future_verifications { + verification.await?; + } + + println!("All ({num_verifications}) contracts were verified!"); + } + + Ok(()) + } + + /// Let the user know if there are any contracts which can not be verified. Also, present some + /// hints on potential causes. + fn check_unverified(&self, unverifiable_contracts: Vec
, verify: VerifyBundle) { + if !unverifiable_contracts.is_empty() { + println!( + "\n{}", + Paint::yellow(format!( + "We haven't found any matching bytecode for the following contracts: {:?}.\n\n{}", + unverifiable_contracts, + "This may occur when resuming a verification, but the underlying source code or compiler version has changed." + )) + .bold(), + ); + + if let Some(commit) = &self.commit { + let current_commit = verify + .project_paths + .root + .map(|root| get_commit_hash(&root).unwrap_or_default()) + .unwrap_or_default(); + + if ¤t_commit != commit { + println!("\tScript was broadcasted on commit `{commit}`, but we are at `{current_commit}`."); + } + } + } + } + + /// Returns the list of the transactions without the metadata. + pub fn typed_transactions(&self) -> Vec<(String, &TypedTransaction)> { + self.transactions + .iter() + .map(|tx| { + (tx.rpc.clone().expect("to have been filled with a proper rpc"), tx.typed_tx()) + }) + .collect() + } +} + +impl Drop for ScriptSequence { + fn drop(&mut self) { + self.sort_receipts(); + self.save().expect("not able to save deployment sequence"); + } +} + +/// Converts the `sig` argument into the corresponding file path. +/// +/// This accepts either the signature of the function or the raw calldata + +fn sig_to_file_name(sig: &str) -> String { + if let Some((name, _)) = sig.split_once('(') { + // strip until call argument parenthesis + return name.to_string() + } + // assume calldata if `sig` is hex + if let Ok(calldata) = hex::decode(sig) { + // in which case we return the function signature + return hex::encode(&calldata[..SELECTOR_LEN]) + } + + // return sig as is + sig.to_string() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_convert_sig() { + assert_eq!(sig_to_file_name("run()").as_str(), "run"); + assert_eq!( + sig_to_file_name( + "522bb704000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfFFb92266" + ) + .as_str(), + "522bb704" + ); + } +} diff --git a/crates/zkforge/bin/cmd/script/transaction.rs b/crates/zkforge/bin/cmd/script/transaction.rs new file mode 100644 index 000000000..873d33a29 --- /dev/null +++ b/crates/zkforge/bin/cmd/script/transaction.rs @@ -0,0 +1,457 @@ +use super::{artifacts::ArtifactInfo, ScriptResult}; +use alloy_primitives::{Address, B256}; +use ethers::{abi, prelude::NameOrAddress, types::transaction::eip2718::TypedTransaction}; +use eyre::{ContextCompat, Result, WrapErr}; +use foundry_common::{abi::format_token_raw, RpcUrl, SELECTOR_LEN}; +use foundry_evm::{ + executor::inspector::DEFAULT_CREATE2_DEPLOYER, trace::CallTraceDecoder, CallKind, +}; +use foundry_utils::types::{ToAlloy, ToEthers}; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; +use tracing::error; + +#[derive(Debug, Deserialize, Serialize, Clone, Default)] +#[serde(rename_all = "camelCase")] +pub struct AdditionalContract { + #[serde(rename = "transactionType")] + pub opcode: CallKind, + #[serde(serialize_with = "wrapper::serialize_addr")] + pub address: Address, + #[serde(with = "hex")] + pub init_code: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Clone, Default)] +#[serde(rename_all = "camelCase")] +pub struct TransactionWithMetadata { + pub hash: Option, + #[serde(rename = "transactionType")] + pub opcode: CallKind, + #[serde(default = "default_string")] + pub contract_name: Option, + #[serde(default = "default_address", serialize_with = "wrapper::serialize_opt_addr")] + pub contract_address: Option
, + #[serde(default = "default_string")] + pub function: Option, + #[serde(default = "default_vec_of_strings")] + pub arguments: Option>, + #[serde(skip)] + pub rpc: Option, + pub transaction: TypedTransaction, + pub additional_contracts: Vec, + pub is_fixed_gas_limit: bool, +} + +fn default_string() -> Option { + Some("".to_string()) +} + +fn default_address() -> Option
{ + Some(Address::ZERO) +} + +fn default_vec_of_strings() -> Option> { + Some(vec![]) +} + +impl TransactionWithMetadata { + pub fn from_typed_transaction(transaction: TypedTransaction) -> Self { + Self { transaction, ..Default::default() } + } + + pub fn new( + transaction: TypedTransaction, + rpc: Option, + result: &ScriptResult, + local_contracts: &BTreeMap, + decoder: &CallTraceDecoder, + additional_contracts: Vec, + is_fixed_gas_limit: bool, + ) -> Result { + let mut metadata = Self { transaction, rpc, is_fixed_gas_limit, ..Default::default() }; + + // Specify if any contract was directly created with this transaction + if let Some(NameOrAddress::Address(to)) = metadata.transaction.to().cloned() { + if to.to_alloy() == DEFAULT_CREATE2_DEPLOYER { + metadata.set_create( + true, + Address::from_slice(&result.returned), + local_contracts, + )?; + } else { + metadata + .set_call(to.to_alloy(), local_contracts, decoder) + .wrap_err("Could not decode transaction type.")?; + } + } else if metadata.transaction.to().is_none() { + metadata.set_create( + false, + result.address.wrap_err("There should be a contract address from CREATE.")?, + local_contracts, + )?; + } + + // Add the additional contracts created in this transaction, so we can verify them later. + if let Some(tx_address) = metadata.contract_address { + metadata.additional_contracts = additional_contracts + .into_iter() + .filter_map(|contract| { + // Filter out the transaction contract repeated init_code. + if contract.address != tx_address { + Some(contract) + } else { + None + } + }) + .collect(); + } + + Ok(metadata) + } + + /// Populate the transaction as CREATE tx + /// + /// If this is a CREATE2 transaction this attempt to decode the arguments from the CREATE2 + /// deployer's function + fn set_create( + &mut self, + is_create2: bool, + address: Address, + contracts: &BTreeMap, + ) -> Result<()> { + if is_create2 { + self.opcode = CallKind::Create2; + } else { + self.opcode = CallKind::Create; + } + + self.contract_name = contracts.get(&address).map(|info| info.contract_name.clone()); + self.contract_address = Some(address); + + if let Some(data) = self.transaction.data() { + if let Some(info) = contracts.get(&address) { + // constructor args are postfixed to creation code + // and create2 transactions are prefixed by 32 byte salt + let contains_constructor_args = if is_create2 { + data.len() - 32 > info.code.len() + } else { + data.len() > info.code.len() + }; + + if contains_constructor_args { + if let Some(constructor) = info.abi.constructor() { + let creation_code = if is_create2 { &data[32..] } else { data }; + + let on_err = || { + let inputs = constructor + .inputs + .iter() + .map(|p| p.kind.to_string()) + .collect::>() + .join(","); + let signature = format!("constructor({inputs})"); + let bytecode = hex::encode(creation_code); + (signature, bytecode) + }; + + let params = + constructor.inputs.iter().map(|p| p.kind.clone()).collect::>(); + + // the constructor args start after bytecode + let constructor_args = &creation_code[info.code.len()..]; + + if let Ok(arguments) = abi::decode(¶ms, constructor_args) { + self.arguments = Some(arguments.iter().map(format_token_raw).collect()); + } else { + let (signature, bytecode) = on_err(); + error!(constructor=?signature, contract=?self.contract_name, bytecode, "Failed to decode constructor arguments") + }; + } + } + } + } + Ok(()) + } + + /// Populate the transaction as CALL tx + fn set_call( + &mut self, + target: Address, + local_contracts: &BTreeMap, + decoder: &CallTraceDecoder, + ) -> Result<()> { + self.opcode = CallKind::Call; + + if let Some(data) = self.transaction.data() { + if data.0.len() >= SELECTOR_LEN { + if let Some(info) = local_contracts.get(&target) { + // This CALL is made to a local contract. + + self.contract_name = Some(info.contract_name.clone()); + if let Some(function) = info + .abi + .functions() + .find(|function| function.short_signature() == data.0[..SELECTOR_LEN]) + { + self.function = Some(function.signature()); + self.arguments = Some( + function + .decode_input(&data.0[SELECTOR_LEN..]) + .map(|tokens| tokens.iter().map(format_token_raw).collect())?, + ); + } + } else { + // This CALL is made to an external contract. We can only decode it, if it has + // been verified and identified by etherscan. + + if let Some(Some(function)) = decoder + .functions + .get(&data.0[..SELECTOR_LEN]) + .map(|functions| functions.first()) + { + self.contract_name = decoder.contracts.get(&target.to_ethers()).cloned(); + + self.function = Some(function.signature()); + self.arguments = Some( + function + .decode_input(&data.0[SELECTOR_LEN..]) + .map(|tokens| tokens.iter().map(format_token_raw).collect())?, + ); + } + } + self.contract_address = Some(target); + } + } + Ok(()) + } + + pub fn set_tx(&mut self, tx: TypedTransaction) { + self.transaction = tx; + } + + pub fn change_type(&mut self, is_legacy: bool) { + self.transaction = if is_legacy { + TypedTransaction::Legacy(self.transaction.clone().into()) + } else { + TypedTransaction::Eip1559(self.transaction.clone().into()) + }; + } + + pub fn typed_tx(&self) -> &TypedTransaction { + &self.transaction + } + + pub fn typed_tx_mut(&mut self) -> &mut TypedTransaction { + &mut self.transaction + } + + pub fn is_create2(&self) -> bool { + self.opcode == CallKind::Create2 + } +} + +// wrapper for modifying ethers-rs type serialization +pub mod wrapper { + pub use super::*; + + use ethers::{ + types::{Bloom, Bytes, Log, TransactionReceipt, H256, U256, U64}, + utils::to_checksum, + }; + + pub fn serialize_addr(addr: &Address, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&to_checksum(&addr.to_ethers(), None)) + } + + pub fn serialize_opt_addr(opt: &Option
, serializer: S) -> Result + where + S: serde::Serializer, + { + match opt { + Some(addr) => serialize_addr(addr, serializer), + None => serializer.serialize_none(), + } + } + + pub fn serialize_vec_with_wrapped( + vec: &[T], + serializer: S, + ) -> Result + where + S: serde::Serializer, + T: Clone, + WrappedType: serde::Serialize + From, + { + serializer.collect_seq(vec.iter().cloned().map(WrappedType::from)) + } + + // copied from https://github.com/gakonst/ethers-rs + #[derive(Serialize, Deserialize)] + struct WrappedLog { + /// H160. the contract that emitted the log + #[serde(serialize_with = "serialize_addr")] + pub address: Address, + + /// topics: Array of 0 to 4 32 Bytes of indexed log arguments. + /// (In solidity: The first topic is the hash of the signature of the event + /// (e.g. `Deposit(address,bytes32,uint256)`), except you declared the event + /// with the anonymous specifier.) + pub topics: Vec, + + /// Data + pub data: Bytes, + + /// Block Hash + #[serde(rename = "blockHash")] + #[serde(skip_serializing_if = "Option::is_none")] + pub block_hash: Option, + + /// Block Number + #[serde(rename = "blockNumber")] + #[serde(skip_serializing_if = "Option::is_none")] + pub block_number: Option, + + /// Transaction Hash + #[serde(rename = "transactionHash")] + #[serde(skip_serializing_if = "Option::is_none")] + pub transaction_hash: Option, + + /// Transaction Index + #[serde(rename = "transactionIndex")] + #[serde(skip_serializing_if = "Option::is_none")] + pub transaction_index: Option, + + /// Integer of the log index position in the block. None if it's a pending log. + #[serde(rename = "logIndex")] + #[serde(skip_serializing_if = "Option::is_none")] + pub log_index: Option, + + /// Integer of the transactions index position log was created from. + /// None when it's a pending log. + #[serde(rename = "transactionLogIndex")] + #[serde(skip_serializing_if = "Option::is_none")] + pub transaction_log_index: Option, + + /// Log Type + #[serde(rename = "logType")] + #[serde(skip_serializing_if = "Option::is_none")] + pub log_type: Option, + + /// True when the log was removed, due to a chain reorganization. + /// false if it's a valid log. + #[serde(skip_serializing_if = "Option::is_none")] + pub removed: Option, + } + impl From for WrappedLog { + fn from(log: Log) -> Self { + Self { + address: log.address.to_alloy(), + topics: log.topics, + data: log.data, + block_hash: log.block_hash, + block_number: log.block_number, + transaction_hash: log.transaction_hash, + transaction_index: log.transaction_index, + log_index: log.log_index, + transaction_log_index: log.transaction_log_index, + log_type: log.log_type, + removed: log.removed, + } + } + } + + fn serialize_logs( + logs: &[Log], + serializer: S, + ) -> Result { + serialize_vec_with_wrapped::(logs, serializer) + } + + // "Receipt" of an executed transaction: details of its execution. + // copied from https://github.com/gakonst/ethers-rs + #[derive(Default, Clone, Serialize, Deserialize)] + pub struct WrappedTransactionReceipt { + /// Transaction hash. + #[serde(rename = "transactionHash")] + pub transaction_hash: H256, + /// Index within the block. + #[serde(rename = "transactionIndex")] + pub transaction_index: U64, + /// Hash of the block this transaction was included within. + #[serde(rename = "blockHash")] + pub block_hash: Option, + /// Number of the block this transaction was included within. + #[serde(rename = "blockNumber")] + pub block_number: Option, + /// The address of the sender. + #[serde(serialize_with = "serialize_addr")] + pub from: Address, + // The address of the receiver. `None` when its a contract creation transaction. + #[serde(serialize_with = "serialize_opt_addr")] + pub to: Option
, + /// Cumulative gas used within the block after this was executed. + #[serde(rename = "cumulativeGasUsed")] + pub cumulative_gas_used: U256, + /// Gas used by this transaction alone. + /// + /// Gas used is `None` if the the client is running in light client mode. + #[serde(rename = "gasUsed")] + pub gas_used: Option, + /// Contract address created, or `None` if not a deployment. + #[serde(rename = "contractAddress", serialize_with = "serialize_opt_addr")] + pub contract_address: Option
, + /// Logs generated within this transaction. + #[serde(serialize_with = "serialize_logs")] + pub logs: Vec, + /// Status: either 1 (success) or 0 (failure). Only present after activation of [EIP-658](https://eips.ethereum.org/EIPS/eip-658) + pub status: Option, + /// State root. Only present before activation of [EIP-658](https://eips.ethereum.org/EIPS/eip-658) + #[serde(default, skip_serializing_if = "Option::is_none")] + pub root: Option, + /// Logs bloom + #[serde(rename = "logsBloom")] + pub logs_bloom: Bloom, + /// Transaction type, Some(1) for AccessList transaction, None for Legacy + #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")] + pub transaction_type: Option, + /// The price paid post-execution by the transaction (i.e. base fee + priority fee). + /// Both fields in 1559-style transactions are *maximums* (max fee + max priority fee), the + /// amount that's actually paid by users can only be determined post-execution + #[serde(rename = "effectiveGasPrice", default, skip_serializing_if = "Option::is_none")] + pub effective_gas_price: Option, + } + impl From for WrappedTransactionReceipt { + fn from(receipt: TransactionReceipt) -> Self { + Self { + transaction_hash: receipt.transaction_hash, + transaction_index: receipt.transaction_index, + block_hash: receipt.block_hash, + block_number: receipt.block_number, + from: receipt.from.to_alloy(), + to: receipt.to.map(|addr| addr.to_alloy()), + cumulative_gas_used: receipt.cumulative_gas_used, + gas_used: receipt.gas_used, + contract_address: receipt.contract_address.map(|addr| addr.to_alloy()), + logs: receipt.logs, + status: receipt.status, + root: receipt.root, + logs_bloom: receipt.logs_bloom, + transaction_type: receipt.transaction_type, + effective_gas_price: receipt.effective_gas_price, + } + } + } + + pub fn serialize_receipts( + receipts: &[TransactionReceipt], + serializer: S, + ) -> Result { + serialize_vec_with_wrapped::( + receipts, serializer, + ) + } +} diff --git a/crates/zkforge/bin/cmd/script/verify.rs b/crates/zkforge/bin/cmd/script/verify.rs new file mode 100644 index 000000000..95972f3a5 --- /dev/null +++ b/crates/zkforge/bin/cmd/script/verify.rs @@ -0,0 +1,119 @@ +use crate::cmd::{ + retry::RetryArgs, + verify::{VerifierArgs, VerifyArgs}, +}; +use alloy_primitives::Address; +use ethers::solc::{info::ContractInfo, Project}; +use foundry_cli::opts::{EtherscanOpts, ProjectPathsArgs}; +use foundry_common::ContractsByArtifact; +use foundry_config::{Chain, Config}; +use semver::Version; + +/// Data struct to help `ScriptSequence` verify contracts on `etherscan`. +#[derive(Clone)] +pub struct VerifyBundle { + pub num_of_optimizations: Option, + pub known_contracts: ContractsByArtifact, + pub project_paths: ProjectPathsArgs, + pub etherscan: EtherscanOpts, + pub retry: RetryArgs, + pub verifier: VerifierArgs, +} + +impl VerifyBundle { + pub fn new( + project: &Project, + config: &Config, + known_contracts: ContractsByArtifact, + retry: RetryArgs, + verifier: VerifierArgs, + ) -> Self { + let num_of_optimizations = + if config.optimizer { Some(config.optimizer_runs) } else { None }; + + let config_path = config.get_config_path(); + + let project_paths = ProjectPathsArgs { + root: Some(project.paths.root.clone()), + contracts: Some(project.paths.sources.clone()), + remappings: project.paths.remappings.clone(), + remappings_env: None, + cache_path: Some(project.paths.cache.clone()), + lib_paths: project.paths.libraries.clone(), + hardhat: config.profile == Config::HARDHAT_PROFILE, + config_path: if config_path.exists() { Some(config_path) } else { None }, + }; + + VerifyBundle { + num_of_optimizations, + known_contracts, + etherscan: Default::default(), + project_paths, + retry, + verifier, + } + } + + /// Configures the chain and sets the etherscan key, if available + pub fn set_chain(&mut self, config: &Config, chain: Chain) { + // If dealing with multiple chains, we need to be able to change inbetween the config + // chain_id. + self.etherscan.key = config.get_etherscan_api_key(Some(chain)); + self.etherscan.chain = Some(chain); + } + + /// Given a `VerifyBundle` and contract details, it tries to generate a valid `VerifyArgs` to + /// use against the `contract_address`. + pub fn get_verify_args( + &self, + contract_address: Address, + create2_offset: usize, + data: &[u8], + libraries: &[String], + ) -> Option { + for (artifact, (_contract, bytecode)) in self.known_contracts.iter() { + // If it's a CREATE2, the tx.data comes with a 32-byte salt in the beginning + // of the transaction + if data.split_at(create2_offset).1.starts_with(bytecode) { + let constructor_args = data.split_at(create2_offset + bytecode.len()).1.to_vec(); + + let contract = ContractInfo { + path: Some( + artifact.source.to_str().expect("There should be an artifact.").to_string(), + ), + name: artifact.name.clone(), + }; + + // We strip the build metadadata information, since it can lead to + // etherscan not identifying it correctly. eg: + // `v0.8.10+commit.fc410830.Linux.gcc` != `v0.8.10+commit.fc410830` + let version = Version::new( + artifact.version.major, + artifact.version.minor, + artifact.version.patch, + ); + + let verify = VerifyArgs { + address: contract_address, + contract, + compiler_version: Some(version.to_string()), + constructor_args: Some(hex::encode(constructor_args)), + constructor_args_path: None, + num_of_optimizations: self.num_of_optimizations, + etherscan: self.etherscan.clone(), + flatten: false, + force: false, + watch: true, + retry: self.retry, + libraries: libraries.to_vec(), + root: None, + verifier: self.verifier.clone(), + show_standard_json_input: false, + }; + + return Some(verify) + } + } + None + } +} diff --git a/crates/zkforge/bin/cmd/selectors.rs b/crates/zkforge/bin/cmd/selectors.rs new file mode 100644 index 000000000..b594081a7 --- /dev/null +++ b/crates/zkforge/bin/cmd/selectors.rs @@ -0,0 +1,182 @@ +use clap::Parser; +use comfy_table::Table; +use ethers::prelude::{artifacts::output_selection::ContractOutputSelection, info::ContractInfo}; +use eyre::Result; +use foundry_cli::{ + opts::{CompilerArgs, CoreBuildArgs, ProjectPathsArgs}, + utils::FoundryPathExt, +}; +use foundry_common::{ + compile, + selectors::{import_selectors, SelectorImportData}, +}; +use std::fs::canonicalize; + +/// CLI arguments for `forge selectors`. +#[derive(Debug, Clone, Parser)] +pub enum SelectorsSubcommands { + /// Check for selector collisions between contracts + #[clap(visible_alias = "co")] + Collision { + /// First contract + #[clap( + help = "The first of the two contracts for which to look selector collisions for, in the form `(:)?`", + value_name = "FIRST_CONTRACT" + )] + first_contract: ContractInfo, + + /// Second contract + #[clap( + help = "The second of the two contracts for which to look selector collisions for, in the form `(:)?`", + value_name = "SECOND_CONTRACT" + )] + second_contract: ContractInfo, + + /// Support build args + #[clap(flatten)] + build: Box, + }, + + /// Upload selectors to registry + #[clap(visible_alias = "up")] + Upload { + /// The name of the contract to upload selectors for. + #[clap(required_unless_present = "all")] + contract: Option, + + /// Upload selectors for all contracts in the project. + #[clap(long, required_unless_present = "contract")] + all: bool, + + #[clap(flatten)] + project_paths: ProjectPathsArgs, + }, +} + +impl SelectorsSubcommands { + pub async fn run(self) -> Result<()> { + match self { + SelectorsSubcommands::Upload { contract, all, project_paths } => { + let build_args = CoreBuildArgs { + project_paths: project_paths.clone(), + compiler: CompilerArgs { + extra_output: vec![ContractOutputSelection::Abi], + ..Default::default() + }, + ..Default::default() + }; + + let project = build_args.project()?; + let outcome = compile::suppress_compile(&project)?; + let artifacts = if all { + outcome + .into_artifacts_with_files() + .filter(|(file, _, _)| { + let is_sources_path = file + .starts_with(&project.paths.sources.to_string_lossy().to_string()); + let is_test = file.is_sol_test(); + + is_sources_path && !is_test + }) + .map(|(_, contract, artifact)| (contract, artifact)) + .collect() + } else { + let contract = contract.unwrap(); + let found_artifact = outcome.find_first(&contract); + let artifact = found_artifact + .ok_or_else(|| { + eyre::eyre!( + "Could not find artifact `{contract}` in the compiled artifacts" + ) + })? + .clone(); + vec![(contract, artifact)] + }; + + let mut artifacts = artifacts.into_iter().peekable(); + while let Some((contract, artifact)) = artifacts.next() { + let abi = artifact.abi.ok_or(eyre::eyre!("Unable to fetch abi"))?; + if abi.abi.functions.is_empty() && + abi.abi.events.is_empty() && + abi.abi.errors.is_empty() + { + continue + } + + println!("Uploading selectors for {contract}..."); + + // upload abi to selector database + import_selectors(SelectorImportData::Abi(vec![abi])).await?.describe(); + + if artifacts.peek().is_some() { + println!() + } + } + } + SelectorsSubcommands::Collision { mut first_contract, mut second_contract, build } => { + // Build first project + let first_project = build.project()?; + let first_outcome = if let Some(ref mut contract_path) = first_contract.path { + let target_path = canonicalize(&*contract_path)?; + *contract_path = target_path.to_string_lossy().to_string(); + compile::compile_files(&first_project, vec![target_path], true) + } else { + compile::suppress_compile(&first_project) + }?; + + // Build second project + let second_project = build.project()?; + let second_outcome = if let Some(ref mut contract_path) = second_contract.path { + let target_path = canonicalize(&*contract_path)?; + *contract_path = target_path.to_string_lossy().to_string(); + compile::compile_files(&second_project, vec![target_path], true) + } else { + compile::suppress_compile(&second_project) + }?; + + // Find the artifacts + let first_found_artifact = first_outcome.find_contract(&first_contract); + let second_found_artifact = second_outcome.find_contract(&second_contract); + + // Unwrap inner artifacts + let first_artifact = first_found_artifact.ok_or_else(|| { + eyre::eyre!("Failed to extract first artifact bytecode as a string") + })?; + let second_artifact = second_found_artifact.ok_or_else(|| { + eyre::eyre!("Failed to extract second artifact bytecode as a string") + })?; + + // Check method selectors for collisions + let first_method_map = first_artifact.method_identifiers.as_ref().unwrap(); + let second_method_map = second_artifact.method_identifiers.as_ref().unwrap(); + + let colliding_methods: Vec<(&String, &String, &String)> = first_method_map + .iter() + .filter_map(|(k1, v1)| { + second_method_map + .iter() + .find_map(|(k2, v2)| if **v2 == *v1 { Some((k2, v2)) } else { None }) + .map(|(k2, v2)| (v2, k1, k2)) + }) + .collect(); + + if colliding_methods.is_empty() { + println!("No colliding method selectors between the two contracts."); + } else { + let mut table = Table::new(); + table.set_header(vec![ + String::from("Selector"), + first_contract.name, + second_contract.name, + ]); + colliding_methods.iter().for_each(|t| { + table.add_row(vec![t.0, t.1, t.2]); + }); + println!("{} collisions found:", colliding_methods.len()); + println!("{table}"); + } + } + } + Ok(()) + } +} diff --git a/crates/zkforge/bin/cmd/snapshot.rs b/crates/zkforge/bin/cmd/snapshot.rs new file mode 100644 index 000000000..91bb5bcbc --- /dev/null +++ b/crates/zkforge/bin/cmd/snapshot.rs @@ -0,0 +1,507 @@ +use super::{ + test, + test::{Test, TestOutcome}, +}; +use alloy_primitives::U256; +use clap::{builder::RangedU64ValueParser, Parser, ValueHint}; +use eyre::{Context, Result}; +use foundry_cli::utils::STATIC_FUZZ_SEED; +use once_cell::sync::Lazy; +use regex::Regex; +use std::{ + cmp::Ordering, + collections::HashMap, + fs, + io::{self, BufRead}, + path::{Path, PathBuf}, + str::FromStr, +}; +use watchexec::config::{InitConfig, RuntimeConfig}; +use yansi::Paint; +use zkforge::result::TestKindReport; + +/// A regex that matches a basic snapshot entry like +/// `Test:testDeposit() (gas: 58804)` +pub static RE_BASIC_SNAPSHOT_ENTRY: Lazy = Lazy::new(|| { + Regex::new(r"(?P(.*?)):(?P(\w+)\s*\((.*?)\))\s*\(((gas:)?\s*(?P\d+)|(runs:\s*(?P\d+),\s*μ:\s*(?P\d+),\s*~:\s*(?P\d+))|(runs:\s*(?P\d+),\s*calls:\s*(?P\d+),\s*reverts:\s*(?P\d+)))\)").unwrap() +}); + +/// CLI arguments for `forge snapshot`. +#[derive(Debug, Clone, Parser)] +pub struct SnapshotArgs { + /// Output a diff against a pre-existing snapshot. + /// + /// By default, the comparison is done with .gas-snapshot. + #[clap( + conflicts_with = "snap", + long, + value_hint = ValueHint::FilePath, + value_name = "SNAPSHOT_FILE", + )] + diff: Option>, + + /// Compare against a pre-existing snapshot, exiting with code 1 if they do not match. + /// + /// Outputs a diff if the snapshots do not match. + /// + /// By default, the comparison is done with .gas-snapshot. + #[clap( + conflicts_with = "diff", + long, + value_hint = ValueHint::FilePath, + value_name = "SNAPSHOT_FILE", + )] + check: Option>, + + // Hidden because there is only one option + /// How to format the output. + #[clap(long, hide(true))] + format: Option, + + /// Output file for the snapshot. + #[clap( + long, + default_value = ".gas-snapshot", + value_hint = ValueHint::FilePath, + value_name = "FILE", + )] + snap: PathBuf, + + /// Tolerates gas deviations up to the specified percentage. + #[clap( + long, + value_parser = RangedU64ValueParser::::new().range(0..100), + value_name = "SNAPSHOT_THRESHOLD" + )] + tolerance: Option, + + /// All test arguments are supported + #[clap(flatten)] + pub(crate) test: test::TestArgs, + + /// Additional configs for test results + #[clap(flatten)] + config: SnapshotConfig, +} + +impl SnapshotArgs { + /// Returns whether `SnapshotArgs` was configured with `--watch` + pub fn is_watch(&self) -> bool { + self.test.is_watch() + } + + /// Returns the [`watchexec::InitConfig`] and [`watchexec::RuntimeConfig`] necessary to + /// bootstrap a new [`watchexe::Watchexec`] loop. + pub(crate) fn watchexec_config(&self) -> Result<(InitConfig, RuntimeConfig)> { + self.test.watchexec_config() + } + + 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)); + + let outcome = self.test.execute_tests().await?; + outcome.ensure_ok()?; + let tests = self.config.apply(outcome); + + if let Some(path) = self.diff { + let snap = path.as_ref().unwrap_or(&self.snap); + let snaps = read_snapshot(snap)?; + diff(tests, snaps)?; + } else if let Some(path) = self.check { + let snap = path.as_ref().unwrap_or(&self.snap); + let snaps = read_snapshot(snap)?; + if check(tests, snaps, self.tolerance) { + std::process::exit(0) + } else { + std::process::exit(1) + } + } else { + write_to_snapshot_file(&tests, self.snap, self.format)?; + } + Ok(()) + } +} + +// TODO implement pretty tables +#[derive(Debug, Clone)] +pub enum Format { + Table, +} + +impl FromStr for Format { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "t" | "table" => Ok(Format::Table), + _ => Err(format!("Unrecognized format `{s}`")), + } + } +} + +/// Additional filters that can be applied on the test results +#[derive(Debug, Clone, Parser, Default)] +struct SnapshotConfig { + /// Sort results by gas used (ascending). + #[clap(long)] + asc: bool, + + /// Sort results by gas used (descending). + #[clap(conflicts_with = "asc", long)] + desc: bool, + + /// Only include tests that used more gas that the given amount. + #[clap(long, value_name = "MIN_GAS")] + min: Option, + + /// Only include tests that used less gas that the given amount. + #[clap(long, value_name = "MAX_GAS")] + max: Option, +} + +impl SnapshotConfig { + fn is_in_gas_range(&self, gas_used: u64) -> bool { + if let Some(min) = self.min { + if gas_used < min { + return false + } + } + if let Some(max) = self.max { + if gas_used > max { + return false + } + } + true + } + + fn apply(&self, outcome: TestOutcome) -> Vec { + let mut tests = outcome + .into_tests() + .filter(|test| self.is_in_gas_range(test.gas_used())) + .collect::>(); + + if self.asc { + tests.sort_by_key(|a| a.gas_used()); + } else if self.desc { + tests.sort_by_key(|b| std::cmp::Reverse(b.gas_used())) + } + + tests + } +} + +/// A general entry in a snapshot file +/// +/// Has the form: +/// `(gas:? 40181)` for normal tests +/// `(runs: 256, μ: 40181, ~: 40181)` for fuzz tests +/// `(runs: 256, calls: 40181, reverts: 40181)` for invariant tests +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct SnapshotEntry { + pub contract_name: String, + pub signature: String, + pub gas_used: TestKindReport, +} + +impl FromStr for SnapshotEntry { + type Err = String; + + fn from_str(s: &str) -> Result { + RE_BASIC_SNAPSHOT_ENTRY + .captures(s) + .and_then(|cap| { + cap.name("file").and_then(|file| { + cap.name("sig").and_then(|sig| { + if let Some(gas) = cap.name("gas") { + Some(SnapshotEntry { + contract_name: file.as_str().to_string(), + signature: sig.as_str().to_string(), + gas_used: TestKindReport::Standard { + gas: gas.as_str().parse().unwrap(), + }, + }) + } else if let Some(runs) = cap.name("runs") { + cap.name("avg") + .and_then(|avg| cap.name("med").map(|med| (runs, avg, med))) + .map(|(runs, avg, med)| SnapshotEntry { + contract_name: file.as_str().to_string(), + signature: sig.as_str().to_string(), + gas_used: TestKindReport::Fuzz { + runs: runs.as_str().parse().unwrap(), + median_gas: med.as_str().parse().unwrap(), + mean_gas: avg.as_str().parse().unwrap(), + }, + }) + } else { + cap.name("invruns") + .and_then(|runs| { + cap.name("calls").and_then(|avg| { + cap.name("reverts").map(|med| (runs, avg, med)) + }) + }) + .map(|(runs, calls, reverts)| SnapshotEntry { + contract_name: file.as_str().to_string(), + signature: sig.as_str().to_string(), + gas_used: TestKindReport::Invariant { + runs: runs.as_str().parse().unwrap(), + calls: calls.as_str().parse().unwrap(), + reverts: reverts.as_str().parse().unwrap(), + }, + }) + } + }) + }) + }) + .ok_or_else(|| format!("Could not extract Snapshot Entry for {s}")) + } +} + +/// Reads a list of snapshot entries from a snapshot file +fn read_snapshot(path: impl AsRef) -> Result> { + let path = path.as_ref(); + let mut entries = Vec::new(); + for line in io::BufReader::new( + fs::File::open(path) + .wrap_err(format!("failed to read snapshot file \"{}\"", path.display()))?, + ) + .lines() + { + entries.push(SnapshotEntry::from_str(line?.as_str()).map_err(|err| eyre::eyre!("{err}"))?); + } + Ok(entries) +} + +/// Writes a series of tests to a snapshot file after sorting them +fn write_to_snapshot_file( + tests: &[Test], + path: impl AsRef, + _format: Option, +) -> Result<()> { + let mut reports = tests + .iter() + .map(|test| { + format!("{}:{} {}", test.contract_name(), test.signature, test.result.kind.report()) + }) + .collect::>(); + + // sort all reports + reports.sort(); + + let content = reports.join("\n"); + Ok(fs::write(path, content)?) +} + +/// A Snapshot entry diff +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct SnapshotDiff { + pub signature: String, + pub source_gas_used: TestKindReport, + pub target_gas_used: TestKindReport, +} + +impl SnapshotDiff { + /// Returns the gas diff + /// + /// `> 0` if the source used more gas + /// `< 0` if the target used more gas + fn gas_change(&self) -> i128 { + self.source_gas_used.gas() as i128 - self.target_gas_used.gas() as i128 + } + + /// Determines the percentage change + fn gas_diff(&self) -> f64 { + self.gas_change() as f64 / self.target_gas_used.gas() as f64 + } +} + +/// Compares the set of tests with an existing snapshot +/// +/// Returns true all tests match +fn check(tests: Vec, snaps: Vec, tolerance: Option) -> bool { + let snaps = snaps + .into_iter() + .map(|s| ((s.contract_name, s.signature), s.gas_used)) + .collect::>(); + let mut has_diff = false; + for test in tests { + if let Some(target_gas) = + snaps.get(&(test.contract_name().to_string(), test.signature.clone())).cloned() + { + let source_gas = test.result.kind.report(); + if !within_tolerance(source_gas.gas(), target_gas.gas(), tolerance) { + eprintln!( + "Diff in \"{}::{}\": consumed \"{}\" gas, expected \"{}\" gas ", + test.contract_name(), + test.signature, + source_gas, + target_gas + ); + has_diff = true; + } + } else { + eprintln!( + "No matching snapshot entry found for \"{}::{}\" in snapshot file", + test.contract_name(), + test.signature + ); + has_diff = true; + } + } + !has_diff +} + +/// Compare the set of tests with an existing snapshot +fn diff(tests: Vec, snaps: Vec) -> Result<()> { + let snaps = snaps + .into_iter() + .map(|s| ((s.contract_name, s.signature), s.gas_used)) + .collect::>(); + let mut diffs = Vec::with_capacity(tests.len()); + for test in tests.into_iter() { + if let Some(target_gas_used) = + snaps.get(&(test.contract_name().to_string(), test.signature.clone())).cloned() + { + diffs.push(SnapshotDiff { + source_gas_used: test.result.kind.report(), + signature: test.signature, + target_gas_used, + }); + } + } + 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) + }); + + for diff in diffs { + let gas_change = diff.gas_change(); + overall_gas_change += gas_change; + overall_gas_used += diff.target_gas_used.gas() as i128; + let gas_diff = diff.gas_diff(); + println!( + "{} (gas: {} ({})) ", + diff.signature, + fmt_change(gas_change), + fmt_pct_change(gas_diff) + ); + } + + let overall_gas_diff = overall_gas_change as f64 / overall_gas_used as f64; + println!( + "Overall gas change: {} ({})", + fmt_change(overall_gas_change), + fmt_pct_change(overall_gas_diff) + ); + Ok(()) +} + +fn fmt_pct_change(change: f64) -> String { + let change_pct = change * 100.0; + match change.partial_cmp(&0.0).unwrap_or(Ordering::Equal) { + Ordering::Less => Paint::green(format!("{change_pct:.3}%")).to_string(), + Ordering::Equal => { + format!("{change_pct:.3}%") + } + Ordering::Greater => Paint::red(format!("{change_pct:.3}%")).to_string(), + } +} + +fn fmt_change(change: i128) -> String { + match change.cmp(&0) { + Ordering::Less => Paint::green(format!("{change}")).to_string(), + Ordering::Equal => { + format!("{change}") + } + Ordering::Greater => Paint::red(format!("{change}")).to_string(), + } +} + +/// Returns true of the difference between the gas values exceeds the tolerance +/// +/// If `tolerance` is `None`, then this returns `true` if both gas values are equal +fn within_tolerance(source_gas: u64, target_gas: u64, tolerance_pct: Option) -> bool { + if let Some(tolerance) = tolerance_pct { + let (hi, lo) = if source_gas > target_gas { + (source_gas, target_gas) + } else { + (target_gas, source_gas) + }; + let diff = (1. - (lo as f64 / hi as f64)) * 100.; + diff < tolerance as f64 + } else { + source_gas == target_gas + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_tolerance() { + assert!(within_tolerance(100, 105, Some(5))); + assert!(within_tolerance(105, 100, Some(5))); + assert!(!within_tolerance(100, 106, Some(5))); + assert!(!within_tolerance(106, 100, Some(5))); + assert!(within_tolerance(100, 100, None)); + } + + #[test] + fn can_parse_basic_snapshot_entry() { + let s = "Test:deposit() (gas: 7222)"; + let entry = SnapshotEntry::from_str(s).unwrap(); + assert_eq!( + entry, + SnapshotEntry { + contract_name: "Test".to_string(), + signature: "deposit()".to_string(), + gas_used: TestKindReport::Standard { gas: 7222 } + } + ); + } + + #[test] + fn can_parse_fuzz_snapshot_entry() { + let s = "Test:deposit() (runs: 256, μ: 100, ~:200)"; + let entry = SnapshotEntry::from_str(s).unwrap(); + assert_eq!( + entry, + SnapshotEntry { + contract_name: "Test".to_string(), + signature: "deposit()".to_string(), + gas_used: TestKindReport::Fuzz { runs: 256, median_gas: 200, mean_gas: 100 } + } + ); + } + + #[test] + fn can_parse_invariant_snapshot_entry() { + let s = "Test:deposit() (runs: 256, calls: 100, reverts: 200)"; + let entry = SnapshotEntry::from_str(s).unwrap(); + assert_eq!( + entry, + SnapshotEntry { + contract_name: "Test".to_string(), + signature: "deposit()".to_string(), + gas_used: TestKindReport::Invariant { runs: 256, calls: 100, reverts: 200 } + } + ); + } + + #[test] + fn can_parse_invariant_snapshot_entry2() { + let s = "ERC20Invariants:invariantBalanceSum() (runs: 256, calls: 3840, reverts: 2388)"; + let entry = SnapshotEntry::from_str(s).unwrap(); + assert_eq!( + entry, + SnapshotEntry { + contract_name: "ERC20Invariants".to_string(), + signature: "invariantBalanceSum()".to_string(), + gas_used: TestKindReport::Invariant { runs: 256, calls: 3840, reverts: 2388 } + } + ); + } +} diff --git a/crates/zkforge/bin/cmd/test/filter.rs b/crates/zkforge/bin/cmd/test/filter.rs new file mode 100644 index 000000000..971a2c9b8 --- /dev/null +++ b/crates/zkforge/bin/cmd/test/filter.rs @@ -0,0 +1,217 @@ +use clap::Parser; +use ethers::solc::{FileFilter, ProjectPathsConfig}; +use foundry_cli::utils::FoundryPathExt; +use foundry_common::glob::GlobMatcher; +use foundry_config::Config; +use std::{fmt, path::Path}; +use zkforge::TestFilter; + +/// The filter to use during testing. +/// +/// See also `FileFilter`. +#[derive(Clone, Parser, Default)] +#[clap(next_help_heading = "Test filtering")] +pub struct FilterArgs { + /// Only run test functions matching the specified regex pattern. + #[clap(long = "match-test", visible_alias = "mt", value_name = "REGEX")] + pub test_pattern: Option, + + /// Only run test functions that do not match the specified regex pattern. + #[clap(long = "no-match-test", visible_alias = "nmt", value_name = "REGEX")] + pub test_pattern_inverse: Option, + + /// Only run tests in contracts matching the specified regex pattern. + #[clap(long = "match-contract", visible_alias = "mc", value_name = "REGEX")] + pub contract_pattern: Option, + + /// Only run tests in contracts that do not match the specified regex pattern. + #[clap(long = "no-match-contract", visible_alias = "nmc", value_name = "REGEX")] + pub contract_pattern_inverse: Option, + + /// Only run tests in source files matching the specified glob pattern. + #[clap(long = "match-path", visible_alias = "mp", value_name = "GLOB")] + pub path_pattern: Option, + + /// Only run tests in source files that do not match the specified glob pattern. + #[clap( + name = "no-match-path", + long = "no-match-path", + visible_alias = "nmp", + value_name = "GLOB" + )] + pub path_pattern_inverse: Option, +} + +impl FilterArgs { + /// Merges the set filter globs with the config's values + pub fn merge_with_config(&self, config: &Config) -> ProjectPathsAwareFilter { + let mut filter = self.clone(); + if filter.test_pattern.is_none() { + filter.test_pattern = config.test_pattern.clone().map(|p| p.into()); + } + if filter.test_pattern_inverse.is_none() { + filter.test_pattern_inverse = config.test_pattern_inverse.clone().map(|p| p.into()); + } + if filter.contract_pattern.is_none() { + filter.contract_pattern = config.contract_pattern.clone().map(|p| p.into()); + } + if filter.contract_pattern_inverse.is_none() { + filter.contract_pattern_inverse = + config.contract_pattern_inverse.clone().map(|p| p.into()); + } + if filter.path_pattern.is_none() { + filter.path_pattern = config.path_pattern.clone().map(Into::into); + } + if filter.path_pattern_inverse.is_none() { + filter.path_pattern_inverse = config.path_pattern_inverse.clone().map(Into::into); + } + ProjectPathsAwareFilter { args_filter: filter, paths: config.project_paths() } + } +} + +impl fmt::Debug for FilterArgs { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FilterArgs") + .field("match-test", &self.test_pattern.as_ref().map(|r| r.as_str())) + .field("no-match-test", &self.test_pattern_inverse.as_ref().map(|r| r.as_str())) + .field("match-contract", &self.contract_pattern.as_ref().map(|r| r.as_str())) + .field("no-match-contract", &self.contract_pattern_inverse.as_ref().map(|r| r.as_str())) + .field("match-path", &self.path_pattern.as_ref().map(|g| g.as_str())) + .field("no-match-path", &self.path_pattern_inverse.as_ref().map(|g| g.as_str())) + .finish_non_exhaustive() + } +} + +impl FileFilter for FilterArgs { + /// Returns true if the file regex pattern match the `file` + /// + /// If no file regex is set this returns true if the file ends with `.t.sol`, see + /// [FoundryPathExr::is_sol_test()] + fn is_match(&self, file: &Path) -> bool { + if let Some(file) = file.as_os_str().to_str() { + if let Some(ref glob) = self.path_pattern { + return glob.is_match(file) + } + if let Some(ref glob) = self.path_pattern_inverse { + return !glob.is_match(file) + } + } + file.is_sol_test() + } +} + +impl TestFilter for FilterArgs { + fn matches_test(&self, test_name: impl AsRef) -> bool { + let mut ok = true; + let test_name = test_name.as_ref(); + if let Some(re) = &self.test_pattern { + ok &= re.is_match(test_name); + } + if let Some(re) = &self.test_pattern_inverse { + ok &= !re.is_match(test_name); + } + ok + } + + fn matches_contract(&self, contract_name: impl AsRef) -> bool { + let mut ok = true; + let contract_name = contract_name.as_ref(); + if let Some(re) = &self.contract_pattern { + ok &= re.is_match(contract_name); + } + if let Some(re) = &self.contract_pattern_inverse { + ok &= !re.is_match(contract_name); + } + ok + } + + fn matches_path(&self, path: impl AsRef) -> bool { + let mut ok = true; + let path = path.as_ref(); + if let Some(ref glob) = self.path_pattern { + ok &= glob.is_match(path); + } + if let Some(ref glob) = self.path_pattern_inverse { + ok &= !glob.is_match(path); + } + ok + } +} + +impl fmt::Display for FilterArgs { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut patterns = Vec::new(); + if let Some(ref p) = self.test_pattern { + patterns.push(format!("\tmatch-test: `{}`", p.as_str())); + } + if let Some(ref p) = self.test_pattern_inverse { + patterns.push(format!("\tno-match-test: `{}`", p.as_str())); + } + if let Some(ref p) = self.contract_pattern { + patterns.push(format!("\tmatch-contract: `{}`", p.as_str())); + } + if let Some(ref p) = self.contract_pattern_inverse { + patterns.push(format!("\tno-match-contract: `{}`", p.as_str())); + } + if let Some(ref p) = self.path_pattern { + patterns.push(format!("\tmatch-path: `{}`", p.as_str())); + } + if let Some(ref p) = self.path_pattern_inverse { + patterns.push(format!("\tno-match-path: `{}`", p.as_str())); + } + write!(f, "{}", patterns.join("\n")) + } +} + +/// A filter that combines all command line arguments and the paths of the current projects +#[derive(Debug, Clone)] +pub struct ProjectPathsAwareFilter { + args_filter: FilterArgs, + paths: ProjectPathsConfig, +} + +// === impl ProjectPathsAwareFilter === + +impl ProjectPathsAwareFilter { + /// Returns the CLI arguments + pub fn args(&self) -> &FilterArgs { + &self.args_filter + } + + /// Returns the CLI arguments mutably + pub fn args_mut(&mut self) -> &mut FilterArgs { + &mut self.args_filter + } +} + +impl FileFilter for ProjectPathsAwareFilter { + /// Returns true if the file regex pattern match the `file` + /// + /// If no file regex is set this returns true if the file ends with `.t.sol`, see + /// [FoundryPathExr::is_sol_test()] + fn is_match(&self, file: &Path) -> bool { + self.args_filter.is_match(file) + } +} + +impl TestFilter for ProjectPathsAwareFilter { + fn matches_test(&self, test_name: impl AsRef) -> bool { + self.args_filter.matches_test(test_name) + } + + fn matches_contract(&self, contract_name: impl AsRef) -> bool { + self.args_filter.matches_contract(contract_name) + } + + fn matches_path(&self, path: impl AsRef) -> bool { + let path = path.as_ref(); + // we don't want to test files that belong to a library + self.args_filter.matches_path(path) && !self.paths.has_library_ancestor(Path::new(path)) + } +} + +impl fmt::Display for ProjectPathsAwareFilter { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.args_filter.fmt(f) + } +} diff --git a/crates/zkforge/bin/cmd/test/mod.rs b/crates/zkforge/bin/cmd/test/mod.rs new file mode 100644 index 000000000..4133d4005 --- /dev/null +++ b/crates/zkforge/bin/cmd/test/mod.rs @@ -0,0 +1,884 @@ +use super::{install, test::filter::ProjectPathsAwareFilter, watch::WatchArgs}; +use crate::cmd::{ + zk_solc::{ZkSolc, ZkSolcOpts}, + zksolc_manager::{ZkSolcManagerBuilder, ZkSolcManagerOpts, DEFAULT_ZKSOLC_VERSION}, +}; +use alloy_primitives::U256; +use clap::Parser; +use eyre::Result; +use foundry_cli::{ + opts::CoreBuildArgs, + utils::{self, LoadConfig}, +}; +use foundry_common::{ + compact_to_contract, compile::ContractSources, evm::EvmArgs, get_contract_name, get_file_name, + shell, +}; +use foundry_config::{ + figment, + figment::{ + value::{Dict, Map}, + Metadata, Profile, Provider, + }, + get_available_profiles, Config, +}; +use foundry_debugger::DebuggerArgs; +use foundry_evm::fuzz::CounterExample; +use regex::Regex; +use std::{collections::BTreeMap, fs, sync::mpsc::channel, time::Duration}; +use tracing::trace; +use watchexec::config::{InitConfig, RuntimeConfig}; +use yansi::Paint; +use zkforge::{ + decode::decode_console_logs, + executor::inspector::CheatsConfig, + gas_report::GasReport, + result::{SuiteResult, TestResult, TestStatus}, + trace::{ + identifier::{EtherscanIdentifier, LocalTraceIdentifier, SignaturesIdentifier}, + CallTraceDecoderBuilder, TraceKind, + }, + MultiContractRunner, MultiContractRunnerBuilder, TestOptions, TestOptionsBuilder, +}; + +mod filter; +mod summary; +use summary::TestSummaryReporter; + +pub use filter::FilterArgs; + +// Loads project's figment and merges the build cli arguments into it +foundry_config::merge_impl_figment_convert!(TestArgs, opts, evm_opts); + +/// CLI arguments for `forge test`. +#[derive(Debug, Clone, Parser, Default)] +#[clap(next_help_heading = "Test options")] +pub struct TestArgs { + /// Run a test in the debugger. + /// + /// The argument passed to this flag is the name of the test function you want to run, and it + /// works the same as --match-test. + /// + /// If more than one test matches your specified criteria, you must add additional filters + /// until only one test is found (see --match-contract and --match-path). + /// + /// The matching test will be opened in the debugger regardless of the outcome of the test. + /// + /// If the matching test is a fuzz test, then it will open the debugger on the first failure + /// case. + /// If the fuzz test does not fail, it will open the debugger on the last fuzz case. + /// + /// For more fine-grained control of which fuzz case is run, see forge run. + #[clap(long, value_name = "TEST_FUNCTION")] + debug: Option, + + /// Print a gas report. + #[clap(long, env = "FORGE_GAS_REPORT")] + gas_report: bool, + + /// Exit with code 0 even if a test fails. + #[clap(long, env = "FORGE_ALLOW_FAILURE")] + allow_failure: bool, + + /// Output test results in JSON format. + #[clap(long, short, help_heading = "Display options")] + json: bool, + + /// Stop running tests after the first failure + #[clap(long)] + pub fail_fast: bool, + + /// The Etherscan (or equivalent) API key + #[clap(long, env = "ETHERSCAN_API_KEY", value_name = "KEY")] + etherscan_api_key: Option, + + /// List tests instead of running them + #[clap(long, short, help_heading = "Display options")] + list: bool, + + /// Set seed used to generate randomness during your fuzz runs. + #[clap(long, value_parser = utils::alloy_parse_u256)] + pub fuzz_seed: Option, + + #[clap(long, env = "FOUNDRY_FUZZ_RUNS", value_name = "RUNS")] + pub fuzz_runs: Option, + + #[clap(flatten)] + filter: FilterArgs, + + #[clap(flatten)] + evm_opts: EvmArgs, + + #[clap(flatten)] + opts: CoreBuildArgs, + + #[clap(flatten)] + pub watch: WatchArgs, + + /// Print test summary table + #[clap(long, help_heading = "Display options")] + pub summary: bool, + + /// Print detailed test summary table + #[clap(long, help_heading = "Display options")] + pub detailed: bool, +} + +impl TestArgs { + /// Returns the flattened [`CoreBuildArgs`]. + pub fn build_args(&self) -> &CoreBuildArgs { + &self.opts + } + + pub async fn run(self) -> Result { + trace!(target: "forge::test", "executing test command"); + shell::set_shell(shell::Shell::from_args(self.opts.silent, self.json))?; + self.execute_tests().await + } + + /// Executes all the tests in the project. + /// + /// This will trigger the build process first. On success all test contracts that match the + /// configured filter will be executed + /// + /// Returns the test results for all matching tests. + pub async fn execute_tests(self) -> Result { + // Merge all configs + let (mut config, mut evm_opts) = self.load_config_and_evm_opts_emit_warnings()?; + + let mut filter = self.filter(&config); + + trace!(target: "forge::test", ?filter, "using filter"); + + // Set up the project + let mut project = config.project()?; + + // install missing dependencies + if install::install_missing_dependencies(&mut config, self.build_args().silent) && + config.auto_detect_remappings + { + // need to re-configure here to also catch additional remappings + config = self.load_config(); + project = config.project()?; + } + + // Create test options from general project settings + // and compiler output + let project_root = project.paths.root.join("zkout"); + let toml = config.get_config_path(); + let profiles = get_available_profiles(toml)?; + + let zksolc_manager = + ZkSolcManagerBuilder::new(ZkSolcManagerOpts::new(DEFAULT_ZKSOLC_VERSION.to_owned())) + .build() + .unwrap(); + + let zksolc_opts = ZkSolcOpts { + compiler_path: zksolc_manager.get_full_compiler_path(), + is_system: false, + force_evmla: false, + }; + + let mut zksolc = ZkSolc::new(zksolc_opts, project); + let output = zksolc.compile().unwrap(); + + let test_options: TestOptions = TestOptionsBuilder::default() + .fuzz(config.fuzz) + .invariant(config.invariant) + .profiles(profiles) + .build(&output, &project_root)?; + + // Determine print verbosity and executor verbosity + let verbosity = evm_opts.verbosity; + if self.gas_report && evm_opts.verbosity < 3 { + evm_opts.verbosity = 3; + } + + let env = evm_opts.evm_env().await?; + + // Prepare the test builder + let should_debug = self.debug.is_some(); + + let mut runner_builder = MultiContractRunnerBuilder::default() + .set_debug(should_debug) + .initial_balance(evm_opts.initial_balance) + .evm_spec(config.evm_spec_id()) + .sender(evm_opts.sender) + .with_fork(evm_opts.get_fork(&config, env.clone())) + .with_cheats_config(CheatsConfig::new(&config, &evm_opts)) + .with_test_options(test_options.clone()); + + let mut runner = runner_builder.clone().build( + &project_root, + output.clone(), + env.clone(), + evm_opts.clone(), + )?; + + if should_debug { + filter.args_mut().test_pattern = self.debug.clone(); + let num_filtered = runner.matching_test_function_count(&filter); + if num_filtered != 1 { + return Err( + eyre::eyre!("{num_filtered} tests matched your criteria, but exactly 1 test must match in order to run the debugger.\n + \n + Use --match-contract and --match-path to further limit the search.")); + } + let test_funcs = runner.get_matching_test_functions(&filter); + // if we debug a fuzz test, we should not collect data on the first run + if !test_funcs.get(0).expect("matching function exists").inputs.is_empty() { + runner_builder = runner_builder.set_debug(false); + runner = runner_builder.clone().build( + project_root, + output.clone(), + env.clone(), + evm_opts.clone(), + )?; + } + } + + let known_contracts = runner.known_contracts.clone(); + let mut local_identifier = LocalTraceIdentifier::new(&known_contracts); + let remote_chain_id = runner.evm_opts.get_remote_chain_id(); + + let outcome = self + .run_tests(runner, config.clone(), verbosity, filter.clone(), test_options.clone()) + .await?; + + if should_debug { + let tests = outcome.clone().into_tests(); + let mut decoders = Vec::new(); + for test in tests { + let mut result = test.result; + // Identify addresses in each trace + let mut builder = CallTraceDecoderBuilder::new() + .with_labels(result.labeled_addresses.clone()) + .with_events(local_identifier.events().cloned()) + .with_verbosity(verbosity); + + // Signatures are of no value for gas reports + if !self.gas_report { + let sig_identifier = + SignaturesIdentifier::new(Config::foundry_cache_dir(), config.offline)?; + builder = builder.with_signature_identifier(sig_identifier.clone()); + } + + let mut decoder = builder.build(); + + if !result.traces.is_empty() { + // Set up identifiers + // Do not re-query etherscan for contracts that you've already queried today. + let mut etherscan_identifier = + EtherscanIdentifier::new(&config, remote_chain_id)?; + + // Decode the traces + for (kind, trace) in &mut result.traces { + decoder.identify(trace, &mut local_identifier); + decoder.identify(trace, &mut etherscan_identifier); + + let should_include = match kind { + // At verbosity level 3, we only display traces for failed tests + // At verbosity level 4, we also display the setup trace for failed + // tests At verbosity level 5, we display + // all traces for all tests + TraceKind::Setup => { + (verbosity >= 5) || + (verbosity == 4 && result.status == TestStatus::Failure) + } + TraceKind::Execution => { + verbosity > 3 || + (verbosity == 3 && result.status == TestStatus::Failure) + } + _ => false, + }; + + // We decode the trace if we either need to build a gas report or we need + // to print it + if should_include || self.gas_report { + decoder.decode(trace).await; + } + } + } + + decoders.push(decoder); + } + + let mut sources: ContractSources = Default::default(); + for (id, artifact) in output.into_artifacts() { + // Sources are only required for the debugger, but it *might* mean that there's + // something wrong with the build and/or artifacts. + if let Some(source) = artifact.source_file() { + let abs_path = source + .ast + .ok_or(eyre::eyre!("Source from artifact has no AST."))? + .absolute_path; + let source_code = fs::read_to_string(abs_path)?; + let contract = artifact.clone().into_contract_bytecode(); + let source_contract = compact_to_contract(contract)?; + sources + .0 + .entry(id.clone().name) + .or_default() + .insert(source.id, (source_code, source_contract)); + } + } + + let test = outcome.clone().into_tests().next().unwrap(); + let result = test.result; + // Run the debugger + let debugger = DebuggerArgs { + debug: result.debug.map_or(vec![], |debug| vec![debug]), + decoder: decoders.first().unwrap(), + sources, + breakpoints: result.breakpoints, + }; + debugger.run()?; + } + + Ok(outcome) + } + + /// Run all tests that matches the filter predicate from a test runner + pub async fn run_tests( + &self, + mut runner: MultiContractRunner, + config: Config, + verbosity: u8, + mut filter: ProjectPathsAwareFilter, + test_options: TestOptions, + ) -> eyre::Result { + if self.debug.is_some() { + filter.args_mut().test_pattern = self.debug.clone(); + // Run the test + let results = runner.test(&filter, None, test_options).await; + + Ok(TestOutcome::new(results, self.allow_failure)) + } else if self.list { + list(runner, filter, self.json) + } else { + if self.detailed && !self.summary { + return Err(eyre::eyre!( + "Missing `--summary` option in your command. You must pass it along with the `--detailed` option to view detailed test summary." + )); + } + test( + config, + runner, + verbosity, + filter, + self.json, + self.allow_failure, + test_options, + self.gas_report, + self.fail_fast, + self.summary, + self.detailed, + ) + .await + } + } + + /// Returns the flattened [`FilterArgs`] arguments merged with [`Config`]. + pub fn filter(&self, config: &Config) -> ProjectPathsAwareFilter { + self.filter.merge_with_config(config) + } + + /// Returns whether `BuildArgs` was configured with `--watch` + pub fn is_watch(&self) -> bool { + self.watch.watch.is_some() + } + + /// Returns the [`watchexec::InitConfig`] and [`watchexec::RuntimeConfig`] necessary to + /// bootstrap a new [`watchexe::Watchexec`] loop. + pub(crate) fn watchexec_config(&self) -> Result<(InitConfig, RuntimeConfig)> { + self.watch.watchexec_config(|| { + let config = Config::from(self); + vec![config.src, config.test] + }) + } +} + +impl Provider for TestArgs { + fn metadata(&self) -> Metadata { + Metadata::named("Core Build Args Provider") + } + + fn data(&self) -> Result, figment::Error> { + let mut dict = Dict::default(); + + let mut fuzz_dict = Dict::default(); + if let Some(fuzz_seed) = self.fuzz_seed { + fuzz_dict.insert("seed".to_string(), fuzz_seed.to_string().into()); + } + if let Some(fuzz_runs) = self.fuzz_runs { + fuzz_dict.insert("runs".to_string(), fuzz_runs.into()); + } + dict.insert("fuzz".to_string(), fuzz_dict.into()); + + if let Some(ref etherscan_api_key) = self.etherscan_api_key { + dict.insert("etherscan_api_key".to_string(), etherscan_api_key.to_string().into()); + } + + Ok(Map::from([(Config::selected_profile(), dict)])) + } +} + +/// The result of a single test +#[derive(Debug, Clone)] +pub struct Test { + /// The identifier of the artifact/contract in the form of `:` + pub artifact_id: String, + /// The signature of the solidity test + pub signature: String, + /// Result of the executed solidity test + pub result: TestResult, +} + +impl Test { + pub fn gas_used(&self) -> u64 { + self.result.kind.report().gas() + } + + /// Returns the contract name of the artifact id + pub fn contract_name(&self) -> &str { + get_contract_name(&self.artifact_id) + } + + /// Returns the file name of the artifact id + pub fn file_name(&self) -> &str { + get_file_name(&self.artifact_id) + } +} + +/// Represents the bundled results of all tests +#[derive(Clone)] +pub struct TestOutcome { + /// Whether failures are allowed + pub allow_failure: bool, + /// Results for each suite of tests `contract -> SuiteResult` + pub results: BTreeMap, +} + +impl TestOutcome { + fn new(results: BTreeMap, allow_failure: bool) -> Self { + Self { results, allow_failure } + } + + /// Iterator over all succeeding tests and their names + pub fn successes(&self) -> impl Iterator { + self.tests().filter(|(_, t)| t.status == TestStatus::Success) + } + + /// Iterator over all failing tests and their names + pub fn failures(&self) -> impl Iterator { + self.tests().filter(|(_, t)| t.status == TestStatus::Failure) + } + + pub fn skips(&self) -> impl Iterator { + self.tests().filter(|(_, t)| t.status == TestStatus::Skipped) + } + + /// Iterator over all tests and their names + pub fn tests(&self) -> impl Iterator { + self.results.values().flat_map(|suite| suite.tests()) + } + + /// Returns an iterator over all `Test` + pub fn into_tests(self) -> impl Iterator { + self.results + .into_iter() + .flat_map(|(file, SuiteResult { test_results, .. })| { + test_results.into_iter().map(move |t| (file.clone(), t)) + }) + .map(|(artifact_id, (signature, result))| Test { artifact_id, signature, result }) + } + + /// Checks if there are any failures and failures are disallowed + pub fn ensure_ok(&self) -> Result<()> { + let failures = self.failures().count(); + if self.allow_failure || failures == 0 { + return Ok(()) + } + + if !shell::verbosity().is_normal() { + // skip printing and exit early + std::process::exit(1); + } + + println!(); + println!("Failing tests:"); + for (suite_name, suite) in self.results.iter() { + let failures = suite.failures().count(); + if failures == 0 { + continue + } + + let term = if failures > 1 { "tests" } else { "test" }; + println!("Encountered {failures} failing {term} in {suite_name}"); + for (name, result) in suite.failures() { + short_test_result(name, result); + } + println!(); + } + let successes = self.successes().count(); + println!( + "Encountered a total of {} failing tests, {} tests succeeded", + Paint::red(failures.to_string()), + Paint::green(successes.to_string()) + ); + + std::process::exit(1); + } + + pub fn duration(&self) -> Duration { + self.results + .values() + .fold(Duration::ZERO, |acc, SuiteResult { duration, .. }| acc + *duration) + } + + pub fn summary(&self) -> String { + let failed = self.failures().count(); + let result = if failed == 0 { Paint::green("ok") } else { Paint::red("FAILED") }; + format!( + "Test result: {}. {} passed; {} failed; {} skipped; finished in {:.2?}", + result, + Paint::green(self.successes().count()), + Paint::red(failed), + Paint::yellow(self.skips().count()), + self.duration() + ) + } +} + +fn short_test_result(name: &str, result: &TestResult) { + let status = if result.status == TestStatus::Success { + Paint::green("[PASS]".to_string()) + } else if result.status == TestStatus::Skipped { + Paint::yellow("[SKIP]".to_string()) + } else { + let reason = result + .reason + .as_ref() + .map(|reason| format!("Reason: {reason}")) + .unwrap_or_else(|| "Reason: Assertion failed.".to_string()); + + let counterexample = result + .counterexample + .as_ref() + .map(|example| match example { + CounterExample::Single(eg) => format!(" Counterexample: {eg}]"), + CounterExample::Sequence(sequence) => { + let mut inner_txt = String::new(); + + for checkpoint in sequence { + inner_txt += format!("\t\t{checkpoint}\n").as_str(); + } + format!("]\n\t[Sequence]\n{inner_txt}\n") + } + }) + .unwrap_or_else(|| "]".to_string()); + + Paint::red(format!("[FAIL. {reason}{counterexample}")) + }; + + println!("{status} {name} {}", result.kind.report()); +} + +/** + * Formats the aggregated summary of all test suites into a string (for printing) + */ +fn format_aggregated_summary( + num_test_suites: usize, + total_passed: usize, + total_failed: usize, + total_skipped: usize, +) -> String { + let total_tests = total_passed + total_failed + total_skipped; + format!( + " \nRan {} test suites: {} tests passed, {} failed, {} skipped ({} total tests)", + num_test_suites, + Paint::green(total_passed), + Paint::red(total_failed), + Paint::yellow(total_skipped), + total_tests + ) +} + +/// Lists all matching tests +fn list( + runner: MultiContractRunner, + filter: ProjectPathsAwareFilter, + json: bool, +) -> Result { + let results = runner.list(&filter); + + if json { + println!("{}", serde_json::to_string(&results)?); + } else { + for (file, contracts) in results.iter() { + println!("{file}"); + for (contract, tests) in contracts.iter() { + println!(" {contract}"); + println!(" {}\n", tests.join("\n ")); + } + } + } + Ok(TestOutcome::new(BTreeMap::new(), false)) +} + +/// Runs all the tests +#[allow(clippy::too_many_arguments)] +async fn test( + config: Config, + mut runner: MultiContractRunner, + verbosity: u8, + filter: ProjectPathsAwareFilter, + json: bool, + allow_failure: bool, + test_options: TestOptions, + gas_reporting: bool, + fail_fast: bool, + summary: bool, + detailed: bool, +) -> Result { + trace!(target: "forge::test", "running all tests"); + + if runner.matching_test_function_count(&filter) == 0 { + let filter_str = filter.to_string(); + if filter_str.is_empty() { + println!( + "\nNo tests found in project! Forge looks for functions that starts with `test`." + ); + } else { + println!("\nNo tests match the provided pattern:"); + println!("{filter_str}"); + // Try to suggest a test when there's no match + if let Some(ref test_pattern) = filter.args().test_pattern { + let test_name = test_pattern.as_str(); + let candidates = runner.get_tests(&filter); + if let Some(suggestion) = utils::did_you_mean(test_name, candidates).pop() { + println!("\nDid you mean `{suggestion}`?"); + } + } + } + } + + if json { + let results = runner.test(filter, None, test_options).await; + println!("{}", serde_json::to_string(&results)?); + return Ok(TestOutcome::new(results, allow_failure)) + } + + // Set up identifiers + let known_contracts = runner.known_contracts.clone(); + let mut local_identifier = LocalTraceIdentifier::new(&known_contracts); + let remote_chain_id = runner.evm_opts.get_remote_chain_id(); + // Do not re-query etherscan for contracts that you've already queried today. + let mut etherscan_identifier = EtherscanIdentifier::new(&config, remote_chain_id)?; + + // Set up test reporter channel + let (tx, rx) = channel::<(String, SuiteResult)>(); + + // Run tests + let handle = + tokio::task::spawn(async move { runner.test(filter, Some(tx), test_options).await }); + + let mut results: BTreeMap = BTreeMap::new(); + let mut gas_report = GasReport::new(config.gas_reports, config.gas_reports_ignore); + let sig_identifier = SignaturesIdentifier::new(Config::foundry_cache_dir(), config.offline)?; + + let mut total_passed = 0; + let mut total_failed = 0; + let mut total_skipped = 0; + let mut suite_results: Vec = Vec::new(); + + 'outer: for (contract_name, suite_result) in rx { + results.insert(contract_name.clone(), suite_result.clone()); + + let mut tests = suite_result.test_results.clone(); + println!(); + for warning in suite_result.warnings.iter() { + eprintln!("{} {warning}", Paint::yellow("Warning:").bold()); + } + if !tests.is_empty() { + let term = if tests.len() > 1 { "tests" } else { "test" }; + println!("Running {} {term} for {contract_name}", tests.len()); + } + for (name, result) in &mut tests { + short_test_result(name, result); + + // If the test failed, we want to stop processing the rest of the tests + if fail_fast && result.status == TestStatus::Failure { + break 'outer + } + + // We only display logs at level 2 and above + if verbosity >= 2 { + // We only decode logs from Hardhat and DS-style console events + let console_logs = decode_console_logs(&result.logs); + if !console_logs.is_empty() { + println!("Logs:"); + for log in console_logs { + println!(" {log}"); + } + println!(); + } + } + + if result.traces.is_empty() { + continue + } + + // Identify addresses in each trace + let mut builder = CallTraceDecoderBuilder::new() + .with_labels(result.labeled_addresses.iter().map(|(a, s)| (*a, s.clone()))) + .with_events(local_identifier.events().cloned()) + .with_verbosity(verbosity); + + // Signatures are of no value for gas reports + if !gas_reporting { + builder = builder.with_signature_identifier(sig_identifier.clone()); + } + + let mut decoder = builder.build(); + + // Decode the traces + let mut decoded_traces = Vec::with_capacity(result.traces.len()); + for (kind, trace) in &mut result.traces { + decoder.identify(trace, &mut local_identifier); + decoder.identify(trace, &mut etherscan_identifier); + + // verbosity: + // - 0..3: nothing + // - 3: only display traces for failed tests + // - 4: also display the setup trace for failed tests + // - 5..: display all traces for all tests + let should_include = match kind { + TraceKind::Execution => { + (verbosity == 3 && result.status.is_failure()) || verbosity >= 4 + } + TraceKind::Setup => { + (verbosity == 4 && result.status.is_failure()) || verbosity >= 5 + } + TraceKind::Deployment => false, + }; + + // Decode the trace if we either need to build a gas report or we need to print it + if should_include || gas_reporting { + decoder.decode(trace).await; + } + + if should_include { + decoded_traces.push(trace.to_string()); + } + } + + if !decoded_traces.is_empty() { + println!("Traces:"); + decoded_traces.into_iter().for_each(|trace| println!("{trace}")); + } + + if gas_reporting { + gas_report.analyze(&result.traces); + } + } + let block_outcome = + TestOutcome::new([(contract_name.clone(), suite_result)].into(), allow_failure); + + total_passed += block_outcome.successes().count(); + total_failed += block_outcome.failures().count(); + total_skipped += block_outcome.skips().count(); + + println!("{}", block_outcome.summary()); + + if summary { + suite_results.push(block_outcome.clone()); + } + } + + if gas_reporting { + println!("{}", gas_report.finalize()); + } + + let num_test_suites = results.len(); + + if num_test_suites > 0 { + println!( + "{}", + format_aggregated_summary(num_test_suites, total_passed, total_failed, total_skipped) + ); + + if summary { + let mut summary_table = TestSummaryReporter::new(detailed); + println!("\n\nTest Summary:"); + summary_table.print_summary(suite_results); + } + } + + // reattach the thread + let _results = handle.await?; + + trace!(target: "forge::test", "received {} results", results.len()); + + Ok(TestOutcome::new(results, allow_failure)) +} + +#[cfg(test)] +mod tests { + use super::*; + + // Sanity test that unknown args are rejected + #[test] + fn test_verbosity() { + #[derive(Debug, Parser)] + pub struct VerbosityArgs { + #[clap(long, short, action = clap::ArgAction::Count)] + verbosity: u8, + } + let res = VerbosityArgs::try_parse_from(["foundry-cli", "-vw"]); + assert!(res.is_err()); + + let res = VerbosityArgs::try_parse_from(["foundry-cli", "-vv"]); + assert!(res.is_ok()); + } + + #[test] + fn test_verbosity_multi_short() { + #[derive(Debug, Parser)] + pub struct VerbosityArgs { + #[clap(long, short)] + verbosity: bool, + #[clap( + long, + short, + num_args(0..), + value_name = "PATH", + )] + watch: Option>, + } + // this is supported by clap + let res = VerbosityArgs::try_parse_from(["foundry-cli", "-vw"]); + assert!(res.is_ok()) + } + + #[test] + fn test_watch_parse() { + let args: TestArgs = TestArgs::parse_from(["foundry-cli", "-vw"]); + assert!(args.watch.watch.is_some()); + } + + #[test] + fn test_fuzz_seed() { + let args: TestArgs = TestArgs::parse_from(["foundry-cli", "--fuzz-seed", "0x10"]); + assert!(args.fuzz_seed.is_some()); + } + + // + #[test] + fn test_5913() { + let args: TestArgs = + TestArgs::parse_from(["foundry-cli", "-vvv", "--gas-report", "--fuzz-seed", "0x10"]); + assert!(args.fuzz_seed.is_some()); + } +} diff --git a/crates/zkforge/bin/cmd/test/summary.rs b/crates/zkforge/bin/cmd/test/summary.rs new file mode 100644 index 000000000..5f8bd9650 --- /dev/null +++ b/crates/zkforge/bin/cmd/test/summary.rs @@ -0,0 +1,111 @@ +use crate::cmd::test::TestOutcome; +use comfy_table::{ + modifiers::UTF8_ROUND_CORNERS, Attribute, Cell, CellAlignment, Color, Row, Table, +}; + +/// A simple summary reporter that prints the test results in a table. +pub struct TestSummaryReporter { + /// The test summary table. + pub(crate) table: Table, + pub(crate) is_detailed: bool, +} + +impl TestSummaryReporter { + pub(crate) fn new(is_detailed: bool) -> Self { + let mut table = Table::new(); + table.apply_modifier(UTF8_ROUND_CORNERS); + let mut row = Row::from(vec![ + Cell::new("Test Suite") + .set_alignment(CellAlignment::Center) + .add_attribute(Attribute::Bold), + Cell::new("Passed") + .set_alignment(CellAlignment::Center) + .add_attribute(Attribute::Bold) + .fg(Color::Green), + Cell::new("Failed") + .set_alignment(CellAlignment::Center) + .add_attribute(Attribute::Bold) + .fg(Color::Red), + Cell::new("Skipped") + .set_alignment(CellAlignment::Center) + .add_attribute(Attribute::Bold) + .fg(Color::Yellow), + ]); + if is_detailed { + row.add_cell( + Cell::new("File Path") + .set_alignment(CellAlignment::Center) + .add_attribute(Attribute::Bold), + ); + row.add_cell( + Cell::new("Duration") + .set_alignment(CellAlignment::Center) + .add_attribute(Attribute::Bold), + ); + } + table.set_header(row); + + Self { table, is_detailed } + } + + pub(crate) fn print_summary(&mut self, mut test_results: Vec) { + // Sort by suite name first + + // Using `sort_by_cached_key` so that the key extraction logic runs only once + test_results.sort_by_cached_key(|test_outcome| { + test_outcome + .results + .keys() + .next() + .and_then(|suite| suite.split(':').nth(1)) + .unwrap() + .to_string() + }); + + // Traverse the test_results vector and build the table + for suite in &test_results { + for contract in suite.results.keys() { + let mut row = Row::new(); + let suite_name = contract.split(':').nth(1).unwrap(); + let suite_path = contract.split(':').nth(0).unwrap(); + + let passed = suite.successes().count(); + let mut passed_cell = Cell::new(passed).set_alignment(CellAlignment::Center); + + let failed = suite.failures().count(); + let mut failed_cell = Cell::new(failed).set_alignment(CellAlignment::Center); + + let skipped = suite.skips().count(); + let mut skipped_cell = Cell::new(skipped).set_alignment(CellAlignment::Center); + + let duration = suite.duration(); + + row.add_cell(Cell::new(suite_name)); + + if passed > 0 { + passed_cell = passed_cell.fg(Color::Green); + } + row.add_cell(passed_cell); + + if failed > 0 { + failed_cell = failed_cell.fg(Color::Red); + } + row.add_cell(failed_cell); + + if skipped > 0 { + skipped_cell = skipped_cell.fg(Color::Yellow); + } + row.add_cell(skipped_cell); + + if self.is_detailed { + row.add_cell(Cell::new(suite_path)); + row.add_cell(Cell::new(format!("{:.2?}", duration).to_string())); + } + + self.table.add_row(row); + } + } + // Print the summary table + println!("\n{}", self.table); + } +} diff --git a/crates/zkforge/bin/cmd/tree.rs b/crates/zkforge/bin/cmd/tree.rs new file mode 100644 index 000000000..b7e766951 --- /dev/null +++ b/crates/zkforge/bin/cmd/tree.rs @@ -0,0 +1,37 @@ +use clap::Parser; +use ethers::solc::{ + resolver::{Charset, TreeOptions}, + Graph, +}; +use eyre::Result; +use foundry_cli::{opts::ProjectPathsArgs, utils::LoadConfig}; + +/// CLI arguments for `forge tree`. +#[derive(Debug, Clone, Parser)] +pub struct TreeArgs { + /// Do not de-duplicate (repeats all shared dependencies) + #[clap(long)] + no_dedupe: bool, + + /// Character set to use in output. + /// + /// [possible values: utf8, ascii] + #[clap(long, default_value = "utf8")] + charset: Charset, + + #[clap(flatten)] + opts: ProjectPathsArgs, +} + +foundry_config::impl_figment_convert!(TreeArgs, opts); + +impl TreeArgs { + pub fn run(self) -> Result<()> { + let config = self.try_load_config_emit_warnings()?; + let graph = Graph::resolve(&config.project_paths())?; + let opts = TreeOptions { charset: self.charset, no_dedupe: self.no_dedupe }; + graph.print_with_options(opts); + + Ok(()) + } +} diff --git a/crates/zkforge/bin/cmd/update.rs b/crates/zkforge/bin/cmd/update.rs new file mode 100644 index 000000000..083c88924 --- /dev/null +++ b/crates/zkforge/bin/cmd/update.rs @@ -0,0 +1,56 @@ +use clap::{Parser, ValueHint}; +use eyre::{Context, Result}; +use foundry_cli::{ + opts::Dependency, + utils::{Git, LoadConfig}, +}; +use foundry_config::{impl_figment_convert_basic, Config}; +use std::path::PathBuf; + +/// CLI arguments for `forge update`. +#[derive(Debug, Clone, Parser)] +pub struct UpdateArgs { + /// The dependencies you want to update. + dependencies: Vec, + + /// The project's root path. + /// + /// By default root of the Git repository, if in one, + /// or the current working directory. + #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + root: Option, + + /// Override the up-to-date check. + #[clap(short, long)] + force: bool, +} +impl_figment_convert_basic!(UpdateArgs); + +impl UpdateArgs { + pub fn run(self) -> Result<()> { + let config = self.try_load_config_emit_warnings()?; + let (root, paths) = dependencies_paths(&self.dependencies, &config)?; + Git::new(&root).submodule_update(self.force, true, false, paths) + } +} + +/// Returns `(root, paths)` where `root` is the root of the Git repository and `paths` are the +/// relative paths of the dependencies. +pub fn dependencies_paths(deps: &[Dependency], config: &Config) -> Result<(PathBuf, Vec)> { + let git_root = Git::root_of(&config.__root.0)?; + let libs = config.install_lib_dir(); + + let mut paths = Vec::with_capacity(deps.len()); + for dep in deps { + let name = dep.name(); + let dep_path = libs.join(name); + let rel_path = dep_path + .strip_prefix(&git_root) + .wrap_err("Library directory is not relative to the repository root")?; + if !dep_path.exists() { + eyre::bail!("Could not find dependency {name:?} in {}", dep_path.display()); + } + paths.push(rel_path.to_owned()); + } + Ok((git_root, paths)) +} diff --git a/crates/zkforge/bin/cmd/verify/etherscan/flatten.rs b/crates/zkforge/bin/cmd/verify/etherscan/flatten.rs new file mode 100644 index 000000000..fb3205792 --- /dev/null +++ b/crates/zkforge/bin/cmd/verify/etherscan/flatten.rs @@ -0,0 +1,112 @@ +use super::{EtherscanSourceProvider, VerifyArgs}; +use ethers::{ + etherscan::verify::CodeFormat, + solc::{ + artifacts::{BytecodeHash, Source}, + AggregatedCompilerOutput, CompilerInput, Project, Solc, + }, +}; +use eyre::{Context, Result}; +use semver::{BuildMetadata, Version}; +use std::{collections::BTreeMap, path::Path}; + +#[derive(Debug)] +pub struct EtherscanFlattenedSource; +impl EtherscanSourceProvider for EtherscanFlattenedSource { + fn source( + &self, + args: &VerifyArgs, + project: &Project, + target: &Path, + version: &Version, + ) -> Result<(String, String, CodeFormat)> { + let metadata = project.solc_config.settings.metadata.as_ref(); + let bch = metadata.and_then(|m| m.bytecode_hash).unwrap_or_default(); + + eyre::ensure!( + bch == BytecodeHash::Ipfs, + "When using flattened source, bytecodeHash must be set to ipfs because Etherscan uses IPFS in its Compiler Settings when re-compiling your code. BytecodeHash is currently: {}. Hint: Set the bytecodeHash key in your foundry.toml :)", + bch, + ); + + let source = project.flatten(target).wrap_err("Failed to flatten contract")?; + + if !args.force { + // solc dry run of flattened code + self.check_flattened(source.clone(), version, target).map_err(|err| { + eyre::eyre!( + "Failed to compile the flattened code locally: `{}`\ + To skip this solc dry, have a look at the `--force` flag of this command.", + err + ) + })?; + } + + let name = args.contract.name.clone(); + Ok((source, name, CodeFormat::SingleFile)) + } +} + +impl EtherscanFlattenedSource { + /// Attempts to compile the flattened content locally with the compiler version. + /// + /// This expects the completely flattened `content´ and will try to compile it using the + /// provided compiler. If the compiler is missing it will be installed. + /// + /// # Errors + /// + /// If it failed to install a missing solc compiler + /// + /// # Exits + /// + /// If the solc compiler output contains errors, this could either be due to a bug in the + /// flattening code or could to conflict in the flattened code, for example if there are + /// multiple interfaces with the same name. + fn check_flattened( + &self, + content: impl Into, + version: &Version, + contract_path: &Path, + ) -> Result<()> { + let version = strip_build_meta(version.clone()); + let solc = Solc::find_svm_installed_version(version.to_string())? + .unwrap_or(Solc::blocking_install(&version)?); + + let input = CompilerInput { + language: "Solidity".to_string(), + sources: BTreeMap::from([("contract.sol".into(), Source::new(content))]), + settings: Default::default(), + }; + + let out = solc.compile(&input)?; + if out.has_error() { + let mut o = AggregatedCompilerOutput::default(); + o.extend(version, out); + eprintln!("{}", o.diagnostics(&[], Default::default())); + + eprintln!( + r#"Failed to compile the flattened code locally. +This could be a bug, please inspect the output of `forge flatten {}` and report an issue. +To skip this solc dry, pass `--force`. +"#, + contract_path.display() + ); + std::process::exit(1) + } + + Ok(()) + } +} + +/// Strips [BuildMetadata] from the [Version] +/// +/// **Note:** this is only for local compilation as a dry run, therefore this will return a +/// sanitized variant of the specific version so that it can be installed. This is merely +/// intended to ensure the flattened code can be compiled without errors. +fn strip_build_meta(version: Version) -> Version { + if version.build != BuildMetadata::EMPTY { + Version::new(version.major, version.minor, version.patch) + } else { + version + } +} diff --git a/crates/zkforge/bin/cmd/verify/etherscan/mod.rs b/crates/zkforge/bin/cmd/verify/etherscan/mod.rs new file mode 100644 index 000000000..7b57d13db --- /dev/null +++ b/crates/zkforge/bin/cmd/verify/etherscan/mod.rs @@ -0,0 +1,597 @@ +use super::{provider::VerificationProvider, VerifyArgs, VerifyCheckArgs}; +use crate::cmd::retry::RETRY_CHECK_ON_VERIFY; +use ethers::{ + abi::Function, + etherscan::{ + utils::lookup_compiler_version, + verify::{CodeFormat, VerifyContract}, + Client, + }, + prelude::errors::EtherscanError, + solc::{artifacts::CompactContract, cache::CacheEntry, Project, Solc}, + utils::to_checksum, +}; +use eyre::{eyre, Context, Result}; +use foundry_cli::utils::{get_cached_entry_by_name, read_constructor_args_file, LoadConfig}; +use foundry_common::abi::encode_args; +use foundry_config::{Chain, Config, SolcReq}; +use foundry_utils::{types::ToEthers, Retry}; +use futures::FutureExt; +use once_cell::sync::Lazy; +use regex::Regex; +use semver::{BuildMetadata, Version}; +use std::{ + fmt::Debug, + path::{Path, PathBuf}, +}; +use tracing::{error, trace, warn}; + +mod flatten; +mod standard_json; + +pub static RE_BUILD_COMMIT: Lazy = + Lazy::new(|| Regex::new(r"(?Pcommit\.[0-9,a-f]{8})").unwrap()); + +#[derive(Debug, Clone, Default)] +#[non_exhaustive] +pub struct EtherscanVerificationProvider { + /// Memoized cached entry of the target contract + cached_entry: Option<(PathBuf, CacheEntry, CompactContract)>, +} + +/// The contract source provider for [EtherscanVerificationProvider] +/// +/// Returns source, contract_name and the source [CodeFormat] +trait EtherscanSourceProvider: Send + Sync + Debug { + fn source( + &self, + args: &VerifyArgs, + project: &Project, + target: &Path, + version: &Version, + ) -> Result<(String, String, CodeFormat)>; +} + +#[async_trait::async_trait] +impl VerificationProvider for EtherscanVerificationProvider { + async fn preflight_check(&mut self, args: VerifyArgs) -> Result<()> { + let _ = self.prepare_request(&args).await?; + Ok(()) + } + + async fn verify(&mut self, args: VerifyArgs) -> Result<()> { + let (etherscan, verify_args) = self.prepare_request(&args).await?; + + if self.is_contract_verified(ðerscan, &verify_args).await? { + println!( + "\nContract [{}] {:?} is already verified. Skipping verification.", + verify_args.contract_name, + to_checksum(&verify_args.address, None) + ); + + return Ok(()) + } + + trace!(target : "forge::verify", ?verify_args, "submitting verification request"); + + let retry: Retry = args.retry.into(); + let resp = retry.run_async(|| { + async { + println!("\nSubmitting verification for [{}] {:?}.", verify_args.contract_name, to_checksum(&verify_args.address, None)); + let resp = etherscan + .submit_contract_verification(&verify_args) + .await + .wrap_err_with(|| { + // valid json + let args = serde_json::to_string(&verify_args).unwrap(); + error!(target : "forge::verify", ?args, "Failed to submit verification"); + format!("Failed to submit contract verification, payload:\n{args}") + })?; + + trace!(target : "forge::verify", ?resp, "Received verification response"); + + if resp.status == "0" { + if resp.result == "Contract source code already verified" { + return Ok(None) + } + + if resp.result.starts_with("Unable to locate ContractCode at") { + warn!("{}", resp.result); + return Err(eyre!("Etherscan could not detect the deployment.")) + } + + warn!("Failed verify submission: {:?}", resp); + eprintln!( + "Encountered an error verifying this contract:\nResponse: `{}`\nDetails: `{}`", + resp.message, resp.result + ); + std::process::exit(1); + } + + Ok(Some(resp)) + } + .boxed() + }).await?; + + if let Some(resp) = resp { + println!( + "Submitted contract for verification:\n\tResponse: `{}`\n\tGUID: `{}`\n\tURL: + {}", + resp.message, + resp.result, + etherscan.address_url(args.address.to_ethers()) + ); + + if args.watch { + let check_args = VerifyCheckArgs { + id: resp.result, + etherscan: args.etherscan, + retry: RETRY_CHECK_ON_VERIFY, + verifier: args.verifier, + }; + // return check_args.run().await + return self.check(check_args).await + } + } else { + println!("Contract source code already verified"); + } + + Ok(()) + } + + /// Executes the command to check verification status on Etherscan + async fn check(&self, args: VerifyCheckArgs) -> Result<()> { + let config = args.try_load_config_emit_warnings()?; + let etherscan = self.client( + args.etherscan.chain.unwrap_or_default(), + args.verifier.verifier_url.as_deref(), + args.etherscan.key.as_deref(), + &config, + )?; + let retry: Retry = args.retry.into(); + retry + .run_async(|| { + async { + let resp = etherscan + .check_contract_verification_status(args.id.clone()) + .await + .wrap_err("Failed to request verification status")?; + + trace!(target : "forge::verify", ?resp, "Received verification response"); + + eprintln!( + "Contract verification status:\nResponse: `{}`\nDetails: `{}`", + resp.message, resp.result + ); + + if resp.result == "Pending in queue" { + return Err(eyre!("Verification is still pending...",)) + } + + if resp.result == "Unable to verify" { + return Err(eyre!("Unable to verify.",)) + } + + if resp.result == "Already Verified" { + println!("Contract source code already verified"); + return Ok(()) + } + + if resp.status == "0" { + println!("Contract failed to verify."); + std::process::exit(1); + } + + if resp.result == "Pass - Verified" { + println!("Contract successfully verified"); + } + + Ok(()) + } + .boxed() + }) + .await + .wrap_err("Checking verification result failed:") + } +} + +impl EtherscanVerificationProvider { + /// Create a source provider + fn source_provider(&self, args: &VerifyArgs) -> Box { + if args.flatten { + Box::new(flatten::EtherscanFlattenedSource) + } else { + Box::new(standard_json::EtherscanStandardJsonSource) + } + } + + /// Return the memoized cache entry for the target contract. + /// Read the artifact from cache on first access. + fn cache_entry( + &mut self, + project: &Project, + contract_name: &str, + ) -> Result<&(PathBuf, CacheEntry, CompactContract)> { + if let Some(ref entry) = self.cached_entry { + return Ok(entry) + } + + let cache = project.read_cache_file()?; + let (path, entry) = get_cached_entry_by_name(&cache, contract_name)?; + let contract: CompactContract = cache.read_artifact(path.clone(), contract_name)?; + Ok(self.cached_entry.insert((path, entry, contract))) + } + + /// Configures the API request to the etherscan API using the given [`VerifyArgs`]. + async fn prepare_request(&mut self, args: &VerifyArgs) -> Result<(Client, VerifyContract)> { + let config = args.try_load_config_emit_warnings()?; + let etherscan = self.client( + args.etherscan.chain.unwrap_or_default(), + args.verifier.verifier_url.as_deref(), + args.etherscan.key.as_deref(), + &config, + )?; + let verify_args = self.create_verify_request(args, Some(config)).await?; + + Ok((etherscan, verify_args)) + } + + /// Queries the etherscan API to verify if the contract is already verified. + async fn is_contract_verified( + &self, + etherscan: &Client, + verify_contract: &VerifyContract, + ) -> Result { + let check = etherscan.contract_abi(verify_contract.address).await; + + if let Err(err) = check { + match err { + EtherscanError::ContractCodeNotVerified(_) => return Ok(false), + error => return Err(error.into()), + } + } + + Ok(true) + } + + /// Create an etherscan client + pub(crate) fn client( + &self, + chain: Chain, + verifier_url: Option<&str>, + etherscan_key: Option<&str>, + config: &Config, + ) -> Result { + let etherscan_config = config.get_etherscan_config_with_chain(Some(chain))?; + + 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()) + .or_else(|| chain.etherscan_urls().map(|urls| urls.1)); + + let etherscan_key = + etherscan_key.or_else(|| etherscan_config.as_ref().map(|c| c.key.as_str())); + + let mut builder = Client::builder(); + + builder = if let Some(api_url) = api_url { + builder.with_api_url(api_url)?.with_url(base_url.unwrap_or(api_url))? + } else { + builder.chain(chain.to_owned().try_into()?)? + }; + + builder + .with_api_key(etherscan_key.unwrap_or_default()) + .build() + .wrap_err("Failed to create etherscan client") + } + + /// Creates the `VerifyContract` etherscan request in order to verify the contract + /// + /// If `--flatten` is set to `true` then this will send with [`CodeFormat::SingleFile`] + /// otherwise this will use the [`CodeFormat::StandardJsonInput`] + pub async fn create_verify_request( + &mut self, + args: &VerifyArgs, + config: Option, + ) -> Result { + let mut config = + if let Some(config) = config { config } else { args.try_load_config_emit_warnings()? }; + + config.libraries.extend(args.libraries.clone()); + + let project = config.project()?; + + let contract_path = self.contract_path(args, &project)?; + let compiler_version = self.compiler_version(args, &config, &project)?; + let (source, contract_name, code_format) = + self.source_provider(args).source(args, &project, &contract_path, &compiler_version)?; + + let compiler_version = format!("v{}", ensure_solc_build_metadata(compiler_version).await?); + let constructor_args = self.constructor_args(args, &project)?; + let mut verify_args = + VerifyContract::new(args.address.to_ethers(), contract_name, source, compiler_version) + .constructor_arguments(constructor_args) + .code_format(code_format); + + if code_format == CodeFormat::SingleFile { + verify_args = if let Some(optimizations) = args.num_of_optimizations { + verify_args.optimized().runs(optimizations as u32) + } else if config.optimizer { + verify_args.optimized().runs(config.optimizer_runs.try_into()?) + } else { + verify_args.not_optimized() + }; + } + + Ok(verify_args) + } + + /// Get the target contract path. If it wasn't provided, attempt a lookup + /// in cache. Validate the path indeed exists on disk. + fn contract_path(&mut self, args: &VerifyArgs, project: &Project) -> Result { + let path = match args.contract.path.as_ref() { + Some(path) => project.root().join(path), + None => { + let (path, _, _) = self.cache_entry(project, &args.contract.name).wrap_err( + "If cache is disabled, contract info must be provided in the format :", + )?; + path.to_owned() + } + }; + + // check that the provided contract is part of the source dir + if !path.exists() { + eyre::bail!("Contract {:?} does not exist.", path); + } + + Ok(path) + } + + /// Parse the compiler version. + /// The priority desc: + /// 1. Through CLI arg `--compiler-version` + /// 2. `solc` defined in foundry.toml + /// 3. The version contract was last compiled with. + fn compiler_version( + &mut self, + args: &VerifyArgs, + config: &Config, + project: &Project, + ) -> Result { + if let Some(ref version) = args.compiler_version { + return Ok(version.trim_start_matches('v').parse()?) + } + + if let Some(ref solc) = config.solc { + match solc { + SolcReq::Version(version) => return Ok(version.to_owned()), + SolcReq::Local(solc) => { + if solc.is_file() { + return Ok(Solc::new(solc).version()?) + } + } + } + } + + let (_, entry, _) = self.cache_entry(project, &args.contract.name).wrap_err( + "If cache is disabled, compiler version must be either provided with `--compiler-version` option or set in foundry.toml" + )?; + let artifacts = entry.artifacts_versions().collect::>(); + if artifacts.len() == 1 { + let mut version = artifacts[0].0.to_owned(); + version.build = match RE_BUILD_COMMIT.captures(version.build.as_str()) { + Some(cap) => BuildMetadata::new(cap.name("commit").unwrap().as_str())?, + _ => BuildMetadata::EMPTY, + }; + return Ok(version) + } + + if artifacts.is_empty() { + warn!("No artifacts detected") + } else { + let versions = artifacts.iter().map(|a| a.0.to_string()).collect::>(); + warn!("Ambiguous compiler versions found in cache: {}", versions.join(", ")); + } + + eyre::bail!("Compiler version has to be set in `foundry.toml`. If the project was not deployed with foundry, specify the version through `--compiler-version` flag.") + } + + /// Return the optional encoded constructor arguments. If the path to + /// constructor arguments was provided, read them and encode. Otherwise, + /// return whatever was set in the [VerifyArgs] args. + fn constructor_args(&mut self, args: &VerifyArgs, project: &Project) -> Result> { + if let Some(ref constructor_args_path) = args.constructor_args_path { + let (_, _, contract) = self.cache_entry(project, &args.contract.name).wrap_err( + "Cache must be enabled in order to use the `--constructor-args-path` option", + )?; + let abi = contract.abi.as_ref().ok_or(eyre!("Can't find ABI in cached artifact."))?; + let constructor = abi + .constructor() + .ok_or(eyre!("Can't retrieve constructor info from artifact ABI."))?; + #[allow(deprecated)] + let func = Function { + name: "constructor".to_string(), + inputs: constructor.inputs.clone(), + outputs: vec![], + constant: None, + state_mutability: Default::default(), + }; + let encoded_args = encode_args( + &func, + &read_constructor_args_file(constructor_args_path.to_path_buf())?, + )?; + let encoded_args = hex::encode(encoded_args); + return Ok(Some(encoded_args[8..].into())) + } + + Ok(args.constructor_args.clone()) + } +} + +/// Given any solc [Version] return a [Version] with build metadata +/// +/// # Example +/// +/// ```ignore +/// use semver::{BuildMetadata, Version}; +/// let version = Version::new(1, 2, 3); +/// let version = ensure_solc_build_metadata(version).await?; +/// assert_ne!(version.build, BuildMetadata::EMPTY); +/// ``` +async fn ensure_solc_build_metadata(version: Version) -> Result { + if version.build != BuildMetadata::EMPTY { + Ok(version) + } else { + Ok(lookup_compiler_version(&version).await?) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use clap::Parser; + use foundry_cli::utils::LoadConfig; + use foundry_common::fs; + use foundry_test_utils::tempfile::tempdir; + + #[test] + fn can_extract_etherscan_verify_config() { + let temp = tempdir().unwrap(); + let root = temp.path(); + + let config = r#" + [profile.default] + + [etherscan] + mumbai = { key = "dummykey", chain = 80001, url = "https://api-testnet.polygonscan.com/" } + "#; + + let toml_file = root.join(Config::FILE_NAME); + fs::write(toml_file, config).unwrap(); + + let args: VerifyArgs = VerifyArgs::parse_from([ + "foundry-cli", + "0xd8509bee9c9bf012282ad33aba0d87241baf5064", + "src/Counter.sol:Counter", + "--chain", + "mumbai", + "--root", + root.as_os_str().to_str().unwrap(), + ]); + + let config = args.load_config(); + + let etherscan = EtherscanVerificationProvider::default(); + let client = etherscan + .client( + args.etherscan.chain.unwrap_or_default(), + args.verifier.verifier_url.as_deref(), + args.etherscan.key.as_deref(), + &config, + ) + .unwrap(); + assert_eq!(client.etherscan_api_url().as_str(), "https://api-testnet.polygonscan.com/"); + + assert!(format!("{client:?}").contains("dummykey")); + + let args: VerifyArgs = VerifyArgs::parse_from([ + "foundry-cli", + "0xd8509bee9c9bf012282ad33aba0d87241baf5064", + "src/Counter.sol:Counter", + "--chain", + "mumbai", + "--verifier-url", + "https://verifier-url.com/", + "--root", + root.as_os_str().to_str().unwrap(), + ]); + + let config = args.load_config(); + + let etherscan = EtherscanVerificationProvider::default(); + let client = etherscan + .client( + args.etherscan.chain.unwrap_or_default(), + args.verifier.verifier_url.as_deref(), + args.etherscan.key.as_deref(), + &config, + ) + .unwrap(); + assert_eq!(client.etherscan_api_url().as_str(), "https://verifier-url.com/"); + assert!(format!("{client:?}").contains("dummykey")); + } + + #[tokio::test(flavor = "multi_thread")] + async fn fails_on_disabled_cache_and_missing_info() { + let temp = tempdir().unwrap(); + let root = temp.path(); + let root_path = root.as_os_str().to_str().unwrap(); + + let config = r" + [profile.default] + cache = false + "; + + let toml_file = root.join(Config::FILE_NAME); + fs::write(toml_file, config).unwrap(); + + let address = "0xd8509bee9c9bf012282ad33aba0d87241baf5064"; + let contract_name = "Counter"; + let src_dir = "src"; + fs::create_dir_all(root.join(src_dir)).unwrap(); + let contract_path = format!("{src_dir}/Counter.sol"); + fs::write(root.join(&contract_path), "").unwrap(); + + let mut etherscan = EtherscanVerificationProvider::default(); + + // No compiler argument + let args = VerifyArgs::parse_from([ + "foundry-cli", + address, + &format!("{contract_path}:{contract_name}"), + "--root", + root_path, + ]); + + let result = etherscan.preflight_check(args).await; + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "If cache is disabled, compiler version must be either provided with `--compiler-version` option or set in foundry.toml" + ); + + // No contract path + let args = + VerifyArgs::parse_from(["foundry-cli", address, contract_name, "--root", root_path]); + + let result = etherscan.preflight_check(args).await; + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "If cache is disabled, contract info must be provided in the format :" + ); + + // Constructor args path + let args = VerifyArgs::parse_from([ + "foundry-cli", + address, + &format!("{contract_path}:{contract_name}"), + "--constructor-args-path", + ".", + "--compiler-version", + "0.8.15", + "--root", + root_path, + ]); + + let result = etherscan.preflight_check(args).await; + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "Cache must be enabled in order to use the `--constructor-args-path` option", + ); + } +} diff --git a/crates/zkforge/bin/cmd/verify/etherscan/standard_json.rs b/crates/zkforge/bin/cmd/verify/etherscan/standard_json.rs new file mode 100644 index 000000000..f79c76461 --- /dev/null +++ b/crates/zkforge/bin/cmd/verify/etherscan/standard_json.rs @@ -0,0 +1,48 @@ +use super::{EtherscanSourceProvider, VerifyArgs}; +use ethers::{ + etherscan::verify::CodeFormat, prelude::artifacts::StandardJsonCompilerInput, solc::Project, +}; +use eyre::{Context, Result}; +use semver::Version; +use std::path::Path; +use tracing::trace; + +#[derive(Debug)] +pub struct EtherscanStandardJsonSource; +impl EtherscanSourceProvider for EtherscanStandardJsonSource { + fn source( + &self, + args: &VerifyArgs, + project: &Project, + target: &Path, + version: &Version, + ) -> Result<(String, String, CodeFormat)> { + let mut input: StandardJsonCompilerInput = project + .standard_json_input(target) + .wrap_err("Failed to get standard json input")? + .normalize_evm_version(version); + + input.settings.libraries.libs = input + .settings + .libraries + .libs + .into_iter() + .map(|(f, libs)| (f.strip_prefix(project.root()).unwrap_or(&f).to_path_buf(), libs)) + .collect(); + + // remove all incompatible settings + input.settings.sanitize(version); + + let source = + serde_json::to_string(&input).wrap_err("Failed to parse standard json input")?; + + trace!(target : "forge::verify", standard_json = source, "determined standard json input"); + + let name = format!( + "{}:{}", + target.strip_prefix(project.root()).unwrap_or(target).display(), + args.contract.name.clone() + ); + Ok((source, name, CodeFormat::StandardJsonInput)) + } +} diff --git a/crates/zkforge/bin/cmd/verify/mod.rs b/crates/zkforge/bin/cmd/verify/mod.rs new file mode 100644 index 000000000..7b9738191 --- /dev/null +++ b/crates/zkforge/bin/cmd/verify/mod.rs @@ -0,0 +1,232 @@ +use super::retry::RetryArgs; +use alloy_primitives::Address; +use clap::{Parser, ValueHint}; +use ethers::solc::info::ContractInfo; +use eyre::Result; +use foundry_cli::{opts::EtherscanOpts, utils::LoadConfig}; +use foundry_config::{figment, impl_figment_convert, impl_figment_convert_cast, Config}; +use provider::VerificationProviderType; +use reqwest::Url; +use std::path::PathBuf; + +mod etherscan; +use etherscan::EtherscanVerificationProvider; + +pub mod provider; +use provider::VerificationProvider; + +mod sourcify; + +/// Verification provider arguments +#[derive(Debug, Clone, Parser)] +pub struct VerifierArgs { + /// The contract verification provider to use. + #[clap(long, help_heading = "Verifier options", default_value = "etherscan", value_enum)] + pub verifier: VerificationProviderType, + + /// The verifier URL, if using a custom provider + #[clap(long, help_heading = "Verifier options", env = "VERIFIER_URL")] + pub verifier_url: Option, +} + +impl Default for VerifierArgs { + fn default() -> Self { + VerifierArgs { verifier: VerificationProviderType::Etherscan, verifier_url: None } + } +} + +/// CLI arguments for `forge verify`. +#[derive(Debug, Clone, Parser)] +pub struct VerifyArgs { + /// The address of the contract to verify. + pub address: Address, + + /// The contract identifier in the form `:`. + pub contract: ContractInfo, + + /// The ABI-encoded constructor arguments. + #[clap(long, conflicts_with = "constructor_args_path", value_name = "ARGS")] + pub constructor_args: Option, + + /// The path to a file containing the constructor arguments. + #[clap(long, value_hint = ValueHint::FilePath, value_name = "PATH")] + pub constructor_args_path: Option, + + /// The `solc` version to use to build the smart contract. + #[clap(long, value_name = "VERSION")] + pub compiler_version: Option, + + /// The number of optimization runs used to build the smart contract. + #[clap(long, visible_alias = "optimizer-runs", value_name = "NUM")] + pub num_of_optimizations: Option, + + /// Flatten the source code before verifying. + #[clap(long)] + pub flatten: bool, + + /// Do not compile the flattened smart contract before verifying (if --flatten is passed). + #[clap(short, long)] + pub force: bool, + + /// Wait for verification result after submission. + #[clap(long)] + pub watch: bool, + + /// Set pre-linked libraries. + #[clap(long, help_heading = "Linker options", env = "DAPP_LIBRARIES")] + pub libraries: Vec, + + /// The project's root path. + /// + /// By default root of the Git repository, if in one, + /// or the current working directory. + #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + pub root: Option, + + /// Prints the standard json compiler input. + /// + /// The standard json compiler input can be used to manually submit contract verification in + /// the browser. + #[clap(long, conflicts_with = "flatten")] + pub show_standard_json_input: bool, + + #[clap(flatten)] + pub etherscan: EtherscanOpts, + + #[clap(flatten)] + pub retry: RetryArgs, + + #[clap(flatten)] + pub verifier: VerifierArgs, +} + +impl_figment_convert!(VerifyArgs); + +impl figment::Provider for VerifyArgs { + fn metadata(&self) -> figment::Metadata { + figment::Metadata::named("Verify Provider") + } + fn data( + &self, + ) -> Result, figment::Error> { + let mut dict = self.etherscan.dict(); + if let Some(root) = self.root.as_ref() { + dict.insert("root".to_string(), figment::value::Value::serialize(root)?); + } + if let Some(optimizer_runs) = self.num_of_optimizations { + dict.insert("optimizer".to_string(), figment::value::Value::serialize(true)?); + dict.insert( + "optimizer_runs".to_string(), + figment::value::Value::serialize(optimizer_runs)?, + ); + } + Ok(figment::value::Map::from([(Config::selected_profile(), dict)])) + } +} + +impl VerifyArgs { + /// Run the verify command to submit the contract's source code for verification on etherscan + pub async fn run(mut self) -> Result<()> { + let config = self.load_config_emit_warnings(); + let chain = config.chain_id.unwrap_or_default(); + self.etherscan.chain = Some(chain); + self.etherscan.key = config.get_etherscan_config_with_chain(Some(chain))?.map(|c| c.key); + + if self.show_standard_json_input { + let args = + EtherscanVerificationProvider::default().create_verify_request(&self, None).await?; + println!("{}", args.source); + return Ok(()) + } + + let verifier_url = self.verifier.verifier_url.clone(); + println!("Start verifying contract `{}` deployed on {chain}", self.address); + self.verifier.verifier.client(&self.etherscan.key)?.verify(self).await.map_err(|err| { + if let Some(verifier_url) = verifier_url { + match Url::parse(&verifier_url) { + Ok(url) => { + if is_host_only(&url) { + return err.wrap_err(format!( + "Provided URL `{verifier_url}` is host only.\n Did you mean to use the API endpoint`{verifier_url}/api` ?" + )) + } + } + Err(url_err) => { + return err.wrap_err(format!( + "Invalid URL {verifier_url} provided: {url_err}" + )) + } + } + } + + err + }) + } + + /// Returns the configured verification provider + pub fn verification_provider(&self) -> Result> { + self.verifier.verifier.client(&self.etherscan.key) + } +} + +/// Check verification status arguments +#[derive(Debug, Clone, Parser)] +pub struct VerifyCheckArgs { + /// The verification ID. + /// + /// For Etherscan - Submission GUID. + /// + /// For Sourcify - Contract Address. + id: String, + + #[clap(flatten)] + retry: RetryArgs, + + #[clap(flatten)] + etherscan: EtherscanOpts, + + #[clap(flatten)] + verifier: VerifierArgs, +} + +impl_figment_convert_cast!(VerifyCheckArgs); + +impl VerifyCheckArgs { + /// Run the verify command to submit the contract's source code for verification on etherscan + pub async fn run(self) -> Result<()> { + println!("Checking verification status on {}", self.etherscan.chain.unwrap_or_default()); + self.verifier.verifier.client(&self.etherscan.key)?.check(self).await + } +} + +impl figment::Provider for VerifyCheckArgs { + fn metadata(&self) -> figment::Metadata { + figment::Metadata::named("Verify Check Provider") + } + + fn data( + &self, + ) -> Result, figment::Error> { + self.etherscan.data() + } +} + +/// Returns `true` if the URL only consists of host. +/// +/// This is used to check user input url for missing /api path +#[inline] +fn is_host_only(url: &Url) -> bool { + matches!(url.path(), "/" | "") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_host_only() { + assert!(!is_host_only(&Url::parse("https://blockscout.net/api").unwrap())); + assert!(is_host_only(&Url::parse("https://blockscout.net/").unwrap())); + assert!(is_host_only(&Url::parse("https://blockscout.net").unwrap())); + } +} diff --git a/crates/zkforge/bin/cmd/verify/provider.rs b/crates/zkforge/bin/cmd/verify/provider.rs new file mode 100644 index 000000000..08f4a65e2 --- /dev/null +++ b/crates/zkforge/bin/cmd/verify/provider.rs @@ -0,0 +1,84 @@ +use super::{ + etherscan::EtherscanVerificationProvider, sourcify::SourcifyVerificationProvider, VerifyArgs, + VerifyCheckArgs, +}; +use async_trait::async_trait; +use eyre::Result; +use std::{fmt, str::FromStr}; + +/// An abstraction for various verification providers such as etherscan, sourcify, blockscout +#[async_trait] +pub trait VerificationProvider { + /// This should ensure the verify request can be prepared successfully. + /// + /// Caution: Implementers must ensure that this _never_ sends the actual verify request + /// `[VerificationProvider::verify]`, instead this is supposed to evaluate whether the given + /// [`VerifyArgs`] are valid to begin with. This should prevent situations where there's a + /// contract deployment that's executed before the verify request and the subsequent verify task + /// fails due to misconfiguration. + async fn preflight_check(&mut self, args: VerifyArgs) -> Result<()>; + + /// Sends the actual verify request for the targeted contract. + async fn verify(&mut self, args: VerifyArgs) -> Result<()>; + + /// Checks whether the contract is verified. + async fn check(&self, args: VerifyCheckArgs) -> Result<()>; +} + +impl FromStr for VerificationProviderType { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "e" | "etherscan" => Ok(VerificationProviderType::Etherscan), + "s" | "sourcify" => Ok(VerificationProviderType::Sourcify), + "b" | "blockscout" => Ok(VerificationProviderType::Blockscout), + _ => Err(format!("Unknown provider: {s}")), + } + } +} + +impl fmt::Display for VerificationProviderType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + VerificationProviderType::Etherscan => { + write!(f, "etherscan")?; + } + VerificationProviderType::Sourcify => { + write!(f, "sourcify")?; + } + VerificationProviderType::Blockscout => { + write!(f, "blockscout")?; + } + }; + Ok(()) + } +} + +#[derive(clap::ValueEnum, Debug, Default, Clone, PartialEq, Eq)] +pub enum VerificationProviderType { + #[default] + Etherscan, + Sourcify, + Blockscout, +} + +impl VerificationProviderType { + /// Returns the corresponding `VerificationProvider` for the key + pub fn client(&self, key: &Option) -> Result> { + match self { + VerificationProviderType::Etherscan => { + if key.as_ref().map_or(true, |key| key.is_empty()) { + eyre::bail!("ETHERSCAN_API_KEY must be set") + } + Ok(Box::::default()) + } + VerificationProviderType::Sourcify => { + Ok(Box::::default()) + } + VerificationProviderType::Blockscout => { + Ok(Box::::default()) + } + } + } +} diff --git a/crates/zkforge/bin/cmd/verify/sourcify.rs b/crates/zkforge/bin/cmd/verify/sourcify.rs new file mode 100644 index 000000000..43ef6bbe8 --- /dev/null +++ b/crates/zkforge/bin/cmd/verify/sourcify.rs @@ -0,0 +1,208 @@ +use super::{provider::VerificationProvider, VerifyArgs, VerifyCheckArgs}; +use async_trait::async_trait; +use ethers::solc::ConfigurableContractArtifact; +use eyre::Result; +use foundry_cli::utils::{get_cached_entry_by_name, LoadConfig}; +use foundry_common::fs; +use foundry_utils::Retry; +use futures::FutureExt; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, path::PathBuf}; +use tracing::{trace, warn}; + +pub static SOURCIFY_URL: &str = "https://sourcify.dev/server/"; + +/// The type that can verify a contract on `sourcify` +#[derive(Debug, Clone, Default)] +#[non_exhaustive] +pub struct SourcifyVerificationProvider; + +#[async_trait] +impl VerificationProvider for SourcifyVerificationProvider { + async fn preflight_check(&mut self, args: VerifyArgs) -> Result<()> { + let _ = self.prepare_request(&args)?; + Ok(()) + } + + async fn verify(&mut self, args: VerifyArgs) -> Result<()> { + let body = self.prepare_request(&args)?; + + trace!("submitting verification request {:?}", body); + + let client = reqwest::Client::new(); + + let retry: Retry = args.retry.into(); + let resp = retry + .run_async(|| { + async { + println!( + "\nSubmitting verification for [{}] {:?}.", + args.contract.name, + args.address.to_string() + ); + let response = client + .post(args.verifier.verifier_url.as_deref().unwrap_or(SOURCIFY_URL)) + .header("Content-Type", "application/json") + .body(serde_json::to_string(&body)?) + .send() + .await?; + + let status = response.status(); + if !status.is_success() { + let error: serde_json::Value = response.json().await?; + eprintln!( + "Sourcify verification request for address ({}) failed with status code {}\nDetails: {:#}", + format_args!("{:?}", args.address), + status, + error + ); + warn!("Failed verify submission: {:?}", error); + std::process::exit(1); + } + + let text = response.text().await?; + Ok(Some(serde_json::from_str::(&text)?)) + } + .boxed() + }) + .await?; + + self.process_sourcify_response(resp.map(|r| r.result)); + Ok(()) + } + + async fn check(&self, args: VerifyCheckArgs) -> Result<()> { + let retry: Retry = args.retry.into(); + let resp = retry + .run_async(|| { + async { + let url = format!( + "{}check-by-addresses?addresses={}&chainIds={}", + args.verifier.verifier_url.as_deref().unwrap_or(SOURCIFY_URL), + args.id, + args.etherscan.chain.unwrap_or_default().id(), + ); + + let response = reqwest::get(url).await?; + if !response.status().is_success() { + eprintln!( + "Failed to request verification status with status code {}", + response.status() + ); + std::process::exit(1); + }; + + Ok(Some(response.json::>().await?)) + } + .boxed() + }) + .await?; + + self.process_sourcify_response(resp); + Ok(()) + } +} + +impl SourcifyVerificationProvider { + /// Configures the API request to the sourcify API using the given [`VerifyArgs`]. + fn prepare_request(&self, args: &VerifyArgs) -> Result { + let mut config = args.try_load_config_emit_warnings()?; + config.libraries.extend(args.libraries.clone()); + + let project = config.project()?; + + if !config.cache { + eyre::bail!("Cache is required for sourcify verification.") + } + + let cache = project.read_cache_file()?; + let (path, entry) = get_cached_entry_by_name(&cache, &args.contract.name)?; + + if entry.solc_config.settings.metadata.is_none() { + eyre::bail!( + r#"Contract {} was compiled without the solc `metadata` setting. +Sourcify requires contract metadata for verification. +metadata output can be enabled via `extra_output = ["metadata"]` in `foundry.toml`"#, + args.contract.name + ) + } + + let mut files = HashMap::with_capacity(2 + entry.imports.len()); + + // the metadata is included in the contract's artifact file + let artifact_path = entry + .find_artifact_path(&args.contract.name) + .ok_or_else(|| eyre::eyre!("No artifact found for contract {}", args.contract.name))?; + + let artifact: ConfigurableContractArtifact = fs::read_json_file(artifact_path)?; + if let Some(metadata) = artifact.metadata { + let metadata = serde_json::to_string_pretty(&metadata)?; + files.insert("metadata.json".to_string(), metadata); + } else { + eyre::bail!( + r#"No metadata found in artifact `{}` for contract {}. +Sourcify requires contract metadata for verification. +metadata output can be enabled via `extra_output = ["metadata"]` in `foundry.toml`"#, + artifact_path.display(), + args.contract.name + ) + } + + let contract_path = args.contract.path.clone().map_or(path, PathBuf::from); + let filename = contract_path.file_name().unwrap().to_string_lossy().to_string(); + files.insert(filename, fs::read_to_string(&contract_path)?); + + for import in entry.imports { + let import_entry = format!("{}", import.display()); + files.insert(import_entry, fs::read_to_string(&import)?); + } + + let req = SourcifyVerifyRequest { + address: args.address.to_string(), + chain: args.etherscan.chain.unwrap_or_default().id().to_string(), + files, + chosen_contract: None, + }; + + Ok(req) + } + + fn process_sourcify_response(&self, response: Option>) { + let response = response.unwrap().remove(0); + if response.status == "perfect" { + if let Some(ts) = response.storage_timestamp { + println!("Contract source code already verified. Storage Timestamp: {ts}"); + } else { + println!("Contract successfully verified") + } + } else if response.status == "partial" { + println!("The recompiled contract partially matches the deployed version") + } else if response.status == "false" { + println!("Contract source code is not verified") + } else { + eprintln!("Unknown status from sourcify. Status: {}", response.status); + std::process::exit(1); + } + } +} + +#[derive(Serialize, Debug)] +pub struct SourcifyVerifyRequest { + address: String, + chain: String, + files: HashMap, + #[serde(rename = "chosenContract", skip_serializing_if = "Option::is_none")] + chosen_contract: Option, +} + +#[derive(Deserialize, Debug)] +pub struct SourcifyVerificationResponse { + result: Vec, +} + +#[derive(Deserialize, Debug)] +pub struct SourcifyResponseElement { + status: String, + #[serde(rename = "storageTimestamp")] + storage_timestamp: Option, +} diff --git a/crates/zkforge/bin/cmd/watch.rs b/crates/zkforge/bin/cmd/watch.rs new file mode 100644 index 000000000..d61cbbe32 --- /dev/null +++ b/crates/zkforge/bin/cmd/watch.rs @@ -0,0 +1,452 @@ +use super::{snapshot::SnapshotArgs, test::TestArgs}; +use clap::Parser; +use eyre::Result; +use foundry_cli::utils::{self, FoundryPathExt}; +use foundry_config::Config; +use std::{collections::HashSet, convert::Infallible, path::PathBuf, sync::Arc}; +use tracing::trace; +use watchexec::{ + action::{Action, Outcome, PreSpawn}, + command::Command, + config::{InitConfig, RuntimeConfig}, + event::{Event, Priority, ProcessEnd}, + handler::SyncFnHandler, + paths::summarise_events_to_env, + signal::source::MainSignal, + Watchexec, +}; + +#[derive(Debug, Clone, Parser, Default)] +#[clap(next_help_heading = "Watch options")] +pub struct WatchArgs { + /// Watch the given files or directories for changes. + /// + /// If no paths are provided, the source and test directories of the project are watched. + #[clap( + long, + short, + num_args(0..), + value_name = "PATH", + )] + pub watch: Option>, + + /// Do not restart the command while it's still running. + #[clap(long)] + pub no_restart: bool, + + /// Explicitly re-run all tests when a change is made. + /// + /// By default, only the tests of the last modified test file are executed. + #[clap(long)] + pub run_all: bool, + + /// File update debounce delay. + /// + /// During the delay, incoming change events are accumulated and + /// only once the delay has passed, is an action taken. Note that + /// this does not mean a command will be started: if --no-restart is + /// given and a command is already running, the outcome of the + /// action will be to do nothing. + /// + /// Defaults to 50ms. Parses as decimal seconds by default, but + /// using an integer with the `ms` suffix may be more convenient. + /// + /// When using --poll mode, you'll want a larger duration, or risk + /// overloading disk I/O. + #[clap(long, value_name = "DELAY")] + pub watch_delay: Option, +} + +impl WatchArgs { + /// Returns new [InitConfig] and [RuntimeConfig] based on the [WatchArgs] + /// + /// If paths were provided as arguments the these will be used as the watcher's pathset, + /// otherwise the path the closure returns will be used + pub fn watchexec_config( + &self, + f: impl FnOnce() -> Vec, + ) -> Result<(InitConfig, RuntimeConfig)> { + let init = init()?; + let mut runtime = runtime(self)?; + + // contains all the arguments `--watch p1, p2, p3` + let has_paths = self.watch.as_ref().map(|paths| !paths.is_empty()).unwrap_or_default(); + + if !has_paths { + // use alternative pathset, but only those that exists + runtime.pathset(f().into_iter().filter(|p| p.exists())); + } + Ok((init, runtime)) + } +} + +/// Executes a [`Watchexec`] that listens for changes in the project's src dir and reruns `forge +/// build` +// pub async fn watch_build(args: ZkBuildArgs) -> Result<()> { +// let (init, mut runtime) = args.watchexec_config()?; +// let cmd = cmd_args(args.watch.watch.as_ref().map(|paths| paths.len()).unwrap_or_default()); + +// trace!("watch build cmd={:?}", cmd); +// runtime.command(watch_command(cmd.clone())); + +// let wx = Watchexec::new(init, runtime.clone())?; +// on_action(args.watch, runtime, Arc::clone(&wx), cmd, (), |_| {}); + +// // start executing the command immediately +// wx.send_event(Event::default(), Priority::default()).await?; +// wx.main().await??; + +// Ok(()) +// } + +/// Executes a [`Watchexec`] that listens for changes in the project's src dir and reruns `forge +/// snapshot` +pub async fn watch_snapshot(args: SnapshotArgs) -> Result<()> { + let (init, mut runtime) = args.watchexec_config()?; + let cmd = cmd_args(args.test.watch.watch.as_ref().map(|paths| paths.len()).unwrap_or_default()); + + trace!("watch snapshot cmd={:?}", cmd); + runtime.command(watch_command(cmd.clone())); + let wx = Watchexec::new(init, runtime.clone())?; + + on_action(args.test.watch.clone(), runtime, Arc::clone(&wx), cmd, (), |_| {}); + + // start executing the command immediately + wx.send_event(Event::default(), Priority::default()).await?; + wx.main().await??; + + Ok(()) +} + +/// Executes a [`Watchexec`] that listens for changes in the project's src dir and reruns `forge +/// test` +pub async fn watch_test(args: TestArgs) -> Result<()> { + let (init, mut runtime) = args.watchexec_config()?; + let cmd = cmd_args(args.watch.watch.as_ref().map(|paths| paths.len()).unwrap_or_default()); + trace!("watch test cmd={:?}", cmd); + runtime.command(watch_command(cmd.clone())); + let wx = Watchexec::new(init, runtime.clone())?; + + let config: Config = args.build_args().into(); + + let filter = args.filter(&config); + + // marker to check whether to override the command + let no_reconfigure = filter.args().test_pattern.is_some() || + filter.args().path_pattern.is_some() || + filter.args().contract_pattern.is_some() || + args.watch.run_all; + + let state = WatchTestState { + project_root: config.__root.0, + no_reconfigure, + last_test_files: Default::default(), + }; + on_action(args.watch.clone(), runtime, Arc::clone(&wx), cmd, state, on_test); + + // start executing the command immediately + wx.send_event(Event::default(), Priority::default()).await?; + wx.main().await??; + + Ok(()) +} + +#[derive(Debug, Clone)] +struct WatchTestState { + /// the root directory of the project + project_root: PathBuf, + /// marks whether we can reconfigure the watcher command with the `--match-path` arg + no_reconfigure: bool, + /// Tracks the last changed test files, if any so that if a non-test file was modified we run + /// this file instead *Note:* this is a vec, so we can also watch out for changes + /// introduced by `forge fmt` + last_test_files: HashSet, +} + +/// The `on_action` hook for `forge test --watch` +fn on_test(action: OnActionState) { + let OnActionState { args, runtime, action, wx, cmd, other } = action; + let WatchTestState { project_root, no_reconfigure, last_test_files } = other; + + if no_reconfigure { + // nothing to reconfigure + return + } + + let mut cmd = cmd.clone(); + + let mut changed_sol_test_files: HashSet<_> = action + .events + .iter() + .flat_map(|e| e.paths()) + .filter(|(path, _)| path.is_sol_test()) + .filter_map(|(path, _)| path.to_str()) + .map(str::to_string) + .collect(); + + // replace `--match-path` | `-mp` argument + if let Some(pos) = cmd.iter().position(|arg| arg == "--match-path" || arg == "-mp") { + // --match-path requires 1 argument + cmd.drain(pos..=(pos + 1)); + } + + if changed_sol_test_files.len() > 1 || + (changed_sol_test_files.is_empty() && last_test_files.is_empty()) + { + // this could happen if multiple files were changed at once, for example `forge fmt` was + // run, or if no test files were changed and no previous test files were modified in which + // case we simply run all + let mut config = runtime.clone(); + config.command(watch_command(cmd.clone())); + // re-register the action + on_action( + args.clone(), + config, + wx, + cmd, + WatchTestState { + project_root, + no_reconfigure, + last_test_files: changed_sol_test_files, + }, + on_test, + ); + return + } + + if changed_sol_test_files.is_empty() { + // reuse the old test files if a non-test file was changed + changed_sol_test_files = last_test_files; + } + + // append `--match-path` glob + let mut file = changed_sol_test_files.clone().into_iter().next().expect("test file present"); + + // remove the project root dir from the detected file + if let Some(root) = project_root.as_os_str().to_str() { + if let Some(f) = file.strip_prefix(root) { + file = f.trim_start_matches('/').to_string(); + } + } + + let mut new_cmd = cmd.clone(); + new_cmd.push("--match-path".to_string()); + new_cmd.push(file); + trace!("reconfigure test command {:?}", new_cmd); + + // reconfigure the executor with a new runtime + let mut config = runtime.clone(); + config.command(watch_command(new_cmd)); + + // re-register the action + on_action( + args.clone(), + config, + wx, + cmd, + WatchTestState { project_root, no_reconfigure, last_test_files: changed_sol_test_files }, + on_test, + ); +} + +/// Converts a list of arguments to a `watchexec::Command` +/// +/// The first index in `args`, is expected to be the path to the executable, See `cmd_args` +/// +/// # Panics +/// if `args` is empty +fn watch_command(mut args: Vec) -> Command { + debug_assert!(!args.is_empty()); + let prog = args.remove(0); + Command::Exec { prog, args } +} + +/// Returns the env args without the `--watch` flag from the args for the Watchexec command +fn cmd_args(num: usize) -> Vec { + clean_cmd_args(num, std::env::args().collect()) +} +fn clean_cmd_args(num: usize, mut cmd_args: Vec) -> Vec { + if let Some(pos) = cmd_args.iter().position(|arg| arg == "--watch" || arg == "-w") { + cmd_args.drain(pos..=(pos + num)); + } + + // There's another edge case where short flags are combined into one which is supported by clap, + // like `-vw` for verbosity and watch + // this removes any `w` from concatenated short flags + if let Some(pos) = cmd_args.iter().position(|arg| { + fn contains_w_in_short(arg: &str) -> Option { + let mut iter = arg.chars(); + if iter.next()? != '-' { + return None + } + if iter.next()? == '-' { + return None + } + Some(iter.any(|c| c == 'w')) + } + contains_w_in_short(arg).unwrap_or(false) + }) { + let clean_arg = cmd_args[pos].replace('w', ""); + if clean_arg == "-" { + cmd_args.remove(pos); + } else { + cmd_args[pos] = clean_arg; + } + } + + cmd_args +} + +/// Returns the Initialisation configuration for [`Watchexec`]. +pub fn init() -> Result { + let mut config = InitConfig::default(); + config.on_error(SyncFnHandler::from(|data| -> std::result::Result<(), Infallible> { + trace!("[[{:?}]]", data); + Ok(()) + })); + + Ok(config) +} + +/// Contains all necessary context to reconfigure a [`Watchexec`] on the fly +struct OnActionState<'a, T: Clone> { + args: &'a WatchArgs, + runtime: &'a RuntimeConfig, + action: &'a Action, + cmd: &'a Vec, + wx: Arc, + // additional context to inject + other: T, +} + +/// Registers the `on_action` hook on the `RuntimeConfig` currently in use in the `Watchexec` +/// +/// **Note** this is a bit weird since we're installing the hook on the config that's already used +/// in `Watchexec` but necessary if we want to have access to it in order to +/// [`Watchexec::reconfigure`] +fn on_action( + args: WatchArgs, + mut config: RuntimeConfig, + wx: Arc, + cmd: Vec, + other: T, + f: F, +) where + F: for<'a> Fn(OnActionState<'a, T>) + Send + 'static, + T: Clone + Send + 'static, +{ + let on_busy = if args.no_restart { "do-nothing" } else { "restart" }; + let runtime = config.clone(); + let w = Arc::clone(&wx); + config.on_action(move |action: Action| { + let fut = async { Ok::<(), Infallible>(()) }; + let signals: Vec = action.events.iter().flat_map(|e| e.signals()).collect(); + let has_paths = action.events.iter().flat_map(|e| e.paths()).next().is_some(); + + if signals.contains(&MainSignal::Terminate) || signals.contains(&MainSignal::Interrupt) { + action.outcome(Outcome::both(Outcome::Stop, Outcome::Exit)); + return fut + } + + if !has_paths { + if !signals.is_empty() { + let mut out = Outcome::DoNothing; + for sig in signals { + out = Outcome::both(out, Outcome::Signal(sig)); + } + + action.outcome(out); + return fut + } + + let completion = action.events.iter().flat_map(|e| e.completions()).next(); + if let Some(status) = completion { + match status { + Some(ProcessEnd::ExitError(code)) => { + tracing::trace!("Command exited with {code}") + } + Some(ProcessEnd::ExitSignal(sig)) => { + tracing::trace!("Command killed by {:?}", sig) + } + Some(ProcessEnd::ExitStop(sig)) => { + tracing::trace!("Command stopped by {:?}", sig) + } + Some(ProcessEnd::Continued) => tracing::trace!("Command continued"), + Some(ProcessEnd::Exception(ex)) => { + tracing::trace!("Command ended by exception {:#x}", ex) + } + Some(ProcessEnd::Success) => tracing::trace!("Command was successful"), + None => tracing::trace!("Command completed"), + }; + + action.outcome(Outcome::DoNothing); + return fut + } + } + + f(OnActionState { + args: &args, + runtime: &runtime, + action: &action, + wx: w.clone(), + cmd: &cmd, + other: other.clone(), + }); + + // mattsse: could be made into flag to never clear the shell + let clear = false; + let when_running = match (clear, on_busy) { + (_, "do-nothing") => Outcome::DoNothing, + (true, "restart") => { + Outcome::both(Outcome::Stop, Outcome::both(Outcome::Clear, Outcome::Start)) + } + (false, "restart") => Outcome::both(Outcome::Stop, Outcome::Start), + _ => Outcome::DoNothing, + }; + + let when_idle = + if clear { Outcome::both(Outcome::Clear, Outcome::Start) } else { Outcome::Start }; + + action.outcome(Outcome::if_running(when_running, when_idle)); + + fut + }); + + let _ = wx.reconfigure(config); +} + +/// Returns the Runtime configuration for [`Watchexec`]. +pub fn runtime(args: &WatchArgs) -> Result { + let mut config = RuntimeConfig::default(); + + config.pathset(args.watch.clone().unwrap_or_default()); + + if let Some(delay) = &args.watch_delay { + config.action_throttle(utils::parse_delay(delay)?); + } + + config.on_pre_spawn(move |prespawn: PreSpawn| async move { + let envs = summarise_events_to_env(prespawn.events.iter()); + if let Some(mut command) = prespawn.command().await { + for (k, v) in envs { + command.env(format!("CARGO_WATCH_{k}_PATH"), v); + } + } + + Ok::<(), Infallible>(()) + }); + + Ok(config) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_cmd_args() { + let args = vec!["-vw".to_string()]; + let cleaned = clean_cmd_args(0, args); + assert_eq!(cleaned, vec!["-v".to_string()]); + } +} diff --git a/crates/zkforge/bin/cmd/zk_build.rs b/crates/zkforge/bin/cmd/zk_build.rs new file mode 100644 index 000000000..4ccc0df10 --- /dev/null +++ b/crates/zkforge/bin/cmd/zk_build.rs @@ -0,0 +1,266 @@ +/// The `zkbuild` module provides comprehensive functionality for the compilation of zkSync +/// smart contracts with a specified Solidity compiler version. +/// +/// This module consists of the following key structures and their corresponding +/// implementations: +/// +/// * `ZkBuildArgs`: This structure encapsulates the parameters necessary for building zkSync +/// contracts. These parameters include the Solidity compiler version, an option to enable +/// the system contract compilation mode, and an option to forcibly switch to the EVM legacy +/// assembly pipeline. Additionally, it includes core build arguments defined in the +/// `CoreBuildArgs` structure. +/// +/// * `Cmd` Implementation for `ZkBuildArgs`: This implementation includes a `run` function, +/// which initiates the zkSync contract compilation process. The `run` function takes care of +/// the various steps involved in the process, including downloading the zksolc compiler, +/// setting up the compiler directory, and invoking the compilation process. +/// +/// This module serves as a facilitator for the zkSync contract compilation process. It allows +/// developers to specify the compiler version and compilation options and handles the +/// intricate tasks necessary for contract compilation. This includes downloading the specified +/// compiler version if it's not already available, preparing the compiler's directory, and +/// finally, invoking the compilation process with the provided options. +/// +/// The `zkbuild` module is part of a larger framework aimed at managing and interacting with +/// zkSync contracts. It is designed to provide a seamless experience for developers, providing +/// an easy-to-use interface for contract compilation while taking care of the underlying +/// complexities. +use super::watch::WatchArgs; +use super::{ + zk_solc::{ZkSolc, ZkSolcOpts}, + zksolc_manager::{ + ZkSolcManager, ZkSolcManagerBuilder, ZkSolcManagerOpts, DEFAULT_ZKSOLC_VERSION, + }, +}; +use clap::Parser; +use ethers::prelude::Project; +use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; +use foundry_config::{ + figment::{ + self, + error::Kind::InvalidType, + value::{Dict, Map, Value}, + Metadata, Profile, Provider, + }, + Config, +}; +use serde::Serialize; +use std::fmt::Debug; + +foundry_config::merge_impl_figment_convert!(ZkBuildArgs, args); + +/// The `ZkBuildArgs` struct encapsulates the parameters required for the zkSync contract +/// compilation process. +/// +/// This includes: +/// * `use_zksolc`: The version of the Solidity compiler (solc) to be used for compilation, or the +/// path to a local solc. The values can be in the format `x.y.z`, `solc:x.y.z`, or +/// `path/to/solc`. It is used to specify the compiler version or location, which is crucial for +/// the contract building process. +/// +/// * `is_system`: A boolean flag indicating whether to enable the system contract compilation mode. +/// In this mode, zkEVM extensions are enabled, for example, calls to addresses `0xFFFF` and below +/// are substituted by special zkEVM instructions. This option is used when we want to compile +/// system contracts. +/// +/// * `force_evmla`: A boolean flag indicating whether to forcibly switch to the EVM legacy assembly +/// pipeline. This is useful for older revisions of `solc` 0.8, where Yul was considered highly +/// experimental and contained more bugs than today. This flag allows us to use the EVM legacy +/// assembly pipeline, which can be beneficial in certain situations. +/// +/// * `args`: Core build arguments encapsulated in the `CoreBuildArgs` struct. These include +/// additional parameters required for building the contract, such as optimization level, output +/// directory etc. +/// +/// This struct is used as input to the `ZkSolc` compiler, which will use these arguments to +/// configure the compilation process. It implements the `Cmd` trait, which triggers the compilation +/// process when the `run` function is called. The struct also implements the `Provider` trait, +/// allowing it to be converted into a form that can be merged into the application's configuration +/// object. +#[derive(Debug, Clone, Parser, Serialize, Default)] +#[clap(next_help_heading = "ZkBuild options", about = None)] +pub struct ZkBuildArgs { + /// Specify the solc version, or a path to a local solc, to build with. + /// + /// Valid values are in the format `x.y.z`, `solc:x.y.z` or `path/to/solc`. + #[clap( + help_heading = "ZkSync Compiler options", + value_name = "ZK_SOLC_VERSION", + long = "use-zksolc", + default_value = DEFAULT_ZKSOLC_VERSION + )] + #[serde(skip)] + pub use_zksolc: String, + + /// A flag indicating whether to enable the system contract compilation mode. + #[clap( + help_heading = "ZkSync Compiler options", + help = "Enable the system contract compilation mode. In this mode zkEVM extensions are enabled. For example, calls + to addresses `0xFFFF` and below are substituted by special zkEVM instructions.", + long = "is-system", + value_name = "SYSTEM_MODE" + )] + pub is_system: bool, + + /// A flag indicating whether to forcibly switch to the EVM legacy assembly pipeline. + #[clap( + help_heading = "ZkSync Compiler options", + help = "Forcibly switch to the EVM legacy assembly pipeline. It is useful for older revisions of `solc` 0.8, where + Yul was considered highly experimental and contained more bugs than today", + long = "force-evmla", + value_name = "FORCE_EVMLA" + )] + pub force_evmla: bool, + + /// Core build arguments encapsulated in the `CoreBuildArgs` struct. + #[clap(flatten)] + #[serde(flatten)] + pub args: CoreBuildArgs, + + #[clap(flatten)] + #[serde(skip)] + pub watch: WatchArgs, +} + +impl ZkBuildArgs { + /// Executes the zkSync contract compilation process based on the parameters encapsulated in the + /// `ZkBuildArgs` instance. + /// + /// This method performs the following steps: + /// 1. Tries to load the application's configuration, emitting warnings if any issues are + /// encountered. + /// 2. Modifies the project's artifact path to be the "zkout" directory in the project's root + /// directory. + /// 3. Creates a `ZkSolcManager` instance based on the specified zkSync Solidity compiler + /// (`use_zksolc` field in `ZkBuildArgs`). + /// 4. Checks if the setup compilers directory is properly set up. If not, it raises an error + /// and halts execution. + /// 5. If the zkSync Solidity compiler does not exist in the compilers directory, it triggers + /// its download. + /// 6. Initiates the contract compilation process using the `ZkSolc` compiler. This process is + /// configured with the `is_system` and `force_evmla` parameters from the `ZkBuildArgs` + /// instance, and the path to the zkSync Solidity compiler. + /// 7. If the compilation process fails, it raises an error and halts execution. + /// + /// The method returns `Ok(())` if the entire process completes successfully, or an error if any + /// step in the process fails. The purpose of this function is to consolidate all steps + /// involved in the zkSync contract compilation process in a single method, allowing for + /// easy invocation of the process with a single function call. + pub fn run(self) -> eyre::Result<()> { + let config = self.try_load_config_emit_warnings()?; + let mut project = config.project()?; + + //set zk out path + let zk_out_path = project.paths.root.join("zkout"); + project.paths.artifacts = zk_out_path; + + let zksolc_manager = self.setup_zksolc_manager()?; + + println!("Compiling smart contracts..."); + self.compile_smart_contracts(zksolc_manager, project) + } + /// Returns whether `ZkBuildArgs` was configured with `--watch` + pub fn _is_watch(&self) -> bool { + self.watch.watch.is_some() + } + /// Returns the [`watchexec::InitConfig`] and [`watchexec::RuntimeConfig`] necessary to + /// bootstrap a new [`watchexe::Watchexec`] loop. + // pub(crate) fn watchexec_config(&self) -> Result<(InitConfig, RuntimeConfig)> { + // // use the path arguments or if none where provided the `src` dir + // self.watch.watchexec_config(|| { + // let config = Config::from(self); + // vec![config.src, config.test, config.script] + // }) + // } + /// The `setup_zksolc_manager` function creates and prepares an instance of `ZkSolcManager`. + /// + /// It follows these steps: + /// 1. Instantiate `ZkSolcManagerOpts` and `ZkSolcManagerBuilder` with the specified zkSync + /// Solidity compiler. + /// 2. Create a `ZkSolcManager` using the builder. + /// 3. Check if the setup compilers directory is properly set up. If not, it raises an error. + /// 4. If the zkSync Solidity compiler does not exist in the compilers directory, it triggers + /// its download. + /// + /// The function returns the `ZkSolcManager` if all steps are successful, or an error if any + /// step fails. + fn setup_zksolc_manager(&self) -> eyre::Result { + let zksolc_manager_opts = ZkSolcManagerOpts::new(self.use_zksolc.clone()); + let zksolc_manager_builder = ZkSolcManagerBuilder::new(zksolc_manager_opts); + let zksolc_manager = zksolc_manager_builder + .build() + .map_err(|e| eyre::eyre!("Error building zksolc_manager: {}", e))?; + + if let Err(err) = zksolc_manager.check_setup_compilers_dir() { + eyre::bail!("Failed to setup compilers directory: {}", err); + } + + if !zksolc_manager.exists() { + println!( + "Downloading zksolc compiler from {:?}", + zksolc_manager.get_full_download_url().unwrap().to_string() + ); + zksolc_manager + .download() + .map_err(|err| eyre::eyre!("Failed to download the file: {}", err))?; + } + + Ok(zksolc_manager) + } + + /// The `compile_smart_contracts` function initiates the contract compilation process. + /// + /// It follows these steps: + /// 1. Create an instance of `ZkSolcOpts` with the appropriate options. + /// 2. Instantiate `ZkSolc` with the created options and the project. + /// 3. Initiate the contract compilation process. + /// + /// The function returns `Ok(())` if the compilation process completes successfully, or an error + /// if it fails. + fn compile_smart_contracts( + &self, + zksolc_manager: ZkSolcManager, + project: Project, + ) -> eyre::Result<()> { + let zksolc_opts = ZkSolcOpts { + compiler_path: zksolc_manager.get_full_compiler_path(), + is_system: self.is_system, + force_evmla: self.force_evmla, + }; + + let mut zksolc = ZkSolc::new(zksolc_opts, project); + + match zksolc.compile() { + Ok(_) => { + println!("Compiled Successfully"); + Ok(()) + } + Err(err) => { + eyre::bail!("Failed to compile smart contracts with zksolc: {}", err); + } + } + } +} + +// Make this args a `figment::Provider` so that it can be merged into the `Config` +impl Provider for ZkBuildArgs { + fn metadata(&self) -> Metadata { + Metadata::named("Build Args Provider") + } + + fn data(&self) -> Result, figment::Error> { + let value = Value::serialize(self)?; + let error = InvalidType(value.to_actual(), "map".into()); + let dict = value.into_dict().ok_or(error)?; + + // if self.names { + // dict.insert("names".to_string(), true.into()); + // } + + // if self.sizes { + // dict.insert("sizes".to_string(), true.into()); + // } + + Ok(Map::from([(Config::selected_profile(), dict)])) + } +} diff --git a/crates/zkforge/bin/cmd/zk_create.rs b/crates/zkforge/bin/cmd/zk_create.rs new file mode 100644 index 000000000..603af4b59 --- /dev/null +++ b/crates/zkforge/bin/cmd/zk_create.rs @@ -0,0 +1,375 @@ +use clap::{Parser, ValueHint}; +use ethers::{ + abi::Abi, + solc::{info::ContractInfo, Project}, + types::Bytes, +}; +use eyre::Context; +/// ZKSync Contract Deployment Module +/// This module encapsulates the logic required for contract deployment, including: +/// - Retrieving the contract bytecode and ABI from the Solidity project +/// - Encoding the constructor arguments +/// - Signing the deployment transaction +/// - Handling the deployment process +/// +/// This module plays a crucial role in the zkSync ecosystem by enabling developers to +/// seamlessly deploy and interact with zkSync contracts. +/// +/// The main struct in this module is `ZkCreateArgs`, which represents the command-line +/// arguments for the `forge zk-create` command. It contains fields such as: +/// - The contract identifier +/// - Constructor arguments +/// - Transaction options +/// - Ethereum-specific options +/// +/// The `ZkCreateArgs` struct implements methods to: +/// - Execute the deployment process +/// - Deploy the contract on the Ethereum network +/// +/// Additionally, this module provides several helper functions to assist with the contract +/// deployment, including: +/// - Retrieving the bytecode and ABI of the contract from the Solidity project +/// - Parsing and encoding the constructor arguments +/// - Creating a signer for transaction signing +/// - Handling factory dependencies, if any +/// +/// The contract deployment process involves: +/// 1. Setting up the project +/// 2. Retrieving the contract bytecode and ABI +/// 3. Parsing and encoding the constructor arguments +/// 4. Creating a signer with the provided private key and chain information +/// 5. Initializing a wallet for deployment +/// 6. Sending the deployment transaction to the Ethereum network +/// 7. Printing contract address, transaction hash, gas used, gas price, and block number if +/// the deployment is successful +/// +/// To use the `forge zk-create` command: +/// 1. Parse the command-line arguments using the `ZkCreateArgs::parse()` method +/// 2. Execute the deployment process by calling the `run()` method on the parsed arguments +/// +/// It's worth noting that this module relies on the following crates for interacting with +/// Ethereum and zkSync: +/// - `ethers` +/// - `zksync` +use foundry_cli::{ + opts::{CoreBuildArgs, EthereumOpts, TransactionOpts}, + utils::read_constructor_args_file, +}; +use foundry_common::zk_utils::{get_chain, get_private_key, get_rpc_url}; +use foundry_config::Config; +use serde_json::Value; +use std::{fs, path::PathBuf, str::FromStr}; +use zksync_web3_rs::{ + providers::Provider, + signers::{LocalWallet, Signer}, + ZKSWallet, +}; + +/// CLI arguments for `forge zk-create`. +/// Struct `ZkCreateArgs` encapsulates the arguments necessary for creating a new zkSync contract. +/// +/// This struct is used to cleanly pass the required parameters for contract deployment to the +/// `create` function. It ensures type safety and reduces the chance of passing incorrect or +/// mismatched parameters. +/// +/// The `ZkCreateArgs` struct has the following fields: +/// +/// * `constructor_args`: This field represents the arguments for the zkSync contract constructor. +/// The arguments are represented as a vector of `Token` values. Each `Token` corresponds to an +/// argument of the contract's constructor. +/// +/// * `encoded_constructor_args`: This is the hex encoded string representation of the constructor +/// arguments. It is used when deploying the contract on the Ethereum network. +/// +/// * `bytecode`: The bytecode of the zkSync contract that is to be deployed. This is a compiled +/// version of the contract's source code. +/// +/// * `private_key`: The private key used for signing the contract deployment transaction. It is the +/// private key of the account that will own the deployed contract. +/// +/// * `chain_id`: The ID of the Ethereum network chain where the contract is to be deployed. +/// Different chain IDs represent different Ethereum networks (e.g., mainnet, testnet). +#[derive(Debug, Clone, Parser)] +#[clap(next_help_heading = "ZkCreate options", about = None)] +pub struct ZkCreateArgs { + /// The contract identifier in the form `:`. + #[clap( + help = "The contract identifier in the form `:`.", + value_name = "CONTRACT" + )] + contract: ContractInfo, + + /// The constructor arguments. + #[clap( + long, + num_args(1..), + help = "The constructor arguments.", + name = "constructor_args", + conflicts_with = "constructor_args_path", + value_name = "ARGS" + )] + constructor_args: Vec, + + /// The path to a file containing the constructor arguments. + #[clap( + long, + help = "The path to a file containing the constructor arguments.", + value_hint = ValueHint::FilePath, + name = "constructor_args_path", + conflicts_with = "constructor_args", + value_name = "FILE" + )] + constructor_args_path: Option, + + /// The factory dependencies in the form `:`. + #[clap( + long, + num_args(1..), + help_heading = "ZkSync Features", + help = "The factory dependencies in the form `:`.", + value_name = "FACTORY-DEPS" + )] + factory_deps: Option>, + + /// Core build arguments. + #[clap(flatten)] + opts: CoreBuildArgs, + + /// Transaction options, such as gas price and gas limit. + #[clap(flatten)] + tx: TransactionOpts, + + /// Ethereum-specific options, such as the network and wallet. + #[clap(flatten)] + eth: EthereumOpts, +} + +impl ZkCreateArgs { + /// Executes the command to create a contract. + /// + /// # Procedure + /// 1. Retrieves private key, RPC URL, and chain information from the current instance. + /// 2. It then sets up the project and artifact paths. + /// 3. Retrieves the bytecode of the contract. + /// 4. If factory dependencies are present, they are processed. + /// 5. A signer is created using the private key and chain. + /// 6. The ABI of the contract is obtained. + /// 7. The constructor arguments are encoded. + /// 8. A wallet is set up using the signer and the RPC URL. + /// 9. The contract deployment is started. + /// 10. If deployment is successful, the contract address, transaction hash, gas used, gas + /// price, and block number are printed to the console. + pub async fn run(self) -> eyre::Result<()> { + let private_key = get_private_key(&self.eth.wallet.raw.private_key)?; + let rpc_url = get_rpc_url(&self.eth.rpc.url)?; + let config = Config::from(&self.eth); + let chain = get_chain(config.chain_id)?; + let mut project = self.opts.project()?; + project.paths.artifacts = project.paths.root.join("zkout"); + + let bytecode = match Self::get_bytecode_from_contract(&project, &self.contract) { + Ok(bytecode) => bytecode, + Err(e) => { + eyre::bail!("Error getting bytecode from contract: {}", e); + } + }; + + //check for additional factory deps + let factory_dependencies = self + .factory_deps + .as_ref() + .map(|fdep_contract_info| self.get_factory_dependencies(&project, fdep_contract_info)); + + // get abi + let abi = match Self::get_abi_from_contract(&project, &self.contract) { + Ok(abi) => abi, + Err(e) => { + eyre::bail!("Error gettting ABI from contract: {}", e); + } + }; + + let contract = match serde_json::from_value(abi) { + Ok(contract) => contract, + Err(e) => { + eyre::bail!("Error converting json abi to Contract ABI: {}", e); + } + }; + + let constructor_args = self.get_constructor_args(&contract); + + let provider = Provider::try_from(rpc_url)?; + let wallet = LocalWallet::from_str(&format!("{private_key:?}"))?.with_chain_id(chain); + let zk_wallet = ZKSWallet::new(wallet, None, Some(provider), None)?; + + let rcpt = zk_wallet + .deploy(contract, bytecode.to_vec(), constructor_args, factory_dependencies) + .await?; + + let deployed_address = rcpt.contract_address.expect("Error retrieving deployed address"); + let gas_used = rcpt.gas_used.expect("Error retrieving gas used"); + let gas_price = rcpt.effective_gas_price.expect("Error retrieving gas price"); + let block_number = rcpt.block_number.expect("Error retrieving block number"); + + println!("+-------------------------------------------------+"); + println!("Contract successfully deployed to address: {:#?}", deployed_address); + println!("Transaction Hash: {:#?}", rcpt.transaction_hash); + println!("Gas used: {:#?}", gas_used); + println!("Effective gas price: {:#?}", gas_price); + println!("Block Number: {:#?}", block_number); + println!("+-------------------------------------------------+"); + + Ok(()) + } + + /// This function retrieves the constructor arguments for the contract. + /// + /// # Returns + /// A vector of `String` which represents the constructor arguments. + /// An empty vector if there are no constructor arguments. + fn get_constructor_args(&self, abi: &Abi) -> Vec { + if abi.constructor.is_some() { + if let Some(ref constructor_args_path) = self.constructor_args_path { + read_constructor_args_file(constructor_args_path.to_path_buf()).unwrap() + } else { + self.constructor_args.clone() + } + } else { + vec![] + } + } + + /// This function retrieves the ABI from the contract. + /// + /// # Errors + /// If there is an error in retrieving or parsing the ABI, it returns a serde_json::Error. + /// + /// # Returns + /// A `Result` which is: + /// - Ok: Contains the ABI as a serde_json::Value. + /// - Err: Contains a serde_json::Error. + fn get_abi_from_contract( + project: &Project, + contract_info: &ContractInfo, + ) -> eyre::Result { + let output_path = Self::get_path_for_contract_output(project, contract_info); + let contract_output = Self::get_contract_output(output_path)?; + serde_json::from_value( + contract_output[contract_info.path.as_ref().unwrap()][&contract_info.name]["abi"] + .clone(), + ) + .wrap_err(format!( + "Failed to find ABI for {} - is it the right contract name?", + contract_info.name + )) + } + + /// This function retrieves the bytecode from the contract. + /// + /// # Procedure + /// 1. Retrieves the contract info, checks if the contract's bytecode exists. + /// 2. If the bytecode exists, it is decoded from hexadecimal representation into bytes. + /// + /// # Errors + /// If there is an error in retrieving or decoding the bytecode, it returns an Error. + /// + /// # Returns + /// A `Result` which is: + /// - Ok: Contains the bytecode as a Bytes. + /// - Err: Contains an error message indicating a problem with retrieving or decoding the + /// bytecode. + fn get_bytecode_from_contract( + project: &Project, + contract_info: &ContractInfo, + ) -> eyre::Result { + let output_path = Self::get_path_for_contract_output(project, contract_info); + let contract_output = Self::get_contract_output(output_path)?; + let contract_file_codes = &contract_output[contract_info.path.as_ref().unwrap()]; + serde_json::from_value( + contract_file_codes[&contract_info.name]["evm"]["bytecode"]["object"].clone(), + ) + .wrap_err(format!( + "Failed to find evm bytecode for {} - is this the correct contract name?", + contract_info.name + )) + } + + /// This function retrieves the contract output. + /// + /// # Arguments + /// + /// * `output_path` - A `PathBuf` that represents the path to the contract output file. + /// + /// # Procedure + /// + /// 1. Reads the contract output file into a string. + /// 2. Converts the string into a `serde_json::Value`. + /// 3. Returns the "contracts" field from the JSON value. + /// + /// # Returns + /// + /// A `serde_json::Value` that represents the contract output. + fn get_contract_output(output_path: PathBuf) -> eyre::Result { + let data = fs::read_to_string(&output_path).wrap_err(format!( + "Unable to read contract output file at {} - did you run zk-build", + output_path.display() + ))?; + let res: serde_json::Value = serde_json::from_str(&data) + .wrap_err(format!("Unable to parse JSON contract from {}", output_path.display()))?; + Ok(res["contracts"].clone()) + } + + /// This function retrieves the path for the contract output. + /// + /// # Arguments + /// + /// * `project` - A `Project` instance that represents the current Solidity project. + /// * `contract_info` - A `ContractInfo` instance that contains information about the contract. + /// + /// # Procedure + /// + /// 1. Retrieves the contract file path from `contract_info`. + /// 2. Retrieves the contract file name from the file path. + /// 3. Joins the artifacts path of the project with the contract file name. + /// 4. Joins the resulting path with "artifacts.json" to create the path to the contract output. + /// + /// # Returns + /// + /// A `PathBuf` that represents the path to the contract output. + fn get_path_for_contract_output(project: &Project, contract_info: &ContractInfo) -> PathBuf { + let filepath = contract_info.path.clone().unwrap(); + let filename = filepath.split('/').last().unwrap(); + project.paths.artifacts.join(filename).join("artifacts.json") + } + + /// This function retrieves the factory dependencies. + /// + /// # Arguments + /// + /// * `project` - A `Project` instance that represents the current Solidity project. + /// * `factory_dep_vector` - A vector that contains the bytecode of each factory dependency + /// contract. + /// * `fdep_contract_info` - A vector of `ContractInfo` instances that contain information about + /// each factory dependency contract. + /// + /// # Procedure + /// + /// 1. Iterates over each factory dependency contract in `fdep_contract_info`. + /// 2. For each contract, retrieves its bytecode and appends it to `factory_dep_vector`. + /// + /// # Returns + /// + /// A vector of vectors of bytes that represents the bytecode of each factory dependency + /// contract. + fn get_factory_dependencies( + &self, + project: &Project, + fdep_contract_info: &[ContractInfo], + ) -> Vec> { + let mut factory_deps = Vec::new(); + for dep in fdep_contract_info.iter() { + let dep_bytecode = Self::get_bytecode_from_contract(project, dep).unwrap(); + factory_deps.push(dep_bytecode.to_vec()); + } + factory_deps + } +} diff --git a/crates/zkforge/bin/cmd/zk_solc.rs b/crates/zkforge/bin/cmd/zk_solc.rs new file mode 100644 index 000000000..24b9621c0 --- /dev/null +++ b/crates/zkforge/bin/cmd/zk_solc.rs @@ -0,0 +1,874 @@ +/// This module provides the implementation of the ZkSolc compiler for Solidity contracts. +/// ZkSolc is a specialized compiler that supports zero-knowledge (ZK) proofs for smart +/// contracts. +/// +/// The `ZkSolc` struct represents an instance of the compiler, and it is responsible for +/// compiling Solidity contracts and handling the output. It uses the `solc` library to +/// interact with the Solidity compiler. +/// +/// The `ZkSolc` struct provides the following functionality: +/// +/// - Configuration: It allows configuring the compiler path, system mode, and force-evmla +/// options through the `ZkSolcOpts` struct. +/// +/// - Compilation: The `compile` method initiates the compilation process. It collects the +/// source files, parses the JSON input, builds compiler arguments, runs the compiler, and +/// handles the output. +/// +/// - Error and Warning Handling: The compiler output is checked for errors and warnings, and +/// they are displayed appropriately. If errors are encountered, the process will exit with a +/// non-zero status code. +/// +/// - JSON Input Generation: The `parse_json_input` method generates the JSON input required by +/// the compiler for each contract. It configures the Solidity compiler, saves the input to +/// the artifacts directory, and handles the output. +/// +/// - Source Management: The `get_versioned_sources` method retrieves the project sources, +/// resolves the graph of sources and versions, and returns the sources grouped by Solc +/// version. +/// +/// - Artifact Path Generation: The `build_artifacts_path` and `build_artifacts_file` methods +/// construct the path and file for saving the compiler output artifacts. +use ansi_term::Colour::{Red, Yellow}; +use ethers::{ + prelude::{artifacts::Source, Solc}, + solc::{ + artifacts::{ + output_selection::FileOutputSelection, CompactBytecode, CompactDeployedBytecode, + LosslessAbi, StandardJsonCompilerInput, + }, + ArtifactFile, Artifacts, ConfigurableContractArtifact, Graph, Project, + ProjectCompileOutput, + }, + types::Bytes, +}; +use eyre::{Context, ContextCompat, Result}; +use semver::Version; +use serde::Deserialize; +use serde_json::Value; +use std::{ + collections::{BTreeMap, HashMap, HashSet}, + fmt, fs, + fs::File, + io::Write, + path::PathBuf, + process::{exit, Command, Stdio}, +}; + +#[derive(Debug, Clone)] +pub struct ZkSolcOpts { + pub compiler_path: PathBuf, + pub is_system: bool, + pub force_evmla: bool, +} + +/// Files that should be compiled with a given solidity version. +type SolidityVersionSources = (Version, BTreeMap); + +/// This struct represents the ZkSolc compiler for compiling Solidity contracts. +/// +/// Key Components: +/// - Manages project details, including paths and configurations. +/// - Stores the compiler path. +/// - Provides a comprehensive interface for compiling Solidity contracts using the ZkSolc compiler. +/// +/// Struct Members: +/// - `project`: Represents the project details and configurations. +/// - `compiler_path`: The path to the ZkSolc compiler executable. +/// - `is_system`: A flag indicating whether the compiler is in system mode. +/// - `force_evmla`: A flag indicating whether to force EVMLA optimization. +/// - `standard_json`: An optional field to store the parsed standard JSON input for the contracts. +/// - `sources`: An optional field to store the versioned sources for the contracts. +/// +/// Functionality: +/// - `new`: Constructs a new `ZkSolc` instance using the provided compiler path, project +/// configurations, and options. +/// - `compile`: Responsible for compiling the contracts in the project's 'sources' directory and +/// its subdirectories. +/// +/// Error Handling: +/// - The methods in this struct return the `Result` type from the `anyhow` crate for flexible and +/// easy-to-use error handling. +/// +/// Example Usage: +/// ```ignore +/// use zk_solc::{ZkSolc}; +/// use ethers_solc::Project; +/// // Set the compiler path and other options +/// let compiler_path = "/path/to/zksolc"; +/// +/// let project = Project::builder().build().unwrap(); +/// +/// // Initialize the ZkSolc compiler +/// let zksolc = ZkSolc::new(compiler_path, project); +/// +/// // Compile the contracts +/// if let Err(err) = zksolc.compile() { +/// eprintln!("Failed to compile contracts: {}", err); +/// // Handle the error +/// } +/// ``` +/// +/// In this example, the `ZkSolc` compiler is initialized with the provided compiler path and +/// project configurations. The `compile` method is then invoked to compile the contracts, and any +/// resulting errors are handled accordingly. +#[derive(Debug)] +pub struct ZkSolc { + project: Project, + compiler_path: PathBuf, + is_system: bool, + force_evmla: bool, + standard_json: Option, +} + +impl fmt::Display for ZkSolc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "ZkSolc ( + compiler_path: {}, + output_path: {}, + )", + self.compiler_path.display(), + self.project.paths.artifacts.display(), + ) + } +} + +impl ZkSolc { + pub fn new(opts: ZkSolcOpts, project: Project) -> Self { + Self { + project, + compiler_path: opts.compiler_path, + is_system: opts.is_system, + force_evmla: opts.force_evmla, + standard_json: None, + } + } + + /// Compiles the Solidity contracts in the project's 'sources' directory and its subdirectories + /// using the ZkSolc compiler. + /// + /// # Arguments + /// + /// * `self` - A mutable reference to the `ZkSolc` instance. + /// + /// # Errors + /// + /// This function can return an error if any of the following occurs: + /// - The Solidity compiler fails to execute or encounters an error during compilation. + /// - The source files cannot be collected from the project's 'sources' directory. + /// - The compiler arguments cannot be built. + /// - The output of the compiler contains errors or warnings. + /// + /// # Examples + /// + /// ```ignore + /// let project = Project::new(...); + /// let opts = ZkSolcOpts { + /// compiler_path: PathBuf::from("/path/to/zksolc"), + /// is_system: false, + /// force_evmla: true, + /// }; + /// let mut zksolc = ZkSolc::new(opts, project); + /// zksolc.compile()?; + /// ``` + /// + /// In this example, a `ZkSolc` instance is created using `ZkSolcOpts` and a `Project`. Then, + /// the `compile` method is invoked to compile the contracts. + /// + /// # Workflow + /// + /// The `compile` function performs the following operations: + /// + /// 1. Collect Source Files: + /// - It collects the source files from the project's 'sources' directory and its + /// subdirectories. + /// - Only the files within the 'sources' directory and its subdirectories are considered for + /// compilation. + /// + /// 2. Configure Solidity Compiler: + /// - It configures the Solidity compiler by setting options like the compiler path, system + /// mode, and force EVMLA flag. + /// + /// 3. Parse JSON Input: + /// - For each source file, it parses the JSON input using the Solidity compiler. + /// - The parsed JSON input is stored in the `standard_json` field of the `ZkSolc` instance. + /// + /// 4. Build Compiler Arguments: + /// - It builds the compiler arguments for each source file. + /// - The compiler arguments include options like the compiler path, system mode, and force + /// EVMLA flag. + /// + /// 5. Run Compiler and Handle Output: + /// - It runs the Solidity compiler for each source file with the corresponding compiler + /// arguments. + /// - The output of the compiler, including errors and warnings, is captured. + /// + /// 6. Handle Output (Errors and Warnings): + /// - It handles the output of the compiler, extracting errors and warnings. + /// - Errors are printed in red, and warnings are printed in yellow. + /// + /// 7. Save Artifacts: + /// - It saves the artifacts (compiler output) as a JSON file for each source file. + /// - The artifacts are saved in the project's artifacts directory under the corresponding + /// source file's directory. + /// + /// # Note + /// + /// The `compile` function modifies the `ZkSolc` instance to store the parsed JSON input and the + /// versioned sources. These modified values can be accessed after the compilation process + /// for further processing or analysis. + pub fn compile(&mut self) -> Result { + let mut displayed_warnings = HashSet::new(); + let mut data = BTreeMap::new(); + // Step 1: Collect Source Files + let sources = self.get_versioned_sources().wrap_err("Cannot get source files")?; + + // Step 2: Compile Contracts for Each Source + for (solc, version) in sources { + //configure project solc for each solc version + for source in version.1 { + // Contract path is an absolute path of the file. + let contract_path = source.0.clone(); + + // Check if the contract_path is in 'sources' directory or its subdirectories + let is_in_sources_dir = contract_path + .ancestors() + .any(|ancestor| ancestor.starts_with(&self.project.paths.sources)); + + // Skip this file if it's not in the 'sources' directory or its subdirectories + if !is_in_sources_dir { + continue + } + + // Step 3: Parse JSON Input for each Source + self.prepare_compiler_input(&contract_path).wrap_err(format!( + "Failed to prepare inputs when compiling {:?}", + contract_path + ))?; + + // Step 4: Build Compiler Arguments + let comp_args = self.build_compiler_args(&source, &solc); + + // Step 5: Run Compiler and Handle Output + let mut cmd = Command::new(&self.compiler_path); + let mut child = cmd + .arg(contract_path.clone()) + .args(&comp_args) + .stdin(Stdio::piped()) + .stderr(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .wrap_err("Failed to start the compiler")?; + + let stdin = child.stdin.take().expect("Stdin exists."); + + serde_json::to_writer(stdin, &self.standard_json.clone().unwrap()) + .wrap_err("Could not assign standard_json to writer")?; + + let output = child.wait_with_output().wrap_err("Could not run compiler cmd")?; + + if !output.status.success() { + eyre::bail!( + "Compilation failed with {:?}. Using compiler: {:?}, with args {:?} {:?}", + String::from_utf8(output.stderr).unwrap_or_default(), + self.compiler_path, + contract_path, + &comp_args + ); + } + + let filename = contract_path + .file_name() + .wrap_err(format!("Could not get filename from {:?}", contract_path))? + .to_str() + .unwrap() + .to_string(); + + // Step 6: Handle Output (Errors and Warnings) + data.insert( + filename.clone(), + ZkSolc::handle_output( + output.stdout, + &filename, + &mut displayed_warnings, + Some( + self.project + .paths + .artifacts + .join(filename.clone()) + .join("artifacts.json"), + ), + ), + ); + } + } + let mut result = ProjectCompileOutput::default(); + result.compiled_artifacts = Artifacts { 0: data }; + Ok(result) + } + + /// Builds the compiler arguments for the Solidity compiler based on the provided versioned + /// source and solc instance. The compiler arguments specify options and settings for the + /// compiler's execution. + /// + /// # Arguments + /// + /// * `versioned_source` - A tuple containing the contract source path (`PathBuf`) and the + /// corresponding `Source` object. + /// * `solc` - The `Solc` instance representing the specific version of the Solidity compiler. + /// + /// # Returns + /// + /// A vector of strings representing the compiler arguments. + fn build_compiler_args( + &self, + versioned_source: &(PathBuf, Source), + solc: &Solc, + ) -> Vec { + // Get the solc compiler path as a string + let solc_path = solc.solc.to_str().expect("Error configuring solc compiler.").to_string(); + + // Build compiler arguments + let mut comp_args = vec!["--standard-json".to_string(), "--solc".to_string(), solc_path]; + + // Check if system mode is enabled or if the source path contains "is-system" + if self.is_system || versioned_source.0.to_str().unwrap().contains("is-system") { + comp_args.push("--system-mode".to_string()); + } + + // Check if force-evmla is enabled + if self.force_evmla { + comp_args.push("--force-evmla".to_string()); + } + comp_args + } + + /// Handles the output of the Solidity compiler after the compilation process is completed. It + /// processes the compiler output, handles errors and warnings, and saves the compiler + /// artifacts. + /// + /// # Arguments + /// + /// * `output` - The output of the Solidity compiler as a `std::process::Output` struct. + /// * `source` - The path of the contract source file that was compiled. + /// * `displayed_warnings` - A mutable set that keeps track of displayed warnings to avoid + /// duplicates. + /// + /// # Output Handling + /// + /// - The output of the Solidity compiler is expected to be in JSON format. + /// - The output is deserialized into a `serde_json::Value` object for further processing. + /// + /// # Error and Warning Handling + /// + /// - The function checks for errors and warnings in the compiler output and handles them + /// accordingly. + /// - Errors are printed in red color. + /// - Warnings are printed in yellow color. + /// - If an error is encountered, the function exits with a non-zero status code. + /// - If only warnings are present, a message indicating the presence of warnings is printed. + /// + /// # Artifacts Saving + /// + /// - The function saves the compiler output (artifacts) in a file. + /// - The artifacts are saved in a file named "artifacts.json" within the contract's artifacts + /// directory. + /// + /// # Example + /// + /// ```ignore + /// let output = std::process::Output { ... }; + /// let source = "/path/to/contract.sol".to_string(); + /// let mut displayed_warnings = HashSet::new(); + /// ZkSolc::handle_output(output, source, &mut displayed_warnings); + /// ``` + /// + /// In this example, the `handle_output` function is called with the compiler output, contract + /// source, and a mutable set for displayed warnings. It processes the output, handles + /// errors and warnings, and saves the artifacts. + pub fn handle_output( + output: Vec, + source: &String, + displayed_warnings: &mut HashSet, + write_artifacts: Option, + ) -> BTreeMap>> { + // Deserialize the compiler output into a serde_json::Value object + let compiler_output: ZkSolcCompilerOutput = serde_json::from_slice(&output) + .unwrap_or_else(|e| panic!("Could not parse zksolc compiler output: {}", e)); + + // Handle errors and warnings in the output + ZkSolc::handle_output_errors(&compiler_output, displayed_warnings); + + // First - let's get all the bytecodes. + let mut all_bytecodes: HashMap = Default::default(); + for (_source_file_name, source_file_results) in &compiler_output.contracts { + for (_contract_name, contract_results) in source_file_results { + if let Some(hash) = &contract_results.hash { + all_bytecodes.insert( + hash.clone(), + contract_results.evm.bytecode.as_ref().unwrap().object.clone(), + ); + } + } + } + + let mut result = BTreeMap::new(); + + // Get the bytecode hashes for each contract in the output + for key in compiler_output.contracts.keys() { + if key.contains(source) { + let contracts_in_file = compiler_output.contracts.get(key).unwrap(); + for (contract_name, contract) in contracts_in_file { + println!( + "{} -> Bytecode Hash: {} ", + contract_name, + contract.hash.as_ref().unwrap() + ); + + let factory_deps: Vec = contract + .factory_dependencies + .keys() + .map(|factory_hash| all_bytecodes.get(factory_hash).unwrap()) + .cloned() + .collect(); + + let packed_bytecode = Bytes::from( + era_revm::factory_deps::PackedEraBytecode::new( + contract.hash.as_ref().unwrap().clone(), + contract.evm.bytecode.as_ref().unwrap().object.clone(), + factory_deps, + ) + .to_vec(), + ); + + let mut art = ConfigurableContractArtifact::default(); + art.bytecode = Some(CompactBytecode { + object: ethers::solc::artifacts::BytecodeObject::Bytecode( + packed_bytecode.clone(), + ), + source_map: None, + link_references: Default::default(), + }); + + art.deployed_bytecode = Some(CompactDeployedBytecode { + bytecode: Some(CompactBytecode { + object: ethers::solc::artifacts::BytecodeObject::Bytecode( + packed_bytecode, + ), + source_map: None, + link_references: Default::default(), + }), + immutable_references: Default::default(), + }); + + art.abi = contract.abi.clone(); + + let artifact = ArtifactFile { + artifact: art, + file: format!("{}.sol", contract_name).into(), + version: Version::parse(&compiler_output.version).unwrap(), + }; + result.insert(contract_name.clone(), vec![artifact]); + } + } + } + if let Some(write_artifacts) = write_artifacts { + let output_json: Value = serde_json::from_slice(&output) + .unwrap_or_else(|e| panic!("Could not parse zksolc compiler output: {}", e)); + + // Beautify the output JSON + let output_json_pretty = serde_json::to_string_pretty(&output_json) + .unwrap_or_else(|e| panic!("Could not beautify zksolc compiler output: {}", e)); + + // Create the artifacts file for saving the compiler output + let mut artifacts_file = + File::create(write_artifacts).wrap_err("Could not create artifacts file").unwrap(); + + // Write the beautified output JSON to the artifacts file + artifacts_file + .write_all(output_json_pretty.as_bytes()) + .unwrap_or_else(|e| panic!("Could not write artifacts file: {}", e)); + } + + result + } + + /// Handles the errors and warnings present in the output JSON from the compiler. + /// + /// # Arguments + /// + /// * `output_json` - A reference to the output JSON from the compiler, represented as a `Value` + /// from the `serde_json` crate. + /// * `displayed_warnings` - A mutable reference to a `HashSet` that tracks displayed warnings + /// to avoid duplicates. + /// + /// # Behavior + /// + /// This function iterates over the `errors` array in the output JSON and processes each error + /// or warning individually. For each error or warning, it extracts the severity and + /// formatted message from the JSON. If the severity is "warning", it checks if the same + /// warning message has been displayed before to avoid duplicates. If the warning message + /// has not been displayed before, it adds the message to the `displayed_warnings` set, + /// prints the formatted warning message in yellow, and sets the `has_warning` flag to true. + /// If the severity is not "warning", it prints the formatted error message in red and sets + /// the `has_error` flag to true. + /// + /// If any errors are encountered, the function calls `exit(1)` to terminate the program. If + /// only warnings are encountered, it prints a message indicating that the compiler run + /// completed with warnings. + pub fn handle_output_errors( + output_json: &ZkSolcCompilerOutput, + displayed_warnings: &mut HashSet, + ) { + let errors = &output_json.errors; + + let mut has_error = false; + let mut has_warning = false; + + for error in errors { + let severity = error.get("severity").and_then(|v| v.as_str()).unwrap_or("Unknown"); + let formatted_message = + error.get("formattedMessage").and_then(|v| v.as_str()).unwrap_or(""); + + let is_warning = severity.eq_ignore_ascii_case("warning"); + if is_warning { + let main_message = formatted_message.lines().next().unwrap_or("").to_string(); + if !displayed_warnings.contains(&main_message) { + displayed_warnings.insert(main_message); + println!("{}", Yellow.paint(formatted_message)); + has_warning = true; + } + } else { + println!("{}", Red.paint(formatted_message)); + has_error = true; + } + } + + if has_error { + exit(1); + } else if has_warning { + println!("Compiler run completed with warnings"); + } + } + + /// Parses the JSON input for a contract and prepares the necessary configuration for the ZkSolc + /// compiler. + /// + /// # Arguments + /// + /// * `contract_path` - The path to the contract source file. + /// + /// # Errors + /// + /// This function can return an error if any of the following occurs: + /// - The standard JSON input cannot be generated for the contract. + /// - The artifacts path for the contract cannot be created. + /// - The JSON input cannot be saved to the artifacts directory. + /// + /// # Workflow + /// + /// The `parse_json_input` function performs the following operations: + /// + /// 1. Configure File Output Selection: + /// - It configures the file output selection to specify which outputs should be included in + /// the compiler output. + /// + /// 2. Configure Solidity Compiler: + /// - It modifies the Solidity compiler settings to exclude metadata from the output. + /// + /// 3. Update Output Selection: + /// - It updates the file output selection settings in the Solidity compiler configuration + /// with the configured values. + /// + /// 4. Generate Standard JSON Input: + /// - It generates the standard JSON input for the contract using the `standard_json_input` + /// method of the project. + /// - The standard JSON input includes the contract's source code, compiler options, and file + /// output selection. + /// + /// 5. Build Artifacts Path: + /// - It builds the path for saving the compiler artifacts based on the contract source file. + /// - The artifacts will be saved in a directory named after the contract's filename within + /// the project's artifacts directory. + /// + /// 6. Save JSON Input: + /// - It saves the standard JSON input as a file named "json_input.json" within the + /// contract's artifacts directory. + /// + /// # Example + /// + /// ```ignore + /// let contract_path = PathBuf::from("/path/to/contract.sol"); + /// self.prepare_compiler_input(contract_path)?; + /// ``` + /// + /// In this example, the `prepare_compiler_input` function is called with the contract source + /// path. It generates the JSON input for the contract, configures the Solidity compiler, + /// and saves the input to the artifacts directory. + fn prepare_compiler_input(&mut self, contract_path: &PathBuf) -> Result<()> { + // Step 1: Configure File Output Selection + let mut file_output_selection: FileOutputSelection = BTreeMap::default(); + file_output_selection.insert( + "*".to_string(), + vec![ + "abi".to_string(), + "evm.methodIdentifiers".to_string(), + // "evm.legacyAssembly".to_string(), + ], + ); + file_output_selection.insert( + "".to_string(), + vec![ + "metadata".to_string(), + // "ast".to_string(), + // "userdoc".to_string(), + // "devdoc".to_string(), + // "storageLayout".to_string(), + // "irOptimized".to_string(), + ], + ); + + // Step 2: Configure Solidity Compiler + // zksolc requires metadata to be 'None' + self.project.solc_config.settings.metadata = None; + + // Step 3: Update Output Selection + self.project + .solc_config + .settings + .output_selection + .0 + .insert("*".to_string(), file_output_selection.clone()); + + // Step 4: Generate Standard JSON Input + let standard_json = self + .project + .standard_json_input(contract_path) + .wrap_err("Could not get standard json input") + .unwrap(); + + // Store the generated standard JSON input in the ZkSolc instance + self.standard_json = Some(standard_json.to_owned()); + + // Step 5: Build Artifacts Path + let artifact_path = &self.build_artifacts_path(contract_path)?; + + // Step 6: Save JSON Input + let json_input_path = artifact_path.join("json_input.json"); + let stdjson = + serde_json::to_value(&standard_json).wrap_err("Could not serialize JSON input")?; + + std::fs::write(json_input_path, serde_json::to_string_pretty(&stdjson).unwrap()) + .wrap_err("Could not write JSON input file")?; + + Ok(()) + } + + /// Retrieves the versioned sources for the Solidity contracts in the project. The versioned + /// sources represent the contracts grouped by their corresponding Solidity compiler + /// versions. The function performs the following steps to obtain the versioned sources: + /// + /// # Workflow: + /// 1. Retrieve Project Sources: + /// - The function calls the `sources` method of the `Project` instance to obtain the + /// Solidity contract sources for the project. + /// - If the retrieval of project sources fails, an error is returned. + /// + /// 2. Resolve Graph of Sources and Versions: + /// - The function creates a graph using the `Graph::resolve_sources` method, passing the + /// project paths and the retrieved contract sources. + /// - The graph represents the relationships between the contract sources and their + /// corresponding Solidity compiler versions. + /// - If the resolution of the graph fails, an error is returned. + /// + /// 3. Extract Versions and Edges: + /// - The function extracts the versions and edges from the resolved graph. + /// - The `versions` variable contains a mapping of Solidity compiler versions to the + /// contracts associated with each version. + /// - The `edges` variable represents the edges between the contract sources and their + /// corresponding Solidity compiler versions. + /// - If the extraction of versions and edges fails, an error is returned. + /// + /// 4. Retrieve Solc Version: + /// - The function attempts to retrieve the Solidity compiler version associated with the + /// project. + /// - If the retrieval of the solc version fails, an error is returned. + /// + /// 5. Return Versioned Sources: + /// - The function returns a `BTreeMap` containing the versioned sources, where each entry in + /// the map represents a Solidity compiler version and its associated contracts. + /// - The map is constructed using the `solc_version` and `versions` variables. + /// - If the construction of the versioned sources map fails, an error is returned. + /// + /// # Arguments + /// + /// * `self` - A mutable reference to the `ZkSolc` instance. + /// + /// # Returns + /// + /// A `Result` containing a `BTreeMap` of the versioned sources on success, or an + /// `anyhow::Error` on failure. + /// + /// # Errors + /// + /// This function can return an error if any of the following occurs: + /// - The retrieval of project sources fails. + /// - The resolution of the graph of sources and versions fails. + /// - The extraction of versions and edges from the resolved graph fails. + /// - The retrieval of the Solidity compiler version associated with the project fails. + /// - The construction of the versioned sources map fails. + /// + /// # Example + /// + /// ```ignore + /// use foundry_cli::cmd::forge::zksolc::ZkSolc; + /// let mut zk_solc = ZkSolc::new(...); + /// let versioned_sources = zk_solc.get_versioned_sources()?; + /// ``` + /// + /// In this example, a `ZkSolc` instance is created, and the `get_versioned_sources` method is + /// called to retrieve the versioned sources for the Solidity contracts in the project. + /// The resulting `BTreeMap` of versioned sources is stored in the `versioned_sources` variable. + /// + /// # Note + /// + /// The `get_versioned_sources` function is typically called internally within the `ZkSolc` + /// struct to obtain the necessary versioned sources for contract compilation. + /// The versioned sources can then be used for further processing or analysis. + fn get_versioned_sources(&mut self) -> Result> { + // Step 1: Retrieve Project Sources + let sources = self.project.sources().wrap_err("Could not get project sources")?; + + // Step 2: Resolve Graph of Sources and Versions + let graph = Graph::resolve_sources(&self.project.paths, sources) + .wrap_err("Could not resolve sources")?; + + // Step 3: Extract Versions and Edges + let (versions, _edges) = graph + .into_sources_by_version(self.project.offline) + .wrap_err("Could not match solc versions to files")?; + + // Step 4: Retrieve Solc Version + versions.get(&self.project).wrap_err("Could not get solc") + } + + /// Builds the path for saving the artifacts (compiler output) of a contract based on the + /// contract's source file. The function performs the following steps to construct the + /// artifacts path: + /// + /// # Workflow: + /// 1. Extract Filename: + /// - The function extracts the filename from the provided contract source path using the + /// `file_name` method. + /// - If the extraction of the filename fails, an error is returned. + /// + /// 2. Build Artifacts Path: + /// - The function constructs the artifacts path by joining the extracted filename with the + /// project's artifacts directory path. + /// - The `join` method is used on the `artifacts` directory path, passing the extracted + /// filename. + /// + /// 3. Create Artifacts Directory: + /// - The function creates the artifacts directory and all its parent directories using the + /// `create_dir_all` method from the `fs` module. + /// - If the creation of the artifacts directory fails, an error is returned. + /// + /// # Arguments + /// + /// * `self` - A reference to the `ZkSolc` instance. + /// * `source` - The contract source path represented as a `PathBuf`. + /// + /// # Returns + /// + /// A `Result` containing the constructed artifacts path (`PathBuf`) on success, or an + /// `anyhow::Error` on failure. + /// + /// # Errors + /// + /// This function can return an error if any of the following occurs: + /// - The extraction of the filename from the contract source path fails. + /// - The creation of the artifacts directory fails. + fn build_artifacts_path(&self, source: &PathBuf) -> Result { + let filename = source.file_name().expect("Failed to get Contract filename."); + let path = self.project.paths.artifacts.join(filename); + fs::create_dir_all(&path).wrap_err("Could not create artifacts directory")?; + Ok(path) + } +} + +#[derive(Debug, Deserialize)] +pub struct ZkSolcCompilerOutput { + // Map from file name -> (Contract name -> Contract) + pub contracts: HashMap>, + pub sources: HashMap, + pub version: String, + pub long_version: String, + pub zk_version: String, + pub errors: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct ZkContract { + pub hash: Option, + // Hashmap from hash to filename:contract_name string. + #[serde(rename = "factoryDependencies", default)] + pub factory_dependencies: HashMap, + pub evm: Evm, + pub abi: Option, +} +#[derive(Debug, Deserialize)] + +pub struct Evm { + pub bytecode: Option, +} +#[derive(Debug, Deserialize)] + +pub struct ZkSolcBytecode { + object: String, +} + +#[derive(Debug, Deserialize)] +pub struct ZkSourceFile { + pub id: u64, +} + +#[cfg(test)] +mod tests { + use std::collections::HashSet; + + use super::{ZkSolc, ZkSolcCompilerOutput}; + + /// Basic test to analyze the single Counter.sol artifact. + #[test] + pub fn test_artifacts_extraction() { + let data = include_str!("../../../../testdata/artifacts-counter/artifacts.json") + .as_bytes() + .to_vec(); + let mut displayed_warnings = HashSet::new(); + let source = "src/Counter.sol".to_owned(); + let result = ZkSolc::handle_output(data, &source, &mut displayed_warnings, None); + + let artifacts = result.get("Counter").unwrap(); + assert_eq!(artifacts.len(), 1); + let first = &artifacts[0]; + assert_eq!(first.file.to_str(), Some("Counter.sol")); + assert_eq!(first.version.to_string(), "0.8.20"); + assert!(first.artifact.abi.is_some()); + assert_eq!(first.artifact.bytecode.as_ref().unwrap().object.bytes_len(), 3883); + } + #[test] + pub fn test_json_parsing() { + let data = include_str!("../../../../testdata/artifacts-counter/artifacts.json") + .as_bytes() + .to_vec(); + let _parsed: ZkSolcCompilerOutput = serde_json::from_slice(&data).unwrap(); + + // Contract that has almost no data (and many fields missing). + let almost_empty_data = + include_str!("../../../../testdata/artifacts-counter/empty.json").as_bytes().to_vec(); + let _parsed_empty: ZkSolcCompilerOutput = + serde_json::from_slice(&almost_empty_data).unwrap(); + } +} diff --git a/crates/zkforge/bin/cmd/zksolc_manager.rs b/crates/zkforge/bin/cmd/zksolc_manager.rs new file mode 100644 index 000000000..ae9f63581 --- /dev/null +++ b/crates/zkforge/bin/cmd/zksolc_manager.rs @@ -0,0 +1,583 @@ +/// The `ZkSolcManager` module manages the downloading and setup of the `zksolc` Solidity +/// compiler. This module provides functionalities to interact with different versions of the +/// zksolc compiler as well as supporting different operating systems. +/// +/// This module consists of several key components: +/// +/// * `ZkSolcVersion`: This enum represents the available versions of the zksolc compiler. The +/// `parse_version` function accepts a string version and returns the corresponding +/// `ZkSolcVersion` variant. +/// +/// * `ZkSolcOS`: This enum represents the supported operating systems for the zksolc compiler. +/// `get_operating_system` function determines the current operating system and returns the +/// corresponding `ZkSolcOS` variant. +/// +/// * `ZkSolcManagerOpts`: This structure holds options for creating a `ZkSolcManagerBuilder`, +/// which includes the version of the compiler to be used. +/// +/// * `ZkSolcManagerBuilder`: This structure is used to construct a `ZkSolcManager`. It holds +/// options like compilers path, version, compiler name, and download URL. It has a `build` +/// function which constructs a `ZkSolcManager` instance. +/// +/// * `ZkSolcManager`: This structure manages a particular version of the zksolc compiler. It +/// includes functions to get the full compiler name, check if the compiler exists, setup the +/// compilers directory, and download the compiler if necessary. +/// +/// This module abstracts the details of managing the zksolc compiler, making it easier for +/// developers to use different versions of the compiler without dealing with the details of +/// downloading, setting up, and switching between versions. It is part of a larger framework +/// for managing and interacting with zkSync contracts. +use anyhow::{anyhow, Context, Error, Result}; +use dirs; +use reqwest::blocking::Client; +use serde::Serialize; +use std::{fmt, fs, fs::File, io::copy, os::unix::prelude::PermissionsExt, path::PathBuf}; +use url::Url; + +const ZKSOLC_DOWNLOAD_BASE_URL: &str = "https://github.com/matter-labs/zksolc-bin/raw/main"; + +/// `ZkSolcVersion` is an enumeration of the supported versions of the `zksolc` compiler. +/// +/// Each variant in this enum represents a specific version of the `zksolc` compiler: +/// * `V138` corresponds to version 1.3.8 of the `zksolc` compiler. +/// * `V139` corresponds to version 1.3.9 of the `zksolc` compiler. +/// +/// The `get_version` method is provided to get a string representation of the version associated +/// with each enum variant. +/// +/// This enumeration is used in the `ZkSolcManager` to specify the `zksolc` compiler version to be +/// used for contract compilation. +#[derive(Debug, Clone, Serialize)] +pub enum ZkSolcVersion { + V135, + V136, + V137, + V138, + V139, + V1310, + V1311, +} + +pub const DEFAULT_ZKSOLC_VERSION: &str = "v1.3.11"; + +/// `parse_version` parses a string representation of a `zksolc` compiler version +/// and returns the `ZkSolcVersion` enum variant if it matches a supported version. +/// +/// # Arguments +/// +/// * `version`: A string slice of the `zksolc` version to parse. +/// +/// # Returns +/// +/// A `Result` with the `ZkSolcVersion` variant for the parsed version, or an `Err` +/// if the version isn't supported. +fn parse_version(version: &str) -> Result { + match version { + "v1.3.5" => Ok(ZkSolcVersion::V135), + "v1.3.6" => Ok(ZkSolcVersion::V136), + "v1.3.7" => Ok(ZkSolcVersion::V137), + "v1.3.8" => Ok(ZkSolcVersion::V138), + "v1.3.9" => Ok(ZkSolcVersion::V139), + "v1.3.10" => Ok(ZkSolcVersion::V1310), + "v1.3.11" => Ok(ZkSolcVersion::V1311), + _ => Err(Error::msg( + "ZkSolc compiler version not supported. Proper version format: 'v1.3.x'", + )), + } +} + +impl ZkSolcVersion { + /// `get_version` returns a string slice representing the `ZkSolcVersion` variant. + /// + /// # Returns + /// + /// A string slice representing the `ZkSolcVersion` variant. + fn get_version(&self) -> &str { + match self { + ZkSolcVersion::V135 => "v1.3.5", + ZkSolcVersion::V136 => "v1.3.6", + ZkSolcVersion::V137 => "v1.3.7", + ZkSolcVersion::V138 => "v1.3.8", + ZkSolcVersion::V139 => "v1.3.9", + ZkSolcVersion::V1310 => "v1.3.10", + ZkSolcVersion::V1311 => "v1.3.11", + } + } +} + +#[derive(Debug, Clone, Serialize)] +enum ZkSolcOS { + Linux, + MacAMD, + MacARM, +} + +/// `get_operating_system` identifies the current operating system and returns the corresponding +/// `ZkSolcOS` variant. +/// +/// # Returns +/// +/// `ZkSolcOS` variant representing the current operating system. +/// +/// # Errors +/// +/// If the operating system is not supported, it returns an error. +fn get_operating_system() -> Result { + match std::env::consts::OS { + "linux" => Ok(ZkSolcOS::Linux), + "macos" | "darwin" => match std::env::consts::ARCH { + "aarch64" => Ok(ZkSolcOS::MacARM), + _ => Ok(ZkSolcOS::MacAMD), + }, + _ => Err(Error::msg(format!("Unsupported operating system {}", std::env::consts::OS))), + } +} + +impl ZkSolcOS { + /// `get_compiler` provides the appropriate compiler string based on the current operating + /// system. + /// + /// # Returns + /// + /// A string representing the compiler, depending on the operating system. + /// + /// # Note + /// + /// This function is used to construct the filename for the zkSync compiler binary. + fn get_compiler(&self) -> &str { + match self { + ZkSolcOS::Linux => "zksolc-linux-amd64-musl-", + ZkSolcOS::MacAMD => "zksolc-macosx-amd64-", + ZkSolcOS::MacARM => "zksolc-macosx-arm64-", + } + } + + /// `get_download_uri` provides the appropriate URI for downloading the compiler binary based on + /// the current operating system. + /// + /// # Returns + /// + /// A string representing the URI, depending on the operating system. + /// + /// # Note + /// + /// This function is used to construct the URI for downloading the zkSync compiler binary. + fn get_download_uri(&self) -> &str { + match self { + ZkSolcOS::Linux => "linux-amd64", + ZkSolcOS::MacAMD => "macosx-amd64", + ZkSolcOS::MacARM => "macosx-arm64", + } + } +} + +#[derive(Debug, Clone, Serialize)] +pub struct ZkSolcManagerOpts { + version: String, +} + +impl ZkSolcManagerOpts { + pub fn new(version: String) -> Self { + Self { version } + } +} + +/// `ZkSolcManagerBuilder` is a structure that helps in creating a `ZkSolcManager` instance. +/// +/// This builder pattern is used to encapsulate all the details needed to create a `ZkSolcManager`. +/// The fields include: +/// +/// * `compilers_path`: An optional `PathBuf` that denotes the directory where the compiler binaries +/// are stored. +/// * `version`: A string that represents the version of the zkSync compiler to be used. +/// * `compiler`: An optional string that describes the compiler name. +/// * `download_url`: The base URL from where the zkSync compiler binary is to be downloaded. +/// +/// The builder provides a `new` function to initialize the structure with `ZkSolcManagerOpts`, and +/// a `build` function which constructs and returns a `ZkSolcManager` instance. +/// +/// The `get_compiler` function is used to determine the appropriate compiler name based on the +/// operating system. +/// +/// The builder follows a fluent interface design, allowing the setting of properties in a chained +/// manner. The `build` function should be the final method invoked after all necessary properties +/// have been set. +/// +/// # Example +/// +/// ```ignore +/// use foundry_cli::cmd::forge::zksolc_manager::{ZkSolcManagerBuilder, ZkSolcManagerOpts}; +/// let opts = ZkSolcManagerOpts::new("v1.3.11") +/// let zk_solc_manager = ZkSolcManagerBuilder::new(opts) +/// .build() +/// .expect("Failed to build ZkSolcManager"); +/// ``` +#[derive(Debug, Clone)] +pub struct ZkSolcManagerBuilder { + _compilers_path: Option, + version: String, + _compiler: Option, + download_url: Url, +} + +impl ZkSolcManagerBuilder { + /// Constructs a new instance of `ZkSolcManagerBuilder` using the provided `ZkSolcManagerOpts`. + /// + /// This function takes a `ZkSolcManagerOpts` instance as a parameter, which contains the zkSync + /// compiler version as a string. The builder's `version` field is initialized with this + /// version. The `compilers_path` and `compiler` fields are initialized as `None` since they + /// are optional and can be set later. + /// + /// The `download_url` field is initialized with the constant `ZKSOLC_DOWNLOAD_BASE_URL`, which + /// is the base URL from where the compiler binary will be downloaded. + /// + /// This function should be used as the starting point for creating a `ZkSolcManager` instance + /// using the builder pattern. + /// + /// # Parameters + /// + /// * `opts`: A `ZkSolcManagerOpts` instance containing the zkSync compiler version. + /// + /// # Returns + /// + /// Returns a new `ZkSolcManagerBuilder` instance. + pub fn new(opts: ZkSolcManagerOpts) -> Self { + Self { + _compilers_path: None, + version: opts.version, + _compiler: None, + download_url: Url::parse(ZKSOLC_DOWNLOAD_BASE_URL).unwrap(), + } + } + + /// Returns the appropriate compiler string based on the current operating system. + /// + /// This function determines the current operating system using `get_operating_system`, and + /// returns the corresponding compiler string based on the operating system. The compiler + /// string is used to construct the filename for the `zksolc` compiler binary. + /// + /// # Returns + /// + /// Returns a `Result` representing the compiler string based on the current operating + /// system. + /// + /// # Errors + /// + /// This function can return an `Err` if the operating system cannot be determined using + /// `get_operating_system`. + fn get_compiler(self) -> Result { + get_operating_system() + .with_context(|| "Failed to determine OS for compiler") + .map(|it| it.get_compiler().to_string()) + } + + /// `build` constructs and returns a `ZkSolcManager` instance based on the provided + /// configuration options. + /// + /// This function finalizes the construction of a `ZkSolcManager` instance using the builder + /// pattern. It validates the provided options, resolves the necessary details, and creates + /// the manager instance. + /// + /// The function performs the following steps: + /// 1. Obtains the home directory path and appends the `.zksync` directory to it, which + /// represents the compilers directory. + /// 2. Parses the provided version string and verifies if it matches one of the supported + /// `ZkSolcVersion` variants. + /// 3. Determines the appropriate compiler string based on the current operating system using + /// the `get_compiler` function. + /// 4. Constructs a `ZkSolcManager` instance with the resolved compilers directory, version, + /// compiler, and download URL. + /// + /// # Returns + /// + /// A `Result` containing the constructed `ZkSolcManager` instance if the build process is + /// successful, or an `Err` if any errors occur. + /// + /// # Errors + /// + /// The function can return an `Err` in the following cases: + /// * If the home directory path cannot be determined. + /// * If the provided version string cannot be parsed into a valid `ZkSolcVersion` variant. + /// * If the current operating system is not supported or cannot be determined. + pub fn build(self) -> Result { + // TODO: try catching & returning errors quickly (rather than doing 'long' if and return + // else at the end) + let mut home_path = + dirs::home_dir().ok_or(anyhow!("Could not build SolcManager - homedir not found"))?; + home_path.push(".zksync"); + let version = self.version.to_string(); + let download_url = self.download_url.to_owned(); + let compiler = self.get_compiler()?; + let compilers_path = home_path.to_owned(); + + let solc_version = parse_version(&version)?; + Ok(ZkSolcManager::new(compilers_path, solc_version, compiler, download_url)) + } +} + +/// `ZkSolcManager` is a structure that manages a specific version of the `zksolc` Solidity +/// compiler. +/// +/// This structure encapsulates the functionality to interact with different versions of the +/// `zksolc` compiler, including downloading, setting up, and checking for the existence of the +/// compiler. It provides an abstraction for managing the `zksolc` compiler, making it easier for +/// developers to use different versions without dealing with the low-level details. +/// +/// # Fields +/// +/// * `compilers_path`: A `PathBuf` representing the directory where the compiler binaries are +/// stored. +/// * `version`: A `ZkSolcVersion` representing the specific version of the `zksolc` compiler +/// managed by this instance. +/// * `compiler`: A `String` representing the compiler name. +/// * `download_url`: A `Url` representing the base URL from which the `zksolc` compiler binary is +/// downloaded. +/// +/// # Example +/// +/// ```ignore +/// let compilers_path = PathBuf::from("/path/to/compilers"); +/// let version = ZkSolcVersion::V139; +/// let compiler = "zksolc-linux-amd64-musl-v1.3.9".to_string(); +/// let download_url = Url::parse("https://github.com/matter-labs/zksolc-bin/raw/main").unwrap(); +/// +/// let zksolc_manager = ZkSolcManager::new(compilers_path, version, compiler, download_url); +/// ``` +/// +/// The example above demonstrates the creation of a `ZkSolcManager` instance. It specifies the +/// `compilers_path` where the compiler binaries are stored, the `version` of the `zksolc` compiler, +/// the `compiler` name, and the `download_url` for the compiler binary. +/// +/// # Implementation Details +/// +/// The `ZkSolcManager` structure provides several methods to interact with the `zksolc` compiler: +/// +/// * `get_full_compiler()`: Returns a `String` representing the full compiler name, including the +/// version. +/// * `get_full_download_url()`: Returns a `Result` representing the full download URL for the +/// compiler binary. +/// * `get_full_compiler_path()`: Returns a `PathBuf` representing the full path to the compiler +/// binary. +/// * `exists()`: Checks if the compiler binary exists in the compilers directory. +/// * `check_setup_compilers_dir()`: Checks and sets up the compilers directory if it doesn't exist. +/// * `download()`: Downloads the compiler binary if it doesn't already exist in the compilers +/// directory. +/// +/// The `ZkSolcManager` structure provides a high-level interface to manage the `zksolc` compiler, +/// simplifying the process of handling different versions and ensuring the availability of the +/// compiler for contract compilation. +#[derive(Debug, Clone)] +pub struct ZkSolcManager { + compilers_path: PathBuf, + version: ZkSolcVersion, + compiler: String, + download_url: Url, +} + +impl fmt::Display for ZkSolcManager { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "ZkSolcManager ( + compilers_path: {}, + version: {}, + compiler: {}, + download_url: {}, + compiler_name: {}, + full_download_url: {} + exists: {} + )", + self.compilers_path.display(), + self.version.get_version(), + self.compiler, + self.download_url, + self.get_full_compiler(), + self.get_full_download_url().unwrap(), + self.exists(), + ) + } +} + +impl ZkSolcManager { + /// Constructs a new instance of `ZkSolcManager` with the specified configuration options. + /// + /// This function creates a new `ZkSolcManager` instance with the provided parameters. It + /// initializes the manager with the compilers directory path, the version of the `zksolc` + /// compiler, the compiler name, and the download URL for the compiler binary. + /// + /// # Parameters + /// + /// * `compilers_path`: A `PathBuf` representing the directory where the compiler binaries are + /// stored. + /// * `version`: A `ZkSolcVersion` representing the specific version of the `zksolc` compiler + /// managed by this instance. + /// * `compiler`: A `String` representing the compiler name. + /// * `download_url`: A `Url` representing the base URL from which the `zksolc` compiler binary + /// is downloaded. + /// + /// # Returns + /// + /// Returns a new `ZkSolcManager` instance with the specified configuration options. + pub fn new( + compilers_path: PathBuf, + version: ZkSolcVersion, + compiler: String, + download_url: Url, + ) -> Self { + Self { compilers_path, version, compiler, download_url } + } + + /// Returns the full name of the `zksolc` compiler, including the version. + /// + /// This function constructs and returns the full name of the `zksolc` compiler by combining the + /// base compiler name with the specific version associated with the `ZkSolcManager` + /// instance. The resulting string represents the complete name of the compiler, including + /// the version. + /// + /// # Returns + /// + /// Returns a `String` representing the full name of the `zksolc` compiler, including the + /// version. + pub fn get_full_compiler(&self) -> String { + format!("{}{}", self.compiler, self.version.get_version()) + } + + /// Returns the full download URL for the `zksolc` compiler binary based on the current + /// operating system. + /// + /// This function constructs the full download URL for the `zksolc` compiler binary by combining + /// the base download URL with the appropriate download URI based on the current operating + /// system. The resulting URL represents the location from which the compiler binary can be + /// downloaded. + /// + /// # Returns + /// + /// Returns a `Result` representing the full download URL for the `zksolc` compiler binary. + /// + /// # Errors + /// + /// This function can return an `Err` if the full download URL cannot be parsed into a valid + /// `Url`. + pub fn get_full_download_url(&self) -> Result { + let zk_solc_os = get_operating_system() + .map_err(|err| anyhow!("Failed to determine OS to select the binary: {}", err))?; + + let download_uri = zk_solc_os.get_download_uri(); + + let full_download_url = + format!("{}/{}/{}", self.download_url, download_uri, self.get_full_compiler()); + + Url::parse(&full_download_url) + .map_err(|err| anyhow!("Could not parse URL for binary download: {}", err)) + } + + pub fn get_full_compiler_path(&self) -> PathBuf { + self.compilers_path.join(self.clone().get_full_compiler()) + } + + /// Checks if the `zksolc` compiler binary exists in the compilers directory. + /// + /// This function checks if the `zksolc` compiler binary file exists in the compilers directory + /// specified by `compilers_path`. and checks if it is a regular file and has executable + /// permissions. + /// + /// # Returns + /// + /// Returns a `bool` indicating whether the compiler binary exists in the compilers directory + /// and has executable permissions. + pub fn exists(&self) -> bool { + let compiler_path = self.compilers_path.join(self.clone().get_full_compiler()); + + fs::metadata(compiler_path) + .map(|metadata| metadata.is_file() && metadata.permissions().mode() & 0o755 != 0) + .unwrap_or(false) + } + + /// Checks if the compilers directory exists and creates it if it doesn't. + /// + /// This function checks if the compilers directory specified by `compilers_path` exists. If the + /// directory doesn't exist, it creates the directory and any necessary parent directories + /// using `fs::create_dir_all`. + /// + /// # Returns + /// + /// Returns a `Result<()>` indicating success if the compilers directory exists or is + /// successfully created. + /// + /// # Errors + /// + /// This function can return an `Err` if an error occurs during the creation of the compilers + /// directory, such as: + /// * If the compilers directory path cannot be resolved. + /// * If there is a failure in creating the directory structure using `fs::create_dir_all`. + pub fn check_setup_compilers_dir(&self) -> Result<()> { + if !self.compilers_path.exists() { + fs::create_dir_all(&self.compilers_path) + .map_err(|e| Error::msg(format!("Could not create compilers path: {}", e)))?; + } + Ok(()) + } + + /// Downloads the `zksolc` compiler binary if it doesn't already exist in the compilers + /// directory. + /// + /// This function downloads the `zksolc` compiler binary from the specified download URL if it + /// doesn't already exist in the compilers directory. It performs the following steps: + /// 1. Checks if the compiler binary already exists in the compilers directory using the + /// `exists` function. + /// 2. If the binary exists, the function returns early without performing any download. + /// 3. If the binary doesn't exist, it sends a HTTP GET request to the download URL to retrieve + /// the binary. + /// 4. If the download is successful, it creates the output file in the compilers directory and + /// writes the binary data to it. + /// 5. Finally, it sets the appropriate permissions for the downloaded compiler binary. + /// + /// # Returns + /// + /// Returns a `Result<()>` indicating success if the download and setup process completes + /// without any errors. + /// + /// # Errors + /// + /// This function can return an `Err` if any errors occur during the download or setup process, + /// including: + /// * If the download URL cannot be obtained using `get_full_download_url`. + /// * If the HTTP GET request to the download URL fails. + /// * If the output file cannot be created or written to. + /// * If the permissions for the downloaded compiler binary cannot be set. + pub fn download(&self) -> Result<()> { + if self.exists() { + // TODO: figure out better don't download if compiler is downloaded + return Ok(()) + } + + let url = self + .get_full_download_url() + .map_err(|e| Error::msg(format!("Could not get full download url: {}", e)))?; + + let client = Client::new(); + let mut response = client + .get(url) + .send() + .map_err(|e| Error::msg(format!("Failed to download file: {}", e)))?; + + if response.status().is_success() { + let mut output_file = File::create(self.get_full_compiler_path()) + .map_err(|e| Error::msg(format!("Failed to create output file: {}", e)))?; + + copy(&mut response, &mut output_file) + .map_err(|e| Error::msg(format!("Failed to write the downloaded file: {}", e)))?; + + let compiler_path = self.compilers_path.join(self.get_full_compiler()); + fs::set_permissions(compiler_path, PermissionsExt::from_mode(0o755)).map_err(|e| { + Error::msg(format!("Failed to set zksync compiler permissions: {e}")) + })?; + } else { + return Err(Error::msg(format!( + "Failed to download file: status code {}", + response.status() + ))) + } + Ok(()) + } +} diff --git a/crates/zkforge/bin/main.rs b/crates/zkforge/bin/main.rs new file mode 100644 index 000000000..9519ca7f3 --- /dev/null +++ b/crates/zkforge/bin/main.rs @@ -0,0 +1,97 @@ +use clap::{CommandFactory, Parser}; +use clap_complete::generate; +use eyre::Result; +use foundry_cli::{handler, utils}; + +mod cmd; +mod opts; + +use cmd::{cache::CacheSubcommands, generate::GenerateSubcommands, watch}; +use opts::{Opts, Subcommands}; + +fn main() -> Result<()> { + utils::load_dotenv(); + handler::install()?; + utils::subscriber(); + utils::enable_paint(); + + let opts = Opts::parse(); + match opts.sub { + Subcommands::Test(cmd) => { + if cmd.is_watch() { + utils::block_on(watch::watch_test(cmd)) + } else { + let outcome = utils::block_on(cmd.run())?; + outcome.ensure_ok() + } + } + Subcommands::Script(cmd) => { + // install the shell before executing the command + foundry_common::shell::set_shell(foundry_common::shell::Shell::from_args( + cmd.opts.args.silent, + cmd.json, + ))?; + utils::block_on(cmd.run_script()) + } + Subcommands::Coverage(cmd) => utils::block_on(cmd.run()), + Subcommands::Bind(cmd) => cmd.run(), + Subcommands::ZkBuild(cmd) => cmd.run(), + Subcommands::Debug(cmd) => utils::block_on(cmd.run()), + Subcommands::VerifyContract(args) => utils::block_on(args.run()), + Subcommands::VerifyCheck(args) => utils::block_on(args.run()), + Subcommands::Cache(cmd) => match cmd.sub { + CacheSubcommands::Clean(cmd) => cmd.run(), + CacheSubcommands::Ls(cmd) => cmd.run(), + }, + Subcommands::ZkCreate(cmd) => utils::block_on(cmd.run()), + Subcommands::Update(cmd) => cmd.run(), + Subcommands::Install(cmd) => cmd.run(), + Subcommands::Remove(cmd) => cmd.run(), + Subcommands::Remappings(cmd) => cmd.run(), + Subcommands::Init(cmd) => cmd.run(), + Subcommands::Completions { shell } => { + generate(shell, &mut Opts::command(), "forge", &mut std::io::stdout()); + Ok(()) + } + Subcommands::GenerateFigSpec => { + clap_complete::generate( + clap_complete_fig::Fig, + &mut Opts::command(), + "forge", + &mut std::io::stdout(), + ); + Ok(()) + } + Subcommands::Clean { root } => { + let config = utils::load_config_with_root(root); + config.project()?.cleanup()?; + Ok(()) + } + Subcommands::Snapshot(cmd) => { + if cmd.is_watch() { + utils::block_on(watch::watch_snapshot(cmd)) + } else { + utils::block_on(cmd.run()) + } + } + Subcommands::Fmt(cmd) => cmd.run(), + Subcommands::Config(cmd) => cmd.run(), + Subcommands::Flatten(cmd) => cmd.run(), + Subcommands::Inspect(cmd) => cmd.run(), + Subcommands::UploadSelectors(args) => utils::block_on(args.run()), + Subcommands::Tree(cmd) => cmd.run(), + Subcommands::Geiger(cmd) => { + let check = cmd.check; + let n = cmd.run()?; + if check && n > 0 { + std::process::exit(n as i32); + } + Ok(()) + } + Subcommands::Doc(cmd) => cmd.run(), + Subcommands::Selectors { command } => utils::block_on(command.run()), + Subcommands::Generate(cmd) => match cmd.sub { + GenerateSubcommands::Test(cmd) => cmd.run(), + }, + } +} diff --git a/crates/zkforge/bin/opts.rs b/crates/zkforge/bin/opts.rs new file mode 100644 index 000000000..d0e677904 --- /dev/null +++ b/crates/zkforge/bin/opts.rs @@ -0,0 +1,173 @@ +use crate::cmd::{ + bind::BindArgs, + cache::CacheArgs, + config, coverage, + debug::DebugArgs, + doc::DocArgs, + flatten, + fmt::FmtArgs, + fourbyte::UploadSelectorsArgs, + geiger, generate, + init::InitArgs, + inspect, + install::InstallArgs, + remappings::RemappingArgs, + remove::RemoveArgs, + script::ScriptArgs, + selectors::SelectorsSubcommands, + snapshot, test, tree, update, + verify::{VerifyArgs, VerifyCheckArgs}, + zk_build::ZkBuildArgs, + zk_create::ZkCreateArgs, +}; +use clap::{Parser, Subcommand, ValueHint}; +use std::path::PathBuf; + +const VERSION_MESSAGE: &str = concat!( + env!("CARGO_PKG_VERSION"), + " (", + env!("VERGEN_GIT_SHA"), + " ", + env!("VERGEN_BUILD_TIMESTAMP"), + ")" +); + +#[derive(Debug, Parser)] +#[clap(name = "forge", version = VERSION_MESSAGE)] +pub struct Opts { + #[clap(subcommand)] + pub sub: Subcommands, +} + +#[derive(Debug, Subcommand)] +#[clap( + about = "Build, test, fuzz, debug and deploy Solidity contracts.", + after_help = "Find more information in the book: http://book.getfoundry.sh/reference/forge/forge.html", + next_display_order = None +)] +#[allow(clippy::large_enum_variant)] +pub enum Subcommands { + /// Run the project's tests. + #[clap(visible_alias = "t")] + Test(test::TestArgs), + + /// Run a smart contract as a script, building transactions that can be sent onchain. + Script(ScriptArgs), + + /// Generate coverage reports. + Coverage(coverage::CoverageArgs), + + /// Generate Rust bindings for smart contracts. + #[clap(alias = "bi")] + Bind(BindArgs), + + /// Debugs a single smart contract as a script. + #[clap(visible_alias = "d")] + Debug(DebugArgs), + + /// Update one or multiple dependencies. + /// + /// If no arguments are provided, then all dependencies are updated. + #[clap(visible_alias = "u")] + Update(update::UpdateArgs), + + /// Install one or multiple dependencies. + /// + /// If no arguments are provided, then existing dependencies will be installed. + #[clap(visible_alias = "i")] + Install(InstallArgs), + + /// Remove one or multiple dependencies. + #[clap(visible_alias = "rm")] + Remove(RemoveArgs), + + /// Get the automatically inferred remappings for the project. + #[clap(visible_alias = "re")] + Remappings(RemappingArgs), + + /// Verify smart contracts on Etherscan. + #[clap(visible_alias = "v")] + VerifyContract(VerifyArgs), + + /// Check verification status on Etherscan. + #[clap(visible_alias = "vc")] + VerifyCheck(VerifyCheckArgs), + + /// Create a new Forge project. + Init(InitArgs), + + /// Generate shell completions script. + #[clap(visible_alias = "com")] + Completions { + #[clap(value_enum)] + shell: clap_complete::Shell, + }, + + /// Generate Fig autocompletion spec. + #[clap(visible_alias = "fig")] + GenerateFigSpec, + + /// Remove the build artifacts and cache directories. + #[clap(visible_alias = "cl")] + Clean { + /// The project's root path. + /// + /// By default root of the Git repository, if in one, + /// or the current working directory. + #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + root: Option, + }, + + /// Manage the Foundry cache. + Cache(CacheArgs), + + /// Create a snapshot of each test's gas usage. + #[clap(visible_alias = "s")] + Snapshot(snapshot::SnapshotArgs), + + /// Display the current config. + #[clap(visible_alias = "co")] + Config(config::ConfigArgs), + + /// Flatten a source file and all of its imports into one file. + #[clap(visible_alias = "f")] + Flatten(flatten::FlattenArgs), + + /// Format Solidity source files. + Fmt(FmtArgs), + + /// Get specialized information about a smart contract. + #[clap(visible_alias = "in")] + Inspect(inspect::InspectArgs), + + /// Uploads abi of given contract to the https://api.openchain.xyz + /// function selector database. + #[clap(visible_alias = "up")] + UploadSelectors(UploadSelectorsArgs), + + /// Display a tree visualization of the project's dependency graph. + #[clap(visible_alias = "tr")] + Tree(tree::TreeArgs), + + /// Detects usage of unsafe cheat codes in a project and its dependencies. + Geiger(geiger::GeigerArgs), + + /// Generate documentation for the project. + Doc(DocArgs), + + #[clap(visible_aliases = ["zkb", "zkbuild", "zk-compile"], about = "Build the project's smart contracts for zksync.")] + ZkBuild(ZkBuildArgs), + + #[clap(visible_aliases = ["zkc", "zkcreate", "zk-deploy"], about = "Deploy smart contracts to zksync.")] + ZkCreate(ZkCreateArgs), + + /// Function selector utilities + #[clap(visible_alias = "se")] + Selectors { + #[clap(subcommand)] + command: SelectorsSubcommands, + }, + + /// Generate scaffold files. + Generate(generate::GenerateArgs), +} diff --git a/crates/zkforge/build.rs b/crates/zkforge/build.rs new file mode 100644 index 000000000..c2f550fb6 --- /dev/null +++ b/crates/zkforge/build.rs @@ -0,0 +1,3 @@ +fn main() { + vergen::EmitBuilder::builder().build_timestamp().git_sha(true).emit().unwrap(); +} diff --git a/crates/zkforge/src/coverage.rs b/crates/zkforge/src/coverage.rs new file mode 100644 index 000000000..d9cfabaff --- /dev/null +++ b/crates/zkforge/src/coverage.rs @@ -0,0 +1,170 @@ +use comfy_table::{presets::ASCII_MARKDOWN, Attribute, Cell, Color, Row, Table}; +pub use foundry_evm::coverage::*; +use std::io::Write; + +/// A coverage reporter. +pub trait CoverageReporter { + fn report(self, report: &CoverageReport) -> eyre::Result<()>; +} + +/// A simple summary reporter that prints the coverage results in a table. +pub struct SummaryReporter { + /// The summary table. + table: Table, + /// The total coverage of the entire project. + total: CoverageSummary, +} + +impl Default for SummaryReporter { + fn default() -> Self { + let mut table = Table::new(); + table.load_preset(ASCII_MARKDOWN); + table.set_header(["File", "% Lines", "% Statements", "% Branches", "% Funcs"]); + + Self { table, total: CoverageSummary::default() } + } +} + +impl SummaryReporter { + fn add_row(&mut self, name: impl Into, summary: CoverageSummary) { + let mut row = Row::new(); + row.add_cell(name.into()) + .add_cell(format_cell(summary.line_hits, summary.line_count)) + .add_cell(format_cell(summary.statement_hits, summary.statement_count)) + .add_cell(format_cell(summary.branch_hits, summary.branch_count)) + .add_cell(format_cell(summary.function_hits, summary.function_count)); + self.table.add_row(row); + } +} + +impl CoverageReporter for SummaryReporter { + fn report(mut self, report: &CoverageReport) -> eyre::Result<()> { + for (path, summary) in report.summary_by_file() { + self.total += &summary; + self.add_row(path, summary); + } + + self.add_row("Total", self.total.clone()); + println!("{}", self.table); + Ok(()) + } +} + +fn format_cell(hits: usize, total: usize) -> Cell { + let percentage = if total == 0 { 1. } else { hits as f64 / total as f64 }; + + let mut cell = + Cell::new(format!("{:.2}% ({hits}/{total})", percentage * 100.)).fg(match percentage { + _ if total == 0 => Color::Grey, + _ if percentage < 0.5 => Color::Red, + _ if percentage < 0.75 => Color::Yellow, + _ => Color::Green, + }); + + if total == 0 { + cell = cell.add_attribute(Attribute::Dim); + } + cell +} + +pub struct LcovReporter<'a> { + /// Destination buffer + destination: &'a mut (dyn Write + 'a), +} + +impl<'a> LcovReporter<'a> { + pub fn new(destination: &'a mut (dyn Write + 'a)) -> LcovReporter<'a> { + Self { destination } + } +} + +impl<'a> CoverageReporter for LcovReporter<'a> { + fn report(self, report: &CoverageReport) -> eyre::Result<()> { + for (file, items) in report.items_by_source() { + let summary = items.iter().fold(CoverageSummary::default(), |mut summary, item| { + summary += item; + summary + }); + + writeln!(self.destination, "TN:")?; + writeln!(self.destination, "SF:{file}")?; + + for item in items { + let line = item.loc.line; + let hits = item.hits; + match item.kind { + CoverageItemKind::Function { name } => { + let name = format!("{}.{name}", item.loc.contract_name); + writeln!(self.destination, "FN:{line},{name}")?; + writeln!(self.destination, "FNDA:{hits},{name}")?; + } + CoverageItemKind::Line => { + writeln!(self.destination, "DA:{line},{hits}")?; + } + CoverageItemKind::Branch { branch_id, path_id } => { + writeln!( + self.destination, + "BRDA:{line},{branch_id},{path_id},{}", + if hits == 0 { "-".to_string() } else { hits.to_string() } + )?; + } + // Statements are not in the LCOV format + CoverageItemKind::Statement => (), + } + } + + // Function summary + writeln!(self.destination, "FNF:{}", summary.function_count)?; + writeln!(self.destination, "FNH:{}", summary.function_hits)?; + + // Line summary + writeln!(self.destination, "LF:{}", summary.line_count)?; + writeln!(self.destination, "LH:{}", summary.line_hits)?; + + // Branch summary + writeln!(self.destination, "BRF:{}", summary.branch_count)?; + writeln!(self.destination, "BRH:{}", summary.branch_hits)?; + + writeln!(self.destination, "end_of_record")?; + } + + println!("Wrote LCOV report."); + + Ok(()) + } +} + +/// A super verbose reporter for debugging coverage while it is still unstable. +pub struct DebugReporter; + +impl CoverageReporter for DebugReporter { + fn report(self, report: &CoverageReport) -> eyre::Result<()> { + for (path, items) in report.items_by_source() { + println!("Uncovered for {path}:"); + items.iter().for_each(|item| { + if item.hits == 0 { + println!("- {item}"); + } + }); + println!(); + } + + for (contract_id, anchors) in &report.anchors { + println!("Anchors for {contract_id}:"); + anchors.iter().for_each(|anchor| { + println!("- {anchor}"); + println!( + " - Refers to item: {}", + report + .items + .get(&contract_id.version) + .and_then(|items| items.get(anchor.item_id)) + .map_or("None".to_owned(), |item| item.to_string()) + ); + }); + println!(); + } + + Ok(()) + } +} diff --git a/crates/zkforge/src/gas_report.rs b/crates/zkforge/src/gas_report.rs new file mode 100644 index 000000000..59e51e78f --- /dev/null +++ b/crates/zkforge/src/gas_report.rs @@ -0,0 +1,168 @@ +use crate::{ + executor::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS}, + trace::{CallTraceArena, RawOrDecodedCall, TraceKind}, +}; +use comfy_table::{presets::ASCII_MARKDOWN, *}; +use ethers::types::U256; +use foundry_common::{calc, TestFunctionExt}; +use foundry_utils::types::ToEthers; +use serde::{Deserialize, Serialize}; +use std::{collections::BTreeMap, fmt::Display}; + +#[derive(Default, Debug, Serialize, Deserialize)] +pub struct GasReport { + pub report_for: Vec, + pub ignore: Vec, + pub contracts: BTreeMap, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct ContractInfo { + pub gas: U256, + pub size: U256, + pub functions: BTreeMap>, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct GasInfo { + pub calls: Vec, + pub min: U256, + pub mean: U256, + pub median: U256, + pub max: U256, +} + +impl GasReport { + pub fn new(report_for: Vec, ignore: Vec) -> Self { + Self { report_for, ignore, ..Default::default() } + } + + pub fn analyze(&mut self, traces: &[(TraceKind, CallTraceArena)]) { + traces.iter().for_each(|(_, trace)| { + self.analyze_node(0, trace); + }); + } + + fn analyze_node(&mut self, node_index: usize, arena: &CallTraceArena) { + let node = &arena.arena[node_index]; + let trace = &node.trace; + + if trace.address == CHEATCODE_ADDRESS.to_ethers() || + trace.address == HARDHAT_CONSOLE_ADDRESS.to_ethers() + { + return + } + + if let Some(name) = &trace.contract { + let contract_name = name.rsplit(':').next().unwrap_or(name.as_str()).to_string(); + // If the user listed the contract in 'gas_reports' (the foundry.toml field) a + // report for the contract is generated even if it's listed in the ignore + // list. This is addressed this way because getting a report you don't expect is + // preferable than not getting one you expect. A warning is printed to stderr + // indicating the "double listing". + if self.report_for.contains(&contract_name) && self.ignore.contains(&contract_name) { + eprintln!( + "{}: {} is listed in both 'gas_reports' and 'gas_reports_ignore'.", + yansi::Paint::yellow("warning").bold(), + contract_name + ); + } + let report_contract = (!self.ignore.contains(&contract_name) && + self.report_for.contains(&"*".to_string())) || + (!self.ignore.contains(&contract_name) && self.report_for.is_empty()) || + (self.report_for.contains(&contract_name)); + if report_contract { + let contract_report = self.contracts.entry(name.to_string()).or_default(); + + match &trace.data { + RawOrDecodedCall::Raw(bytes) if trace.created() => { + contract_report.gas = trace.gas_cost.into(); + contract_report.size = bytes.len().into(); + } + // TODO: More robust test contract filtering + RawOrDecodedCall::Decoded(func, sig, _) + if !func.is_test() && !func.is_setup() => + { + let function_report = contract_report + .functions + .entry(func.clone()) + .or_default() + .entry(sig.clone()) + .or_default(); + function_report.calls.push(trace.gas_cost.into()); + } + _ => (), + } + } + } + + node.children.iter().for_each(|index| { + self.analyze_node(*index, arena); + }); + } + + #[must_use] + pub fn finalize(mut self) -> Self { + self.contracts.iter_mut().for_each(|(_, contract)| { + contract.functions.iter_mut().for_each(|(_, sigs)| { + sigs.iter_mut().for_each(|(_, func)| { + func.calls.sort_unstable(); + func.min = func.calls.first().copied().unwrap_or_default(); + func.max = func.calls.last().copied().unwrap_or_default(); + func.mean = calc::mean(&func.calls); + func.median = calc::median_sorted(&func.calls); + }); + }); + }); + self + } +} + +impl Display for GasReport { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + for (name, contract) in self.contracts.iter() { + if contract.functions.is_empty() { + continue + } + + let mut table = Table::new(); + table.load_preset(ASCII_MARKDOWN); + table.set_header(vec![Cell::new(format!("{name} contract")) + .add_attribute(Attribute::Bold) + .fg(Color::Green)]); + table.add_row(vec![ + Cell::new("Deployment Cost").add_attribute(Attribute::Bold).fg(Color::Cyan), + Cell::new("Deployment Size").add_attribute(Attribute::Bold).fg(Color::Cyan), + ]); + table.add_row(vec![contract.gas.to_string(), contract.size.to_string()]); + + table.add_row(vec![ + Cell::new("Function Name").add_attribute(Attribute::Bold).fg(Color::Magenta), + Cell::new("min").add_attribute(Attribute::Bold).fg(Color::Green), + Cell::new("avg").add_attribute(Attribute::Bold).fg(Color::Yellow), + Cell::new("median").add_attribute(Attribute::Bold).fg(Color::Yellow), + Cell::new("max").add_attribute(Attribute::Bold).fg(Color::Red), + Cell::new("# calls").add_attribute(Attribute::Bold), + ]); + contract.functions.iter().for_each(|(fname, sigs)| { + sigs.iter().for_each(|(sig, function)| { + // show function signature if overloaded else name + let fn_display = + if sigs.len() == 1 { fname.clone() } else { sig.replace(':', "") }; + + table.add_row(vec![ + Cell::new(fn_display).add_attribute(Attribute::Bold), + Cell::new(function.min.to_string()).fg(Color::Green), + Cell::new(function.mean.to_string()).fg(Color::Yellow), + Cell::new(function.median.to_string()).fg(Color::Yellow), + Cell::new(function.max.to_string()).fg(Color::Red), + Cell::new(function.calls.len().to_string()), + ]); + }) + }); + writeln!(f, "{table}")?; + writeln!(f, "\n")?; + } + Ok(()) + } +} diff --git a/crates/zkforge/src/lib.rs b/crates/zkforge/src/lib.rs new file mode 100644 index 000000000..99af3a7e9 --- /dev/null +++ b/crates/zkforge/src/lib.rs @@ -0,0 +1,218 @@ +use ethers::solc::ProjectCompileOutput; +use foundry_config::{ + validate_profiles, Config, FuzzConfig, InlineConfig, InlineConfigError, InlineConfigParser, + InvariantConfig, NatSpec, +}; + +use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner}; +use std::path::Path; + +#[macro_use] +extern crate tracing; + +/// Gas reports +pub mod gas_report; + +/// Coverage reports +pub mod coverage; + +/// The Forge test runner +mod runner; +pub use runner::ContractRunner; + +/// Forge test runners for multiple contracts +mod multi_runner; +pub use multi_runner::{MultiContractRunner, MultiContractRunnerBuilder}; + +/// reexport +pub use foundry_common::traits::TestFilter; + +pub mod result; + +/// The Forge EVM backend +pub use foundry_evm::*; + +/// Metadata on how to run fuzz/invariant tests +#[derive(Debug, Clone, Default)] +pub struct TestOptions { + /// The base "fuzz" test configuration. To be used as a fallback in case + /// no more specific configs are found for a given run. + pub fuzz: FuzzConfig, + /// The base "invariant" test configuration. To be used as a fallback in case + /// no more specific configs are found for a given run. + pub invariant: InvariantConfig, + /// Contains per-test specific "fuzz" configurations. + pub inline_fuzz: InlineConfig, + /// Contains per-test specific "invariant" configurations. + pub inline_invariant: InlineConfig, +} + +impl TestOptions { + /// Tries to create a new instance by detecting inline configurations from the project compile + /// output. + pub fn new( + output: &ProjectCompileOutput, + root: &Path, + profiles: Vec, + base_fuzz: FuzzConfig, + base_invariant: InvariantConfig, + ) -> Result { + let natspecs: Vec = NatSpec::parse(output, root); + let mut inline_invariant = InlineConfig::::default(); + let mut inline_fuzz = InlineConfig::::default(); + + for natspec in natspecs { + // Perform general validation + validate_profiles(&natspec, &profiles)?; + FuzzConfig::validate_configs(&natspec)?; + InvariantConfig::validate_configs(&natspec)?; + + // Apply in-line configurations for the current profile + let configs: Vec = natspec.current_profile_configs().collect(); + let c: &str = &natspec.contract; + let f: &str = &natspec.function; + let line: String = natspec.debug_context(); + + match base_fuzz.try_merge(&configs) { + Ok(Some(conf)) => inline_fuzz.insert(c, f, conf), + Ok(None) => { /* No inline config found, do nothing */ } + Err(e) => Err(InlineConfigError { line: line.clone(), source: e })?, + } + + match base_invariant.try_merge(&configs) { + Ok(Some(conf)) => inline_invariant.insert(c, f, conf), + Ok(None) => { /* No inline config found, do nothing */ } + Err(e) => Err(InlineConfigError { line: line.clone(), source: e })?, + } + } + + Ok(Self { fuzz: base_fuzz, invariant: base_invariant, inline_fuzz, inline_invariant }) + } + + /// Returns a "fuzz" test runner instance. Parameters are used to select tight scoped fuzz + /// configs that apply for a contract-function pair. A fallback configuration is applied + /// if no specific setup is found for a given input. + /// + /// - `contract_id` is the id of the test contract, expressed as a relative path from the + /// project root. + /// - `test_fn` is the name of the test function declared inside the test contract. + pub fn fuzz_runner(&self, contract_id: S, test_fn: S) -> TestRunner + where + S: Into, + { + let fuzz = self.fuzz_config(contract_id, test_fn); + self.fuzzer_with_cases(fuzz.runs) + } + + /// Returns an "invariant" test runner instance. Parameters are used to select tight scoped fuzz + /// configs that apply for a contract-function pair. A fallback configuration is applied + /// if no specific setup is found for a given input. + /// + /// - `contract_id` is the id of the test contract, expressed as a relative path from the + /// project root. + /// - `test_fn` is the name of the test function declared inside the test contract. + pub fn invariant_runner(&self, contract_id: S, test_fn: S) -> TestRunner + where + S: Into, + { + let invariant = self.invariant_config(contract_id, test_fn); + self.fuzzer_with_cases(invariant.runs) + } + + /// Returns a "fuzz" configuration setup. Parameters are used to select tight scoped fuzz + /// configs that apply for a contract-function pair. A fallback configuration is applied + /// if no specific setup is found for a given input. + /// + /// - `contract_id` is the id of the test contract, expressed as a relative path from the + /// project root. + /// - `test_fn` is the name of the test function declared inside the test contract. + pub fn fuzz_config(&self, contract_id: S, test_fn: S) -> &FuzzConfig + where + S: Into, + { + self.inline_fuzz.get(contract_id, test_fn).unwrap_or(&self.fuzz) + } + + /// Returns an "invariant" configuration setup. Parameters are used to select tight scoped + /// invariant configs that apply for a contract-function pair. A fallback configuration is + /// applied if no specific setup is found for a given input. + /// + /// - `contract_id` is the id of the test contract, expressed as a relative path from the + /// project root. + /// - `test_fn` is the name of the test function declared inside the test contract. + pub fn invariant_config(&self, contract_id: S, test_fn: S) -> &InvariantConfig + where + S: Into, + { + self.inline_invariant.get(contract_id, test_fn).unwrap_or(&self.invariant) + } + + pub fn fuzzer_with_cases(&self, cases: u32) -> TestRunner { + // TODO: Add Options to modify the persistence + let cfg = proptest::test_runner::Config { + failure_persistence: None, + cases, + max_global_rejects: self.fuzz.max_test_rejects, + ..Default::default() + }; + + if let Some(ref fuzz_seed) = self.fuzz.seed { + trace!(target: "forge::test", "building deterministic fuzzer with seed {}", fuzz_seed); + let mut bytes: [u8; 32] = [0; 32]; + fuzz_seed.to_big_endian(&mut bytes); + let rng = TestRng::from_seed(RngAlgorithm::ChaCha, &bytes); + TestRunner::new_with_rng(cfg, rng) + } else { + trace!(target: "forge::test", "building stochastic fuzzer"); + TestRunner::new(cfg) + } + } +} + +/// Builder utility to create a [`TestOptions`] instance. +#[derive(Default)] +#[must_use = "builders do nothing unless you call `build` on them"] +pub struct TestOptionsBuilder { + fuzz: Option, + invariant: Option, + profiles: Option>, +} + +impl TestOptionsBuilder { + /// Sets a [`FuzzConfig`] to be used as base "fuzz" configuration. + pub fn fuzz(mut self, conf: FuzzConfig) -> Self { + self.fuzz = Some(conf); + self + } + + /// Sets a [`InvariantConfig`] to be used as base "invariant" configuration. + pub fn invariant(mut self, conf: InvariantConfig) -> Self { + self.invariant = Some(conf); + self + } + + /// Sets available configuration profiles. Profiles are useful to validate existing in-line + /// configurations. This argument is necessary in case a `compile_output`is provided. + pub fn profiles(mut self, p: Vec) -> Self { + self.profiles = Some(p); + self + } + + /// Creates an instance of [`TestOptions`]. This takes care of creating "fuzz" and + /// "invariant" fallbacks, and extracting all inline test configs, if available. + /// + /// `root` is a reference to the user's project root dir. This is essential + /// to determine the base path of generated contract identifiers. This is to provide correct + /// matchers for inline test configs. + pub fn build( + self, + output: &ProjectCompileOutput, + root: &Path, + ) -> Result { + let profiles: Vec = + self.profiles.unwrap_or_else(|| vec![Config::selected_profile().into()]); + let base_fuzz = self.fuzz.unwrap_or_default(); + let base_invariant = self.invariant.unwrap_or_default(); + TestOptions::new(output, root, profiles, base_fuzz, base_invariant) + } +} diff --git a/crates/zkforge/src/multi_runner.rs b/crates/zkforge/src/multi_runner.rs new file mode 100644 index 000000000..6f26d6755 --- /dev/null +++ b/crates/zkforge/src/multi_runner.rs @@ -0,0 +1,417 @@ +use crate::{result::SuiteResult, ContractRunner, TestFilter, TestOptions}; +use alloy_primitives::{Address, Bytes, U256}; +use ethers::{ + abi::{Abi, Function}, + prelude::{artifacts::CompactContractBytecode, ArtifactId, ArtifactOutput}, + solc::{contracts::ArtifactContracts, Artifact, ProjectCompileOutput}, +}; +use eyre::Result; +use foundry_common::{ContractsByArtifact, TestFunctionExt}; +use foundry_evm::{ + executor::{ + backend::Backend, fork::CreateFork, inspector::CheatsConfig, opts::EvmOpts, Executor, + ExecutorBuilder, + }, + revm, +}; +use foundry_utils::{types::ToEthers, PostLinkInput, ResolvedDependency}; +use rayon::prelude::*; +use revm::primitives::SpecId; +use std::{ + collections::{BTreeMap, HashSet}, + iter::Iterator, + path::Path, + sync::{mpsc::Sender, Arc}, +}; + +pub type DeployableContracts = BTreeMap)>; + +/// A multi contract runner receives a set of contracts deployed in an EVM instance and proceeds +/// to run all test functions in these contracts. +pub struct MultiContractRunner { + /// Mapping of contract name to Abi, creation bytecode and library bytecode which + /// needs to be deployed & linked against + pub contracts: DeployableContracts, + /// Compiled contracts by name that have an Abi and runtime bytecode + pub known_contracts: ContractsByArtifact, + /// The EVM instance used in the test runner + pub evm_opts: EvmOpts, + /// The configured evm + pub env: revm::primitives::Env, + /// The EVM spec + pub evm_spec: SpecId, + /// All known errors, used for decoding reverts + pub errors: Option, + /// The address which will be used as the `from` field in all EVM calls + pub sender: Option
, + /// A map of contract names to absolute source file paths + pub source_paths: BTreeMap, + /// The fork to use at launch + pub fork: Option, + /// Additional cheatcode inspector related settings derived from the `Config` + pub cheats_config: Arc, + /// Whether to collect coverage info + pub coverage: bool, + /// Whether to collect debug info + pub debug: bool, + /// Settings related to fuzz and/or invariant tests + pub test_options: TestOptions, +} + +impl MultiContractRunner { + /// Returns the number of matching tests + pub fn matching_test_function_count(&self, filter: &impl TestFilter) -> usize { + self.matching_test_functions(filter).count() + } + + /// Returns all test functions matching the filter + pub fn get_matching_test_functions<'a>( + &'a self, + filter: &'a impl TestFilter, + ) -> Vec<&Function> { + self.matching_test_functions(filter).collect() + } + + /// Returns all test functions matching the filter + pub fn matching_test_functions<'a>( + &'a self, + filter: &'a impl TestFilter, + ) -> impl Iterator { + self.contracts + .iter() + .filter(|(id, _)| { + filter.matches_path(id.source.to_string_lossy()) && + filter.matches_contract(&id.name) + }) + .flat_map(|(_, (abi, _, _))| { + abi.functions().filter(|func| filter.matches_test(func.signature())) + }) + } + + /// Get an iterator over all test contract functions that matches the filter path and contract + /// name + fn filtered_tests<'a>( + &'a self, + filter: &'a impl TestFilter, + ) -> impl Iterator { + self.contracts + .iter() + .filter(|(id, _)| { + filter.matches_path(id.source.to_string_lossy()) && + filter.matches_contract(&id.name) + }) + .flat_map(|(_, (abi, _, _))| abi.functions()) + } + + /// Get all test names matching the filter + pub fn get_tests(&self, filter: &impl TestFilter) -> Vec { + self.filtered_tests(filter) + .map(|func| func.name.clone()) + .filter(|name| name.is_test()) + .collect() + } + + /// Returns all matching tests grouped by contract grouped by file (file -> (contract -> tests)) + pub fn list( + &self, + filter: &impl TestFilter, + ) -> BTreeMap>> { + self.contracts + .iter() + .filter(|(id, _)| { + filter.matches_path(id.source.to_string_lossy()) && + filter.matches_contract(&id.name) + }) + .filter(|(_, (abi, _, _))| abi.functions().any(|func| filter.matches_test(&func.name))) + .map(|(id, (abi, _, _))| { + let source = id.source.as_path().display().to_string(); + let name = id.name.clone(); + let tests = abi + .functions() + .filter(|func| func.name.is_test()) + .filter(|func| filter.matches_test(func.signature())) + .map(|func| func.name.clone()) + .collect::>(); + + (source, name, tests) + }) + .fold(BTreeMap::new(), |mut acc, (source, name, tests)| { + acc.entry(source).or_default().insert(name, tests); + acc + }) + } + + /// Executes _all_ tests that match the given `filter` + /// + /// This will create the runtime based on the configured `evm` ops and create the `Backend` + /// before executing all contracts and their tests in _parallel_. + /// + /// Each Executor gets its own instance of the `Backend`. + pub async fn test( + &mut self, + filter: impl TestFilter, + stream_result: Option>, + test_options: TestOptions, + ) -> BTreeMap { + trace!("running all tests"); + + // the db backend that serves all the data, each contract gets its own instance + let db = Backend::spawn(self.fork.take()).await; + let filter = &filter; + + self.contracts + .par_iter() + .filter(|(id, _)| { + filter.matches_path(id.source.to_string_lossy()) && + filter.matches_contract(&id.name) + }) + .filter(|(_, (abi, _, _))| abi.functions().any(|func| filter.matches_test(&func.name))) + .map_with(stream_result, |stream_result, (id, (abi, deploy_code, libs))| { + let executor = ExecutorBuilder::new() + .inspectors(|stack| { + stack + .cheatcodes(self.cheats_config.clone()) + .trace(self.evm_opts.verbosity >= 3 || self.debug) + .debug(self.debug) + .coverage(self.coverage) + }) + .spec(self.evm_spec) + .gas_limit(self.evm_opts.gas_limit()) + .build(self.env.clone(), db.clone()); + let identifier = id.identifier(); + trace!(contract=%identifier, "start executing all tests in contract"); + + let result = self.run_tests( + &identifier, + abi, + executor, + deploy_code.clone(), + libs, + filter, + test_options.clone(), + ); + trace!(contract= ?identifier, "executed all tests in contract"); + + if let Some(stream_result) = stream_result { + let _ = stream_result.send((identifier.clone(), result.clone())); + } + + (identifier, result) + }) + .collect() + } + + #[instrument(skip_all, fields(name = %name))] + #[allow(clippy::too_many_arguments)] + fn run_tests( + &self, + name: &str, + contract: &Abi, + executor: Executor, + deploy_code: Bytes, + libs: &[Bytes], + filter: impl TestFilter, + test_options: TestOptions, + ) -> SuiteResult { + let libs = libs.iter().map(|l| l.0.clone().into()).collect::>(); + let runner = ContractRunner::new( + name, + executor, + contract, + deploy_code.0.into(), + self.evm_opts.initial_balance.to_ethers(), + self.sender.map(|a| a.to_ethers()), + self.errors.as_ref(), + &libs, + self.debug, + ); + runner.run_tests(filter, test_options, Some(&self.known_contracts)) + } +} + +/// Builder used for instantiating the multi-contract runner +#[derive(Debug, Default, Clone)] +pub struct MultiContractRunnerBuilder { + /// The address which will be used to deploy the initial contracts and send all + /// transactions + pub sender: Option
, + /// The initial balance for each one of the deployed smart contracts + pub initial_balance: U256, + /// The EVM spec to use + pub evm_spec: Option, + /// The fork to use at launch + pub fork: Option, + /// Additional cheatcode inspector related settings derived from the `Config` + pub cheats_config: Option, + /// Whether or not to collect coverage info + pub coverage: bool, + /// Whether or not to collect debug info + pub debug: bool, + /// Settings related to fuzz and/or invariant tests + pub test_options: Option, +} + +impl MultiContractRunnerBuilder { + /// Given an EVM, proceeds to return a runner which is able to execute all tests + /// against that evm + pub fn build( + self, + root: impl AsRef, + output: ProjectCompileOutput, + env: revm::primitives::Env, + evm_opts: EvmOpts, + ) -> Result + where + A: ArtifactOutput, + { + // This is just the contracts compiled, but we need to merge this with the read cached + // artifacts + let contracts = output + .with_stripped_file_prefixes(&root) + .into_artifacts() + .map(|(i, c)| (i, c.into_contract_bytecode())) + .collect::>(); + + let mut known_contracts = ContractsByArtifact::default(); + let source_paths = contracts + .iter() + .map(|(i, _)| (i.identifier(), root.as_ref().join(&i.source).to_string_lossy().into())) + .collect::>(); + // create a mapping of name => (abi, deployment code, Vec) + let mut deployable_contracts = DeployableContracts::default(); + + fn unique_deps(deps: Vec) -> Vec { + let mut filtered = Vec::new(); + let mut seen = HashSet::new(); + for dep in deps { + if !seen.insert(dep.id.clone()) { + continue + } + filtered.push(dep); + } + + filtered + } + + foundry_utils::link_with_nonce_or_address( + ArtifactContracts::from_iter(contracts), + &mut known_contracts, + Default::default(), + evm_opts.sender, + 1, + &mut deployable_contracts, + |post_link_input| { + let PostLinkInput { + contract, + known_contracts, + id, + extra: deployable_contracts, + dependencies, + } = post_link_input; + let dependencies = unique_deps(dependencies); + + let abi = contract.abi.expect("We should have an abi by now"); + + // get bytes if deployable, else add to known contracts and return. + // interfaces and abstract contracts should be known to enable fuzzing of their ABI + // but they should not be deployable and their source code should be skipped by the + // debugger and linker. + let Some(bytecode) = contract.bytecode.and_then(|b| b.object.into_bytes()) else { + known_contracts.insert(id.clone(), (abi.clone(), vec![])); + return Ok(()) + }; + + // if it's a test, add it to deployable contracts + if abi.constructor.as_ref().map(|c| c.inputs.is_empty()).unwrap_or(true) && + abi.functions() + .any(|func| func.name.is_test() || func.name.is_invariant_test()) + { + deployable_contracts.insert( + id.clone(), + ( + abi.clone(), + bytecode.0.into(), + dependencies + .into_iter() + .map(|dep| dep.bytecode.0.into()) + .collect::>(), + ), + ); + } + + contract + .deployed_bytecode + .and_then(|d_bcode| d_bcode.bytecode) + .and_then(|bcode| bcode.object.into_bytes()) + .and_then(|bytes| known_contracts.insert(id.clone(), (abi, bytes.to_vec()))); + Ok(()) + }, + root, + )?; + + let execution_info = known_contracts.flatten(); + Ok(MultiContractRunner { + contracts: deployable_contracts, + known_contracts, + evm_opts, + env, + evm_spec: self.evm_spec.unwrap_or(SpecId::MERGE), + sender: self.sender, + errors: Some(execution_info.2), + source_paths, + fork: self.fork, + cheats_config: self.cheats_config.unwrap_or_default().into(), + coverage: self.coverage, + debug: self.debug, + test_options: self.test_options.unwrap_or_default(), + }) + } + + #[must_use] + pub fn sender(mut self, sender: Address) -> Self { + self.sender = Some(sender); + self + } + + #[must_use] + pub fn initial_balance(mut self, initial_balance: U256) -> Self { + self.initial_balance = initial_balance; + self + } + + #[must_use] + pub fn evm_spec(mut self, spec: SpecId) -> Self { + self.evm_spec = Some(spec); + self + } + + #[must_use] + pub fn with_fork(mut self, fork: Option) -> Self { + self.fork = fork; + self + } + + #[must_use] + pub fn with_cheats_config(mut self, cheats_config: CheatsConfig) -> Self { + self.cheats_config = Some(cheats_config); + self + } + + #[must_use] + pub fn with_test_options(mut self, test_options: TestOptions) -> Self { + self.test_options = Some(test_options); + self + } + + #[must_use] + pub fn set_coverage(mut self, enable: bool) -> Self { + self.coverage = enable; + self + } + + #[must_use] + pub fn set_debug(mut self, enable: bool) -> Self { + self.debug = enable; + self + } +} diff --git a/crates/zkforge/src/result.rs b/crates/zkforge/src/result.rs new file mode 100644 index 000000000..cb26320bb --- /dev/null +++ b/crates/zkforge/src/result.rs @@ -0,0 +1,281 @@ +//! test outcomes + +use crate::Address; +use ethers::prelude::Log; +use foundry_common::evm::Breakpoints; +use foundry_evm::{ + coverage::HitMaps, + debug::DebugArena, + executor::EvmError, + fuzz::{types::FuzzCase, CounterExample}, + trace::{TraceKind, Traces}, +}; +use foundry_utils::types::ToEthers; +use serde::{Deserialize, Serialize}; +use std::{collections::BTreeMap, fmt, time::Duration}; + +/// Results and duration for a set of tests included in the same test contract +#[derive(Debug, Clone, Serialize)] +pub struct SuiteResult { + /// Total duration of the test run for this block of tests + pub duration: Duration, + /// Individual test results. `test fn signature -> TestResult` + pub test_results: BTreeMap, + /// Warnings + pub warnings: Vec, +} + +impl SuiteResult { + pub fn new( + duration: Duration, + test_results: BTreeMap, + warnings: Vec, + ) -> Self { + Self { duration, test_results, warnings } + } + + /// Iterator over all succeeding tests and their names + pub fn successes(&self) -> impl Iterator { + self.tests().filter(|(_, t)| t.status == TestStatus::Success) + } + + /// Iterator over all failing tests and their names + pub fn failures(&self) -> impl Iterator { + self.tests().filter(|(_, t)| t.status == TestStatus::Failure) + } + + /// Iterator over all tests and their names + pub fn tests(&self) -> impl Iterator { + self.test_results.iter() + } + + /// Whether this test suite is empty. + pub fn is_empty(&self) -> bool { + self.test_results.is_empty() + } + + /// The number of tests in this test suite. + pub fn len(&self) -> usize { + self.test_results.len() + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum TestStatus { + Success, + #[default] + Failure, + Skipped, +} + +impl TestStatus { + /// Returns `true` if the test was successful. + #[inline] + pub fn is_success(self) -> bool { + matches!(self, Self::Success) + } + + /// Returns `true` if the test failed. + #[inline] + pub fn is_failure(self) -> bool { + matches!(self, Self::Failure) + } + + /// Returns `true` if the test was skipped. + #[inline] + pub fn is_skipped(self) -> bool { + matches!(self, Self::Skipped) + } +} + +/// The result of an executed solidity test +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct TestResult { + /// The test status, indicating whether the test case succeeded, failed, or was marked as + /// skipped. This means that the transaction executed properly, the test was marked as + /// skipped with vm.skip(), or that there was a revert and that the test was expected to + /// fail (prefixed with `testFail`) + pub status: TestStatus, + + /// If there was a revert, this field will be populated. Note that the test can + /// still be successful (i.e self.success == true) when it's expected to fail. + pub reason: Option, + + /// Minimal reproduction test case for failing test + pub counterexample: Option, + + /// Any captured & parsed as strings logs along the test's execution which should + /// be printed to the user. + pub logs: Vec, + + /// The decoded DSTest logging events and Hardhat's `console.log` from [logs](Self::logs). + pub decoded_logs: Vec, + + /// What kind of test this was + pub kind: TestKind, + + /// Traces + pub traces: Traces, + + /// Raw coverage info + #[serde(skip)] + pub coverage: Option, + + /// Labeled addresses + pub labeled_addresses: BTreeMap, + + /// The debug nodes of the call + pub debug: Option, + + /// pc breakpoint char map + pub breakpoints: Breakpoints, +} + +impl TestResult { + pub fn fail(reason: String) -> Self { + Self { status: TestStatus::Failure, reason: Some(reason), ..Default::default() } + } + + /// Returns `true` if this is the result of a fuzz test + pub fn is_fuzz(&self) -> bool { + matches!(self.kind, TestKind::Fuzz { .. }) + } +} + +/// Data report by a test. +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum TestKindReport { + Standard { gas: u64 }, + Fuzz { runs: usize, mean_gas: u64, median_gas: u64 }, + Invariant { runs: usize, calls: usize, reverts: usize }, +} + +impl fmt::Display for TestKindReport { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TestKindReport::Standard { gas } => { + write!(f, "(gas: {gas})") + } + TestKindReport::Fuzz { runs, mean_gas, median_gas } => { + write!(f, "(runs: {runs}, μ: {mean_gas}, ~: {median_gas})") + } + TestKindReport::Invariant { runs, calls, reverts } => { + write!(f, "(runs: {runs}, calls: {calls}, reverts: {reverts})") + } + } + } +} + +impl TestKindReport { + /// Returns the main gas value to compare against + pub fn gas(&self) -> u64 { + match self { + TestKindReport::Standard { gas } => *gas, + // We use the median for comparisons + TestKindReport::Fuzz { median_gas, .. } => *median_gas, + // We return 0 since it's not applicable + TestKindReport::Invariant { .. } => 0, + } + } +} + +/// Various types of tests +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum TestKind { + /// A standard test that consists of calling the defined solidity function + /// + /// Holds the consumed gas + Standard(u64), + /// A solidity fuzz test, that stores all test cases + Fuzz { + /// we keep this for the debugger + first_case: FuzzCase, + runs: usize, + mean_gas: u64, + median_gas: u64, + }, + /// A solidity invariant test, that stores all test cases + Invariant { runs: usize, calls: usize, reverts: usize }, +} + +impl Default for TestKind { + fn default() -> Self { + Self::Standard(0) + } +} + +impl TestKind { + /// The gas consumed by this test + pub fn report(&self) -> TestKindReport { + match self { + TestKind::Standard(gas) => TestKindReport::Standard { gas: *gas }, + TestKind::Fuzz { runs, mean_gas, median_gas, .. } => { + TestKindReport::Fuzz { runs: *runs, mean_gas: *mean_gas, median_gas: *median_gas } + } + TestKind::Invariant { runs, calls, reverts } => { + TestKindReport::Invariant { runs: *runs, calls: *calls, reverts: *reverts } + } + } + } +} + +#[derive(Clone, Debug, Default)] +pub struct TestSetup { + /// The address at which the test contract was deployed + pub address: Address, + /// The logs emitted during setup + pub logs: Vec, + /// Call traces of the setup + pub traces: Traces, + /// Addresses labeled during setup + pub labeled_addresses: BTreeMap, + /// The reason the setup failed, if it did + pub reason: Option, +} + +impl TestSetup { + pub fn from_evm_error_with( + error: EvmError, + mut logs: Vec, + mut traces: Traces, + mut labeled_addresses: BTreeMap, + ) -> Self { + match error { + EvmError::Execution(err) => { + // force the tracekind to be setup so a trace is shown. + traces.extend(err.traces.map(|traces| (TraceKind::Setup, traces))); + logs.extend(err.logs); + labeled_addresses.extend(err.labels.into_iter().map(|l| (l.0.to_ethers(), l.1))); + Self::failed_with(logs, traces, labeled_addresses, err.reason) + } + e => Self::failed_with( + logs, + traces, + labeled_addresses, + format!("Failed to deploy contract: {e}"), + ), + } + } + + pub fn success( + address: Address, + logs: Vec, + traces: Traces, + labeled_addresses: BTreeMap, + ) -> Self { + Self { address, logs, traces, labeled_addresses, reason: None } + } + + pub fn failed_with( + logs: Vec, + traces: Traces, + labeled_addresses: BTreeMap, + reason: String, + ) -> Self { + Self { address: Address::zero(), logs, traces, labeled_addresses, reason: Some(reason) } + } + + pub fn failed(reason: String) -> Self { + Self { reason: Some(reason), ..Default::default() } + } +} diff --git a/crates/zkforge/src/runner.rs b/crates/zkforge/src/runner.rs new file mode 100644 index 000000000..6b91fb07f --- /dev/null +++ b/crates/zkforge/src/runner.rs @@ -0,0 +1,674 @@ +use crate::{ + result::{SuiteResult, TestKind, TestResult, TestSetup, TestStatus}, + TestFilter, TestOptions, +}; +use alloy_primitives::U256 as rU256; +use ethers::{ + abi::{Abi, Function}, + types::{Address, Bytes, U256}, +}; +use eyre::Result; +use foundry_common::{ + contracts::{ContractsByAddress, ContractsByArtifact}, + TestFunctionExt, +}; +use foundry_config::{FuzzConfig, InvariantConfig}; +use foundry_evm::{ + decode::decode_console_logs, + executor::{CallResult, EvmError, ExecutionErr, Executor}, + fuzz::{ + invariant::{ + replay_run, InvariantContract, InvariantExecutor, InvariantFuzzError, + InvariantFuzzTestResult, + }, + types::{CaseOutcome, CounterExampleOutcome, FuzzOutcome}, + CounterExample, FuzzedExecutor, + }, + trace::{load_contracts, TraceKind}, + CALLER, +}; +use foundry_utils::types::{ToAlloy, ToEthers}; +use proptest::test_runner::{TestError, TestRunner}; +use rayon::prelude::*; +use std::{ + collections::{BTreeMap, HashMap}, + time::Instant, +}; + +/// A type that executes all tests of a contract +#[derive(Debug, Clone)] +pub struct ContractRunner<'a> { + pub name: &'a str, + /// The executor used by the runner. + pub executor: Executor, + /// Library contracts to be deployed before the test contract + pub predeploy_libs: &'a [Bytes], + /// The deployed contract's code + pub code: Bytes, + /// The test contract's ABI + pub contract: &'a Abi, + /// All known errors, used to decode reverts + pub errors: Option<&'a Abi>, + /// The initial balance of the test contract + pub initial_balance: U256, + /// The address which will be used as the `from` field in all EVM calls + pub sender: Address, + /// Should generate debug traces + pub debug: bool, +} + +impl<'a> ContractRunner<'a> { + #[allow(clippy::too_many_arguments)] + pub fn new( + name: &'a str, + executor: Executor, + contract: &'a Abi, + code: Bytes, + initial_balance: U256, + sender: Option
, + errors: Option<&'a Abi>, + predeploy_libs: &'a [Bytes], + debug: bool, + ) -> Self { + Self { + name, + executor, + contract, + code, + initial_balance, + sender: sender.unwrap_or_default(), + errors, + predeploy_libs, + debug, + } + } +} + +impl<'a> ContractRunner<'a> { + /// Deploys the test contract inside the runner from the sending account, and optionally runs + /// the `setUp` function on the test contract. + pub fn setup(&mut self, setup: bool) -> TestSetup { + match self._setup(setup) { + Ok(setup) => setup, + Err(err) => TestSetup::failed(err.to_string()), + } + } + + fn _setup(&mut self, setup: bool) -> Result { + trace!(?setup, "Setting test contract"); + + // We max out their balance so that they can deploy and make calls. + self.executor.set_balance(self.sender.to_alloy(), rU256::MAX)?; + self.executor.set_balance(CALLER, rU256::MAX)?; + + // We set the nonce of the deployer accounts to 1 to get the same addresses as DappTools + self.executor.set_nonce(self.sender.to_alloy(), 1)?; + + // Deploy libraries + let mut logs = Vec::new(); + let mut traces = Vec::with_capacity(self.predeploy_libs.len()); + for code in self.predeploy_libs.iter() { + match self.executor.deploy( + self.sender.to_alloy(), + code.0.clone().into(), + rU256::ZERO, + self.errors, + ) { + Ok(d) => { + logs.extend(d.logs); + traces.extend(d.traces.map(|traces| (TraceKind::Deployment, traces))); + } + Err(e) => { + return Ok(TestSetup::from_evm_error_with(e, logs, traces, Default::default())) + } + } + } + + // Deploy the test contract + let address = match self.executor.deploy( + self.sender.to_alloy(), + self.code.0.clone().into(), + rU256::ZERO, + self.errors, + ) { + Ok(d) => { + logs.extend(d.logs); + traces.extend(d.traces.map(|traces| (TraceKind::Deployment, traces))); + d.address + } + Err(e) => { + return Ok(TestSetup::from_evm_error_with(e, logs, traces, Default::default())) + } + }; + + // Now we set the contracts initial balance, and we also reset `self.sender`s and `CALLER`s + // balance to the initial balance we want + self.executor.set_balance(address, self.initial_balance.to_alloy())?; + self.executor.set_balance(self.sender.to_alloy(), self.initial_balance.to_alloy())?; + self.executor.set_balance(CALLER, self.initial_balance.to_alloy())?; + + self.executor.deploy_create2_deployer()?; + + // Optionally call the `setUp` function + let setup = if setup { + trace!("setting up"); + let (setup_logs, setup_traces, labeled_addresses, reason) = + match self.executor.setup(None, address) { + Ok(CallResult { traces, labels, logs, .. }) => { + trace!(contract = ?address, "successfully setUp test"); + (logs, traces, labels, None) + } + Err(EvmError::Execution(err)) => { + let ExecutionErr { traces, labels, logs, reason, .. } = *err; + error!(reason = ?reason, contract = ?address, "setUp failed"); + (logs, traces, labels, Some(format!("Setup failed: {reason}"))) + } + Err(err) => { + error!(reason=?err, contract= ?address, "setUp failed"); + (Vec::new(), None, BTreeMap::new(), Some(format!("Setup failed: {err}"))) + } + }; + traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces))); + logs.extend(setup_logs); + + TestSetup { + address: address.to_ethers(), + logs, + traces, + labeled_addresses: labeled_addresses + .into_iter() + .map(|l| (l.0.to_ethers(), l.1)) + .collect(), + reason, + } + } else { + TestSetup::success(address.to_ethers(), logs, traces, Default::default()) + }; + + Ok(setup) + } + + /// Runs all tests for a contract whose names match the provided regular expression + pub fn run_tests( + mut self, + filter: impl TestFilter, + test_options: TestOptions, + known_contracts: Option<&ContractsByArtifact>, + ) -> SuiteResult { + info!("starting tests"); + let start = Instant::now(); + let mut warnings = Vec::new(); + + let setup_fns: Vec<_> = + self.contract.functions().filter(|func| func.name.is_setup()).collect(); + + let needs_setup = setup_fns.len() == 1 && setup_fns[0].name == "setUp"; + + // There is a single miss-cased `setUp` function, so we add a warning + for setup_fn in setup_fns.iter() { + if setup_fn.name != "setUp" { + warnings.push(format!( + "Found invalid setup function \"{}\" did you mean \"setUp()\"?", + setup_fn.signature() + )); + } + } + + // There are multiple setUp function, so we return a single test result for `setUp` + if setup_fns.len() > 1 { + return SuiteResult::new( + start.elapsed(), + [("setUp()".to_string(), TestResult::fail("Multiple setUp functions".to_string()))] + .into(), + warnings, + ) + } + + let has_invariants = self.contract.functions().any(|func| func.is_invariant_test()); + + // Invariant testing requires tracing to figure out what contracts were created. + let tmp_tracing = self.executor.inspector.tracer.is_none() && has_invariants && needs_setup; + if tmp_tracing { + self.executor.set_tracing(true); + } + let setup = self.setup(needs_setup); + if tmp_tracing { + self.executor.set_tracing(false); + } + + if setup.reason.is_some() { + // The setup failed, so we return a single test result for `setUp` + return SuiteResult::new( + start.elapsed(), + [( + "setUp()".to_string(), + TestResult { + status: TestStatus::Failure, + reason: setup.reason, + counterexample: None, + decoded_logs: decode_console_logs(&setup.logs), + logs: setup.logs, + kind: TestKind::Standard(0), + traces: setup.traces, + coverage: None, + labeled_addresses: setup.labeled_addresses, + ..Default::default() + }, + )] + .into(), + warnings, + ) + } + + let functions: Vec<_> = self.contract.functions().collect(); + let mut test_results = functions + .par_iter() + .filter(|&&func| func.is_test() && filter.matches_test(func.signature())) + .map(|&func| { + let should_fail = func.is_test_fail(); + let res = if func.is_fuzz_test() { + let runner = test_options.fuzz_runner(self.name, &func.name); + let fuzz_config = test_options.fuzz_config(self.name, &func.name); + self.run_fuzz_test(func, should_fail, runner, setup.clone(), *fuzz_config) + } else { + self.run_test(func, should_fail, setup.clone()) + }; + (func.signature(), res) + }) + .collect::>(); + + if has_invariants { + let identified_contracts = load_contracts(setup.traces.clone(), known_contracts); + let results: Vec<_> = functions + .par_iter() + .filter(|&&func| func.is_invariant_test() && filter.matches_test(func.signature())) + .map(|&func| { + let runner = test_options.invariant_runner(self.name, &func.name); + let invariant_config = test_options.invariant_config(self.name, &func.name); + let res = self.run_invariant_test( + runner, + setup.clone(), + *invariant_config, + func, + known_contracts, + &identified_contracts, + ); + (func.signature(), res) + }) + .collect(); + test_results.extend(results); + } + + let duration = start.elapsed(); + if !test_results.is_empty() { + let successful = + test_results.iter().filter(|(_, tst)| tst.status == TestStatus::Success).count(); + info!( + duration = ?duration, + "done. {}/{} successful", + successful, + test_results.len() + ); + } + + SuiteResult::new(duration, test_results, warnings) + } + + /// Runs a single test + /// + /// Calls the given functions and returns the `TestResult`. + /// + /// State modifications are not committed to the evm database but discarded after the call, + /// similar to `eth_call`. + #[instrument(name = "test", skip_all, fields(name = %func.signature(), %should_fail))] + pub fn run_test(&self, func: &Function, should_fail: bool, setup: TestSetup) -> TestResult { + let TestSetup { address, mut logs, mut traces, mut labeled_addresses, .. } = setup; + + // Run unit test + let mut executor = self.executor.clone(); + let start = Instant::now(); + let debug_arena; + let (reverted, reason, gas, stipend, coverage, state_changeset, breakpoints) = + match executor.execute_test::<(), _, _>( + self.sender.to_alloy(), + address.to_alloy(), + func.clone(), + (), + rU256::ZERO, + self.errors, + ) { + Ok(CallResult { + reverted, + gas_used: gas, + stipend, + logs: execution_logs, + traces: execution_trace, + coverage, + labels: new_labels, + state_changeset, + debug, + breakpoints, + .. + }) => { + traces.extend(execution_trace.map(|traces| (TraceKind::Execution, traces))); + labeled_addresses + .extend(new_labels.into_iter().map(|l| (l.0.to_ethers(), l.1))); + logs.extend(execution_logs); + debug_arena = debug; + (reverted, None, gas, stipend, coverage, state_changeset, breakpoints) + } + Err(EvmError::Execution(err)) => { + traces.extend(err.traces.map(|traces| (TraceKind::Execution, traces))); + labeled_addresses + .extend(err.labels.into_iter().map(|l| (l.0.to_ethers(), l.1))); + logs.extend(err.logs); + debug_arena = err.debug; + ( + err.reverted, + Some(err.reason), + err.gas_used, + err.stipend, + None, + err.state_changeset, + HashMap::new(), + ) + } + Err(EvmError::SkipError) => { + return TestResult { + status: TestStatus::Skipped, + reason: None, + decoded_logs: decode_console_logs(&logs), + traces, + labeled_addresses, + kind: TestKind::Standard(0), + ..Default::default() + } + } + Err(err) => { + return TestResult { + status: TestStatus::Failure, + reason: Some(err.to_string()), + decoded_logs: decode_console_logs(&logs), + traces, + labeled_addresses, + kind: TestKind::Standard(0), + ..Default::default() + } + } + }; + + let success = executor.is_success( + setup.address.to_alloy(), + reverted, + state_changeset.expect("we should have a state changeset"), + should_fail, + ); + + // Record test execution time + debug!( + duration = ?start.elapsed(), + %success, + %gas + ); + + TestResult { + status: match success { + true => TestStatus::Success, + false => TestStatus::Failure, + }, + reason, + counterexample: None, + decoded_logs: decode_console_logs(&logs), + logs, + kind: TestKind::Standard(gas.overflowing_sub(stipend).0), + traces, + coverage, + labeled_addresses, + debug: debug_arena, + breakpoints, + } + } + + #[instrument(name = "invariant-test", skip_all)] + pub fn run_invariant_test( + &self, + runner: TestRunner, + setup: TestSetup, + invariant_config: InvariantConfig, + func: &Function, + known_contracts: Option<&ContractsByArtifact>, + identified_contracts: &ContractsByAddress, + ) -> TestResult { + trace!(target: "forge::test::fuzz", "executing invariant test for {:?}", func.name); + let empty = ContractsByArtifact::default(); + let project_contracts = known_contracts.unwrap_or(&empty); + let TestSetup { address, logs, traces, labeled_addresses, .. } = setup; + + // First, run the test normally to see if it needs to be skipped. + if let Err(EvmError::SkipError) = self.executor.clone().execute_test::<(), _, _>( + self.sender.to_alloy(), + address.to_alloy(), + func.clone(), + (), + rU256::ZERO, + self.errors, + ) { + return TestResult { + status: TestStatus::Skipped, + reason: None, + decoded_logs: decode_console_logs(&logs), + traces, + labeled_addresses, + kind: TestKind::Invariant { runs: 1, calls: 1, reverts: 1 }, + ..Default::default() + } + }; + + let mut evm = InvariantExecutor::new( + self.executor.clone(), + runner, + invariant_config, + identified_contracts, + project_contracts, + ); + + let invariant_contract = + InvariantContract { address, invariant_function: func, abi: self.contract }; + + let InvariantFuzzTestResult { error, cases, reverts, last_run_inputs } = match evm + .invariant_fuzz(invariant_contract.clone()) + { + Ok(x) => x, + Err(e) => { + return TestResult { + status: TestStatus::Failure, + reason: Some(format!("Failed to set up invariant testing environment: {e}")), + decoded_logs: decode_console_logs(&logs), + traces, + labeled_addresses, + kind: TestKind::Invariant { runs: 0, calls: 0, reverts: 0 }, + ..Default::default() + } + } + }; + + let mut counterexample = None; + let mut logs = logs.clone(); + let mut traces = traces.clone(); + let success = error.is_none(); + let reason = error + .as_ref() + .and_then(|err| (!err.revert_reason.is_empty()).then(|| err.revert_reason.clone())); + match error { + // If invariants were broken, replay the error to collect logs and traces + Some(error @ InvariantFuzzError { test_error: TestError::Fail(_, _), .. }) => { + match error.replay( + self.executor.clone(), + known_contracts, + identified_contracts.clone(), + &mut logs, + &mut traces, + ) { + Ok(c) => counterexample = c, + Err(err) => { + error!(?err, "Failed to replay invariant error") + } + }; + + logs.extend(error.logs); + + if let Some(error_traces) = error.traces { + traces.push((TraceKind::Execution, error_traces)); + } + } + + // If invariants ran successfully, replay the last run to collect logs and + // traces. + _ => { + replay_run( + &invariant_contract, + self.executor.clone(), + known_contracts, + identified_contracts.clone(), + &mut logs, + &mut traces, + func.clone(), + last_run_inputs.clone(), + ); + } + } + + let kind = TestKind::Invariant { + runs: cases.len(), + calls: cases.iter().map(|sequence| sequence.cases().len()).sum(), + reverts, + }; + + TestResult { + status: match success { + true => TestStatus::Success, + false => TestStatus::Failure, + }, + reason, + counterexample, + decoded_logs: decode_console_logs(&logs), + logs, + kind, + coverage: None, // TODO ? + traces, + labeled_addresses: labeled_addresses.clone(), + ..Default::default() // TODO collect debug traces on the last run or error + } + } + + #[instrument(name = "fuzz-test", skip_all, fields(name = %func.signature(), %should_fail))] + pub fn run_fuzz_test( + &self, + func: &Function, + should_fail: bool, + runner: TestRunner, + setup: TestSetup, + fuzz_config: FuzzConfig, + ) -> TestResult { + let TestSetup { address, mut logs, mut traces, mut labeled_addresses, .. } = setup; + + // Run fuzz test + let start = Instant::now(); + let fuzzed_executor = + FuzzedExecutor::new(&self.executor, runner.clone(), self.sender, fuzz_config); + let state = fuzzed_executor.build_fuzz_state(); + let mut result = fuzzed_executor.fuzz(func, address, should_fail, self.errors); + + let mut debug = Default::default(); + let mut breakpoints = Default::default(); + + // Check the last test result and skip the test + // if it's marked as so. + if let Some("SKIPPED") = result.reason.as_deref() { + return TestResult { + status: TestStatus::Skipped, + reason: None, + decoded_logs: decode_console_logs(&logs), + traces, + labeled_addresses, + kind: TestKind::Standard(0), + debug, + breakpoints, + ..Default::default() + } + } + + // if should debug + if self.debug { + let mut debug_executor = self.executor.clone(); + // turn the debug traces on + debug_executor.inspector.enable_debugger(true); + debug_executor.inspector.tracing(true); + let calldata = if let Some(counterexample) = result.counterexample.as_ref() { + match counterexample { + CounterExample::Single(ce) => ce.calldata.clone(), + _ => unimplemented!(), + } + } else { + result.first_case.calldata.clone() + }; + // rerun the last relevant test with traces + let debug_result = FuzzedExecutor::new( + &debug_executor, + runner, + self.sender, + fuzz_config, + ) + .single_fuzz(&state, address, should_fail, calldata); + + (debug, breakpoints) = match debug_result { + Ok(fuzz_outcome) => match fuzz_outcome { + FuzzOutcome::Case(CaseOutcome { debug, breakpoints, .. }) => { + (debug, breakpoints) + } + FuzzOutcome::CounterExample(CounterExampleOutcome { + debug, + breakpoints, + .. + }) => (debug, breakpoints), + }, + Err(_) => (Default::default(), Default::default()), + }; + } + + let kind = TestKind::Fuzz { + median_gas: result.median_gas(false), + mean_gas: result.mean_gas(false), + first_case: result.first_case, + runs: result.gas_by_case.len(), + }; + + // Record logs, labels and traces + logs.append(&mut result.logs); + labeled_addresses.append(&mut result.labeled_addresses); + traces.extend(result.traces.map(|traces| (TraceKind::Execution, traces))); + + // Record test execution time + debug!( + duration = ?start.elapsed(), + success = %result.success + ); + + TestResult { + status: match result.success { + true => TestStatus::Success, + false => TestStatus::Failure, + }, + reason: result.reason, + counterexample: result.counterexample, + decoded_logs: decode_console_logs(&logs), + logs, + kind, + traces, + coverage: result.coverage, + labeled_addresses, + debug, + breakpoints, + } + } +} diff --git a/crates/zkforge/tests/cli/cache.rs b/crates/zkforge/tests/cli/cache.rs new file mode 100644 index 000000000..22fc6ea23 --- /dev/null +++ b/crates/zkforge/tests/cli/cache.rs @@ -0,0 +1,20 @@ +//! Tests for various cache command +use foundry_test_utils::{ + forgetest, + util::{TestCommand, TestProject}, +}; + +forgetest!(can_list_cache, |_prj: TestProject, mut cmd: TestCommand| { + cmd.args(["cache", "ls"]); + cmd.assert_success(); +}); + +forgetest!(can_list_cache_all, |_prj: TestProject, mut cmd: TestCommand| { + cmd.args(["cache", "ls", "all"]); + cmd.assert_success(); +}); + +forgetest!(can_list_specific_chain, |_prj: TestProject, mut cmd: TestCommand| { + cmd.args(["cache", "ls", "mainnet"]); + cmd.assert_success(); +}); diff --git a/crates/zkforge/tests/cli/cmd.rs b/crates/zkforge/tests/cli/cmd.rs new file mode 100644 index 000000000..71d6805c1 --- /dev/null +++ b/crates/zkforge/tests/cli/cmd.rs @@ -0,0 +1,1634 @@ +//! Contains various tests for checking forge's commands + +use crate::constants::*; +use ethers::{ + prelude::remappings::Remapping, + solc::{ + artifacts::{BytecodeHash, Metadata}, + ConfigurableContractArtifact, + }, +}; +use foundry_config::{parse_with_profile, BasicConfig, Chain, Config, SolidityErrorCode}; +use foundry_test_utils::{ + ethers_solc::PathStyle, + forgetest, forgetest_init, + util::{pretty_err, read_string, OutputExt, TestCommand, TestProject}, +}; +use semver::Version; +use std::{ + env, fs, + path::PathBuf, + process::{Command, Stdio}, + str::FromStr, +}; + +// tests `--help` is printed to std out +forgetest!(print_help, |_: TestProject, mut cmd: TestCommand| { + cmd.arg("--help"); + cmd.assert_non_empty_stdout(); +}); + +// checks that `clean` can be invoked even if out and cache don't exist +forgetest!(can_clean_non_existing, |prj: TestProject, mut cmd: TestCommand| { + cmd.arg("clean"); + cmd.assert_empty_stdout(); + prj.assert_cleaned(); +}); + +// checks that `cache ls` can be invoked and displays the foundry cache +forgetest!( + #[ignore] + can_cache_ls, + |_: TestProject, mut cmd: TestCommand| { + let chain = Chain::Named(ethers::prelude::Chain::Mainnet); + let block1 = 100; + let block2 = 101; + + let block1_cache_dir = Config::foundry_block_cache_dir(chain, block1).unwrap(); + let block1_file = Config::foundry_block_cache_file(chain, block1).unwrap(); + let block2_cache_dir = Config::foundry_block_cache_dir(chain, block2).unwrap(); + let block2_file = Config::foundry_block_cache_file(chain, block2).unwrap(); + let etherscan_cache_dir = Config::foundry_etherscan_chain_cache_dir(chain).unwrap(); + fs::create_dir_all(block1_cache_dir).unwrap(); + fs::write(block1_file, "{}").unwrap(); + fs::create_dir_all(block2_cache_dir).unwrap(); + fs::write(block2_file, "{}").unwrap(); + fs::create_dir_all(etherscan_cache_dir).unwrap(); + + cmd.args(["cache", "ls"]); + let output_string = String::from_utf8_lossy(&cmd.output().stdout).to_string(); + let output_lines = output_string.split('\n').collect::>(); + println!("{output_string}"); + + assert_eq!(output_lines.len(), 6); + assert!(output_lines[0].starts_with("-️ mainnet (")); + assert!(output_lines[1].starts_with("\t-️ Block Explorer (")); + assert_eq!(output_lines[2], ""); + assert!(output_lines[3].starts_with("\t-️ Block 100 (")); + assert!(output_lines[4].starts_with("\t-️ Block 101 (")); + assert_eq!(output_lines[5], ""); + + Config::clean_foundry_cache().unwrap(); + } +); + +// checks that `cache clean` can be invoked and cleans the foundry cache +// this test is not isolated and modifies ~ so it is ignored +forgetest!( + #[ignore] + can_cache_clean, + |_: TestProject, mut cmd: TestCommand| { + let cache_dir = Config::foundry_cache_dir().unwrap(); + let path = cache_dir.as_path(); + fs::create_dir_all(path).unwrap(); + cmd.args(["cache", "clean"]); + cmd.assert_empty_stdout(); + + assert!(!path.exists()); + } +); + +// checks that `cache clean --etherscan` can be invoked and only cleans the foundry etherscan cache +// this test is not isolated and modifies ~ so it is ignored +forgetest!( + #[ignore] + can_cache_clean_etherscan, + |_: TestProject, mut cmd: TestCommand| { + let cache_dir = Config::foundry_cache_dir().unwrap(); + let etherscan_cache_dir = Config::foundry_etherscan_cache_dir().unwrap(); + let path = cache_dir.as_path(); + let etherscan_path = etherscan_cache_dir.as_path(); + fs::create_dir_all(etherscan_path).unwrap(); + cmd.args(["cache", "clean", "--etherscan"]); + cmd.assert_empty_stdout(); + + assert!(path.exists()); + assert!(!etherscan_path.exists()); + + Config::clean_foundry_cache().unwrap(); + } +); + +// checks that `cache clean all --etherscan` can be invoked and only cleans the foundry etherscan +// cache. This test is not isolated and modifies ~ so it is ignored +forgetest!( + #[ignore] + can_cache_clean_all_etherscan, + |_: TestProject, mut cmd: TestCommand| { + let rpc_cache_dir = Config::foundry_rpc_cache_dir().unwrap(); + let etherscan_cache_dir = Config::foundry_etherscan_cache_dir().unwrap(); + let rpc_path = rpc_cache_dir.as_path(); + let etherscan_path = etherscan_cache_dir.as_path(); + fs::create_dir_all(rpc_path).unwrap(); + fs::create_dir_all(etherscan_path).unwrap(); + cmd.args(["cache", "clean", "all", "--etherscan"]); + cmd.assert_empty_stdout(); + + assert!(rpc_path.exists()); + assert!(!etherscan_path.exists()); + + Config::clean_foundry_cache().unwrap(); + } +); + +// checks that `cache clean ` can be invoked and cleans the chain cache +// this test is not isolated and modifies ~ so it is ignored +forgetest!( + #[ignore] + can_cache_clean_chain, + |_: TestProject, mut cmd: TestCommand| { + let chain = Chain::Named(ethers::prelude::Chain::Mainnet); + let cache_dir = Config::foundry_chain_cache_dir(chain).unwrap(); + let etherscan_cache_dir = Config::foundry_etherscan_chain_cache_dir(chain).unwrap(); + let path = cache_dir.as_path(); + let etherscan_path = etherscan_cache_dir.as_path(); + fs::create_dir_all(path).unwrap(); + fs::create_dir_all(etherscan_path).unwrap(); + cmd.args(["cache", "clean", "mainnet"]); + cmd.assert_empty_stdout(); + + assert!(!path.exists()); + assert!(!etherscan_path.exists()); + + Config::clean_foundry_cache().unwrap(); + } +); + +// checks that `cache clean --blocks 100,101` can be invoked and cleans the chain block +// caches this test is not isolated and modifies ~ so it is ignored +forgetest!( + #[ignore] + can_cache_clean_blocks, + |_: TestProject, mut cmd: TestCommand| { + let chain = Chain::Named(ethers::prelude::Chain::Mainnet); + let block1 = 100; + let block2 = 101; + let block3 = 102; + let block1_cache_dir = Config::foundry_block_cache_dir(chain, block1).unwrap(); + let block2_cache_dir = Config::foundry_block_cache_dir(chain, block2).unwrap(); + let block3_cache_dir = Config::foundry_block_cache_dir(chain, block3).unwrap(); + let etherscan_cache_dir = Config::foundry_etherscan_chain_cache_dir(chain).unwrap(); + let block1_path = block1_cache_dir.as_path(); + let block2_path = block2_cache_dir.as_path(); + let block3_path = block3_cache_dir.as_path(); + let etherscan_path = etherscan_cache_dir.as_path(); + fs::create_dir_all(block1_path).unwrap(); + fs::create_dir_all(block2_path).unwrap(); + fs::create_dir_all(block3_path).unwrap(); + fs::create_dir_all(etherscan_path).unwrap(); + cmd.args(["cache", "clean", "mainnet", "--blocks", "100,101"]); + cmd.assert_empty_stdout(); + + assert!(!block1_path.exists()); + assert!(!block2_path.exists()); + assert!(block3_path.exists()); + assert!(etherscan_path.exists()); + + Config::clean_foundry_cache().unwrap(); + } +); + +// checks that `cache clean --etherscan` can be invoked and cleans the etherscan chain cache +// this test is not isolated and modifies ~ so it is ignored +forgetest!( + #[ignore] + can_cache_clean_chain_etherscan, + |_: TestProject, mut cmd: TestCommand| { + let cache_dir = + Config::foundry_chain_cache_dir(Chain::Named(ethers::prelude::Chain::Mainnet)).unwrap(); + let etherscan_cache_dir = Config::foundry_etherscan_chain_cache_dir(Chain::Named( + ethers::prelude::Chain::Mainnet, + )) + .unwrap(); + let path = cache_dir.as_path(); + let etherscan_path = etherscan_cache_dir.as_path(); + fs::create_dir_all(path).unwrap(); + fs::create_dir_all(etherscan_path).unwrap(); + cmd.args(["cache", "clean", "mainnet", "--etherscan"]); + cmd.assert_empty_stdout(); + + assert!(path.exists()); + assert!(!etherscan_path.exists()); + + Config::clean_foundry_cache().unwrap(); + } +); + +// checks that init works +forgetest!(can_init_repo_with_config, |prj: TestProject, mut cmd: TestCommand| { + let foundry_toml = prj.root().join(Config::FILE_NAME); + assert!(!foundry_toml.exists()); + + cmd.args(["init", "--force"]).arg(prj.root()); + cmd.assert_non_empty_stdout(); + + let s = read_string(&foundry_toml); + let _config: BasicConfig = parse_with_profile(&s).unwrap().unwrap().1; +}); + +// Checks that a forge project fails to initialise if dir is already git repo and dirty +forgetest!(can_detect_dirty_git_status_on_init, |prj: TestProject, mut cmd: TestCommand| { + prj.wipe(); + + // initialize new git repo + cmd.git_init(); + + std::fs::write(prj.root().join("untracked.text"), "untracked").unwrap(); + + // create nested dir and execute init in nested dir + let nested = prj.root().join("nested"); + fs::create_dir_all(&nested).unwrap(); + + cmd.current_dir(&nested); + cmd.arg("init"); + cmd.unchecked_output().stderr_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_detect_dirty_git_status_on_init.stderr"), + ); + + // ensure nothing was emitted, dir is empty + assert!(!nested.read_dir().map(|mut i| i.next().is_some()).unwrap_or_default()); +}); + +// Checks that a forge project can be initialized without creating a git repository +forgetest!(can_init_no_git, |prj: TestProject, mut cmd: TestCommand| { + prj.wipe(); + + cmd.arg("init").arg(prj.root()).arg("--no-git"); + cmd.assert_non_empty_stdout(); + prj.assert_config_exists(); + + assert!(!prj.root().join(".git").exists()); + assert!(prj.root().join("lib/forge-std").exists()); + assert!(!prj.root().join("lib/forge-std/.git").exists()); +}); + +// Checks that quiet mode does not print anything +forgetest!(can_init_quiet, |prj: TestProject, mut cmd: TestCommand| { + prj.wipe(); + + cmd.arg("init").arg(prj.root()).arg("-q"); + let _ = cmd.output(); +}); + +// `forge init foobar` works with dir argument +forgetest!(can_init_with_dir, |prj: TestProject, mut cmd: TestCommand| { + prj.create_file("README.md", "non-empty dir"); + cmd.args(["init", "foobar"]); + + cmd.assert_success(); + assert!(prj.root().join("foobar").exists()); +}); + +// `forge init --force` works on non-empty dirs +forgetest!(can_init_non_empty, |prj: TestProject, mut cmd: TestCommand| { + prj.create_file("README.md", "non-empty dir"); + cmd.arg("init").arg(prj.root()); + cmd.assert_err(); + + cmd.arg("--force"); + cmd.assert_non_empty_stdout(); + assert!(prj.root().join(".git").exists()); + assert!(prj.root().join("lib/forge-std").exists()); +}); + +// `forge init --force` works on already initialized git repository +forgetest!(can_init_in_empty_repo, |prj: TestProject, mut cmd: TestCommand| { + let root = prj.root(); + + // initialize new git repo + let status = Command::new("git") + .arg("init") + .current_dir(root) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() + .expect("could not run git init"); + assert!(status.success()); + assert!(root.join(".git").exists()); + + cmd.arg("init").arg(root); + cmd.assert_err(); + + cmd.arg("--force"); + cmd.assert_non_empty_stdout(); + assert!(root.join("lib/forge-std").exists()); +}); + +// `forge init --force` works on already initialized git repository +forgetest!(can_init_in_non_empty_repo, |prj: TestProject, mut cmd: TestCommand| { + let root = prj.root(); + + // initialize new git repo + let status = Command::new("git") + .arg("init") + .current_dir(root) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() + .expect("could not run git init"); + assert!(status.success()); + assert!(root.join(".git").exists()); + + prj.create_file("README.md", "non-empty dir"); + prj.create_file(".gitignore", "not foundry .gitignore"); + + cmd.arg("init").arg(root); + cmd.assert_err(); + + cmd.arg("--force"); + cmd.assert_non_empty_stdout(); + assert!(root.join("lib/forge-std").exists()); + + // not overwritten + let gitignore = root.join(".gitignore"); + let gitignore = fs::read_to_string(gitignore).unwrap(); + assert_eq!(gitignore, "not foundry .gitignore"); +}); + +// Checks that remappings.txt and .vscode/settings.json is generated +forgetest!(can_init_vscode, |prj: TestProject, mut cmd: TestCommand| { + prj.wipe(); + + cmd.arg("init").arg(prj.root()).arg("--vscode"); + cmd.assert_non_empty_stdout(); + + let settings = prj.root().join(".vscode/settings.json"); + assert!(settings.is_file()); + let settings: serde_json::Value = ethers::solc::utils::read_json_file(&settings).unwrap(); + assert_eq!( + settings, + serde_json::json!({ + "solidity.packageDefaultDependenciesContractsDirectory": "src", + "solidity.packageDefaultDependenciesDirectory": "lib" + }) + ); + + let remappings = prj.root().join("remappings.txt"); + assert!(remappings.is_file()); + let content = std::fs::read_to_string(remappings).unwrap(); + assert_eq!(content, "ds-test/=lib/forge-std/lib/ds-test/src/\nforge-std/=lib/forge-std/src/",); +}); + +// checks that forge can init with template +forgetest!(can_init_template, |prj: TestProject, mut cmd: TestCommand| { + prj.wipe(); + cmd.args(["init", "--template", "foundry-rs/forge-template"]).arg(prj.root()); + cmd.assert_non_empty_stdout(); + assert!(prj.root().join(".git").exists()); + assert!(prj.root().join("foundry.toml").exists()); + assert!(prj.root().join("lib/forge-std").exists()); + // assert that gitmodules were correctly initialized + assert!(prj.root().join(".git/modules").exists()); + assert!(prj.root().join("src").exists()); + assert!(prj.root().join("test").exists()); +}); + +// checks that forge can init with template and branch +forgetest!(can_init_template_with_branch, |prj: TestProject, mut cmd: TestCommand| { + prj.wipe(); + cmd.args(["init", "--template", "foundry-rs/forge-template", "--branch", "test/deployments"]) + .arg(prj.root()); + cmd.assert_non_empty_stdout(); + assert!(prj.root().join(".git").exists()); + assert!(prj.root().join(".dapprc").exists()); + assert!(prj.root().join("lib/ds-test").exists()); + // assert that gitmodules were correctly initialized + assert!(prj.root().join(".git/modules").exists()); + assert!(prj.root().join("src").exists()); + assert!(prj.root().join("scripts").exists()); +}); + +// checks that init fails when the provided template doesn't exist +forgetest!(fail_init_nonexistent_template, |prj: TestProject, mut cmd: TestCommand| { + prj.wipe(); + cmd.args(["init", "--template", "a"]).arg(prj.root()); + cmd.assert_non_empty_stderr(); +}); + +// checks that `clean` removes dapptools style paths +forgetest!(can_clean, |prj: TestProject, mut cmd: TestCommand| { + prj.assert_create_dirs_exists(); + prj.assert_style_paths_exist(PathStyle::Dapptools); + cmd.arg("clean"); + cmd.assert_empty_stdout(); + prj.assert_cleaned(); +}); + +// checks that `clean` removes hardhat style paths +forgetest!(can_clean_hardhat, PathStyle::HardHat, |prj: TestProject, mut cmd: TestCommand| { + prj.assert_create_dirs_exists(); + prj.assert_style_paths_exist(PathStyle::HardHat); + cmd.arg("clean"); + cmd.assert_empty_stdout(); + prj.assert_cleaned(); +}); + +// checks that `clean` also works with the "out" value set in Config +forgetest_init!(can_clean_config, |prj: TestProject, mut cmd: TestCommand| { + let config = Config { out: "custom-out".into(), ..Default::default() }; + prj.write_config(config); + cmd.arg("build"); + cmd.assert_non_empty_stdout(); + + // default test contract is written in custom out directory + let artifact = prj.root().join(format!("custom-out/{TEMPLATE_TEST_CONTRACT_ARTIFACT_JSON}")); + assert!(artifact.exists()); + + cmd.forge_fuse().arg("clean"); + cmd.output(); + assert!(!artifact.exists()); +}); + +// checks that extra output works +forgetest_init!(can_emit_extra_output, |prj: TestProject, mut cmd: TestCommand| { + cmd.args(["build", "--extra-output", "metadata"]); + cmd.assert_non_empty_stdout(); + + let artifact_path = prj.paths().artifacts.join(TEMPLATE_CONTRACT_ARTIFACT_JSON); + let artifact: ConfigurableContractArtifact = + ethers::solc::utils::read_json_file(artifact_path).unwrap(); + assert!(artifact.metadata.is_some()); + + cmd.forge_fuse().args(["build", "--extra-output-files", "metadata", "--force"]).root_arg(); + cmd.assert_non_empty_stdout(); + + let metadata_path = + prj.paths().artifacts.join(format!("{TEMPLATE_CONTRACT_ARTIFACT_BASE}.metadata.json")); + let _artifact: Metadata = ethers::solc::utils::read_json_file(metadata_path).unwrap(); +}); + +// checks that extra output works +forgetest_init!(can_emit_multiple_extra_output, |prj: TestProject, mut cmd: TestCommand| { + cmd.args(["build", "--extra-output", "metadata", "ir-optimized", "--extra-output", "ir"]); + cmd.assert_non_empty_stdout(); + + let artifact_path = prj.paths().artifacts.join(TEMPLATE_CONTRACT_ARTIFACT_JSON); + let artifact: ConfigurableContractArtifact = + ethers::solc::utils::read_json_file(artifact_path).unwrap(); + assert!(artifact.metadata.is_some()); + assert!(artifact.ir.is_some()); + assert!(artifact.ir_optimized.is_some()); + + cmd.forge_fuse() + .args([ + "build", + "--extra-output-files", + "metadata", + "ir-optimized", + "evm.bytecode.sourceMap", + "--force", + ]) + .root_arg(); + cmd.assert_non_empty_stdout(); + + let metadata_path = + prj.paths().artifacts.join(format!("{TEMPLATE_CONTRACT_ARTIFACT_BASE}.metadata.json")); + let _artifact: Metadata = ethers::solc::utils::read_json_file(metadata_path).unwrap(); + + let iropt = prj.paths().artifacts.join(format!("{TEMPLATE_CONTRACT_ARTIFACT_BASE}.iropt")); + std::fs::read_to_string(iropt).unwrap(); + + let sourcemap = + prj.paths().artifacts.join(format!("{TEMPLATE_CONTRACT_ARTIFACT_BASE}.sourcemap")); + std::fs::read_to_string(sourcemap).unwrap(); +}); + +forgetest!(can_print_warnings, |prj: TestProject, mut cmd: TestCommand| { + prj.inner() + .add_source( + "Foo", + r" +// SPDX-License-Identifier: UNLICENSED +pragma solidity >0.8.9; +contract Greeter { + function foo(uint256 a) public { + uint256 x = 1; + } +} + ", + ) + .unwrap(); + + // explicitly set to run with 0.8.10 + let config = Config { solc: Some("0.8.10".into()), ..Default::default() }; + prj.write_config(config); + + cmd.arg("build"); + + let output = cmd.stdout_lossy(); + assert!(output.contains( + " +Compiler run successful with warnings: +Warning (5667): Warning: Unused function parameter. Remove or comment out the variable name to silence this warning. +", + )); +}); + +// Tests that direct import paths are handled correctly +// +// NOTE(onbjerg): Disabled for Windows -- for some reason solc fails with a bogus error message +// here: error[9553]: TypeError: Invalid type for argument in function call. Invalid implicit +// conversion from struct Bar memory to struct Bar memory requested. --> src\Foo.sol:12:22: +// | +// 12 | FooLib.check(b); +// | ^ +// +// +// +// error[9553]: TypeError: Invalid type for argument in function call. Invalid implicit conversion +// from contract Foo to contract Foo requested. --> src\Foo.sol:15:23: +// | +// 15 | FooLib.check2(this); +// | ^^^^ +#[cfg(not(target_os = "windows"))] +forgetest!(can_handle_direct_imports_into_src, |prj: TestProject, mut cmd: TestCommand| { + prj.inner() + .add_source( + "Foo", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +import {FooLib} from "src/FooLib.sol"; +struct Bar { + uint8 x; +} +contract Foo { + mapping(uint256 => Bar) bars; + function checker(uint256 id) external { + Bar memory b = bars[id]; + FooLib.check(b); + } + function checker2() external { + FooLib.check2(this); + } +} + "#, + ) + .unwrap(); + + prj.inner() + .add_source( + "FooLib", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +import {Foo, Bar} from "src/Foo.sol"; +library FooLib { + function check(Bar memory b) public {} + function check2(Foo f) public {} +} + "#, + ) + .unwrap(); + + cmd.arg("build"); + + assert!(cmd.stdout_lossy().ends_with( + " +Compiler run successful! +" + )); +}); + +// tests that the `inspect` command works correctly +forgetest!(can_execute_inspect_command, |prj: TestProject, mut cmd: TestCommand| { + // explicitly set to include the ipfs bytecode hash + let config = Config { bytecode_hash: BytecodeHash::Ipfs, ..Default::default() }; + prj.write_config(config); + let contract_name = "Foo"; + let path = prj + .inner() + .add_source( + contract_name, + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +contract Foo { + event log_string(string); + function run() external { + emit log_string("script ran"); + } +} + "#, + ) + .unwrap(); + + // Remove the ipfs hash from the metadata + let mut dynamic_bytecode = "0x608060405234801561001057600080fd5b5060c08061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063c040622614602d575b600080fd5b60336035565b005b7f0b2e13ff20ac7b474198655583edf70dedd2c1dc980e329c4fbb2fc0748b796b6040516080906020808252600a908201526939b1b934b83a103930b760b11b604082015260600190565b60405180910390a156fea264697066735822122065c066d19101ad1707272b9a884891af8ab0cf5a0e0bba70c4650594492c14be64736f6c634300080a0033\n".to_string(); + let ipfs_start = dynamic_bytecode.len() - (24 + 64); + let ipfs_end = ipfs_start + 65; + dynamic_bytecode.replace_range(ipfs_start..ipfs_end, ""); + + let check_output = |mut output: String| { + output.replace_range(ipfs_start..ipfs_end, ""); + assert_eq!(dynamic_bytecode, output); + }; + + cmd.arg("inspect").arg(contract_name).arg("bytecode"); + check_output(cmd.stdout_lossy()); + + let info = format!("src/{}:{}", path.file_name().unwrap().to_string_lossy(), contract_name); + cmd.forge_fuse().arg("inspect").arg(info).arg("bytecode"); + check_output(cmd.stdout_lossy()); +}); + +// test that `forge snapshot` commands work +forgetest!( + #[serial_test::serial] + can_check_snapshot, + |prj: TestProject, mut cmd: TestCommand| { + prj.insert_ds_test(); + + prj.inner() + .add_source( + "ATest.t.sol", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +import "./test.sol"; +contract ATest is DSTest { + function testExample() public { + assertTrue(true); + } +} + "#, + ) + .unwrap(); + + cmd.arg("snapshot"); + + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_check_snapshot.stdout"), + ); + + cmd.arg("--check"); + let _ = cmd.output(); + } +); + +// test that `forge build` does not print `(with warnings)` if there arent any +forgetest!(can_compile_without_warnings, |prj: TestProject, mut cmd: TestCommand| { + let config = Config { + ignored_error_codes: vec![SolidityErrorCode::SpdxLicenseNotProvided], + ..Default::default() + }; + prj.write_config(config); + prj.inner() + .add_source( + "A", + r" +pragma solidity 0.8.10; +contract A { + function testExample() public {} +} + ", + ) + .unwrap(); + + cmd.args(["build", "--force"]); + let out = cmd.stdout(); + // no warnings + assert!(out.trim().contains("Compiler run successful!")); + assert!(!out.trim().contains("Compiler run successful with warnings:")); + + // don't ignore errors + let config = Config { ignored_error_codes: vec![], ..Default::default() }; + prj.write_config(config); + let out = cmd.stdout(); + + assert!(out.trim().contains("Compiler run successful with warnings:")); + assert!( + out.contains( + r#"Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information."# + ) + ); +}); + +// test that `forge build` compiles when severity set to error, fails when set to warning, and +// handles ignored error codes as an exception +forgetest!(can_fail_compile_with_warnings, |prj: TestProject, mut cmd: TestCommand| { + let config = Config { ignored_error_codes: vec![], deny_warnings: false, ..Default::default() }; + prj.write_config(config); + prj.inner() + .add_source( + "A", + r" +pragma solidity 0.8.10; +contract A { + function testExample() public {} +} + ", + ) + .unwrap(); + + cmd.args(["build", "--force"]); + let out = cmd.stdout(); + // there are no errors + assert!(out.trim().contains("Compiler run successful")); + assert!(out.trim().contains("Compiler run successful with warnings:")); + + // warning fails to compile + let config = Config { ignored_error_codes: vec![], deny_warnings: true, ..Default::default() }; + prj.write_config(config); + cmd.assert_err(); + + // ignores error code and compiles + let config = Config { + ignored_error_codes: vec![SolidityErrorCode::SpdxLicenseNotProvided], + deny_warnings: true, + ..Default::default() + }; + prj.write_config(config); + let out = cmd.stdout(); + + assert!(out.trim().contains("Compiler run successful!")); + assert!(!out.trim().contains("Compiler run successful with warnings:")); +}); + +// test against a local checkout, useful to debug with local ethers-rs patch +forgetest!( + #[ignore] + can_compile_local_spells, + |_: TestProject, mut cmd: TestCommand| { + let current_dir = std::env::current_dir().unwrap(); + let root = current_dir + .join("../../foundry-integration-tests/testdata/spells-mainnet") + .to_string_lossy() + .to_string(); + println!("project root: \"{root}\""); + + let eth_rpc_url = foundry_utils::rpc::next_http_archive_rpc_endpoint(); + let dss_exec_lib = "src/DssSpell.sol:DssExecLib:0xfD88CeE74f7D78697775aBDAE53f9Da1559728E4"; + + cmd.args([ + "test", + "--root", + root.as_str(), + "--fork-url", + eth_rpc_url.as_str(), + "--fork-block-number", + "14435000", + "--libraries", + dss_exec_lib, + "-vvv", + ]); + cmd.print_output(); + } +); + +// test that a failing `forge build` does not impact followup builds +forgetest!(can_build_after_failure, |prj: TestProject, mut cmd: TestCommand| { + prj.insert_ds_test(); + + prj.inner() + .add_source( + "ATest.t.sol", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +import "./test.sol"; +contract ATest is DSTest { + function testExample() public { + assertTrue(true); + } +} + "#, + ) + .unwrap(); + prj.inner() + .add_source( + "BTest.t.sol", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +import "./test.sol"; +contract BTest is DSTest { + function testExample() public { + assertTrue(true); + } +} + "#, + ) + .unwrap(); + + cmd.arg("build"); + cmd.assert_non_empty_stdout(); + prj.assert_cache_exists(); + prj.assert_artifacts_dir_exists(); + + let syntax_err = r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +import "./test.sol"; +contract CTest is DSTest { + function testExample() public { + THIS WILL CAUSE AN ERROR + } +} + "#; + + // introduce contract with syntax error + prj.inner().add_source("CTest.t.sol", syntax_err).unwrap(); + + // `forge build --force` which should fail + cmd.arg("--force"); + cmd.assert_err(); + + // but ensure this cleaned cache and artifacts + assert!(!prj.paths().artifacts.exists()); + assert!(!prj.cache_path().exists()); + + // still errors + cmd.forge_fuse().arg("build"); + cmd.assert_err(); + + // resolve the error by replacing the file + prj.inner() + .add_source( + "CTest.t.sol", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +import "./test.sol"; +contract CTest is DSTest { + function testExample() public { + assertTrue(true); + } +} + "#, + ) + .unwrap(); + + cmd.assert_non_empty_stdout(); + prj.assert_cache_exists(); + prj.assert_artifacts_dir_exists(); + + // ensure cache is unchanged after error + let cache = fs::read_to_string(prj.cache_path()).unwrap(); + + // introduce the error again but building without force + prj.inner().add_source("CTest.t.sol", syntax_err).unwrap(); + cmd.assert_err(); + + // ensure unchanged cache file + let cache_after = fs::read_to_string(prj.cache_path()).unwrap(); + assert_eq!(cache, cache_after); +}); + +// test to check that install/remove works properly +forgetest!(can_install_and_remove, |prj: TestProject, mut cmd: TestCommand| { + cmd.git_init(); + + let libs = prj.root().join("lib"); + let git_mod = prj.root().join(".git/modules/lib"); + let git_mod_file = prj.root().join(".gitmodules"); + + let forge_std = libs.join("forge-std"); + let forge_std_mod = git_mod.join("forge-std"); + + let install = |cmd: &mut TestCommand| { + cmd.forge_fuse().args(["install", "foundry-rs/forge-std", "--no-commit"]); + cmd.assert_non_empty_stdout(); + assert!(forge_std.exists()); + assert!(forge_std_mod.exists()); + + let submods = read_string(&git_mod_file); + assert!(submods.contains("https://github.com/foundry-rs/forge-std")); + }; + + let remove = |cmd: &mut TestCommand, target: &str| { + cmd.forge_fuse().args(["remove", "--force", target]); + cmd.assert_non_empty_stdout(); + assert!(!forge_std.exists()); + assert!(!forge_std_mod.exists()); + let submods = read_string(&git_mod_file); + assert!(!submods.contains("https://github.com/foundry-rs/forge-std")); + }; + + install(&mut cmd); + remove(&mut cmd, "forge-std"); + + // install again and remove via relative path + install(&mut cmd); + remove(&mut cmd, "lib/forge-std"); +}); + +// test to check that package can be reinstalled after manually removing the directory +forgetest!(can_reinstall_after_manual_remove, |prj: TestProject, mut cmd: TestCommand| { + cmd.git_init(); + + let libs = prj.root().join("lib"); + let git_mod = prj.root().join(".git/modules/lib"); + let git_mod_file = prj.root().join(".gitmodules"); + + let forge_std = libs.join("forge-std"); + let forge_std_mod = git_mod.join("forge-std"); + + let install = |cmd: &mut TestCommand| { + cmd.forge_fuse().args(["install", "foundry-rs/forge-std", "--no-commit"]); + cmd.assert_non_empty_stdout(); + assert!(forge_std.exists()); + assert!(forge_std_mod.exists()); + + let submods = read_string(&git_mod_file); + assert!(submods.contains("https://github.com/foundry-rs/forge-std")); + }; + + install(&mut cmd); + fs::remove_dir_all(forge_std.clone()).expect("Failed to remove forge-std"); + + // install again + install(&mut cmd); +}); + +// test that we can repeatedly install the same dependency without changes +forgetest!(can_install_repeatedly, |_prj: TestProject, mut cmd: TestCommand| { + cmd.git_init(); + + cmd.forge_fuse().args(["install", "foundry-rs/forge-std"]); + for _ in 0..3 { + cmd.assert_success(); + } +}); + +// test that by default we install the latest semver release tag +// +forgetest!(can_install_latest_release_tag, |prj: TestProject, mut cmd: TestCommand| { + cmd.git_init(); + cmd.forge_fuse().args(["install", "openzeppelin/openzeppelin-contracts"]); + cmd.assert_success(); + + let dep = prj.paths().libraries[0].join("openzeppelin-contracts"); + assert!(dep.exists()); + + // the latest release at the time this test was written + let version: Version = "4.8.0".parse().unwrap(); + let out = Command::new("git").current_dir(&dep).args(["describe", "--tags"]).output().unwrap(); + let tag = String::from_utf8_lossy(&out.stdout); + let current: Version = tag.as_ref().trim_start_matches('v').trim().parse().unwrap(); + + assert!(current >= version); +}); + +// Tests that forge update doesn't break a working dependency by recursively updating nested +// dependencies +forgetest!( + #[ignore] + can_update_library_with_outdated_nested_dependency, + |prj: TestProject, mut cmd: TestCommand| { + cmd.git_init(); + + let libs = prj.root().join("lib"); + let git_mod = prj.root().join(".git/modules/lib"); + let git_mod_file = prj.root().join(".gitmodules"); + + let package = libs.join("issue-2264-repro"); + let package_mod = git_mod.join("issue-2264-repro"); + + let install = |cmd: &mut TestCommand| { + cmd.forge_fuse().args(["install", "foundry-rs/issue-2264-repro", "--no-commit"]); + cmd.assert_non_empty_stdout(); + assert!(package.exists()); + assert!(package_mod.exists()); + + let submods = read_string(&git_mod_file); + assert!(submods.contains("https://github.com/foundry-rs/issue-2264-repro")); + }; + + install(&mut cmd); + cmd.forge_fuse().args(["update", "lib/issue-2264-repro"]); + cmd.stdout_lossy(); + + prj.inner() + .add_source( + "MyTokenCopy", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.6.0; +import "issue-2264-repro/MyToken.sol"; +contract MyTokenCopy is MyToken { +} + "#, + ) + .unwrap(); + + cmd.forge_fuse().args(["build"]); + let output = cmd.stdout_lossy(); + + assert!(output.contains("Compiler run successful",)); + } +); + +forgetest!(gas_report_all_contracts, |prj: TestProject, mut cmd: TestCommand| { + prj.insert_ds_test(); + prj.inner() + .add_source( + "Contracts.sol", + r#" +//SPDX-license-identifier: MIT +pragma solidity ^0.8.0; + +import "./test.sol"; + +contract ContractOne { + int public i; + + constructor() { + i = 0; + } + + function foo() public{ + while(i<5){ + i++; + } + } +} + +contract ContractOneTest is DSTest { + ContractOne c1; + + function setUp() public { + c1 = new ContractOne(); + } + + function testFoo() public { + c1.foo(); + } +} + + +contract ContractTwo { + int public i; + + constructor() { + i = 0; + } + + function bar() public{ + while(i<50){ + i++; + } + } +} + +contract ContractTwoTest is DSTest { + ContractTwo c2; + + function setUp() public { + c2 = new ContractTwo(); + } + + function testBar() public { + c2.bar(); + } +} + +contract ContractThree { + int public i; + + constructor() { + i = 0; + } + + function baz() public{ + while(i<500){ + i++; + } + } +} + +contract ContractThreeTest is DSTest { + ContractThree c3; + + function setUp() public { + c3 = new ContractThree(); + } + + function testBaz() public { + c3.baz(); + } +} + "#, + ) + .unwrap(); + + // report for all + prj.write_config(Config { + gas_reports: (vec!["*".to_string()]), + gas_reports_ignore: (vec![]), + ..Default::default() + }); + cmd.forge_fuse(); + let first_out = cmd.arg("test").arg("--gas-report").stdout(); + assert!(first_out.contains("foo") && first_out.contains("bar") && first_out.contains("baz")); + // cmd.arg("test").arg("--gas-report").print_output(); + + cmd.forge_fuse(); + prj.write_config(Config { gas_reports: (vec![]), ..Default::default() }); + cmd.forge_fuse(); + let second_out = cmd.arg("test").arg("--gas-report").stdout(); + assert!(second_out.contains("foo") && second_out.contains("bar") && second_out.contains("baz")); + // cmd.arg("test").arg("--gas-report").print_output(); + + cmd.forge_fuse(); + prj.write_config(Config { gas_reports: (vec!["*".to_string()]), ..Default::default() }); + cmd.forge_fuse(); + let third_out = cmd.arg("test").arg("--gas-report").stdout(); + assert!(third_out.contains("foo") && third_out.contains("bar") && third_out.contains("baz")); + // cmd.arg("test").arg("--gas-report").print_output(); + + cmd.forge_fuse(); + prj.write_config(Config { + gas_reports: (vec![ + "ContractOne".to_string(), + "ContractTwo".to_string(), + "ContractThree".to_string(), + ]), + ..Default::default() + }); + cmd.forge_fuse(); + let fourth_out = cmd.arg("test").arg("--gas-report").stdout(); + assert!(fourth_out.contains("foo") && fourth_out.contains("bar") && fourth_out.contains("baz")); + // cmd.arg("test").arg("--gas-report").print_output(); +}); + +forgetest!(gas_report_some_contracts, |prj: TestProject, mut cmd: TestCommand| { + prj.insert_ds_test(); + prj.inner() + .add_source( + "Contracts.sol", + r#" +//SPDX-license-identifier: MIT +pragma solidity ^0.8.0; + +import "./test.sol"; + +contract ContractOne { + int public i; + + constructor() { + i = 0; + } + + function foo() public{ + while(i<5){ + i++; + } + } +} + +contract ContractOneTest is DSTest { + ContractOne c1; + + function setUp() public { + c1 = new ContractOne(); + } + + function testFoo() public { + c1.foo(); + } +} + + +contract ContractTwo { + int public i; + + constructor() { + i = 0; + } + + function bar() public{ + while(i<50){ + i++; + } + } +} + +contract ContractTwoTest is DSTest { + ContractTwo c2; + + function setUp() public { + c2 = new ContractTwo(); + } + + function testBar() public { + c2.bar(); + } +} + +contract ContractThree { + int public i; + + constructor() { + i = 0; + } + + function baz() public{ + while(i<500){ + i++; + } + } +} + +contract ContractThreeTest is DSTest { + ContractThree c3; + + function setUp() public { + c3 = new ContractThree(); + } + + function testBaz() public { + c3.baz(); + } +} + "#, + ) + .unwrap(); + + // report for One + prj.write_config(Config { + gas_reports: (vec!["ContractOne".to_string()]), + gas_reports_ignore: (vec![]), + ..Default::default() + }); + cmd.forge_fuse(); + let first_out = cmd.arg("test").arg("--gas-report").stdout(); + assert!(first_out.contains("foo") && !first_out.contains("bar") && !first_out.contains("baz")); + // cmd.arg("test").arg("--gas-report").print_output(); + + // report for Two + cmd.forge_fuse(); + prj.write_config(Config { + gas_reports: (vec!["ContractTwo".to_string()]), + ..Default::default() + }); + cmd.forge_fuse(); + let second_out = cmd.arg("test").arg("--gas-report").stdout(); + assert!( + !second_out.contains("foo") && second_out.contains("bar") && !second_out.contains("baz") + ); + // cmd.arg("test").arg("--gas-report").print_output(); + + // report for Three + cmd.forge_fuse(); + prj.write_config(Config { + gas_reports: (vec!["ContractThree".to_string()]), + ..Default::default() + }); + cmd.forge_fuse(); + let third_out = cmd.arg("test").arg("--gas-report").stdout(); + assert!(!third_out.contains("foo") && !third_out.contains("bar") && third_out.contains("baz")); + // cmd.arg("test").arg("--gas-report").print_output(); +}); + +forgetest!(gas_ignore_some_contracts, |prj: TestProject, mut cmd: TestCommand| { + prj.insert_ds_test(); + prj.inner() + .add_source( + "Contracts.sol", + r#" +//SPDX-license-identifier: MIT +pragma solidity ^0.8.0; + +import "./test.sol"; + +contract ContractOne { + int public i; + + constructor() { + i = 0; + } + + function foo() public{ + while(i<5){ + i++; + } + } +} + +contract ContractOneTest is DSTest { + ContractOne c1; + + function setUp() public { + c1 = new ContractOne(); + } + + function testFoo() public { + c1.foo(); + } +} + + +contract ContractTwo { + int public i; + + constructor() { + i = 0; + } + + function bar() public{ + while(i<50){ + i++; + } + } +} + +contract ContractTwoTest is DSTest { + ContractTwo c2; + + function setUp() public { + c2 = new ContractTwo(); + } + + function testBar() public { + c2.bar(); + } +} + +contract ContractThree { + int public i; + + constructor() { + i = 0; + } + + function baz() public{ + while(i<500){ + i++; + } + } +} + +contract ContractThreeTest is DSTest { + ContractThree c3; + + function setUp() public { + c3 = new ContractThree(); + } + + function testBaz() public { + c3.baz(); + } +} + "#, + ) + .unwrap(); + + // ignore ContractOne + prj.write_config(Config { + gas_reports: (vec!["*".to_string()]), + gas_reports_ignore: (vec!["ContractOne".to_string()]), + ..Default::default() + }); + cmd.forge_fuse(); + let first_out = cmd.arg("test").arg("--gas-report").stdout(); + assert!(!first_out.contains("foo") && first_out.contains("bar") && first_out.contains("baz")); + // cmd.arg("test").arg("--gas-report").print_output(); + + // ignore ContractTwo + cmd.forge_fuse(); + prj.write_config(Config { + gas_reports: (vec![]), + gas_reports_ignore: (vec!["ContractTwo".to_string()]), + ..Default::default() + }); + cmd.forge_fuse(); + let second_out = cmd.arg("test").arg("--gas-report").stdout(); + assert!( + second_out.contains("foo") && !second_out.contains("bar") && second_out.contains("baz") + ); + // cmd.arg("test").arg("--gas-report").print_output(); + + // ignore ContractThree + cmd.forge_fuse(); + prj.write_config(Config { + gas_reports: (vec![ + "ContractOne".to_string(), + "ContractTwo".to_string(), + "ContractThree".to_string(), + ]), + gas_reports_ignore: (vec!["ContractThree".to_string()]), + ..Default::default() + }); + cmd.forge_fuse(); + let third_out = cmd.arg("test").arg("--gas-report").stdout(); + assert!(third_out.contains("foo") && third_out.contains("bar") && third_out.contains("baz")); +}); + +forgetest_init!(can_use_absolute_imports, |prj: TestProject, mut cmd: TestCommand| { + let remapping = prj.paths().libraries[0].join("myDepdendency"); + let config = Config { + remappings: vec![Remapping::from_str(&format!("myDepdendency/={}", remapping.display())) + .unwrap() + .into()], + ..Default::default() + }; + prj.write_config(config); + + prj.inner() + .add_lib( + "myDepdendency/src/interfaces/IConfig.sol", + r" + pragma solidity ^0.8.10; + + interface IConfig {} + ", + ) + .unwrap(); + + prj.inner() + .add_lib( + "myDepdendency/src/Config.sol", + r#" + pragma solidity ^0.8.10; + import "src/interfaces/IConfig.sol"; + + contract Config {} + "#, + ) + .unwrap(); + + prj.inner() + .add_source( + "Greeter", + r#" + pragma solidity ^0.8.10; + import "myDepdendency/src/Config.sol"; + + contract Greeter {} + "#, + ) + .unwrap(); + + cmd.arg("build"); + let stdout = cmd.stdout_lossy(); + assert!(stdout.contains("Compiler run successful")); +}); + +// +forgetest_init!( + can_use_absolute_imports_from_test_and_script, + |prj: TestProject, mut cmd: TestCommand| { + prj.inner() + .add_script( + "IMyScript.sol", + r" + pragma solidity ^0.8.10; + + interface IMyScript {} + ", + ) + .unwrap(); + + prj.inner() + .add_script( + "MyScript.sol", + r#" + pragma solidity ^0.8.10; + import "script/IMyScript.sol"; + + contract MyScript is IMyScript {} + "#, + ) + .unwrap(); + + prj.inner() + .add_test( + "IMyTest.sol", + r" + pragma solidity ^0.8.10; + + interface IMyTest {} + ", + ) + .unwrap(); + + prj.inner() + .add_test( + "MyTest.sol", + r#" + pragma solidity ^0.8.10; + import "test/IMyTest.sol"; + + contract MyTest is IMyTest {} + "#, + ) + .unwrap(); + + cmd.arg("build"); + let stdout = cmd.stdout_lossy(); + assert!(stdout.contains("Compiler run successful")); + } +); + +// checks `forge inspect irOptimized works +forgetest_init!(can_inspect_ir_optimized, |_prj: TestProject, mut cmd: TestCommand| { + cmd.args(["inspect", TEMPLATE_CONTRACT, "irOptimized"]); + cmd.assert_success(); +}); + +// checks forge bind works correctly on the default project +forgetest_init!(can_bind, |_prj: TestProject, mut cmd: TestCommand| { + cmd.arg("bind"); + cmd.assert_non_empty_stdout(); +}); + +// checks missing dependencies are auto installed +forgetest_init!(can_install_missing_deps_test, |prj: TestProject, mut cmd: TestCommand| { + // wipe forge-std + let forge_std_dir = prj.root().join("lib/forge-std"); + pretty_err(&forge_std_dir, fs::remove_dir_all(&forge_std_dir)); + + cmd.arg("test"); + + let output = cmd.stdout_lossy(); + assert!(output.contains("Missing dependencies found. Installing now"), "{}", output); + assert!(output.contains("[PASS]"), "{}", output); +}); + +// checks missing dependencies are auto installed +forgetest_init!(can_install_missing_deps_build, |prj: TestProject, mut cmd: TestCommand| { + // wipe forge-std + let forge_std_dir = prj.root().join("lib/forge-std"); + pretty_err(&forge_std_dir, fs::remove_dir_all(&forge_std_dir)); + + cmd.arg("build"); + + let output = cmd.stdout_lossy(); + assert!(output.contains("Missing dependencies found. Installing now"), "{}", output); + assert!(output.contains("Compiler run successful"), "{}", output); +}); + +// checks that extra output works +forgetest_init!(can_build_skip_contracts, |prj: TestProject, mut cmd: TestCommand| { + // explicitly set to run with 0.8.17 for consistent output + let config = Config { solc: Some("0.8.17".into()), ..Default::default() }; + prj.write_config(config); + + // only builds the single template contract `src/*` + cmd.args(["build", "--skip", "tests", "--skip", "scripts"]); + + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_build_skip_contracts.stdout"), + ); + // re-run command + let out = cmd.stdout(); + + // unchanged + assert!(out.trim().contains("No files changed, compilation skipped"), "{}", out); +}); + +forgetest_init!(can_build_skip_glob, |prj: TestProject, mut cmd: TestCommand| { + // explicitly set to run with 0.8.17 for consistent output + let config = Config { solc: Some("0.8.17".into()), ..Default::default() }; + prj.write_config(config); + prj.inner() + .add_test( + "Foo", + r" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.17; +contract TestDemo { +function test_run() external {} +}", + ) + .unwrap(); + // only builds the single template contract `src/*` even if `*.t.sol` or `.s.sol` is absent + cmd.args(["build", "--skip", "*/test/**", "--skip", "*/script/**"]); + + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/can_build_skip_glob.stdout"), + ); +}); + +// checks that build --sizes includes all contracts even if unchanged +forgetest_init!(can_build_sizes_repeatedly, |_prj: TestProject, mut cmd: TestCommand| { + cmd.args(["build", "--sizes"]); + let out = cmd.stdout(); + + // contains: Counter ┆ 0.247 ┆ 24.329 + assert!(out.contains(TEMPLATE_CONTRACT)); + + // get the entire table + let table = out.split("Compiler run successful!").nth(1).unwrap().trim(); + + let unchanged = cmd.stdout(); + assert!(unchanged.contains(table), "{}", table); +}); + +// checks that build --names includes all contracts even if unchanged +forgetest_init!(can_build_names_repeatedly, |_prj: TestProject, mut cmd: TestCommand| { + cmd.args(["build", "--names"]); + let out = cmd.stdout(); + + assert!(out.contains(TEMPLATE_CONTRACT)); + + // get the entire list + let list = out.split("Compiler run successful!").nth(1).unwrap().trim(); + + let unchanged = cmd.stdout(); + assert!(unchanged.contains(list), "{}", list); +}); diff --git a/crates/zkforge/tests/cli/config.rs b/crates/zkforge/tests/cli/config.rs new file mode 100644 index 000000000..40da4c6b9 --- /dev/null +++ b/crates/zkforge/tests/cli/config.rs @@ -0,0 +1,639 @@ +//! Contains various tests for checking forge commands related to config values + +use ethers::{ + prelude::artifacts::YulDetails, + solc::artifacts::RevertStrings, + types::{Address, H256, U256}, +}; +use foundry_cli::utils as forge_utils; +use foundry_config::{ + cache::{CachedChains, CachedEndpoints, StorageCachingConfig}, + Config, FuzzConfig, InvariantConfig, OptimizerDetails, SolcReq, +}; +use foundry_evm::executor::opts::EvmOpts; +use foundry_test_utils::{ + ethers_solc::{remappings::Remapping, EvmVersion}, + forgetest, forgetest_init, pretty_eq, + util::{pretty_err, OutputExt, TestCommand, TestProject}, +}; +use path_slash::PathBufExt; +use std::{fs, path::PathBuf, str::FromStr}; + +// tests all config values that are in use +forgetest!(can_extract_config_values, |prj: TestProject, mut cmd: TestCommand| { + // explicitly set all values + let input = Config { + profile: Config::DEFAULT_PROFILE, + __root: Default::default(), + src: "test-src".into(), + test: "test-test".into(), + script: "test-script".into(), + out: "out-test".into(), + libs: vec!["lib-test".into()], + cache: true, + cache_path: "test-cache".into(), + broadcast: "broadcast".into(), + force: true, + evm_version: EvmVersion::Byzantium, + gas_reports: vec!["Contract".to_string()], + gas_reports_ignore: vec![], + solc: Some(SolcReq::Local(PathBuf::from("custom-solc"))), + auto_detect_solc: false, + auto_detect_remappings: true, + offline: true, + optimizer: false, + optimizer_runs: 1000, + optimizer_details: Some(OptimizerDetails { + yul: Some(false), + yul_details: Some(YulDetails { stack_allocation: Some(true), ..Default::default() }), + ..Default::default() + }), + model_checker: None, + extra_output: Default::default(), + extra_output_files: Default::default(), + names: true, + sizes: true, + test_pattern: None, + test_pattern_inverse: None, + contract_pattern: None, + contract_pattern_inverse: None, + path_pattern: None, + path_pattern_inverse: None, + fuzz: FuzzConfig { + runs: 1000, + max_test_rejects: 100203, + seed: Some(1000.into()), + ..Default::default() + }, + invariant: InvariantConfig { runs: 256, ..Default::default() }, + ffi: true, + sender: "00a329c0648769A73afAc7F9381D08FB43dBEA72".parse().unwrap(), + tx_origin: "00a329c0648769A73afAc7F9F81E08FB43dBEA72".parse().unwrap(), + initial_balance: U256::from(0xffffffffffffffffffffffffu128), + block_number: 10, + fork_block_number: Some(200), + chain_id: Some(9999.into()), + gas_limit: 99_000_000u64.into(), + code_size_limit: Some(100000), + gas_price: Some(999), + block_base_fee_per_gas: 10, + block_coinbase: Address::random(), + block_timestamp: 10, + block_difficulty: 10, + block_prevrandao: H256::random(), + block_gas_limit: Some(100u64.into()), + memory_limit: 2u64.pow(25), + eth_rpc_url: Some("localhost".to_string()), + eth_rpc_jwt: None, + etherscan_api_key: None, + etherscan: Default::default(), + verbosity: 4, + remappings: vec![Remapping::from_str("forge-std=lib/forge-std/").unwrap().into()], + libraries: vec![ + "src/DssSpell.sol:DssExecLib:0x8De6DDbCd5053d32292AAA0D2105A32d108484a6".to_string() + ], + ignored_error_codes: vec![], + deny_warnings: false, + via_ir: true, + rpc_storage_caching: StorageCachingConfig { + chains: CachedChains::None, + endpoints: CachedEndpoints::Remote, + }, + no_storage_caching: true, + no_rpc_rate_limit: true, + use_literal_content: false, + bytecode_hash: Default::default(), + cbor_metadata: true, + revert_strings: Some(RevertStrings::Strip), + sparse_mode: true, + allow_paths: vec![], + include_paths: vec![], + rpc_endpoints: Default::default(), + build_info: false, + build_info_path: None, + fmt: Default::default(), + doc: Default::default(), + fs_permissions: Default::default(), + cancun: true, + __non_exhaustive: (), + __warnings: vec![], + }; + prj.write_config(input.clone()); + let config = cmd.config(); + pretty_assertions::assert_eq!(input, config); +}); + +// tests config gets printed to std out +forgetest!( + #[serial_test::serial] + can_show_config, + |prj: TestProject, mut cmd: TestCommand| { + cmd.arg("config"); + let expected = + Config::load_with_root(prj.root()).to_string_pretty().unwrap().trim().to_string(); + assert_eq!(expected, cmd.stdout().trim().to_string()); + } +); + +// checks that config works +// - foundry.toml is properly generated +// - paths are resolved properly +// - config supports overrides from env, and cli +forgetest_init!( + #[serial_test::serial] + can_override_config, + |prj: TestProject, mut cmd: TestCommand| { + cmd.set_current_dir(prj.root()); + let foundry_toml = prj.root().join(Config::FILE_NAME); + assert!(foundry_toml.exists()); + + let profile = Config::load_with_root(prj.root()); + // ensure that the auto-generated internal remapping for forge-std's ds-test exists + assert_eq!(profile.remappings.len(), 2); + pretty_eq!("ds-test/=lib/forge-std/lib/ds-test/src/", profile.remappings[0].to_string()); + + // ensure remappings contain test + pretty_eq!("ds-test/=lib/forge-std/lib/ds-test/src/", profile.remappings[0].to_string()); + // the loaded config has resolved, absolute paths + pretty_eq!( + "ds-test/=lib/forge-std/lib/ds-test/src/", + Remapping::from(profile.remappings[0].clone()).to_string() + ); + + cmd.arg("config"); + let expected = profile.to_string_pretty().unwrap(); + pretty_eq!(expected.trim().to_string(), cmd.stdout().trim().to_string()); + + // remappings work + let remappings_txt = + prj.create_file("remappings.txt", "ds-test/=lib/forge-std/lib/ds-test/from-file/"); + let config = forge_utils::load_config_with_root(Some(prj.root().into())); + pretty_eq!( + format!( + "ds-test/={}/", + prj.root().join("lib/forge-std/lib/ds-test/from-file").to_slash_lossy() + ), + Remapping::from(config.remappings[0].clone()).to_string() + ); + + // env vars work + std::env::set_var("DAPP_REMAPPINGS", "ds-test/=lib/forge-std/lib/ds-test/from-env/"); + let config = forge_utils::load_config_with_root(Some(prj.root().into())); + pretty_eq!( + format!( + "ds-test/={}/", + prj.root().join("lib/forge-std/lib/ds-test/from-env").to_slash_lossy() + ), + Remapping::from(config.remappings[0].clone()).to_string() + ); + + let config = + prj.config_from_output(["--remappings", "ds-test/=lib/forge-std/lib/ds-test/from-cli"]); + pretty_eq!( + format!( + "ds-test/={}/", + prj.root().join("lib/forge-std/lib/ds-test/from-cli").to_slash_lossy() + ), + Remapping::from(config.remappings[0].clone()).to_string() + ); + + let config = prj.config_from_output(["--remappings", "other-key/=lib/other/"]); + assert_eq!(config.remappings.len(), 3); + pretty_eq!( + format!("other-key/={}/", prj.root().join("lib/other").to_slash_lossy()), + // As CLI has the higher priority, it'll be found at the first slot. + Remapping::from(config.remappings[0].clone()).to_string() + ); + + std::env::remove_var("DAPP_REMAPPINGS"); + pretty_err(&remappings_txt, fs::remove_file(&remappings_txt)); + + cmd.set_cmd(prj.forge_bin()).args(["config", "--basic"]); + let expected = profile.into_basic().to_string_pretty().unwrap(); + pretty_eq!(expected.trim().to_string(), cmd.stdout().trim().to_string()); + } +); + +forgetest_init!( + #[serial_test::serial] + can_parse_remappings_correctly, + |prj: TestProject, mut cmd: TestCommand| { + cmd.set_current_dir(prj.root()); + let foundry_toml = prj.root().join(Config::FILE_NAME); + assert!(foundry_toml.exists()); + + let profile = Config::load_with_root(prj.root()); + // ensure that the auto-generated internal remapping for forge-std's ds-test exists + assert_eq!(profile.remappings.len(), 2); + let [r, _] = &profile.remappings[..] else { unreachable!() }; + pretty_eq!("ds-test/=lib/forge-std/lib/ds-test/src/", r.to_string()); + + // the loaded config has resolved, absolute paths + pretty_eq!( + "ds-test/=lib/forge-std/lib/ds-test/src/", + Remapping::from(r.clone()).to_string() + ); + + cmd.arg("config"); + let expected = profile.to_string_pretty().unwrap(); + pretty_eq!(expected.trim().to_string(), cmd.stdout().trim().to_string()); + + let install = |cmd: &mut TestCommand, dep: &str| { + cmd.forge_fuse().args(["install", dep, "--no-commit"]); + cmd.assert_non_empty_stdout(); + }; + + install(&mut cmd, "transmissions11/solmate"); + let profile = Config::load_with_root(prj.root()); + // remappings work + let remappings_txt = prj.create_file( + "remappings.txt", + "solmate/=lib/solmate/src/\nsolmate-contracts/=lib/solmate/src/", + ); + let config = forge_utils::load_config_with_root(Some(prj.root().into())); + // trailing slashes are removed on windows `to_slash_lossy` + let path = prj.root().join("lib/solmate/src/").to_slash_lossy().into_owned(); + #[cfg(windows)] + let path = path + "/"; + pretty_eq!( + format!("solmate/={path}"), + Remapping::from(config.remappings[0].clone()).to_string() + ); + // As this is an user-generated remapping, it is not removed, even if it points to the same + // location. + pretty_eq!( + format!("solmate-contracts/={path}"), + Remapping::from(config.remappings[1].clone()).to_string() + ); + pretty_err(&remappings_txt, fs::remove_file(&remappings_txt)); + + cmd.set_cmd(prj.forge_bin()).args(["config", "--basic"]); + let expected = profile.into_basic().to_string_pretty().unwrap(); + pretty_eq!(expected.trim().to_string(), cmd.stdout().trim().to_string()); + } +); + +forgetest_init!( + #[serial_test::serial] + can_detect_config_vals, + |prj: TestProject, _cmd: TestCommand| { + let url = "http://127.0.0.1:8545"; + let config = prj.config_from_output(["--no-auto-detect", "--rpc-url", url]); + assert!(!config.auto_detect_solc); + assert_eq!(config.eth_rpc_url, Some(url.to_string())); + + let mut config = Config::load_with_root(prj.root()); + config.eth_rpc_url = Some("http://127.0.0.1:8545".to_string()); + config.auto_detect_solc = false; + // write to `foundry.toml` + prj.create_file( + Config::FILE_NAME, + &config.to_string_pretty().unwrap().replace("eth_rpc_url", "eth-rpc-url"), + ); + let config = prj.config_from_output(["--force"]); + assert!(!config.auto_detect_solc); + assert_eq!(config.eth_rpc_url, Some(url.to_string())); + } +); + +// checks that `clean` removes dapptools style paths +forgetest_init!( + #[serial_test::serial] + can_get_evm_opts, + |prj: TestProject, _cmd: TestCommand| { + let url = "http://127.0.0.1:8545"; + let config = prj.config_from_output(["--rpc-url", url, "--ffi"]); + assert_eq!(config.eth_rpc_url, Some(url.to_string())); + assert!(config.ffi); + + std::env::set_var("FOUNDRY_ETH_RPC_URL", url); + let figment = Config::figment_with_root(prj.root()).merge(("debug", false)); + let evm_opts: EvmOpts = figment.extract().unwrap(); + assert_eq!(evm_opts.fork_url, Some(url.to_string())); + std::env::remove_var("FOUNDRY_ETH_RPC_URL"); + } +); + +// checks that we can set various config values +forgetest_init!(can_set_config_values, |prj: TestProject, _cmd: TestCommand| { + let config = prj.config_from_output(["--via-ir"]); + assert!(config.via_ir); +}); + +// tests that solc can be explicitly set +forgetest!(can_set_solc_explicitly, |prj: TestProject, mut cmd: TestCommand| { + prj.inner() + .add_source( + "Foo", + r" +// SPDX-License-Identifier: UNLICENSED +pragma solidity >0.8.9; +contract Greeter {} + ", + ) + .unwrap(); + + // explicitly set to run with 0.8.10 + let config = Config { solc: Some("0.8.10".into()), ..Default::default() }; + prj.write_config(config); + + cmd.arg("build"); + + assert!(cmd.stdout_lossy().ends_with( + " +Compiler run successful! +", + )); +}); + +// tests that `--use ` works +forgetest!(can_use_solc, |prj: TestProject, mut cmd: TestCommand| { + prj.inner() + .add_source( + "Foo", + r" +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.7.0; +contract Foo {} + ", + ) + .unwrap(); + + cmd.args(["build", "--use", "0.7.1"]); + + let stdout = cmd.stdout_lossy(); + assert!(stdout.contains("Compiler run successful")); + + cmd.forge_fuse().args(["build", "--force", "--use", "solc:0.7.1"]).root_arg(); + + assert!(stdout.contains("Compiler run successful")); + + // fails to use solc that does not exist + cmd.forge_fuse().args(["build", "--use", "this/solc/does/not/exist"]); + assert!(cmd.stderr_lossy().contains("this/solc/does/not/exist does not exist")); + + // 0.7.1 was installed in previous step, so we can use the path to this directly + let local_solc = ethers::solc::Solc::find_svm_installed_version("0.7.1") + .unwrap() + .expect("solc 0.7.1 is installed"); + cmd.forge_fuse().args(["build", "--force", "--use"]).arg(local_solc.solc).root_arg(); + assert!(stdout.contains("Compiler run successful")); +}); + +// test to ensure yul optimizer can be set as intended +forgetest!(can_set_yul_optimizer, |prj: TestProject, mut cmd: TestCommand| { + prj.inner() + .add_source( + "Foo", + r" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +contract Foo { + function bar() public pure { + assembly { + let result_start := msize() + } + } +} + ", + ) + .unwrap(); + + cmd.arg("build"); + cmd.unchecked_output().stderr_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_set_yul_optimizer.stderr"), + ); + + // disable yul optimizer explicitly + let config = Config { + optimizer_details: Some(OptimizerDetails { yul: Some(false), ..Default::default() }), + ..Default::default() + }; + prj.write_config(config); + + assert!(cmd.stdout_lossy().ends_with( + " +Compiler run successful! +", + )); +}); + +// tests that the lib triple can be parsed +forgetest_init!(can_parse_dapp_libraries, |_prj: TestProject, mut cmd: TestCommand| { + cmd.set_env( + "DAPP_LIBRARIES", + "src/DssSpell.sol:DssExecLib:0x8De6DDbCd5053d32292AAA0D2105A32d108484a6", + ); + let config = cmd.config(); + assert_eq!( + config.libraries, + vec!["src/DssSpell.sol:DssExecLib:0x8De6DDbCd5053d32292AAA0D2105A32d108484a6".to_string(),] + ); +}); + +// test that optimizer runs works +forgetest!(can_set_optimizer_runs, |prj: TestProject, mut cmd: TestCommand| { + // explicitly set optimizer runs + let config = Config { optimizer_runs: 1337, ..Default::default() }; + prj.write_config(config); + + let config = cmd.config(); + assert_eq!(config.optimizer_runs, 1337); + + let config = prj.config_from_output(["--optimizer-runs", "300"]); + assert_eq!(config.optimizer_runs, 300); +}); + +// test that gas_price can be set +forgetest!(can_set_gas_price, |prj: TestProject, mut cmd: TestCommand| { + // explicitly set gas_price + let config = Config { gas_price: Some(1337), ..Default::default() }; + prj.write_config(config); + + let config = cmd.config(); + assert_eq!(config.gas_price, Some(1337)); + + let config = prj.config_from_output(["--gas-price", "300"]); + assert_eq!(config.gas_price, Some(300)); +}); + +// test that we can detect remappings from foundry.toml +forgetest_init!(can_detect_lib_foundry_toml, |prj: TestProject, mut cmd: TestCommand| { + let config = cmd.config(); + let remappings = config.remappings.iter().cloned().map(Remapping::from).collect::>(); + pretty_assertions::assert_eq!( + remappings, + vec![ + // global + "ds-test/=lib/forge-std/lib/ds-test/src/".parse().unwrap(), + "forge-std/=lib/forge-std/src/".parse().unwrap(), + ] + ); + + // create a new lib directly in the `lib` folder with a remapping + let mut config = config; + config.remappings = vec![Remapping::from_str("nested/=lib/nested").unwrap().into()]; + let nested = prj.paths().libraries[0].join("nested-lib"); + pretty_err(&nested, fs::create_dir_all(&nested)); + let toml_file = nested.join("foundry.toml"); + pretty_err(&toml_file, fs::write(&toml_file, config.to_string_pretty().unwrap())); + + let config = cmd.config(); + let remappings = config.remappings.iter().cloned().map(Remapping::from).collect::>(); + pretty_assertions::assert_eq!( + remappings, + vec![ + // default + "ds-test/=lib/forge-std/lib/ds-test/src/".parse().unwrap(), + "forge-std/=lib/forge-std/src/".parse().unwrap(), + // remapping is local to the lib + "nested-lib/=lib/nested-lib/src/".parse().unwrap(), + // global + "nested/=lib/nested-lib/lib/nested/".parse().unwrap(), + ] + ); + + // nest another lib under the already nested lib + let mut config = config; + config.remappings = vec![Remapping::from_str("nested-twice/=lib/nested-twice").unwrap().into()]; + let nested = nested.join("lib/another-lib"); + pretty_err(&nested, fs::create_dir_all(&nested)); + let toml_file = nested.join("foundry.toml"); + pretty_err(&toml_file, fs::write(&toml_file, config.to_string_pretty().unwrap())); + + let another_config = cmd.config(); + let remappings = + another_config.remappings.iter().cloned().map(Remapping::from).collect::>(); + pretty_assertions::assert_eq!( + remappings, + vec![ + // local to the lib + "another-lib/=lib/nested-lib/lib/another-lib/src/".parse().unwrap(), + // global + "ds-test/=lib/forge-std/lib/ds-test/src/".parse().unwrap(), + "forge-std/=lib/forge-std/src/".parse().unwrap(), + "nested-lib/=lib/nested-lib/src/".parse().unwrap(), + // remappings local to the lib + "nested-twice/=lib/nested-lib/lib/another-lib/lib/nested-twice/".parse().unwrap(), + "nested/=lib/nested-lib/lib/nested/".parse().unwrap(), + ] + ); + + config.src = "custom-source-dir".into(); + pretty_err(&toml_file, fs::write(&toml_file, config.to_string_pretty().unwrap())); + let config = cmd.config(); + let remappings = config.remappings.iter().cloned().map(Remapping::from).collect::>(); + pretty_assertions::assert_eq!( + remappings, + vec![ + // local to the lib + "another-lib/=lib/nested-lib/lib/another-lib/custom-source-dir/".parse().unwrap(), + // global + "ds-test/=lib/forge-std/lib/ds-test/src/".parse().unwrap(), + "forge-std/=lib/forge-std/src/".parse().unwrap(), + "nested-lib/=lib/nested-lib/src/".parse().unwrap(), + // remappings local to the lib + "nested-twice/=lib/nested-lib/lib/another-lib/lib/nested-twice/".parse().unwrap(), + "nested/=lib/nested-lib/lib/nested/".parse().unwrap(), + ] + ); +}); + +// test remappings with closer paths are prioritised +// so that `dep/=lib/a/src` will take precedent over `dep/=lib/a/lib/b/src` +forgetest_init!( + #[serial_test::serial] + can_prioritise_closer_lib_remappings, + |prj: TestProject, mut cmd: TestCommand| { + let config = cmd.config(); + + // create a new lib directly in the `lib` folder with conflicting remapping `forge-std/` + let mut config = config; + config.remappings = + vec![Remapping::from_str("forge-std/=lib/forge-std/src/").unwrap().into()]; + let nested = prj.paths().libraries[0].join("dep1"); + pretty_err(&nested, fs::create_dir_all(&nested)); + let toml_file = nested.join("foundry.toml"); + pretty_err(&toml_file, fs::write(&toml_file, config.to_string_pretty().unwrap())); + + let config = cmd.config(); + let remappings = config.get_all_remappings(); + pretty_assertions::assert_eq!( + remappings, + vec![ + "dep1/=lib/dep1/src/".parse().unwrap(), + "ds-test/=lib/forge-std/lib/ds-test/src/".parse().unwrap(), + "forge-std/=lib/forge-std/src/".parse().unwrap() + ] + ); + } +); + +// test to check that foundry.toml libs section updates on install +forgetest!(can_update_libs_section, |prj: TestProject, mut cmd: TestCommand| { + cmd.git_init(); + + // explicitly set gas_price + let init = Config { libs: vec!["node_modules".into()], ..Default::default() }; + prj.write_config(init); + + cmd.args(["install", "foundry-rs/forge-std", "--no-commit"]); + cmd.assert_non_empty_stdout(); + + let config = cmd.forge_fuse().config(); + // `lib` was added automatically + let expected = vec![PathBuf::from("node_modules"), PathBuf::from("lib")]; + assert_eq!(config.libs, expected); + + // additional install don't edit `libs` + cmd.forge_fuse().args(["install", "dapphub/ds-test", "--no-commit"]); + cmd.assert_non_empty_stdout(); + + let config = cmd.forge_fuse().config(); + assert_eq!(config.libs, expected); +}); + +// test to check that loading the config emits warnings on the root foundry.toml and +// is silent for any libs +forgetest!(config_emit_warnings, |prj: TestProject, mut cmd: TestCommand| { + cmd.git_init(); + + cmd.args(["install", "foundry-rs/forge-std", "--no-commit"]); + cmd.assert_non_empty_stdout(); + + let faulty_toml = r"[default] + src = 'src' + out = 'out' + libs = ['lib']"; + + fs::write(prj.root().join("foundry.toml"), faulty_toml).unwrap(); + fs::write(prj.root().join("lib").join("forge-std").join("foundry.toml"), faulty_toml).unwrap(); + + cmd.forge_fuse().args(["config"]); + let output = cmd.execute(); + assert!(output.status.success()); + assert_eq!( + String::from_utf8_lossy(&output.stderr) + .lines() + .filter(|line| { line.contains("Unknown section [default]") }) + .count(), + 1 + ) +}); + +forgetest_init!(can_skip_remappings_auto_detection, |prj: TestProject, mut cmd: TestCommand| { + // explicitly set remapping and libraries + let config = Config { + remappings: vec![Remapping::from_str("remapping/=lib/remapping/").unwrap().into()], + auto_detect_remappings: false, + ..Default::default() + }; + prj.write_config(config); + + let config = cmd.config(); + + // only loads remappings from foundry.toml + assert_eq!(config.remappings.len(), 1); + assert_eq!("remapping/=lib/remapping/", config.remappings[0].to_string()); +}); diff --git a/crates/zkforge/tests/cli/constants.rs b/crates/zkforge/tests/cli/constants.rs new file mode 100644 index 000000000..d0ac96686 --- /dev/null +++ b/crates/zkforge/tests/cli/constants.rs @@ -0,0 +1,11 @@ +//! various constants + +pub const TEMPLATE_CONTRACT: &str = "Counter"; + +pub const TEMPLATE_TEST_CONTRACT: &str = "CounterTest"; + +pub const TEMPLATE_CONTRACT_ARTIFACT_BASE: &str = "Counter.sol/Counter"; + +pub const TEMPLATE_CONTRACT_ARTIFACT_JSON: &str = "Counter.sol/Counter.json"; + +pub const TEMPLATE_TEST_CONTRACT_ARTIFACT_JSON: &str = "Counter.t.sol/CounterTest.json"; diff --git a/crates/zkforge/tests/cli/coverage.rs b/crates/zkforge/tests/cli/coverage.rs new file mode 100644 index 000000000..eb950832d --- /dev/null +++ b/crates/zkforge/tests/cli/coverage.rs @@ -0,0 +1,16 @@ +use foundry_test_utils::{forgetest, TestCommand, TestProject}; + +forgetest!(basic_coverage, |_prj: TestProject, mut cmd: TestCommand| { + cmd.args(["coverage"]); + cmd.assert_success(); +}); + +forgetest!(report_file_coverage, |prj: TestProject, mut cmd: TestCommand| { + cmd.arg("coverage").args([ + "--report".to_string(), + "lcov".to_string(), + "--report-file".to_string(), + prj.root().join("lcov.info").to_str().unwrap().to_string(), + ]); + cmd.assert_success(); +}); diff --git a/crates/zkforge/tests/cli/create.rs b/crates/zkforge/tests/cli/create.rs new file mode 100644 index 000000000..acdbc06eb --- /dev/null +++ b/crates/zkforge/tests/cli/create.rs @@ -0,0 +1,220 @@ +//! Contains various tests for checking the `forge create` subcommand + +use crate::{ + constants::*, + utils::{self, EnvExternalities}, +}; +use anvil::{spawn, NodeConfig}; +use ethers::{ + solc::{artifacts::BytecodeHash, remappings::Remapping}, + types::Address, +}; +use foundry_config::Config; +use foundry_test_utils::{ + forgetest, forgetest_async, + util::{OutputExt, TestCommand, TestProject}, +}; +use std::{path::PathBuf, str::FromStr}; + +/// This will insert _dummy_ contract that uses a library +/// +/// **NOTE** This is intended to be linked against a random address and won't actually work. The +/// purpose of this is _only_ to make sure we can deploy contracts linked against addresses. +/// +/// This will create a library `remapping/MyLib.sol:MyLib` +/// +/// returns the contract argument for the create command +fn setup_with_simple_remapping(prj: &TestProject) -> String { + // explicitly set remapping and libraries + let config = Config { + remappings: vec![Remapping::from_str("remapping/=lib/remapping/").unwrap().into()], + libraries: vec![format!("remapping/MyLib.sol:MyLib:{:?}", Address::random())], + ..Default::default() + }; + prj.write_config(config); + + prj.inner() + .add_source( + "LinkTest", + r#" +// SPDX-License-Identifier: MIT +import "remapping/MyLib.sol"; +contract LinkTest { + function foo() public returns (uint256) { + return MyLib.foobar(1); + } +} +"#, + ) + .unwrap(); + + prj.inner() + .add_lib( + "remapping/MyLib", + r" +// SPDX-License-Identifier: MIT +library MyLib { + function foobar(uint256 a) public view returns (uint256) { + return a * 100; + } +} +", + ) + .unwrap(); + + "src/LinkTest.sol:LinkTest".to_string() +} + +fn setup_oracle(prj: &TestProject) -> String { + let config = Config { + libraries: vec![format!( + "./src/libraries/ChainlinkTWAP.sol:ChainlinkTWAP:{:?}", + Address::random() + )], + ..Default::default() + }; + prj.write_config(config); + + prj.inner() + .add_source( + "Contract", + r#" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; +import {ChainlinkTWAP} from "./libraries/ChainlinkTWAP.sol"; +contract Contract { + function getPrice() public view returns (int latest) { + latest = ChainlinkTWAP.getLatestPrice(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); + } +} +"#, + ) + .unwrap(); + + prj.inner() + .add_source( + "libraries/ChainlinkTWAP", + r" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +library ChainlinkTWAP { + function getLatestPrice(address base) public view returns (int256) { + return 0; + } +} +", + ) + .unwrap(); + + "src/Contract.sol:Contract".to_string() +} + +/// configures the `TestProject` with the given closure and calls the `forge create` command +fn create_on_chain(info: Option, prj: TestProject, mut cmd: TestCommand, f: F) +where + F: FnOnce(&TestProject) -> String, +{ + if let Some(info) = info { + let contract_path = f(&prj); + cmd.arg("create"); + cmd.args(info.create_args()).arg(contract_path); + + let out = cmd.stdout_lossy(); + let _address = utils::parse_deployed_address(out.as_str()) + .unwrap_or_else(|| panic!("Failed to parse deployer {out}")); + } +} + +// tests `forge` create on goerli if correct env vars are set +forgetest!(can_create_simple_on_goerli, |prj: TestProject, cmd: TestCommand| { + create_on_chain(EnvExternalities::goerli(), prj, cmd, setup_with_simple_remapping); +}); + +// tests `forge` create on goerli if correct env vars are set +forgetest!(can_create_oracle_on_goerli, |prj: TestProject, cmd: TestCommand| { + create_on_chain(EnvExternalities::goerli(), prj, cmd, setup_oracle); +}); + +// tests `forge` create on mumbai if correct env vars are set +forgetest!(can_create_oracle_on_mumbai, |prj: TestProject, cmd: TestCommand| { + create_on_chain(EnvExternalities::mumbai(), prj, cmd, setup_oracle); +}); + +// tests that we can deploy the template contract +forgetest_async!( + #[serial_test::serial] + can_create_template_contract, + |prj: TestProject, mut cmd: TestCommand| async move { + let (_api, handle) = spawn(NodeConfig::test()).await; + let rpc = handle.http_endpoint(); + let wallet = handle.dev_wallets().next().unwrap(); + let pk = hex::encode(wallet.signer().to_bytes()); + cmd.args(["init", "--force"]); + cmd.assert_non_empty_stdout(); + + // explicitly byte code hash for consistent checks + let config = Config { bytecode_hash: BytecodeHash::None, ..Default::default() }; + prj.write_config(config); + + cmd.forge_fuse().args([ + "create", + format!("./src/{TEMPLATE_CONTRACT}.sol:{TEMPLATE_CONTRACT}").as_str(), + "--use", + "solc:0.8.15", + "--rpc-url", + rpc.as_str(), + "--private-key", + pk.as_str(), + ]); + + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_create_template_contract.stdout"), + ); + + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_create_template_contract-2nd.stdout"), + ); + } +); + +// tests that we can deploy the template contract +forgetest_async!( + #[serial_test::serial] + can_create_using_unlocked, + |prj: TestProject, mut cmd: TestCommand| async move { + let (_api, handle) = spawn(NodeConfig::test()).await; + let rpc = handle.http_endpoint(); + let dev = handle.dev_accounts().next().unwrap(); + cmd.args(["init", "--force"]); + cmd.assert_non_empty_stdout(); + + // explicitly byte code hash for consistent checks + let config = Config { bytecode_hash: BytecodeHash::None, ..Default::default() }; + prj.write_config(config); + + cmd.forge_fuse().args([ + "create", + format!("./src/{TEMPLATE_CONTRACT}.sol:{TEMPLATE_CONTRACT}").as_str(), + "--use", + "solc:0.8.15", + "--rpc-url", + rpc.as_str(), + "--from", + format!("{dev:?}").as_str(), + "--unlocked", + ]); + + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_create_using_unlocked.stdout"), + ); + + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_create_using_unlocked-2nd.stdout"), + ); + } +); diff --git a/crates/zkforge/tests/cli/doc.rs b/crates/zkforge/tests/cli/doc.rs new file mode 100644 index 000000000..9ad34c1df --- /dev/null +++ b/crates/zkforge/tests/cli/doc.rs @@ -0,0 +1,11 @@ +use foundry_test_utils::util::{setup_forge_remote, RemoteProject}; + +#[test] +fn can_generate_solmate_docs() { + let (prj, _) = + setup_forge_remote(RemoteProject::new("transmissions11/solmate").set_build(false)); + prj.forge_command() + .args(["doc", "--build"]) + .ensure_execute_success() + .expect("`forge doc` failed"); +} diff --git a/crates/zkforge/tests/cli/ext_integration.rs b/crates/zkforge/tests/cli/ext_integration.rs new file mode 100644 index 000000000..049d8eca3 --- /dev/null +++ b/crates/zkforge/tests/cli/ext_integration.rs @@ -0,0 +1,26 @@ +use foundry_test_utils::forgetest_external; + +forgetest_external!(solmate, "transmissions11/solmate"); +forgetest_external!(prb_math, "PaulRBerg/prb-math"); +forgetest_external!(prb_proxy, "PaulRBerg/prb-proxy"); +forgetest_external!(solady, "Vectorized/solady"); +forgetest_external!( + geb, + "reflexer-labs/geb", + &["--chain-id", "99", "--sender", "0x00a329c0648769A73afAc7F9381E08FB43dBEA72"] +); +forgetest_external!(stringutils, "Arachnid/solidity-stringutils"); +forgetest_external!(lootloose, "gakonst/lootloose"); +forgetest_external!(lil_web3, "m1guelpf/lil-web3"); + +// Forking tests + +forgetest_external!(multicall, "makerdao/multicall", &["--block-number", "1"]); +forgetest_external!( + drai, + "mds1/drai", + 13633752, + &["--chain-id", "99", "--sender", "0x00a329c0648769A73afAc7F9381E08FB43dBEA72"] +); +forgetest_external!(gunilev, "hexonaut/guni-lev", 13633752); +forgetest_external!(convex, "mds1/convex-shutdown-simulation", 14445961); diff --git a/crates/zkforge/tests/cli/heavy_integration.rs b/crates/zkforge/tests/cli/heavy_integration.rs new file mode 100644 index 000000000..7363b0ca4 --- /dev/null +++ b/crates/zkforge/tests/cli/heavy_integration.rs @@ -0,0 +1,5 @@ +//! Heavy integration tests that can take an hour to run or more. + +use foundry_test_utils::forgetest_external; + +forgetest_external!(maple, "maple-labs/maple-core-v2"); diff --git a/crates/zkforge/tests/cli/main.rs b/crates/zkforge/tests/cli/main.rs new file mode 100644 index 000000000..69868c93a --- /dev/null +++ b/crates/zkforge/tests/cli/main.rs @@ -0,0 +1,19 @@ +pub mod constants; +pub mod utils; + +mod cache; +mod cmd; +mod config; +mod coverage; +mod create; +mod doc; +mod multi_script; +mod script; +mod svm; +mod test_cmd; +mod verify; + +mod ext_integration; + +#[cfg(feature = "heavy-integration-tests")] +mod heavy_integration; diff --git a/crates/zkforge/tests/cli/multi_script.rs b/crates/zkforge/tests/cli/multi_script.rs new file mode 100644 index 000000000..2320b7938 --- /dev/null +++ b/crates/zkforge/tests/cli/multi_script.rs @@ -0,0 +1,88 @@ +//! Contains various tests related to forge script +use anvil::{spawn, NodeConfig}; +use foundry_test_utils::{ + forgetest_async, + util::{TestCommand, TestProject}, + ScriptOutcome, ScriptTester, +}; +use foundry_utils::types::ToEthers; + +forgetest_async!( + can_deploy_multi_chain_script_without_lib, + |prj: TestProject, cmd: TestCommand| async move { + let (api1, handle1) = spawn(NodeConfig::test()).await; + let (api2, handle2) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast_without_endpoint(cmd, prj.root()); + + tester + .load_private_keys(vec![0, 1]) + .await + .add_sig("MultiChainBroadcastNoLink", "deploy(string memory,string memory)") + .args(vec![handle1.http_endpoint(), handle2.http_endpoint()]) + .broadcast(ScriptOutcome::OkBroadcast); + + assert!( + 1 == api1 + .transaction_count(tester.accounts_pub[0].to_ethers(), None) + .await + .unwrap() + .as_u32() + ); + assert!( + 1 == api1 + .transaction_count(tester.accounts_pub[1].to_ethers(), None) + .await + .unwrap() + .as_u32() + ); + + assert!( + 2 == api2 + .transaction_count(tester.accounts_pub[0].to_ethers(), None) + .await + .unwrap() + .as_u32() + ); + assert!( + 3 == api2 + .transaction_count(tester.accounts_pub[1].to_ethers(), None) + .await + .unwrap() + .as_u32() + ); + } +); + +forgetest_async!( + can_not_deploy_multi_chain_script_with_lib, + |prj: TestProject, cmd: TestCommand| async move { + let (_, handle1) = spawn(NodeConfig::test()).await; + let (_, handle2) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast_without_endpoint(cmd, prj.root()); + + tester + .load_private_keys(vec![0, 1]) + .await + .add_deployer(0) + .add_sig("MultiChainBroadcastLink", "deploy(string memory,string memory)") + .args(vec![handle1.http_endpoint(), handle2.http_endpoint()]) + .broadcast(ScriptOutcome::UnsupportedLibraries); + } +); + +forgetest_async!( + can_not_change_fork_during_broadcast, + |prj: TestProject, cmd: TestCommand| async move { + let (_, handle1) = spawn(NodeConfig::test()).await; + let (_, handle2) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast_without_endpoint(cmd, prj.root()); + + tester + .load_private_keys(vec![0, 1]) + .await + .add_deployer(0) + .add_sig("MultiChainBroadcastNoLink", "deployError(string memory,string memory)") + .args(vec![handle1.http_endpoint(), handle2.http_endpoint()]) + .broadcast(ScriptOutcome::ErrorSelectForkOnBroadcast); + } +); diff --git a/crates/zkforge/tests/cli/script.rs b/crates/zkforge/tests/cli/script.rs new file mode 100644 index 000000000..0cd73f95b --- /dev/null +++ b/crates/zkforge/tests/cli/script.rs @@ -0,0 +1,1133 @@ +//! Contains various tests related to forge script +use crate::constants::TEMPLATE_CONTRACT; +use anvil::{spawn, NodeConfig}; +use ethers::abi::Address; +use foundry_config::Config; +use foundry_test_utils::{ + forgetest, forgetest_async, forgetest_init, + util::{OutputExt, TestCommand, TestProject}, + ScriptOutcome, ScriptTester, +}; +use foundry_utils::{rpc, types::ToAlloy}; +use regex::Regex; +use serde_json::Value; +use std::{env, path::PathBuf, str::FromStr}; + +// Tests that fork cheat codes can be used in script +forgetest_init!( + #[ignore] + can_use_fork_cheat_codes_in_script, + |prj: TestProject, mut cmd: TestCommand| { + let script = prj + .inner() + .add_source( + "Foo", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.10; + +import "forge-std/Script.sol"; + +contract ContractScript is Script { + function setUp() public {} + + function run() public { + uint256 fork = vm.activeFork(); + vm.rollFork(11469702); + } +} + "#, + ) + .unwrap(); + + let rpc = foundry_utils::rpc::next_http_rpc_endpoint(); + + cmd.arg("script").arg(script).args(["--fork-url", rpc.as_str(), "-vvvv"]); + } +); + +// Tests that the `run` command works correctly +forgetest!(can_execute_script_command2, |prj: TestProject, mut cmd: TestCommand| { + let script = prj + .inner() + .add_source( + "Foo", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +contract Demo { + event log_string(string); + function run() external { + emit log_string("script ran"); + } +} + "#, + ) + .unwrap(); + + cmd.arg("script").arg(script); + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_execute_script_command.stdout"), + ); +}); + +// Tests that the `run` command works correctly when path *and* script name is specified +forgetest!(can_execute_script_command_fqn, |prj: TestProject, mut cmd: TestCommand| { + let script = prj + .inner() + .add_source( + "Foo", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +contract Demo { + event log_string(string); + function run() external { + emit log_string("script ran"); + } +} + "#, + ) + .unwrap(); + + cmd.arg("script").arg(format!("{}:Demo", script.display())); + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_execute_script_command_fqn.stdout"), + ); +}); + +// Tests that the run command can run arbitrary functions +forgetest!(can_execute_script_command_with_sig, |prj: TestProject, mut cmd: TestCommand| { + let script = prj + .inner() + .add_source( + "Foo", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +contract Demo { + event log_string(string); + function myFunction() external { + emit log_string("script ran"); + } +} + "#, + ) + .unwrap(); + + cmd.arg("script").arg(script).arg("--sig").arg("myFunction()"); + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_execute_script_command_with_sig.stdout"), + ); +}); + +// Tests that the manually specified gas limit is used when using the --unlocked option +forgetest_async!( + can_execute_script_command_with_manual_gas_limit_unlocked, + |prj: TestProject, mut cmd: TestCommand| async move { + foundry_test_utils::util::initialize(prj.root()); + let deploy_script = prj + .inner() + .add_source( + "Foo", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +import "forge-std/Script.sol"; + +contract GasWaster { + function wasteGas(uint256 minGas) public { + require(gasleft() >= minGas, "Gas left needs to be higher"); + } +} +contract DeployScript is Script { + function run() external returns (uint256 result, uint8) { + vm.startBroadcast(); + GasWaster gasWaster = new GasWaster(); + gasWaster.wasteGas{gas: 500000}(200000); + } +} + "#, + ) + .unwrap(); + + let deploy_contract = deploy_script.display().to_string() + ":DeployScript"; + + let node_config = NodeConfig::test() + .with_eth_rpc_url(Some(rpc::next_http_archive_rpc_endpoint())) + .silent(); + let (_api, handle) = spawn(node_config).await; + let dev = handle.dev_accounts().next().unwrap(); + cmd.set_current_dir(prj.root()); + + cmd.args([ + "script", + &deploy_contract, + "--root", + prj.root().to_str().unwrap(), + "--fork-url", + &handle.http_endpoint(), + "--sender", + format!("{dev:?}").as_str(), + "-vvvvv", + "--slow", + "--broadcast", + "--unlocked", + ]); + + let output = cmd.stdout_lossy(); + assert!(output.contains("ONCHAIN EXECUTION COMPLETE & SUCCESSFUL")); + assert!(output.contains("Gas limit was set in script to 500000")); + } +); + +// Tests that the manually specified gas limit is used. +forgetest_async!( + can_execute_script_command_with_manual_gas_limit, + |prj: TestProject, mut cmd: TestCommand| async move { + foundry_test_utils::util::initialize(prj.root()); + let deploy_script = prj + .inner() + .add_source( + "Foo", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +import "forge-std/Script.sol"; + +contract GasWaster { + function wasteGas(uint256 minGas) public { + require(gasleft() >= minGas, "Gas left needs to be higher"); + } +} +contract DeployScript is Script { + function run() external returns (uint256 result, uint8) { + vm.startBroadcast(); + GasWaster gasWaster = new GasWaster(); + gasWaster.wasteGas{gas: 500000}(200000); + } +} + "#, + ) + .unwrap(); + + let deploy_contract = deploy_script.display().to_string() + ":DeployScript"; + + let node_config = NodeConfig::test() + .with_eth_rpc_url(Some(rpc::next_http_archive_rpc_endpoint())) + .silent(); + let (_api, handle) = spawn(node_config).await; + let private_key = + "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".to_string(); + cmd.set_current_dir(prj.root()); + + cmd.args([ + "script", + &deploy_contract, + "--root", + prj.root().to_str().unwrap(), + "--fork-url", + &handle.http_endpoint(), + "-vvvvv", + "--slow", + "--broadcast", + "--private-key", + &private_key, + ]); + + let output = cmd.stdout_lossy(); + assert!(output.contains("ONCHAIN EXECUTION COMPLETE & SUCCESSFUL")); + assert!(output.contains("Gas limit was set in script to 500000")); + } +); + +// Tests that the run command can run functions with arguments +forgetest!(can_execute_script_command_with_args, |prj: TestProject, mut cmd: TestCommand| { + let script = prj + .inner() + .add_source( + "Foo", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +contract Demo { + event log_string(string); + event log_uint(uint); + function run(uint256 a, uint256 b) external { + emit log_string("script ran"); + emit log_uint(a); + emit log_uint(b); + } +} + "#, + ) + .unwrap(); + + cmd.arg("script").arg(script).arg("--sig").arg("run(uint256,uint256)").arg("1").arg("2"); + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_execute_script_command_with_args.stdout"), + ); +}); + +// Tests that the run command can run functions with return values +forgetest!(can_execute_script_command_with_returned, |prj: TestProject, mut cmd: TestCommand| { + let script = prj + .inner() + .add_source( + "Foo", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +contract Demo { + event log_string(string); + function run() external returns (uint256 result, uint8) { + emit log_string("script ran"); + return (255, 3); + } +}"#, + ) + .unwrap(); + cmd.arg("script").arg(script); + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_execute_script_command_with_returned.stdout"), + ); +}); + +forgetest_async!( + can_broadcast_script_skipping_simulation, + |prj: TestProject, mut cmd: TestCommand| async move { + foundry_test_utils::util::initialize(prj.root()); + // This example script would fail in on-chain simulation + let deploy_script = prj + .inner() + .add_source( + "DeployScript", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +import "forge-std/Script.sol"; + +contract HashChecker { + bytes32 public lastHash; + function update() public { + bytes32 newHash = blockhash(block.number - 1); + require(newHash != lastHash, "Hash didn't change"); + lastHash = newHash; + } + + function checkLastHash() public { + require(lastHash != bytes32(0), "Hash shouldn't be zero"); + } +} +contract DeployScript is Script { + function run() external returns (uint256 result, uint8) { + vm.startBroadcast(); + HashChecker hashChecker = new HashChecker(); + } +}"#, + ) + .unwrap(); + + let deploy_contract = deploy_script.display().to_string() + ":DeployScript"; + + let node_config = NodeConfig::test() + .with_eth_rpc_url(Some(rpc::next_http_archive_rpc_endpoint())) + .silent(); + let (_api, handle) = spawn(node_config).await; + let private_key = + "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".to_string(); + cmd.set_current_dir(prj.root()); + + cmd.args([ + "script", + &deploy_contract, + "--root", + prj.root().to_str().unwrap(), + "--fork-url", + &handle.http_endpoint(), + "-vvvvv", + "--broadcast", + "--slow", + "--skip-simulation", + "--private-key", + &private_key, + ]); + + let output = cmd.stdout_lossy(); + + assert!(output.contains("SKIPPING ON CHAIN SIMULATION")); + assert!(output.contains("ONCHAIN EXECUTION COMPLETE & SUCCESSFUL")); + + let run_log = + std::fs::read_to_string("broadcast/DeployScript.sol/1/run-latest.json").unwrap(); + let run_object: Value = serde_json::from_str(&run_log).unwrap(); + let contract_address = ethers::utils::to_checksum( + &run_object["receipts"][0]["contractAddress"].as_str().unwrap().parse().unwrap(), + None, + ); + + let run_code = r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +import "forge-std/Script.sol"; +import { HashChecker } from "./DeployScript.sol"; + +contract RunScript is Script { + function run() external returns (uint256 result, uint8) { + vm.startBroadcast(); + HashChecker hashChecker = HashChecker(CONTRACT_ADDRESS); + uint numUpdates = 8; + vm.roll(block.number - numUpdates); + for(uint i = 0; i < numUpdates; i++) { + vm.roll(block.number + 1); + hashChecker.update(); + hashChecker.checkLastHash(); + } + } +}"# + .replace("CONTRACT_ADDRESS", &contract_address); + + let run_script = prj.inner().add_source("RunScript", run_code).unwrap(); + let run_contract = run_script.display().to_string() + ":RunScript"; + + cmd.forge_fuse(); + cmd.set_current_dir(prj.root()); + cmd.args([ + "script", + &run_contract, + "--root", + prj.root().to_str().unwrap(), + "--fork-url", + &handle.http_endpoint(), + "-vvvvv", + "--broadcast", + "--slow", + "--skip-simulation", + "--gas-estimate-multiplier", + "200", + "--private-key", + &private_key, + ]); + + let output = cmd.stdout_lossy(); + assert!(output.contains("SKIPPING ON CHAIN SIMULATION")); + assert!(output.contains("ONCHAIN EXECUTION COMPLETE & SUCCESSFUL")); + } +); + +forgetest_async!(can_deploy_script_without_lib, |prj: TestProject, cmd: TestCommand| async move { + let (_api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + tester + .load_private_keys(vec![0, 1]) + .await + .add_sig("BroadcastTestNoLinking", "deployDoesntPanic()") + .simulate(ScriptOutcome::OkSimulation) + .broadcast(ScriptOutcome::OkBroadcast) + .assert_nonce_increment(vec![(0, 1), (1, 2)]) + .await; +}); + +forgetest_async!(can_deploy_script_with_lib, |prj: TestProject, cmd: TestCommand| async move { + let (_api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + tester + .load_private_keys(vec![0, 1]) + .await + .add_sig("BroadcastTest", "deploy()") + .simulate(ScriptOutcome::OkSimulation) + .broadcast(ScriptOutcome::OkBroadcast) + .assert_nonce_increment(vec![(0, 2), (1, 1)]) + .await; +}); + +forgetest_async!( + #[serial_test::serial] + can_deploy_script_private_key, + |prj: TestProject, cmd: TestCommand| async move { + let (_api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + tester + .load_addresses(vec![Address::from_str("0x90F79bf6EB2c4f870365E785982E1f101E93b906") + .unwrap() + .to_alloy()]) + .await + .add_sig("BroadcastTest", "deployPrivateKey()") + .simulate(ScriptOutcome::OkSimulation) + .broadcast(ScriptOutcome::OkBroadcast) + .assert_nonce_increment_addresses(vec![( + Address::from_str("0x90F79bf6EB2c4f870365E785982E1f101E93b906").unwrap().to_alloy(), + 3, + )]) + .await; + } +); + +forgetest_async!( + #[serial_test::serial] + can_deploy_unlocked, + |prj: TestProject, cmd: TestCommand| async move { + let (_api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + tester + .sender("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266".parse().unwrap()) + .unlocked() + .add_sig("BroadcastTest", "deployOther()") + .simulate(ScriptOutcome::OkSimulation) + .broadcast(ScriptOutcome::OkBroadcast); + } +); + +forgetest_async!( + #[serial_test::serial] + can_deploy_script_remember_key, + |prj: TestProject, cmd: TestCommand| async move { + let (_api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + tester + .load_addresses(vec![Address::from_str("0x90F79bf6EB2c4f870365E785982E1f101E93b906") + .unwrap() + .to_alloy()]) + .await + .add_sig("BroadcastTest", "deployRememberKey()") + .simulate(ScriptOutcome::OkSimulation) + .broadcast(ScriptOutcome::OkBroadcast) + .assert_nonce_increment_addresses(vec![( + Address::from_str("0x90F79bf6EB2c4f870365E785982E1f101E93b906").unwrap().to_alloy(), + 2, + )]) + .await; + } +); + +forgetest_async!( + #[serial_test::serial] + can_deploy_script_remember_key_and_resume, + |prj: TestProject, cmd: TestCommand| async move { + let (_api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + tester + .add_deployer(0) + .load_addresses(vec![Address::from_str("0x90F79bf6EB2c4f870365E785982E1f101E93b906") + .unwrap() + .to_alloy()]) + .await + .add_sig("BroadcastTest", "deployRememberKeyResume()") + .simulate(ScriptOutcome::OkSimulation) + .resume(ScriptOutcome::MissingWallet) + // load missing wallet + .load_private_keys(vec![0]) + .await + .run(ScriptOutcome::OkBroadcast) + .assert_nonce_increment_addresses(vec![( + Address::from_str("0x90F79bf6EB2c4f870365E785982E1f101E93b906").unwrap().to_alloy(), + 1, + )]) + .await + .assert_nonce_increment(vec![(0, 2)]) + .await; + } +); + +forgetest_async!(can_resume_script, |prj: TestProject, cmd: TestCommand| async move { + let (_api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + tester + .load_private_keys(vec![0]) + .await + .add_sig("BroadcastTest", "deploy()") + .simulate(ScriptOutcome::OkSimulation) + .resume(ScriptOutcome::MissingWallet) + // load missing wallet + .load_private_keys(vec![1]) + .await + .run(ScriptOutcome::OkBroadcast) + .assert_nonce_increment(vec![(0, 2), (1, 1)]) + .await; +}); + +forgetest_async!(can_deploy_broadcast_wrap, |prj: TestProject, cmd: TestCommand| async move { + let (_api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + tester + .add_deployer(2) + .load_private_keys(vec![0, 1, 2]) + .await + .add_sig("BroadcastTest", "deployOther()") + .simulate(ScriptOutcome::OkSimulation) + .broadcast(ScriptOutcome::OkBroadcast) + .assert_nonce_increment(vec![(0, 4), (1, 4), (2, 1)]) + .await; +}); + +forgetest_async!(panic_no_deployer_set, |prj: TestProject, cmd: TestCommand| async move { + let (_api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + tester + .load_private_keys(vec![0, 1]) + .await + .add_sig("BroadcastTest", "deployOther()") + .simulate(ScriptOutcome::WarnSpecifyDeployer) + .broadcast(ScriptOutcome::MissingSender); +}); + +forgetest_async!(can_deploy_no_arg_broadcast, |prj: TestProject, cmd: TestCommand| async move { + let (_api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + tester + .add_deployer(0) + .load_private_keys(vec![0]) + .await + .add_sig("BroadcastTest", "deployNoArgs()") + .simulate(ScriptOutcome::OkSimulation) + .broadcast(ScriptOutcome::OkBroadcast) + .assert_nonce_increment(vec![(0, 3)]) + .await; +}); + +forgetest_async!(can_deploy_with_create2, |prj: TestProject, cmd: TestCommand| async move { + let (api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + // Prepare CREATE2 Deployer + let addr = Address::from_str("0x4e59b44847b379578588920ca78fbf26c0b4956c").unwrap(); + let code = hex::decode("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3").expect("Could not decode create2 deployer init_code").into(); + api.anvil_set_code(addr, code).await.unwrap(); + + tester + .add_deployer(0) + .load_private_keys(vec![0]) + .await + .add_sig("BroadcastTestNoLinking", "deployCreate2()") + .simulate(ScriptOutcome::OkSimulation) + .broadcast(ScriptOutcome::OkBroadcast) + .assert_nonce_increment(vec![(0, 2)]) + .await + // Running again results in error, since we're repeating the salt passed to CREATE2 + .run(ScriptOutcome::FailedScript); +}); + +forgetest_async!( + #[serial_test::serial] + can_deploy_and_simulate_25_txes_concurrently, + |prj: TestProject, cmd: TestCommand| async move { + let (_api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + tester + .load_private_keys(vec![0]) + .await + .add_sig("BroadcastTestNoLinking", "deployMany()") + .simulate(ScriptOutcome::OkSimulation) + .broadcast(ScriptOutcome::OkBroadcast) + .assert_nonce_increment(vec![(0, 25)]) + .await; + } +); + +forgetest_async!( + #[serial_test::serial] + can_deploy_and_simulate_mixed_broadcast_modes, + |prj: TestProject, cmd: TestCommand| async move { + let (_api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + tester + .load_private_keys(vec![0]) + .await + .add_sig("BroadcastMix", "deployMix()") + .simulate(ScriptOutcome::OkSimulation) + .broadcast(ScriptOutcome::OkBroadcast) + .assert_nonce_increment(vec![(0, 15)]) + .await; + } +); + +forgetest_async!(deploy_with_setup, |prj: TestProject, cmd: TestCommand| async move { + let (_api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + tester + .load_private_keys(vec![0]) + .await + .add_sig("BroadcastTestSetup", "run()") + .simulate(ScriptOutcome::OkSimulation) + .broadcast(ScriptOutcome::OkBroadcast) + .assert_nonce_increment(vec![(0, 6)]) + .await; +}); + +forgetest_async!(fail_broadcast_staticcall, |prj: TestProject, cmd: TestCommand| async move { + let (_api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + tester + .load_private_keys(vec![0]) + .await + .add_sig("BroadcastTestNoLinking", "errorStaticCall()") + .simulate(ScriptOutcome::StaticCallNotAllowed); +}); + +forgetest_async!( + #[serial_test::serial] + check_broadcast_log, + |prj: TestProject, cmd: TestCommand| async move { + let (api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + // Prepare CREATE2 Deployer + let addr = Address::from_str("0x4e59b44847b379578588920ca78fbf26c0b4956c").unwrap(); + let code = hex::decode("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3").expect("Could not decode create2 deployer init_code").into(); + api.anvil_set_code(addr, code).await.unwrap(); + + tester + .load_private_keys(vec![0]) + .await + .add_sig("BroadcastTestSetup", "run()") + .simulate(ScriptOutcome::OkSimulation) + .broadcast(ScriptOutcome::OkBroadcast) + .assert_nonce_increment(vec![(0, 6)]) + .await; + + // Uncomment to recreate the broadcast log + // std::fs::copy( + // "broadcast/Broadcast.t.sol/31337/run-latest.json", + // PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../testdata/fixtures/broadcast. + // log. json" ), ); + + // Check broadcast logs + // Ignore timestamp, blockHash, blockNumber, cumulativeGasUsed, effectiveGasPrice, + // transactionIndex and logIndex values since they can change inbetween runs + let re = Regex::new(r#"((timestamp":).[0-9]*)|((blockHash":).*)|((blockNumber":).*)|((cumulativeGasUsed":).*)|((effectiveGasPrice":).*)|((transactionIndex":).*)|((logIndex":).*)"#).unwrap(); + + let fixtures_log = std::fs::read_to_string( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../testdata/fixtures/broadcast.log.json"), + ) + .unwrap(); + let _fixtures_log = re.replace_all(&fixtures_log, ""); + + let run_log = + std::fs::read_to_string("broadcast/Broadcast.t.sol/31337/run-latest.json").unwrap(); + let _run_log = re.replace_all(&run_log, ""); + + // pretty_assertions::assert_eq!(fixtures_log, run_log); + + // Uncomment to recreate the sensitive log + // std::fs::copy( + // "cache/Broadcast.t.sol/31337/run-latest.json", + // PathBuf::from(env!("CARGO_MANIFEST_DIR")) + // .join("../../testdata/fixtures/broadcast.sensitive.log.json"), + // ); + + // Check sensitive logs + // Ignore port number since it can change inbetween runs + let re = Regex::new(r":[0-9]+").unwrap(); + + let fixtures_log = std::fs::read_to_string( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../testdata/fixtures/broadcast.sensitive.log.json"), + ) + .unwrap(); + let fixtures_log = re.replace_all(&fixtures_log, ""); + + let run_log = + std::fs::read_to_string("cache/Broadcast.t.sol/31337/run-latest.json").unwrap(); + let run_log = re.replace_all(&run_log, ""); + + // Clean up carriage return OS differences + let re = Regex::new(r"\r\n").unwrap(); + let fixtures_log = re.replace_all(&fixtures_log, "\n"); + let run_log = re.replace_all(&run_log, "\n"); + + pretty_assertions::assert_eq!(fixtures_log, run_log); + } +); + +forgetest_async!(test_default_sender_balance, |prj: TestProject, cmd: TestCommand| async move { + let (_api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + // Expect the default sender to have uint256.max balance. + tester + .add_sig("TestInitialBalance", "runDefaultSender()") + .simulate(ScriptOutcome::OkSimulation); +}); + +forgetest_async!(test_custom_sender_balance, |prj: TestProject, cmd: TestCommand| async move { + let (_api, handle) = spawn(NodeConfig::test()).await; + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + // Expect the sender to have its starting balance. + tester + .add_deployer(0) + .add_sig("TestInitialBalance", "runCustomSender()") + .simulate(ScriptOutcome::OkSimulation); +}); + +#[derive(serde::Deserialize)] +struct Transactions { + transactions: Vec, +} + +#[derive(serde::Deserialize)] +struct Transaction { + arguments: Vec, +} + +// test we output arguments +forgetest_async!( + can_execute_script_with_arguments, + |prj: TestProject, mut cmd: TestCommand| async move { + cmd.args(["init", "--force"]).arg(prj.root()); + cmd.assert_non_empty_stdout(); + cmd.forge_fuse(); + + let (_api, handle) = spawn(NodeConfig::test()).await; + let script = prj + .inner() + .add_script( + "Counter.s.sol", + r#" +pragma solidity ^0.8.15; + +import "forge-std/Script.sol"; + +struct Point { + uint256 x; + uint256 y; +} + +contract A { + address a; + uint b; + int c; + bytes32 d; + bool e; + + constructor(address _a, uint _b, int _c, bytes32 _d, bool _e, bytes memory _f, Point memory _g, string memory _h) { + a = _a; + b = _b; + c = _c; + d = _d; + e = _e; + } +} + +contract Script0 is Script { + function run() external { + vm.broadcast(); + + new A(msg.sender, 2 ** 32, -1 * (2 ** 32), keccak256(abi.encode(1)), true, "abcdef", Point(10, 99), "hello"); + } +} + "#, + ) + .unwrap(); + + cmd.arg("script").arg(script).args([ + "--tc", + "Script0", + "--sender", + "0x00a329c0648769A73afAc7F9381E08FB43dBEA72", + "--rpc-url", + handle.http_endpoint().as_str(), + ]); + + assert!(cmd.stdout_lossy().contains("SIMULATION COMPLETE")); + + let run_latest = foundry_common::fs::json_files(prj.root().join("broadcast")) + .into_iter() + .find(|file| file.ends_with("run-latest.json")) + .expect("No broadcast artifacts"); + + let content = foundry_common::fs::read_to_string(run_latest).unwrap(); + + let transactions: Transactions = serde_json::from_str(&content).unwrap(); + let transactions = transactions.transactions; + assert_eq!(transactions.len(), 1); + assert_eq!( + transactions[0].arguments, + vec![ + "0x00a329c0648769A73afAc7F9381E08FB43dBEA72".to_string(), + "4294967296".to_string(), + "-4294967296".to_string(), + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6".to_string(), + "true".to_string(), + "0x616263646566".to_string(), + "(10, 99)".to_string(), + "hello".to_string(), + ] + ); + } +); + +// test we output arguments +forgetest_async!( + can_execute_script_with_arguments_nested_deploy, + |prj: TestProject, mut cmd: TestCommand| async move { + cmd.args(["init", "--force"]).arg(prj.root()); + cmd.assert_non_empty_stdout(); + cmd.forge_fuse(); + + let (_api, handle) = spawn(NodeConfig::test()).await; + let script = prj + .inner() + .add_script( + "Counter.s.sol", + r#" +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; + +contract A { + address a; + uint b; + int c; + bytes32 d; + bool e; + bytes f; + string g; + + constructor(address _a, uint _b, int _c, bytes32 _d, bool _e, bytes memory _f, string memory _g) { + a = _a; + b = _b; + c = _c; + d = _d; + e = _e; + f = _f; + g = _g; + } +} + +contract B { + constructor(address _a, uint _b, int _c, bytes32 _d, bool _e, bytes memory _f, string memory _g) { + new A(_a, _b, _c, _d, _e, _f, _g); + } +} + +contract Script0 is Script { + function run() external { + vm.broadcast(); + new B(msg.sender, 2 ** 32, -1 * (2 ** 32), keccak256(abi.encode(1)), true, "abcdef", "hello"); + } +} + "#, + ) + .unwrap(); + + cmd.arg("script").arg(script).args([ + "--tc", + "Script0", + "--sender", + "0x00a329c0648769A73afAc7F9381E08FB43dBEA72", + "--rpc-url", + handle.http_endpoint().as_str(), + ]); + + assert!(cmd.stdout_lossy().contains("SIMULATION COMPLETE")); + + let run_latest = foundry_common::fs::json_files(prj.root().join("broadcast")) + .into_iter() + .find(|file| file.ends_with("run-latest.json")) + .expect("No broadcast artifacts"); + + let content = foundry_common::fs::read_to_string(run_latest).unwrap(); + + let transactions: Transactions = serde_json::from_str(&content).unwrap(); + let transactions = transactions.transactions; + assert_eq!(transactions.len(), 1); + assert_eq!( + transactions[0].arguments, + vec![ + "0x00a329c0648769A73afAc7F9381E08FB43dBEA72".to_string(), + "4294967296".to_string(), + "-4294967296".to_string(), + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6".to_string(), + "true".to_string(), + "0x616263646566".to_string(), + "hello".to_string(), + ] + ); + } +); + +// checks that skipping build +forgetest_init!(can_execute_script_and_skip_contracts, |prj: TestProject, mut cmd: TestCommand| { + // explicitly set to run with 0.8.17 for consistent output + let config = Config { solc: Some("0.8.17".into()), ..Default::default() }; + prj.write_config(config); + + let script = prj + .inner() + .add_source( + "Foo", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.17; +contract Demo { + event log_string(string); + function run() external returns (uint256 result, uint8) { + emit log_string("script ran"); + return (255, 3); + } +}"#, + ) + .unwrap(); + cmd.arg("script").arg(script).args(["--skip", "tests", "--skip", TEMPLATE_CONTRACT]); + + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_execute_script_and_skip_contracts.stdout"), + ); +}); + +forgetest_async!( + can_run_script_with_empty_setup, + |prj: TestProject, cmd: TestCommand| async move { + let mut tester = ScriptTester::new_broadcast_without_endpoint(cmd, prj.root()); + + tester.add_sig("BroadcastEmptySetUp", "run()").simulate(ScriptOutcome::OkNoEndpoint); + } +); + +forgetest_async!(does_script_override_correctly, |prj: TestProject, cmd: TestCommand| async move { + let mut tester = ScriptTester::new_broadcast_without_endpoint(cmd, prj.root()); + + tester.add_sig("CheckOverrides", "run()").simulate(ScriptOutcome::OkNoEndpoint); +}); + +forgetest_async!( + assert_tx_origin_is_not_overritten, + |prj: TestProject, mut cmd: TestCommand| async move { + cmd.args(["init", "--force"]).arg(prj.root()); + cmd.assert_non_empty_stdout(); + cmd.forge_fuse(); + + let script = prj + .inner() + .add_script( + "ScriptTxOrigin.s.sol", + r#" +pragma solidity ^0.8.13; + +import { Script } from "forge-std/Script.sol"; + +contract ScriptTxOrigin is Script { + function run() public { + uint256 pk = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80; + vm.startBroadcast(pk); // 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + + ContractA contractA = new ContractA(); + ContractB contractB = new ContractB(); + + contractA.test(address(contractB)); + contractB.method(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + + require(tx.origin == 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38); + vm.stopBroadcast(); + } +} + +contract ContractA { + function test(address _contractB) public { + require(msg.sender == 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + require(tx.origin == 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + ContractB contractB = ContractB(_contractB); + ContractC contractC = new ContractC(); + require(msg.sender == 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + require(tx.origin == 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + contractB.method(address(this)); + contractC.method(address(this)); + require(msg.sender == 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + require(tx.origin == 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + } +} + +contract ContractB { + function method(address sender) public view { + require(msg.sender == sender); + require(tx.origin == 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + } +} +contract ContractC { + function method(address sender) public view { + require(msg.sender == sender); + require(tx.origin == 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + } +} + "#, + ) + .unwrap(); + + cmd.arg("script").arg(script).args(["--tc", "ScriptTxOrigin"]); + assert!(cmd.stdout_lossy().contains("Script ran successfully.")); + } +); + +forgetest_async!( + assert_can_create_multiple_contracts_with_correct_nonce, + |prj: TestProject, mut cmd: TestCommand| async move { + cmd.args(["init", "--force"]).arg(prj.root()); + cmd.assert_non_empty_stdout(); + cmd.forge_fuse(); + + let script = prj + .inner() + .add_script( + "ScriptTxOrigin.s.sol", + r#" +pragma solidity ^0.8.17; + +import {Script, console} from "forge-std/Script.sol"; + +contract Contract { + constructor() { + console.log(tx.origin); + } +} +contract SubContract { + constructor() { + console.log(tx.origin); + } +} +contract BadContract { + constructor() { + // new SubContract(); + console.log(tx.origin); + } +} +contract NestedCreateFail is Script { + function run() public { + address sender = address(uint160(uint(keccak256("woops")))); + + vm.broadcast(sender); + new BadContract(); + + vm.broadcast(sender); + new Contract(); + } +} + "#, + ) + .unwrap(); + + cmd.arg("script").arg(script).args(["--tc", "NestedCreateFail"]); + assert!(cmd.stdout_lossy().contains("Script ran successfully.")); + } +); diff --git a/crates/zkforge/tests/cli/svm.rs b/crates/zkforge/tests/cli/svm.rs new file mode 100644 index 000000000..5635098ed --- /dev/null +++ b/crates/zkforge/tests/cli/svm.rs @@ -0,0 +1,62 @@ +//! svm sanity checks + +use foundry_test_utils::{forgetest_init, TestCommand, TestProject}; +use semver::Version; +use svm::Platform; + +/// The latest solc release +/// +/// solc to foundry release process: +/// 1. new solc release +/// 2. svm updated with all build info +/// 3. svm bumped in ethers-rs +/// 4. ethers bumped in foundry + update the `LATEST_SOLC` +const LATEST_SOLC: Version = Version::new(0, 8, 21); + +macro_rules! ensure_svm_releases { + ($($test:ident => $platform:ident),* $(,)?) => {$( + #[tokio::test(flavor = "multi_thread")] + async fn $test() { + ensure_latest_release(Platform::$platform).await + } + )*}; +} + +async fn ensure_latest_release(platform: Platform) { + let releases = svm::all_releases(platform) + .await + .unwrap_or_else(|err| panic!("Could not fetch releases for {platform}: {err:?}")); + assert!( + releases.releases.contains_key(&LATEST_SOLC), + "platform {platform:?} is missing solc info for v{LATEST_SOLC}" + ); +} + +// ensures all platform have the latest solc release version +ensure_svm_releases!( + test_svm_releases_linux_amd64 => LinuxAmd64, + test_svm_releases_linux_aarch64 => LinuxAarch64, + test_svm_releases_macos_amd64 => MacOsAmd64, + test_svm_releases_macos_aarch64 => MacOsAarch64, + test_svm_releases_windows_amd64 => WindowsAmd64 +); + +// Ensures we can always test with the latest solc build +forgetest_init!(can_test_with_latest_solc, |prj: TestProject, mut cmd: TestCommand| { + let src = format!( + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity ={LATEST_SOLC}; + +import "forge-std/Test.sol"; + +contract CounterTest is Test {{ + function testAssert() public {{ + assert(true); + }} +}} + "# + ); + prj.inner().add_test("Counter", src).unwrap(); + cmd.arg("test").stdout().contains("[PASS]") +}); diff --git a/crates/zkforge/tests/cli/test_cmd.rs b/crates/zkforge/tests/cli/test_cmd.rs new file mode 100644 index 000000000..79c37007e --- /dev/null +++ b/crates/zkforge/tests/cli/test_cmd.rs @@ -0,0 +1,406 @@ +//! Contains various tests for checking `forge test` +use foundry_config::Config; +use foundry_test_utils::{ + forgetest, forgetest_init, + util::{OutputExt, TestCommand, TestProject}, +}; +use foundry_utils::rpc; +use std::{path::PathBuf, str::FromStr}; + +// tests that test filters are handled correctly +forgetest!(can_set_filter_values, |prj: TestProject, mut cmd: TestCommand| { + let patt = regex::Regex::new("test*").unwrap(); + let glob = globset::Glob::from_str("foo/bar/baz*").unwrap(); + + // explicitly set patterns + let config = Config { + test_pattern: Some(patt.clone().into()), + test_pattern_inverse: None, + contract_pattern: Some(patt.clone().into()), + contract_pattern_inverse: None, + path_pattern: Some(glob.clone()), + path_pattern_inverse: None, + ..Default::default() + }; + prj.write_config(config); + + let config = cmd.config(); + + assert_eq!(config.test_pattern.unwrap().as_str(), patt.as_str()); + assert_eq!(config.test_pattern_inverse, None); + assert_eq!(config.contract_pattern.unwrap().as_str(), patt.as_str()); + assert_eq!(config.contract_pattern_inverse, None); + assert_eq!(config.path_pattern.unwrap(), glob); + assert_eq!(config.path_pattern_inverse, None); +}); + +// tests that warning is displayed when there are no tests in project +forgetest!(warn_no_tests, |prj: TestProject, mut cmd: TestCommand| { + prj.inner() + .add_source( + "dummy", + r" +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.13; + +contract Dummy {} +", + ) + .unwrap(); + // set up command + cmd.args(["test"]); + + // run command and assert + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/warn_no_tests.stdout"), + ); +}); + +// tests that warning is displayed with pattern when no tests match +forgetest!(warn_no_tests_match, |prj: TestProject, mut cmd: TestCommand| { + prj.inner() + .add_source( + "dummy", + r" +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.13; + +contract Dummy {} +", + ) + .unwrap(); + + // set up command + cmd.args(["test", "--match-test", "testA.*", "--no-match-test", "testB.*"]); + cmd.args(["--match-contract", "TestC.*", "--no-match-contract", "TestD.*"]); + cmd.args(["--match-path", "*TestE*", "--no-match-path", "*TestF*"]); + + // run command and assert + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/warn_no_tests_match.stdout"), + ); +}); + +// tests that suggestion is provided with pattern when no tests match +forgetest!(suggest_when_no_tests_match, |prj: TestProject, mut cmd: TestCommand| { + // set up project + prj.inner() + .add_source( + "TestE.t.sol", + r" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; + +contract TestC { + function test1() public { + } +} + ", + ) + .unwrap(); + + // set up command + cmd.args(["test", "--match-test", "testA.*", "--no-match-test", "testB.*"]); + cmd.args(["--match-contract", "TestC.*", "--no-match-contract", "TestD.*"]); + cmd.args(["--match-path", "*TestE*", "--no-match-path", "*TestF*"]); + + // run command and assert + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/suggest_when_no_tests_match.stdout"), + ); +}); + +// tests that direct import paths are handled correctly +forgetest!(can_fuzz_array_params, |prj: TestProject, mut cmd: TestCommand| { + prj.insert_ds_test(); + + prj.inner() + .add_source( + "ATest.t.sol", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +import "./test.sol"; +contract ATest is DSTest { + function testArray(uint64[2] calldata values) external { + assertTrue(true); + } +} + "#, + ) + .unwrap(); + + cmd.arg("test"); + cmd.stdout().contains("[PASS]") +}); + +// tests that `bytecode_hash` will be sanitized +forgetest!(can_test_pre_bytecode_hash, |prj: TestProject, mut cmd: TestCommand| { + prj.insert_ds_test(); + + prj.inner() + .add_source( + "ATest.t.sol", + r#" +// SPDX-License-Identifier: UNLICENSED +// pre bytecode hash version, was introduced in 0.6.0 +pragma solidity 0.5.17; +import "./test.sol"; +contract ATest is DSTest { + function testArray(uint64[2] calldata values) external { + assertTrue(true); + } +} + "#, + ) + .unwrap(); + + cmd.arg("test"); + cmd.stdout().contains("[PASS]") +}); + +// tests that using the --match-path option only runs files matching the path +forgetest!(can_test_with_match_path, |prj: TestProject, mut cmd: TestCommand| { + prj.insert_ds_test(); + + prj.inner() + .add_source( + "ATest.t.sol", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +import "./test.sol"; +contract ATest is DSTest { + function testArray(uint64[2] calldata values) external { + assertTrue(true); + } +} + "#, + ) + .unwrap(); + + prj.inner() + .add_source( + "FailTest.t.sol", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +import "./test.sol"; +contract FailTest is DSTest { + function testNothing() external { + assertTrue(false); + } +} + "#, + ) + .unwrap(); + + cmd.args(["test", "--match-path", "*src/ATest.t.sol"]); + cmd.stdout().contains("[PASS]") && !cmd.stdout().contains("[FAIL]") +}); + +// tests that `forge test` will pick up tests that are stored in the `test = ` config value +forgetest!(can_run_test_in_custom_test_folder, |prj: TestProject, mut cmd: TestCommand| { + prj.insert_ds_test(); + + // explicitly set the test folder + let config = Config { test: "nested/forge-tests".into(), ..Default::default() }; + prj.write_config(config); + let config = cmd.config(); + assert_eq!(config.test, PathBuf::from("nested/forge-tests")); + + prj.inner() + .add_source( + "nested/forge-tests/MyTest.t.sol", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; +import "../../test.sol"; +contract MyTest is DSTest { + function testTrue() public { + assertTrue(true); + } +} + "#, + ) + .unwrap(); + + cmd.arg("test"); + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_run_test_in_custom_test_folder.stdout"), + ); +}); + +// checks that forge test repeatedly produces the same output +forgetest_init!(can_test_repeatedly, |_prj: TestProject, mut cmd: TestCommand| { + cmd.arg("test"); + cmd.assert_non_empty_stdout(); + + for _ in 0..5 { + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_test_repeatedly.stdout"), + ); + } +}); + +// tests that `forge test` will run a test only once after changing the version +forgetest!( + runs_tests_exactly_once_with_changed_versions, + |prj: TestProject, mut cmd: TestCommand| { + prj.insert_ds_test(); + + prj.inner() + .add_source( + "Contract.t.sol", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.10; +import "./test.sol"; +contract ContractTest is DSTest { + function setUp() public {} + + function testExample() public { + assertTrue(true); + } +} + "#, + ) + .unwrap(); + + // pin version + let config = Config { solc: Some("0.8.10".into()), ..Default::default() }; + prj.write_config(config); + + cmd.arg("test"); + cmd.unchecked_output() + .stdout_matches_path(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join( + "tests/fixtures/runs_tests_exactly_once_with_changed_versions.0.8.10.stdout", + )); + + // pin version + let config = Config { solc: Some("0.8.13".into()), ..Default::default() }; + prj.write_config(config); + + cmd.unchecked_output() + .stdout_matches_path(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join( + "tests/fixtures/runs_tests_exactly_once_with_changed_versions.0.8.13.stdout", + )); + } +); + +// checks that we can test forge std successfully +// `forgetest_init!` will install with `forge-std` under `lib/forge-std` +forgetest_init!( + #[serial_test::serial] + can_test_forge_std, + |prj: TestProject, mut cmd: TestCommand| { + let forge_std_dir = prj.root().join("lib/forge-std"); + // execute in subdir + cmd.cmd().current_dir(forge_std_dir); + cmd.args(["test", "--root", "."]); + let stdout = cmd.stdout(); + assert!(stdout.contains("[PASS]"), "No tests passed:\n{stdout}"); + assert!(!stdout.contains("[FAIL]"), "Tests failed :\n{stdout}"); + } +); + +// tests that libraries are handled correctly in multiforking mode +forgetest_init!(can_use_libs_in_multi_fork, |prj: TestProject, mut cmd: TestCommand| { + prj.wipe_contracts(); + prj.inner() + .add_source( + "Contract.sol", + r" +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.13; + +library Library { + function f(uint256 a, uint256 b) public pure returns (uint256) { + return a + b; + } +} + +contract Contract { + uint256 c; + + constructor() { + c = Library.f(1, 2); + } +} + ", + ) + .unwrap(); + + let endpoint = rpc::next_http_archive_rpc_endpoint(); + + prj.inner() + .add_test( + "Contract.t.sol", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.13; + +import "forge-std/Test.sol"; +import "src/Contract.sol"; + +contract ContractTest is Test { + function setUp() public { + vm.createSelectFork(""); + } + + function test() public { + new Contract(); + } +} + "# + .replace("", &endpoint), + ) + .unwrap(); + + cmd.arg("test"); + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_use_libs_in_multi_fork.stdout"), + ); +}); + +static FAILING_TEST: &str = r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; + +contract FailingTest is Test { + function testShouldFail() public { + assertTrue(false); + } +} +"#; + +forgetest_init!(exit_code_error_on_fail_fast, |prj: TestProject, mut cmd: TestCommand| { + prj.wipe_contracts(); + prj.inner().add_source("failing_test", FAILING_TEST).unwrap(); + + // set up command + cmd.args(["test", "--fail-fast"]); + + // run command and assert error exit code + cmd.assert_err(); +}); + +forgetest_init!( + exit_code_error_on_fail_fast_with_json, + |prj: TestProject, mut cmd: TestCommand| { + prj.wipe_contracts(); + + prj.inner().add_source("failing_test", FAILING_TEST).unwrap(); + // set up command + cmd.args(["test", "--fail-fast", "--json"]); + + // run command and assert error exit code + cmd.assert_err(); + } +); diff --git a/crates/zkforge/tests/cli/utils.rs b/crates/zkforge/tests/cli/utils.rs new file mode 100644 index 000000000..2c6fbe3bd --- /dev/null +++ b/crates/zkforge/tests/cli/utils.rs @@ -0,0 +1,141 @@ +//! Various helper functions + +use ethers::prelude::{Address, Chain, LocalWallet, Signer}; + +/// Returns the current millis since unix epoch. +/// +/// This way we generate unique contracts so, etherscan will always have to verify them +pub fn millis_since_epoch() -> u128 { + let now = std::time::SystemTime::now(); + now.duration_since(std::time::SystemTime::UNIX_EPOCH) + .unwrap_or_else(|err| panic!("Current time {now:?} is invalid: {err:?}")) + .as_millis() +} + +pub fn etherscan_key(chain: Chain) -> Option { + match chain { + Chain::Fantom | Chain::FantomTestnet => { + std::env::var("FTMSCAN_API_KEY").or_else(|_| std::env::var("FANTOMSCAN_API_KEY")).ok() + } + Chain::OptimismKovan => std::env::var("OP_KOVAN_API_KEY").ok(), + _ => std::env::var("ETHERSCAN_API_KEY").ok(), + } +} + +pub fn network_rpc_key(chain: &str) -> Option { + let key = format!("{}_RPC_URL", chain.to_uppercase().replace('-', "_")); + std::env::var(key).ok() +} + +pub fn network_private_key(chain: &str) -> Option { + let key = format!("{}_PRIVATE_KEY", chain.to_uppercase().replace('-', "_")); + std::env::var(key).or_else(|_| std::env::var("TEST_PRIVATE_KEY")).ok() +} + +/// Represents external input required for executing verification requests +pub struct EnvExternalities { + pub chain: Chain, + pub rpc: String, + pub pk: String, + pub etherscan: String, + pub verifier: String, +} + +#[allow(dead_code)] +impl EnvExternalities { + pub fn address(&self) -> Option
{ + let pk: LocalWallet = self.pk.parse().ok()?; + Some(pk.address()) + } + + pub fn goerli() -> Option { + Some(Self { + chain: Chain::Goerli, + rpc: network_rpc_key("goerli")?, + pk: network_private_key("goerli")?, + etherscan: etherscan_key(Chain::Goerli)?, + verifier: "etherscan".to_string(), + }) + } + + pub fn ftm_testnet() -> Option { + Some(Self { + chain: Chain::FantomTestnet, + rpc: network_rpc_key("ftm_testnet")?, + pk: network_private_key("ftm_testnet")?, + etherscan: etherscan_key(Chain::FantomTestnet)?, + verifier: "etherscan".to_string(), + }) + } + + pub fn optimism_kovan() -> Option { + Some(Self { + chain: Chain::OptimismKovan, + rpc: network_rpc_key("op_kovan")?, + pk: network_private_key("op_kovan")?, + etherscan: etherscan_key(Chain::OptimismKovan)?, + verifier: "etherscan".to_string(), + }) + } + + pub fn arbitrum_goerli() -> Option { + Some(Self { + chain: Chain::ArbitrumGoerli, + rpc: network_rpc_key("arbitrum-goerli")?, + pk: network_private_key("arbitrum-goerli")?, + etherscan: etherscan_key(Chain::ArbitrumGoerli)?, + verifier: "blockscout".to_string(), + }) + } + + pub fn mumbai() -> Option { + Some(Self { + chain: Chain::PolygonMumbai, + rpc: network_rpc_key("mumbai")?, + pk: network_private_key("mumbai")?, + etherscan: etherscan_key(Chain::PolygonMumbai)?, + verifier: "etherscan".to_string(), + }) + } + + pub fn sepolia() -> Option { + Some(Self { + chain: Chain::Sepolia, + rpc: network_rpc_key("sepolia")?, + pk: network_private_key("sepolia")?, + etherscan: etherscan_key(Chain::Sepolia)?, + verifier: "etherscan".to_string(), + }) + } + + /// Returns the arguments required to deploy the contract + pub fn create_args(&self) -> Vec { + vec![ + "--chain".to_string(), + self.chain.to_string(), + "--rpc-url".to_string(), + self.rpc.clone(), + "--private-key".to_string(), + self.pk.clone(), + ] + } +} + +/// Parses the address the contract was deployed to +pub fn parse_deployed_address(out: &str) -> Option { + for line in out.lines() { + if line.starts_with("Deployed to") { + return Some(line.trim_start_matches("Deployed to: ").to_string()) + } + } + None +} + +pub fn parse_verification_guid(out: &str) -> Option { + for line in out.lines() { + if line.contains("GUID") { + return Some(line.replace("GUID:", "").replace('`', "").trim().to_string()) + } + } + None +} diff --git a/crates/zkforge/tests/cli/verify.rs b/crates/zkforge/tests/cli/verify.rs new file mode 100644 index 000000000..bdd35d591 --- /dev/null +++ b/crates/zkforge/tests/cli/verify.rs @@ -0,0 +1,131 @@ +//! Contains various tests for checking forge commands related to verifying contracts on etherscan +//! and sourcify + +use crate::utils::{self, EnvExternalities}; +use foundry_test_utils::{ + forgetest, + util::{TestCommand, TestProject}, +}; +use foundry_utils::Retry; + +/// Adds a `Unique` contract to the source directory of the project that can be imported as +/// `import {Unique} from "./unique.sol";` +fn add_unique(prj: &TestProject) { + let timestamp = utils::millis_since_epoch(); + prj.inner() + .add_source( + "unique", + format!( + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.4.0; + +contract Unique {{ + uint public _timestamp = {timestamp}; +}} +"# + ), + ) + .unwrap(); +} + +fn add_verify_target(prj: &TestProject) { + prj.inner() + .add_source( + "Verify.sol", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.10; +import {Unique} from "./unique.sol"; +contract Verify is Unique { +function doStuff() external {} +} +"#, + ) + .unwrap(); +} + +fn parse_verification_result(cmd: &mut TestCommand, retries: u32) -> eyre::Result<()> { + // give etherscan some time to verify the contract + let retry = Retry::new(retries, Some(30)); + retry.run(|| -> eyre::Result<()> { + let output = cmd.unchecked_output(); + let out = String::from_utf8_lossy(&output.stdout); + if out.contains("Contract successfully verified") { + return Ok(()) + } + eyre::bail!( + "Failed to get verification, stdout: {}, stderr: {}", + out, + String::from_utf8_lossy(&output.stderr) + ) + }) +} + +fn verify_on_chain(info: Option, prj: TestProject, mut cmd: TestCommand) { + // only execute if keys present + if let Some(info) = info { + println!("verifying on {}", info.chain); + add_unique(&prj); + add_verify_target(&prj); + + let contract_path = "src/Verify.sol:Verify"; + cmd.arg("create").args(info.create_args()).arg(contract_path); + + let out = cmd.stdout_lossy(); + let address = utils::parse_deployed_address(out.as_str()) + .unwrap_or_else(|| panic!("Failed to parse deployer {out}")); + + cmd.forge_fuse().arg("verify-contract").root_arg().args([ + "--chain-id".to_string(), + info.chain.to_string(), + address, + contract_path.to_string(), + info.etherscan.to_string(), + "--verifier".to_string(), + info.verifier.to_string(), + ]); + + // `verify-contract` + let guid = { + // give etherscan some time to detect the transaction + let retry = Retry::new(5, Some(60)); + retry + .run(|| -> eyre::Result { + let output = cmd.unchecked_output(); + let out = String::from_utf8_lossy(&output.stdout); + utils::parse_verification_guid(&out).ok_or_else(|| { + eyre::eyre!( + "Failed to get guid, stdout: {}, stderr: {}", + out, + String::from_utf8_lossy(&output.stderr) + ) + }) + }) + .expect("Failed to get verify guid") + }; + + // verify-check + cmd.forge_fuse() + .arg("verify-check") + .arg(guid) + .arg("--chain-id") + .arg(info.chain.to_string()) + .arg("--etherscan-key") + .arg(info.etherscan) + .arg("--verifier") + .arg(info.verifier); + + parse_verification_result(&mut cmd, 6).expect("Failed to verify check") + } +} + +// tests `create && contract-verify && verify-check` on Fantom testnet if correct env vars are set +forgetest!(can_verify_random_contract_fantom_testnet, |prj: TestProject, cmd: TestCommand| { + verify_on_chain(EnvExternalities::ftm_testnet(), prj, cmd); +}); + +// tests `create && contract-verify && verify-check` on Optimism kovan if correct env vars are set +forgetest!(can_verify_random_contract_optimism_kovan, |prj: TestProject, cmd: TestCommand| { + verify_on_chain(EnvExternalities::optimism_kovan(), prj, cmd); +}); diff --git a/crates/zkforge/tests/fixtures/ScriptVerify.sol b/crates/zkforge/tests/fixtures/ScriptVerify.sol new file mode 100644 index 000000000..e282f9e5c --- /dev/null +++ b/crates/zkforge/tests/fixtures/ScriptVerify.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.16; + +import {Unique} from "./unique.sol"; + +interface HEVM { + function startBroadcast() external; +} + +library F { + function f() public pure returns (uint256) { + return 1; + } +} + +library C { + function c() public pure returns (uint256) { + return 2; + } +} + +contract Hello { + function world() public { + F.f(); + C.c(); + } +} + +contract CC1 is Unique { + uint256 a; + + constructor(uint256 _a) { + a = _a; + } +} + +contract CC2 is Unique { + uint8 b; + + constructor(uint256 _b) { + b = uint8(_b); + new CC3("hello"); + } +} + +contract CC3 is Unique { + string c; + + constructor(string memory _c) { + c = _c; + } +} + +contract InnerContracts is Unique { + constructor(uint256 _a) public { + CC1 c1 = new CC1(_a); + } + + function c2(uint256 _b) public { + CC2 c2 = new CC2{salt: bytes32(uint256(1))}(_b); + } +} + +contract ScriptVerify { + function run() public { + address vm = address(bytes20(uint160(uint256(keccak256("hevm cheat code"))))); + HEVM(vm).startBroadcast(); + new Hello(); + InnerContracts contracts = new InnerContracts(1); + contracts.c2(3); + } +} diff --git a/crates/zkforge/tests/fixtures/can_build_skip_contracts.stdout b/crates/zkforge/tests/fixtures/can_build_skip_contracts.stdout new file mode 100644 index 000000000..74ddb6b4e --- /dev/null +++ b/crates/zkforge/tests/fixtures/can_build_skip_contracts.stdout @@ -0,0 +1,3 @@ +Compiling 1 files with 0.8.17 +Solc 0.8.17 finished in 34.45ms +Compiler run successful! diff --git a/crates/zkforge/tests/fixtures/can_build_skip_glob.stdout b/crates/zkforge/tests/fixtures/can_build_skip_glob.stdout new file mode 100644 index 000000000..522beb3e2 --- /dev/null +++ b/crates/zkforge/tests/fixtures/can_build_skip_glob.stdout @@ -0,0 +1,3 @@ +Compiling 1 files with 0.8.17 +Solc 0.8.17 finished in 33.25ms +Compiler run successful! diff --git a/crates/zkforge/tests/fixtures/can_check_snapshot.stdout b/crates/zkforge/tests/fixtures/can_check_snapshot.stdout new file mode 100644 index 000000000..6a5df56b3 --- /dev/null +++ b/crates/zkforge/tests/fixtures/can_check_snapshot.stdout @@ -0,0 +1,9 @@ +Compiling 2 files with 0.8.10 +Solc 0.8.10 finished in 424.55ms +Compiler run successful! + +Running 1 test for src/ATest.t.sol:ATest +[PASS] testExample() (gas: 168) +Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.42ms + +Ran 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests) diff --git a/crates/zkforge/tests/fixtures/can_create_template_contract-2nd.stdout b/crates/zkforge/tests/fixtures/can_create_template_contract-2nd.stdout new file mode 100644 index 000000000..c04ae5bd5 --- /dev/null +++ b/crates/zkforge/tests/fixtures/can_create_template_contract-2nd.stdout @@ -0,0 +1,4 @@ +No files changed, compilation skipped +Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 +Deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 +Transaction hash: 0x3d78b08c411f05d5e79adc92a4c814e0f818d1a09c111b0ab688270f35a07ae7 diff --git a/crates/zkforge/tests/fixtures/can_create_template_contract.stdout b/crates/zkforge/tests/fixtures/can_create_template_contract.stdout new file mode 100644 index 000000000..a67e4dd96 --- /dev/null +++ b/crates/zkforge/tests/fixtures/can_create_template_contract.stdout @@ -0,0 +1,6 @@ +Compiling 22 files with 0.8.15 +Solc 0.8.15 finished in 2.27s +Compiler run successful! +Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 +Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 +Transaction hash: 0x4c3d9f7c4cc26876b43a11ba7ff218374471786a8ae8bf5574deb1d97fc1e851 diff --git a/crates/zkforge/tests/fixtures/can_create_using_unlocked-2nd.stdout b/crates/zkforge/tests/fixtures/can_create_using_unlocked-2nd.stdout new file mode 100644 index 000000000..c04ae5bd5 --- /dev/null +++ b/crates/zkforge/tests/fixtures/can_create_using_unlocked-2nd.stdout @@ -0,0 +1,4 @@ +No files changed, compilation skipped +Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 +Deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 +Transaction hash: 0x3d78b08c411f05d5e79adc92a4c814e0f818d1a09c111b0ab688270f35a07ae7 diff --git a/crates/zkforge/tests/fixtures/can_create_using_unlocked.stdout b/crates/zkforge/tests/fixtures/can_create_using_unlocked.stdout new file mode 100644 index 000000000..ab3097524 --- /dev/null +++ b/crates/zkforge/tests/fixtures/can_create_using_unlocked.stdout @@ -0,0 +1,6 @@ +Compiling 22 files with 0.8.15 +Solc 0.8.15 finished in 1.95s +Compiler run successful! +Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 +Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 +Transaction hash: 0x4c3d9f7c4cc26876b43a11ba7ff218374471786a8ae8bf5574deb1d97fc1e851 diff --git a/crates/zkforge/tests/fixtures/can_detect_dirty_git_status_on_init.stderr b/crates/zkforge/tests/fixtures/can_detect_dirty_git_status_on_init.stderr new file mode 100644 index 000000000..de86b264d --- /dev/null +++ b/crates/zkforge/tests/fixtures/can_detect_dirty_git_status_on_init.stderr @@ -0,0 +1,10 @@ +Error: +The target directory is a part of or on its own an already initialized git repository, +and it requires clean working and staging areas, including no untracked files. + +Check the current git repository's status with `git status`. +Then, you can track files with `git add ...` and then commit them with `git commit`, +ignore them in the `.gitignore` file, or run this command again with the `--no-commit` flag. + +If none of the previous steps worked, please open an issue at: +https://github.com/foundry-rs/foundry/issues/new/choose diff --git a/crates/zkforge/tests/fixtures/can_execute_script_and_skip_contracts.stdout b/crates/zkforge/tests/fixtures/can_execute_script_and_skip_contracts.stdout new file mode 100644 index 000000000..46f580d03 --- /dev/null +++ b/crates/zkforge/tests/fixtures/can_execute_script_and_skip_contracts.stdout @@ -0,0 +1,12 @@ +Compiling 1 files with 0.8.17 +Solc 0.8.17 +Compiler run successful! +Script ran successfully. +Gas used: 22900 + +== Return == +result: uint256 255 +1: uint8 3 + +== Logs == + script ran diff --git a/crates/zkforge/tests/fixtures/can_execute_script_command.stdout b/crates/zkforge/tests/fixtures/can_execute_script_command.stdout new file mode 100644 index 000000000..459bf7369 --- /dev/null +++ b/crates/zkforge/tests/fixtures/can_execute_script_command.stdout @@ -0,0 +1,8 @@ +Compiling 1 files with 0.8.10 +Solc 0.8.10 finished in 23.34ms +Compiler run successful! +Script ran successfully. +Gas used: 22815 + +== Logs == + script ran diff --git a/crates/zkforge/tests/fixtures/can_execute_script_command_fqn.stdout b/crates/zkforge/tests/fixtures/can_execute_script_command_fqn.stdout new file mode 100644 index 000000000..6b2a06b30 --- /dev/null +++ b/crates/zkforge/tests/fixtures/can_execute_script_command_fqn.stdout @@ -0,0 +1,8 @@ +Compiling 1 files with 0.8.10 +Solc 0.8.10 finished in 23.70ms +Compiler run successful! +Script ran successfully. +Gas used: 22815 + +== Logs == + script ran diff --git a/crates/zkforge/tests/fixtures/can_execute_script_command_with_args.stdout b/crates/zkforge/tests/fixtures/can_execute_script_command_with_args.stdout new file mode 100644 index 000000000..882fe3a47 --- /dev/null +++ b/crates/zkforge/tests/fixtures/can_execute_script_command_with_args.stdout @@ -0,0 +1,10 @@ +Compiling 1 files with 0.8.10 +Solc 0.8.10 finished in 35.28ms +Compiler run successful! +Script ran successfully. +Gas used: 25301 + +== Logs == + script ran + 1 + 2 diff --git a/crates/zkforge/tests/fixtures/can_execute_script_command_with_returned.stdout b/crates/zkforge/tests/fixtures/can_execute_script_command_with_returned.stdout new file mode 100644 index 000000000..0cc0e7f97 --- /dev/null +++ b/crates/zkforge/tests/fixtures/can_execute_script_command_with_returned.stdout @@ -0,0 +1,12 @@ +Compiling 1 files with 0.8.10 +Solc 0.8.10 finished in 1.27s +Compiler run successful! +Script ran successfully. +Gas used: 22900 + +== Return == +result: uint256 255 +1: uint8 3 + +== Logs == + script ran diff --git a/crates/zkforge/tests/fixtures/can_execute_script_command_with_sig.stdout b/crates/zkforge/tests/fixtures/can_execute_script_command_with_sig.stdout new file mode 100644 index 000000000..54f954f36 --- /dev/null +++ b/crates/zkforge/tests/fixtures/can_execute_script_command_with_sig.stdout @@ -0,0 +1,8 @@ +Compiling 1 files with 0.8.10 +Solc 0.8.10 finished in 24.49ms +Compiler run successful! +Script ran successfully. +Gas used: 22815 + +== Logs == + script ran diff --git a/crates/zkforge/tests/fixtures/can_run_test_in_custom_test_folder.stdout b/crates/zkforge/tests/fixtures/can_run_test_in_custom_test_folder.stdout new file mode 100644 index 000000000..28a027349 --- /dev/null +++ b/crates/zkforge/tests/fixtures/can_run_test_in_custom_test_folder.stdout @@ -0,0 +1,9 @@ +Compiling 2 files with 0.8.10 +Solc 0.8.10 finished in 185.25ms +Compiler run successful! + +Running 1 test for src/nested/forge-tests/MyTest.t.sol:MyTest +[PASS] testTrue() (gas: 168) +Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.93ms + +Ran 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests) diff --git a/crates/zkforge/tests/fixtures/can_set_yul_optimizer.stderr b/crates/zkforge/tests/fixtures/can_set_yul_optimizer.stderr new file mode 100644 index 000000000..c7c847bf9 --- /dev/null +++ b/crates/zkforge/tests/fixtures/can_set_yul_optimizer.stderr @@ -0,0 +1,9 @@ +Error: +Compiler run failed: +Error (6553): SyntaxError: The msize instruction cannot be used when the Yul optimizer is activated because it can change its semantics. Either disable the Yul optimizer or do not use the instruction. + --> src/Foo.sol:6:8: + | +6 | assembly { + | ^ (Relevant source part starts here and spans across multiple lines). + + diff --git a/crates/zkforge/tests/fixtures/can_test_repeatedly.stdout b/crates/zkforge/tests/fixtures/can_test_repeatedly.stdout new file mode 100644 index 000000000..6d645a560 --- /dev/null +++ b/crates/zkforge/tests/fixtures/can_test_repeatedly.stdout @@ -0,0 +1,8 @@ +No files changed, compilation skipped + +Running 2 tests for test/Counter.t.sol:CounterTest +[PASS] testFuzz_SetNumber(uint256) (runs: 256, μ: 26521, ~: 28387) +[PASS] test_Increment() (gas: 28379) +Test result: ok. 2 passed; 0 failed; 0 skipped; finished in 9.42ms + +Ran 1 test suites: 2 tests passed, 0 failed, 0 skipped (2 total tests) diff --git a/crates/zkforge/tests/fixtures/can_use_libs_in_multi_fork.stdout b/crates/zkforge/tests/fixtures/can_use_libs_in_multi_fork.stdout new file mode 100644 index 000000000..499648933 --- /dev/null +++ b/crates/zkforge/tests/fixtures/can_use_libs_in_multi_fork.stdout @@ -0,0 +1,9 @@ +Compiling 20 files with 0.8.13 +Solc 0.8.13 finished in 1.95s +Compiler run successful! + +Running 1 test for test/Contract.t.sol:ContractTest +[PASS] test() (gas: 70351) +Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.21s + +Ran 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests) diff --git a/crates/zkforge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.0.8.10.stdout b/crates/zkforge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.0.8.10.stdout new file mode 100644 index 000000000..3a027e182 --- /dev/null +++ b/crates/zkforge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.0.8.10.stdout @@ -0,0 +1,9 @@ +Compiling 2 files with 0.8.10 +Solc 0.8.10 finished in 185.25ms +Compiler run successful! + +Running 1 test for src/Contract.t.sol:ContractTest +[PASS] testExample() (gas: 190) +Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.89ms + +Ran 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests) diff --git a/crates/zkforge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.0.8.13.stdout b/crates/zkforge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.0.8.13.stdout new file mode 100644 index 000000000..006c3cfc4 --- /dev/null +++ b/crates/zkforge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.0.8.13.stdout @@ -0,0 +1,9 @@ +Compiling 2 files with 0.8.13 +Solc 0.8.13 finished in 185.25ms +Compiler run successful! + +Running 1 test for src/Contract.t.sol:ContractTest +[PASS] testExample() (gas: 190) +Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.89ms + +Ran 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests) diff --git a/crates/zkforge/tests/fixtures/suggest_when_no_tests_match.stdout b/crates/zkforge/tests/fixtures/suggest_when_no_tests_match.stdout new file mode 100644 index 000000000..3c58603b9 --- /dev/null +++ b/crates/zkforge/tests/fixtures/suggest_when_no_tests_match.stdout @@ -0,0 +1,13 @@ +Compiling 1 files with 0.8.10 +Solc 0.8.10 finished in 185.25ms +Compiler run successful! + +No tests match the provided pattern: + match-test: `testA.*` + no-match-test: `testB.*` + match-contract: `TestC.*` + no-match-contract: `TestD.*` + match-path: `*TestE*` + no-match-path: `*TestF*` + +Did you mean `test1`? diff --git a/crates/zkforge/tests/fixtures/warn_no_tests.stdout b/crates/zkforge/tests/fixtures/warn_no_tests.stdout new file mode 100644 index 000000000..3af59c7af --- /dev/null +++ b/crates/zkforge/tests/fixtures/warn_no_tests.stdout @@ -0,0 +1,5 @@ +Compiling 1 files with 0.8.13 +Solc 0.8.13 +Compiler run successful! + +No tests found in project! Forge looks for functions that starts with `test`. diff --git a/crates/zkforge/tests/fixtures/warn_no_tests_match.stdout b/crates/zkforge/tests/fixtures/warn_no_tests_match.stdout new file mode 100644 index 000000000..6eede4274 --- /dev/null +++ b/crates/zkforge/tests/fixtures/warn_no_tests_match.stdout @@ -0,0 +1,11 @@ +Compiling 1 files with 0.8.13 +Solc 0.8.13 +Compiler run successful! + +No tests match the provided pattern: + match-test: `testA.*` + no-match-test: `testB.*` + match-contract: `TestC.*` + no-match-contract: `TestD.*` + match-path: `*TestE*` + no-match-path: `*TestF*` diff --git a/crates/zkforge/tests/it/cheats.rs b/crates/zkforge/tests/it/cheats.rs new file mode 100644 index 000000000..5cc668665 --- /dev/null +++ b/crates/zkforge/tests/it/cheats.rs @@ -0,0 +1,24 @@ +//! forge tests for cheat codes + +use foundry_config::{fs_permissions::PathPermission, Config, FsPermissions}; + +use crate::{ + config::*, + test_helpers::{filter::Filter, PROJECT, RE_PATH_SEPARATOR}, +}; + +/// Executes all cheat code tests but not fork cheat codes +#[tokio::test(flavor = "multi_thread")] +async fn test_cheats_local() { + let mut config = Config::with_root(PROJECT.root()); + config.fs_permissions = FsPermissions::new(vec![PathPermission::read_write("./")]); + let runner = runner_with_config(config); + let filter = + Filter::new(".*", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}*")).exclude_paths("Fork"); + + // on windows exclude ffi tests since no echo and file test that expect a certain file path + #[cfg(windows)] + let filter = filter.exclude_tests("(Ffi|File|Line|Root)"); + + TestConfig::with_filter(runner.await, filter).run().await; +} diff --git a/crates/zkforge/tests/it/config.rs b/crates/zkforge/tests/it/config.rs new file mode 100644 index 000000000..f7f988ee1 --- /dev/null +++ b/crates/zkforge/tests/it/config.rs @@ -0,0 +1,290 @@ +//! Test setup + +use crate::test_helpers::{ + filter::Filter, COMPILED, COMPILED_WITH_LIBS, EVM_OPTS, LIBS_PROJECT, PROJECT, +}; +use foundry_config::{ + fs_permissions::PathPermission, Config, FsPermissions, FuzzConfig, FuzzDictionaryConfig, + InvariantConfig, RpcEndpoint, RpcEndpoints, +}; +use foundry_evm::{ + decode::decode_console_logs, executor::inspector::CheatsConfig, revm::primitives::SpecId, +}; +use foundry_utils::types::ToAlloy; +use std::{ + collections::BTreeMap, + path::{Path, PathBuf}, +}; +use zkforge::{ + result::{SuiteResult, TestStatus}, + MultiContractRunner, MultiContractRunnerBuilder, TestOptions, +}; + +/// How to execute a a test run +pub struct TestConfig { + pub runner: MultiContractRunner, + pub should_fail: bool, + pub filter: Filter, + pub opts: TestOptions, +} + +// === impl TestConfig === + +impl TestConfig { + pub fn new(runner: MultiContractRunner) -> Self { + Self::with_filter(runner, Filter::matches_all()) + } + + pub fn with_filter(runner: MultiContractRunner, filter: Filter) -> Self { + Self { runner, should_fail: false, filter, opts: test_opts() } + } + + pub async fn filter(filter: Filter) -> Self { + Self::with_filter(runner().await, filter) + } + + pub fn evm_spec(mut self, spec: SpecId) -> Self { + self.runner.evm_spec = 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 async fn test(&mut self) -> BTreeMap { + self.runner.test(&self.filter, None, self.opts.clone()).await + } + + 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.runner.test(&self.filter, None, self.opts.clone()).await; + if suite_result.is_empty() { + eyre::bail!("empty test result"); + } + for (_, SuiteResult { test_results, .. }) in suite_result { + for (test_name, 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" }; + + eyre::bail!( + "Test {} did not {} as expected.\nReason: {:?}\nLogs:\n{}", + test_name, + outcome, + result.reason, + logs.join("\n") + ) + } + } + } + + Ok(()) + } +} + +pub fn test_opts() -> TestOptions { + TestOptions { + fuzz: FuzzConfig { + runs: 256, + max_test_rejects: 65536, + seed: None, + dictionary: FuzzDictionaryConfig { + include_storage: true, + include_push_bytes: true, + dictionary_weight: 40, + max_fuzz_dictionary_addresses: 10_000, + max_fuzz_dictionary_values: 10_000, + }, + }, + invariant: InvariantConfig { + runs: 256, + depth: 15, + fail_on_revert: false, + call_override: false, + dictionary: FuzzDictionaryConfig { + dictionary_weight: 80, + include_storage: true, + include_push_bytes: true, + max_fuzz_dictionary_addresses: 10_000, + max_fuzz_dictionary_values: 10_000, + }, + shrink_sequence: true, + }, + inline_fuzz: Default::default(), + inline_invariant: Default::default(), + } +} + +#[allow(unused)] +pub(crate) fn init_tracing() { + let _ = tracing_subscriber::FmtSubscriber::builder() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .try_init(); +} + +pub fn manifest_root() -> PathBuf { + 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.to_path_buf() +} + +/// Builds a base runner +pub fn base_runner() -> MultiContractRunnerBuilder { + MultiContractRunnerBuilder::default().sender(EVM_OPTS.sender) +} + +/// Builds a non-tracing runner +pub async fn runner() -> MultiContractRunner { + let mut config = Config::with_root(PROJECT.root()); + config.fs_permissions = FsPermissions::new(vec![PathPermission::read_write(manifest_root())]); + runner_with_config(config).await +} + +/// Builds a non-tracing runner +pub async fn runner_with_config(mut config: Config) -> MultiContractRunner { + config.rpc_endpoints = rpc_endpoints(); + config.allow_paths.push(manifest_root()); + + base_runner() + .with_cheats_config(CheatsConfig::new(&config, &EVM_OPTS)) + .sender(config.sender.to_alloy()) + .build( + &PROJECT.paths.root, + (*COMPILED).clone(), + EVM_OPTS.evm_env().await.expect("Could not instantiate fork environment"), + EVM_OPTS.clone(), + ) + .unwrap() +} + +/// Builds a tracing runner +pub async fn tracing_runner() -> MultiContractRunner { + let mut opts = EVM_OPTS.clone(); + opts.verbosity = 5; + base_runner() + .build( + &PROJECT.paths.root, + (*COMPILED).clone(), + EVM_OPTS.evm_env().await.expect("Could not instantiate fork environment"), + opts, + ) + .unwrap() +} + +// Builds a runner that runs against forked state +pub async fn forked_runner(rpc: &str) -> MultiContractRunner { + let mut opts = EVM_OPTS.clone(); + + opts.env.chain_id = None; // clear chain id so the correct one gets fetched from the RPC + opts.fork_url = Some(rpc.to_string()); + + let env = opts.evm_env().await.expect("Could not instantiate fork environment"); + let fork = opts.get_fork(&Default::default(), env.clone()); + + base_runner() + .with_fork(fork) + .build(&LIBS_PROJECT.paths.root, (*COMPILED_WITH_LIBS).clone(), env, opts) + .unwrap() +} + +/// the RPC endpoints used during tests +pub fn rpc_endpoints() -> RpcEndpoints { + RpcEndpoints::new([ + ( + "rpcAlias", + RpcEndpoint::Url( + "https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf".to_string(), + ), + ), + ("rpcEnvAlias", RpcEndpoint::Env("${RPC_ENV_ALIAS}".to_string())), + ]) +} + +/// A helper to assert the outcome of multiple tests with helpful assert messages +#[track_caller] +#[allow(clippy::type_complexity)] +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 = &actuals[*contract_name].test_results[*test_name].decoded_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!( + logs.iter().eq(expected_logs.iter()), + "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/zkforge/tests/it/core.rs b/crates/zkforge/tests/it/core.rs new file mode 100644 index 000000000..248ba868e --- /dev/null +++ b/crates/zkforge/tests/it/core.rs @@ -0,0 +1,725 @@ +//! forge tests for core functionality + +use crate::{config::*, test_helpers::filter::Filter}; +use foundry_evm::trace::TraceKind; +use std::{collections::BTreeMap, env}; +use zkforge::result::SuiteResult; + +#[tokio::test(flavor = "multi_thread")] +async fn test_core() { + let mut runner = runner().await; + let results = runner.test(&Filter::new(".*", ".*", ".*core"), None, test_opts()).await; + + assert_multiple( + &results, + BTreeMap::from([ + ( + "core/FailingSetup.t.sol:FailingSetupTest", + vec![( + "setUp()", + false, + Some("Setup failed: setup failed predictably".to_string()), + None, + None, + )], + ), + ( + "core/MultipleSetup.t.sol:MultipleSetup", + vec![( + "setUp()", + false, + Some("Multiple setUp functions".to_string()), + None, + Some(1), + )], + ), + ( + "core/Reverting.t.sol:RevertingTest", + vec![("testFailRevert()", true, None, None, None)], + ), + ( + "core/SetupConsistency.t.sol:SetupConsistencyCheck", + vec![ + ("testAdd()", true, None, None, None), + ("testMultiply()", true, None, None, None), + ], + ), + ( + "core/DSStyle.t.sol:DSStyleTest", + vec![("testFailingAssertions()", true, None, None, None)], + ), + ( + "core/ContractEnvironment.t.sol:ContractEnvironmentTest", + vec![ + ("testAddresses()", true, None, None, None), + ("testEnvironment()", true, None, None, None), + ], + ), + ( + "core/PaymentFailure.t.sol:PaymentFailureTest", + vec![("testCantPay()", false, Some("EvmError: Revert".to_string()), None, None)], + ), + ("core/Abstract.t.sol:AbstractTest", vec![("testSomething()", true, None, None, None)]), + ( + "core/FailingTestAfterFailedSetup.t.sol:FailingTestAfterFailedSetupTest", + vec![( + "setUp()", + false, + Some("Setup failed: execution error".to_string()), + None, + None, + )], + ), + ]), + ); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_linking() { + let mut runner = runner().await; + let results = runner.test(&Filter::new(".*", ".*", ".*linking"), None, test_opts()).await; + + assert_multiple( + &results, + BTreeMap::from([ + ( + "linking/simple/Simple.t.sol:SimpleLibraryLinkingTest", + vec![("testCall()", true, None, None, None)], + ), + ( + "linking/nested/Nested.t.sol:NestedLibraryLinkingTest", + vec![ + ("testDirect()", true, None, None, None), + ("testNested()", true, None, None, None), + ], + ), + ( + "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 mut runner = runner().await; + let results = runner.test(&Filter::new(".*", ".*", ".*logs"), None, test_opts()).await; + + assert_multiple( + &results, + BTreeMap::from([ + ( + "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, + ), + ( + "testFailWithRequire()", + true, + None, + Some(vec!["0".into(), "1".into(), "5".into()]), + None, + ), + ( + "testFailWithRevert()", + 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, + ), + ], + ), + ( + "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 mut runner = runner().await; + + // test `setEnv` first, and confirm that it can correctly set environment variables, + // so that we can use it in subsequent `env*` tests + runner.test(&Filter::new("testSetEnv", ".*", ".*"), None, test_opts()).await; + let env_var_key = "_foundryCheatcodeSetEnvTestKey"; + let env_var_val = "_foundryCheatcodeSetEnvTestVal"; + let res = env::var(env_var_key); + assert!( + res.is_ok() && res.unwrap() == env_var_val, + "Test `testSetEnv` did not pass as expected. +Reason: `setEnv` failed to set an environment variable `{env_var_key}={env_var_val}`" + ); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_doesnt_run_abstract_contract() { + let mut runner = runner().await; + let results = runner + .test(&Filter::new(".*", ".*", ".*Abstract.t.sol".to_string().as_str()), None, test_opts()) + .await; + assert!(results.get("core/Abstract.t.sol:AbstractTestBase").is_none()); + assert!(results.get("core/Abstract.t.sol:AbstractTest").is_some()); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_trace() { + let mut runner = tracing_runner().await; + let suite_result = runner.test(&Filter::new(".*", ".*", ".*trace"), None, test_opts()).await; + + // 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::Deployment); + + assert_eq!( + deployment_traces.count(), + 1, + "Test {test_name} did not have exactly 1 deployment trace." + ); + 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 not have exactly 1 execution trace." + ); + } + } +} diff --git a/crates/zkforge/tests/it/fork.rs b/crates/zkforge/tests/it/fork.rs new file mode 100644 index 000000000..2c77b417d --- /dev/null +++ b/crates/zkforge/tests/it/fork.rs @@ -0,0 +1,100 @@ +//! forge tests for cheat codes + +use crate::{ + config::*, + test_helpers::{filter::Filter, PROJECT, RE_PATH_SEPARATOR}, +}; +use foundry_config::{fs_permissions::PathPermission, Config, FsPermissions}; +use zkforge::result::SuiteResult; + +/// Executes reverting fork test +#[tokio::test(flavor = "multi_thread")] +async fn test_cheats_fork_revert() { + let mut runner = runner().await; + let suite_result = runner + .test( + &Filter::new( + "testNonExistingContractRevert", + ".*", + &format!(".*cheats{RE_PATH_SEPARATOR}Fork"), + ), + None, + test_opts(), + ) + .await; + 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 mut config = Config::with_root(PROJECT.root()); + config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); + let runner = runner_with_config(config); + let filter = Filter::new(".*", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) + .exclude_tests(".*Revert"); + TestConfig::with_filter(runner.await, filter).run().await; +} + +/// Executes eth_getLogs cheatcode +#[tokio::test(flavor = "multi_thread")] +async fn test_get_logs_fork() { + let mut config = Config::with_root(PROJECT.root()); + config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); + let runner = runner_with_config(config); + let filter = Filter::new("testEthGetLogs", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) + .exclude_tests(".*Revert"); + TestConfig::with_filter(runner.await, filter).run().await; +} + +/// Executes rpc cheatcode +#[tokio::test(flavor = "multi_thread")] +async fn test_rpc_fork() { + let mut config = Config::with_root(PROJECT.root()); + config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); + let runner = runner_with_config(config); + let filter = Filter::new("testRpc", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) + .exclude_tests(".*Revert"); + TestConfig::with_filter(runner.await, filter).run().await; +} + +/// Tests that we can launch in forking mode +#[tokio::test(flavor = "multi_thread")] +async fn test_launch_fork() { + let rpc_url = foundry_utils::rpc::next_http_archive_rpc_endpoint(); + let runner = 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_utils::rpc::next_ws_archive_rpc_endpoint(); + let runner = 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 filter = Filter::new(".*", ".*", &format!(".*fork{RE_PATH_SEPARATOR}Transact")); + TestConfig::filter(filter).await.run().await; +} + +/// Tests that we can create the same fork (provider,block) concurretnly in different tests +#[tokio::test(flavor = "multi_thread")] +async fn test_create_same_fork() { + let filter = Filter::new(".*", ".*", &format!(".*fork{RE_PATH_SEPARATOR}ForkSame")); + TestConfig::filter(filter).await.run().await; +} diff --git a/crates/zkforge/tests/it/fs.rs b/crates/zkforge/tests/it/fs.rs new file mode 100644 index 000000000..bb0c00745 --- /dev/null +++ b/crates/zkforge/tests/it/fs.rs @@ -0,0 +1,25 @@ +//! Tests for reproducing issues + +use crate::{ + config::*, + test_helpers::{filter::Filter, PROJECT}, +}; +use foundry_config::{fs_permissions::PathPermission, Config, FsPermissions}; + +#[tokio::test(flavor = "multi_thread")] +async fn test_fs_disabled() { + let mut config = Config::with_root(PROJECT.root()); + config.fs_permissions = FsPermissions::new(vec![PathPermission::none("./")]); + let runner = runner_with_config(config).await; + let filter = Filter::new(".*", ".*", ".*fs/Disabled"); + TestConfig::with_filter(runner, filter).run().await; +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_fs_default() { + let mut config = Config::with_root(PROJECT.root()); + config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); + let runner = runner_with_config(config); + let filter = Filter::new(".*", ".*", ".*fs/Default"); + TestConfig::with_filter(runner.await, filter).run().await; +} diff --git a/crates/zkforge/tests/it/fuzz.rs b/crates/zkforge/tests/it/fuzz.rs new file mode 100644 index 000000000..a35658280 --- /dev/null +++ b/crates/zkforge/tests/it/fuzz.rs @@ -0,0 +1,83 @@ +//! Tests for invariants + +use crate::{config::*, test_helpers::filter::Filter}; +use ethers::types::U256; +use std::collections::BTreeMap; +use zkforge::result::{SuiteResult, TestStatus}; + +#[tokio::test(flavor = "multi_thread")] +async fn test_fuzz() { + let mut runner = runner().await; + + let suite_result = runner + .test( + &Filter::new(".*", ".*", ".*fuzz/") + .exclude_tests(r"invariantCounter|testIncrement\(address\)|testNeedle\(uint256\)") + .exclude_paths("invariant"), + None, + test_opts(), + ) + .await; + + 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!( + result.status == TestStatus::Success, + "Test {} did not pass as expected.\nReason: {:?}\nLogs:\n{}", + test_name, + result.reason, + result.decoded_logs.join("\n") + ), + _ => assert!( + result.status == TestStatus::Failure, + "Test {} did not fail as expected.\nReason: {:?}\nLogs:\n{}", + test_name, + result.reason, + result.decoded_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 mut runner = runner().await; + + let mut opts = test_opts(); + opts.invariant.depth = 100; + opts.invariant.runs = 1000; + opts.fuzz.runs = 1000; + opts.fuzz.seed = Some(U256::from(6u32)); + runner.test_options = opts.clone(); + + let results = + runner.test(&Filter::new(".*", ".*", ".*fuzz/FuzzCollection.t.sol"), None, opts).await; + + assert_multiple( + &results, + BTreeMap::from([( + "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), + ], + )]), + ); +} diff --git a/crates/zkforge/tests/it/inline.rs b/crates/zkforge/tests/it/inline.rs new file mode 100644 index 000000000..096751e56 --- /dev/null +++ b/crates/zkforge/tests/it/inline.rs @@ -0,0 +1,107 @@ +use crate::{ + config::runner, + test_helpers::{filter::Filter, COMPILED, PROJECT}, +}; +use foundry_config::{FuzzConfig, InvariantConfig}; +use zkforge::{ + result::{SuiteResult, TestKind, TestResult}, + TestOptions, TestOptionsBuilder, +}; + +#[tokio::test(flavor = "multi_thread")] +async fn inline_config_run_fuzz() { + let opts = test_options(); + + let filter = Filter::new(".*", ".*", ".*inline/FuzzInlineConf.t.sol"); + + let mut runner = runner().await; + runner.test_options = opts.clone(); + + let result = runner.test(&filter, None, opts).await; + let suite_result: &SuiteResult = + result.get("inline/FuzzInlineConf.t.sol:FuzzInlineConf").unwrap(); + let test_result: &TestResult = + suite_result.test_results.get("testInlineConfFuzz(uint8)").unwrap(); + match &test_result.kind { + TestKind::Fuzz { runs, .. } => { + assert_eq!(runs, &1024); + } + _ => { + unreachable!() + } + } +} + +#[tokio::test(flavor = "multi_thread")] +async fn inline_config_run_invariant() { + const ROOT: &str = "inline/InvariantInlineConf.t.sol"; + + let opts = test_options(); + let filter = Filter::new(".*", ".*", ".*inline/InvariantInlineConf.t.sol"); + let mut runner = runner().await; + runner.test_options = opts.clone(); + + let result = runner.test(&filter, None, opts).await; + + 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(); + let test_result_2 = suite_result_2.test_results.get("invariant_neverFalse()").unwrap(); + + match &test_result_1.kind { + TestKind::Invariant { runs, .. } => { + assert_eq!(runs, &333); + } + _ => { + unreachable!() + } + } + + match &test_result_2.kind { + TestKind::Invariant { runs, .. } => { + assert_eq!(runs, &42); + } + _ => { + unreachable!() + } + } +} + +#[test] +fn build_test_options() { + let root = &PROJECT.paths.root; + let profiles = vec!["default".to_string(), "ci".to_string()]; + let build_result = TestOptionsBuilder::default() + .fuzz(FuzzConfig::default()) + .invariant(InvariantConfig::default()) + .profiles(profiles) + .build(&COMPILED, root); + + assert!(build_result.is_ok()); +} + +#[test] +fn build_test_options_just_one_valid_profile() { + let root = &PROJECT.paths.root; + let valid_profiles = vec!["profile-sheldon-cooper".to_string()]; + let build_result = TestOptionsBuilder::default() + .fuzz(FuzzConfig::default()) + .invariant(InvariantConfig::default()) + .profiles(valid_profiles) + .build(&COMPILED, root); + + // We expect an error, since COMPILED contains in-line + // per-test configs for "default" and "ci" profiles + assert!(build_result.is_err()); +} + +fn test_options() -> TestOptions { + let root = &PROJECT.paths.root; + TestOptionsBuilder::default() + .fuzz(FuzzConfig::default()) + .invariant(InvariantConfig::default()) + .build(&COMPILED, root) + .expect("Config loaded") +} diff --git a/crates/zkforge/tests/it/invariant.rs b/crates/zkforge/tests/it/invariant.rs new file mode 100644 index 000000000..c07fe2175 --- /dev/null +++ b/crates/zkforge/tests/it/invariant.rs @@ -0,0 +1,184 @@ +//! Tests for invariants + +use crate::{config::*, test_helpers::filter::Filter}; +use ethers::types::U256; +use std::collections::BTreeMap; +use zkforge::fuzz::CounterExample; + +#[tokio::test(flavor = "multi_thread")] +async fn test_invariant() { + let mut runner = runner().await; + + let results = runner + .test( + &Filter::new(".*", ".*", ".*fuzz/invariant/(target|targetAbi|common)"), + None, + test_opts(), + ) + .await; + + assert_multiple( + &results, + BTreeMap::from([ + ( + "fuzz/invariant/common/InvariantInnerContract.t.sol:InvariantInnerContract", + vec![("invariantHideJesus()", false, Some("jesus betrayed.".into()), None, None)], + ), + ( + "fuzz/invariant/common/InvariantReentrancy.t.sol:InvariantReentrancy", + vec![("invariantNotStolen()", true, None, None, None)], + ), + ( + "fuzz/invariant/common/InvariantTest1.t.sol:InvariantTest", + vec![ + ("invariant_neverFalse()", false, Some("false.".into()), None, None), + ( + "statefulFuzz_neverFalseWithInvariantAlias()", + false, + Some("false.".into()), + None, + None, + ), + ], + ), + ( + "fuzz/invariant/target/ExcludeContracts.t.sol:ExcludeContracts", + vec![("invariantTrueWorld()", true, None, None, None)], + ), + ( + "fuzz/invariant/target/TargetContracts.t.sol:TargetContracts", + vec![("invariantTrueWorld()", true, None, None, None)], + ), + ( + "fuzz/invariant/target/TargetSenders.t.sol:TargetSenders", + vec![("invariantTrueWorld()", false, Some("false world.".into()), None, None)], + ), + ( + "fuzz/invariant/target/TargetInterfaces.t.sol:TargetWorldInterfaces", + vec![("invariantTrueWorld()", false, Some("false world.".into()), None, None)], + ), + ( + "fuzz/invariant/target/ExcludeSenders.t.sol:ExcludeSenders", + vec![("invariantTrueWorld()", true, None, None, None)], + ), + ( + "fuzz/invariant/target/TargetSelectors.t.sol:TargetSelectors", + vec![("invariantTrueWorld()", true, None, None, None)], + ), + ( + "fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol:ExcludeArtifacts", + vec![("invariantShouldPass()", true, None, None, None)], + ), + ( + "fuzz/invariant/targetAbi/TargetArtifacts.t.sol:TargetArtifacts", + vec![ + ("invariantShouldPass()", true, None, None, None), + ("invariantShouldFail()", false, Some("false world.".into()), None, None), + ], + ), + ( + "fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol:TargetArtifactSelectors", + vec![("invariantShouldPass()", true, None, None, None)], + ), + ( + "fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol:TargetArtifactSelectors2", + vec![("invariantShouldFail()", false, Some("its false.".into()), None, None)], + ), + ]), + ); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_invariant_override() { + let mut runner = runner().await; + + let mut opts = test_opts(); + opts.invariant.call_override = true; + runner.test_options = opts.clone(); + + let results = runner + .test( + &Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantReentrancy.t.sol"), + None, + opts, + ) + .await; + + assert_multiple( + &results, + BTreeMap::from([( + "fuzz/invariant/common/InvariantReentrancy.t.sol:InvariantReentrancy", + vec![("invariantNotStolen()", false, Some("stolen.".into()), None, None)], + )]), + ); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_invariant_storage() { + let mut runner = runner().await; + + let mut opts = test_opts(); + opts.invariant.depth = 100 + (50 * cfg!(windows) as u32); + opts.fuzz.seed = Some(U256::from(6u32)); + runner.test_options = opts.clone(); + + let results = runner + .test( + &Filter::new(".*", ".*", ".*fuzz/invariant/storage/InvariantStorageTest.t.sol"), + None, + opts, + ) + .await; + + assert_multiple( + &results, + BTreeMap::from([( + "fuzz/invariant/storage/InvariantStorageTest.t.sol:InvariantStorageTest", + vec![ + ("invariantChangeAddress()", false, Some("changedAddr".into()), None, None), + ("invariantChangeString()", false, Some("changedStr".into()), None, None), + ("invariantChangeUint()", false, Some("changedUint".into()), None, None), + ("invariantPush()", false, Some("pushUint".into()), None, None), + ], + )]), + ); +} + +#[tokio::test(flavor = "multi_thread")] +// for some reason there's different rng +#[cfg(not(windows))] +async fn test_invariant_shrink() { + let mut runner = runner().await; + + let mut opts = test_opts(); + opts.fuzz.seed = Some(U256::from(102u32)); + runner.test_options = opts.clone(); + + let results = runner + .test( + &Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantInnerContract.t.sol"), + None, + opts, + ) + .await; + + let results = + results.values().last().expect("`InvariantInnerContract.t.sol` should be testable."); + + let result = + results.test_results.values().last().expect("`InvariantInnerContract` should be testable."); + + let counter = result + .counterexample + .as_ref() + .expect("`InvariantInnerContract` should have failed with a counterexample."); + + match counter { + CounterExample::Single(_) => panic!("CounterExample should be a sequence."), + // `fuzz_seed` at 100 makes this sequence shrinkable from 4 to 2. + CounterExample::Sequence(sequence) => { + // there some diff across platforms for some reason, either 3 or 2 + assert!(sequence.len() <= 3) + } + }; +} diff --git a/crates/zkforge/tests/it/main.rs b/crates/zkforge/tests/it/main.rs new file mode 100644 index 000000000..f8197c99d --- /dev/null +++ b/crates/zkforge/tests/it/main.rs @@ -0,0 +1,11 @@ +mod cheats; +pub mod config; +mod core; +mod fork; +mod fs; +mod fuzz; +mod inline; +mod invariant; +mod repros; +mod spec; +pub mod test_helpers; diff --git a/crates/zkforge/tests/it/repros.rs b/crates/zkforge/tests/it/repros.rs new file mode 100644 index 000000000..73d0d64e2 --- /dev/null +++ b/crates/zkforge/tests/it/repros.rs @@ -0,0 +1,303 @@ +//! Tests for reproducing issues + +use crate::{ + config::*, + test_helpers::{filter::Filter, PROJECT}, +}; +use ethers::abi::{Address, Event, EventParam, Log, LogParam, ParamType, RawLog, Token}; +use foundry_config::{fs_permissions::PathPermission, Config, FsPermissions}; +use std::str::FromStr; + +/// A macro that tests a single pattern (".*/repros/") +macro_rules! test_repro { + ($issue:expr) => { + test_repro!($issue, false, None) + }; + ($issue:expr, $should_fail:expr, $sender:expr) => { + let pattern = concat!(".*repros/", $issue); + let filter = Filter::path(pattern); + + let mut config = Config::with_root(PROJECT.root()); + config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); + if let Some(sender) = $sender { + config.sender = sender; + } + + let mut config = TestConfig::with_filter(runner_with_config(config).await, filter) + .set_should_fail($should_fail); + config.run().await; + }; +} + +macro_rules! test_repro_fail { + ($issue:expr) => { + test_repro!($issue, true, None) + }; +} + +macro_rules! test_repro_with_sender { + ($issue:expr, $sender:expr) => { + test_repro!($issue, false, Some($sender)) + }; +} + +macro_rules! run_test_repro { + ($issue:expr) => { + run_test_repro!($issue, false, None) + }; + ($issue:expr, $should_fail:expr, $sender:expr) => {{ + let pattern = concat!(".*repros/", $issue); + let filter = Filter::path(pattern); + + let mut config = Config::default(); + if let Some(sender) = $sender { + config.sender = sender; + } + + let mut config = TestConfig::with_filter(runner_with_config(config).await, filter) + .set_should_fail($should_fail); + config.test().await + }}; +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_2623() { + test_repro!("Issue2623"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_2629() { + test_repro!("Issue2629"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_2723() { + test_repro!("Issue2723"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_2898() { + test_repro!("Issue2898"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_2956() { + test_repro!("Issue2956"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_2984() { + test_repro!("Issue2984"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_4640() { + test_repro!("Issue4640"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3077() { + test_repro!("Issue3077"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3055() { + test_repro_fail!("Issue3055"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3192() { + test_repro!("Issue3192"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3110() { + test_repro!("Issue3110"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3189() { + test_repro_fail!("Issue3189"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3119() { + test_repro!("Issue3119"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3190() { + test_repro!("Issue3190"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3221() { + test_repro!("Issue3221"); +} + +// +// 1.0 related +// #[tokio::test(flavor = "multi_thread")] +// async fn test_issue_3437() { +// test_repro!("Issue3437"); +// } + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3708() { + test_repro!("Issue3708"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3223() { + test_repro_with_sender!( + "Issue3223", + Address::from_str("0xF0959944122fb1ed4CfaBA645eA06EED30427BAA").unwrap() + ); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3220() { + test_repro!("Issue3220"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3347() { + let mut res = run_test_repro!("Issue3347"); + let mut res = res.remove("repros/Issue3347.sol:Issue3347Test").unwrap(); + let test = res.test_results.remove("test()").unwrap(); + assert_eq!(test.logs.len(), 1); + let event = Event { + name: "log2".to_string(), + inputs: vec![ + EventParam { name: "x".to_string(), kind: ParamType::Uint(256), indexed: false }, + EventParam { name: "y".to_string(), kind: ParamType::Uint(256), indexed: false }, + ], + anonymous: false, + }; + let raw_log = + RawLog { topics: test.logs[0].topics.clone(), data: test.logs[0].data.clone().to_vec() }; + let log = event.parse_log(raw_log).unwrap(); + assert_eq!( + log, + Log { + params: vec![ + LogParam { name: "x".to_string(), value: Token::Uint(1u64.into()) }, + LogParam { name: "y".to_string(), value: Token::Uint(2u64.into()) } + ] + } + ); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3685() { + test_repro!("Issue3685"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3653() { + test_repro!("Issue3653"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3596() { + test_repro!("Issue3596", true, None); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3661() { + test_repro!("Issue3661"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3674() { + test_repro_with_sender!( + "Issue3674", + Address::from_str("0xF0959944122fb1ed4CfaBA645eA06EED30427BAA").unwrap() + ); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3703() { + test_repro!("Issue3703"); +} + +// +// 1.0 related +// #[tokio::test(flavor = "multi_thread")] +// async fn test_issue_3723() { +// test_repro!("Issue3723"); +// } + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3753() { + test_repro!("Issue3753"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_4630() { + test_repro!("Issue4630"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_4586() { + test_repro!("Issue4586"); +} + +// https://github.com/foundry-rs/foundry/issues/4832 +// 1.0 related +// #[tokio::test(flavor = "multi_thread")] +// async fn test_issue_4832() { +// test_repro!("Issue4832"); +// } + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_5038() { + test_repro!("Issue5038"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_3792() { + test_repro!("Issue3792"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_6006() { + test_repro!("Issue6006"); +} + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_issue_5808() { + test_repro!("Issue5808"); +} diff --git a/crates/zkforge/tests/it/spec.rs b/crates/zkforge/tests/it/spec.rs new file mode 100644 index 000000000..8afe3b57a --- /dev/null +++ b/crates/zkforge/tests/it/spec.rs @@ -0,0 +1,8 @@ +use crate::{config::*, test_helpers::filter::Filter}; +use foundry_evm::revm::primitives::SpecId; + +#[tokio::test(flavor = "multi_thread")] +async fn test_shanghai_compat() { + let filter = Filter::new("", "ShanghaiCompat", ".*spec"); + TestConfig::filter(filter).await.evm_spec(SpecId::SHANGHAI).run().await; +} diff --git a/crates/zkforge/tests/it/test_helpers.rs b/crates/zkforge/tests/it/test_helpers.rs new file mode 100644 index 000000000..90ce1bc80 --- /dev/null +++ b/crates/zkforge/tests/it/test_helpers.rs @@ -0,0 +1,181 @@ +#![allow(unused)] + +use super::*; +use ethers::{ + prelude::{artifacts::Settings, Lazy, ProjectCompileOutput, SolcConfig}, + solc::{artifacts::Libraries, Project, ProjectPathsConfig}, + types::{Address, U256}, +}; +use foundry_config::Config; +use foundry_evm::{ + executor::{ + backend::Backend, + opts::{Env, EvmOpts}, + DatabaseRef, Executor, ExecutorBuilder, + }, + fuzz::FuzzedExecutor, + CALLER, +}; +use foundry_utils::types::{ToAlloy, ToEthers}; +use std::{path::PathBuf, str::FromStr}; + +pub static PROJECT: Lazy = Lazy::new(|| { + let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../testdata"); + let paths = ProjectPathsConfig::builder().root(root.clone()).sources(root).build().unwrap(); + Project::builder().paths(paths).ephemeral().no_artifacts().build().unwrap() +}); + +pub static LIBS_PROJECT: Lazy = Lazy::new(|| { + let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../testdata"); + let paths = ProjectPathsConfig::builder().root(root.clone()).sources(root).build().unwrap(); + let libs = + ["fork/Fork.t.sol:DssExecLib:0xfD88CeE74f7D78697775aBDAE53f9Da1559728E4".to_string()]; + + let settings = Settings { libraries: Libraries::parse(&libs).unwrap(), ..Default::default() }; + + let solc_config = SolcConfig::builder().settings(settings).build(); + Project::builder() + .paths(paths) + .ephemeral() + .no_artifacts() + .solc_config(solc_config) + .build() + .unwrap() +}); + +pub static COMPILED: Lazy = Lazy::new(|| { + let out = (*PROJECT).compile().unwrap(); + if out.has_compiler_errors() { + eprintln!("{out}"); + panic!("Compiled with errors"); + } + out +}); + +pub static COMPILED_WITH_LIBS: Lazy = Lazy::new(|| { + let out = (*LIBS_PROJECT).compile().unwrap(); + if out.has_compiler_errors() { + eprintln!("{out}"); + panic!("Compiled with errors"); + } + out +}); + +pub static EVM_OPTS: Lazy = Lazy::new(|| EvmOpts { + env: Env { + gas_limit: u64::MAX, + chain_id: None, + tx_origin: Config::DEFAULT_SENDER.to_alloy(), + block_number: 1, + block_timestamp: 1, + ..Default::default() + }, + sender: Config::DEFAULT_SENDER.to_alloy(), + initial_balance: U256::MAX.to_alloy(), + ffi: true, + memory_limit: 2u64.pow(24), + ..Default::default() +}); + +pub fn fuzz_executor(executor: &Executor) -> FuzzedExecutor { + let cfg = proptest::test_runner::Config { failure_persistence: None, ..Default::default() }; + + FuzzedExecutor::new( + executor, + proptest::test_runner::TestRunner::new(cfg), + CALLER.to_ethers(), + config::test_opts().fuzz, + ) +} + +pub const RE_PATH_SEPARATOR: &str = "/"; + +pub mod filter { + use super::*; + use foundry_common::TestFilter; + use regex::Regex; + + pub struct Filter { + test_regex: Regex, + contract_regex: Regex, + path_regex: Regex, + exclude_tests: Option, + exclude_paths: Option, + } + + impl Filter { + pub fn new(test_pattern: &str, contract_pattern: &str, path_pattern: &str) -> Self { + Filter { + test_regex: Regex::new(test_pattern) + .unwrap_or_else(|_| panic!("Failed to parse test pattern: `{test_pattern}`")), + contract_regex: Regex::new(contract_pattern).unwrap_or_else(|_| { + panic!("Failed to parse contract pattern: `{contract_pattern}`") + }), + path_regex: Regex::new(path_pattern) + .unwrap_or_else(|_| panic!("Failed to parse path pattern: `{path_pattern}`")), + exclude_tests: None, + exclude_paths: None, + } + } + + pub fn contract(contract_pattern: &str) -> Self { + Self::new(".*", contract_pattern, ".*") + } + + pub fn path(path_pattern: &str) -> Self { + Self::new(".*", ".*", path_pattern) + } + + /// All tests to also exclude + /// + /// This is a workaround since regex does not support negative look aheads + pub fn exclude_tests(mut self, pattern: &str) -> Self { + self.exclude_tests = Some(Regex::new(pattern).unwrap()); + self + } + + /// All paths to also exclude + /// + /// This is a workaround since regex does not support negative look aheads + pub fn exclude_paths(mut self, pattern: &str) -> Self { + self.exclude_paths = Some(Regex::new(pattern).unwrap()); + self + } + + pub fn matches_all() -> Self { + Filter { + test_regex: Regex::new(".*").unwrap(), + contract_regex: Regex::new(".*").unwrap(), + path_regex: Regex::new(".*").unwrap(), + exclude_tests: None, + exclude_paths: None, + } + } + } + + impl TestFilter for Filter { + fn matches_test(&self, test_name: impl AsRef) -> bool { + let test_name = test_name.as_ref(); + if let Some(ref exclude) = self.exclude_tests { + if exclude.is_match(test_name) { + return false + } + } + self.test_regex.is_match(test_name) + } + + fn matches_contract(&self, contract_name: impl AsRef) -> bool { + self.contract_regex.is_match(contract_name.as_ref()) + } + + fn matches_path(&self, path: impl AsRef) -> bool { + let path = path.as_ref(); + if let Some(ref exclude) = self.exclude_paths { + if exclude.is_match(path) { + return false + } + } + self.path_regex.is_match(path) + } + } +} diff --git a/crates/zkforge/tests/rpc-cache-keyfile b/crates/zkforge/tests/rpc-cache-keyfile new file mode 100644 index 000000000..211412923 --- /dev/null +++ b/crates/zkforge/tests/rpc-cache-keyfile @@ -0,0 +1,5 @@ +This file serves as the key for the github actions/cache + +Any change in this file will invalidate the cache in CI that stores RPC data. + +Last updated: 05-26-2022 \ No newline at end of file diff --git a/testdata/artifacts-counter/artifacts.json b/testdata/artifacts-counter/artifacts.json new file mode 100644 index 000000000..37c88a8a5 --- /dev/null +++ b/testdata/artifacts-counter/artifacts.json @@ -0,0 +1,478 @@ +{ + "contracts": { + "src/Counter.sol": { + "Counter": { + "abi": [ + { + "inputs": [], + "name": "increment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "number", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "number2", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newNumber", + "type": "uint256" + } + ], + "name": "setNumber", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "newNumber", + "type": "uint16" + } + ], + "name": "setNumber2", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "metadata": { + "optimizer_settings": "M3B3", + "solc_metadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"increment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"number\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"number2\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newNumber\",\"type\":\"uint256\"}],\"name\":\"setNumber\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"newNumber\",\"type\":\"uint16\"}],\"name\":\"setNumber2\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/Counter.sol\":\"Counter\"},\"evmVersion\":\"shanghai\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[],\"viaIR\":true},\"sources\":{\"src/Counter.sol\":{\"keccak256\":\"0x65cf6ea1544d4b1b06b728a2f18540e47799c27d9cb5afe9cf285a3a862497bf\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://26cc5247e7ac0bf55f9e911a7a9e948796048edf0d8f0e4ae1c38b665bcc93a9\",\"dweb:/ipfs/QmPGCoPjeZsWUTKFJgLkQCTUD7i9TWtNtNhLF7ijC3wadN\"]}},\"version\":1}", + "zk_version": "1.3.11" + }, + "evm": { + "legacyAssembly": null, + "assembly": "\t.text\n\t.file\t\"Counter.sol:Counter\"\n\t.globl\t__entry\n__entry:\n.func_begin0:\n\tptr.add\tr1, r0, stack[@ptr_calldata]\n\tshr.s\t96, r1, r1\n\tand\t@CPI0_0[0], r1, stack[@calldatasize]\n\tadd\t128, r0, r1\n\tst.1\t64, r1\n\tand!\t1, r2, r1\n\tjump.ne\t@.BB0_1\n\tadd\tstack[@calldatasize], r0, r1\n\tsub.s!\t4, r1, r1\n\tjump.lt\t@.BB0_2\n\tptr.add\tstack[@ptr_calldata], r0, r1\n\tld\tr1, r1\n\tshr.s\t224, r1, r1\n\tsub.s!\t@CPI0_2[0], r1, r2\n\tjump.le\t@.BB0_6\n\tsub.s!\t@CPI0_3[0], r1, r2\n\tjump.eq\t@.BB0_18\n\tsub.s!\t@CPI0_4[0], r1, r2\n\tjump.eq\t@.BB0_21\n\tsub.s!\t@CPI0_5[0], r1, r1\n\tjump.ne\t@.BB0_2\n\tcontext.get_context_u128\tr1\n\tsub.s!\t0, r1, r1\n\tjump.ne\t@.BB0_2\n\tsub.s\t4, r0, r1\n\tadd\tstack[@calldatasize], r1, r1\n\tadd\t@CPI0_8[0], r0, r2\n\tsub.s!\t0, r1, r3\n\tadd\t0, r0, r3\n\tadd.lt\tr2, r0, r3\n\tand\t@CPI0_8[0], r1, r1\n\tsub.s!\t0, r1, r4\n\tadd.le\t0, r0, r2\n\tsub.s!\t@CPI0_8[0], r1, r1\n\tadd\tr3, r0, r1\n\tadd.eq\tr2, r0, r1\n\tsub.s!\t0, r1, r1\n\tjump.ne\t@.BB0_2\n\tsload\tr0, r1\n\tsub.s\t1, r0, r2\n\tsub!\tr1, r2, r2\n\tjump.ne\t@.BB0_28\n\tadd\t@CPI0_9[0], r0, r1\n\tst.1\t0, r1\n\tadd\t17, r0, r1\n\tst.1\t4, r1\n\tadd\t@CPI0_10[0], r0, r1\n\tret.revert.to_label\tr1, @DEFAULT_FAR_REVERT\n.BB0_1:\n\tcontext.get_context_u128\tr1\n\tsub.s!\t0, r1, r1\n\tjump.ne\t@.BB0_2\n\tadd\t32, r0, r1\n\tst.2\t256, r1\n\tst.2\t288, r0\n\tadd\t@CPI0_1[0], r0, r1\n\tret.ok.to_label\tr1, @DEFAULT_FAR_RETURN\n.BB0_6:\n\tsub.s!\t@CPI0_6[0], r1, r2\n\tjump.eq\t@.BB0_11\n\tsub.s!\t@CPI0_7[0], r1, r1\n\tjump.ne\t@.BB0_2\n\tcontext.get_context_u128\tr1\n\tsub.s!\t0, r1, r1\n\tjump.ne\t@.BB0_2\n\tsub.s\t4, r0, r1\n\tadd\tstack[@calldatasize], r1, r1\n\tadd\t@CPI0_8[0], r0, r2\n\tsub.s!\t32, r1, r3\n\tadd\t0, r0, r3\n\tadd.lt\tr2, r0, r3\n\tand\t@CPI0_8[0], r1, r1\n\tsub.s!\t0, r1, r4\n\tadd.le\t0, r0, r2\n\tsub.s!\t@CPI0_8[0], r1, r1\n\tadd\tr3, r0, r1\n\tadd.eq\tr2, r0, r1\n\tsub.s!\t0, r1, r1\n\tjump.ne\t@.BB0_2\n\tadd\t4, r0, r1\n\tptr.add\tstack[@ptr_calldata], r1, r1\n\tld\tr1, r1\n\tsstore\tr0, r1\n\tadd\tr0, r0, r1\n\tret.ok.to_label\tr1, @DEFAULT_FAR_RETURN\n.BB0_18:\n\tcontext.get_context_u128\tr1\n\tsub.s!\t0, r1, r1\n\tjump.ne\t@.BB0_2\n\tsub.s\t4, r0, r1\n\tadd\tstack[@calldatasize], r1, r1\n\tadd\t@CPI0_8[0], r0, r2\n\tsub.s!\t0, r1, r3\n\tadd\t0, r0, r3\n\tadd.lt\tr2, r0, r3\n\tand\t@CPI0_8[0], r1, r1\n\tsub.s!\t0, r1, r4\n\tadd.le\t0, r0, r2\n\tsub.s!\t@CPI0_8[0], r1, r1\n\tadd\tr3, r0, r1\n\tadd.eq\tr2, r0, r1\n\tsub.s!\t0, r1, r1\n\tjump.ne\t@.BB0_2\n\tsload\tr0, r1\n\tst.1\t128, r1\n\tadd\t@CPI0_12[0], r0, r1\n\tret.ok.to_label\tr1, @DEFAULT_FAR_RETURN\n.BB0_21:\n\tcontext.get_context_u128\tr1\n\tsub.s!\t0, r1, r1\n\tjump.ne\t@.BB0_2\n\tsub.s\t4, r0, r1\n\tadd\tstack[@calldatasize], r1, r1\n\tadd\t@CPI0_8[0], r0, r2\n\tsub.s!\t0, r1, r3\n\tadd\t0, r0, r3\n\tadd.lt\tr2, r0, r3\n\tand\t@CPI0_8[0], r1, r1\n\tsub.s!\t0, r1, r4\n\tadd.le\t0, r0, r2\n\tsub.s!\t@CPI0_8[0], r1, r1\n\tadd\tr3, r0, r1\n\tadd.eq\tr2, r0, r1\n\tsub.s!\t0, r1, r1\n\tjump.ne\t@.BB0_2\n\tadd\t1, r0, r1\n\tsload\tr1, r1\n\tand\t65535, r1, r1\n\tld.1\t64, r2\n\tst.1\tr2, r1\n\tadd\t@CPI0_0[0], r0, r1\n\tsub.s!\t@CPI0_0[0], r2, r3\n\tadd.lt\tr2, r0, r1\n\tshl.s\t64, r1, r1\n\tor\t@CPI0_11[0], r1, r1\n\tret.ok.to_label\tr1, @DEFAULT_FAR_RETURN\n.BB0_11:\n\tcontext.get_context_u128\tr1\n\tsub.s!\t0, r1, r1\n\tjump.ne\t@.BB0_2\n\tsub.s\t4, r0, r1\n\tadd\tstack[@calldatasize], r1, r1\n\tadd\t@CPI0_8[0], r0, r2\n\tsub.s!\t32, r1, r3\n\tadd\t0, r0, r3\n\tadd.lt\tr2, r0, r3\n\tand\t@CPI0_8[0], r1, r1\n\tsub.s!\t0, r1, r4\n\tadd.le\t0, r0, r2\n\tsub.s!\t@CPI0_8[0], r1, r1\n\tadd\tr3, r0, r1\n\tadd.eq\tr2, r0, r1\n\tsub.s!\t0, r1, r1\n\tjump.ne\t@.BB0_2\n\tadd\t4, r0, r1\n\tptr.add\tstack[@ptr_calldata], r1, r1\n\tld\tr1, r1\n\tsub.s!\t65535, r1, r2\n\tjump.le\t@.BB0_14\n.BB0_2:\n\tadd\tr0, r0, r1\n\tret.revert.to_label\tr1, @DEFAULT_FAR_REVERT\n.BB0_28:\n\tadd\t1, r1, r1\n\tsstore\tr0, r1\n\tadd\tr0, r0, r1\n\tret.ok.to_label\tr1, @DEFAULT_FAR_RETURN\n.BB0_14:\n\tadd\t1, r0, r2\n\tsload\tr2, r3\n\tand\t@CPI0_13[0], r3, r3\n\tor\tr3, r1, r1\n\tsstore\tr2, r1\n\tadd\tr0, r0, r1\n\tret.ok.to_label\tr1, @DEFAULT_FAR_RETURN\n.func_end0:\n\n\t.data\n\t.p2align\t5\ncalldatasize:\n\t.cell 0\n\n\t.p2align\t5\nptr_calldata:\n.cell\t0\n\n\t.note.GNU-stack\n\t.rodata\nCPI0_0:\n\t.cell 4294967295\nCPI0_1:\n\t.cell 53919893334301279589334030174039261352344891250716429051063678533632\nCPI0_2:\n\t.cell 2206332297\nCPI0_3:\n\t.cell 2206332298\nCPI0_4:\n\t.cell 2428838979\nCPI0_5:\n\t.cell 3500007562\nCPI0_6:\n\t.cell 868990922\nCPI0_7:\n\t.cell 1068876235\nCPI0_8:\n\t.cell -57896044618658097711785492504343953926634992332820282019728792003956564819968\nCPI0_9:\n\t.cell 35408467139433450592217433187231851964531694900788300625387963629091585785856\nCPI0_10:\n\t.cell 2852213850513516153367582212096\nCPI0_11:\n\t.cell 2535301200456458802993406410752\nCPI0_12:\n\t.cell 2535301202817642044428229017600\nCPI0_13:\n\t.cell -65536\n", + "bytecode": { + "object": "0002000000000002000100000001035500000060011002700000002b0010019d0000008001000039000000400010043f0000000101200190000000310000c13d0000000001000031000000040110008c0000009b0000413d0000000101000367000000000101043b000000e0011002700000002d0210009c000000390000a13d0000002e0210009c000000540000613d0000002f0210009c000000690000613d000000300110009c0000009b0000c13d0000000001000416000000000110004c0000009b0000c13d000000040100008a00000000011000310000003302000041000000000310004c000000000300001900000000030240190000003301100197000000000410004c000000000200a019000000330110009c00000000010300190000000001026019000000000110004c0000009b0000c13d000000000100041a000000010200008a000000000221004b0000009d0000c13d000000340100004100000000001004350000001101000039000000040010043f0000003501000041000000aa000104300000000001000416000000000110004c0000009b0000c13d0000002001000039000001000010044300000120000004430000002c01000041000000a90001042e000000310210009c000000850000613d000000320110009c0000009b0000c13d0000000001000416000000000110004c0000009b0000c13d000000040100008a00000000011000310000003302000041000000200310008c000000000300001900000000030240190000003301100197000000000410004c000000000200a019000000330110009c00000000010300190000000001026019000000000110004c0000009b0000c13d00000004010000390000000101100367000000000101043b000000000010041b0000000001000019000000a90001042e0000000001000416000000000110004c0000009b0000c13d000000040100008a00000000011000310000003302000041000000000310004c000000000300001900000000030240190000003301100197000000000410004c000000000200a019000000330110009c00000000010300190000000001026019000000000110004c0000009b0000c13d000000000100041a000000800010043f0000003701000041000000a90001042e0000000001000416000000000110004c0000009b0000c13d000000040100008a00000000011000310000003302000041000000000310004c000000000300001900000000030240190000003301100197000000000410004c000000000200a019000000330110009c00000000010300190000000001026019000000000110004c0000009b0000c13d0000000101000039000000000101041a0000ffff0110018f000000400200043d00000000001204350000002b010000410000002b0320009c0000000001024019000000400110021000000036011001c7000000a90001042e0000000001000416000000000110004c0000009b0000c13d000000040100008a00000000011000310000003302000041000000200310008c000000000300001900000000030240190000003301100197000000000410004c000000000200a019000000330110009c00000000010300190000000001026019000000000110004c0000009b0000c13d00000004010000390000000101100367000000000101043b0000ffff0210008c000000a10000a13d0000000001000019000000aa000104300000000101100039000000000010041b0000000001000019000000a90001042e0000000102000039000000000302041a0000003803300197000000000113019f000000000012041b0000000001000019000000a90001042e000000a800000432000000a90001042e000000aa00010430000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0000000200000000000000000000000000000040000001000000000000000000000000000000000000000000000000000000000000000000000000008381f589000000000000000000000000000000000000000000000000000000008381f58a0000000000000000000000000000000000000000000000000000000090c5244300000000000000000000000000000000000000000000000000000000d09de08a0000000000000000000000000000000000000000000000000000000033cbbfca000000000000000000000000000000000000000000000000000000003fb5c1cb80000000000000000000000000000000000000000000000000000000000000004e487b7100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000800000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000005aeb0e246c153c89cd032ff3215aeee190b85f142a3cc3f0cb2dc21046bdd6a7" + }, + "methodIdentifiers": { + "increment()": "d09de08a", + "number()": "8381f58a", + "number2()": "90c52443", + "setNumber(uint256)": "3fb5c1cb", + "setNumber2(uint16)": "33cbbfca" + } + }, + "irOptimized": "/// @use-src 0:\"src/Counter.sol\"\nobject \"Counter_33\" {\n code {\n {\n /// @src 0:140:447 \"contract Counter {...\"\n let _1 := memoryguard(0x80)\n mstore(64, _1)\n if callvalue() { revert(0, 0) }\n let _2 := datasize(\"Counter_33_deployed\")\n codecopy(_1, dataoffset(\"Counter_33_deployed\"), _2)\n return(_1, _2)\n }\n }\n /// @use-src 0:\"src/Counter.sol\"\n object \"Counter_33_deployed\" {\n code {\n {\n /// @src 0:140:447 \"contract Counter {...\"\n let _1 := memoryguard(0x80)\n mstore(64, _1)\n if iszero(lt(calldatasize(), 4))\n {\n let _2 := 0\n switch shr(224, calldataload(_2))\n case 0x33cbbfca {\n if callvalue() { revert(_2, _2) }\n if slt(add(calldatasize(), not(3)), 32) { revert(_2, _2) }\n let value := calldataload(4)\n let _3 := and(value, 0xffff)\n if iszero(eq(value, _3)) { revert(_2, _2) }\n sstore(/** @src 0:360:379 \"number2 = newNumber\" */ 0x01, /** @src 0:140:447 \"contract Counter {...\" */ or(and(sload(/** @src 0:360:379 \"number2 = newNumber\" */ 0x01), /** @src 0:140:447 \"contract Counter {...\" */ not(65535)), _3))\n return(_2, _2)\n }\n case 0x3fb5c1cb {\n if callvalue() { revert(_2, _2) }\n if slt(add(calldatasize(), not(3)), 32) { revert(_2, _2) }\n sstore(_2, calldataload(4))\n return(_2, _2)\n }\n case 0x8381f58a {\n if callvalue() { revert(_2, _2) }\n if slt(add(calldatasize(), not(3)), _2) { revert(_2, _2) }\n mstore(_1, sload(_2))\n return(_1, 32)\n }\n case 0x90c52443 {\n if callvalue() { revert(_2, _2) }\n if slt(add(calldatasize(), not(3)), _2) { revert(_2, _2) }\n let value_1 := and(sload(/** @src 0:191:212 \"uint16 public number2\" */ 1), /** @src 0:140:447 \"contract Counter {...\" */ 0xffff)\n let memPos := mload(64)\n mstore(memPos, value_1)\n return(memPos, 32)\n }\n case 0xd09de08a {\n if callvalue() { revert(_2, _2) }\n if slt(add(calldatasize(), not(3)), _2) { revert(_2, _2) }\n let _4 := sload(_2)\n if eq(_4, not(0))\n {\n mstore(_2, shl(224, 0x4e487b71))\n mstore(4, 0x11)\n revert(_2, 0x24)\n }\n sstore(_2, add(_4, 1))\n return(_2, _2)\n }\n }\n revert(0, 0)\n }\n }\n data \".metadata\" hex\"a2646970667358221220089d6b053d935e4455a74b14962b1f25e8e7071f625c730150bdb68bd671ebf964736f6c63430008140033\"\n }\n}\n", + "hash": "0100003bc44686be52940f3f2bd8a0feef17700663cba9edb978886c08123811", + "factoryDependencies": {} + } + } + }, + "sources": { + "src/Counter.sol": { + "id": 0, + "ast": { + "absolutePath": "src/Counter.sol", + "exportedSymbols": { + "Counter": [ + 33 + ] + }, + "id": 34, + "license": "UNLICENSED", + "nodeType": "SourceUnit", + "nodes": [ + { + "id": 1, + "literals": [ + "solidity", + "^", + "0.8", + ".0" + ], + "nodeType": "PragmaDirective", + "src": "39:23:0" + }, + { + "abstract": false, + "baseContracts": [], + "canonicalName": "Counter", + "contractDependencies": [], + "contractKind": "contract", + "fullyImplemented": true, + "id": 33, + "linearizedBaseContracts": [ + 33 + ], + "name": "Counter", + "nameLocation": "149:7:0", + "nodeType": "ContractDefinition", + "nodes": [ + { + "constant": false, + "functionSelector": "8381f58a", + "id": 3, + "mutability": "mutable", + "name": "number", + "nameLocation": "178:6:0", + "nodeType": "VariableDeclaration", + "scope": 33, + "src": "163:21:0", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "163:7:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "public" + }, + { + "constant": false, + "functionSelector": "90c52443", + "id": 5, + "mutability": "mutable", + "name": "number2", + "nameLocation": "205:7:0", + "nodeType": "VariableDeclaration", + "scope": 33, + "src": "191:21:0", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint16", + "typeString": "uint16" + }, + "typeName": { + "id": 4, + "name": "uint16", + "nodeType": "ElementaryTypeName", + "src": "191:6:0", + "typeDescriptions": { + "typeIdentifier": "t_uint16", + "typeString": "uint16" + } + }, + "visibility": "public" + }, + { + "body": { + "id": 14, + "nodeType": "Block", + "src": "264:35:0", + "statements": [ + { + "expression": { + "id": 12, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "id": 10, + "name": "number", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 3, + "src": "274:6:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "id": 11, + "name": "newNumber", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 7, + "src": "283:9:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "274:18:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 13, + "nodeType": "ExpressionStatement", + "src": "274:18:0" + } + ] + }, + "functionSelector": "3fb5c1cb", + "id": 15, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "setNumber", + "nameLocation": "228:9:0", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 8, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 7, + "mutability": "mutable", + "name": "newNumber", + "nameLocation": "246:9:0", + "nodeType": "VariableDeclaration", + "scope": 15, + "src": "238:17:0", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 6, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "238:7:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "237:19:0" + }, + "returnParameters": { + "id": 9, + "nodeType": "ParameterList", + "parameters": [], + "src": "264:0:0" + }, + "scope": 33, + "src": "219:80:0", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + }, + { + "body": { + "id": 24, + "nodeType": "Block", + "src": "350:36:0", + "statements": [ + { + "expression": { + "id": 22, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "id": 20, + "name": "number2", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 5, + "src": "360:7:0", + "typeDescriptions": { + "typeIdentifier": "t_uint16", + "typeString": "uint16" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "id": 21, + "name": "newNumber", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 17, + "src": "370:9:0", + "typeDescriptions": { + "typeIdentifier": "t_uint16", + "typeString": "uint16" + } + }, + "src": "360:19:0", + "typeDescriptions": { + "typeIdentifier": "t_uint16", + "typeString": "uint16" + } + }, + "id": 23, + "nodeType": "ExpressionStatement", + "src": "360:19:0" + } + ] + }, + "functionSelector": "33cbbfca", + "id": 25, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "setNumber2", + "nameLocation": "314:10:0", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 18, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 17, + "mutability": "mutable", + "name": "newNumber", + "nameLocation": "332:9:0", + "nodeType": "VariableDeclaration", + "scope": 25, + "src": "325:16:0", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint16", + "typeString": "uint16" + }, + "typeName": { + "id": 16, + "name": "uint16", + "nodeType": "ElementaryTypeName", + "src": "325:6:0", + "typeDescriptions": { + "typeIdentifier": "t_uint16", + "typeString": "uint16" + } + }, + "visibility": "internal" + } + ], + "src": "324:18:0" + }, + "returnParameters": { + "id": 19, + "nodeType": "ParameterList", + "parameters": [], + "src": "350:0:0" + }, + "scope": 33, + "src": "305:81:0", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + }, + { + "body": { + "id": 31, + "nodeType": "Block", + "src": "420:25:0", + "statements": [ + { + "expression": { + "id": 29, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "UnaryOperation", + "operator": "++", + "prefix": false, + "src": "430:8:0", + "subExpression": { + "id": 28, + "name": "number", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 3, + "src": "430:6:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 30, + "nodeType": "ExpressionStatement", + "src": "430:8:0" + } + ] + }, + "functionSelector": "d09de08a", + "id": 32, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "increment", + "nameLocation": "401:9:0", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 26, + "nodeType": "ParameterList", + "parameters": [], + "src": "410:2:0" + }, + "returnParameters": { + "id": 27, + "nodeType": "ParameterList", + "parameters": [], + "src": "420:0:0" + }, + "scope": 33, + "src": "392:53:0", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "scope": 34, + "src": "140:307:0", + "usedErrors": [], + "usedEvents": [] + } + ], + "src": "39:409:0" + } + } + }, + "errors": [], + "version": "0.8.20", + "long_version": "0.8.20+commit.a1b79de6.Darwin.appleclang", + "zk_version": "1.3.11" + } \ No newline at end of file diff --git a/testdata/artifacts-counter/empty.json b/testdata/artifacts-counter/empty.json new file mode 100644 index 000000000..a7b1462f0 --- /dev/null +++ b/testdata/artifacts-counter/empty.json @@ -0,0 +1,22 @@ +{ + "contracts": { + "lib/forge-std/src/Base.sol": { + "CommonBase": { + "abi": [], + "metadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"lib/forge-std/src/Base.sol\":\"CommonBase\"},\"evmVersion\":\"shanghai\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[],\"viaIR\":true},\"sources\":{\"lib/forge-std/src/Base.sol\":{\"keccak256\":\"0x4ff1a785311017d1eedb1b4737956fa383067ad34eb439abfec1d989754dde1c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://f553622969b9fdb930246704a4c10dfaee6b1a4468c142fa7eb9dc292a438224\",\"dweb:/ipfs/QmcxqHnqdQsMVtgsfH9VNLmZ3g7GhgNagfq7yvNCDcCHFK\"]},\"lib/forge-std/src/StdStorage.sol\":{\"keccak256\":\"0x391a28a2e54aea51a6fb03a3a48035304ca4d24bc669ddf3d4c152c7162e514d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://475fd0d87ccb0fdc4418dea2babffb4adb4aafb817e61f7ef31c2303f10c6c26\",\"dweb:/ipfs/QmQgcgtZxpkW6DRmbJszN1F8mU6zhaTZGdWWsj77yCuWN9\"]},\"lib/forge-std/src/Vm.sol\":{\"keccak256\":\"0xb569d0b4398fad95f508fb854e832143edf69a897af4250f5f60fe195a2066c5\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://84b722ada97ea7bb841cdc0fa556aa36a02ff2d380fa24f6dc0717a71c6d9c7c\",\"dweb:/ipfs/QmfCH9Q4tvJhPM286GFsE4UCP4YncLpRu4Nwa2ZkHdRfbw\"]}},\"version\":1}", + "evm": { + "legacyAssembly": null, + "assembly": null, + "bytecode": null, + "methodIdentifiers": {} + }, + "irOptimized": "" + } + } + }, + "sources": {}, + "errors": [], + "version": "0.8.20", + "long_version": "0.8.20+commit.a1b79de6.Darwin.appleclang", + "zk_version": "1.3.11" +} \ No newline at end of file diff --git a/testdata/fixtures/Json/write_test.json b/testdata/fixtures/Json/write_test.json index 43d325f27..0c973def3 100644 --- a/testdata/fixtures/Json/write_test.json +++ b/testdata/fixtures/Json/write_test.json @@ -1,4 +1,4 @@ { - "a": 123, - "b": "0x000000000000000000000000000000000000bEEF" + "b": "0x000000000000000000000000000000000000bEEF", + "a": 123 } \ No newline at end of file