Repository: foundry-rs/starknet-foundry Branch: master Commit: 6878292c2125 Files: 1666 Total size: 5.4 MB Directory structure: gitextract_dvj2_r5r/ ├── .cargo/ │ └── config.toml ├── .gitattributes ├── .github/ │ ├── CODEOWNERS │ ├── ISSUE_TEMPLATE/ │ │ ├── 1-bug_report.yml │ │ ├── 2-feature_request.yml │ │ ├── 3-work_item.yml │ │ ├── 4-child_work_item.yml │ │ └── config.yml │ ├── actions/ │ │ └── setup-tools/ │ │ └── action.yml │ ├── dependabot.yml │ ├── pull_request_template.md │ └── workflows/ │ ├── _build-binaries-native.yml │ ├── _build-binaries.yml │ ├── _build-plugin-binaries.yml │ ├── _publish-plugin.yml │ ├── _test-binaries.yml │ ├── automate-stale.yml │ ├── ci.yml │ ├── docs.yml │ ├── nightly.yml │ ├── publish-plugin.yml │ ├── publish-std.yml │ ├── release.yml │ └── scheduled.yml ├── .gitignore ├── .tool-versions ├── CAIRO_NATIVE.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.toml ├── Cross.toml ├── LICENSE ├── README.md ├── RELEASING.md ├── ROADMAP.md ├── _typos.toml ├── crates/ │ ├── cheatnet/ │ │ ├── Cargo.toml │ │ ├── src/ │ │ │ ├── constants.rs │ │ │ ├── data/ │ │ │ │ ├── eth_erc20_casm.json │ │ │ │ └── strk_erc20_casm.json │ │ │ ├── forking/ │ │ │ │ ├── cache.rs │ │ │ │ ├── data.rs │ │ │ │ ├── mod.rs │ │ │ │ └── state.rs │ │ │ ├── lib.rs │ │ │ ├── predeployment/ │ │ │ │ ├── erc20/ │ │ │ │ │ ├── constructor_data.rs │ │ │ │ │ ├── eth.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── predeployed_contract.rs │ │ │ │ │ └── strk.rs │ │ │ │ ├── mod.rs │ │ │ │ └── predeployed_contract.rs │ │ │ ├── runtime_extensions/ │ │ │ │ ├── call_to_blockifier_runtime_extension/ │ │ │ │ │ ├── execution/ │ │ │ │ │ │ ├── cairo1_execution.rs │ │ │ │ │ │ ├── calls.rs │ │ │ │ │ │ ├── cheated_syscalls.rs │ │ │ │ │ │ ├── deprecated/ │ │ │ │ │ │ │ ├── cairo0_execution.rs │ │ │ │ │ │ │ └── mod.rs │ │ │ │ │ │ ├── entry_point.rs │ │ │ │ │ │ ├── execution_info.rs │ │ │ │ │ │ ├── execution_utils.rs │ │ │ │ │ │ ├── mod.rs │ │ │ │ │ │ └── syscall_hooks.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── panic_parser.rs │ │ │ │ │ └── rpc.rs │ │ │ │ ├── cheatable_starknet_runtime_extension.rs │ │ │ │ ├── common.rs │ │ │ │ ├── deprecated_cheatable_starknet_extension/ │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── runtime.rs │ │ │ │ ├── forge_config_extension/ │ │ │ │ │ └── config.rs │ │ │ │ ├── forge_config_extension.rs │ │ │ │ ├── forge_runtime_extension/ │ │ │ │ │ ├── cheatcodes/ │ │ │ │ │ │ ├── cheat_account_contract_address.rs │ │ │ │ │ │ ├── cheat_block_hash.rs │ │ │ │ │ │ ├── cheat_block_number.rs │ │ │ │ │ │ ├── cheat_block_timestamp.rs │ │ │ │ │ │ ├── cheat_caller_address.rs │ │ │ │ │ │ ├── cheat_execution_info.rs │ │ │ │ │ │ ├── cheat_sequencer_address.rs │ │ │ │ │ │ ├── declare.rs │ │ │ │ │ │ ├── generate_random_felt.rs │ │ │ │ │ │ ├── get_class_hash.rs │ │ │ │ │ │ ├── l1_handler_execute.rs │ │ │ │ │ │ ├── mock_call.rs │ │ │ │ │ │ ├── mod.rs │ │ │ │ │ │ ├── precalculate_address.rs │ │ │ │ │ │ ├── replace_bytecode.rs │ │ │ │ │ │ ├── spy_events.rs │ │ │ │ │ │ ├── spy_messages_to_l1.rs │ │ │ │ │ │ └── storage.rs │ │ │ │ │ ├── contracts_data.rs │ │ │ │ │ ├── file_operations.rs │ │ │ │ │ ├── fuzzer.rs │ │ │ │ │ └── mod.rs │ │ │ │ ├── mod.rs │ │ │ │ └── native/ │ │ │ │ ├── call.rs │ │ │ │ ├── deploy.rs │ │ │ │ ├── execution.rs │ │ │ │ ├── mod.rs │ │ │ │ └── native_syscall_handler.rs │ │ │ ├── state.rs │ │ │ ├── sync_client.rs │ │ │ └── trace_data.rs │ │ └── tests/ │ │ ├── builtins/ │ │ │ ├── mod.rs │ │ │ ├── panic_call.rs │ │ │ └── segment_arena.rs │ │ ├── cheatcodes/ │ │ │ ├── cheat_account_contract_address.rs │ │ │ ├── cheat_block_hash.rs │ │ │ ├── cheat_block_number.rs │ │ │ ├── cheat_block_timestamp.rs │ │ │ ├── cheat_caller_address.rs │ │ │ ├── cheat_execution_info.rs │ │ │ ├── cheat_sequencer_address.rs │ │ │ ├── declare.rs │ │ │ ├── generate_random_felt.rs │ │ │ ├── get_class_hash.rs │ │ │ ├── library_call.rs │ │ │ ├── load.rs │ │ │ ├── meta_tx_v0.rs │ │ │ ├── mock_call.rs │ │ │ ├── mod.rs │ │ │ ├── multiple_writes_same_storage.rs │ │ │ ├── precalculate_address.rs │ │ │ ├── replace_bytecode.rs │ │ │ ├── spy_events.rs │ │ │ ├── store.rs │ │ │ └── test_environment.rs │ │ ├── common/ │ │ │ ├── assertions.rs │ │ │ ├── cache.rs │ │ │ ├── mod.rs │ │ │ └── state.rs │ │ ├── contracts/ │ │ │ ├── Scarb.toml │ │ │ └── src/ │ │ │ ├── bytearray_string_panic_call.cairo │ │ │ ├── cheat_block_hash/ │ │ │ │ ├── checker.cairo │ │ │ │ ├── checker_library_call.cairo │ │ │ │ ├── checker_meta_tx_v0.cairo │ │ │ │ ├── checker_proxy.cairo │ │ │ │ └── constructor_checker.cairo │ │ │ ├── cheat_block_hash.cairo │ │ │ ├── cheat_block_number/ │ │ │ │ ├── checker.cairo │ │ │ │ ├── checker_library_call.cairo │ │ │ │ ├── checker_meta_tx_v0.cairo │ │ │ │ ├── checker_proxy.cairo │ │ │ │ └── constructor_checker.cairo │ │ │ ├── cheat_block_number.cairo │ │ │ ├── cheat_block_timestamp/ │ │ │ │ ├── checker.cairo │ │ │ │ ├── checker_library_call.cairo │ │ │ │ ├── checker_meta_tx_v0.cairo │ │ │ │ ├── checker_proxy.cairo │ │ │ │ └── constructor_checker.cairo │ │ │ ├── cheat_block_timestamp.cairo │ │ │ ├── cheat_caller_address/ │ │ │ │ ├── checker.cairo │ │ │ │ ├── checker_cairo0.cairo │ │ │ │ ├── checker_library_call.cairo │ │ │ │ ├── checker_meta_tx_v0.cairo │ │ │ │ ├── checker_proxy.cairo │ │ │ │ └── constructor_checker.cairo │ │ │ ├── cheat_caller_address.cairo │ │ │ ├── cheat_sequencer_address/ │ │ │ │ ├── checker.cairo │ │ │ │ ├── checker_library_call.cairo │ │ │ │ ├── checker_meta_tx_v0.cairo │ │ │ │ ├── checker_proxy.cairo │ │ │ │ └── constructor_checker.cairo │ │ │ ├── cheat_sequencer_address.cairo │ │ │ ├── cheat_tx_info/ │ │ │ │ ├── constructor_tx_hash_checker.cairo │ │ │ │ ├── tx_hash_checker_proxy.cairo │ │ │ │ ├── tx_info_checker.cairo │ │ │ │ ├── tx_info_checker_library_call.cairo │ │ │ │ └── tx_info_checker_meta_tx_v0.cairo │ │ │ ├── cheat_tx_info.cairo │ │ │ ├── common/ │ │ │ │ ├── constructor_simple.cairo │ │ │ │ ├── constructor_simple2.cairo │ │ │ │ └── hello_starknet.cairo │ │ │ ├── common.cairo │ │ │ ├── events/ │ │ │ │ ├── constructor_spy_events_checker.cairo │ │ │ │ ├── spy_events_cairo0.cairo │ │ │ │ ├── spy_events_checker.cairo │ │ │ │ ├── spy_events_checker_proxy.cairo │ │ │ │ ├── spy_events_lib_call.cairo │ │ │ │ └── spy_events_order_checker.cairo │ │ │ ├── events.cairo │ │ │ ├── get_class_hash/ │ │ │ │ └── get_class_hash_checker.cairo │ │ │ ├── get_class_hash.cairo │ │ │ ├── lib.cairo │ │ │ ├── library_calls.cairo │ │ │ ├── meta_tx_v0/ │ │ │ │ └── checker.cairo │ │ │ ├── meta_tx_v0.cairo │ │ │ ├── mock/ │ │ │ │ ├── constructor_mock_checker.cairo │ │ │ │ ├── mock_checker.cairo │ │ │ │ ├── mock_checker_library_call.cairo │ │ │ │ └── mock_checker_proxy.cairo │ │ │ ├── mock.cairo │ │ │ ├── panic_call.cairo │ │ │ ├── replace_bytecode/ │ │ │ │ ├── replace_bytecode_a.cairo │ │ │ │ ├── replace_bytecode_b.cairo │ │ │ │ └── replace_fork.cairo │ │ │ ├── replace_bytecode.cairo │ │ │ ├── revert.cairo │ │ │ ├── segment_arena_user.cairo │ │ │ ├── starknet/ │ │ │ │ ├── block_info_checker_library_call.cairo │ │ │ │ ├── block_info_checker_proxy.cairo │ │ │ │ ├── blocker.cairo │ │ │ │ ├── forking_checker.cairo │ │ │ │ ├── noncer.cairo │ │ │ │ └── timestamper.cairo │ │ │ ├── starknet.cairo │ │ │ ├── store_load/ │ │ │ │ └── map_simple_value_simple_key.cairo │ │ │ ├── store_load.cairo │ │ │ └── tracked_resources.cairo │ │ ├── main.rs │ │ └── starknet/ │ │ ├── block.rs │ │ ├── cheat_fork.rs │ │ ├── execution.rs │ │ ├── forking.rs │ │ ├── mod.rs │ │ ├── nonce.rs │ │ └── timestamp.rs │ ├── configuration/ │ │ ├── Cargo.toml │ │ ├── src/ │ │ │ ├── core.rs │ │ │ ├── lib.rs │ │ │ └── test_utils.rs │ │ └── tests/ │ │ └── data/ │ │ └── stubtool_snfoundry.toml │ ├── conversions/ │ │ ├── Cargo.toml │ │ ├── cairo-serde-macros/ │ │ │ ├── Cargo.toml │ │ │ └── src/ │ │ │ ├── cairo_deserialize.rs │ │ │ ├── cairo_serialize.rs │ │ │ └── lib.rs │ │ ├── src/ │ │ │ ├── byte_array.rs │ │ │ ├── class_hash.rs │ │ │ ├── contract_address.rs │ │ │ ├── entrypoint_selector.rs │ │ │ ├── eth_address.rs │ │ │ ├── felt.rs │ │ │ ├── lib.rs │ │ │ ├── non_zero_felt.rs │ │ │ ├── non_zero_u128.rs │ │ │ ├── non_zero_u64.rs │ │ │ ├── nonce.rs │ │ │ ├── padded_felt.rs │ │ │ ├── primitive.rs │ │ │ ├── serde/ │ │ │ │ ├── deserialize/ │ │ │ │ │ └── deserialize_impl.rs │ │ │ │ ├── deserialize.rs │ │ │ │ ├── serialize/ │ │ │ │ │ └── serialize_impl.rs │ │ │ │ ├── serialize.rs │ │ │ │ └── serialized_value.rs │ │ │ ├── serde.rs │ │ │ └── string.rs │ │ └── tests/ │ │ ├── derive_cairo_deserialize.rs │ │ ├── derive_cairo_serialize.rs │ │ ├── e2e/ │ │ │ ├── class_hash.rs │ │ │ ├── contract_address.rs │ │ │ ├── entrypoint_selector.rs │ │ │ ├── felt.rs │ │ │ ├── field_elements.rs │ │ │ ├── mod.rs │ │ │ ├── non_zero_felt.rs │ │ │ ├── non_zero_u128.rs │ │ │ ├── non_zero_u64.rs │ │ │ ├── nonce.rs │ │ │ ├── padded_felt.rs │ │ │ └── string.rs │ │ └── main.rs │ ├── data-transformer/ │ │ ├── ARCHITECTURE.md │ │ ├── Cargo.toml │ │ ├── src/ │ │ │ ├── cairo_types/ │ │ │ │ ├── bytes31.rs │ │ │ │ ├── helpers.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── u256.rs │ │ │ │ ├── u384.rs │ │ │ │ ├── u512.rs │ │ │ │ └── u96.rs │ │ │ ├── lib.rs │ │ │ ├── reverse_transformer/ │ │ │ │ ├── mod.rs │ │ │ │ ├── transform.rs │ │ │ │ └── types.rs │ │ │ ├── shared/ │ │ │ │ ├── extraction.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── parsing.rs │ │ │ │ └── path.rs │ │ │ └── transformer/ │ │ │ ├── mod.rs │ │ │ └── sierra_abi/ │ │ │ ├── binary.rs │ │ │ ├── complex_types.rs │ │ │ ├── data_representation.rs │ │ │ ├── literals.rs │ │ │ ├── macros.rs │ │ │ ├── mod.rs │ │ │ └── parsing.rs │ │ └── tests/ │ │ ├── data/ │ │ │ └── data_transformer/ │ │ │ ├── Scarb.toml │ │ │ └── src/ │ │ │ └── lib.cairo │ │ ├── integration/ │ │ │ ├── identity.rs │ │ │ ├── mod.rs │ │ │ ├── reverse_transformer.rs │ │ │ └── transformer.rs │ │ ├── lib.rs │ │ └── unit/ │ │ ├── bytes31.rs │ │ ├── mod.rs │ │ ├── u384.rs │ │ └── u96.rs │ ├── debugging/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── contracts_data_store.rs │ │ ├── lib.rs │ │ ├── trace/ │ │ │ ├── collect.rs │ │ │ ├── components.rs │ │ │ ├── context.rs │ │ │ ├── mod.rs │ │ │ └── types.rs │ │ └── tree/ │ │ ├── building/ │ │ │ ├── builder.rs │ │ │ ├── mod.rs │ │ │ └── node.rs │ │ ├── mod.rs │ │ └── ui/ │ │ ├── as_tree_node.rs │ │ ├── display.rs │ │ └── mod.rs │ ├── docs/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── lib.rs │ │ ├── snippet.rs │ │ ├── utils.rs │ │ └── validation.rs │ ├── forge/ │ │ ├── Cargo.toml │ │ ├── src/ │ │ │ ├── block_number_map.rs │ │ │ ├── clean.rs │ │ │ ├── combine_configs.rs │ │ │ ├── compatibility_check.rs │ │ │ ├── lib.rs │ │ │ ├── main.rs │ │ │ ├── new.rs │ │ │ ├── optimize_inlining/ │ │ │ │ ├── args.rs │ │ │ │ ├── contract_size.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── optimizer.rs │ │ │ │ ├── paths.rs │ │ │ │ └── runner.rs │ │ │ ├── profile_validation/ │ │ │ │ ├── backtrace.rs │ │ │ │ ├── coverage.rs │ │ │ │ └── mod.rs │ │ │ ├── run_tests/ │ │ │ │ ├── maat.rs │ │ │ │ ├── messages/ │ │ │ │ │ ├── collected_tests_count.rs │ │ │ │ │ ├── latest_blocks_numbers.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── overall_summary.rs │ │ │ │ │ ├── partition.rs │ │ │ │ │ ├── tests_failure_summary.rs │ │ │ │ │ ├── tests_run.rs │ │ │ │ │ └── tests_summary.rs │ │ │ │ ├── package.rs │ │ │ │ ├── resolve_config.rs │ │ │ │ ├── test_target.rs │ │ │ │ └── workspace.rs │ │ │ ├── run_tests.rs │ │ │ ├── scarb/ │ │ │ │ └── config.rs │ │ │ ├── scarb.rs │ │ │ ├── shared_cache.rs │ │ │ ├── test_filter.rs │ │ │ └── warn.rs │ │ └── tests/ │ │ ├── data/ │ │ │ ├── backtrace_panic/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── src/ │ │ │ │ └── lib.cairo │ │ │ ├── backtrace_vm_error/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── src/ │ │ │ │ └── lib.cairo │ │ │ ├── collection_with_lib/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── fob.cairo │ │ │ │ ├── src/ │ │ │ │ │ ├── fab/ │ │ │ │ │ │ ├── fab_impl.cairo │ │ │ │ │ │ └── fibfabfob.cairo │ │ │ │ │ ├── fab.cairo │ │ │ │ │ ├── fib.cairo │ │ │ │ │ ├── fob/ │ │ │ │ │ │ ├── fibfabfob.cairo │ │ │ │ │ │ └── fob_impl.cairo │ │ │ │ │ ├── fob.cairo │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ ├── fab/ │ │ │ │ │ └── fab_mod.cairo │ │ │ │ ├── fab.cairo │ │ │ │ ├── fibfabfob.cairo │ │ │ │ ├── lib.cairo │ │ │ │ ├── not_found/ │ │ │ │ │ └── not_found.cairo │ │ │ │ └── not_found.cairo │ │ │ ├── collection_without_lib/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── fob.cairo │ │ │ │ ├── src/ │ │ │ │ │ ├── fab/ │ │ │ │ │ │ ├── fab_impl.cairo │ │ │ │ │ │ └── fibfabfob.cairo │ │ │ │ │ ├── fab.cairo │ │ │ │ │ ├── fib.cairo │ │ │ │ │ ├── fob/ │ │ │ │ │ │ ├── fibfabfob.cairo │ │ │ │ │ │ └── fob_impl.cairo │ │ │ │ │ ├── fob.cairo │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ ├── fab/ │ │ │ │ │ └── fab_mod.cairo │ │ │ │ ├── fab.cairo │ │ │ │ ├── fibfabfob.cairo │ │ │ │ └── not_found/ │ │ │ │ └── not_found.cairo │ │ │ ├── component_macros/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ ├── example.cairo │ │ │ │ │ ├── lib.cairo │ │ │ │ │ └── oz_ac_component.cairo │ │ │ │ └── tests/ │ │ │ │ └── test_contract.cairo │ │ │ ├── contract_state/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ ├── balance.cairo │ │ │ │ │ ├── lib.cairo │ │ │ │ │ └── storage_node.cairo │ │ │ │ └── tests/ │ │ │ │ ├── test_fork.cairo │ │ │ │ ├── test_state.cairo │ │ │ │ ├── test_storage_node.cairo │ │ │ │ └── utils.cairo │ │ │ ├── contracts/ │ │ │ │ ├── block_hash_checker.cairo │ │ │ │ ├── block_info_checker.cairo │ │ │ │ ├── catching_error.cairo │ │ │ │ ├── cheat_block_hash_checker.cairo │ │ │ │ ├── cheat_block_number_checker.cairo │ │ │ │ ├── cheat_block_timestamp_checker.cairo │ │ │ │ ├── cheat_caller_address_checker.cairo │ │ │ │ ├── cheat_sequencer_address_checker.cairo │ │ │ │ ├── cheat_tx_info_checker.cairo │ │ │ │ ├── deploy_checker.cairo │ │ │ │ ├── dict_using_contract.cairo │ │ │ │ ├── erc20.cairo │ │ │ │ ├── gas_checker.cairo │ │ │ │ ├── gas_checker_proxy.cairo │ │ │ │ ├── gas_constructor_checker.cairo │ │ │ │ ├── hello_starknet.cairo │ │ │ │ ├── hello_starknet_extended.cairo │ │ │ │ ├── hello_starknet_for_nested_calls.cairo │ │ │ │ ├── keccak_usage.cairo │ │ │ │ ├── l1_handler_execute_checker.cairo │ │ │ │ ├── message_to_l1_checker.cairo │ │ │ │ ├── meta_tx_v0_checkers.cairo │ │ │ │ ├── meta_tx_v0_test.cairo │ │ │ │ ├── mock_checker.cairo │ │ │ │ ├── response_with_2_felts.cairo │ │ │ │ ├── reverts_caller.cairo │ │ │ │ ├── reverts_contract.cairo │ │ │ │ ├── reverts_proxy.cairo │ │ │ │ ├── serding.cairo │ │ │ │ ├── spy_events_checker.cairo │ │ │ │ ├── storage_tester.cairo │ │ │ │ ├── too_many_events.cairo │ │ │ │ ├── trace_dummy.cairo │ │ │ │ ├── trace_info_checker.cairo │ │ │ │ ├── trace_info_proxy.cairo │ │ │ │ └── two_implementations.cairo │ │ │ ├── coverage_project/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ └── lib.cairo │ │ │ ├── debugging/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ └── test_trace.cairo │ │ │ ├── debugging_fork/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ └── test_trace.cairo │ │ │ ├── deterministic_output/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── src/ │ │ │ │ └── lib.cairo │ │ │ ├── diagnostics/ │ │ │ │ ├── attributes/ │ │ │ │ │ ├── .cairofmtignore │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── tests/ │ │ │ │ │ └── contract.cairo │ │ │ │ ├── generic/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── tests/ │ │ │ │ │ └── contract.cairo │ │ │ │ ├── inline_macros/ │ │ │ │ │ ├── .cairofmtignore │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── tests/ │ │ │ │ │ └── contract.cairo │ │ │ │ ├── multiple/ │ │ │ │ │ ├── .cairofmtignore │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── tests/ │ │ │ │ │ └── contract.cairo │ │ │ │ ├── parameters/ │ │ │ │ │ ├── .cairofmtignore │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── tests/ │ │ │ │ │ └── contract.cairo │ │ │ │ ├── semantic/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── tests/ │ │ │ │ │ └── contract.cairo │ │ │ │ ├── syntax/ │ │ │ │ │ ├── .cairofmtignore │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── tests/ │ │ │ │ │ └── contract.cairo │ │ │ │ └── test_case_attr/ │ │ │ │ ├── .cairofmtignore │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ └── basic.cairo │ │ │ ├── dispatchers/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ ├── error_handler.cairo │ │ │ │ │ ├── failable.cairo │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ └── test.cairo │ │ │ ├── duplicated_test_names/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ ├── tests_a.cairo │ │ │ │ └── tests_b.cairo │ │ │ ├── empty/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── src/ │ │ │ │ └── lib.cairo │ │ │ ├── env/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── src/ │ │ │ │ └── lib.cairo │ │ │ ├── erc20_package/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ ├── erc20.cairo │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ └── test_complex.cairo │ │ │ ├── exit_first/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ └── ext_function_test.cairo │ │ │ ├── features/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ └── test.cairo │ │ │ ├── file_reading/ │ │ │ │ ├── .cairofmtignore │ │ │ │ ├── Scarb.toml │ │ │ │ ├── data/ │ │ │ │ │ ├── json/ │ │ │ │ │ │ ├── invalid.json │ │ │ │ │ │ ├── nested_valid.json │ │ │ │ │ │ ├── valid.json │ │ │ │ │ │ └── with_array.json │ │ │ │ │ ├── negative_number.txt │ │ │ │ │ ├── non_ascii.txt │ │ │ │ │ └── valid.txt │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ ├── tests/ │ │ │ │ │ └── test.cairo │ │ │ │ └── valid_file.txt │ │ │ ├── forking/ │ │ │ │ ├── .gitignore │ │ │ │ ├── .snfoundry_cache/ │ │ │ │ │ ├── README.md │ │ │ │ │ └── http___188_34_188_184_7070_rpc_v0_10_54060_v0_59_0.json │ │ │ │ ├── Scarb.toml │ │ │ │ └── src/ │ │ │ │ └── lib.cairo │ │ │ ├── fuzzing/ │ │ │ │ ├── .cairofmtignore │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ ├── exit_first_fuzz.cairo │ │ │ │ ├── exit_first_single_fail.cairo │ │ │ │ ├── generate_arg.cairo │ │ │ │ ├── generic_struct.cairo │ │ │ │ ├── incorrect_args.cairo │ │ │ │ └── multiple_attributes.cairo │ │ │ ├── hello_workspaces/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── crates/ │ │ │ │ │ ├── addition/ │ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ │ ├── src/ │ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ │ └── tests/ │ │ │ │ │ │ ├── nested/ │ │ │ │ │ │ │ └── test_nested.cairo │ │ │ │ │ │ └── nested.cairo │ │ │ │ │ └── fibonacci/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── tests/ │ │ │ │ │ ├── abc/ │ │ │ │ │ │ └── efg.cairo │ │ │ │ │ ├── abc.cairo │ │ │ │ │ ├── lib.cairo │ │ │ │ │ └── not_collected.cairo │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ └── test_failing.cairo │ │ │ ├── nonexistent_selector/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ └── test_contract.cairo │ │ │ ├── panic_decoding/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ └── test_panic_decoding.cairo │ │ │ ├── partitioning/ │ │ │ │ ├── .tool-versions │ │ │ │ ├── Scarb.toml │ │ │ │ ├── crates/ │ │ │ │ │ ├── package_a/ │ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ │ ├── src/ │ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ │ └── tests/ │ │ │ │ │ │ └── tests.cairo │ │ │ │ │ └── package_b/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── tests/ │ │ │ │ │ └── tests.cairo │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ └── tests.cairo │ │ │ ├── runtime_errors_package/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ ├── hello_starknet.cairo │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ └── with_error.cairo │ │ │ ├── should_panic_test/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ └── should_panic_test.cairo │ │ │ ├── simple_package/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ ├── hello_starknet.cairo │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ ├── contract.cairo │ │ │ │ ├── ext_function_test.cairo │ │ │ │ ├── test_simple.cairo │ │ │ │ └── without_prefix.cairo │ │ │ ├── simple_package_with_cheats/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ └── contract.cairo │ │ │ ├── steps/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── src/ │ │ │ │ └── lib.cairo │ │ │ ├── targets/ │ │ │ │ ├── custom_target/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── tests/ │ │ │ │ │ └── tests.cairo │ │ │ │ ├── custom_target_custom_names/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── tests/ │ │ │ │ │ └── tests.cairo │ │ │ │ ├── custom_target_only_integration/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── tests/ │ │ │ │ │ └── tests.cairo │ │ │ │ ├── only_integration/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── tests/ │ │ │ │ │ └── tests.cairo │ │ │ │ ├── only_lib_integration/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── tests/ │ │ │ │ │ ├── lib.cairo │ │ │ │ │ └── tests.cairo │ │ │ │ ├── only_unit/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ └── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ ├── unit_and_integration/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── tests/ │ │ │ │ │ └── tests.cairo │ │ │ │ ├── unit_and_lib_integration/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── tests/ │ │ │ │ │ ├── lib.cairo │ │ │ │ │ └── tests.cairo │ │ │ │ └── with_features/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ └── tests.cairo │ │ │ ├── test_case/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ ├── exit_first.cairo │ │ │ │ ├── multiple_attributes.cairo │ │ │ │ ├── single_attribute.cairo │ │ │ │ └── with_deploy.cairo │ │ │ ├── trace/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ └── test_trace.cairo │ │ │ ├── trace_resources/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ ├── empty.cairo │ │ │ │ │ ├── lib.cairo │ │ │ │ │ ├── trace_dummy.cairo │ │ │ │ │ ├── trace_info_checker.cairo │ │ │ │ │ └── trace_info_proxy.cairo │ │ │ │ └── tests/ │ │ │ │ ├── lib.cairo │ │ │ │ ├── test_call.cairo │ │ │ │ ├── test_deploy.cairo │ │ │ │ ├── test_failed_call.cairo │ │ │ │ ├── test_failed_lib_call.cairo │ │ │ │ ├── test_l1_handler.cairo │ │ │ │ └── test_lib_call.cairo │ │ │ ├── virtual_workspace/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── dummy_name/ │ │ │ │ │ ├── fibonacci_virtual/ │ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ │ ├── src/ │ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ │ └── tests/ │ │ │ │ │ │ ├── abc/ │ │ │ │ │ │ │ └── efg.cairo │ │ │ │ │ │ ├── abc.cairo │ │ │ │ │ │ ├── lib.cairo │ │ │ │ │ │ └── not_collected.cairo │ │ │ │ │ └── subtraction/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── tests/ │ │ │ │ │ ├── nested/ │ │ │ │ │ │ └── test_nested.cairo │ │ │ │ │ └── nested.cairo │ │ │ │ └── not_collected.cairo │ │ │ └── wasm_oracles/ │ │ │ ├── Scarb.toml │ │ │ ├── build-fixtures.sh │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ ├── tests/ │ │ │ │ └── test.cairo │ │ │ ├── wasm_oracle/ │ │ │ │ ├── Cargo.toml │ │ │ │ └── src/ │ │ │ │ └── lib.rs │ │ │ └── wasm_oracle.wasm │ │ ├── e2e/ │ │ │ ├── backtrace.rs │ │ │ ├── build_profile.rs │ │ │ ├── build_trace_data.rs │ │ │ ├── clean.rs │ │ │ ├── code_quality.rs │ │ │ ├── collection.rs │ │ │ ├── color.rs │ │ │ ├── common/ │ │ │ │ ├── mod.rs │ │ │ │ ├── output.rs │ │ │ │ └── runner.rs │ │ │ ├── completions.rs │ │ │ ├── components.rs │ │ │ ├── contract_artifacts.rs │ │ │ ├── coverage.rs │ │ │ ├── debugger.rs │ │ │ ├── debugging.rs │ │ │ ├── docs_snippets_validation.rs │ │ │ ├── env.rs │ │ │ ├── features.rs │ │ │ ├── fork_warning.rs │ │ │ ├── forking.rs │ │ │ ├── fuzzing.rs │ │ │ ├── gas_report.rs │ │ │ ├── io_operations.rs │ │ │ ├── mod.rs │ │ │ ├── new.rs │ │ │ ├── optimize_inlining.rs │ │ │ ├── oracles.rs │ │ │ ├── package_warnings.rs │ │ │ ├── partitioning.rs │ │ │ ├── plugin_diagnostics.rs │ │ │ ├── plugin_versions.rs │ │ │ ├── profiles.rs │ │ │ ├── requirements.rs │ │ │ ├── running.rs │ │ │ ├── snapshots/ │ │ │ │ ├── backtrace/ │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace@2.15.2.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace@2.16.0.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace@2.16.1.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace@2.17.0.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace_panic@2.15.2.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace_panic@2.16.0.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace_panic@2.16.1.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace_panic@2.17.0.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace_panic_without_inlines@2.15.2.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace_panic_without_inlines@2.16.0.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace_panic_without_inlines@2.16.1.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace_panic_without_inlines@2.17.0.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace_panic_without_optimizations@2.15.2.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace_panic_without_optimizations@2.16.0.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace_panic_without_optimizations@2.16.1.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace_panic_without_optimizations@2.17.0.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace_without_inlines@2.15.2.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace_without_inlines@2.16.0.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace_without_inlines@2.16.1.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_backtrace_without_inlines@2.17.0.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_handled_error_not_display@2.15.2.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_handled_error_not_display@2.16.0.snap │ │ │ │ │ ├── main__e2e__backtrace__snap_test_handled_error_not_display@2.16.1.snap │ │ │ │ │ └── main__e2e__backtrace__snap_test_handled_error_not_display@2.17.0.snap │ │ │ │ ├── gas_report/ │ │ │ │ │ ├── main__e2e__gas_report__snap_basic@2.15.2.snap │ │ │ │ │ ├── main__e2e__gas_report__snap_basic@2.16.0.snap │ │ │ │ │ ├── main__e2e__gas_report__snap_basic@2.16.1.snap │ │ │ │ │ ├── main__e2e__gas_report__snap_basic@2.17.0.snap │ │ │ │ │ ├── main__e2e__gas_report__snap_fork@2.15.2.snap │ │ │ │ │ ├── main__e2e__gas_report__snap_fork@2.16.0.snap │ │ │ │ │ ├── main__e2e__gas_report__snap_fork@2.16.1.snap │ │ │ │ │ ├── main__e2e__gas_report__snap_fork@2.17.0.snap │ │ │ │ │ ├── main__e2e__gas_report__snap_multiple_contracts_and_constructor@2.15.2.snap │ │ │ │ │ ├── main__e2e__gas_report__snap_multiple_contracts_and_constructor@2.16.0.snap │ │ │ │ │ ├── main__e2e__gas_report__snap_multiple_contracts_and_constructor@2.16.1.snap │ │ │ │ │ ├── main__e2e__gas_report__snap_multiple_contracts_and_constructor@2.17.0.snap │ │ │ │ │ ├── main__e2e__gas_report__snap_recursive_calls@2.15.2.snap │ │ │ │ │ ├── main__e2e__gas_report__snap_recursive_calls@2.16.0.snap │ │ │ │ │ ├── main__e2e__gas_report__snap_recursive_calls@2.16.1.snap │ │ │ │ │ └── main__e2e__gas_report__snap_recursive_calls@2.17.0.snap │ │ │ │ ├── main__e2e__optimize_inlining__optimize_inlining_dry_run.snap │ │ │ │ ├── main__e2e__optimize_inlining__optimize_inlining_updates_manifest.snap │ │ │ │ └── optimize_inlining/ │ │ │ │ ├── main__e2e__optimize_inlining__snap_optimize_inlining_dry_run@2.15.2.snap │ │ │ │ ├── main__e2e__optimize_inlining__snap_optimize_inlining_dry_run@2.16.0.snap │ │ │ │ ├── main__e2e__optimize_inlining__snap_optimize_inlining_dry_run@2.16.1.snap │ │ │ │ ├── main__e2e__optimize_inlining__snap_optimize_inlining_dry_run@2.17.0.snap │ │ │ │ ├── main__e2e__optimize_inlining__snap_optimize_inlining_updates_manifest@2.15.2.snap │ │ │ │ ├── main__e2e__optimize_inlining__snap_optimize_inlining_updates_manifest@2.16.0.snap │ │ │ │ ├── main__e2e__optimize_inlining__snap_optimize_inlining_updates_manifest@2.16.1.snap │ │ │ │ └── main__e2e__optimize_inlining__snap_optimize_inlining_updates_manifest@2.17.0.snap │ │ │ ├── steps.rs │ │ │ ├── templates.rs │ │ │ ├── test_case.rs │ │ │ ├── trace_print.rs │ │ │ ├── trace_resources.rs │ │ │ └── workspaces.rs │ │ ├── integration/ │ │ │ ├── available_gas.rs │ │ │ ├── builtins.rs │ │ │ ├── cheat_block_hash.rs │ │ │ ├── cheat_block_number.rs │ │ │ ├── cheat_block_timestamp.rs │ │ │ ├── cheat_caller_address.rs │ │ │ ├── cheat_execution_info.rs │ │ │ ├── cheat_fork.rs │ │ │ ├── cheat_sequencer_address.rs │ │ │ ├── config.rs │ │ │ ├── declare.rs │ │ │ ├── deploy.rs │ │ │ ├── deploy_at.rs │ │ │ ├── dict.rs │ │ │ ├── dispatchers.rs │ │ │ ├── env.rs │ │ │ ├── fuzzing.rs │ │ │ ├── gas.rs │ │ │ ├── generate_random_felt.rs │ │ │ ├── get_available_gas.rs │ │ │ ├── get_class_hash.rs │ │ │ ├── get_current_vm_step.rs │ │ │ ├── interact_with_state.rs │ │ │ ├── l1_handler_executor.rs │ │ │ ├── message_to_l1.rs │ │ │ ├── meta_tx_v0.rs │ │ │ ├── mock_call.rs │ │ │ ├── mod.rs │ │ │ ├── precalculate_address.rs │ │ │ ├── pure_cairo.rs │ │ │ ├── replace_bytecode.rs │ │ │ ├── resources.rs │ │ │ ├── reverts.rs │ │ │ ├── runtime.rs │ │ │ ├── set_balance.rs │ │ │ ├── setup_fork.rs │ │ │ ├── should_panic.rs │ │ │ ├── signing.rs │ │ │ ├── spy_events.rs │ │ │ ├── store_load.rs │ │ │ ├── syscalls.rs │ │ │ ├── test_state.rs │ │ │ ├── too_many_events.rs │ │ │ └── trace.rs │ │ ├── main.rs │ │ └── utils/ │ │ ├── mod.rs │ │ ├── runner.rs │ │ └── running_tests.rs │ ├── forge-runner/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── backtrace/ │ │ │ ├── data.rs │ │ │ ├── display.rs │ │ │ └── mod.rs │ │ ├── build_trace_data.rs │ │ ├── coverage_api.rs │ │ ├── debugging/ │ │ │ ├── args.rs │ │ │ ├── component.rs │ │ │ ├── mod.rs │ │ │ └── trace_verbosity.rs │ │ ├── expected_result.rs │ │ ├── filtering.rs │ │ ├── forge_config.rs │ │ ├── gas/ │ │ │ ├── report.rs │ │ │ ├── resources.rs │ │ │ ├── stats.rs │ │ │ └── utils.rs │ │ ├── gas.rs │ │ ├── lib.rs │ │ ├── messages.rs │ │ ├── package_tests/ │ │ │ ├── raw.rs │ │ │ ├── with_config.rs │ │ │ └── with_config_resolved.rs │ │ ├── package_tests.rs │ │ ├── partition.rs │ │ ├── printing.rs │ │ ├── profiler_api.rs │ │ ├── running/ │ │ │ ├── config_run.rs │ │ │ ├── execution.rs │ │ │ ├── hints.rs │ │ │ ├── setup.rs │ │ │ ├── syscall_handler.rs │ │ │ └── target.rs │ │ ├── running.rs │ │ ├── scarb.rs │ │ ├── test_case_summary.rs │ │ ├── test_target_summary.rs │ │ └── tests_summary.rs │ ├── foundry-ui/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── components/ │ │ │ ├── error.rs │ │ │ ├── labeled.rs │ │ │ ├── mod.rs │ │ │ ├── tagged.rs │ │ │ └── warning.rs │ │ ├── lib.rs │ │ ├── message.rs │ │ ├── output.rs │ │ └── styling.rs │ ├── native-api/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── lib.rs │ ├── runtime/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── lib.rs │ │ ├── starknet/ │ │ │ ├── constants.rs │ │ │ ├── context.rs │ │ │ ├── mod.rs │ │ │ └── state.rs │ │ └── vm.rs │ ├── scarb-api/ │ │ ├── Cargo.toml │ │ ├── src/ │ │ │ ├── artifacts/ │ │ │ │ ├── deserialized.rs │ │ │ │ └── representation.rs │ │ │ ├── artifacts.rs │ │ │ ├── command.rs │ │ │ ├── lib.rs │ │ │ ├── manifest.rs │ │ │ ├── metadata.rs │ │ │ └── version.rs │ │ └── tests/ │ │ └── data/ │ │ ├── basic_package/ │ │ │ ├── Scarb.toml │ │ │ └── src/ │ │ │ └── lib.cairo │ │ └── empty_lib/ │ │ ├── Scarb.toml │ │ └── src/ │ │ └── lib.cairo │ ├── shared/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── auto_completions.rs │ │ ├── command.rs │ │ ├── consts.rs │ │ ├── lib.rs │ │ ├── rpc.rs │ │ ├── spinner.rs │ │ ├── test_utils/ │ │ │ ├── mod.rs │ │ │ ├── node_url.rs │ │ │ └── output_assert.rs │ │ ├── utils.rs │ │ └── vm.rs │ ├── sncast/ │ │ ├── .cargo/ │ │ │ └── config.toml │ │ ├── .gitignore │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src/ │ │ │ ├── helpers/ │ │ │ │ ├── account.rs │ │ │ │ ├── artifacts.rs │ │ │ │ ├── block_explorer.rs │ │ │ │ ├── braavos.rs │ │ │ │ ├── command.rs │ │ │ │ ├── config.rs │ │ │ │ ├── configuration.rs │ │ │ │ ├── constants.rs │ │ │ │ ├── devnet/ │ │ │ │ │ ├── detection/ │ │ │ │ │ │ ├── direct.rs │ │ │ │ │ │ ├── docker.rs │ │ │ │ │ │ └── flag_parsing.rs │ │ │ │ │ ├── detection.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── provider.rs │ │ │ │ ├── fee.rs │ │ │ │ ├── interactive.rs │ │ │ │ ├── ledger/ │ │ │ │ │ ├── account.rs │ │ │ │ │ ├── emulator_transport.rs │ │ │ │ │ ├── hd_path.rs │ │ │ │ │ ├── key_locator.rs │ │ │ │ │ └── mod.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── output_format.rs │ │ │ │ ├── proof.rs │ │ │ │ ├── rpc.rs │ │ │ │ ├── scarb_utils.rs │ │ │ │ ├── signer.rs │ │ │ │ └── token.rs │ │ │ ├── lib.rs │ │ │ ├── main.rs │ │ │ ├── response/ │ │ │ │ ├── account/ │ │ │ │ │ ├── create.rs │ │ │ │ │ ├── delete.rs │ │ │ │ │ ├── deploy.rs │ │ │ │ │ ├── import.rs │ │ │ │ │ └── mod.rs │ │ │ │ ├── balance.rs │ │ │ │ ├── call.rs │ │ │ │ ├── cast_message.rs │ │ │ │ ├── class_hash_at.rs │ │ │ │ ├── completions.rs │ │ │ │ ├── declare.rs │ │ │ │ ├── deploy.rs │ │ │ │ ├── errors.rs │ │ │ │ ├── explorer_link.rs │ │ │ │ ├── invoke.rs │ │ │ │ ├── ledger.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── multicall/ │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── new.rs │ │ │ │ │ └── run.rs │ │ │ │ ├── nonce.rs │ │ │ │ ├── script/ │ │ │ │ │ ├── init.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── run.rs │ │ │ │ ├── show_config.rs │ │ │ │ ├── transaction.rs │ │ │ │ ├── transformed_call.rs │ │ │ │ ├── tx_status.rs │ │ │ │ ├── ui.rs │ │ │ │ ├── utils/ │ │ │ │ │ ├── class_hash.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── selector.rs │ │ │ │ │ └── serialize.rs │ │ │ │ └── verify.rs │ │ │ ├── starknet_commands/ │ │ │ │ ├── account/ │ │ │ │ │ ├── create.rs │ │ │ │ │ ├── delete.rs │ │ │ │ │ ├── deploy.rs │ │ │ │ │ ├── import.rs │ │ │ │ │ ├── list.rs │ │ │ │ │ └── mod.rs │ │ │ │ ├── call.rs │ │ │ │ ├── declare.rs │ │ │ │ ├── declare_from.rs │ │ │ │ ├── deploy.rs │ │ │ │ ├── get/ │ │ │ │ │ ├── balance.rs │ │ │ │ │ ├── class_hash_at.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── nonce.rs │ │ │ │ │ ├── transaction.rs │ │ │ │ │ └── tx_status.rs │ │ │ │ ├── invoke.rs │ │ │ │ ├── ledger/ │ │ │ │ │ ├── app_version.rs │ │ │ │ │ ├── get_public_key.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── sign_hash.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── multicall/ │ │ │ │ │ ├── contract_registry.rs │ │ │ │ │ ├── deploy.rs │ │ │ │ │ ├── execute.rs │ │ │ │ │ ├── invoke.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── new.rs │ │ │ │ │ └── run.rs │ │ │ │ ├── script/ │ │ │ │ │ ├── init.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── run/ │ │ │ │ │ │ └── script_runtime.rs │ │ │ │ │ └── run.rs │ │ │ │ ├── show_config.rs │ │ │ │ ├── utils/ │ │ │ │ │ ├── class_hash.rs │ │ │ │ │ ├── felt_or_id.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── selector.rs │ │ │ │ │ └── serialize.rs │ │ │ │ └── verify/ │ │ │ │ ├── explorer.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── voyager.rs │ │ │ │ └── walnut.rs │ │ │ └── state/ │ │ │ ├── hashing.rs │ │ │ ├── mod.rs │ │ │ └── state_file.rs │ │ └── tests/ │ │ ├── code_quality.rs │ │ ├── data/ │ │ │ ├── accounts/ │ │ │ │ ├── accounts.json │ │ │ │ ├── faulty_accounts.json │ │ │ │ ├── faulty_accounts_invalid_felt.json │ │ │ │ └── invalid_format.json │ │ │ ├── contracts/ │ │ │ │ ├── build_fails/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ └── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ ├── constructor_with_params/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ └── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ ├── contract_with_constructor_params/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ └── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ ├── map/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ └── src/ │ │ │ │ │ ├── lib.cairo │ │ │ │ │ ├── test_helpers.cairo │ │ │ │ │ └── tests.cairo │ │ │ │ ├── multiple_packages/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── crates/ │ │ │ │ │ │ ├── package1/ │ │ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ │ │ └── src/ │ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ │ └── package2/ │ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ │ └── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ ├── no_casm/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ └── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ ├── no_sierra/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ └── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── virtual_workspace/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── crates/ │ │ │ │ ├── cast_addition/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ └── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── cast_fibonacci/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── src/ │ │ │ │ └── lib.cairo │ │ │ ├── files/ │ │ │ │ ├── correct_snfoundry.toml │ │ │ │ ├── data_transformer_contract_abi.json │ │ │ │ ├── data_transformer_contract_abi_missing_function.json │ │ │ │ ├── data_transformer_contract_abi_missing_type.json │ │ │ │ ├── invalid_snfoundry.toml │ │ │ │ ├── pre_0.34.0_state_with_tx.json │ │ │ │ ├── state_corrupt_missing_field.json │ │ │ │ ├── state_no_txs.json │ │ │ │ ├── state_with_tx.json │ │ │ │ ├── state_with_txs.json │ │ │ │ └── state_wrong_version.json │ │ │ ├── keystore/ │ │ │ │ ├── my_account.json │ │ │ │ ├── my_account_braavos_invalid_multisig.json │ │ │ │ ├── my_account_braavos_multiple_signers.json │ │ │ │ ├── my_account_braavos_undeployed_happy_case.json │ │ │ │ ├── my_account_invalid.json │ │ │ │ ├── my_account_oz_undeployed_happy_case.json │ │ │ │ ├── my_account_ready_undeployed_happy_case.json │ │ │ │ ├── my_account_undeployed.json │ │ │ │ ├── my_account_undeployed_happy_case_other_args.json │ │ │ │ ├── my_key.json │ │ │ │ ├── my_key_invalid.json │ │ │ │ ├── predeployed_account.json │ │ │ │ └── predeployed_key.json │ │ │ ├── ledger-app/ │ │ │ │ └── nanox#strk#0.25.13.elf │ │ │ ├── multicall_configs/ │ │ │ │ ├── deploy_invalid.toml │ │ │ │ ├── deploy_invoke.toml │ │ │ │ ├── deploy_invoke_calldata_ids.toml │ │ │ │ ├── deploy_invoke_numeric_inputs.toml │ │ │ │ ├── deploy_invoke_numeric_overflow.toml │ │ │ │ ├── deploy_succ_invoke_fail.toml │ │ │ │ ├── invoke_invalid.toml │ │ │ │ └── invoke_ledger.toml │ │ │ └── scripts/ │ │ │ ├── call/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── src/ │ │ │ │ ├── invalid_address.cairo │ │ │ │ ├── invalid_calldata.cairo │ │ │ │ ├── invalid_entry_point.cairo │ │ │ │ └── lib.cairo │ │ │ ├── declare/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── src/ │ │ │ │ ├── fee_settings.cairo │ │ │ │ ├── insufficient_account_balance.cairo │ │ │ │ ├── lib.cairo │ │ │ │ ├── no_contract.cairo │ │ │ │ ├── same_contract_twice.cairo │ │ │ │ ├── time_out.cairo │ │ │ │ ├── with_invalid_max_fee.cairo │ │ │ │ └── with_invalid_nonce.cairo │ │ │ ├── deploy/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── src/ │ │ │ │ ├── fee_settings.cairo │ │ │ │ ├── invalid_calldata.cairo │ │ │ │ ├── invalid_class_hash.cairo │ │ │ │ ├── invalid_nonce.cairo │ │ │ │ ├── lib.cairo │ │ │ │ ├── same_class_hash_and_salt.cairo │ │ │ │ └── with_calldata.cairo │ │ │ ├── invoke/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── src/ │ │ │ │ ├── contract_does_not_exist.cairo │ │ │ │ ├── lib.cairo │ │ │ │ ├── max_fee_too_low.cairo │ │ │ │ ├── wrong_calldata.cairo │ │ │ │ └── wrong_function_name.cairo │ │ │ ├── map_script/ │ │ │ │ ├── contracts/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ └── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── scripts/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── src/ │ │ │ │ ├── display_debug_traits_for_subcommand_responses.cairo │ │ │ │ ├── lib.cairo │ │ │ │ └── map_script.cairo │ │ │ ├── misc/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── src/ │ │ │ │ ├── call_fail.cairo │ │ │ │ ├── call_happy.cairo │ │ │ │ ├── lib.cairo │ │ │ │ └── using_starknet_syscall.cairo │ │ │ ├── missing_field/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── src/ │ │ │ │ ├── lib.cairo │ │ │ │ └── missing_field.cairo │ │ │ ├── old_sncast_std/ │ │ │ │ └── scripts/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── src/ │ │ │ │ ├── lib.cairo │ │ │ │ └── map_script.cairo │ │ │ ├── packages/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── crates/ │ │ │ │ └── scripts/ │ │ │ │ ├── script1/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ └── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── script2/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── src/ │ │ │ │ └── lib.cairo │ │ │ ├── state_file/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── rerun_failed_tx_alpha-sepolia_state.json │ │ │ │ └── src/ │ │ │ │ ├── all_tx_fail.cairo │ │ │ │ ├── lib.cairo │ │ │ │ └── rerun_failed_tx.cairo │ │ │ ├── state_script/ │ │ │ │ ├── contracts/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ └── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── scripts/ │ │ │ │ ├── Scarb.toml │ │ │ │ └── src/ │ │ │ │ ├── lib.cairo │ │ │ │ └── state_script.cairo │ │ │ └── tx_status/ │ │ │ ├── Scarb.toml │ │ │ └── src/ │ │ │ ├── incorrect_transaction_hash.cairo │ │ │ ├── lib.cairo │ │ │ ├── status_reverted.cairo │ │ │ └── status_succeeded.cairo │ │ ├── docs_snippets/ │ │ │ ├── ledger.rs │ │ │ ├── mod.rs │ │ │ └── validation.rs │ │ ├── e2e/ │ │ │ ├── account/ │ │ │ │ ├── create.rs │ │ │ │ ├── delete.rs │ │ │ │ ├── deploy.rs │ │ │ │ ├── helpers.rs │ │ │ │ ├── import.rs │ │ │ │ ├── list.rs │ │ │ │ └── mod.rs │ │ │ ├── balance.rs │ │ │ ├── call.rs │ │ │ ├── class_hash.rs │ │ │ ├── class_hash_at.rs │ │ │ ├── completions.rs │ │ │ ├── declare.rs │ │ │ ├── declare_from.rs │ │ │ ├── deploy.rs │ │ │ ├── devnet_accounts.rs │ │ │ ├── fee.rs │ │ │ ├── invoke.rs │ │ │ ├── ledger/ │ │ │ │ ├── account.rs │ │ │ │ ├── basic.rs │ │ │ │ ├── mod.rs │ │ │ │ └── network.rs │ │ │ ├── main_tests.rs │ │ │ ├── mod.rs │ │ │ ├── multicall/ │ │ │ │ ├── execute.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── new.rs │ │ │ │ └── run.rs │ │ │ ├── nonce.rs │ │ │ ├── proof.rs │ │ │ ├── script/ │ │ │ │ ├── call.rs │ │ │ │ ├── declare.rs │ │ │ │ ├── deploy.rs │ │ │ │ ├── general.rs │ │ │ │ ├── init.rs │ │ │ │ ├── invoke.rs │ │ │ │ ├── mod.rs │ │ │ │ └── tx_status.rs │ │ │ ├── selector.rs │ │ │ ├── serialize.rs │ │ │ ├── show_config.rs │ │ │ ├── transaction.rs │ │ │ ├── tx_status.rs │ │ │ └── verify/ │ │ │ ├── mod.rs │ │ │ ├── voyager.rs │ │ │ └── walnut.rs │ │ ├── helpers/ │ │ │ ├── constants.rs │ │ │ ├── devnet.rs │ │ │ ├── devnet_detection.rs │ │ │ ├── devnet_provider.rs │ │ │ ├── env.rs │ │ │ ├── fixtures.rs │ │ │ ├── mod.rs │ │ │ ├── runner.rs │ │ │ └── shell.rs │ │ ├── integration/ │ │ │ ├── fee.rs │ │ │ ├── lib_tests.rs │ │ │ ├── mod.rs │ │ │ └── wait_for_tx.rs │ │ ├── main.rs │ │ └── shell/ │ │ ├── call.sh │ │ ├── call_unsigned.sh │ │ ├── deploy.sh │ │ ├── invoke.sh │ │ └── serialize.sh │ ├── snforge-scarb-plugin/ │ │ ├── Cargo.toml │ │ ├── Scarb.toml │ │ ├── clippy.toml │ │ ├── src/ │ │ │ ├── args/ │ │ │ │ ├── named.rs │ │ │ │ └── unnamed.rs │ │ │ ├── args.rs │ │ │ ├── asserts.rs │ │ │ ├── attributes/ │ │ │ │ ├── available_gas.rs │ │ │ │ ├── disable_predeployed_contracts.rs │ │ │ │ ├── fork/ │ │ │ │ │ └── block_id.rs │ │ │ │ ├── fork.rs │ │ │ │ ├── fuzzer/ │ │ │ │ │ └── wrapper.rs │ │ │ │ ├── fuzzer.rs │ │ │ │ ├── ignore.rs │ │ │ │ ├── internal_config_statement.rs │ │ │ │ ├── should_panic/ │ │ │ │ │ └── expected.rs │ │ │ │ ├── should_panic.rs │ │ │ │ ├── test.rs │ │ │ │ ├── test_case/ │ │ │ │ │ └── name.rs │ │ │ │ └── test_case.rs │ │ │ ├── attributes.rs │ │ │ ├── cairo_expression.rs │ │ │ ├── common.rs │ │ │ ├── config_statement.rs │ │ │ ├── external_inputs.rs │ │ │ ├── lib.rs │ │ │ ├── parse.rs │ │ │ ├── types.rs │ │ │ └── utils.rs │ │ └── tests/ │ │ └── integration/ │ │ ├── main.rs │ │ ├── multiple_attributes.rs │ │ ├── single_attributes/ │ │ │ ├── available_gas.rs │ │ │ ├── disable_predeployed_contracts.rs │ │ │ ├── fork.rs │ │ │ ├── fuzzer.rs │ │ │ ├── ignore.rs │ │ │ ├── internal_config_statement.rs │ │ │ ├── should_panic.rs │ │ │ ├── test.rs │ │ │ └── test_case.rs │ │ ├── single_attributes.rs │ │ └── utils.rs │ ├── testing/ │ │ └── packages_validation/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── lib.rs │ └── universal-sierra-compiler-api/ │ ├── Cargo.toml │ └── src/ │ ├── command.rs │ ├── compile.rs │ ├── lib.rs │ └── representation.rs ├── design_documents/ │ ├── accessing_emitted_events.md │ ├── cairo_deployment_scripts.md │ ├── contract_verification.md │ ├── loading_data_from_files.md │ ├── parametrizing_tests_with_fixed_values.md │ ├── prepare_cheatcode.md │ ├── sncast_interface_overhaul.md │ ├── stark_curve_signatures.md │ ├── state_forking/ │ │ ├── multi-forking.md │ │ └── state_forking.md │ ├── store_load_cheatcodes.md │ ├── template.md │ ├── test_design.md │ └── transactional_testing/ │ └── transactional_testing.md ├── docs/ │ ├── .gitignore │ ├── README.md │ ├── book.toml │ ├── codeSnippets.js │ ├── count.js │ ├── example_workflows/ │ │ ├── basic_workflow.yml │ │ └── partitioned_workflow.yml │ ├── listings/ │ │ ├── basic_example/ │ │ │ ├── Scarb.toml │ │ │ └── src/ │ │ │ ├── basic_example.cairo │ │ │ └── lib.cairo │ │ ├── call/ │ │ │ ├── Scarb.toml │ │ │ └── src/ │ │ │ └── lib.cairo │ │ ├── cheatcodes_reference/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ ├── l1_handler_example.cairo │ │ │ │ ├── lib.cairo │ │ │ │ └── mock_call_example.cairo │ │ │ └── tests/ │ │ │ ├── test_l1_handler.cairo │ │ │ └── test_mock_call.cairo │ │ ├── declare/ │ │ │ ├── Scarb.toml │ │ │ └── src/ │ │ │ └── lib.cairo │ │ ├── deploy/ │ │ │ ├── Scarb.toml │ │ │ └── src/ │ │ │ └── lib.cairo │ │ ├── deployment_with_constructor_args/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ ├── test_with_deploy_for_test.cairo │ │ │ └── test_with_serialization.cairo │ │ ├── detailed_resources_example/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ └── test_contract.cairo │ │ ├── direct_storage_access/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ ├── complex_structures.cairo │ │ │ │ ├── felts_only.cairo │ │ │ │ ├── lib.cairo │ │ │ │ └── using_enums.cairo │ │ │ └── tests/ │ │ │ ├── complex_structures.cairo │ │ │ ├── felts_only/ │ │ │ │ ├── field.cairo │ │ │ │ └── map_entry.cairo │ │ │ ├── felts_only.cairo │ │ │ ├── lib.cairo │ │ │ ├── storage_address.cairo │ │ │ ├── using_enums.cairo │ │ │ └── using_storage_address_from_base.cairo │ │ ├── error_handling/ │ │ │ ├── Scarb.toml │ │ │ └── src/ │ │ │ ├── error_handling.cairo │ │ │ └── lib.cairo │ │ ├── failing_example/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ └── lib.cairo │ │ ├── first_test/ │ │ │ ├── Scarb.toml │ │ │ └── src/ │ │ │ └── lib.cairo │ │ ├── fork_testing/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ ├── explicit/ │ │ │ │ ├── block_hash.cairo │ │ │ │ ├── block_number.cairo │ │ │ │ └── block_tag.cairo │ │ │ ├── explicit.cairo │ │ │ ├── lib.cairo │ │ │ ├── name.cairo │ │ │ └── overridden_name.cairo │ │ ├── full_example/ │ │ │ ├── Scarb.toml │ │ │ └── src/ │ │ │ ├── full_example.cairo │ │ │ └── lib.cairo │ │ ├── fuzz_testing/ │ │ │ ├── Scarb.toml │ │ │ └── src/ │ │ │ ├── basic_example.cairo │ │ │ ├── lib.cairo │ │ │ └── with_parameters.cairo │ │ ├── get_nonce/ │ │ │ ├── Scarb.toml │ │ │ └── src/ │ │ │ └── lib.cairo │ │ ├── hello_sncast/ │ │ │ ├── Scarb.toml │ │ │ ├── accounts.json │ │ │ ├── snfoundry.toml │ │ │ └── src/ │ │ │ ├── constructor_contract.cairo │ │ │ ├── data_transformer_contract.cairo │ │ │ ├── hello_sncast.cairo │ │ │ └── lib.cairo │ │ ├── hello_snforge/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ └── test_contract.cairo │ │ ├── hello_starknet/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ └── test_contract.cairo │ │ ├── hello_workspaces_docs/ │ │ │ ├── Scarb.toml │ │ │ ├── crates/ │ │ │ │ ├── addition_docs/ │ │ │ │ │ ├── Scarb.toml │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── lib.cairo │ │ │ │ │ └── tests/ │ │ │ │ │ ├── nested/ │ │ │ │ │ │ └── test_nested.cairo │ │ │ │ │ └── nested.cairo │ │ │ │ └── fibonacci_docs/ │ │ │ │ ├── Scarb.toml │ │ │ │ ├── src/ │ │ │ │ │ └── lib.cairo │ │ │ │ └── tests/ │ │ │ │ ├── abc/ │ │ │ │ │ └── efg.cairo │ │ │ │ ├── abc.cairo │ │ │ │ ├── lib.cairo │ │ │ │ └── not_collected.cairo │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ └── test_failing.cairo │ │ ├── ignoring_example/ │ │ │ ├── Scarb.toml │ │ │ └── src/ │ │ │ └── lib.cairo │ │ ├── invoke/ │ │ │ ├── Scarb.toml │ │ │ └── src/ │ │ │ └── lib.cairo │ │ ├── map3/ │ │ │ ├── Scarb.toml │ │ │ ├── snfoundry.toml │ │ │ └── src/ │ │ │ └── lib.cairo │ │ ├── panicking_test/ │ │ │ ├── Scarb.toml │ │ │ └── src/ │ │ │ └── lib.cairo │ │ ├── parametrized_testing_advanced/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ └── example.cairo │ │ ├── parametrized_testing_basic/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ └── example.cairo │ │ ├── parametrized_testing_fuzzer/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ └── example.cairo │ │ ├── should_panic_example/ │ │ │ ├── Scarb.toml │ │ │ └── src/ │ │ │ └── lib.cairo │ │ ├── snforge_library_reference/ │ │ │ ├── Scarb.toml │ │ │ ├── data/ │ │ │ │ ├── hello_starknet.txt │ │ │ │ └── user.json │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ ├── test_fs_file_trait.cairo │ │ │ ├── test_fs_parse_json.cairo │ │ │ ├── test_fs_read_json.cairo │ │ │ └── test_fs_read_txt.cairo │ │ ├── testing_contract_internals/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ ├── contract.cairo │ │ │ │ ├── lib.cairo │ │ │ │ ├── spying_for_events.cairo │ │ │ │ ├── using_interact_with_state.cairo │ │ │ │ └── using_library_calls.cairo │ │ │ └── tests/ │ │ │ ├── interact_with_state.cairo │ │ │ ├── lib.cairo │ │ │ ├── mocking_the_context_info.cairo │ │ │ ├── spying_for_events/ │ │ │ │ ├── syscall_tests.cairo │ │ │ │ └── tests.cairo │ │ │ ├── spying_for_events.cairo │ │ │ └── using_library_calls.cairo │ │ ├── testing_events/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ ├── contract.cairo │ │ │ │ ├── lib.cairo │ │ │ │ ├── syscall.cairo │ │ │ │ └── syscall_dummy.cairo │ │ │ └── tests/ │ │ │ ├── assert_base.cairo │ │ │ ├── assert_emitted.cairo │ │ │ ├── assert_manually.cairo │ │ │ ├── filter.cairo │ │ │ └── syscall.cairo │ │ ├── testing_messages_to_l1/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ ├── detailed.cairo │ │ │ ├── lib.cairo │ │ │ └── simple.cairo │ │ ├── testing_reference/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ └── tests.cairo │ │ ├── testing_smart_contracts_handling_errors/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ ├── handle_panic.cairo │ │ │ └── panic.cairo │ │ ├── testing_smart_contracts_safe_dispatcher/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ └── safe_dispatcher.cairo │ │ ├── testing_smart_contracts_writing_tests/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ └── simple_contract.cairo │ │ ├── tests_partitioning/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ └── example.cairo │ │ ├── tx_status/ │ │ │ ├── Scarb.toml │ │ │ └── src/ │ │ │ └── lib.cairo │ │ ├── using_cheatcodes/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ └── lib.cairo │ │ ├── using_cheatcodes_cancelling_cheat/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ └── lib.cairo │ │ ├── using_cheatcodes_cheat_address/ │ │ │ ├── Scarb.toml │ │ │ ├── src/ │ │ │ │ └── lib.cairo │ │ │ └── tests/ │ │ │ └── lib.cairo │ │ └── using_cheatcodes_others/ │ │ ├── Scarb.toml │ │ ├── src/ │ │ │ └── lib.cairo │ │ └── tests/ │ │ ├── caller_address/ │ │ │ ├── proper_use_global.cairo │ │ │ └── span.cairo │ │ ├── caller_address.cairo │ │ ├── cheat_constructor.cairo │ │ ├── lib.cairo │ │ ├── set_balance_custom_token.cairo │ │ └── set_balance_strk.cairo │ ├── src/ │ │ ├── README.md │ │ ├── SUMMARY.md │ │ ├── appendix/ │ │ │ ├── 0-56-0-migration-guide.md │ │ │ ├── cheatcodes/ │ │ │ │ ├── account_contract_address.md │ │ │ │ ├── account_deployment_data.md │ │ │ │ ├── block_hash.md │ │ │ │ ├── block_number.md │ │ │ │ ├── block_timestamp.md │ │ │ │ ├── caller_address.md │ │ │ │ ├── chain_id.md │ │ │ │ ├── cheat_span.md │ │ │ │ ├── fee_data_availability_mode.md │ │ │ │ ├── generate_arg.md │ │ │ │ ├── generate_random_felt.md │ │ │ │ ├── get_class_hash.md │ │ │ │ ├── global.md │ │ │ │ ├── interact_with_state.md │ │ │ │ ├── l1_handler.md │ │ │ │ ├── load.md │ │ │ │ ├── max_fee.md │ │ │ │ ├── mock_call.md │ │ │ │ ├── nonce.md │ │ │ │ ├── nonce_data_availability_mode.md │ │ │ │ ├── paymaster_data.md │ │ │ │ ├── proof_facts.md │ │ │ │ ├── replace_bytecode.md │ │ │ │ ├── resource_bounds.md │ │ │ │ ├── sequencer_address.md │ │ │ │ ├── set_balance.md │ │ │ │ ├── signature.md │ │ │ │ ├── spy_events.md │ │ │ │ ├── spy_messages_to_l1.md │ │ │ │ ├── store.md │ │ │ │ ├── tip.md │ │ │ │ ├── token.md │ │ │ │ ├── transaction_hash.md │ │ │ │ └── transaction_version.md │ │ │ ├── cheatcodes.md │ │ │ ├── inlining-optimizer.md │ │ │ ├── scarb-toml.md │ │ │ ├── sncast/ │ │ │ │ ├── account/ │ │ │ │ │ ├── account.md │ │ │ │ │ ├── create.md │ │ │ │ │ ├── delete.md │ │ │ │ │ ├── deploy.md │ │ │ │ │ ├── import.md │ │ │ │ │ └── list.md │ │ │ │ ├── balance.md │ │ │ │ ├── call.md │ │ │ │ ├── common.md │ │ │ │ ├── completions.md │ │ │ │ ├── declare.md │ │ │ │ ├── declare_from.md │ │ │ │ ├── deploy.md │ │ │ │ ├── get/ │ │ │ │ │ ├── balance.md │ │ │ │ │ ├── class_hash_at.md │ │ │ │ │ ├── get.md │ │ │ │ │ ├── nonce.md │ │ │ │ │ ├── tx-status.md │ │ │ │ │ └── tx.md │ │ │ │ ├── invoke.md │ │ │ │ ├── ledger/ │ │ │ │ │ ├── app-version.md │ │ │ │ │ ├── get-public-key.md │ │ │ │ │ ├── ledger.md │ │ │ │ │ └── sign-hash.md │ │ │ │ ├── multicall/ │ │ │ │ │ ├── execute/ │ │ │ │ │ │ ├── deploy.md │ │ │ │ │ │ └── invoke.md │ │ │ │ │ ├── execute.md │ │ │ │ │ ├── multicall.md │ │ │ │ │ ├── new.md │ │ │ │ │ └── run.md │ │ │ │ ├── script/ │ │ │ │ │ ├── init.md │ │ │ │ │ ├── run.md │ │ │ │ │ └── script.md │ │ │ │ ├── show_config.md │ │ │ │ ├── tx-status.md │ │ │ │ ├── utils/ │ │ │ │ │ ├── class_hash.md │ │ │ │ │ ├── selector.md │ │ │ │ │ ├── serialize.md │ │ │ │ │ └── utils.md │ │ │ │ └── verify.md │ │ │ ├── sncast-library/ │ │ │ │ ├── call.md │ │ │ │ ├── declare.md │ │ │ │ ├── deploy.md │ │ │ │ ├── errors.md │ │ │ │ ├── fee_settings_trait.md │ │ │ │ ├── get_nonce.md │ │ │ │ ├── invoke.md │ │ │ │ └── tx_status.md │ │ │ ├── sncast-library.md │ │ │ ├── sncast.md │ │ │ ├── snforge/ │ │ │ │ ├── check-requirements.md │ │ │ │ ├── clean-cache.md │ │ │ │ ├── clean.md │ │ │ │ ├── completions.md │ │ │ │ ├── new.md │ │ │ │ ├── optimize-inlining.md │ │ │ │ └── test.md │ │ │ ├── snforge-library/ │ │ │ │ ├── byte_array.md │ │ │ │ ├── contract_class.md │ │ │ │ ├── declare.md │ │ │ │ ├── env.md │ │ │ │ ├── fs/ │ │ │ │ │ ├── file.md │ │ │ │ │ ├── file_format_rules.md │ │ │ │ │ ├── file_parser.md │ │ │ │ │ ├── read_json.md │ │ │ │ │ └── read_txt.md │ │ │ │ ├── fs.md │ │ │ │ ├── fuzzable.md │ │ │ │ ├── get_call_trace.md │ │ │ │ ├── signature.md │ │ │ │ ├── testing/ │ │ │ │ │ └── get_current_vm_step.md │ │ │ │ └── testing.md │ │ │ ├── snforge-library.md │ │ │ ├── snforge.md │ │ │ ├── snfoundry-toml.md │ │ │ └── starknet-foundry-github-action.md │ │ ├── development/ │ │ │ ├── environment-setup.md │ │ │ ├── shell-snippets.md │ │ │ └── snapshot-tests.md │ │ ├── getting-started/ │ │ │ ├── blake-hash-support.md │ │ │ ├── first-steps.md │ │ │ ├── installation.md │ │ │ └── scarb.md │ │ ├── projects/ │ │ │ └── configuration.md │ │ ├── snforge-advanced-features/ │ │ │ ├── debugging.md │ │ │ ├── fork-testing.md │ │ │ ├── fuzz-testing.md │ │ │ ├── oracles.md │ │ │ ├── parametrized-testing.md │ │ │ ├── profiling.md │ │ │ ├── storage-cheatcodes.md │ │ │ └── tests-partitioning.md │ │ ├── starknet/ │ │ │ ├── 101.md │ │ │ ├── account-balance.md │ │ │ ├── account-import.md │ │ │ ├── account.md │ │ │ ├── block_explorer.md │ │ │ ├── call.md │ │ │ ├── calldata-transformation.md │ │ │ ├── declare.md │ │ │ ├── deploy.md │ │ │ ├── developer.md │ │ │ ├── eip-2645-hd-paths.md │ │ │ ├── integration_with_devnet.md │ │ │ ├── invoke.md │ │ │ ├── ledger.md │ │ │ ├── multicall.md │ │ │ ├── script.md │ │ │ ├── show_config.md │ │ │ ├── sncast-overview.md │ │ │ ├── tx-status.md │ │ │ └── verify.md │ │ └── testing/ │ │ ├── contract-collection/ │ │ │ ├── new-mechanism.md │ │ │ └── old-mechanism.md │ │ ├── contracts-collection.md │ │ ├── contracts.md │ │ ├── coverage.md │ │ ├── gas-and-resource-estimation.md │ │ ├── running-tests.md │ │ ├── test-attributes.md │ │ ├── test-collection.md │ │ ├── testing-contract-internals.md │ │ ├── testing-events.md │ │ ├── testing-messages-to-l1.md │ │ ├── testing-workspaces.md │ │ ├── testing.md │ │ └── using-cheatcodes.md │ └── theme/ │ ├── head.hbs │ ├── header.hbs │ └── index.hbs ├── scripts/ │ ├── build_docs.sh │ ├── check_snapshots.sh │ ├── compareVersions.js │ ├── get_scarb_versions.sh │ ├── handle_version.sh │ ├── install.sh │ ├── package.json │ ├── package.sh │ ├── release.sh │ ├── scarbfmt.sh │ ├── smoke_test.sh │ ├── snfoundryup │ └── verify_cairo_listings.sh ├── sncast_std/ │ ├── README.md │ ├── Scarb.lock │ ├── Scarb.toml │ └── src/ │ └── lib.cairo ├── snforge_std/ │ ├── README.md │ ├── Scarb.lock │ ├── Scarb.toml │ └── src/ │ ├── byte_array.cairo │ ├── cheatcode.cairo │ ├── cheatcodes/ │ │ ├── block_hash.cairo │ │ ├── contract_class.cairo │ │ ├── erc20.cairo │ │ ├── events.cairo │ │ ├── execution_info/ │ │ │ ├── account_contract_address.cairo │ │ │ ├── account_deployment_data.cairo │ │ │ ├── block_number.cairo │ │ │ ├── block_timestamp.cairo │ │ │ ├── caller_address.cairo │ │ │ ├── chain_id.cairo │ │ │ ├── contract_address.cairo │ │ │ ├── fee_data_availability_mode.cairo │ │ │ ├── max_fee.cairo │ │ │ ├── nonce.cairo │ │ │ ├── nonce_data_availability_mode.cairo │ │ │ ├── paymaster_data.cairo │ │ │ ├── proof_facts.cairo │ │ │ ├── resource_bounds.cairo │ │ │ ├── sequencer_address.cairo │ │ │ ├── signature.cairo │ │ │ ├── tip.cairo │ │ │ ├── transaction_hash.cairo │ │ │ └── version.cairo │ │ ├── execution_info.cairo │ │ ├── generate_arg.cairo │ │ ├── generate_random_felt.cairo │ │ ├── l1_handler.cairo │ │ ├── message_to_l1.cairo │ │ └── storage.cairo │ ├── cheatcodes.cairo │ ├── config_types.cairo │ ├── env/ │ │ └── env_vars.cairo │ ├── env.cairo │ ├── fs/ │ │ └── file_operations.cairo │ ├── fs.cairo │ ├── fuzzable.cairo │ ├── lib.cairo │ ├── signature/ │ │ ├── secp256k1_curve.cairo │ │ ├── secp256r1_curve.cairo │ │ └── stark_curve.cairo │ ├── signature.cairo │ ├── testing.cairo │ └── trace.cairo └── snforge_templates/ ├── balance_contract/ │ ├── src/ │ │ └── lib.cairo │ └── tests/ │ └── test_contract.cairo ├── cairo_program/ │ └── src/ │ └── lib.cairo └── erc20_contract/ ├── src/ │ ├── lib.cairo │ ├── mock_erc20.cairo │ └── token_sender.cairo └── tests/ ├── test_erc20.cairo └── test_token_sender.cairo ================================================ FILE CONTENTS ================================================ ================================================ FILE: .cargo/config.toml ================================================ [alias] lint = "clippy --all-targets --all-features -- --no-deps -W clippy::pedantic -W clippy::undocumented_unsafe_blocks -A clippy::module_name_repetitions -A clippy::missing_errors_doc -A clippy::missing_panics_doc" ================================================ FILE: .gitattributes ================================================ *.* text eol=lf *.png -text *.jpg -text *.gif -text ================================================ FILE: .github/CODEOWNERS ================================================ * @foundry-rs/starknet-foundry ================================================ FILE: .github/ISSUE_TEMPLATE/1-bug_report.yml ================================================ name: Problem Report description: Report a problem with a tool type: "Bug" body: - type: dropdown id: tool-name attributes: label: Select Tool description: Select for which tool you would like to report a bug. multiple: false options: - snforge - sncast validations: required: true - type: input id: foundry-version attributes: label: Foundry Version description: What tool version are you using? This can be checked by running `snforge --version` and `sncast --version` accordingly. validations: required: true - type: dropdown id: operating-system attributes: label: What operating system are you using? multiple: false options: - Linux - MacOS validations: required: true - type: dropdown id: system-architecture attributes: label: What system architecture are you using? multiple: false options: - x86 - arm validations: required: true - type: textarea id: what-happened attributes: label: Issue Description description: Describe the problem that happened to you. The more details you provide, the better. placeholder: Running `snforge` doesn't produce tests validations: required: true - type: textarea id: logs attributes: label: Command Line Output description: If relevant, please provide the command output related to the problem described above. validations: required: false ================================================ FILE: .github/ISSUE_TEMPLATE/2-feature_request.yml ================================================ name: Feature Request description: Request a feature to be added to a tool type: "Feature" body: - type: dropdown id: tool-name attributes: label: Select Tool description: Select for which tool you would like to request a feature. multiple: false options: - snforge - sncast validations: required: true - type: textarea id: Suggestion attributes: label: Requested Feature description: Describe the feature you would like to be added to the tool selected above. validations: required: true ================================================ FILE: .github/ISSUE_TEMPLATE/3-work_item.yml ================================================ name: Work Item description: Submit an actionable task body: - type: textarea id: current-state attributes: label: Current State description: Describe the current state and outline the problem placeholder: Currently, the `print_a` cheatcode prints `"a"` to stdout. This is problematic because user might want it to be printed on their actual printer. validations: required: true - type: input id: objective attributes: label: Objective description: Briefly describe the correct state placeholder: The `print_a` cheatcode should magically detect if user wants to print to stdout or a printer. validations: required: true - type: textarea id: additional-context attributes: label: Additional Context description: Provide additional context on the desired state. placeholder: If we can detect any printers in local network it might indicate that user wants to print to it. validations: required: false ================================================ FILE: .github/ISSUE_TEMPLATE/4-child_work_item.yml ================================================ name: Child Item description: Child work item for tracking issues type: "Task" body: - type: input id: objective attributes: label: Objective description: Briefly describe the task placeholder: Add unit tests for `print_a` cheatcode. validations: required: true - type: textarea id: additional-context attributes: label: Additional Context description: Provide additional context on the task placeholder: Make sure to include case where `print_a` cheatcode is called with an empty string. validations: required: false ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: Telegram Support Channel url: https://t.me/starknet_foundry_support about: This issue tracker is only for bugs and feature requests. Support is available through Telegram channel. ================================================ FILE: .github/actions/setup-tools/action.yml ================================================ name: Setup Tools description: Installs Rust and optionally Scarb, Universal Sierra Compiler and LLVM 19 toolchain inputs: install-llvm: description: 'Whether to install the LLVM 19 toolchain' required: false default: 'false' setup-scarb: description: 'Whether to setup scarb' required: false default: 'true' scarb-version: description: 'Optional: Scarb version to install. If not set, uses version from .tool-versions' required: false default: '' setup-usc: description: 'Whether to setup universal-sierra-compiler' required: false default: 'true' runs: using: "composite" steps: - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 - uses: software-mansion/setup-scarb@v1 if: ${{ inputs.setup-scarb == 'true' }} with: scarb-version: ${{ inputs.scarb-version }} - uses: software-mansion/setup-universal-sierra-compiler@v1 if: ${{ inputs.setup-usc == 'true' }} - name: Add LLVM APT repository uses: myci-actions/add-deb-repo@11 if: ${{ inputs.install-llvm == 'true' }} with: repo: deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main repo-name: llvm-repo keys-asc: https://apt.llvm.org/llvm-snapshot.gpg.key - name: Install LLVM shell: bash if: ${{ inputs.install-llvm == 'true' }} run: | sudo apt-get update sudo apt-get install -y \ llvm-19 llvm-19-dev llvm-19-runtime \ clang-19 clang-tools-19 \ lld-19 libpolly-19-dev libmlir-19-dev mlir-19-tools - name: Set environment variables if: ${{ inputs.install-llvm == 'true' }} shell: bash run: | echo "MLIR_SYS_190_PREFIX=/usr/lib/llvm-19/" >> $GITHUB_ENV echo "LLVM_SYS_191_PREFIX=/usr/lib/llvm-19/" >> $GITHUB_ENV echo "TABLEGEN_190_PREFIX=/usr/lib/llvm-19/" >> $GITHUB_ENV ================================================ FILE: .github/dependabot.yml ================================================ # To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "cargo" directory: "/" schedule: interval: "monthly" ignore: - dependency-name: "cairo-*" groups: all-dependencies: patterns: - "*" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" groups: actions: patterns: - "*" ================================================ FILE: .github/pull_request_template.md ================================================ Closes # ## Introduced changes - ## Checklist - [ ] Linked relevant issue - [ ] Updated relevant documentation - [ ] Added relevant tests - [ ] Performed self-review of the code - [ ] Added changes to `CHANGELOG.md` ================================================ FILE: .github/workflows/_build-binaries-native.yml ================================================ # NOTE: This is a draft created for the future if we continue integration with the native. # TODO(#3790) this is currently unused, integrate into nightly release flow name: Build Native binaries on: workflow_call: inputs: # Specify the version in MAJOR.MINOR.PATCH format, without a leading 'v' version: required: true type: string ref: required: false type: string jobs: build-binaries: name: Build ${{ matrix.target }} runs-on: ${{ matrix.os }} env: # Cross-compiled targets will override this to `cross`. CARGO: cargo strategy: matrix: # Currently we have only managed to build successfuly on x86 gnu Linux # - On MacOS, LLVM is installed with brew and the built binary requires having LLVM installed through it, # even though it is supossed to be bundled. # - MUSL Linux was not investigated # - Aarch64 Linux requires LLVM to be build for Aarch64, but proc macros run against host OS, which is x86_64 # when building through cross. With current setup, we install LLVM for only one of these arches. # We need to investigate cross compiling LLVM for multiple architectures. include: - target: x86_64-unknown-linux-gnu os: ubuntu-latest system: linux # - target: x86_64-unknown-linux-musl # os: ubuntu-latest # system: linux # # - target: aarch64-unknown-linux-gnu # os: ubuntu-latest # system: linux # # - target: aarch64-unknown-linux-musl # os: ubuntu-latest # system: linux # # - target: x86_64-apple-darwin # os: macos-14-large # system: macos # # - target: aarch64-apple-darwin # os: macos-latest # system: macos steps: - name: Checkout with ref if: inputs.ref != '' uses: actions/checkout@v6 with: ref: ${{ inputs.ref }} - name: Checkout default if: inputs.ref == '' uses: actions/checkout@v6 - name: Setup rust run: | rustup target add ${{ matrix.target }} - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 - name: Setup LLVM on MacOS if: matrix.system == 'macos' run: | brew install llvm@19 brew reinstall zstd echo "MLIR_SYS_190_PREFIX=$(brew --prefix llvm@19)" >> $GITHUB_ENV echo "LLVM_SYS_191_PREFIX=$(brew --prefix llvm@19)" >> $GITHUB_ENV echo "TABLEGEN_190_PREFIX=$(brew --prefix llvm@19)" >> $GITHUB_ENV echo "LIBRARY_PATH=/opt/homebrew/lib" >> $GITHUB_ENV - name: Install cross if: matrix.system == 'linux' run: | cargo install cross --git https://github.com/cross-rs/cross - name: Build MacOS if: matrix.system == 'macos' run: | export MLIR_SYS_190_PREFIX=${{ env.MLIR_SYS_190_PREFIX }} export LLVM_SYS_191_PREFIX=${{ env.LLVM_SYS_191_PREFIX }} export TABLEGEN_190_PREFIX=${{ env.TABLEGEN_190_PREFIX }} export LIBRARY_PATH=${{ env.LIBRARY_PATH }} cargo build --release --locked --target ${{ matrix.target }} - name: Build Linux if: matrix.system == 'linux' run: | # Use cross to link oldest GLIBC possible. cross build --release --locked --target ${{ matrix.target }} - name: Package shell: bash run: | set -euxo pipefail PKG_FULL_NAME="starknet-foundry-v${{ inputs.version }}-${{ matrix.target }}" echo "PKG_FULL_NAME=$PKG_FULL_NAME" >> $GITHUB_ENV ./scripts/package.sh "${{ matrix.target }}" "$PKG_FULL_NAME" - name: Upload artifact uses: actions/upload-artifact@v5 with: # TODO(#3790): Perhaps this name needs to be changed to avoid conflicts with `_build-binaries.yml` workflow name: build-${{ matrix.target }} path: ${{ env.PKG_FULL_NAME }}.* ================================================ FILE: .github/workflows/_build-binaries.yml ================================================ name: Build binaries on: workflow_call: inputs: # Specify the version in MAJOR.MINOR.PATCH format, without a leading 'v' version: required: true type: string ref: required: false type: string jobs: build-binaries: name: Build ${{ matrix.target }} runs-on: ${{ matrix.os }} env: # Cross-compiled targets will override this to `cross`. CARGO: cargo strategy: matrix: include: - target: x86_64-unknown-linux-gnu os: ubuntu-latest # Use cross to link oldest GLIBC possible. cross: true - target: x86_64-unknown-linux-musl os: ubuntu-latest cross: true - target: aarch64-unknown-linux-gnu os: ubuntu-latest cross: true - target: aarch64-unknown-linux-musl os: ubuntu-latest cross: true - target: x86_64-apple-darwin os: macos-latest - target: aarch64-apple-darwin os: macos-latest steps: - name: Checkout with ref if: inputs.ref != '' uses: actions/checkout@v6 with: ref: ${{ inputs.ref }} - name: Checkout default if: inputs.ref == '' uses: actions/checkout@v6 - name: Setup rust run: | rustup target add ${{ matrix.target }} - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 with: workspaces: starknet-foundry - name: Install cross if: matrix.cross uses: taiki-e/install-action@cross - name: Enable cross-compilation if: matrix.cross shell: bash run: | echo "CARGO=cross" >> $GITHUB_ENV - name: Build run: ${{ env.CARGO }} build --release --locked --bins --target ${{ matrix.target }} - name: Package shell: bash run: | set -euxo pipefail PKG_FULL_NAME="starknet-foundry-v${{ inputs.version }}-${{ matrix.target }}" echo "PKG_FULL_NAME=$PKG_FULL_NAME" >> $GITHUB_ENV ./scripts/package.sh "${{ matrix.target }}" "$PKG_FULL_NAME" - name: Upload artifact uses: actions/upload-artifact@v5 with: name: build-${{ matrix.target }} path: ${{ env.PKG_FULL_NAME }}.* overwrite: true ================================================ FILE: .github/workflows/_build-plugin-binaries.yml ================================================ name: Build snforge_scarb_plugin on: workflow_call: inputs: # Specify the version in MAJOR.MINOR.PATCH format, without a leading 'v' overridden_plugin_version: required: false type: string ref: required: false type: string plugin_name: required: true type: string workflow_dispatch: inputs: # Specify the version in MAJOR.MINOR.PATCH format, without a leading 'v' overridden_plugin_version: required: false type: string plugin_name: required: true type: string jobs: build-binaries: name: Build ${{ matrix.target }} runs-on: ${{ matrix.os }} env: # Cross-compiled targets will override this to `cross`. CARGO: cargo strategy: matrix: include: - target: x86_64-unknown-linux-gnu os: ubuntu-latest # Use cross to link oldest GLIBC possible. cross: true lib-name-prefix: "lib" ext: "so" - target: aarch64-unknown-linux-gnu os: ubuntu-latest cross: true lib-name-prefix: "lib" ext: "so" - target: x86_64-apple-darwin os: macos-latest lib-name-prefix: "lib" ext: "dylib" - target: aarch64-apple-darwin os: macos-latest lib-name-prefix: "lib" ext: "dylib" # The scarb builds for following platforms are experimental and not officially supported by starknet-foundry. # https://docs.swmansion.com/scarb/download.html#platform-support # Reference issue: TODO(#2886) # - target: aarch64-unknown-linux-musl # os: ubuntu-latest # cross: true # ext: "so" # - target: x86_64-unknown-linux-musl # os: ubuntu-latest # cross: true # ext: "so" steps: - name: Checkout with ref if: inputs.ref != '' uses: actions/checkout@v6 with: ref: ${{ inputs.ref }} - name: Checkout default if: inputs.ref == '' uses: actions/checkout@v6 - name: Setup rust run: | rustup target add ${{ matrix.target }} - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 - name: Install cross if: matrix.cross uses: taiki-e/install-action@cross - name: Enable cross-compilation if: matrix.cross shell: bash run: | echo "CARGO=cross" >> $GITHUB_ENV - name: Build working-directory: crates/${{ inputs.plugin_name }} run: ${{ env.CARGO }} build --release --locked --target ${{ matrix.target }} - name: Rename Binary shell: bash run: | set -euxo pipefail source scripts/handle_version.sh PACKAGE_NAME=${{ inputs.plugin_name }} PACKAGE_NAME=${PACKAGE_NAME//-/_} # Replace `-` with `_` in name PACKAGE_VERSION=$(get_version "${{ inputs.overridden_plugin_version }}" "crates/${{ inputs.plugin_name }}/Scarb.toml") TARGET="${{ matrix.target }}" EXT="${{ matrix.ext }}" LIB_NAME="${{ matrix.lib-name-prefix }}${PACKAGE_NAME}" OUTPUT_BINARY="${PACKAGE_NAME}_v${PACKAGE_VERSION}_${TARGET}.${EXT}" mv ./crates/${{ inputs.plugin_name }}/target/${TARGET}/release/${LIB_NAME}.${EXT} ./crates/${{ inputs.plugin_name }}/target/${TARGET}/release/${OUTPUT_BINARY} echo "OUTPUT_BINARY_PATH=./crates/${{ inputs.plugin_name }}/target/${TARGET}/release/${OUTPUT_BINARY}" >> $GITHUB_ENV - name: Upload Artifact uses: actions/upload-artifact@v5 with: name: build-${{ inputs.plugin_name }}-${{ matrix.target }} path: ${{ env.OUTPUT_BINARY_PATH }} compression-level: 0 overwrite: true ================================================ FILE: .github/workflows/_publish-plugin.yml ================================================ name: Upload plugin to registry on: workflow_call: inputs: prod_registry: required: false type: boolean # Specify the version in MAJOR.MINOR.PATCH format, without a leading 'v' overridden_plugin_version: required: false type: string plugin_name: required: true type: string jobs: check-uploaded: name: Check ${{ inputs.plugin_name }} Version runs-on: ubuntu-latest outputs: plugin_uploaded: ${{ steps.check-uploaded.outputs.plugin_uploaded }} steps: - uses: actions/checkout@v6 - name: Check version id: check-uploaded run: | set -exo pipefail source scripts/handle_version.sh plugin_name_underscores=${{ inputs.plugin_name }} plugin_name_underscores=${plugin_name_underscores//-/_} plugin_version=$(get_version "${{ inputs.overridden_plugin_version }}" "crates/${{ inputs.plugin_name }}/Scarb.toml") registry_url=${{ inputs.prod_registry == true && 'https://scarbs.xyz' || 'https://scarbs.dev' }} plugin_uploaded=$(curl -s ${registry_url}/api/v1/index/sn/fo/${plugin_name_underscores}.json | jq --arg version "$plugin_version" '[.[] | select(.v == $version)] | length > 0') echo "plugin_uploaded=$plugin_uploaded" >> $GITHUB_OUTPUT upload-to-registry: name: Upload ${{ inputs.plugin_name }} to the registry runs-on: ubuntu-latest needs: [check-uploaded] env: SCARB_REGISTRY_AUTH_TOKEN: ${{ inputs.prod_registry == true && secrets.SCARB_REGISTRY_AUTH_TOKEN || secrets.DEV_SCARB_REGISTRY_AUTH_TOKEN }} steps: - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - uses: software-mansion/setup-scarb@v1 - name: Download artifacts uses: actions/download-artifact@v6 with: path: artifacts-dl - name: Unpack artifacts to target directory run: | set -euxo pipefail plugin_name_underscores=${{ inputs.plugin_name }} plugin_name_underscores=${plugin_name_underscores//-/_} mkdir -p crates/${{ inputs.plugin_name }}/target/scarb/cairo-plugin mv artifacts-dl/build-*/${plugin_name_underscores}_v* crates/${{ inputs.plugin_name }}/target/scarb/cairo-plugin/ # Required for testing prebuild plugin while creating release. if [[ -n "${{ inputs.overridden_plugin_version }}" ]]; then cd crates/${{ inputs.plugin_name }}/target/scarb/cairo-plugin/ overridden_version="${{ inputs.overridden_plugin_version }}" for file in ${plugin_name_underscores}_v*; do if [[ -f "$file" && ! "$file" =~ "${plugin_name_underscores}_v${overridden_version}" ]]; then platform=$(echo "$file" | sed -E "s/${plugin_name_underscores}_v[0-9]+\.[0-9]+\.[0-9]+(-rc.[0-9]+)?_(.+)/\2/") new_file="${plugin_name_underscores}_v${overridden_version}_${platform}" mv "$file" "$new_file" fi done fi - name: Publish ${{ inputs.plugin_name }} if: needs.check-uploaded.outputs.plugin_uploaded == 'false' working-directory: crates/${{ inputs.plugin_name }} run: | set -exo pipefail source ../../scripts/handle_version.sh update_version_in_file "Scarb.toml" "${{ inputs.overridden_plugin_version }}" update_version_in_file "Cargo.toml" "${{ inputs.overridden_plugin_version }}" scarb publish --allow-dirty ${{ inputs.prod_registry == true && ' ' || '--index https://scarbs.dev/' }} ================================================ FILE: .github/workflows/_test-binaries.yml ================================================ name: Test binaries on: workflow_call: inputs: # Specify the version in MAJOR.MINOR.PATCH format, without a leading 'v' bin_version: required: true type: string # Specify the version in MAJOR.MINOR.PATCH format, without a leading 'v' std_version: required: true type: string jobs: test-binary: name: Test binary runs-on: ${{ matrix.os }} strategy: fail-fast: true matrix: include: - target: x86_64-unknown-linux-gnu os: ubuntu-latest - target: aarch64-apple-darwin os: macos-latest - target: x86_64-apple-darwin # Target macos-latest uses Mac with ARM, macos-14-large is still on Intel os: macos-14-large steps: - uses: actions/checkout@v6 - uses: software-mansion/setup-scarb@v1 - name: Setup rust run: | rustup target add ${{ matrix.target }} - name: Download artifacts uses: actions/download-artifact@v6 with: path: artifacts-dl - name: Move artifacts to staging directory shell: bash run: | mkdir -p artifacts mv artifacts-dl/build-*/starknet-foundry-v* artifacts/ - name: Get artifacts path shell: bash run: | ARTIFACTS_PATH="artifacts/starknet-foundry-v${{ inputs.bin_version }}-${{ matrix.target }}.tar.gz" echo "ARTIFACTS_PATH=$ARTIFACTS_PATH" >> $GITHUB_ENV - name: Unpack artifact shell: bash run: | tar xzvf ${{ env.ARTIFACTS_PATH }} - name: Install universal-sierra-compiler uses: software-mansion/setup-universal-sierra-compiler@v1 - name: Smoke test shell: bash env: RPC_URL: ${{ secrets.NODE_URL }} run: | ARTIFACTS_PATH="${{ env.ARTIFACTS_PATH }}" ARTIFACTS_PATH="${ARTIFACTS_PATH%.tar.gz}" ARTIFACTS_PATH="${ARTIFACTS_PATH%.zip}" ARTIFACTS_PATH="${ARTIFACTS_PATH#artifacts/}" SNFORGE_PATH=$(readlink -f $ARTIFACTS_PATH/bin/snforge) SNCAST_PATH=$(readlink -f $ARTIFACTS_PATH/bin/sncast) REPO_URL=${{ github.repositoryUrl }} REVISION=${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} VERSION=${{ inputs.std_version }} ./scripts/smoke_test.sh "$RPC_URL" "$SNFORGE_PATH" "$SNCAST_PATH" "$REPO_URL" "$REVISION" "$VERSION" ================================================ FILE: .github/workflows/automate-stale.yml ================================================ name: 'Automation - Stale issues and PRs' on: schedule: - cron: '0 7 * * 1-5' env: DAYS_BEFORE_STALE: 30 DAYS_BEFORE_CLOSE: 14 jobs: stale: runs-on: ubuntu-latest permissions: pull-requests: write steps: - name: Run Stale Bot id: stale uses: actions/stale@v10 with: # General settings days-before-stale: ${{ env.DAYS_BEFORE_STALE }} days-before-close: ${{ env.DAYS_BEFORE_CLOSE }} operations-per-run: 3000 enable-statistics: true # This is only useful if secret ACTIONS_STEP_DEBUG=true remove-stale-when-updated: true # PR settings stale-pr-label: stale stale-pr-message: | Hi! This pull request hasn't had any activity for a while, so I am marking it as stale. It will close in ${{ env.DAYS_BEFORE_CLOSE }} days if it is not updated. Thanks for contributing! close-pr-message: | This pull request has been automatically closed due to inactivity. Feel free to reopen it or create a new one if needed. Thanks for contributing! ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI on: pull_request: merge_group: push: branches: - master workflow_dispatch: concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: get-scarb-versions: if: github.event_name == 'merge_group' name: Get Scarb versions runs-on: ubuntu-latest outputs: versions: ${{ steps.get_versions.outputs.versions }} steps: - uses: actions/checkout@v6 - uses: asdf-vm/actions/setup@b7bcd026f18772e44fe1026d729e1611cc435d47 - run: | asdf plugin add scarb asdf install scarb latest asdf set --home scarb latest - name: Get versions id: get_versions run: | scarb_versions=$(./scripts/get_scarb_versions.sh --previous) echo ${scarb_versions[@]} echo "versions=[${scarb_versions[@]}]" >> "$GITHUB_OUTPUT" test-workspace: # Runs tests from all crates across the workspace, except: # - `sncast` tests # - `forge` integration and e2e tests # `native-api` is also excluded as there are no tests and it requires extra setup to compile. name: Workspace Tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: ./.github/actions/setup-tools - run: cargo test --profile ci --workspace --exclude sncast --exclude forge --exclude native-api - run: cargo test --profile ci --lib -p forge build-test-forge-nextest-archive: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: ./.github/actions/setup-tools with: setup-scarb: 'false' setup-usc: 'false' - name: Install nextest uses: taiki-e/install-action@v2 with: tool: nextest@0.9.98 - name: Determine test features id: test_features run: | FEATURES="run_test_for_scarb_since_2_15_1" echo "features=$FEATURES" >> "$GITHUB_OUTPUT" - name: Build and archive tests run: | if [ -n "${{ steps.test_features.outputs.features }}" ]; then cargo nextest archive --cargo-profile ci -p forge --features "${{ steps.test_features.outputs.features }}" --archive-file 'nextest-archive-${{ runner.os }}.tar.zst' else cargo nextest archive --cargo-profile ci -p forge --archive-file 'nextest-archive-${{ runner.os }}.tar.zst' fi - name: Upload archive to workflow uses: actions/upload-artifact@v5 with: name: nextest-archive-${{ runner.os }} path: nextest-archive-${{ runner.os }}.tar.zst build-test-forge-nextest-archive-native: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: ./.github/actions/setup-tools with: install-llvm: 'true' setup-scarb: 'false' setup-usc: 'false' - name: Install nextest uses: taiki-e/install-action@v2 with: tool: nextest@0.9.98 - name: Determine test features id: test_features run: | FEATURES="cairo-native,run_test_for_scarb_since_2_15_1" echo "features=$FEATURES" >> "$GITHUB_OUTPUT" - name: Build and archive tests run: cargo nextest archive --cargo-profile ci -p forge --features "${{ steps.test_features.outputs.features }}" --archive-file 'nextest-archive-${{ runner.os }}-native.tar.zst' - name: Upload archive to workflow uses: actions/upload-artifact@v5 with: name: nextest-archive-${{ runner.os }}-native path: nextest-archive-${{ runner.os }}-native.tar.zst test-forge-integration: name: Test Forge / Integration Tests runs-on: [ ubuntu-latest ] needs: [ build-test-forge-nextest-archive ] strategy: fail-fast: false matrix: partition: [ 1, 2, 3 ] steps: - uses: actions/checkout@v6 - uses: ./.github/actions/setup-tools - uses: taiki-e/install-action@v2 with: tool: nextest@0.9.98 - uses: actions/download-artifact@v6 with: name: nextest-archive-${{ runner.os }} - name: nextest partition ${{ matrix.partition }}/3 run: cargo nextest run --no-fail-fast --partition 'count:${{ matrix.partition }}/3' --archive-file 'nextest-archive-${{ runner.os }}.tar.zst' integration test-forge-integration-native: name: Test Forge / Integration Tests (native) runs-on: [ ubuntu-latest ] needs: [ build-test-forge-nextest-archive-native ] strategy: fail-fast: false matrix: partition: [ 1, 2, 3 ] steps: - uses: actions/checkout@v6 - uses: ./.github/actions/setup-tools - uses: taiki-e/install-action@v2 with: tool: nextest@0.9.98 - uses: actions/download-artifact@v6 with: name: nextest-archive-${{ runner.os }}-native - name: nextest partition ${{ matrix.partition }}/3 run: cargo nextest run --no-fail-fast --partition 'count:${{ matrix.partition }}/3' --archive-file 'nextest-archive-${{ runner.os }}-native.tar.zst' integration test-forge-e2e: name: Test Forge / E2E Tests runs-on: ubuntu-latest needs: [ build-test-forge-nextest-archive ] strategy: fail-fast: false matrix: partition: [ 1, 2, 3 ] steps: - name: Extract branch name if: github.event_name != 'pull_request' run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV shell: bash - name: Extract branch name on pull request if: github.event_name == 'pull_request' run: echo "BRANCH_NAME=$(echo $GITHUB_HEAD_REF)" >> $GITHUB_ENV shell: bash - name: Extract repo name and owner if: github.event_name != 'pull_request' run: echo "REPO_NAME=$(echo ${{ github.repository }}.git)" >> $GITHUB_ENV shell: bash - name: Extract repo name and owner on pull request if: github.event_name == 'pull_request' run: echo "REPO_NAME=$(echo ${{ github.event.pull_request.head.repo.full_name }}.git)" >> $GITHUB_ENV shell: bash - name: Install cairo-profiler run: | curl -L https://raw.githubusercontent.com/software-mansion/cairo-profiler/main/scripts/install.sh | sh - name: Install cairo-coverage run: | curl -L https://raw.githubusercontent.com/software-mansion/cairo-coverage/main/scripts/install.sh | sh - uses: actions/checkout@v6 - uses: asdf-vm/actions/install@b7bcd026f18772e44fe1026d729e1611cc435d47 - uses: ./.github/actions/setup-tools - uses: taiki-e/install-action@v2 with: tool: nextest@0.9.98 - uses: actions/download-artifact@v6 with: name: nextest-archive-${{ runner.os }} - name: nextest partition ${{ matrix.partition }}/3 run: cargo nextest run --no-fail-fast --partition 'count:${{ matrix.partition }}/3' --archive-file 'nextest-archive-${{ runner.os }}.tar.zst' e2e test-forge-e2e-native: name: Test Forge / E2E Tests (native) runs-on: ubuntu-latest needs: [ build-test-forge-nextest-archive-native ] strategy: fail-fast: false matrix: partition: [ 1, 2, 3 ] steps: - name: Extract branch name if: github.event_name != 'pull_request' run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV shell: bash - name: Extract branch name on pull request if: github.event_name == 'pull_request' run: echo "BRANCH_NAME=$(echo $GITHUB_HEAD_REF)" >> $GITHUB_ENV shell: bash - name: Extract repo name and owner if: github.event_name != 'pull_request' run: echo "REPO_NAME=$(echo ${{ github.repository }}.git)" >> $GITHUB_ENV shell: bash - name: Extract repo name and owner on pull request if: github.event_name == 'pull_request' run: echo "REPO_NAME=$(echo ${{ github.event.pull_request.head.repo.full_name }}.git)" >> $GITHUB_ENV shell: bash - name: Install cairo-profiler run: | curl -L https://raw.githubusercontent.com/software-mansion/cairo-profiler/main/scripts/install.sh | sh - name: Install cairo-coverage run: | curl -L https://raw.githubusercontent.com/software-mansion/cairo-coverage/main/scripts/install.sh | sh - uses: actions/checkout@v6 - uses: asdf-vm/actions/install@b7bcd026f18772e44fe1026d729e1611cc435d47 - uses: ./.github/actions/setup-tools - uses: taiki-e/install-action@v2 with: tool: nextest@0.9.98 - uses: actions/download-artifact@v6 with: name: nextest-archive-${{ runner.os }}-native - name: nextest partition ${{ matrix.partition }}/3 run: cargo nextest run --no-fail-fast --partition 'count:${{ matrix.partition }}/3' --archive-file 'nextest-archive-${{ runner.os }}-native.tar.zst' e2e test-forge-e2e-snap: if: github.event_name == 'merge_group' name: Test Forge / E2E Snap (Scarb ${{ matrix.version }}) runs-on: ubuntu-latest needs: get-scarb-versions strategy: fail-fast: false matrix: version: ${{ fromJSON(needs.get-scarb-versions.outputs.versions) }} steps: - name: Extract branch name if: github.event_name != 'pull_request' run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV shell: bash - name: Extract branch name on pull request if: github.event_name == 'pull_request' run: echo "BRANCH_NAME=$(echo $GITHUB_HEAD_REF)" >> $GITHUB_ENV shell: bash - name: Extract repo name and owner if: github.event_name != 'pull_request' run: echo "REPO_NAME=$(echo ${{ github.repository }}.git)" >> $GITHUB_ENV shell: bash - name: Extract repo name and owner on pull request if: github.event_name == 'pull_request' run: echo "REPO_NAME=$(echo ${{ github.event.pull_request.head.repo.full_name }}.git)" >> $GITHUB_ENV shell: bash - name: Install cairo-profiler run: | curl -L https://raw.githubusercontent.com/software-mansion/cairo-profiler/main/scripts/install.sh | sh - name: Install cairo-coverage run: | curl -L https://raw.githubusercontent.com/software-mansion/cairo-coverage/main/scripts/install.sh | sh - uses: actions/checkout@v6 - uses: ./.github/actions/setup-tools with: scarb-version: ${{ matrix.version }} - name: Run snap E2E tests run: cargo test --profile ci -p forge --test main snap_ test-plugin-checks: name: Test plugin across different scarb versions runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: ./.github/actions/setup-tools # No setup scarb action is used because we want to install multiple versions of scarb through asdf with: setup-scarb: 'false' - uses: asdf-vm/actions/install@b7bcd026f18772e44fe1026d729e1611cc435d47 - run: cargo test --profile ci --package forge --features test_for_multiple_scarb_versions e2e::plugin_diagnostic test-requirements-check-special-conditions: name: Test requirements check special conditions runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: ./.github/actions/setup-tools with: setup-scarb: 'false' - run: cargo test --profile ci --package forge --features no_scarb_installed --lib compatibility_check::tests::failing_tool_not_installed - uses: software-mansion/setup-scarb@v1 with: scarb-version: "2.12.0" - run: cargo test --profile ci --package forge --features scarb_2_12_0 --test main e2e::requirements::test_warning_on_scarb_version_below_recommended test-forge-oracles: name: Test Oracles in Forge runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: ./.github/actions/setup-tools with: scarb-version: '2.13.1' - run: cargo test --profile ci -p forge --features run_test_for_scarb_since_2_13_1 e2e::oracles test-forge-scarb-plugin: name: Test Forge Scarb Plugin runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: ./.github/actions/setup-tools - name: Run Forge Scarb Plugin tests working-directory: crates/snforge-scarb-plugin run: cargo test --profile ci test-cast: name: Test Cast runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: asdf-vm/actions/install@b7bcd026f18772e44fe1026d729e1611cc435d47 - uses: ./.github/actions/setup-tools - name: Run tests run: cargo test --profile ci -p sncast test-ledger: name: Test Ledger runs-on: ubuntu-latest container: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools steps: - uses: actions/checkout@v6 - uses: asdf-vm/actions/install@b7bcd026f18772e44fe1026d729e1611cc435d47 - uses: ./.github/actions/setup-tools - name: Run tests run: cargo test --profile ci -p sncast --features ledger-emulator ledger -- --ignored fmt-lint-typos: name: Lint and Format runs-on: ubuntu-latest env: # Make sure CI fails on all warnings - including Clippy lints. RUSTFLAGS: "-Dwarnings" steps: - uses: actions/checkout@v6 - uses: ./.github/actions/setup-tools with: install-llvm: 'true' setup-usc: 'false' - run: rustup component add rustfmt - name: Check formatting run: cargo fmt --check - name: Check formatting snforge-scarb-plugin run: cargo fmt --check working-directory: crates/snforge-scarb-plugin - name: Lint run: cargo lint - name: Lint snforge-scarb-plugin run: cargo lint working-directory: crates/snforge-scarb-plugin - name: Check cairo files format run: | output=$(find . -type f -name "Scarb.toml" -execdir sh -c ' echo "Running \"scarb fmt\" in directory: $PWD" scarb fmt --check ' \;) echo "$output" if grep -iq "Diff" <<< "$output"; then exit 1 fi exit 0 - name: Check typos uses: crate-ci/typos@v1.40.0 build-docs: name: Test Building Docs runs-on: ubuntu-latest env: MDBOOK_VERSION: 0.4.52 # TODO(#3970): Use latest version after resolving issue MDBOOK_VARIABLES_VERSION: 0.3.0 steps: - uses: actions/checkout@v6 - uses: ./.github/actions/setup-tools - name: Install mdBook run: | cargo install --version ${MDBOOK_VERSION} mdbook cargo install --version ${MDBOOK_VARIABLES_VERSION} mdbook-variables - name: Install mdBook Link-Check run: | cargo install mdbook-linkcheck - name: Build with mdBook run: | # TODO(#2781): Use `mdbook build` ./scripts/build_docs.sh - name: Install Forge env: CARGO_INCREMENTAL: 0 run: | cargo install --path crates/forge --locked --debug - name: Verify Cairo listings run: | ./scripts/verify_cairo_listings.sh ================================================ FILE: .github/workflows/docs.yml ================================================ # Initial version from: https://github.com/actions/starter-workflows/blob/main/pages/mdbook.yml # name: Deploy mdBook site to Pages on: # Allows using this workflow in other workflows workflow_call: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: release: types: - released # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: contents: read pages: write id-token: write # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. concurrency: group: "pages" cancel-in-progress: false jobs: # Build job build: runs-on: ubuntu-latest env: MDBOOK_VERSION: 0.4.52 # TODO(#3970): Use latest version after resolving issue MDBOOK_VARIABLES_VERSION: 0.3.0 steps: - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@0b1efabc08b657293548b77fb76cc02d26091c7e with: toolchain: stable - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 - uses: actions/setup-node@v6 - name: Install sitemap CLI run: | npm i -g static-sitemap-cli - name: Install mdBook run: | cargo install --version ${MDBOOK_VERSION} mdbook cargo install --version ${MDBOOK_VARIABLES_VERSION} mdbook-variables - name: Install mdBook Link-Check run: | cargo install mdbook-linkcheck - name: Install Scarb uses: software-mansion/setup-scarb@v1 with: scarb-version: latest - name: Generate and build snforge_std docs run: scarb doc --build working-directory: ./snforge_std - name: Generate and build sncast_std docs run: scarb doc --build working-directory: ./sncast_std - name: Setup Pages id: pages uses: actions/configure-pages@v5 - name: Build with mdBook run: mdbook build working-directory: ./docs - name: Add snforge_std docs run: | mkdir -p ./docs/book/html/snforge_std cp -r ./snforge_std/target/doc/snforge_std/book/* ./docs/book/html/snforge_std/ - name: Add sncast_std docs run: | mkdir -p ./docs/book/html/sncast_std cp -r ./sncast_std/target/doc/sncast_std/book/* ./docs/book/html/sncast_std/ - name: Generate sitemap run: | sscli --base https://foundry-rs.github.io/starknet-foundry working-directory: ./docs/book/html - name: Upload artifact uses: actions/upload-pages-artifact@v4 with: path: ./docs/book/html # Deployment job deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 ================================================ FILE: .github/workflows/nightly.yml ================================================ name: Nightly on: workflow_dispatch: inputs: dry_run: description: "Dry run" type: boolean default: true workflow_call: concurrency: group: ${{ github.workflow }} cancel-in-progress: true permissions: contents: write jobs: prepare: runs-on: ubuntu-latest outputs: nightly_tag: ${{ steps.version.outputs.nightly_tag }} nightly_version: ${{ steps.version.outputs.nightly_version }} nightly_branch: ${{ steps.version.outputs.nightly_branch }} steps: - uses: actions/checkout@v6 - name: Configure Git for committing run: | git config user.name github-actions git config user.email github-actions@github.com - name: Determine nightly version id: version shell: bash run: | NIGHTLY_TAG="nightly-$(date -u +%Y-%m-%d)" NIGHTLY_BRANCH="nightly/tmp/$NIGHTLY_TAG" CURR_VERSION=$(grep '^version' Cargo.toml | cut -d '"' -f2) NIGHTLY_VERSION="${CURR_VERSION}+${NIGHTLY_TAG}" echo "NIGHTLY_TAG=$NIGHTLY_TAG" >> $GITHUB_ENV echo "NIGHTLY_VERSION=$NIGHTLY_VERSION" >> $GITHUB_ENV echo "NIGHTLY_BRANCH=$NIGHTLY_BRANCH" >> $GITHUB_ENV echo "nightly_tag=$NIGHTLY_TAG" >> $GITHUB_OUTPUT echo "nightly_version=$NIGHTLY_VERSION" >> $GITHUB_OUTPUT echo "nightly_branch=$NIGHTLY_BRANCH" >> $GITHUB_OUTPUT - uses: software-mansion/setup-scarb@v1 - name: Update metadata before release run: ./scripts/release.sh ${{ env.NIGHTLY_VERSION }} - name: Create release notes run: | repo="${{ github.repository }}" hash="${{ github.sha }}" echo "Source commit: [\`${hash:0:7}\`](https://github.com/$repo/commit/$hash)" > NIGHTLY_RELEASE_NOTES.md - name: Commit patches run: | git checkout -b ${{ env.NIGHTLY_BRANCH }} git add . git commit -m ${{ env.NIGHTLY_TAG }} echo $(git log -1) # NOTE: This must be the last operation done in this job in order for cleanup job to work properly. - name: Push patches to the repository run: git push origin ${{ env.NIGHTLY_BRANCH }} build-binaries: needs: prepare uses: ./.github/workflows/_build-binaries.yml with: version: ${{ needs.prepare.outputs.nightly_version }} ref: ${{ needs.prepare.outputs.nightly_branch }} build-plugin-binaries: name: Build plugin binaries needs: prepare uses: ./.github/workflows/_build-plugin-binaries.yml with: overridden_plugin_version: ${{ needs.prepare.outputs.nightly_version }} ref: ${{ needs.prepare.outputs.nightly_branch }} plugin_name: "snforge-scarb-plugin" publish-plugin: needs: [ prepare, build-plugin-binaries ] if: ${{ !(inputs.dry_run) }} uses: ./.github/workflows/_publish-plugin.yml secrets: inherit with: prod_registry: false overridden_plugin_version: ${{ needs.prepare.outputs.nightly_version }} plugin_name: "snforge-scarb-plugin" publish-std: needs: [ prepare, publish-plugin ] if: ${{ !(inputs.dry_run) }} uses: ./.github/workflows/publish-std.yml secrets: inherit with: prod_registry: false plugin_dep_version: ${{ needs.prepare.outputs.nightly_version }} override_std_version: ${{ needs.prepare.outputs.nightly_version }} test-binary: name: Test binary needs: [ prepare, build-binaries, build-plugin-binaries, publish-plugin, publish-std ] uses: ./.github/workflows/_test-binaries.yml secrets: inherit with: bin_version: ${{ needs.prepare.outputs.nightly_version }} std_version: ${{ needs.prepare.outputs.nightly_version }} create-release: runs-on: ubuntu-latest needs: [ prepare, build-binaries, test-binary ] # Do not run on dry_run if: ${{ !(inputs.dry_run) }} env: GH_TOKEN: ${{ secrets.SNFOUNDRY_NIGHTLIES_CONTENTS_WRITE }} steps: - uses: actions/checkout@v6 with: ref: ${{ needs.prepare.outputs.nightly_branch }} - name: Create source code archives run: | git archive "--prefix=starknet-foundry-${{ needs.prepare.outputs.nightly_tag }}/" -o "starknet-foundry-${{ needs.prepare.outputs.nightly_tag }}.zip" HEAD git archive "--prefix=starknet-foundry-${{ needs.prepare.outputs.nightly_tag }}/" -o "starknet-foundry-${{ needs.prepare.outputs.nightly_tag }}.tar.gz" HEAD - name: Download artifacts uses: actions/download-artifact@v6 with: path: artifacts-dl - name: Unpack artifacts to staging directory run: | mkdir -p artifacts mv artifacts-dl/build-*/starknet-foundry-* artifacts/ ls -lh artifacts/ - name: Create GitHub release run: | gh release create \ "${{ needs.prepare.outputs.nightly_tag }}" \ --repo software-mansion-labs/starknet-foundry-nightlies \ --latest \ --title "${{ needs.prepare.outputs.nightly_tag }}" \ --notes-file NIGHTLY_RELEASE_NOTES.md - name: Upload built artifacts run: | for file in ./artifacts/* do # We remove the version tag from the filename so it can # be easily accessed in asdf and installation scripts. # # For example: # starknet-foundry-v0.44.0+nightly-2025-05-22-aarch64-apple-darwin.tar.gz # becomes: # starknet-foundry-nightly-2025-05-22-aarch64-apple-darwin.tar.gz label=$(echo "$file" | sed -E "s/v[^+]*\+//" | sed -E "s|.*/||") cp "$file" "$label" file="$label" gh release upload \ "${{ needs.prepare.outputs.nightly_tag }}" \ "$file" \ --repo software-mansion-labs/starknet-foundry-nightlies done - name: Upload source code archives run: | for file in \ "starknet-foundry-${{ needs.prepare.outputs.nightly_tag }}.zip#Starknet Foundry source code (zip)" \ "starknet-foundry-${{ needs.prepare.outputs.nightly_tag }}.tar.gz#Starknet Foundry source code (tar.gz)" do gh release upload \ "${{ needs.prepare.outputs.nightly_tag }}" \ "$file" \ --repo software-mansion-labs/starknet-foundry-nightlies done cleanup: runs-on: ubuntu-latest if: always() && needs.prepare.result == 'success' needs: [ prepare, create-release ] steps: - uses: actions/checkout@v6 - name: Delete nightly branch run: | git push origin -d ${{ needs.prepare.outputs.nightly_branch }} ================================================ FILE: .github/workflows/publish-plugin.yml ================================================ name: Publish snforge_scarb_plugin on: workflow_dispatch: inputs: prod_registry: required: false type: boolean # Specify the version in MAJOR.MINOR.PATCH format, without a leading 'v' overridden_plugin_version: required: false type: string plugin_name: required: true type: string jobs: build-binaries: name: Build Plugin Binaries uses: ./.github/workflows/_build-plugin-binaries.yml with: overridden_plugin_version: ${{ inputs.overridden_plugin_version != '' && inputs.overridden_plugin_version || '' }} plugin_name: ${{ inputs.plugin_name }} publish-plugin: name: Publish Plugin needs: build-binaries uses: ./.github/workflows/_publish-plugin.yml with: prod_registry: ${{ inputs.prod_registry }} overridden_plugin_version: ${{ inputs.overridden_plugin_version != '' && inputs.overridden_plugin_version || '' }} plugin_name: '${{ inputs.plugin_name }}' secrets: inherit ================================================ FILE: .github/workflows/publish-std.yml ================================================ name: Publish snforge_std and sncast_std on: workflow_call: inputs: prod_registry: required: false type: boolean # Specify the version in MAJOR.MINOR.PATCH format, without a leading 'v' override_std_version: required: false type: string # snforge_std in the repository has a plugin dependency specified as a relative path, which must be overridden each time before publishing. # Specify the version in MAJOR.MINOR.PATCH format, without a leading 'v' plugin_dep_version: required: true type: string workflow_dispatch: inputs: prod_registry: required: false type: boolean # Specify the version in MAJOR.MINOR.PATCH format, without a leading 'v' override_std_version: required: false type: string # snforge_std in the repository has a plugin dependency specified as a relative path, which must be overridden each time before publishing. # Specify the version in MAJOR.MINOR.PATCH format, without a leading 'v' plugin_dep_version: required: true type: string jobs: check-uploaded: name: Check stds uploaded runs-on: ubuntu-latest outputs: snforge_std_uploaded: ${{ steps.check-uploaded.outputs.snforge_std_uploaded }} sncast_std_uploaded: ${{ steps.check-uploaded.outputs.sncast_std_uploaded }} steps: - uses: actions/checkout@v6 - name: Check version id: check-uploaded run: | set -exo pipefail source scripts/handle_version.sh snforge_std_version=$(get_version "${{ inputs.override_std_version }}" "snforge_std") sncast_std_version=$(get_version "${{ inputs.override_std_version }}" "sncast_std") registry_url=${{ inputs.prod_registry == true && 'https://scarbs.xyz' || 'https://scarbs.dev' }} snforge_std_uploaded=$(curl -s ${registry_url}/api/v1/index/sn/fo/snforge_std.json | jq --arg version "$snforge_std_version" '[.[] | select(.v == $version)] | length > 0') sncast_std_uploaded=$(curl -s ${registry_url}/api/v1/index/sn/ca/sncast_std.json | jq --arg version "$sncast_std_version" '[.[] | select(.v == $version)] | length > 0') echo "snforge_std_uploaded=$snforge_std_uploaded" >> $GITHUB_OUTPUT echo "sncast_std_uploaded=$sncast_std_uploaded" >> $GITHUB_OUTPUT publish-to-registry: name: Publish packages to the registry runs-on: ubuntu-latest needs: [ check-uploaded ] env: SCARB_REGISTRY_AUTH_TOKEN: ${{ inputs.prod_registry == true && secrets.SCARB_REGISTRY_AUTH_TOKEN || secrets.DEV_SCARB_REGISTRY_AUTH_TOKEN }} steps: - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@0b1efabc08b657293548b77fb76cc02d26091c7e with: toolchain: stable - uses: software-mansion/setup-scarb@v1 - name: Publish sncast_std if: needs.check-uploaded.outputs.sncast_std_uploaded == 'false' working-directory: sncast_std run: | source ../scripts/handle_version.sh update_version_in_file "Scarb.toml" "${{ inputs.override_std_version }}" scarb publish --allow-dirty ${{ inputs.prod_registry == true && ' ' || '--index https://scarbs.dev/' }} - name: Publish snforge_std if: needs.check-uploaded.outputs.snforge_std_uploaded == 'false' working-directory: snforge_std run: | source ../scripts/handle_version.sh update_version_in_file "Scarb.toml" "${{ inputs.override_std_version }}" if ${{ inputs.prod_registry == true }}; then scarb add snforge_scarb_plugin@${{ inputs.plugin_dep_version }} else sed -i.bak "/snforge_scarb_plugin/ s/\(snforge_scarb_plugin = \).*/\1{ version = \"=${{ inputs.plugin_dep_version }}\", registry = \"https:\/\/scarbs.dev\/\" }/" Scarb.toml rm Scarb.toml.bak 2>/dev/null fi scarb publish --allow-dirty ${{ inputs.prod_registry == true && ' ' || '--index https://scarbs.dev/' }} ================================================ FILE: .github/workflows/release.yml ================================================ name: Release on: push: branches: - 'master' tags: - v[0-9]+.* permissions: contents: write jobs: verify-version: name: Verify that version that triggered this workflow is greater than most recent release runs-on: ubuntu-latest outputs: # Steps `verifyBranchVersion` and `verifyTagVersion` are mutually exclusive, only one of them will run versionIsValid: ${{ steps.verifyBranchVersion.outputs.versionIsValid }}${{ steps.verifyTagVersion.outputs.versionIsValid }} version: ${{ steps.verifyBranchVersion.outputs.version }}${{ steps.verifyTagVersion.outputs.version }} steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: cache: 'npm' cache-dependency-path: scripts/package-lock.json - run: npm ci working-directory: scripts - name: Get version from Cargo.toml id: lookupVersion uses: mikefarah/yq@7ccaf8e700ce99eb3f0f6cef7f5930a0b3c827cd with: cmd: yq -oy '.workspace.package.version' 'Cargo.toml' - name: Get version from the latest releases id: lookupVersionRelease uses: pozetroninc/github-action-get-latest-release@master with: owner: foundry-rs repo: starknet-foundry excludes: draft - name: Verify branch version id: verifyBranchVersion if: github.ref_type == 'branch' run: | RELEASE_VERSION=${{ steps.lookupVersionRelease.outputs.release }} COMMIT_VERSION=${{ steps.lookupVersion.outputs.result }} echo "Project version from newest release = $RELEASE_VERSION" echo "Project version from this commit = $COMMIT_VERSION" if gh release view "v$COMMIT_VERSION" >/dev/null 2>&1; then echo "Release v$COMMIT_VERSION already exists - aborting" echo "versionIsValid=false" >> "$GITHUB_OUTPUT" exit 0 fi IS_GREATER=$(node ./scripts/compareVersions.js $RELEASE_VERSION $COMMIT_VERSION) echo "versionIsValid=$IS_GREATER" >> "$GITHUB_OUTPUT" echo "version=$COMMIT_VERSION" >> "$GITHUB_OUTPUT" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Verify tag version id: verifyTagVersion if: github.ref_type == 'tag' run: | TAG_NAME="$GITHUB_REF_NAME" TAG_VERSION="${TAG_NAME#v}" CARGO_VERSION="${{ steps.lookupVersion.outputs.result }}" echo "Tag version = $TAG_VERSION" echo "Project version from Cargo.toml = $CARGO_VERSION" if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then echo "Tag version ($TAG_VERSION) does not match Cargo.toml version ($CARGO_VERSION)" exit 1 fi # Tag is usually created when publishing release from GitHub UI. # Then it triggers this workflow again and we want to exit without error. if gh release view "$TAG_NAME" >/dev/null 2>&1; then echo "Release $TAG_NAME already exists - aborting" echo "versionIsValid=false" >> "$GITHUB_OUTPUT" exit 0 fi echo "versionIsValid=true" >> "$GITHUB_OUTPUT" echo "version=$TAG_VERSION" >> "$GITHUB_OUTPUT" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} build-binaries: name: Build binaries needs: verify-version if: ${{ needs.verify-version.outputs.versionIsValid == 'true' }} uses: ./.github/workflows/_build-binaries.yml with: version: ${{ needs.verify-version.outputs.version }} build-plugin-binaries: name: Build plugin binaries if: ${{ needs.verify-version.outputs.versionIsValid == 'true' }} needs: verify-version uses: ./.github/workflows/_build-plugin-binaries.yml with: plugin_name: "snforge-scarb-plugin" dev-publish-plugin: needs: [verify-version, build-plugin-binaries] if: ${{ needs.verify-version.outputs.versionIsValid == 'true' }} uses: ./.github/workflows/_publish-plugin.yml secrets: inherit with: prod_registry: false overridden_plugin_version: ${{ needs.verify-version.outputs.version }}-test.${{ github.run_id }} plugin_name: "snforge-scarb-plugin" dev-publish-std: needs: [verify-version, dev-publish-plugin] if: ${{ needs.verify-version.outputs.versionIsValid == 'true' }} uses: ./.github/workflows/publish-std.yml secrets: inherit with: prod_registry: false plugin_dep_version: ${{ needs.verify-version.outputs.version }}-test.${{ github.run_id }} override_std_version: ${{ needs.verify-version.outputs.version }}-test.${{ github.run_id }} test-binary: name: Test binary needs: [build-binaries, verify-version, dev-publish-std, dev-publish-plugin] uses: ./.github/workflows/_test-binaries.yml secrets: inherit with: bin_version: ${{ needs.verify-version.outputs.version }} std_version: ${{ needs.verify-version.outputs.version }}-test.${{ github.run_id }} create-release: name: Create release runs-on: ubuntu-latest needs: [ test-binary, verify-version ] steps: - uses: actions/checkout@v6 - name: Download artifacts uses: actions/download-artifact@v6 with: path: artifacts-dl - name: Unpack artifacts to staging directory run: | mkdir -p artifacts mv artifacts-dl/build-*/starknet-foundry-* artifacts/ - name: Create GitHub release id: create-release uses: taiki-e/create-gh-release-action@26b80501670402f1999aff4b934e1574ef2d3705 with: token: ${{ secrets.GITHUB_TOKEN }} draft: true changelog: CHANGELOG.md allow-missing-changelog: false title: $version ref: refs/tags/v${{ needs.verify-version.outputs.version }} - name: Upload artifacts to the release working-directory: artifacts run: gh release upload "$TAG" * env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAG: ${{ steps.create-release.outputs.computed-prefix }}${{ steps.create-release.outputs.version }} publish-snforge-scarb-plugin: name: Publish snforge_scarb_plugin needs: [test-binary, create-release] uses: ./.github/workflows/_publish-plugin.yml secrets: inherit with: prod_registry: true plugin_name: "snforge-scarb-plugin" publish-to-registry: name: Publish packages to the registry needs: [ verify-version, publish-snforge-scarb-plugin ] uses: ./.github/workflows/publish-std.yml secrets: inherit with: plugin_dep_version: ${{ needs.verify-version.outputs.version }} prod_registry: true ================================================ FILE: .github/workflows/scheduled.yml ================================================ name: Scheduled on: pull_request: paths: - scripts/get_scarb_versions.sh - .github/workflows/scheduled.yml schedule: # Two schedules needed so we can distinguish between nightly and non-nightly runs - cron: '0 0 * * 0' - cron: '0 0 * * 3' workflow_dispatch: jobs: get-scarb-versions: if: github.event.repository.fork == false name: Get Scarb versions outputs: versions: ${{ steps.get_versions.outputs.versions }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: asdf-vm/actions/setup@b7bcd026f18772e44fe1026d729e1611cc435d47 - run: | asdf plugin add scarb asdf install scarb latest asdf set --home scarb latest - name: Get versions id: get_versions run: | scarb_versions=$(./scripts/get_scarb_versions.sh) echo ${scarb_versions[@]} echo "versions=[${scarb_versions[@]}]" >> "$GITHUB_OUTPUT" test-forge-unit-and-integration: if: github.event.repository.fork == false runs-on: ubuntu-latest needs: get-scarb-versions strategy: fail-fast: false matrix: version: ${{ fromJSON(needs.get-scarb-versions.outputs.versions) }} steps: - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 - uses: software-mansion/setup-scarb@v1 with: scarb-version: ${{ matrix.version }} - uses: software-mansion/setup-universal-sierra-compiler@v1 - run: cargo test --profile ci --lib -p forge - run: cargo test --profile ci -p forge integration --features non_exact_gas_assertions test-forge-e2e: if: github.event.repository.fork == false runs-on: ubuntu-latest needs: get-scarb-versions strategy: fail-fast: false matrix: version: ${{ fromJSON(needs.get-scarb-versions.outputs.versions) }} steps: - name: Extract branch name if: github.event_name != 'pull_request' run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV - name: Extract branch name on pull request if: github.event_name == 'pull_request' run: echo "BRANCH_NAME=$(echo $GITHUB_HEAD_REF)" >> $GITHUB_ENV - name: Extract repo name and owner if: github.event_name != 'pull_request' run: echo "REPO_NAME=$(echo ${{ github.repository }}.git)" >> $GITHUB_ENV - name: Extract repo name and owner on pull request if: github.event_name == 'pull_request' run: echo "REPO_NAME=$(echo ${{ github.event.pull_request.head.repo.full_name }}.git)" >> $GITHUB_ENV - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 - uses: software-mansion/setup-scarb@v1 with: scarb-version: ${{ matrix.version }} - uses: software-mansion/setup-universal-sierra-compiler@v1 - name: Install cairo-profiler run: | curl -L https://raw.githubusercontent.com/software-mansion/cairo-profiler/main/scripts/install.sh | sh - name: Install cairo-coverage run: | curl -L https://raw.githubusercontent.com/software-mansion/cairo-coverage/main/scripts/install.sh | sh - name: Determine test features id: test_features run: | SCARB_VERSION="${{ matrix.version }}" if [ "$(printf '%s\n' "2.15.1" "$SCARB_VERSION" | sort -V | head -n1)" = "2.15.1" ]; then FEATURES="skip_test_for_only_latest_scarb,run_test_for_scarb_since_2_15_1" else FEATURES="skip_test_for_only_latest_scarb" fi echo "features=$FEATURES" >> "$GITHUB_OUTPUT" - run: cargo test --profile ci -p forge --features "${{ steps.test_features.outputs.features }}" e2e test-cast: if: github.event.repository.fork == false runs-on: ubuntu-latest needs: get-scarb-versions strategy: fail-fast: false matrix: version: ${{ fromJSON(needs.get-scarb-versions.outputs.versions) }} steps: - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 - uses: software-mansion/setup-scarb@v1 with: scarb-version: ${{ matrix.version }} - uses: software-mansion/setup-universal-sierra-compiler@v1 - name: Get Devnet version from .tool-versions id: get-devnet-version run: | devnet_version=$(grep starknet-devnet .tool-versions | cut -d " " -f 2) echo "Devnet version: $devnet_version" echo "version=$devnet_version" >> "$GITHUB_OUTPUT" - uses: asdf-vm/actions/install@b7bcd026f18772e44fe1026d729e1611cc435d47 with: tool_versions: | starknet-devnet ${{ steps.get-devnet-version.outputs.version }} - run: cargo test --profile ci -p sncast get-version: name: Get current foundry version if: github.event.repository.fork == false runs-on: ubuntu-latest outputs: version: ${{ steps.validVersion.outputs.version }} steps: - uses: actions/checkout@v6 - name: Get version from Cargo.toml id: lookupVersion uses: mikefarah/yq@7ccaf8e700ce99eb3f0f6cef7f5930a0b3c827cd with: cmd: yq -oy '.workspace.package.version' 'Cargo.toml' - name: Return version id: validVersion run: | COMMIT_VERSION=${{ steps.lookupVersion.outputs.result }} echo "Project version from this commit = $COMMIT_VERSION" echo "version=$COMMIT_VERSION" >> "$GITHUB_OUTPUT" build-plugin-binaries: name: Build plugin binaries needs: get-version uses: ./.github/workflows/_build-plugin-binaries.yml with: overridden_plugin_version: ${{ needs.get-version.outputs.version }}-test.${{ github.run_id }} plugin_name: 'snforge-scarb-plugin' publish-plugin: needs: [ get-version, build-plugin-binaries ] uses: ./.github/workflows/_publish-plugin.yml secrets: inherit with: prod_registry: false overridden_plugin_version: ${{ needs.get-version.outputs.version }}-test.${{ github.run_id }} plugin_name: 'snforge-scarb-plugin' publish-std: needs: [ get-version, publish-plugin ] uses: ./.github/workflows/publish-std.yml secrets: inherit with: prod_registry: false plugin_dep_version: ${{ needs.get-version.outputs.version }}-test.${{ github.run_id }} override_std_version: ${{ needs.get-version.outputs.version }}-test.${{ github.run_id }} build-binaries: needs: [ get-version ] uses: ./.github/workflows/_build-binaries.yml with: version: ${{ needs.get-version.outputs.version }}-test.${{ github.run_id }} test-binary: name: Test binary needs: [ build-binaries, get-version, publish-std ] uses: ./.github/workflows/_test-binaries.yml secrets: inherit with: bin_version: ${{ needs.get-version.outputs.version }}-test.${{ github.run_id }} std_version: ${{ needs.get-version.outputs.version }}-test.${{ github.run_id }} create-nightly-release: # Creating a nightly release will trigger a Maat experiment for it as well # Publish nightly only on Sundays if: ${{ (github.event_name == 'schedule') && (github.event.schedule == '0 0 * * 0') }} needs: [ test-binary ] secrets: inherit uses: ./.github/workflows/nightly.yml notify_if_failed: runs-on: ubuntu-latest if: always() && contains(needs.*.result, 'failure') && github.event_name == 'schedule' needs: [ create-nightly-release, test-forge-unit-and-integration, test-forge-e2e, test-cast , build-plugin-binaries, build-binaries, publish-plugin, publish-std, test-binary ] steps: - name: Notify that the workflow has failed uses: slackapi/slack-github-action@v2.1.1 with: webhook: ${{ secrets.SLACK_SCHEDULED_TESTS_FAILURE_WEBHOOK_URL }} webhook-type: webhook-trigger payload: | url: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" ================================================ FILE: .gitignore ================================================ .idea pyproject.toml .DS_Store */.tool-versions target .vscode/ .env Scarb.lock !snforge_std/Scarb.lock !sncast_std/Scarb.lock */node_modules .spr.yml .snfoundry_cache/ .snfoundry_versioned_programs/ snfoundry_trace/ coverage/ **/.forge_e2e_cache/ *.snap.new ================================================ FILE: .tool-versions ================================================ scarb 2.17.0 starknet-devnet 0.8.0-rc.3 ================================================ FILE: CAIRO_NATIVE.md ================================================ # Running Cairo Native * [Running Cairo Native](#running-cairo-native) * [Installing Starknet Foundry With Cairo Native Support](#installing-starknet-foundry-with-cairo-native-support) * [LLVM](#llvm) * [`ld`](#ld) * [Linux](#linux) * [MacOS](#macos) * [Running Tests](#running-tests) ## Installing Starknet Foundry With Cairo Native Support Cairo Native introduces additional dependencies outside of the Rust ecosystem. ### LLVM LLVM is linked into Starknet Foundry binary, so it doesn't have to be installed separately at the cost of an increased binary size. ### `ld` Cairo Native crate makes direct calls to `ld`, which must in turn be installed on the system. #### Linux The package `binutils` contains `ld`, install it with package manager relevant to your distribution or build it from source. #### MacOS `ld` is part of the Xcode command line tools. Install it with `xcode-select --install`. ## Running Tests To run tests run `snforge test --run-native`, without the flag, the execution will still run in the VM. When running native features that rely on VM trace like test backtrace, profiler, coverage will not work. Running native enforces the test to run with `sierra-gas` as tracked resource. Tracking vm resources is not possible with the native execution. ================================================ FILE: CHANGELOG.md ================================================ # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ### Forge #### Added - `--launch-debugger` flag that allows launching a test in debug mode using `cairo-debugger` crate. Read more [here](https://foundry-rs.github.io/starknet-foundry/snforge-advanced-features/debugging.html#live-debugging) #### Changed - Minimal recommended `Scarb` version is now `2.15.2` (updated from `2.14.0`) ### Cast #### Fixed - `sncast verify` now uses the configured network or infers it from the RPC chain ID when `--network` is omitted. ## [0.59.0] - 2026-04-10 ### Forge #### Added - Cheats for transaction info `proof_facts` #### Changed - Updated execution to reflect Starknet `v0.14.2`, specifically the storage gas price changes (see [SNIP-37](https://community.starknet.io/t/snip-37-revisit-storage-access-cost/116143) for more details) ### Cast #### Added - `sncast get tx` command to get transaction details by hash. - `sncast utils selector` command to calculate entrypoint selector (`sn_keccak`) from function name. - `sncast get class-hash-at` command to get the class hash of a contract at a given address. - `--with-proof-facts` flag for `sncast get tx` to include proof facts in tx response. Read more [here](https://community.starknet.io/t/snip-36-in-protocol-proof-verification/116123l). - `--proof-file` and `--proof-facts-file` flags for `sncast invoke` to attach proof data from files. ## [0.58.1] - 2026-03-31 ### Forge #### Fixed - Bug with using cheats in library calls ### Cast #### Added - `--nonce` flag to `sncast multicall run` ## [0.58.0] - 2026-03-18 ### Forge #### Added - `--max-threads` flag to control the maximum number of threads used for test execution - `optimize-inlining` subcommand, which performs a brute-force search of the optimal `inlining-strategy` threshold for the project over user defined benchmark #### Fixed - Bug with invalid function name mappings for functions with `#[test]` attribute - `--exit-first` flag now correctly stops execution for all packages when tests are run in a workspace ### Cast #### Added - Support for Ledger hardware wallet as a transaction signer - `sncast get nonce` command to get the nonce of a contract - `sncast multicall execute`, which allows to pass calls directly as CLI arguments. Read more [here](https://foundry-rs.github.io/starknet-foundry/starknet/multicall.html#multicall-with-cli-arguments) #### Changed - `sncast balance` and `sncast tx-status` commands have been moved under `sncast get` subcommand (`sncast get balance`, `sncast get tx-status`). The old commands still work, but will be removed in the future. - Referencing an id in multicall configuration files now requires the `@` prefix. ## [0.57.0] - 2026-02-24 ### Forge #### Added - Partitioned test execution with `--partition /` flag. Read more [here](https://foundry-rs.github.io/starknet-foundry/snforge-advanced-features/tests-partitioning.html) #### Changed - Minimal recommended `Scarb` version is now `2.14.0` (updated from `2.13.1`) - In case of a test failure, generic `ENTRYPOINT_FAILED` errors are now included in the panic data #### Fixed - State modified by failed contract calls executed within the test function is now correctly reverted - `--exact` flag now requires `--test-filter` to be non-empty ### Cast #### Added - `--sierra-file` flag to `sncast declare-from` to allow declaring contract from a local Sierra file. #### Removed - Due to termination of starkscan.co, `StarkScan` option for `block-explorer` field in `snfoundry.toml` was removed. For a full list of supported block explorer services, see the [block explorer documentation](https://foundry-rs.github.io/starknet-foundry/appendix/snfoundry-toml.html#block-explorer) ## [0.56.0] - 2026-02-03 ### Forge #### Removed - Support for Scarb versions < 2.12.0. `snforge` now requires Scarb 2.12.0 or newer - The deprecated `snforge_scarb_plugin_deprecated` - The deprecated `snforge_std_deprecated` #### Changed - `--detailed-resources` output now includes all gas-related resources - `deploy` and `deploy_at` methods on `ContractClass` instance now allow constructor errors to be caught instead of failing immediately. This change diverges from the behavior of the `deploy_syscall` on the network, where such errors cannot be caught. - Template created by `snforge new --template erc20-contract` now uses `openzeppelin_token` version `3.0.0` (updated from `1.0.0`). This template requires Scarb version >= `2.15.1`. ## [0.55.0] - 2026-01-13 ### Forge #### Changed - Minimal recommended `Scarb` version is now `2.13.1` (updated from `2.12.2`) - `snforge_scarb_plugin` now emits an error if unexpected named args are passed to `#[available_gas]`, `#[fork]`, `#[fuzzer]`, `#[should_panic]` and `#[test_case]` attributes #### Fixed - Byte arrays containing non-printable characters are now displayed correctly in fuzz test output ### Cast #### Added - `network` field can now be configured in `snfoundry.toml` #### Changed - `--network` flag can be used with `--add-profile` flag #### Fixed - `sncast balance` command now correctly displays token unit instead of token for `eth` and `strk` ## [0.55.0-rc.0] - 2025-12-18 ### Forge #### Changed - Minimal recommended `Scarb` version is now `2.12.2` (updated from `2.11.4`) #### Fixed - Signature generation now enforces the low-s rule - Fixed a bug that caused a slowdown in test execution - Panic that occurred in debug trace and gas report when using mock calls #### Removed - The deprecated `snforge completion`. Use `snforge completions` instead. ### Cast #### Changed - Error message for already declared contracts now includes the class hash #### Fixed - When using `account create` or `account import` commands without specifying `--network` or `--url`, url from snfoundry.toml is now used correctly (if present) - Fixed account's balance value from `sncast balance` command #### Removed - The deprecated `sncast completion`. Use `sncast completions` instead. ## [0.54.0] - 2025-12-09 ### Forge #### Added - `get_current_vm_step` function to get the current step during test execution. For more see [docs](https://foundry-rs.github.io/starknet-foundry/snforge-library/testing/get_current_vm_step.html) #### Fixed - Cheatcodes are now reflected in called contract, when directly using a library call from test code - Oracles are fully supported for Scarb versions >= 2.13.1. Bugs related to oracles' output handling have been fixed. Removed the `--experimental-oracles` flag.` - Fixed a bug that caused failure data to be displayed twice ### Cast #### Added - `type` field to most responses when using `--json` output format. #### Changed - `sncast script init` now longer adds `cairo_test` as dependency in `Scarb.toml` - Replaced the free RPC provider to Zan - The supported RPC version is now 0.10.0 #### Fixed - Restored missing `command` missing from some responses when using `--json` output format. - When deploying account with `--keystore` flag `sncast` now longer ask two times for password ## [0.53.0] - 2025-11-24 ### Cast #### Changed - Hash function used when declaring contracts is selected based on the Starknet version ## [0.53.0-rc.0] - 2025-11-18 ### Forge #### Added - `--gas-report` flag to display a table of L2 gas breakdown for each contract and selector ### Cast #### Changed - The supported RPC version is now 0.10.0-rc.1 ## [0.52.0] - 2025-11-05 ### Forge #### Changed - Gas values in fuzzing test output are now displayed as whole numbers without fractional parts - Minimal recommended `Scarb` version is now `2.11.4` (updated from `2.10.1`) #### Fixed - A bug that prevented the `#[test_case]` attribute from being used on its own with cheatcodes ### Cast #### Added - Possibility to configure urls of predefined networks used by `--network` flag via `sncast` profile in `snfoundry.toml` ## [0.51.2] - 2025-10-31 ### Cast #### Changed - Replaced the free RPC provider used. ## [0.51.1] - 2025-10-23 ### Forge #### Fixed - A bug that caused `meta_tx_v0` to panic when tracked resources are Sierra gas. ## [0.51.0] - 2025-10-21 ### Forge #### Added - `Fuzzable` trait implementations for `bool` and `ContractAddress` - Type aliases for `StarkCurveKeyPair`, `Secp256k1CurveKeyPair`, `Secp256r1CurveKeyPair` - Option to display L2 gas for each call in the debugging trace #### Changed - Updated the error message returned when calling a nonexistent method on a contract to better align with the format used by the network - The default tracked resource is now Sierra gas, so gas reporting results may differ compared to previous versions. For more information refer to the [documentation](https://foundry-rs.github.io/starknet-foundry/testing/gas-and-resource-estimation.html) - When using the `--detailed-resources` flag, the used Sierra gas key is now shown as `sierra gas` instead of `sierra_gas_consumption` ### Cast #### Added - Debug logging for `sncast` commands that can be enabled by setting `SNCAST_LOG` env variable. - `sncast declare` command now outputs a ready-to-use deployment command after successful declaration. - Possibility to use [`starknet-devnet`](https://github.com/0xSpaceShard/starknet-devnet) predeployed accounts directly in `sncast` without needing to import them. They are available under specific names - `devnet-1`, `devnet-2`, ..., `devnet-`. Read more [here](https://foundry-rs.github.io/starknet-foundry/starknet/integration_with_devnet.html#predeployed-accounts) - Support for `--network devnet` flag that attempts to auto-detect running `starknet-devnet` instance and connect to it. - Support for automatically declaring the contract when running `sncast deploy`, by providing `--contract-name` flag instead of `--class-hash`. - `sncast balance` command to fetch the balance of an account for a specified token. #### Fixed - `sncast declare` now shows a correct error message when contract is already declared. ## [0.50.0] - 2025-09-29 ### Forge #### Added - `#[test_case]` attribute for parameterized testing. Read more [here](https://foundry-rs.github.io/starknet-foundry/snforge-advanced-features/parametrized-testing.html) #### Changed - Minimal supported `Scarb` version is now `2.10.0` (updated from `2.9.1`) - Minimal supported `snforge_std` and `snforge_std_deprecated` version is now `0.50.0` - `deploy` and `deploy_at` methods on `ContractClass` instance now fail immediately upon encountering an error, preventing the error from being caught. This change aligns with the behavior of the `deploy_syscall` on the network #### Fixed - [`core::testing::get_available_gas`](https://docs.starknet.io/build/corelib/core-testing-get_available_gas) now works correctly in snforge tests #### Removed - Possibility to use `#[available_gas]` with unnamed argument. Use named arguments instead, e.g. `#[available_gas(l2_gas: 5)]`. - The deprecated command `snforge init`. Use `snforge new` to initialize new `Forge` projects ### Cast #### Added - `sncast declare-from` command to declare a contract by fetching it from a different Starknet instance ## [0.49.0] - 2025-09-03 ### Forge #### Added - Support for `meta_tx_v0` syscall with cheatcode compatibility - `snforge` now supports [oracles](https://docs.swmansion.com/cairo-oracle/) with `--experimental-oracles` flag. - `--trace-components` flag to allow selecting which components of the trace to do display. Read more [here](https://foundry-rs.github.io/starknet-foundry/snforge-advanced-features/debugging.html#trace-components) ### Cast #### Added - `--test-files` flag to `verify` command to include test files under src/ for verification (only applies to voyager) - `--tip` flag to `invoke`, `declare`, `deploy`, `multicall run` and `account deploy` commands to set the transaction tip - `--estimate-tip` flag which automatically adds an estimated tip to the transaction. The tip is calculated based on the current network conditions and added to the transaction fee - `utils class-hash` command to calculate the class hash for a contract #### Changed - The supported RPC version is now 0.9.0 - [New UDC](https://starkscan.co/contract/0x02ceed65a4bd731034c01113685c831b01c15d7d432f71afb1cf1634b53a2125) is now used during deployment ## [0.48.1] - 2025-08-14 ### Forge #### Fixed - A bug that caused `#[fuzzer]` attribute to fail when used with generic structs ## [0.48.0] - 2025-08-05 ### Forge #### Added - `snforge_std` is now compatible with Scarb procedural macros V2. Migration is required if using Scarb versions before `2.12.0`. See [migration guide](https://foundry-rs.github.io/starknet-foundry/getting-started/0-47-0-migration-guide.html). #### Changed - If using a Scarb version before `2.10.0` or not using `allow-prebuild-plugins`, the minimal required rust version to run `snforge` is now `1.87.0` ### Cast #### Fixed - Block explorer links are now hidden by default when using [`starknet-devnet`](https://github.com/0xSpaceShard/starknet-devnet). Set `SNCAST_FORCE_SHOW_EXPLORER_LINKS=1` env variable to display them. ## [0.47.0] - 2025-07-28 ### Forge #### Added - `interact_with_state` cheatcode to enable effective use of `contract_state_for_testing` in snforge tests - Support for using [Scarb profiles](https://docs.swmansion.com/scarb/docs/reference/profiles.html) with `snforge test`, allowing to pass the same profile flags as in Scarb (`--release`, `--dev`, `--profile`) to build artifacts using a specific profile #### Deprecated - The `snforge completion` command. Use `snforge completions` instead #### Fixed - Passing a cheatcode span of 0 was incorrectly treated as `CheatSpan::Indefinite`. This is now resolved by making `CheatSpan::TargetCalls` accept `NonZero` instead of just `usize` in `snforge_std`. ### Cast #### Added - `ready` option for `--type` flag in `account create` and `account import` commands (Argent wallet has rebranded as Ready) #### Changed - Braavos accounts with all class hashes are now supported #### Deprecated - `argent` option for `--type` flag in `account create` and `account import` commands. Use `ready` instead - The `sncast completion` command. Use `sncast completions` instead ## [0.46.0] - 2025-07-09 ### Forge #### Added - Total test summary when running tests across multiple packages (for example when running `snforge test --workspace`) #### Fixed - Bug where syscall execution resources from nested calls were being calculated twice ### Cast #### Added - `sncast utils serialize` command to serialize Cairo expressions into calldata - `sncast verify` now supports verifying against [voyager](https://voyager.online/) block explorer. #### Changed - Improved commands output readability with colors and simplified layout. - `sncast verify` no longer defaults to using walnut. #### Fixed - Bug where `account create` raised an error if no `--name` was provided and the file specified by `--accounts-file` was empty #### Removed - `--int-format` and `--hex-format`, all values are displayed with default format ## [0.45.0] - 2025-06-16 ### Forge #### Added - ETH token is now pre-deployed by default in every test, and `Token::ETH` was added to `snforge_std` - `--skip` flag to allow excluding any test whose name contains the provided string #### Changed - Updated output format for `--exit-first` flag. Tests skipped due to preceding failures are no longer displayed in the summary. Alternative information is shown when applicable. - `storage address` was renamed to `contract address` in the output of `--trace-verbosity` #### Fixed - bug that caused `--trace-verbosity` to panic in fork tests - fixed a bug in tests where resources used in nested calls were counted multiple times, leading to overestimated gas and resource usage #### Removed - Windows support. For details on migration, see the WSL [installation guide](https://foundry-rs.github.io/starknet-foundry/getting-started/installation.html#linux-and-macos). ### Cast #### Fixed - bug where `account create` raised an error if the file specified by `--accounts-file` was empty #### Removed - Windows support. For details on migration, see the WSL [installation guide](https://foundry-rs.github.io/starknet-foundry/getting-started/installation.html#linux-and-macos). ## [0.44.0] - 2025-05-26 ### Forge #### Changed - Minimal supported `snforge_std` version is 0.44.0 - Changed the code generated by `snforge_std`'s `#[test]` attribute #### Fixed - "invalid syscall selector" error appearing when using arithmetic circuits - Bug that caused incorrect gas tracking for contracts using Sierra version less than `1.7.0` when `sierra-gas` was passed as the `tracked-resource` ### Cast #### Added - Displaying the path of the config file when adding a new profile #### Changed - OpenZeppelin account updated to v1.0.0 [preset](https://docs.openzeppelin.com/contracts-cairo/1.0.0/api/account#AccountUpgradeable) - Restored support for Braavos accounts - Accounts created with `--type braavos` use updated v1.2.0 class hash - Output of `sncast account create` is now clearer; the estimated fee is displayed in both STRK and FRI. - Renamed the field `max_fee` to `estimated_fee` in the `sncast account create` output. ## [0.43.1] - 2025-05-16 ### Cast #### Removed - Broken Voyager RPC provider ## [0.43.0] - 2025-05-09 ### Forge #### Added - `set_balance` cheatcode for setting an ERC20 token balance for specified contract address. The STRK token is now pre-deployed in every test by default. This can be disabled by adding `#[disable_predeployed_contracts]` attribute to test. - added option to display trace of contracts execution. Read more [here](https://foundry-rs.github.io/starknet-foundry/snforge-advanced-features/debugging.html) #### Changed - "Success data" message is no longer printed when a test using the `#[should_panic]` attribute passes ### Cast #### Added - when using `sncast call` the response will be printed as a Cairo-like string representation of the return values #### Changed - The supported RPC version is now 0.8.1 ## [0.42.0] - 2025-04-28 ### Forge #### Added - Safe dispatchers can now be used inside contracts #### Changed - Minimal supported Scarb version is now `2.9.1` - Improved display of backtrace for contracts that panicked, when `panic-backtrace = true` in `Scarb.toml`. Without using this feature, the backtrace may be less accurate than before. As of this release, this feature is available only in `scarb nightly-2025-03-27`. #### Fixed - The state correctly reverts after failed internal calls ### Cast #### Fixed - Bug that prevented from passing values to `--arguments` that started with a leading minus `-` sign. - User is now prompted to save an imported or deployed account in `sncast` config even when using `--network` flag ## [0.41.0] - 2025-04-08 ### Forge #### Added - `--template` flag to `snforge new` command that allows selecting a template for the new project. Possible values are `balance-contract` (default), `cairo-program` and `erc20-contract` #### Fixed - fixed incorrect extra newlines in test summary ### Cast #### Added - Support for `array![].span()` in `--arguments` command #### Changed - `verify` command now supports the `--class-hash` for Walnut verification #### Removed - `NftScan` is no longer supported as `block-explorer` option ## [0.40.0] - 2025-03-26 ### Cast #### Added - `--l1-gas`, `--l1-gas-price`, `--l2-gas`, `--l2-gas-price`, `--l1-data-gas`, `--l1-data-gas-price` flags - methods for fee settings creation, in `FeeSettingsTrait`: `max_fee()`, `resource_bounds()` and `estimate()` (in `sncast_std`) #### Changed - Updated argent class hash used in account creation to v0.4.0 - wrapped error for `ContractError` is now of type `ContractErrorData` (in `sncast_std`) - field `execution_error` in `TransactionExecutionErrorData` is now of type `ContractExecutionError` (in `sncast_std`) - Using Braavos accounts is temporarily disabled because they don't yet work with the RPC version supported by `sncast` - `sncast script init` command now initializes project with the `sncast_std` dependency from the [registry](https://scarbs.xyz/packages/sncast_std) #### Removed - `--max-gas` and `--max-gas-unit-price` flags - `max_gas`, `max_gas_unit_price` fields in `FeeSettings` (in `sncast_std`) ## [0.39.0] - 2025-03-19 ### Forge #### Added - `snforge completion` command - used to generate autocompletion script - Cheats for `get_block_hash_syscall` - new `--tracked-resource` flag, that will change currently tracked resource (`cairo-steps` for vm resources - default; `sierra-gas` for sierra gas consumed resources in cairo native) - Testing events api improvements. New `IsEmitted` trait. Implemented `Into` for `starknet::Event` and `PartialEq` trait implementations for `snforge_std::Event` and `snforge_std::Events`. #### Changed - gas is now reported using resource bounds triplet (l1_gas, l1_data_gas and l2_gas) - `available_gas` now accepts named arguments denoting resource bounds (eg `#[available_gas(l1_gas: 1, l1_data_gas: 2, l2_gas: 3)]`) #### Fixed - Bug with file locking that prevented forking from working on Windows ### Cast #### Added - `sncast completion` command - used to generate autocompletion script ## [0.38.3] - 2025-03-07 ### Forge #### Fixed - Issue with uploading `snforge_std` to scarbs package registry that prevented it from including package reexports required in Scarb >= 2.11.0 ## [0.38.2] - 2025-03-06 ### Forge #### Changed - Fork cache version is pinned to the forge version. #### Fixed - `snforge_scarb_plugin` now emits an error when parameters are passed without using the `#[fuzzer]` attribute - A bug that was causing execution to hang if using forking ## [0.38.0] - 2025-02-25 ### Forge #### Added - `snforge clean` command - used to manage and remove files generated by snforge. It supports cleaning the following components: coverage, profile, cache, trace, all - `snforge new` now adds the `snfoundry_trace`, `coverage`, and `profile` directories to `.gitignore`. - Custom types can be used in fuzz testing by implementing the `Fuzzable` trait - Support for Cairo 2.10.0 #### Changed - It is now required to include the `#[fuzzer]` attribute for fuzz tests to work - Scarb `2.8.5` is now the minimal recommended version. Using Starknet Foundry with versions below it is no longer officially supported and may not work. #### Deprecated - `snforge clean-cache` command ### Cast #### Changed - `--name` flag is now optional when using `account create` (default name is generated) ## [0.37.0] - 2025-02-03 ### Forge #### Added - Rust is no longer required to use `snforge` if using Scarb >= 2.10.0 on supported platforms - precompiled `snforge_scarb_plugin` plugin binaries are now published to [package registry](https://scarbs.xyz) for new versions. - Added a suggestion for using the `--max-n-steps` flag when the Cairo VM returns the error: `Could not reach the end of the program. RunResources has no remaining steps`. #### Fixed - coverage validation now supports comments in `Scarb.toml` ### Cast #### Added - Default RPC providers under `--network` flag #### Changed - Renamed `--network` flag to `--network-name` in `sncast account delete` command #### Fixed - Bug resulting in value passed for `max_fee` being ignored in cast scripts when using `deploy` with STRK token #### Removed - `--fee-token` and `--version` flags, subsequently support for transaction versions other than v3 - Support for ETH transactions in scripts ## [0.36.0] - 2025-01-15 ### Forge #### Changed - Trace files saved in `snfoundry_trace` directory will now use `_` as separators instead of `::` ### Cast #### Added - When using `--max-fee` with transactions v3, calculated max gas and max gas unit price are automatically validated to ensure they are greater than 0 after conversion - interactive interface that allows setting created or imported account as the default #### Changed - Values passed to the `--max-fee`, `--max-gas`, and `--max-gas-unit-price` flags must be greater than 0 #### Deprecated - `--version` flag ## [0.35.1] - 2024-12-16 ### Forge #### Fixed - Minimal Rust version in requirements check is the same as in docs (`1.80.1`) - `snforge` produces trace for contracts even if they fail or panic (assuming test passed) ## [0.35.0] - 2024-12-13 ### Forge #### Added - Requirements validation during `snforge` runtime - `snforge check-requirements` command - `snforge new` command #### Changed - `snforge_scarb_plugin` will now also emit warnings when errors occur - `snforge_std` migrated to `2024_07` edition - `snforge_std` from scarbs package registry is now used in `snforge new` template #### Deprecated - `snforge init` command ### Cast #### Added - `account create` command shows prepared deployment command #### Changed - `--version` flag is now optional and `v3` will be used by default - Displaying underlying RPC error instead of "Unknown RPC error" in edge cases #### Deprecated - `--fee-token` flag - `strk` will be used by default ## [0.34.0] - 2024-11-26 ### Forge #### Added - `generate_random_felt()` for generating (pseudo) random felt value. - Printing information about compiling Sierra using `universal-sierra-compiler` - Displaying backtrace when contract call fails #### Changed - Tests config run is now executed in parallel resulting in faster `snforge test` setup in some cases ### Cast #### Added - You can skip `--name` flag when using `account import` - a default name will be generated. - Addresses outputted when calling `sncast account create`, `sncast deploy` and `sncast declare` are now padded to 64 characters length and prefixed with `0x0` - Globally available configuration to store profiles to share between projects. #### Changed - Changed return type of `declare` in Cairo Deployment Scripts so it can handle already declared contracts without failing - Allow using `show-config` command without providing rpc url ## [0.33.0] - 2024-11-04 ### Cast #### Added - You can now use numbers without quotes as inputs for calls in multicall config file. - New `--arguments` flag to `call`, `invoke` and `deploy` for automatic conversion of Cairo expressions instead of serialized form. ### Forge #### Changed - You can now pass arguments to `cairo-profiler` and `cairo-coverage`. Everything after `--` will be passed to underlying binary. E.g. `snforge test --build-profile -- --show-inlined-functions` - You can't use now `--coverage` and `--build-profile` flags at the same time. If you want to use both, you need to run `snforge test` twice with different flags. - Contract artifacts are compiled to CASM concurrently. - Starknet artifacts are now loaded from all tests targets - Cairo Edition in `snforge init` template set to `2024_07` #### Fixed - Scarb features work with optimized compilation - Custom test targets are now supported with optimized compilation - Calling contract functions via safe-dispatcher now returns an `Err` when attempting to invoke a non-existent entry point, instead of causing a panic. ## [0.32.0] - 2024-10-16 ### Cast #### Changed - Short option for `--accounts-file` flag has been removed. - Short option for `--contract-address` is now `-d` instead of `-a`. - `account add` is renamed to `account import`. - `account import` can be now used without specifying `--private-key` or `--private-key-file` flags. Instead private key will be read interactively from the user. #### Fixed - `account delete` command: It is no longer necessary to provide the `--url` argument each time. Either the `--url` or `--network` argument must be provided, but not both, as they are mutually exclusive. ### Forge #### Changed - When using test name filter with `--exact` flag, forge will try to compile only the selected test. ## [0.31.0] - 2024-09-26 ### Cast #### Changed - `declare` and `verify` commands now use the Scarb `release` profile instead of the `dev` profile as the default for building artifacts - StarkScan links now point to specific pages for transactions, contracts and classes. #### Fixed - Explorer links displayed upon committing transactions are now properly formatted - `sncast declare` no longer fails for flat contracts (i.e. CASM artifacts with `bytecode_segment_lengths` being a number) ### Forge #### Added - Project generated by `snforge` contains `assert_macros` dependency with version 0.1.0 for Scarb <= 2.8.0, otherwise equal to Scarb - Support for overriding fork configuration in test attribute with a different block ID, tag, or hash. - `--no-optimization` flag that can be used to build contracts using the [starknet contract target](https://docs.swmansion.com/scarb/docs/extensions/starknet/contract-target.html#starknet-contract-target. This is the default behavior when using Scarb < 2.8.3 #### Changed - For Scarb >= `2.8.3` contract artifacts are built as part of the test target now. This process speeds up the compilation time, but the behavior of the contracts potentially may not be 100% consistent with the real networks. It can be disabled using the [--no-optimization flag](https://foundry-rs.github.io/starknet-foundry/appendix/snforge/test.html#--no-optimization) - `snforge` now validates if your project is setup to generate debug info needed for `cairo-coverage` when running `--coverage` flag ### `snforge_scarb_plugin` #### Fixed - The package is now correctly versioned ## [0.30.0] - 2024-09-04 ### Forge #### Added - Derived `Debug` and `Clone` on `trace.cairo` items - `--coverage` flag to the `test` command. Saves trace data and then generates coverage report of test cases which pass and are not fuzz tests. You need [cairo-coverage](https://github.com/software-mansion/cairo-coverage) installed on your system. #### Fixed - `latest` fork block id tag validation in `Scarb.toml` is now consistent - `RangeCheck96`, `AddMod`, `MulMod` builtins are now properly supported - Fixed escaping `'` in `#[should_panic]`s - Fixed `scarb init` with snforge runner ## [0.29.0] - 2024-08-28 ### Forge #### Added - Support for Scarb features in `snforge test` - flags the same as in Scarb. Read more [here](https://docs.swmansion.com/scarb/docs/reference/conditional-compilation.html#features) #### Fixed - `snforge init` no longer emits warnings - Project template generated by `snforge init` matches new `declare` cheatcode interface and compiles properly ## [0.28.0] - 2024-08-21 ### Forge #### Changed - Bumped Cairo version to `2.7.0` - Max steps in tests (configured by argument `--max-n-steps`) now defaults to 10 million if not provided (changed from 4 million). ### Cast #### Added - Commands that commit transactions now display links to block explorers. When in human-readable mode, `invoke`, `declare`, `deploy`, `multicall run`, `account create` and `account deploy` will display additional information with an url. A new key in Cast configuration - `block-explorer` determines which block explorer service the displayed link leads to. Possible options are:` StarkScan`, `Voyager`, `ViewBlock`, `OkLink`, `NftScan`. #### Changed - `account create` outputs hint about the type of the tokens required to prefund a newly created account with before deployment - `sncast` no longer expects `--url` as a common argument. It is now required specifically by commands that utilise it, i.e. `account add`, `account create`, `account delete`, `account deploy`, `multicall run`, `script run`, `call`, `declare`, `deploy`, `invoke`, `show-config`, `tx-status`. Commands that do not require `--url` anymore: `account list`, `multicall new`, `script init`, `verify` ### Forge #### Changed - Fork tests now discover chain ID via provided RPC URL, defaulting to `SN_SEPOLIA` - `#[fork]` attribute parameters format. [Read more here](https://foundry-rs.github.io/starknet-foundry/snforge-advanced-features/fork-testing.html) - steps counting - Block tag changed name from `Latest` to `latest` - `declare` cheatcode now returns `Result>` [Read more here](https://foundry-rs.github.io/starknet-foundry/appendix/snforge-library/declare.html) ## [0.27.0] - 2024-07-24 ### Forge #### Added - `spy_messages_to_l1()` for listening in on messages to L1 sent by your contracts. [Read more here](https://foundry-rs.github.io/starknet-foundry/testing/testing-messages-to-l1.html). #### Changed - Renamed global cheatcodes listed [here](https://foundry-rs.github.io/starknet-foundry/appendix/cheatcodes.html) - cheatcode invocations affecting the global scope and working indefinitely, already marked with a `_global` suffix, received a `start_` prefix ### Cast #### Added - `verify` subcommand to verify contract (walnut APIs supported as of this version). [Read more here](https://foundry-rs.github.io/starknet-foundry/appendix/sncast/verify.html) - support for v3 transactions on account deploy, deploy, declare, invoke - Newest class hash for OpenZeppelin account contracts - `account list` subcommand for listing all available accounts [Read more here](https://foundry-rs.github.io/starknet-foundry/appendix/sncast/account/list.html) #### Changed - `multicall new` no longer prints generated template to stdout and now requires specifying output path. [Read more here](https://foundry-rs.github.io/starknet-foundry/appendix/sncast/multicall/new.html) ## [0.26.0] - 2024-07-03 ### Forge #### Changed - Updated event testing - read more [here](./docs/src/testing/testing-events.md) on how it now works and [here](./docs/src/appendix/cheatcodes/spy_events.md) about updated `spy_events` cheatcode ## [0.25.0] - 2024-06-12 ### Forge #### Changed - `SyscallResultStringErrorTrait::map_error_to_string` removed in favor of utility function (`snforge_std::byte_array::try_deserialize_bytearray_error`) ### Cast #### Removed - `--class-hash` flag from `account deploy` command #### Added - `tx-status` subcommand to get transaction status. [Read more here](./docs/src/starknet/tx-status.md) - `tx_status` function to cast_std. [Read more here](./docs/src/appendix/sncast-library/tx_status.md) - Support for creating argent accounts - Support for creating braavos accounts ## [0.24.0] - 2024-05-22 ### Forge #### Removed - `prank`, `warp`, `roll`, `elect`, `spoof` cheatcodes in favour of `cheat_execution_info` #### Added - `cheat_execution_info` cheatcode and per variable helpers for it ### Cast #### Added - New required flag `--type` to `account add` command ### Forge #### Changed - `SignerTrait::sign` now returns `Result` instead of failing the test - `L1HandlerTrait::execute()` takes source address and payloads as arguments [Read more here](https://foundry-rs.github.io/starknet-foundry/appendix/cheatcodes/l1_handler.html) - When calling to an address which does not exists, error is forwarded to cairo runtime instead of failing the test ## [0.23.0] - 2024-05-08 ### Forge #### Removed - `event_name_hash` removal, in favour of `selector!` usage #### Changed - the tool now always compiles Sierra contract artifacts to CASM using [`USC`](https://github.com/software-mansion/universal-sierra-compiler) - before it used to consume CASM artifacts produced by Scarb if they were present. Setting up `casm = true` in `Scarb.toml` is no longer recommended - it may slow down the compilation. - The `replace_bytecode` cheatcode now returns `Result` with a possible `ReplaceBytecodeError`, since it may cause unexpected errors down the line when not handled properly ### Cast #### Changed - the tool now always compiles Sierra contract artifacts to CASM using [`USC`](https://github.com/software-mansion/universal-sierra-compiler) - before it used to consume CASM artifacts produced by Scarb if they were present. Setting up `casm = true` in `Scarb.toml` is no longer recommended - it may slow down the compilation. #### Fixed - scripts built with release profile are now properly recognized and ran ## [0.22.0] - 2024-04-17 ### Forge #### Changed - `deploy` / `deploy_at` now additionally return the constructor return data via `SyscallResult<(ContractAddress, Span)>` - `declare` returns `Result>` instead of `ContractClass` - `L1HandlerTrait::execute()` returns `SyscallResult<()>` - `SyscallResultStringErrorTrait::map_string_error` renamed to `SyscallResultStringErrorTrait::map_error_to_string` - `var` now supports `ByteArray` with double quoting, and returns `Array` instead of a single `felt252` #### Removed - `snforge_std::RevertedTransaction` ## [0.21.0] - 2024-04-03 ### Forge #### Changed - `read_txt` and `read_json` now supports `ByteArray` ### Cast #### Added - sncast script idempotency feature - every action done by the script that alters the network state will be tracked in state file, and won't be replayed if previously succeeded ## [0.20.1] - 2024-03-22 ## [0.20.0] - 2024-03-20 ### Forge #### Added - variants of cheatcodes with `CheatSpan` (read more [here](https://foundry-rs.github.io/starknet-foundry/testing/using-cheatcodes#setting-cheatcode-span)) - Providing configuration data with env variables [DOCS](https://foundry-rs.github.io/starknet-foundry/projects/configuration.html#environmental-variables) #### Fixed - Events emitted in cairo 0 contracts are now properly collected - `--build-profile` no longer fails silently (compatible with [`cairo-profiler`](https://github.com/software-mansion/cairo-profiler) 0.2.0) #### Changed - Default `chain_id` has been changed from `SN_GOERLI` to `SN_SEPOLIA` - Supported RPC version is now 0.7.0 - Gas calculation is in sync with starknet 0.13.1 (with EIP 4844 blob usage enabled) - Resources displayed (steps, builtins) now include OS costs of syscalls ### Cast #### Added - Support for OpenZeppelin Cairo 1 (or higher) accounts creation, deployment and usage - Providing configuration data with env variables [DOCS](https://foundry-rs.github.io/starknet-foundry/projects/configuration.html#environmental-variables) #### Changed - Supported RPC version is now 0.7.0 - Default class hash in `account create` and `account deploy` has been changed to [cairo2 class hash](https://starkscan.co/class/0x04c6d6cf894f8bc96bb9c525e6853e5483177841f7388f74a46cfda6f028c755) ## [0.19.0] - 2024-03-06 ### Forge ⚠️ This version requires installing external [universal-sierra-compiler (v2.0.0)](https://github.com/software-mansion/universal-sierra-compiler) ⚠️ #### Added - [`replace_bytecode`](https://foundry-rs.github.io/starknet-foundry/appendix/cheatcodes/replace_bytecode.html) cheatcode - result of the call to the trace - added `--build-profile` flag to the `--test` command. Saves trace data and then builds profiles of test cases which pass and are not fuzz tests. You need [cairo-profiler](https://github.com/software-mansion/cairo-profiler) installed on your system. - dependency on the [universal-sierra-compiler](https://github.com/software-mansion/universal-sierra-compiler) binary, which will allow forge to be independent of sierra version #### Changed - `var()`, `read_txt()`, `read_json()`, `FileTrait::new()`, `declare()` now use regular strings (`ByteArray`) instead of short strings (`felt252`) - `start_mock_call()`, `stop_mock_call()`, `L1Handler` now use selector (`selector!()`) instead of names ### Cast #### Changed - `declare()` now uses regular strings (`ByteArray`) instead of short strings (`felt252`) - `call()` and `invoke()` now require function selector (`selector!()`) instead of function name in scripts (sncast_std) #### Removed - `--path-to-scarb-toml` optional flag that allowed to specify the path to the `Scarb.toml` file - `--deployed` flag from `account add` subcommand ## [0.18.0] - 2024-02-21 ### Forge #### Added - contract names to call trace - `--max-n-steps` argument that allows setting own steps limit #### Changed - Unknown entry point error when calling a contract counts as a panic - Cairo edition set to `2023_11` #### Fixed - Calling Cairo 0 contract no longer cancels cheatcodes in further calls ### Cast #### Added - `script init` command to generate a template file structure for deployment scripts - Warning is emitted when executing sncast commands if the node's JSON-RPC version is incompatible #### Changed - to run a deployment script it is required to use `script run` subcommand ## [0.17.1] - 2024-02-12 ### Cast #### Changed - fixed a bug where a profile was passed to scarb even when it did not exist - error handling from inside deployment scripts is now possible (`declare`, `deploy`, `call`, `invoke` now return `Result`) ### Forge #### Added - `map_string_error` for use with dispatchers, which automatically converts string errors from the syscall result (read more [here](https://foundry-rs.github.io/starknet-foundry/testing/contracts#handling-errors)) ## [0.17.0] - 2024-02-07 ### Forge #### Added - Warning in fork testing is emitted, when node JSON-RPC version is incompatible - `get_call_trace` library function for retrieving call trace in tests #### Changed - Gas estimation is now aligned with the Starknet v0.13 #### Removed - `snforge_std::PrintTrait` - use `print!`, `println!` macros and / or `core::debug::PrintTrait` instead #### Fixed - Gas used in constructors and handling of L1 messages is now properly included in total gas cost ### Cast #### Changed - sncast tool configuration is now moved away from `Scarb.toml` to `snfoundry.toml` file. This file must be present in current or any parent directories in order to use profiles. #### Added - `--package` flag for `declare` and `script` subcommands, that specifies scarb package to work with - `Debug` and `Display` impls for script subcommand responses - use `print!`, `println!` macros instead of calling `.print()` ## [0.16.0] - 2024-01-26 ### Forge #### Added - Bump to cairo 2.5.0 #### Changed - `SafeDispatcher`s usages need to be tagged with `#[feature("safe_dispatcher)]` (directly before usage), see [the shamans post](https://community.starknet.io/t/cairo-v2-5-0-is-out/112807#safe-dispatchers-15) ## [0.15.0] - 2024-01-24 ### Forge #### Added - `--detailed-resources` flag for displaying additional info about used resources - `store` and `load` cheatcodes - `--save-trace-data` flag to `snforge test` command. Traces can be used for profiling purposes. #### Changed - `available_gas` attribute is now supported (Scarb >= 2.4.4 is required) #### Fixed - Error message for tests that should panic but pass ### Cast #### Changed - the 'pending' block is used instead of 'latest' as the default when obtaining the nonce ## [0.14.0] - 2024-01-11 ### Forge #### Added - `Secp256k1` and `Secp256r1` curves support for `KeyPair` in `snforge_std` #### Changed - maximum number of computational steps per call set to current Starknet limit (3M) - `mean` and `std deviation` fields are displayed for gas usage while running fuzzing tests - Cairo edition in `snforge_std` and `sncast_std` set to `2023_10` - `snforge_std::signature` module with `stark_curve`, `secp256k1_curve` and `secp256r1_curve` submodules #### Fixed - Safe library dispatchers in test code no longer propagate errors when not intended to ## [0.13.1] - 2023-12-20 ### Forge #### Added - `assert_not_emitted` assert to check if an event was not emitted #### Changed - fields from `starknet::info::v2::TxInfo` are now part of `TxInfoMock` from `snforge_std::cheatcodes::tx_info` - consistent latest block numbers for each url are now used across the whole run when testing against forks #### Fixed - Parsing panic data from call contract result ### Cast #### Added - add support for sepolia network - `--yes` option to `account delete` command that allows to skip confirmation prompt #### Changed - Argument `max-fee` in `account deploy` is now optional ## [0.13.0] - 2023-12-14 ### Forge #### Changed - Bump cairo to 2.4.0. - Migrated test compilation and collection to Scarb, snforge should now be compatible with every Scarb version >= 2.4.0 unless breaking changes happen ## [0.12.0] - 2023-12-06 ### Forge #### Added - print gas usage for each test - Support for test collector built-in in Scarb with the `--use-scarb-collector` flag. Requires at least `nightly-2023-12-04` version of Scarb. ### Cast #### Added - `--wait-timeout` to set timeout for waiting for tx on network using `--wait` flag (default 60s) - `--wait-retry-interval` to adjust the time between consecutive attempts to fetch tx from network using `--wait` flag (default 5s) - allow setting nonce in declare, deploy and invoke (using `--nonce` and in deployment scripts) - add `get_nonce` function to cast_std - `--private-key-file` option to `account add` command that allows to provide a path to the file holding account private key ## [0.11.0] - 2023-11-22 ### Forge #### Added - `elect` cheatcode for mocking the sequencer address. Read more [here](./docs/src/appendix/cheatcodes/sequencer_address/start_elect.md). - `--rerun-failed` option to run tests that failed during the last run. #### Changed - `start_warp` and `stop_warp` now take `CheatTarget` as the first argument instead of `ContractAddress`. Read more [here](./docs/src/appendix/cheatcodes/block_timestamp/start_warp.md). - `start_prank` and `stop_prank` now take `CheatTarget` as the first argument instead of `ContractAddress`. Read more [here](./docs/src/appendix/cheatcodes/caller_address/start_prank.md). - `start_roll` and `stop_roll` now take `CheatTarget` as the first argument instead of `ContractAddress`. Read more [here](./docs/src/appendix/cheatcodes/block_number/start_roll.md). PS: Credits to @bllu404 for the help with the new interfaces for cheats! #### Fixed - using unsupported `available_gas` attribute now fails the specific test case instead of the whole runner ### Cast #### Added - MVP for cairo deployment scripts with declare, deploy, invoke and call ## [0.10.2] - 2023-11-13 ### Forge #### Changed - Bump cairo to 2.3.1 #### Removed - `available_gas` attribute, it didn't compute correctly gas usage. Contract functions execution cost would not be included. ## [0.10.1] - 2023-11-09 ### Cast #### Fixed - scarb metadata in declare subcommand now takes manifest path from cli if passed instead of looking for it ## [0.10.0] - 2023-11-08 ### Forge #### Removed - forking of the `Pending` block #### Added - `--color` option to control when colored output is used - when specifying `BlockId::Tag(Latest)` block number of the used block will be printed - printing number of ignored and filtered out tests #### Fixed - Segment Arena Builtin crashing with `CairoResourcesNotContainedInFeeCosts` when Felt252Dict was used ### Cast #### Fixed - account commands now always return valid json when `--json` flag is passed - allow passing multiple calldata argument items without quotes - display correct error message when account file is invalid ## [0.9.1] - 2023-10-30 ### Forge #### Fixed - diagnostic paths referring to `tests` folder - caching `get_class_hash_at` in forking test mode (credits to @jainkunal for catching the bug) ## [0.9.0] - 2023-10-25 ### Forge #### Added - `#[ignore]` attribute together with `--ignored` and `include-ignored` flags - read more [here](https://foundry-rs.github.io/starknet-foundry/testing/testing.html#ignoring-some-tests-unless-specifically-requested) - support for `deploy_syscall` directly in the test code (alternative to `deploy`) - `snforge_std::signature` module for performing ecdsa signatures #### Changed - updated Cairo version to 2.3.0 - compatible Scarb version is 2.3.0: - tests in `src` folder now have to be in a module annotated with `#[cfg(test)]` - `snforge_std::PrintTrait` will not convert values representing ASCII control characters to strings - separated `snforge` to subcommands: `snforge test`, `snforge init` and `snforge clean-cache`. Read more [here](https://foundry-rs.github.io/starknet-foundry/appendix/snforge.html). - `starknet::get_block_info` now returns correct block info in a forked block ### Cast #### Added - `show-config` subcommand to display currently used configuration - `account delete` command for removing accounts from the accounts file - `--hex-format` flag has been added #### Removed - `-i` short for `--int-format` is removed, now have to use the full form `--int-format` ## [0.8.3] - 2023-10-17 ### Forge #### Changed - Test from different crates are no longer run in parallel - Test outputs are printed in non-deterministic order #### Fixed - Test output are printed in real time again - Bug when application would not wait for tasks to terminate after execution was cancelled ## [0.8.2] - 2023-10-12 ### Forge #### Fixed - incorrect caller address bug ## [0.8.1] - 2023-10-12 ### Forge #### Fixed - significantly reduced ram usage ## [0.8.0] - 2023-10-11 ### Forge #### Added - `#[fuzzer(...)]` attribute allowing to specify a fuzzer configuration for a single test case - Support for `u8`, `u16`, `u32`, `u64`, `u128`, `u256` types to fuzzer - `--clean-cache` flag - Changed interface of `L1Handler.execute` and `L1Handler` (dropped `fee` parameter, added result handling with `RevertedTransaction`) - Contract now has associated state, more about it [here](https://foundry-rs.github.io/starknet-foundry/testing/testing_contract_internals.html) - cheatcodes (`prank`, `roll`, `warp`) now work on forked Cairo 0 contracts #### Changed - Spying events interface is updated to enable the use of events defined inside contracts in assertions - Test are executed in parallel - Fixed inconsistent pointers bug https://github.com/foundry-rs/starknet-foundry/issues/659 - Fixed an issue where `deploy_at` would not trigger the constructors https://github.com/foundry-rs/starknet-foundry/issues/805 ### Cast #### Changed - dropped official support for cairo 1 compiled contracts. While they still should be working without any problems, from now on the only officially supported cairo compiler version is 2 ## [0.7.1] - 2023-09-27 ### Forge #### Added - `var` library function for reading environmental variables #### Fixed - Using any concrete `block_id` when using forking mode, would lead to crashes ## [0.7.0] - 2023-09-27 ### Forge #### Added - Support for scarb workspaces - Initial version of fuzz testing with randomly generated values - `#[fork(...)]` attribute allowing testing against a network fork #### Changed - Tests are collected only from a package tree (`src/lib.cairo` as an entrypoint) and `tests` folder: - If there is a `lib.cairo` file in `tests` folder, then it is treated as an entrypoint to the `tests` package from which tests are collected - Otherwise, all test files matching `tests/*.cairo` regex are treated as modules and added to a single virtual `lib.cairo`, which is treated as described above ### Cast #### Added - `account add` command for importing accounts to the accounts file - `account create` command for creating openzeppelin accounts with starkli-style keystore - `account deploy` command for deploying openzeppelin accounts with starkli-style keystore ### Changed - `--add-profile` no longer accepts `-a` for short - allow the `id` property in multicalls to be referenced in the inputs of `deploy` and `invoke` calls ## [0.6.0] - 2023-09-13 ### Forge #### Added - `deploy_at` cheatcode - printing failures summary at the end of an execution - filtering tests now uses an absolute module tree path — it is possible to filter tests by module names, etc. #### Fixed - non-zero exit code is returned when any tests fail - mock_call works with dispatchers if contract does not exists ### Cast #### Added - support for starkli-style accounts, allowing the use of existing accounts #### Changed - fixed misleading error message when there was no scarb in PATH and `--path-to-scarb-toml` was passed - modified `multicall new` command output, to be in line with other commands outputs ## [0.5.0] - 2023-08-30 ### Forge #### Added - support for `keccak_syscall` syscall. It can be used directly in cairo tests - `l1_handler_execute` cheatcode - support for `roll`ing/`warp`ing/`prank`ing the constructor logic (precalculate address, prank, assert pranked state in constructor) - `spy_events` cheatcode - Functions `read_json` and `FileParser::parse_json` to load data from json files and deserialize it #### Changed - rename `TxtParser` trait to `FileParser` - rename `parse_txt` trait to `read_txt` - support for printing in contracts - `spoof` cheatcode - snforge command-line flag `--init` ### Cast #### Added - Support for custom networks - accounts created on custom networks are saved in `accounts-file` under network's chain_id - `accounts-file` field in Scarb.toml profile - Include the class hash of an account contract in the `accounts-file` #### Removed - `--network` option together with the `network` field in Scarb.toml profile — previously used as a validation factor; now networks are identified by their chain_id ## [0.4.0] - 2023-08-17 ### Forge #### Added - `#[should_panic]` attribute support - Documentation to public methods - Information sections to documentation about importing `snforge_std` - Print support for basic numeric data types - Functions `parse_txt` and `TxtParser::deserialize_txt` to load data from plain text files and serialize it - `get_class_hash` cheatcode - `mock_call` cheatcode - `precalculate_address` cheatcode #### Changed - Exported `snforge_std` as a Scarb package, now you have to import it explicitly with e.g. `use snforge_std::declare` and add it as a dependency to your Scarb.toml ```toml [dependencies] # ... snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "v0.4.0" } ``` - Moved `ForgeConfigFromScarb` to `scarb.rs` and renamed to `ForgeConfig` - Made private: - `print_collected_tests_count` - `print_running_tests` - `print_test_result` - `print_test_summary` - `TestCaseSummary::from_run_result` - `TestCaseSummary::skipped` - `extract_result_data` - `StarknetArtifacts` - `StarknetContractArtifactPaths` - `StarknetContract` - Split `dependencies_for_package` into separate methods: - `paths_for_package` - `corelib_for_package` - `target_name_for_package` - `compilation_unit_for_package` - Fails test when user tries to use syscalls not supported by forge test runner - Updated cairo-lang to 2.1.0, starknet-api to 0.4.1 and blockifier to 0.2.0-rc0 ### Cast #### Added - Added `--class-hash` flag to account create/deploy, allowing for custom openzeppelin account contract class hash ## [0.3.0] - 2023-08-02 ### Forge #### Added - `warp` cheatcode - `roll` cheatcode - `prank` cheatcode - Most unsafe libfuncs can now be used in contracts #### Changed - `declare` return type to `starknet::ClassHash`, doesn't return a `Result` - `PreparedContract` `class_hash` changed to `starknet::ClassHash` - `deploy` return type to `starknet::ContractAddress` #### Fixed - Using the same cairo file names as corelib files no longer fails test execution ### Cast #### Added - multicall as a single transaction - account creation and deployment - `--wait` flag to wait for transaction to be accepted/rejected #### Changed - sierra and casm artifacts are now required in Scarb.toml for contract declaration - improved error messages ## [0.1.1] - 2023-07-26 ### Forge & Cast #### Fixed - `class_hash`es calculation - Test collection ## [0.1.0] - 2023-07-19 ### Forge & Cast #### Added - Initial release ================================================ FILE: CONTRIBUTING.md ================================================ # Contribution Guideline Starknet Foundry is under active development and is open for contributions! ## Opening an issue If you think something doesn't work or something is missing please open an issue! This way we can address this problem and make Starknet Foundry better! Before opening an issue, it is always a good idea to search existing [issues](https://github.com/foundry-rs/starknet-foundry/issues) and verify if a similar one doesn't already exist. ## Contributing ### Environment setup See [development guide](https://foundry-rs.github.io/starknet-foundry/development/environment-setup.html) in Starknet Foundry book for environment setup. ### Selecting an issue If you are a first time contributor pick up any issue labeled as `good-first-issue`. Write a comment that you would like to work on it and we will assign it to you. Need some guidance? Reach out to other developers on [Telegram](https://t.me/+d8ULaPxeRqlhMDNk). If you are a more experienced Starknet Foundry contributor you can pick any issue labeled as `help-wanted`. Make sure to discuss the details with the core team beforehand. ### Writing Tests Please make sure the feature you are implementing is thoroughly tested with automatic tests. You can check existing tests in the repository to see the recommended approach to testing. #### Snapshot tests Some tests use [`insta`](https://crates.io/crates/insta) snapshots files (`.snap`) to store expected test output, specifically for testing some of Starknet Foundry features with older Scarb versions. When adding such test case, please add a `snap_` prefix to its name to ensure it's tested on CI for all supported Scarb versions. To make sure snapshot tests pass for all currently supported Scarb versions, run: ```sh ./scripts/check_snapshots.sh ``` If some of the snapshot tests fail, run: ```sh ./scripts/check_snapshots.sh --fix ``` and review the newly generated snapshots. ### Pull Request Size Try to make your pull request self-contained, only introducing the necessary changes. If your feature is complicated, consider splitting the changes into meaningful parts and introducing them as separate pull requests. Creating very large pull requests may significantly increase review time or even prevent them from being merged. ### Contributions Related to Spelling and Grammar At this time, we will not be accepting contributions that only fix spelling or grammar errors in documentation, code or elsewhere. ### `sncast` Guidelines #### Command Outputs Please follow these rules when creating outputs for `sncast`: - Use an imperative tone - Keep your message concise and to the point - When displaying config, use `key: value` format - If the executed command has a natural successor-command, display it as hint in the output. For example, the output of `declare` command should include a hint to use `deploy` command next. ================================================ FILE: Cargo.toml ================================================ [workspace] resolver = "2" members = [ "crates/shared", "crates/forge", "crates/forge-runner", "crates/sncast", "crates/cheatnet", "crates/conversions", "crates/conversions/cairo-serde-macros", "crates/data-transformer", "crates/runtime", "crates/scarb-api", "crates/configuration", "crates/universal-sierra-compiler-api", "crates/docs", "crates/debugging", "crates/testing/packages_validation", "crates/foundry-ui", "crates/native-api", ] exclude = ["crates/snforge-scarb-plugin"] [profile.release] codegen-units = 1 lto = "thin" [profile.ci] inherits = "dev" incremental = false [workspace.package] version = "0.59.0" edition = "2024" repository = "https://github.com/foundry-rs/starknet-foundry" license = "MIT" license-file = "LICENSE" [workspace.dependencies] blockifier = { version = "0.18.0-rc.1", features = ["testing", "tracing", "node_api"] } starknet_api = "0.18.0-rc.1" bigdecimal = "0.4.10" cairo-debugger = { git = "https://github.com/software-mansion-labs/cairo-debugger", rev = "90fa679cd7572fedc5e17dadf15ec44a97b9ce11" } cairo-native = "0.9.0-rc.0" cairo-lang-casm = { version = "=2.17.0", features = ["serde"] } cairo-lang-sierra = "=2.17.0" cairo-lang-utils = "=2.17.0" cairo-lang-starknet = "=2.17.0" cairo-lang-filesystem = "=2.17.0" cairo-lang-diagnostics = "=2.17.0" cairo-lang-syntax = "=2.17.0" cairo-lang-test-plugin = "=2.17.0" cairo-lang-starknet-classes = "=2.17.0" cairo-lang-parser = "=2.17.0" cairo-lang-sierra-to-casm = "=2.17.0" cairo-vm = { version = "=3.2.0", features = ["test_utils"] } cairo-annotations = { version = "0.8.0", features = ["cairo-lang"] } dirs = "6.0.0" dialoguer = "0.12.0" starknet-types-core = { version = "0.2.4", features = ["hash", "prime-bigint"] } anyhow = "1.0.100" assert_fs = "1.1.2" camino = { version = "1.2.1", features = ["serde1"] } clap = { version = "4.5.57", features = ["derive", "deprecated"] } clap_complete = "4.5.65" console = "0.16.1" include_dir = "0.7.4" indoc = "2" itertools = "0.14.0" indexmap = "2.11.4" num-traits = "0.2.19" rayon = "1.10" regex = "1.11.3" serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.149" starknet-rust = "0.19.0-rc.2" starknet-rust-crypto = "0.9.0" tempfile = "3.24.0" thiserror = "2.0.17" ctor = "0.6.2" url = { "version" = "2.5.4", "features" = ["serde"] } tokio = { version = "1.50.0", features = ["full"] } futures = "0.3.31" num-bigint = { version = "0.4.6", features = ["rand"] } walkdir = "2.5.0" rand = "0.8.5" project-root = "0.2.2" which = "8.0.2" conversions = { path = "./crates/conversions" } shared = { path = "./crates/shared" } docs = { path = "./crates/docs" } test-case = "3.3.1" scarb-metadata = "1.14.0" flatten-serde-json = "0.1.0" snapbox = "1.0.1" scarb-ui = "0.1.7" semver = "1.0.27" bimap = "0.6.3" primitive-types = { version = "0.14.0", features = ["serde"] } shellexpand = "3.1.0" toml = "1.0.6" rpassword = "7.3.1" promptly = "0.3.1" ptree = "0.5.2" reqwest = { version = "0.13.1", features = ["json", "rustls"] } fs_extra = "1.3.0" openssl = { version = "0.10", features = ["vendored"] } toml_edit = "0.25.4" axum = "0.8.8" fs2 = "0.4.3" flate2 = "1.1.0" k256 = { version = "0.13.4", features = ["sha256", "ecdsa", "serde"] } p256 = { version = "0.13.2", features = ["sha256", "ecdsa", "serde"] } glob = "0.3.2" sha3 = "0.10.8" sha2 = "0.10.8" base16ct = { version = "1.0.0", features = ["alloc"] } async-trait = "0.1.87" serde_path_to_error = "0.1.20" wiremock = "0.6.3" coins-ledger = "0.13.0" speculos-client = "0.1.2" const-hex = "1.18.1" indicatif = "0.18.3" shell-words = "1.1.0" sanitize-filename = "0.6.0" derive_more = { version = "2.1.1", features = ["display"] } paste = "1.0.15" strum = "0.28" strum_macros = "0.28" scarb-oracle-hint-service = "0.1.1" chrono = "0.4.42" tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } tracing = "0.1.43" tracing-chrome = "0.7.2" tracing-log = "0.2.0" comfy-table = "7" create-output-dir = "1.0.0" insta = { version = "1.46.0", features = ["filters"] } plotters = { version = "0.3", default-features = false, features = ["all_series", "all_elements", "svg_backend", "bitmap_backend", "bitmap_encoder", "full_palette", "colormaps", "ab_glyph"] } mimalloc = "0.1" ================================================ FILE: Cross.toml ================================================ # TODO(#3790) Setup cross for native #[build] #pre-build = [ # "dpkg --add-architecture $CROSS_DEB_ARCH", # "apt-get update", # "apt-get install -y --no-install-recommends ca-certificates gnupg wget software-properties-common lsb-release", # "apt-get install -y --no-install-recommends zlib1g-dev libzstd-dev zlib1g-dev:$CROSS_DEB_ARCH libzstd-dev:$CROSS_DEB_ARCH", # "echo \"deb http://apt.llvm.org/focal/ llvm-toolchain-focal-19 main\" > /etc/apt/sources.list.d/llvm-toolchain-focal.list", # "echo \"deb-src http://apt.llvm.org/focal/ llvm-toolchain-focal-19 main\" >> /etc/apt/sources.list.d/llvm-toolchain-focal.list", # "wget -qO - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -", # "apt-get update", # "apt-get install -y --no-install-recommends llvm-19 llvm-19-dev llvm-19-runtime clang-19 clang-tools-19 lld-19 libpolly-19-dev libmlir-19-dev mlir-19-tools", #] # #[target.x86_64-unknown-linux-gnu.env] #passthrough = [ # "MLIR_SYS_190_PREFIX=/usr/lib/llvm-19", # "LLVM_SYS_191_PREFIX=/usr/lib/llvm-19", # "TABLEGEN_190_PREFIX=/usr/lib/llvm-19", #] ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2023 Software Mansion Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ logo ## Starknet Foundry [![Telegram Chat][tg-badge]][tg-url] [![Telegram Support][tg-support-badge]][tg-support-url] [tg-badge]: https://img.shields.io/endpoint?color=neon&logo=telegram&label=chat&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fstarknet_foundry [tg-url]: https://t.me/starknet_foundry [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%2Fstarknet_foundry_support [tg-support-url]: https://t.me/starknet_foundry_support Blazingly fast toolkit for developing Starknet contracts designed & developed by ex [Protostar](https://github.com/software-mansion/protostar) team from [Software Mansion](https://swmansion.com) based on native [Cairo](https://github.com/starkware-libs/cairo) test runner and [Blockifier](https://github.com/starkware-libs/sequencer/tree/main/crates/blockifier), written in Rust 🦀. Need help getting started with Starknet Foundry? Read the 📖 [Starknet Foundry Book](https://foundry-rs.github.io/starknet-foundry/)! ![Example run](.github/images/demo.gif) Starknet Foundry, like its [Ethereum counterpart](https://github.com/foundry-rs/foundry), consists of different modules - [`snforge`](https://github.com/foundry-rs/starknet-foundry/tree/master/crates/forge): Starknet testing framework (like Truffle, Hardhat and DappTools but for Starknet). - [`sncast`](https://github.com/foundry-rs/starknet-foundry/tree/master/crates/sncast): All-in-one tool for interacting with Starknet smart contracts, sending transactions and getting chain data. ## Installation [Follow the installation manual](https://foundry-rs.github.io/starknet-foundry/getting-started/installation.html) ## Roadmap Starknet Foundry is under active development! Expect a lot of new features to appear soon! 🔥 - [x] Running tests written in Cairo - [x] Contract interactions testing - [x] Interacting with Starknet from command line - [x] Multicall support - [x] Cheatcodes - [x] Starknet state forking - [x] Fuzz testing - [x] Parallel tests execution - [x] Performance improvements - [x] Deployment scripts written in Cairo - [ ] Transactions profiling 🏗️ - [ ] Debugging utilities 🏗️ - [ ] Test coverage reports (check out [cairo-coverage](https://github.com/software-mansion/cairo-coverage)) 🏗️ - [ ] L1 ↔ L2 messaging and cross-chain testing See the [whole roadmap](./ROADMAP.md). ## Getting Help You haven't found your answer to your question in the [Starknet Foundry Book](https://foundry-rs.github.io/starknet-foundry/)? - Join the [Telegram](https://t.me/starknet_foundry_support) group to get help - Open a [GitHub discussion](https://github.com/foundry-rs/starknet-foundry/discussions) with your question - Join the [Starknet Discord](https://discord.com/invite/starknet-community) Found a bug? Open an [issue](https://github.com/foundry-rs/starknet-foundry/issues). ## Contributions Starknet Foundry is under active development, and we appreciate any help from the community! Want to contribute? Read the [contribution guidelines](./CONTRIBUTING.md). Check out [development guide](https://foundry-rs.github.io/starknet-foundry/development/environment-setup.html) for local environment setup guide. ================================================ FILE: RELEASING.md ================================================ # Instruction For Creating New Starknet Foundry Releases 1. Create a new branch. 2. Run `./scripts/release.sh MAJOR.MINOR.PATCH`. 3. Merge introduced changes to master branch. 4. Wait for release workflows to pass. A new release will be created on GitHub. ## Manually Creating a Release In case a manual creation of release is necessary, for example when cherry-picking changes, a release can also be triggered by creating a tag with the name format `vMAJOR.MINOR.PATCH`. Note that in this case `CHANGELOG.md`, `Cargo.toml` and `Cargo.lock` files have to be updated accordingly. # Nightly releases Nightly release must be triggered manually using the Nightly GitHub action. This action builds binaries from specified ref and uploads them to the [starknet-foundry-nightlies](https://github.com/software-mansion-labs/starknet-foundry-nightlies) repository. Additionally, there are `stds` and `plugin` versions uploaded to the [_dev_ registry](https://scarbs.dev/). After a successful release, [Ma'at](https://github.com/software-mansion/maat) is automatically triggered to run experiments in nightly workspace. Results can be found [here](https://docs.swmansion.com/maat/) ### Maintenance Some access tokens require annual renewal due to expiration: - `SNFOUNDRY_NIGHTLIES_CONTENTS_WRITE` - grants permission to create releases in nightlies repository. - `MAAT_CONTENTS_READ_ACTIONS_WRITE` - required to trigger Ma'at run. ================================================ FILE: ROADMAP.md ================================================ # Roadmap Tentative roadmap for Starknet Foundry. This document reflects the state of the roadmap at the time of writing. We strive for our roadmap to reflect user needs and feedback, expect changes to this document. **All feedback and request welcome.** ## Table of Contents * [Roadmap](#roadmap) * [Table of Contents](#table-of-contents) * [Reference](#reference) * [Forge](#forge) * [🏗 Test Partitioning](#-test-partitioning) * [🏗 Asserting Steps in Execution](#-asserting-steps-in-execution) * [Reverting Storage Changes in Execution](#reverting-storage-changes-in-execution) * [🏗 Sierra -> Casm Compilation](#-sierra---casm-compilation) * [Performance Investigation](#performance-investigation) * [Advanced Forking / Forking State Asserting](#advanced-forking--forking-state-asserting) * [Derive Macro for `Fuzzable` Trait](#derive-macro-for-fuzzable-trait) * [Typesafe Contract "declare"](#typesafe-contract-declare) * [Research Variant and Differential Testing, Better Fuzzing Algorithms](#research-variant-and-differential-testing-better-fuzzing-algorithms) * [Cast](#cast) * [New Cast Scripts](#new-cast-scripts) * [Transaction Dry Run](#transaction-dry-run) * [CLI Revamp and Configuration Refactor](#cli-revamp-and-configuration-refactor) * [Better Accounts Support](#better-accounts-support) * [New Multicall Interface](#new-multicall-interface) * [Contract Aliases in `snfoundry.toml`](#contract-aliases-in-snfoundrytoml) ## Reference * Item "size" is in the scale 1 to 5 and reflects its relative complexity compared to other items in the roadmap. * Items marked with 🏗️ are in progress. * Items marked with ✅ are done. ## Forge ### 🏗 Test Partitioning _Size: 3_ https://github.com/foundry-rs/starknet-foundry/issues/3548 Partitioning test suite into smaller test suites, to be run on separate machines in CI. Similar to `cargo nextest`. ### 🏗 Asserting Steps in Execution _Size: 2_ https://github.com/foundry-rs/starknet-foundry/issues/2671 Feature for asserting the number of steps used in test execution. ### Reverting Storage Changes in Execution _Size: 3_ https://github.com/foundry-rs/starknet-foundry/issues/3837 Change the test execution model to revert storage changes from top-level calls in case of recoverable failure. ### 🏗 Sierra -> Casm Compilation _Size: 3_ https://github.com/foundry-rs/starknet-foundry/issues/3832 Sierra -> Casm performance investigation and optimization (if viable). ### Performance Investigation _Size: 3_ https://github.com/foundry-rs/starknet-foundry/issues/3899 Investigate bottlenecks in standard test execution (workspace & package processing, config run, collecting configs, test execution) using tracing harnesses. Performance report on eventual findings and measurements for future optimizations. ### Advanced Forking / Forking State Asserting _Size: 5_ New test mechanism for detecting regressions in new contract versions (for upgrades on chain). Forking and asserting state changes after executing a test scenario. ### Derive Macro for `Fuzzable` Trait _Size: 2_ https://github.com/foundry-rs/starknet-foundry/issues/2968 Ability to automatically derive `Fuzzable` trait for structs if they contain only `Fuzzable` fields. ### Typesafe Contract "declare" _Size: 4_ https://github.com/foundry-rs/starknet-foundry/issues/1531 Detect and fail on invalid contract names at compilation time. ### Research Variant and Differential Testing, Better Fuzzing Algorithms _Size: 5_ https://github.com/foundry-rs/starknet-foundry/issues/2464 Inspired by features from Ethereum's Foundry, research the viability of adding variant and differential testing and integrating better fuzzing algorithms. ## Cast ### New Cast Scripts _Size: 5_ https://github.com/foundry-rs/starknet-foundry/issues/3523 New Cast Scripts with focus on the ease of use, using Scarb plugins, integrated into snforge/scarb tests structure. ### Transaction Dry Run _Size: 1_ https://github.com/foundry-rs/starknet-foundry/issues/2136 Running `sncast` transaction without executing them through the fee estimation endpoint. ### CLI Revamp and Configuration Refactor _Size: 4_ Removing non-common arguments that are used as common (e.g. `-account`). Internal changes to how `sncast` loads and combines configuration. ### Better Accounts Support _Size: 4_ Support for Ledger wallet, keystore support with encryption, account storage rework. ### New Multicall Interface _Size: 3_ https://github.com/foundry-rs/starknet-foundry/issues/3810 Native multicall support for invoking transactions in `sncast invoke` or a better dedicated command. Removal of multicall files. ### Contract Aliases in `snfoundry.toml` _Size: 1_ https://github.com/foundry-rs/starknet-foundry/issues/2240 Aliases for contracts in `snfoundry.toml` that can be used in commands instead of contract addresses. ================================================ FILE: _typos.toml ================================================ [default.extend-words] ba = "ba" [files] extend-exclude = ["docs/theme/head.hbs"] ================================================ FILE: crates/cheatnet/Cargo.toml ================================================ [package] name = "cheatnet" version.workspace = true edition.workspace = true [features] testing = [] cairo-native = ["dep:cairo-native", "blockifier/cairo_native"] [dependencies] anyhow.workspace = true blockifier.workspace = true bimap.workspace = true camino.workspace = true starknet_api.workspace = true starknet-types-core.workspace = true cairo-lang-casm.workspace = true cairo-lang-utils.workspace = true cairo-lang-starknet-classes.workspace = true cairo-vm.workspace = true cairo-annotations.workspace = true regex.workspace = true indoc.workspace = true indexmap.workspace = true starknet-rust.workspace = true thiserror.workspace = true serde_json.workspace = true serde.workspace = true flatten-serde-json.workspace = true num-traits.workspace = true url.workspace = true rayon.workspace = true tokio.workspace = true num-bigint.workspace = true conversions.workspace = true fs2.workspace = true flate2.workspace = true data-transformer = { path = "../data-transformer" } scarb-api = { path = "../scarb-api" } runtime = { path = "../runtime" } universal-sierra-compiler-api = { path = "../universal-sierra-compiler-api" } k256.workspace = true p256.workspace = true shared.workspace = true rand.workspace = true foundry-ui = { path = "../foundry-ui" } scarb-oracle-hint-service.workspace = true cairo-native = { workspace = true, optional = true } [dev-dependencies] ctor.workspace = true indoc.workspace = true rayon.workspace = true glob.workspace = true test-case.workspace = true tempfile.workspace = true ================================================ FILE: crates/cheatnet/src/constants.rs ================================================ use starknet_api::contract_class::{ContractClass, SierraVersion}; use std::collections::HashMap; use std::sync::Arc; use blockifier::execution::entry_point::{CallType, ExecutableCallEntryPoint}; use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; use cairo_lang_starknet_classes::compiler_version::current_sierra_version_id; use conversions::IntoConv; use conversions::string::TryFromHexStr; use indoc::indoc; use runtime::starknet::constants::{ TEST_ADDRESS, TEST_CONTRACT_CLASS_HASH, TEST_ENTRY_POINT_SELECTOR, }; use runtime::starknet::context::ERC20_CONTRACT_ADDRESS; use runtime::starknet::state::DictStateReader; use starknet_api::contract_class::EntryPointType; use starknet_api::core::ClassHash; use starknet_api::{core::ContractAddress, transaction::fields::Calldata}; use starknet_rust::core::utils::get_selector_from_name; // Mocked class hashes, those are not checked anywhere pub const TEST_ERC20_CONTRACT_CLASS_HASH: &str = "0x1010"; fn contract_class_no_entrypoints() -> ContractClass { let raw_contract_class = indoc!( r#"{ "prime": "0x800000000000011000000000000000000000000000000000000000000000001", "compiler_version": "2.4.0", "bytecode": [], "hints": [], "entry_points_by_type": { "EXTERNAL": [], "L1_HANDLER": [], "CONSTRUCTOR": [] } }"#, ); let casm_contract_class: CasmContractClass = serde_json::from_str(raw_contract_class) .expect("`raw_contract_class` should be valid casm contract class"); ContractClass::V1((casm_contract_class, SierraVersion::default())) } #[must_use] pub fn contract_class(raw_casm: &str, sierra_version: SierraVersion) -> ContractClass { let casm_contract_class: CasmContractClass = serde_json::from_str(raw_casm).expect("`raw_casm` should be valid casm contract class"); ContractClass::V1((casm_contract_class, sierra_version)) } // Creates a state with predeployed account and erc20 used to send transactions during tests. // Deployed contracts are cairo 0 contracts // Account does not include validations #[must_use] pub fn build_testing_state() -> DictStateReader { let test_erc20_class_hash = TryFromHexStr::try_from_hex_str(TEST_ERC20_CONTRACT_CLASS_HASH).unwrap(); let test_contract_class_hash = TryFromHexStr::try_from_hex_str(TEST_CONTRACT_CLASS_HASH).unwrap(); let class_hash_to_class = HashMap::from([ // This is dummy put here only to satisfy blockifier // this class is not used and the test contract cannot be called (test_contract_class_hash, contract_class_no_entrypoints()), ]); let test_erc20_address = TryFromHexStr::try_from_hex_str(ERC20_CONTRACT_ADDRESS).unwrap(); let test_address = TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap(); let address_to_class_hash = HashMap::from([ (test_erc20_address, test_erc20_class_hash), (test_address, test_contract_class_hash), ]); DictStateReader { address_to_class_hash, class_hash_to_class, ..Default::default() } } #[must_use] pub fn build_test_entry_point() -> ExecutableCallEntryPoint { let test_selector = get_selector_from_name(TEST_ENTRY_POINT_SELECTOR).unwrap(); let entry_point_selector = test_selector.into_(); ExecutableCallEntryPoint { class_hash: ClassHash(TryFromHexStr::try_from_hex_str(TEST_CONTRACT_CLASS_HASH).unwrap()), code_address: Some(TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap()), entry_point_type: EntryPointType::External, entry_point_selector, calldata: Calldata(Arc::new(vec![])), storage_address: TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap(), caller_address: ContractAddress::default(), call_type: CallType::Call, initial_gas: i64::MAX as u64, } } #[must_use] pub fn get_current_sierra_version() -> SierraVersion { let version_id = current_sierra_version_id(); SierraVersion::new( version_id.major as u64, version_id.minor as u64, version_id.patch as u64, ) } ================================================ FILE: crates/cheatnet/src/data/eth_erc20_casm.json ================================================ {"bytecode":["0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x136","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x106","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xf1","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xbd","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xa1","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x7f","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x63","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4d55","0x482480017fff8000","0x4d54","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x2503a","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x25","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127fd27fff8000","0x48127fe07fff8000","0x48127fea7fff8000","0x1104800180018000","0x23fb","0x20680017fff7ffd","0xd","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x64","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x136","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x106","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xf1","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xbd","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xa1","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x7f","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x63","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4c09","0x482480017fff8000","0x4c08","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x2503a","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x25","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127fd27fff8000","0x48127fe07fff8000","0x48127fea7fff8000","0x1104800180018000","0x2356","0x20680017fff7ffd","0xd","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x64","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x136","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x106","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xf1","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xbd","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xa1","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x7f","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x63","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4abd","0x482480017fff8000","0x4abc","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x2503a","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x25","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127fd27fff8000","0x48127fe07fff8000","0x48127fea7fff8000","0x1104800180018000","0x2163","0x20680017fff7ffd","0xd","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x64","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x136","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x106","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xf1","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xbd","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xa1","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x7f","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x63","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4971","0x482480017fff8000","0x4970","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x2503a","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x25","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127fd27fff8000","0x48127fe07fff8000","0x48127fea7fff8000","0x1104800180018000","0x20be","0x20680017fff7ffd","0xd","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x64","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x95","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x1bf8","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x11","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x48127ffb7fff8000","0x482480017ffb8000","0x492","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x48bd","0x482480017fff8000","0x48bc","0x48127ffb7fff8000","0x480080007ffe8000","0xa0680017fff8000","0x9","0x4824800180007ffd","0x3336","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff57fff","0x10780017fff7fff","0x5c","0x4824800180007ffd","0x3336","0x400080007ff67fff","0x48127fff7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x3fc801c47df4de8d5835f8bfd4d0b8823ba63e5a3f278086901402d680abfc","0x482480017ff38000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffc","0x400280037ffb7ffd","0x480280057ffb8000","0x20680017fff7fff","0x39","0x480280047ffb8000","0x480280067ffb8000","0x482680017ffb8000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x12","0x4824800180007ffc","0x10000000000000000","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480080007ff57fff","0x482480017ffe8000","0xefffffffffffffdeffffffffffffffff","0x480080017ff37fff","0x400080027ff27ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x16","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x482480017ffc8000","0xffffffffffffffff0000000000000000","0x400080017ff77fff","0x40780017fff7fff","0x1","0x400080007fff7ffa","0x482480017ff68000","0x2","0x482480017ffb8000","0x55a","0x48127ff97fff8000","0x480680017fff8000","0x0","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f7265553634202d206e6f6e20753634","0x400080007ffe7fff","0x482480017ff08000","0x3","0x48127ff57fff8000","0x48127ff37fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0xa","0x480280047ffb8000","0x48127ffc7fff8000","0x482480017ffe8000","0x776","0x482680017ffb8000","0x8","0x480280067ffb8000","0x480280077ffb8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017ff38000","0x1","0x48127ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff98000","0x1","0x482680017ffa8000","0x21b6","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0xffffffffffffffffffffffffffffdc06","0x400280007ff87fff","0x10780017fff7fff","0x91","0x4825800180007ffa","0x23fa","0x400280007ff87fff","0x482680017ff88000","0x1","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x1104800180018000","0x2038","0x48127f8e7fff8000","0x20680017fff7ff5","0x7a","0x48127fff7fff8000","0x20680017fff7ff7","0x66","0x48127fff7fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x13","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff77fff8000","0x48127fee7fff8000","0x480a7ff97fff8000","0x482480017ff98000","0x55a","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4808","0x482480017fff8000","0x4807","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x482480017fff8000","0x6630","0xa0680017fff8000","0x8","0x48307ffe80007ffb","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007fe77fff","0x10780017fff7fff","0x2d","0x48307ffe80007ffb","0x400080007fe87fff","0x482480017fe88000","0x1","0x48127ffe7fff8000","0x480a7ff77fff8000","0x480a7ff97fff8000","0x480a7ffb7fff8000","0x48127fe87fff8000","0x48127fe87fff8000","0x48127fe87fff8000","0x48127fe87fff8000","0x48127fe87fff8000","0x48127fe87fff8000","0x1104800180018000","0x20bb","0x20680017fff7ffd","0x10","0x40780017fff7fff","0x1","0x400080007fff7ffe","0x48127ff97fff8000","0x48127ff67fff8000","0x48127ff87fff8000","0x48127ff57fff8000","0x48127ff77fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff97fff8000","0x482480017ff68000","0xc8","0x48127ff87fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff77fff8000","0x482480017fe48000","0x1","0x480a7ff97fff8000","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff77fff8000","0x48127ff07fff8000","0x480a7ff97fff8000","0x482480017ffa8000","0x686","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x480a7ff77fff8000","0x48127ff37fff8000","0x480a7ff97fff8000","0x482480017ffc8000","0x87a","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff77fff8000","0x48127ff77fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff77fff8000","0x482680017ff88000","0x1","0x480a7ff97fff8000","0x482680017ffa8000","0x20ee","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0xffffffffffffffffffffffffffffdba2","0x400280007ff87fff","0x10780017fff7fff","0x91","0x4825800180007ffa","0x245e","0x400280007ff87fff","0x482680017ff88000","0x1","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x1104800180018000","0x1f90","0x48127f8e7fff8000","0x20680017fff7ff5","0x7a","0x48127fff7fff8000","0x20680017fff7ff7","0x66","0x48127fff7fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x13","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff77fff8000","0x48127fee7fff8000","0x480a7ff97fff8000","0x482480017ff98000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4760","0x482480017fff8000","0x475f","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1e1a4","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007fe67fff","0x10780017fff7fff","0x2b","0x48307ffe80007ffa","0x400080007fe77fff","0x482480017fe78000","0x1","0x48127ffe7fff8000","0x480a7ff77fff8000","0x480a7ff97fff8000","0x480a7ffb7fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x1104800180018000","0x20d3","0x20680017fff7ffd","0xe","0x40780017fff7fff","0x1","0x48127ff97fff8000","0x48127ff67fff8000","0x48127ff87fff8000","0x48127ff57fff8000","0x48127ff77fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff97fff8000","0x482480017ff68000","0x64","0x48127ff87fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff77fff8000","0x482480017fe38000","0x1","0x480a7ff97fff8000","0x48127ff37fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff77fff8000","0x48127ff07fff8000","0x480a7ff97fff8000","0x482480017ffa8000","0x6ea","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x480a7ff77fff8000","0x48127ff37fff8000","0x480a7ff97fff8000","0x482480017ffc8000","0x8de","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff77fff8000","0x48127ff77fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff77fff8000","0x482680017ff88000","0x1","0x480a7ff97fff8000","0x482680017ffa8000","0x20ee","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0xffffffffffffffffffffffffffffdba2","0x400280007ff87fff","0x10780017fff7fff","0x91","0x4825800180007ffa","0x245e","0x400280007ff87fff","0x482680017ff88000","0x1","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x1104800180018000","0x1ee8","0x48127f8e7fff8000","0x20680017fff7ff5","0x7a","0x48127fff7fff8000","0x20680017fff7ff7","0x66","0x48127fff7fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x13","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff77fff8000","0x48127fee7fff8000","0x480a7ff97fff8000","0x482480017ff98000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x46b8","0x482480017fff8000","0x46b7","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x5","0x482480017fff8000","0x1ea28","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007fe67fff","0x10780017fff7fff","0x2b","0x48307ffe80007ffa","0x400080007fe77fff","0x482480017fe78000","0x1","0x48127ffe7fff8000","0x480a7ff77fff8000","0x480a7ff97fff8000","0x480a7ffb7fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x1104800180018000","0x21a6","0x20680017fff7ffd","0xe","0x40780017fff7fff","0x1","0x48127ff97fff8000","0x48127ff67fff8000","0x48127ff87fff8000","0x48127ff57fff8000","0x48127ff77fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff97fff8000","0x482480017ff68000","0x64","0x48127ff87fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff77fff8000","0x482480017fe38000","0x1","0x480a7ff97fff8000","0x48127ff37fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff77fff8000","0x48127ff07fff8000","0x480a7ff97fff8000","0x482480017ffa8000","0x6ea","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x480a7ff77fff8000","0x48127ff37fff8000","0x480a7ff97fff8000","0x482480017ffc8000","0x8de","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff77fff8000","0x48127ff77fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff77fff8000","0x482680017ff88000","0x1","0x480a7ff97fff8000","0x482680017ffa8000","0x20ee","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0xffffffffffffffffffffffffffffdba2","0x400280007ff87fff","0x10780017fff7fff","0x91","0x4825800180007ffa","0x245e","0x400280007ff87fff","0x482680017ff88000","0x1","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x1104800180018000","0x1e40","0x48127f8e7fff8000","0x20680017fff7ff5","0x7a","0x48127fff7fff8000","0x20680017fff7ff7","0x66","0x48127fff7fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x13","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff77fff8000","0x48127fee7fff8000","0x480a7ff97fff8000","0x482480017ff98000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4610","0x482480017fff8000","0x460f","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x6","0x482480017fff8000","0x391e8","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007fe67fff","0x10780017fff7fff","0x2b","0x48307ffe80007ffa","0x400080007fe77fff","0x482480017fe78000","0x1","0x48127ffe7fff8000","0x480a7ff77fff8000","0x480a7ff97fff8000","0x480a7ffb7fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x1104800180018000","0x21e1","0x20680017fff7ffd","0xe","0x40780017fff7fff","0x1","0x48127ff97fff8000","0x48127ff67fff8000","0x48127ff87fff8000","0x48127ff57fff8000","0x48127ff77fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff97fff8000","0x482480017ff68000","0x64","0x48127ff87fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff77fff8000","0x482480017fe38000","0x1","0x480a7ff97fff8000","0x48127ff37fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff77fff8000","0x48127ff07fff8000","0x480a7ff97fff8000","0x482480017ffa8000","0x6ea","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x480a7ff77fff8000","0x48127ff37fff8000","0x480a7ff97fff8000","0x482480017ffc8000","0x8de","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff77fff8000","0x48127ff77fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff77fff8000","0x482680017ff88000","0x1","0x480a7ff97fff8000","0x482680017ffa8000","0x20ee","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x112","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0xfd2","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xf6","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x48127ffc7fff8000","0x480280007ffc8000","0x48307ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffd7fff8000","0x482480017ffa8000","0x1","0x48127ffa7fff8000","0x480680017fff8000","0x0","0x480080007ff78000","0x10780017fff7fff","0x9","0x48127ffd7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xc8","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007fee7ffc","0x480080017fed7ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027fec7ffd","0x10780017fff7fff","0xb3","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007fef7ffd","0x480080017fee7ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027fed7ffe","0x482480017fed8000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4537","0x482480017fff8000","0x4536","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x371e","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x70","0x48307ffe80007ffa","0x400080007ff37fff","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x400280007ff87fff","0x400280017ff87fe5","0x480280027ff88000","0x400280037ff87fff","0x400280047ff87fea","0x480280057ff88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080017fec7ffc","0x480080027feb7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080037fe97ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080017fec7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080027fea7ffd","0x400080037fe97ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x482680017ff88000","0x6","0x482480017fe68000","0x4","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffc","0x400280037ffb7ffa","0x480280057ffb8000","0x20680017fff7fff","0x2d","0x480280047ffb8000","0x480280067ffb8000","0x482680017ffb8000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x40780017fff7fff","0x1","0x480680017fff8000","0x1","0x48307ffd80007fff","0x20680017fff7fff","0x7","0x48127ffb7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x6","0x482480017ffb8000","0x64","0x480680017fff8000","0x1","0x400080007ffb7fff","0x48127ff17fff8000","0x48127ff17fff8000","0x48127ffc7fff8000","0x48127ff47fff8000","0x480680017fff8000","0x0","0x48127ff67fff8000","0x482480017ff58000","0x1","0x208b7fff7fff7ffe","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x482480017ffd8000","0x5dc","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017fec8000","0x3","0x482480017ff88000","0x55a","0x10780017fff7fff","0x5","0x48127ff37fff8000","0x482480017ffa8000","0xa78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0xff0","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xa9","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x1874","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x8d","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x48127ffc7fff8000","0x480280007ffc8000","0x48307ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ff57fff8000","0x482480017ff98000","0x55a","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4440","0x482480017fff8000","0x443f","0x48127ffa7fff8000","0x480080007ffe8000","0x480080007fff8000","0x482480017fff8000","0x3142","0xa0680017fff8000","0x8","0x48307ffe80007ffb","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007fee7fff","0x10780017fff7fff","0x52","0x48307ffe80007ffb","0x400080007fef7fff","0x480680017fff8000","0x2e9f66c6eea14532c94ad25405a4fcb32faa4969559c128d837caa0ec50a655","0x400280007ff87fff","0x400280017ff87ff4","0x480280027ff88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080017fe97ffc","0x480080027fe87ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080037fe67ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080017fe97ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080027fe77ffd","0x400080037fe67ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff67fff8000","0x480680017fff8000","0x0","0x482680017ff88000","0x3","0x482480017fe38000","0x4","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffc","0x400280037ffb7ffa","0x480280057ffb8000","0x20680017fff7fff","0x12","0x480280047ffb8000","0x40780017fff7fff","0x1","0x480280067ffb8000","0x400080007ffe7fff","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ffb7fff8000","0x482680017ffb8000","0x7","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x482480017ffd8000","0x12c","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017feb8000","0x1","0x48127ff57fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x74e","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xfa","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x122a","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xca","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xb5","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x435a","0x482480017fff8000","0x4359","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x3782","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x72","0x48307ffe80007ffa","0x400080007ff37fff","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x480680017fff8000","0x3711c9d994faf6055172091cb841fd4831aa743e6f3315163b06a122c841846","0x400280007ff87ffe","0x400280017ff87fff","0x480280027ff88000","0x400280037ff87fff","0x400280047ff87fe9","0x480280057ff88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080017feb7ffc","0x480080027fea7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080037fe87ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080017feb7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080027fe97ffd","0x400080037fe87ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff47fff8000","0x480680017fff8000","0x0","0x482680017ff88000","0x6","0x482480017fe58000","0x4","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffc","0x400280037ffb7ffa","0x480280057ffb8000","0x20680017fff7fff","0x2d","0x480280047ffb8000","0x480280067ffb8000","0x482680017ffb8000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x40780017fff7fff","0x1","0x480680017fff8000","0x1","0x48307ffd80007fff","0x20680017fff7fff","0x7","0x48127ffb7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x6","0x482480017ffb8000","0x64","0x480680017fff8000","0x1","0x400080007ffb7fff","0x48127ff17fff8000","0x48127ff17fff8000","0x48127ffc7fff8000","0x48127ff47fff8000","0x480680017fff8000","0x0","0x48127ff67fff8000","0x482480017ff58000","0x1","0x208b7fff7fff7ffe","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x482480017ffd8000","0x5dc","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x55a","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xfa","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x122a","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xca","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xb5","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x424a","0x482480017fff8000","0x4249","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x3782","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x72","0x48307ffe80007ffa","0x400080007ff37fff","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x480680017fff8000","0x251e864ca2a080f55bce5da2452e8cfcafdbc951a3e7fff5023d558452ec228","0x400280007ff87ffe","0x400280017ff87fff","0x480280027ff88000","0x400280037ff87fff","0x400280047ff87fe9","0x480280057ff88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080017feb7ffc","0x480080027fea7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080037fe87ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080017feb7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080027fe97ffd","0x400080037fe87ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff47fff8000","0x480680017fff8000","0x0","0x482680017ff88000","0x6","0x482480017fe58000","0x4","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffc","0x400280037ffb7ffa","0x480280057ffb8000","0x20680017fff7fff","0x2d","0x480280047ffb8000","0x480280067ffb8000","0x482680017ffb8000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x40780017fff7fff","0x1","0x480680017fff8000","0x1","0x48307ffd80007fff","0x20680017fff7fff","0x7","0x48127ffb7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x6","0x482480017ffb8000","0x64","0x480680017fff8000","0x1","0x400080007ffb7fff","0x48127ff17fff8000","0x48127ff17fff8000","0x48127ffc7fff8000","0x48127ff47fff8000","0x480680017fff8000","0x0","0x48127ff67fff8000","0x482480017ff58000","0x1","0x208b7fff7fff7ffe","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x482480017ffd8000","0x5dc","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x55a","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xdf","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x122a","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xaf","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x9a","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x413a","0x482480017fff8000","0x4139","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x2413a","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x57","0x48307ffe80007ffa","0x400080007ff37fff","0x48127fff7fff8000","0x482480017ff28000","0x1","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffb7fff","0x400280017ffb7ffd","0x480280037ffb8000","0x20680017fff7fff","0x30","0x480280027ffb8000","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffd7fff8000","0x480a7ff87fff8000","0x482680017ffb8000","0x5","0x480680017fff8000","0x3711c9d994faf6055172091cb841fd4831aa743e6f3315163b06a122c841846","0x48127fe17fff8000","0x480680017fff8000","0x7","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127fdb7fff8000","0x480080027ff38000","0x1104800180018000","0x1ff3","0x20680017fff7ffd","0xe","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x482480017ff78000","0x258","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff97fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x14","0x480280027ffb8000","0x1104800180018000","0x40ec","0x482480017fff8000","0x40eb","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x213ea","0x480a7ff87fff8000","0x48127ff47fff8000","0x48307ffd7ff68000","0x482680017ffb8000","0x6","0x480280047ffb8000","0x480280057ffb8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x55a","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xdf","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x122a","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xaf","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x9a","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4045","0x482480017fff8000","0x4044","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x24072","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x57","0x48307ffe80007ffa","0x400080007ff37fff","0x48127fff7fff8000","0x482480017ff28000","0x1","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffb7fff","0x400280017ffb7ffd","0x480280037ffb8000","0x20680017fff7fff","0x30","0x480280027ffb8000","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffd7fff8000","0x480a7ff87fff8000","0x482680017ffb8000","0x5","0x480680017fff8000","0x3711c9d994faf6055172091cb841fd4831aa743e6f3315163b06a122c841846","0x48127fe17fff8000","0x480680017fff8000","0x5","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127fdb7fff8000","0x480080027ff38000","0x1104800180018000","0x2051","0x20680017fff7ffd","0xe","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x482480017ff78000","0x258","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff97fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x14","0x480280027ffb8000","0x1104800180018000","0x3ff7","0x482480017fff8000","0x3ff6","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x21322","0x480a7ff87fff8000","0x48127ff47fff8000","0x48307ffd7ff68000","0x482680017ffb8000","0x6","0x480280047ffb8000","0x480280057ffb8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x55a","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xdf","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x122a","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xaf","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x9a","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3f50","0x482480017fff8000","0x3f4f","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x2413a","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x57","0x48307ffe80007ffa","0x400080007ff37fff","0x48127fff7fff8000","0x482480017ff28000","0x1","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffb7fff","0x400280017ffb7ffd","0x480280037ffb8000","0x20680017fff7fff","0x30","0x480280027ffb8000","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffd7fff8000","0x480a7ff87fff8000","0x482680017ffb8000","0x5","0x480680017fff8000","0x251e864ca2a080f55bce5da2452e8cfcafdbc951a3e7fff5023d558452ec228","0x48127fe17fff8000","0x480680017fff8000","0x3","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127fdb7fff8000","0x480080027ff38000","0x1104800180018000","0x1e09","0x20680017fff7ffd","0xe","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x482480017ff78000","0x258","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff97fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x14","0x480280027ffb8000","0x1104800180018000","0x3f02","0x482480017fff8000","0x3f01","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x213ea","0x480a7ff87fff8000","0x48127ff47fff8000","0x48307ffd7ff68000","0x482680017ffb8000","0x6","0x480280047ffb8000","0x480280057ffb8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x55a","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xdf","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x122a","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xaf","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x9a","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3e5b","0x482480017fff8000","0x3e5a","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x24072","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x57","0x48307ffe80007ffa","0x400080007ff37fff","0x48127fff7fff8000","0x482480017ff28000","0x1","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffb7fff","0x400280017ffb7ffd","0x480280037ffb8000","0x20680017fff7fff","0x30","0x480280027ffb8000","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffd7fff8000","0x480a7ff87fff8000","0x482680017ffb8000","0x5","0x480680017fff8000","0x251e864ca2a080f55bce5da2452e8cfcafdbc951a3e7fff5023d558452ec228","0x48127fe17fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127fdb7fff8000","0x480080027ff38000","0x1104800180018000","0x1e67","0x20680017fff7ffd","0xe","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x482480017ff78000","0x258","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff97fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x14","0x480280027ffb8000","0x1104800180018000","0x3e0d","0x482480017fff8000","0x3e0c","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x21322","0x480a7ff87fff8000","0x48127ff47fff8000","0x48307ffd7ff68000","0x482680017ffb8000","0x6","0x480280047ffb8000","0x480280057ffb8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x55a","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x7c","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x1810","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x60","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x48127ffc7fff8000","0x480280007ffc8000","0x48307ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ff57fff8000","0x482480017ff98000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3d8d","0x482480017fff8000","0x3d8c","0x48127ffa7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1451e","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007fed7fff","0x10780017fff7fff","0x23","0x48307ffe80007ffa","0x400080007fee7fff","0x482480017fee8000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127ff07fff8000","0x1104800180018000","0x1eea","0x20680017fff7ffd","0xd","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x64","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fea8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x7b2","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x68","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x1bf8","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x11","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x48127ffb7fff8000","0x482480017ffb8000","0x492","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3d06","0x482480017fff8000","0x3d05","0x48127ffb7fff8000","0x480080007ffe8000","0xa0680017fff8000","0x9","0x4824800180007ffd","0x2af8","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff57fff","0x10780017fff7fff","0x2f","0x4824800180007ffd","0x2af8","0x400080007ff67fff","0x48127fff7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x341c1bdfd89f69748aa00b5742b03adbffd79b8e80cab5c50d91cd8c2a79be1","0x482480017ff38000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffc","0x400280037ffb7ffd","0x480280057ffb8000","0x20680017fff7fff","0x11","0x480280047ffb8000","0x40780017fff7fff","0x1","0x480280067ffb8000","0x400080007ffe7fff","0x48127ffa7fff8000","0x48127ffc7fff8000","0x482680017ffb8000","0x7","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x480280047ffb8000","0x48127ffc7fff8000","0x482480017ffe8000","0x12c","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017ff38000","0x1","0x48127ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff98000","0x1","0x482680017ffa8000","0x21b6","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x68","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x1bf8","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x11","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x48127ffb7fff8000","0x482480017ffb8000","0x492","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3c89","0x482480017fff8000","0x3c88","0x48127ffb7fff8000","0x480080007ffe8000","0xa0680017fff8000","0x9","0x4824800180007ffd","0x2af8","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff57fff","0x10780017fff7fff","0x2f","0x4824800180007ffd","0x2af8","0x400080007ff67fff","0x48127fff7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0xb6ce5410fca59d078ee9b2a4371a9d684c530d697c64fbef0ae6d5e8f0ac72","0x482480017ff38000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffc","0x400280037ffb7ffd","0x480280057ffb8000","0x20680017fff7fff","0x11","0x480280047ffb8000","0x40780017fff7fff","0x1","0x480280067ffb8000","0x400080007ffe7fff","0x48127ffa7fff8000","0x48127ffc7fff8000","0x482680017ffb8000","0x7","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x480280047ffb8000","0x48127ffc7fff8000","0x482480017ffe8000","0x12c","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017ff38000","0x1","0x48127ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff98000","0x1","0x482680017ffa8000","0x21b6","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x95","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x1bf8","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x11","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x48127ffb7fff8000","0x482480017ffb8000","0x492","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3c0c","0x482480017fff8000","0x3c0b","0x48127ffb7fff8000","0x480080007ffe8000","0xa0680017fff8000","0x9","0x4824800180007ffd","0x3336","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff57fff","0x10780017fff7fff","0x5c","0x4824800180007ffd","0x3336","0x400080007ff67fff","0x48127fff7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x1f0d4aa99431d246bac9b8e48c33e888245b15e9678f64f9bdfc8823dc8f979","0x482480017ff38000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffc","0x400280037ffb7ffd","0x480280057ffb8000","0x20680017fff7fff","0x39","0x480280047ffb8000","0x480280067ffb8000","0x482680017ffb8000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x12","0x4824800180007ffc","0x100","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480080007ff57fff","0x482480017ffe8000","0xefffffffffffffde00000000000000ff","0x480080017ff37fff","0x400080027ff27ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x16","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x482480017ffc8000","0xffffffffffffffffffffffffffffff00","0x400080017ff77fff","0x40780017fff7fff","0x1","0x400080007fff7ffa","0x482480017ff68000","0x2","0x482480017ffb8000","0x55a","0x48127ff97fff8000","0x480680017fff8000","0x0","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f72655538202d206e6f6e207538","0x400080007ffe7fff","0x482480017ff08000","0x3","0x48127ff57fff8000","0x48127ff37fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0xa","0x480280047ffb8000","0x48127ffc7fff8000","0x482480017ffe8000","0x776","0x482680017ffb8000","0x8","0x480280067ffb8000","0x480280077ffb8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017ff38000","0x1","0x48127ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff98000","0x1","0x482680017ffa8000","0x21b6","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x5e","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x1bf8","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x11","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x48127ffb7fff8000","0x482480017ffb8000","0x492","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3b62","0x482480017fff8000","0x3b61","0x48127ffb7fff8000","0x480080007ffe8000","0xa0680017fff8000","0x9","0x4824800180007ffd","0x6bc6","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff57fff","0x10780017fff7fff","0x25","0x4824800180007ffd","0x6bc6","0x400080007ff67fff","0x482480017ff68000","0x1","0x48127ffe7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x110e2f729c9c2b988559994a3daccd838cf52faf88e18101373e67dd061455a","0x1104800180018000","0x1d4b","0x20680017fff7ffd","0xf","0x40780017fff7fff","0x1","0x400080007fff7ffd","0x400080017fff7ffe","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x0","0x48127ffb7fff8000","0x482480017ffa8000","0x2","0x208b7fff7fff7ffe","0x48127ffa7fff8000","0x482480017ffa8000","0x12c","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017ff38000","0x1","0x48127ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff98000","0x1","0x482680017ffa8000","0x21b6","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xae","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x128e","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x7e","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x69","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x55a","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3abd","0x482480017fff8000","0x3abc","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x482480017fff8000","0x6e82","0xa0680017fff8000","0x8","0x48307ffe80007ffb","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff37fff","0x10780017fff7fff","0x28","0x48307ffe80007ffb","0x400080007ff47fff","0x482480017ff48000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x48127fe87fff8000","0x1104800180018000","0x1d64","0x20680017fff7ffd","0x10","0x40780017fff7fff","0x1","0x400080007fff7ffd","0x400080017fff7ffe","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x482480017ff98000","0x2","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x12c","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017ff08000","0x1","0x48127ff57fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x4f6","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa14","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xfa","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x9ec","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xca","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xb5","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x480080007fef8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x81","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x6c","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x39c8","0x482480017fff8000","0x39c7","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x7012","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x29","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x3c87bf42ed4f01f11883bf54f43d91d2cbbd5fec26d1df9c74c57ae138800a4","0x48127fd97fff8000","0x48127fe67fff8000","0x1104800180018000","0x1d35","0x20680017fff7ffd","0x10","0x40780017fff7fff","0x1","0x400080007fff7ffd","0x400080017fff7ffe","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x482480017ff98000","0x2","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x12c","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x55a","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0xd98","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x12b6","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x161","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x131","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x11c","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xe8","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xcc","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xaa","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x8e","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3883","0x482480017fff8000","0x3882","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x21962","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x50","0x48307ffe80007ffa","0x400080007ff37fff","0x48127fff7fff8000","0x482480017ff28000","0x1","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffb7fff","0x400280017ffb7ffd","0x480280037ffb8000","0x20680017fff7fff","0x29","0x480280027ffb8000","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffd7fff8000","0x480a7ff87fff8000","0x482680017ffb8000","0x5","0x480080027ffb8000","0x48127fcb7fff8000","0x48127fd97fff8000","0x48127fe37fff8000","0x1104800180018000","0x1cb0","0x20680017fff7ffd","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x1","0x400080007ffe7fff","0x48127ff97fff8000","0x48127ff67fff8000","0x482480017ff68000","0x190","0x48127ff77fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff97fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x14","0x480280027ffb8000","0x1104800180018000","0x383c","0x482480017fff8000","0x383b","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1ec12","0x480a7ff87fff8000","0x48127ff47fff8000","0x48307ffd7ff68000","0x482680017ffb8000","0x6","0x480280047ffb8000","0x480280057ffb8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x4","0xa0680017fff8000","0x7","0x482680017ffa8000","0xfffffffffffffffffffffffffffffc68","0x400280007ff97fff","0x10780017fff7fff","0x1cb","0x4825800180007ffa","0x398","0x400280007ff97fff","0x482680017ff98000","0x1","0x48127ffe7fff8000","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x19c","0x40137fff7fff8003","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4825800180048003","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x186","0x484480017fff8001","0x8000000000000000000000000000000","0x48317fff80008003","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x480080007fef8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x152","0x40137fff7fff8002","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4825800180048002","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x13c","0x484480017fff8001","0x8000000000000000000000000000000","0x48317fff80008002","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x108","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xec","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xca","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xae","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x36d8","0x482480017fff8000","0x36d7","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x8","0x482480017fff8000","0x3479c","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x70","0x48307ffe80007ffa","0x400080007ff37fff","0x48127fff7fff8000","0x482480017ff28000","0x1","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffb7fff","0x400280017ffb7ffd","0x480280037ffb8000","0x20680017fff7fff","0x49","0x480280027ffb8000","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffd7fff8000","0x480a7ff87fff8000","0x482680017ffb8000","0x5","0x480a80037fff8000","0x480080027ffa8000","0x40137fd97fff8000","0x40137fe47fff8001","0x480a80007fff8000","0x480a80017fff8000","0x1104800180018000","0x1edc","0x20680017fff7ffd","0x26","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480a80037fff8000","0x480a80027fff8000","0x480a80007fff8000","0x480a80017fff8000","0x1104800180018000","0x1af7","0x20680017fff7ffd","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x1","0x400080007ffe7fff","0x48127ff97fff8000","0x48127ff67fff8000","0x482480017ff68000","0x190","0x48127ff77fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff97fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x26","0x1104800180018000","0x3684","0x482480017fff8000","0x3683","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1eb4a","0x48127ff47fff8000","0x48127ff17fff8000","0x48307ffd7ff18000","0x48127ff27fff8000","0x48127ff37fff8000","0x48127ff37fff8000","0x10780017fff7fff","0x14","0x480280027ffb8000","0x1104800180018000","0x3671","0x482480017fff8000","0x3670","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x8","0x482480017fff8000","0x31a4c","0x480a7ff87fff8000","0x48127ff47fff8000","0x48307ffd7ff68000","0x482680017ffb8000","0x6","0x480280047ffb8000","0x480280057ffb8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202333","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x1716","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x1a36","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x1fae","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x20c6","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x161","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x131","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x11c","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xe8","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xcc","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xaa","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x8e","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3529","0x482480017fff8000","0x3528","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xe2a4","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x50","0x48307ffe80007ffa","0x400080007ff37fff","0x48127fff7fff8000","0x482480017ff28000","0x1","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffb7fff","0x400280017ffb7ffd","0x480280037ffb8000","0x20680017fff7fff","0x29","0x480280027ffb8000","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffd7fff8000","0x480a7ff87fff8000","0x482680017ffb8000","0x5","0x480080027ffb8000","0x48127fcb7fff8000","0x48127fd97fff8000","0x48127fe37fff8000","0x1104800180018000","0x1eb5","0x20680017fff7ffd","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x1","0x400080007ffe7fff","0x48127ff97fff8000","0x48127ff67fff8000","0x482480017ff68000","0x190","0x48127ff77fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff97fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x14","0x480280027ffb8000","0x1104800180018000","0x34e2","0x482480017fff8000","0x34e1","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb554","0x480a7ff87fff8000","0x48127ff47fff8000","0x48307ffd7ff68000","0x482680017ffb8000","0x6","0x480280047ffb8000","0x480280057ffb8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x144","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x114","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xff","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xcb","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xaf","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x8d","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x71","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x33b2","0x482480017fff8000","0x33b1","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x156ee","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x33","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127fd27fff8000","0x48127fe07fff8000","0x48127fea7fff8000","0x1104800180018000","0x1e2e","0x20680017fff7ffd","0x1b","0x40780017fff7fff","0x1","0x48127ff97fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x1","0x400080007ffc7fff","0x48127ff77fff8000","0x48127ff47fff8000","0x48127ffc7fff8000","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff77fff8000","0x482480017ff68000","0x1","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x2bc","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x144","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x114","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xff","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xcb","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xaf","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x8d","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x71","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3258","0x482480017fff8000","0x3257","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x156ee","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x33","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127fd27fff8000","0x48127fe07fff8000","0x48127fea7fff8000","0x1104800180018000","0x1e60","0x20680017fff7ffd","0x1b","0x40780017fff7fff","0x1","0x48127ff97fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x1","0x400080007ffc7fff","0x48127ff77fff8000","0x48127ff47fff8000","0x48127ffc7fff8000","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff77fff8000","0x482480017ff68000","0x1","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x2bc","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x5e","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x1bf8","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x11","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x48127ffb7fff8000","0x482480017ffb8000","0x492","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3196","0x482480017fff8000","0x3195","0x48127ffb7fff8000","0x480080007ffe8000","0xa0680017fff8000","0x9","0x4824800180007ffd","0x6bc6","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff57fff","0x10780017fff7fff","0x25","0x4824800180007ffd","0x6bc6","0x400080007ff67fff","0x482480017ff68000","0x1","0x48127ffe7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x110e2f729c9c2b988559994a3daccd838cf52faf88e18101373e67dd061455a","0x1104800180018000","0x137f","0x20680017fff7ffd","0xf","0x40780017fff7fff","0x1","0x400080007fff7ffd","0x400080017fff7ffe","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x0","0x48127ffb7fff8000","0x482480017ffa8000","0x2","0x208b7fff7fff7ffe","0x48127ffa7fff8000","0x482480017ffa8000","0x12c","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017ff38000","0x1","0x48127ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff98000","0x1","0x482680017ffa8000","0x21b6","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xae","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x128e","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x7e","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x69","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x55a","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x30f1","0x482480017fff8000","0x30f0","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x482480017fff8000","0x6e82","0xa0680017fff8000","0x8","0x48307ffe80007ffb","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff37fff","0x10780017fff7fff","0x28","0x48307ffe80007ffb","0x400080007ff47fff","0x482480017ff48000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x48127fe87fff8000","0x1104800180018000","0x1398","0x20680017fff7ffd","0x10","0x40780017fff7fff","0x1","0x400080007fff7ffd","0x400080017fff7ffe","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x482480017ff98000","0x2","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x12c","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017ff08000","0x1","0x48127ff57fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x4f6","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa14","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x4","0xa0680017fff8000","0x7","0x482680017ffa8000","0xfffffffffffffffffffffffffffffc68","0x400280007ff97fff","0x10780017fff7fff","0x1cb","0x4825800180007ffa","0x398","0x400280007ff97fff","0x482680017ff98000","0x1","0x48127ffe7fff8000","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x19c","0x40137fff7fff8003","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4825800180048003","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x186","0x484480017fff8001","0x8000000000000000000000000000000","0x48317fff80008003","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x480080007fef8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x152","0x40137fff7fff8002","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4825800180048002","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x13c","0x484480017fff8001","0x8000000000000000000000000000000","0x48317fff80008002","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x108","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xec","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xca","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xae","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x2f93","0x482480017fff8000","0x2f92","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x8","0x482480017fff8000","0x3479c","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x70","0x48307ffe80007ffa","0x400080007ff37fff","0x48127fff7fff8000","0x482480017ff28000","0x1","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffb7fff","0x400280017ffb7ffd","0x480280037ffb8000","0x20680017fff7fff","0x49","0x480280027ffb8000","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffd7fff8000","0x480a7ff87fff8000","0x482680017ffb8000","0x5","0x480a80037fff8000","0x480080027ffa8000","0x40137fd97fff8000","0x40137fe47fff8001","0x480a80007fff8000","0x480a80017fff8000","0x1104800180018000","0x1797","0x20680017fff7ffd","0x26","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480a80037fff8000","0x480a80027fff8000","0x480a80007fff8000","0x480a80017fff8000","0x1104800180018000","0x13b2","0x20680017fff7ffd","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x1","0x400080007ffe7fff","0x48127ff97fff8000","0x48127ff67fff8000","0x482480017ff68000","0x190","0x48127ff77fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff97fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x26","0x1104800180018000","0x2f3f","0x482480017fff8000","0x2f3e","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1eb4a","0x48127ff47fff8000","0x48127ff17fff8000","0x48307ffd7ff18000","0x48127ff27fff8000","0x48127ff37fff8000","0x48127ff37fff8000","0x10780017fff7fff","0x14","0x480280027ffb8000","0x1104800180018000","0x2f2c","0x482480017fff8000","0x2f2b","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x8","0x482480017fff8000","0x31a4c","0x480a7ff87fff8000","0x48127ff47fff8000","0x48307ffd7ff68000","0x482680017ffb8000","0x6","0x480280047ffb8000","0x480280057ffb8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202333","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x1716","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x1a36","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x1fae","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x20c6","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x144","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x114","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xff","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xcb","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xaf","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x8d","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x71","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x2de4","0x482480017fff8000","0x2de3","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x156ee","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x33","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127fd27fff8000","0x48127fe07fff8000","0x48127fea7fff8000","0x1104800180018000","0x1860","0x20680017fff7ffd","0x1b","0x40780017fff7fff","0x1","0x48127ff97fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x1","0x400080007ffc7fff","0x48127ff77fff8000","0x48127ff47fff8000","0x48127ffc7fff8000","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff77fff8000","0x482480017ff68000","0x1","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x2bc","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x144","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x114","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xff","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xcb","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xaf","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x8d","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x71","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x2c8a","0x482480017fff8000","0x2c89","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x156ee","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x33","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127fd27fff8000","0x48127fe07fff8000","0x48127fea7fff8000","0x1104800180018000","0x1892","0x20680017fff7ffd","0x1b","0x40780017fff7fff","0x1","0x48127ff97fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x1","0x400080007ffc7fff","0x48127ff77fff8000","0x48127ff47fff8000","0x48127ffc7fff8000","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff77fff8000","0x482480017ff68000","0x1","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x2bc","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0xffffffffffffffffffffffffffffe192","0x400280007ff97fff","0x10780017fff7fff","0x297","0x4825800180007ffa","0x1e6e","0x400280007ff97fff","0x482680017ff98000","0x1","0x48127ffe7fff8000","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x27c","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x48127ffc7fff8000","0x480280007ffc8000","0x48307ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x262","0x482480017ffb8000","0x1","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480080007ff88000","0x48307ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffd7fff8000","0x482480017ffa8000","0x1","0x48127ffa7fff8000","0x480680017fff8000","0x0","0x48127ff77fff8000","0x10780017fff7fff","0x9","0x48127ffd7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x234","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x12","0x4824800180007ffd","0x100","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480080007fe87fff","0x482480017ffe8000","0xefffffffffffffde00000000000000ff","0x480080017fe67fff","0x400080027fe57ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x21c","0x402780017fff7fff","0x1","0x400080007feb7ffd","0x482480017ffd8000","0xffffffffffffffffffffffffffffff00","0x400080017fea7fff","0x482480017fea8000","0x2","0x48127ffc7fff8000","0x48307ff680007ff7","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff48000","0x1","0x48127ff47fff8000","0x480680017fff8000","0x0","0x48127ff17fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x1ea","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x1ce","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x1ac","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x190","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48127ff07fff8000","0x48127ffa7fff8000","0x48307ff580007ff6","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffc7fff8000","0x482480017ff38000","0x1","0x48127ff37fff8000","0x480680017fff8000","0x0","0x480080007ff08000","0x10780017fff7fff","0x9","0x48127ffc7fff8000","0x48127ff37fff8000","0x48127ff37fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x15f","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff17ffc","0x480080017ff07ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027fef7ffd","0x10780017fff7fff","0x14a","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff27ffd","0x480080017ff17ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff07ffe","0x482480017ff08000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x480080007fef8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x116","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x101","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x480080007fef8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xcd","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xb8","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x84","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x12","0x4824800180007ffd","0x10000000000000000","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480080007ff27fff","0x482480017ffe8000","0xefffffffffffffdeffffffffffffffff","0x480080017ff07fff","0x400080027fef7ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x6c","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ffd8000","0xffffffffffffffff0000000000000000","0x400080017ff47fff","0x482480017ff48000","0x2","0x48127ffc7fff8000","0x48307ff680007ff7","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x2a55","0x482480017fff8000","0x2a54","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0xb","0x482480017fff8000","0x56a54","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x2b","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127f917fff8000","0x48127f957fff8000","0x48127f9b7fff8000","0x48127fb67fff8000","0x48127fb67fff8000","0x48127fbb7fff8000","0x48127fc87fff8000","0x48127fd57fff8000","0x48127fe37fff8000","0x1104800180018000","0x17e3","0x20680017fff7ffd","0xd","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x64","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017fef8000","0x3","0x482480017ff78000","0x384","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x96a","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202338","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0xc8a","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x11a8","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202337","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x14c8","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x19e6","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202336","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017fef8000","0x3","0x482480017ff88000","0x1d06","0x10780017fff7fff","0x5","0x48127ff67fff8000","0x482480017ffa8000","0x2224","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202335","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x2260","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x28aa","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x28e6","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x2f30","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202334","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017fe58000","0x3","0x482480017ff78000","0x307a","0x10780017fff7fff","0x5","0x48127fee7fff8000","0x482480017ffa8000","0x3660","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202333","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ff57fff8000","0x482480017ff98000","0x3bd8","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x3e30","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffa7fff","0x400380017ffa7ff8","0x480280037ffa8000","0x20680017fff7fff","0x8d","0x480280027ffa8000","0x480280047ffa8000","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x1390569bb0a3a722eb4228e8700301347da081211d5c2ded2db22ef389551ab","0x480080007ffc8000","0x480080017ffb8000","0x480080027ffa8000","0x480080037ff98000","0x480080047ff88000","0x480680017fff8000","0x53746f7261676552656164","0x400280057ffa7fff","0x400280067ffa7ff7","0x400280077ffa7ff8","0x400280087ffa7ff9","0x4802800a7ffa8000","0x20680017fff7fff","0x5e","0x480280097ffa8000","0x4802800b7ffa8000","0x482680017ffa8000","0xc","0x48127ffd7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffc","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480280007ff77ffc","0x480280017ff77ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400280027ff77ffd","0x10780017fff7fff","0x33","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffb","0x480280007ff77ffd","0x480280017ff77ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400280027ff77ffe","0x48307ff880007ff2","0x482680017ff78000","0x3","0x48127ff87fff8000","0x20680017fff7ffd","0xc","0x48127ffe7fff8000","0x48127ffe7fff8000","0x480a7ff97fff8000","0x48127ff37fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x1104800180018000","0x17b1","0x208b7fff7fff7ffe","0x1104800180018000","0x28f6","0x482480017fff8000","0x28f5","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1e9d8","0x40780017fff7fff","0x1","0x480680017fff8000","0x4d494e5445525f4f4e4c59","0x400080007ffe7fff","0x48127ff57fff8000","0x48307ffc7ff58000","0x480a7ff97fff8000","0x48127fea7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x28dd","0x482480017fff8000","0x28dc","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1e848","0x40780017fff7fff","0x1","0x480680017fff8000","0x4e6f6e20436f6e747261637441646472657373","0x400080007ffe7fff","0x482680017ff78000","0x3","0x48307ffc7fef8000","0x48127fed7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x13","0x480280097ffa8000","0x1104800180018000","0x28c4","0x482480017fff8000","0x28c3","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1ef5a","0x480a7ff77fff8000","0x48307ffe7ff78000","0x482680017ffa8000","0xd","0x4802800b7ffa8000","0x4802800c7ffa8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ff97fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480280027ffa8000","0x1104800180018000","0x28aa","0x482480017fff8000","0x28a9","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x21f02","0x480a7ff77fff8000","0x48307ffe7ff78000","0x480a7ff97fff8000","0x482680017ffa8000","0x6","0x480680017fff8000","0x1","0x480280047ffa8000","0x480280057ffa8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffa7fff","0x400380017ffa7ff8","0x480280037ffa8000","0x20680017fff7fff","0x8d","0x480280027ffa8000","0x480280047ffa8000","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x1390569bb0a3a722eb4228e8700301347da081211d5c2ded2db22ef389551ab","0x480080007ffc8000","0x480080017ffb8000","0x480080027ffa8000","0x480080037ff98000","0x480080047ff88000","0x480680017fff8000","0x53746f7261676552656164","0x400280057ffa7fff","0x400280067ffa7ff7","0x400280077ffa7ff8","0x400280087ffa7ff9","0x4802800a7ffa8000","0x20680017fff7fff","0x5e","0x480280097ffa8000","0x4802800b7ffa8000","0x482680017ffa8000","0xc","0x48127ffd7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffc","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480280007ff77ffc","0x480280017ff77ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400280027ff77ffd","0x10780017fff7fff","0x33","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffb","0x480280007ff77ffd","0x480280017ff77ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400280027ff77ffe","0x48307ff880007ff2","0x482680017ff78000","0x3","0x48127ff87fff8000","0x20680017fff7ffd","0xc","0x48127ffe7fff8000","0x48127ffe7fff8000","0x480a7ff97fff8000","0x48127ff37fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x1104800180018000","0x19e5","0x208b7fff7fff7ffe","0x1104800180018000","0x284f","0x482480017fff8000","0x284e","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1e9d8","0x40780017fff7fff","0x1","0x480680017fff8000","0x4d494e5445525f4f4e4c59","0x400080007ffe7fff","0x48127ff57fff8000","0x48307ffc7ff58000","0x480a7ff97fff8000","0x48127fea7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x2836","0x482480017fff8000","0x2835","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1e848","0x40780017fff7fff","0x1","0x480680017fff8000","0x4e6f6e20436f6e747261637441646472657373","0x400080007ffe7fff","0x482680017ff78000","0x3","0x48307ffc7fef8000","0x48127fed7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x13","0x480280097ffa8000","0x1104800180018000","0x281d","0x482480017fff8000","0x281c","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1ef5a","0x480a7ff77fff8000","0x48307ffe7ff78000","0x482680017ffa8000","0xd","0x4802800b7ffa8000","0x4802800c7ffa8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ff97fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480280027ffa8000","0x1104800180018000","0x2803","0x482480017fff8000","0x2802","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x21f02","0x480a7ff77fff8000","0x48307ffe7ff78000","0x480a7ff97fff8000","0x482680017ffa8000","0x6","0x480680017fff8000","0x1","0x480280047ffa8000","0x480280057ffa8000","0x208b7fff7fff7ffe","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xa","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x8","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x98","0xa0680017fff8004","0xe","0x4824800180047ffe","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480280007ffb7ffc","0x480280017ffb7ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400280027ffb7ffd","0x10780017fff7fff","0x84","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffd","0x480280007ffb7ffd","0x480280017ffb7ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400280027ffb7ffe","0x482680017ffb8000","0x3","0x48127ff67fff8000","0x48127ff67fff8000","0x1104800180018000","0x1c2e","0x20680017fff7ff8","0x5e","0x20680017fff7ffb","0x46","0x48307ff980007ffa","0x20680017fff7fff","0x4","0x10780017fff7fff","0xa","0x482480017ff88000","0x1","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ff57fff8000","0x10780017fff7fff","0x8","0x48127ff87fff8000","0x48127ff87fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x1b","0x480080007fff8000","0x20680017fff7fff","0x6","0x480680017fff8000","0x1","0x10780017fff7fff","0x4","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x48127fef7fff8000","0x480680017fff8000","0x0","0x48127ff77fff8000","0x48127ff77fff8000","0x480680017fff8000","0x0","0x48127f9e7fff8000","0x48127fee7fff8000","0x48127fee7fff8000","0x48127fee7fff8000","0x48127fee7fff8000","0x48307ff480007ff5","0x208b7fff7fff7ffe","0x40780017fff7fff","0x3","0x48127fef7fff8000","0x480680017fff8000","0x0","0x48127ff77fff8000","0x48127ff77fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x8","0x48127fef7fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x48127fef7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x8","0x48127fef7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127fed7fff8000","0x48127fed7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x56","0x482680017ffb8000","0x3","0x10780017fff7fff","0x5","0x40780017fff7fff","0x5c","0x480a7ffb7fff8000","0x480680017fff8000","0x0","0x48127f9e7fff8000","0x48127f9e7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480a7ff37fff8000","0x480a7ff47fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff77fff8000","0x48127ff67fff8000","0x1104800180018000","0x1c70","0x20680017fff7ffd","0x9d","0x1104800180018000","0x271e","0x482480017fff8000","0x271d","0x48127ff87fff8000","0x48127ff87fff8000","0x480a7ff67fff8000","0x480080007ffc8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff47fff8000","0x48127ff47fff8000","0x1104800180018000","0x1c9e","0x20680017fff7ffc","0x7a","0x480680017fff8000","0x1ac8d354f2e793629cb233a16f10d13cf15b9c45bbc620577c8e1df95ede545","0x400280007ff57fff","0x400280017ff57ffe","0x480280027ff58000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff37ffc","0x480080017ff27ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027ff07ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff37ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff17ffd","0x400080027ff07ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff17fff8000","0x480680017fff8000","0x0","0x482680017ff58000","0x3","0x482480017fed8000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ff77fff","0x400280017ff77ffb","0x400280027ff77ffc","0x400280037ff77ffa","0x480280057ff78000","0x20680017fff7fff","0x3b","0x480280047ff78000","0x480280067ff78000","0x482680017ff78000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x12","0x4824800180007ffc","0x10000000000000000","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480080007ff57fff","0x482480017ffe8000","0xefffffffffffffdeffffffffffffffff","0x480080017ff37fff","0x400080027ff27ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x15","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x482480017ffc8000","0xffffffffffffffff0000000000000000","0x400080017ff77fff","0x482480017ff78000","0x2","0x482480017ffc8000","0x3ca","0x48127ff47fff8000","0x48127fe37fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff47fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f7265553634202d206e6f6e20753634","0x400080007ffe7fff","0x482480017ff08000","0x3","0x48127ff57fff8000","0x48127fed7fff8000","0x48127fdc7fff8000","0x48127ff17fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x480280047ff78000","0x48127ffc7fff8000","0x482480017ffe8000","0x712","0x48127ff97fff8000","0x48127fe87fff8000","0x482680017ff78000","0x8","0x480680017fff8000","0x1","0x480280067ff78000","0x480280077ff78000","0x208b7fff7fff7ffe","0x1104800180018000","0x2692","0x482480017fff8000","0x2691","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x346c","0x48127ff37fff8000","0x48307ffe7ff38000","0x48127ff37fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x10780017fff7fff","0xf","0x1104800180018000","0x2683","0x482480017fff8000","0x2682","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x4326","0x48127ff57fff8000","0x48307ffe7ff58000","0x480a7ff67fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ff57fff8000","0x48127ffa7fff8000","0x480a7ff77fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x4","0x480a7ff37fff8000","0x480a7ff47fff8000","0x480a7ff57fff8000","0x480a7ff77fff8000","0x1104800180018000","0x1c94","0x20680017fff7ffd","0x15f","0x48127ffa7fff8000","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400080007ffa7fff","0x400080017ffa7ffe","0x480080037ffa8000","0x20680017fff7fff","0x141","0x480080027ff98000","0x480080047ff88000","0x480080007fff8000","0x48127ffd7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x3fc801c47df4de8d5835f8bfd4d0b8823ba63e5a3f278086901402d680abfc","0x480080007ffc8000","0x480080017ffb8000","0x480080027ffa8000","0x480680017fff8000","0x53746f7261676552656164","0x400080057fef7fff","0x400080067fef7ff9","0x400080077fef7ffa","0x400080087fef7ffb","0x4800800a7fef8000","0x20680017fff7fff","0x110","0x480080097fee8000","0x4800800b7fed8000","0x482480017fec8000","0xc","0x48127ffd7fff8000","0xa0680017fff8000","0x12","0x4824800180007ffc","0x10000000000000000","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480080007fe37fff","0x482480017ffe8000","0xefffffffffffffdeffffffffffffffff","0x480080017fe17fff","0x400080027fe07ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0xe3","0x402780017fff7fff","0x1","0x400080007fe67ffc","0x482480017ffc8000","0xffffffffffffffff0000000000000000","0x400080017fe57fff","0x48127ffd7fff8000","0xa0680017fff8000","0x8","0x48307ff97ff48000","0x4824800180007fff","0x10000000000000000","0x400080027fe17fff","0x10780017fff7fff","0xb9","0x48307ff97ff48001","0x4824800180007fff","0xffffffffffffffff0000000000000000","0x400080027fe17ffe","0x480680017fff8000","0x127500","0x48127ffb7fff8000","0xa0680017fff8000","0x8","0x48307ffd7ffc8000","0x4824800180007fff","0x10000000000000000","0x400080037fdc7fff","0x10780017fff7fff","0x8f","0x48307ffd7ffc8001","0x4824800180007fff","0xffffffffffffffff0000000000000000","0x400080037fdc7ffe","0x482480017fdc8000","0x4","0x48127ffb7fff8000","0x48127fdc7fff8000","0x480a7ff67fff8000","0x48127fef7fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127fef7fff8000","0x40137ff37fff8003","0x1104800180018000","0x1cc7","0x20680017fff7ffd","0x67","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480a80037fff8000","0x1104800180018000","0x1d4e","0x40137ffa7fff8002","0x40137ffb7fff8001","0x40137ffc7fff8000","0x20680017fff7ffd","0x49","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff67fff8000","0x48127ff67fff8000","0x480680017fff8000","0x15","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x1104800180018000","0x1dcc","0x20680017fff7ffb","0x28","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0x10","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80027fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80027fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80027fff8000","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x48127ff87fff8000","0x482480017ff88000","0x4ef2","0x480a80027fff8000","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x2598","0x482480017fff8000","0x2597","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xaeba","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff27fff8000","0x48127ff27fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x2586","0x482480017fff8000","0x2585","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x10e32","0x40780017fff7fff","0x1","0x480680017fff8000","0x7536345f616464204f766572666c6f77","0x400080007ffe7fff","0x482480017fd38000","0x4","0x48307ffc7ff28000","0x48127fd37fff8000","0x480a7ff67fff8000","0x48127fe67fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x256b","0x482480017fff8000","0x256a","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x110d0","0x40780017fff7fff","0x1","0x480680017fff8000","0x7536345f616464204f766572666c6f77","0x400080007ffe7fff","0x482480017fd88000","0x3","0x48307ffc7ff28000","0x48127fd87fff8000","0x480a7ff67fff8000","0x48127feb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x2550","0x482480017fff8000","0x254f","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x10e78","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f7265553634202d206e6f6e20753634","0x400080007ffe7fff","0x482480017fd78000","0x3","0x48307ffc7fee8000","0x48127fec7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x15","0x40780017fff7fff","0xc","0x480080097fe28000","0x1104800180018000","0x2535","0x482480017fff8000","0x2534","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x11512","0x48127fd77fff8000","0x48307ffe7ff78000","0x482480017fd88000","0xd","0x4800800b7fd78000","0x4800800c7fd68000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fd27fff8000","0x480a7ff67fff8000","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x480080027ff98000","0x1104800180018000","0x251a","0x482480017fff8000","0x2519","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x14532","0x48127fee7fff8000","0x48307ffe7ff78000","0x48127fee7fff8000","0x480a7ff67fff8000","0x482480017fed8000","0x6","0x480680017fff8000","0x1","0x480080047feb8000","0x480080057fea8000","0x208b7fff7fff7ffe","0x1104800180018000","0x2505","0x482480017fff8000","0x2504","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x16efe","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x480a7ff67fff8000","0x48127ff17fff8000","0x480680017fff8000","0x1","0x48127ff17fff8000","0x48127ff17fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x3","0x480a7ff37fff8000","0x480a7ff47fff8000","0x480a7ff57fff8000","0x480a7ff77fff8000","0x1104800180018000","0x1b19","0x20680017fff7ffd","0xc7","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480a7ff67fff8000","0x48127ff87fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffdaf","0x20680017fff7ffd","0xa4","0x48127ff97fff8000","0x20680017fff7ffe","0x18","0x1104800180018000","0x24d5","0x482480017fff8000","0x24d4","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x10f68","0x48127ff07fff8000","0x48307ffe7ff78000","0x48127ff07fff8000","0x48127ff07fff8000","0x48127ff07fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x48127ff77fff8000","0x48127ffe7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x1104800180018000","0x1b78","0x20680017fff7ffd","0x68","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x1104800180018000","0x1bfe","0x40137ffa7fff8002","0x40137ffb7fff8001","0x40137ffc7fff8000","0x20680017fff7ffd","0x49","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff67fff8000","0x48127ff67fff8000","0x480680017fff8000","0x13","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x1104800180018000","0x1c7c","0x20680017fff7ffb","0x28","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0x10","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80027fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80027fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80027fff8000","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x48127ff87fff8000","0x482480017ff88000","0x4ef2","0x480a80027fff8000","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x2448","0x482480017fff8000","0x2447","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xaeba","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff27fff8000","0x48127ff27fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x2436","0x482480017fff8000","0x2435","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x11030","0x48127ff17fff8000","0x48307ffe7ff18000","0x48127ff17fff8000","0x48127ff17fff8000","0x48127ff17fff8000","0x480680017fff8000","0x1","0x48127ff17fff8000","0x48127ff17fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x2422","0x482480017fff8000","0x2421","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x3","0x482480017fff8000","0x1778c","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x480a7ff67fff8000","0x48127ff17fff8000","0x480680017fff8000","0x1","0x48127ff17fff8000","0x48127ff17fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x9","0x480a7ff37fff8000","0x480a7ff47fff8000","0x480a7ff57fff8000","0x480a7ff77fff8000","0x1104800180018000","0x1a36","0x20680017fff7ffd","0x2e0","0x48127ffa7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0xcfc0e4c73ce8e46b07c3167ce01ce17e6c2deaaa5b88b977bbb10abe25c9ad","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffc","0x400080027ff87ffd","0x400080037ff87ffe","0x480080057ff88000","0x20680017fff7fff","0x2bc","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x48307ffe80007fff","0x20680017fff7fff","0x28d","0x48127ffc7fff8000","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400080007ff87fff","0x400080017ff87ffe","0x480080037ff88000","0x20680017fff7fff","0x26f","0x480080027ff78000","0x480080047ff68000","0x480080007fff8000","0x48127fe67fff8000","0x48127ffc7fff8000","0x48127fe67fff8000","0x480a7ff67fff8000","0x482480017ff08000","0x5","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x400180007ff48006","0x400180017ff48007","0x400180027ff48008","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffc9a","0x20680017fff7ffd","0x245","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x40137ff47fff8005","0x1104800180018000","0x1ce1","0x40137ffa7fff8001","0x40137ffb7fff8002","0x40137ffc7fff8004","0x20680017fff7ffd","0x21e","0x48127ff97fff8000","0x20780017fff8005","0x1c","0x1104800180018000","0x23ad","0x482480017fff8000","0x23ac","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1f216","0x40780017fff7fff","0x1","0x480680017fff8000","0x554e4b4e4f574e5f494d504c454d454e544154494f4e","0x400080007ffe7fff","0x48127fee7fff8000","0x48307ffc7ff58000","0x480a80017fff8000","0x480a80027fff8000","0x480a80047fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x48127fff7fff8000","0x4829800580018007","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff37fff","0x10780017fff7fff","0x1dd","0x400080007ff47fff","0x48127ffd7fff8000","0x4828800780017ffa","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff07fff","0x10780017fff7fff","0x1b8","0x400080017ff17fff","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x482480017fef8000","0x2","0x48127ffa7fff8000","0x480680017fff8000","0x11","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x1104800180018000","0x1b60","0x20680017fff7ffb","0x186","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080047fff","0x4002800180047ffe","0x4002800280047ffa","0x4002800380047ffb","0x4002800480047ffc","0x4002800580047ffd","0x4802800780048000","0x20680017fff7fff","0x168","0x4802800680048000","0x4826800180048000","0x8","0x48127ffe7fff8000","0x20780017fff7ffd","0x8","0x48127ff37fff8000","0x482480017ffe8000","0x7b0c","0x48127ffc7fff8000","0x10780017fff7fff","0x42","0x48127fff7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0xcfc0e4c73ce8e46b07c3167ce01ce17e6c2deaaa5b88b977bbb10abe25c9ad","0x480680017fff8000","0x1","0x480680017fff8000","0x53746f726167655772697465","0x400080007ff97fff","0x400080017ff97ffb","0x400080027ff97ffc","0x400080037ff97ffd","0x400080047ff97ffe","0x480080067ff98000","0x20680017fff7fff","0x135","0x480080057ff88000","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127fea7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0xf","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480a7ff87fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x402580017fe88003","0x7","0x1104800180018000","0x1b19","0x20680017fff7ffb","0xfd","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080037fff","0x4002800180037ffe","0x4002800280037ffa","0x4002800380037ffb","0x4002800480037ffc","0x4002800580037ffd","0x4802800780038000","0x20680017fff7fff","0xdf","0x4802800680038000","0x48127ff57fff8000","0x48127ffe7fff8000","0x4826800180038000","0x8","0x40137fff7fff8000","0x20780017fff7ff9","0x67","0x40780017fff7fff","0x1","0x48297ffb80007ffc","0x400080007ffe7fff","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x1104800180018000","0x1cee","0x20680017fff7ffd","0x44","0x48127ffc7fff8000","0x480680017fff8000","0x3ea3b9a8522d36784cb325f9c7e2ec3c9f3e6d63031a6c6b8743cc22412f604","0x480680017fff8000","0x4c69627261727943616c6c","0x4002800080007fff","0x4002800180007ffd","0x4003800280007ffa","0x4002800380007ffe","0x4002800480007ffb","0x4002800580007ffc","0x4802800780008000","0x20680017fff7fff","0xc","0x4802800680008000","0x48127fff7fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x0","0x4802800880008000","0x4802800980008000","0x10780017fff7fff","0xb","0x4802800680008000","0x482480017fff8000","0x64","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x20680017fff7ffd","0x7","0x48127ff17fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x34","0x1104800180018000","0x22d6","0x482480017fff8000","0x22d5","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xe8d0","0x40780017fff7fff","0x1","0x480680017fff8000","0x4549435f4c49425f43414c4c5f4641494c4544","0x400080007ffe7fff","0x48127fe87fff8000","0x48307ffc7ff18000","0x480a80017fff8000","0x480a80027fff8000","0x48127fef7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x22bc","0x482480017fff8000","0x22bb","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x11878","0x48127ff47fff8000","0x48307ffe7ff48000","0x480a80017fff8000","0x480a80027fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff17fff8000","0x48127ff17fff8000","0x208b7fff7fff7ffe","0x48127ffd7fff8000","0x482480017ffd8000","0x3886","0x480a80007fff8000","0x480680017fff8000","0x5265706c616365436c617373","0x400080007ffe7fff","0x400080017ffe7ffd","0x400180027ffe7ff8","0x480080047ffe8000","0x20680017fff7fff","0xe","0x480080037ffd8000","0x48127fff7fff8000","0x482480017ffb8000","0x5","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x10780017fff7fff","0xb","0x480080037ffd8000","0x482480017fff8000","0x64","0x482480017ffb8000","0x7","0x480680017fff8000","0x1","0x480080057ff98000","0x480080067ff88000","0x20680017fff7ffd","0x35","0x48127ff57fff8000","0x48127ffa7fff8000","0x480a80017fff8000","0x480a80027fff8000","0x48127ff87fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x1104800180018000","0x193e","0x20680017fff7ffd","0x12","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x1104800180018000","0x19c4","0x208b7fff7fff7ffe","0x1104800180018000","0x2264","0x482480017fff8000","0x2263","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x5b36","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff27fff8000","0x48127ff27fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x2252","0x482480017fff8000","0x2251","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xbab8","0x40780017fff7fff","0x1","0x480680017fff8000","0x5245504c4143455f434c4153535f484153485f4641494c4544","0x400080007ffe7fff","0x48127fec7fff8000","0x48307ffc7ff18000","0x480a80017fff8000","0x480a80027fff8000","0x48127fef7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x4802800680038000","0x1104800180018000","0x2237","0x482480017fff8000","0x2236","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x12214","0x48307fff7ff88000","0x4826800180038000","0xa","0x4802800880038000","0x4802800980038000","0x10780017fff7fff","0x12","0x40780017fff7fff","0x4","0x1104800180018000","0x2224","0x482480017fff8000","0x2223","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x14d48","0x48307fff7fef8000","0x480a80037fff8000","0x48127ff17fff8000","0x48127ff17fff8000","0x48127fea7fff8000","0x48127ffb7fff8000","0x480a80017fff8000","0x480a80027fff8000","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x480080057ff88000","0x1104800180018000","0x220b","0x482480017fff8000","0x220a","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x17354","0x48127fe57fff8000","0x48307ffe7ff78000","0x480a80017fff8000","0x480a80027fff8000","0x482480017fec8000","0x9","0x480680017fff8000","0x1","0x480080077fea8000","0x480080087fe98000","0x208b7fff7fff7ffe","0x4802800680048000","0x1104800180018000","0x21f5","0x482480017fff8000","0x21f4","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x19eb0","0x48307fff7ff88000","0x4826800180048000","0xa","0x4802800880048000","0x4802800980048000","0x10780017fff7fff","0x12","0x40780017fff7fff","0x4","0x1104800180018000","0x21e2","0x482480017fff8000","0x21e1","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1c9e4","0x48307fff7fef8000","0x480a80047fff8000","0x48127ff17fff8000","0x48127ff17fff8000","0x48127fea7fff8000","0x48127ffb7fff8000","0x480a80017fff8000","0x480a80027fff8000","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x21ca","0x482480017fff8000","0x21c9","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1eda2","0x40780017fff7fff","0x1","0x480680017fff8000","0x494d504c454d454e544154494f4e5f45585049524544","0x400080007ffe7fff","0x482480017fe78000","0x2","0x48307ffc7ff28000","0x480a80017fff8000","0x480a80027fff8000","0x480a80047fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x21af","0x482480017fff8000","0x21ae","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1ef78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4e4f545f454e41424c45445f594554","0x400080007ffe7fff","0x482480017fea8000","0x1","0x48307ffc7ff28000","0x480a80017fff8000","0x480a80027fff8000","0x480a80047fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x2194","0x482480017fff8000","0x2193","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1f40a","0x48127ff17fff8000","0x48307ffe7ff18000","0x480a80017fff8000","0x480a80027fff8000","0x480a80047fff8000","0x480680017fff8000","0x1","0x48127ff17fff8000","0x48127ff17fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x2180","0x482480017fff8000","0x217f","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x3","0x482480017fff8000","0x25cce","0x48127ff17fff8000","0x48307ffe7ff18000","0x48127ff17fff8000","0x48127ff17fff8000","0x48127ff17fff8000","0x480680017fff8000","0x1","0x48127ff17fff8000","0x48127ff17fff8000","0x208b7fff7fff7ffe","0x480080027ff78000","0x1104800180018000","0x216b","0x482480017fff8000","0x216a","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x2c600","0x48127fe17fff8000","0x48307ffe7ff78000","0x48127fe17fff8000","0x480a7ff67fff8000","0x482480017feb8000","0x6","0x480680017fff8000","0x1","0x480080047fe98000","0x480080057fe88000","0x208b7fff7fff7ffe","0x1104800180018000","0x2156","0x482480017fff8000","0x2155","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x2eea0","0x40780017fff7fff","0x1","0x480680017fff8000","0x46494e414c495a4544","0x400080007ffe7fff","0x48127fe37fff8000","0x48307ffc7ff28000","0x48127fe37fff8000","0x480a7ff67fff8000","0x48127fed7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x480080047ff78000","0x1104800180018000","0x213b","0x482480017fff8000","0x213a","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x2f3b4","0x48127fec7fff8000","0x48307ffe7ff78000","0x48127fec7fff8000","0x480a7ff67fff8000","0x482480017feb8000","0x8","0x480680017fff8000","0x1","0x480080067fe98000","0x480080077fe88000","0x208b7fff7fff7ffe","0x1104800180018000","0x2126","0x482480017fff8000","0x2125","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x31f10","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x480a7ff67fff8000","0x48127ff17fff8000","0x480680017fff8000","0x1","0x48127ff17fff8000","0x48127ff17fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x400280007ff37fff","0x400380017ff37ff5","0x480280027ff38000","0x400280037ff37fff","0x400380047ff37ff6","0x480280057ff38000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff17ffc","0x480280017ff17ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff17ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff17ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff17ffd","0x400280027ff17ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ff38000","0x6","0x482680017ff18000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ff47fff","0x400380017ff47ff2","0x400280027ff47ffc","0x400280037ff47ffb","0x480280057ff48000","0x20680017fff7fff","0x10a","0x480280047ff48000","0x480280067ff48000","0x482680017ff48000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x48307ffe80007fff","0x20680017fff7fff","0xe0","0x48127ffc7fff8000","0x20780017fff7ff6","0x1b","0x1104800180018000","0x20c4","0x482480017fff8000","0x20c3","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x7","0x482480017fff8000","0x1d100","0x40780017fff7fff","0x1","0x480680017fff8000","0x494e56414c49445f4143434f554e545f41444452455353","0x400080007ffe7fff","0x48127feb7fff8000","0x48307ffc7ff58000","0x48127fe87fff8000","0x48127fed7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480680017fff8000","0x2e9f66c6eea14532c94ad25405a4fcb32faa4969559c128d837caa0ec50a655","0x400080007ff27fff","0x400180017ff27ff5","0x480080027ff28000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007fee7ffc","0x480080017fed7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027feb7ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007fee7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017fec7ffd","0x400080027feb7ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff67fff8000","0x480680017fff8000","0x0","0x482480017fe88000","0x3","0x482480017fe88000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400080007feb7fff","0x400080017feb7ffb","0x400080027feb7ffc","0x400080037feb7ffa","0x480080057feb8000","0x20680017fff7fff","0x77","0x480080047fea8000","0x48127ffc7fff8000","0x48127ffe7fff8000","0x48127ff97fff8000","0x482480017fe68000","0x7","0x480080067fe58000","0x1104800180018000","0x1a93","0x20680017fff7ffd","0x5a","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480a7ff57fff8000","0x480a7ff67fff8000","0x1104800180018000","0x1b1f","0x40137ffb7fff8001","0x40137ffc7fff8000","0x20680017fff7ffd","0x45","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff77fff8000","0x48127ff77fff8000","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x1104800180018000","0x1845","0x20680017fff7ffb","0x26","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0xf","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x4c36","0x480a80017fff8000","0x480a80007fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x26","0x1104800180018000","0x2016","0x482480017fff8000","0x2015","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x13510","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x48127ff27fff8000","0x48127ff37fff8000","0x48127ff37fff8000","0x10780017fff7fff","0x14","0x480080047fea8000","0x1104800180018000","0x2003","0x482480017fff8000","0x2002","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x6","0x482480017fff8000","0x19dca","0x48127ff57fff8000","0x48307ffe7ff78000","0x48127ff27fff8000","0x482480017fdf8000","0x8","0x480080067fde8000","0x480080077fdd8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x1fe9","0x482480017fff8000","0x1fe8","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x7","0x482480017fff8000","0x1d2f4","0x48127fee7fff8000","0x48307ffe7ff48000","0x48127feb7fff8000","0x48127ff07fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x480280047ff48000","0x1104800180018000","0x1fd3","0x482480017fff8000","0x1fd2","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x7","0x482480017fff8000","0x1d6dc","0x48127ff57fff8000","0x48307ffe7ff78000","0x48127ff27fff8000","0x482680017ff48000","0x8","0x480680017fff8000","0x1","0x480280067ff48000","0x480280077ff48000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x400280007ff37fff","0x400380017ff37ff5","0x480280027ff38000","0x400280037ff37fff","0x400380047ff37ff6","0x480280057ff38000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff17ffc","0x480280017ff17ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff17ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff17ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff17ffd","0x400280027ff17ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ff38000","0x6","0x482680017ff18000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ff47fff","0x400380017ff47ff2","0x400280027ff47ffc","0x400280037ff47ffb","0x480280057ff48000","0x20680017fff7fff","0xee","0x480280047ff48000","0x480280067ff48000","0x482680017ff48000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x48307ffe80007fff","0x20680017fff7fff","0x17","0x1104800180018000","0x1f74","0x482480017fff8000","0x1f73","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x7","0x482480017fff8000","0x1d22c","0x48127fee7fff8000","0x48307ffe7ff48000","0x48127feb7fff8000","0x48127ff07fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x480680017fff8000","0x2e9f66c6eea14532c94ad25405a4fcb32faa4969559c128d837caa0ec50a655","0x400080007ff37fff","0x400180017ff37ff5","0x480080027ff38000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007fef7ffc","0x480080017fee7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027fec7ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007fef7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017fed7ffd","0x400080027fec7ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff37fff8000","0x480680017fff8000","0x0","0x482480017fe98000","0x3","0x482480017fe98000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400080007fec7fff","0x400080017fec7ffb","0x400080027fec7ffc","0x400080037fec7ffa","0x480080057fec8000","0x20680017fff7fff","0x77","0x480080047feb8000","0x48127ffc7fff8000","0x48127ffe7fff8000","0x48127ff97fff8000","0x482480017fe78000","0x7","0x480080067fe68000","0x1104800180018000","0x1947","0x20680017fff7ffd","0x5a","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480a7ff57fff8000","0x480a7ff67fff8000","0x1104800180018000","0x1aec","0x40137ffb7fff8001","0x40137ffc7fff8000","0x20680017fff7ffd","0x45","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff77fff8000","0x48127ff77fff8000","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x1104800180018000","0x16f9","0x20680017fff7ffb","0x26","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0xf","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x4c36","0x480a80017fff8000","0x480a80007fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x26","0x1104800180018000","0x1eca","0x482480017fff8000","0x1ec9","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x13510","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x48127ff27fff8000","0x48127ff37fff8000","0x48127ff37fff8000","0x10780017fff7fff","0x14","0x480080047feb8000","0x1104800180018000","0x1eb7","0x482480017fff8000","0x1eb6","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x6","0x482480017fff8000","0x19dca","0x48127ff57fff8000","0x48307ffe7ff78000","0x48127ff27fff8000","0x482480017fe08000","0x8","0x480080067fdf8000","0x480080077fde8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480280047ff48000","0x1104800180018000","0x1e9c","0x482480017fff8000","0x1e9b","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x7","0x482480017fff8000","0x1d614","0x48127ff57fff8000","0x48307ffe7ff78000","0x48127ff27fff8000","0x482680017ff48000","0x8","0x480680017fff8000","0x1","0x480280067ff48000","0x480280077ff48000","0x208b7fff7fff7ffe","0x4825800180007ffd","0x3711c9d994faf6055172091cb841fd4831aa743e6f3315163b06a122c841846","0x20680017fff7fff","0x1b","0x1104800180018000","0x1e84","0x482480017fff8000","0x1e83","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x13c22","0x40780017fff7fff","0x1","0x480680017fff8000","0x474f565f41444d494e5f43414e4e4f545f53454c465f52454d4f5645","0x400080007ffe7fff","0x480a7ff97fff8000","0x48327ffc7ffa8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480a7ffa7fff8000","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffc7fff","0x400280017ffc7ffe","0x480280037ffc8000","0x20680017fff7fff","0x51","0x480280027ffc8000","0x480280047ffc8000","0x48127ffe7fff8000","0x480080007ffe8000","0x480080017ffd8000","0x480080027ffc8000","0x480080037ffb8000","0x480080047ffa8000","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280057ffc7fff","0x400280067ffc7ff9","0x480280087ffc8000","0x20680017fff7fff","0x2d","0x480280077ffc8000","0x480280097ffc8000","0x480080027fff8000","0x48307ff880007fff","0x482680017ffc8000","0xa","0x48127ffb7fff8000","0x20680017fff7ffd","0xb","0x480a7ff97fff8000","0x48127ffe7fff8000","0x480a7ffb7fff8000","0x48127ffb7fff8000","0x480a7ffd7fff8000","0x48127ff07fff8000","0x1104800180018000","0x1a15","0x208b7fff7fff7ffe","0x1104800180018000","0x1e42","0x482480017fff8000","0x1e41","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0xe3da","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f4e4c595f53454c465f43414e5f52454e4f554e4345","0x400080007ffe7fff","0x480a7ff97fff8000","0x48307ffc7ff58000","0x480a7ffb7fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480280077ffc8000","0x1104800180018000","0x1e28","0x482480017fff8000","0x1e27","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0xe75e","0x480a7ff97fff8000","0x48307ffe7ff78000","0x480a7ffb7fff8000","0x482680017ffc8000","0xb","0x480680017fff8000","0x1","0x480280097ffc8000","0x4802800a7ffc8000","0x208b7fff7fff7ffe","0x480280027ffc8000","0x1104800180018000","0x1e13","0x482480017fff8000","0x1e12","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x11382","0x480a7ff97fff8000","0x48307ffe7ff78000","0x480a7ffb7fff8000","0x482680017ffc8000","0x6","0x480680017fff8000","0x1","0x480280047ffc8000","0x480280057ffc8000","0x208b7fff7fff7ffe","0xa0680017fff8005","0xe","0x4825800180057ffd","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ffa7ffc","0x480280017ffa7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ffa7ffc","0x10780017fff7fff","0x11","0x480a7ffd7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ffa7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ffa7ffd","0x400280027ffa7ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ffa8000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffc7fff","0x400380017ffc7ffb","0x400280027ffc7ffd","0x400280037ffc7ffc","0x480280057ffc8000","0x20680017fff7fff","0x87","0x480280047ffc8000","0x480280067ffc8000","0x482680017ffc8000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x57","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff48000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0x38","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x11","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x40780017fff7fff","0xc","0x482480017fec8000","0x1","0x482480017ff18000","0x6b8","0x48127fef7fff8000","0x480680017fff8000","0x0","0x48127fe17fff8000","0x48127feb7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017ff18000","0x3","0x48127ff67fff8000","0x48127ff47fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x1d","0x40780017fff7fff","0xb","0x480080047fec8000","0x48127ff17fff8000","0x482480017ffe8000","0x6a4","0x482480017fe98000","0x8","0x480080067fe88000","0x480080077fe78000","0x10780017fff7fff","0x23","0x40780017fff7fff","0xb","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fe68000","0x3","0x482480017feb8000","0x2d8c","0x48127fe97fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x16","0x480280047ffc8000","0x48127fe67fff8000","0x482480017ffe8000","0x3494","0x482680017ffc8000","0x8","0x480280067ffc8000","0x480280077ffc8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x400380007ffa7ffc","0x400380017ffa7ffd","0x480280027ffa8000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff87ffc","0x480280017ff87ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff87ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff87ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff87ffd","0x400280027ff87ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ffa8000","0x3","0x482680017ff88000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400380017ffb7ff9","0x400280027ffb7ffc","0x400280037ffb7ffb","0x480280057ffb8000","0x20680017fff7fff","0x89","0x480280047ffb8000","0x480280067ffb8000","0x482680017ffb8000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x58","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff38000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0x39","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x12","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x40780017fff7fff","0xc","0x482480017fec8000","0x1","0x482480017ff18000","0x6b8","0x48127fde7fff8000","0x48127fee7fff8000","0x480680017fff8000","0x0","0x48127fe07fff8000","0x48127fea7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017ff18000","0x3","0x48127ff67fff8000","0x48127ff47fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x1d","0x40780017fff7fff","0xb","0x480080047fec8000","0x48127ff17fff8000","0x482480017ffe8000","0x6a4","0x482480017fe98000","0x8","0x480080067fe88000","0x480080077fe78000","0x10780017fff7fff","0x24","0x40780017fff7fff","0xb","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fe68000","0x3","0x482480017feb8000","0x2d8c","0x48127fe97fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fde7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x16","0x480280047ffb8000","0x48127fe67fff8000","0x482480017ffe8000","0x3494","0x482680017ffb8000","0x8","0x480280067ffb8000","0x480280077ffb8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fde7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x400380007ff97ffb","0x400380017ff97ffc","0x480280027ff98000","0x400280037ff97fff","0x400380047ff97ffd","0x480280057ff98000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff77ffc","0x480280017ff77ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff77ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff77ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff77ffd","0x400280027ff77ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ff98000","0x6","0x482680017ff78000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffa7fff","0x400380017ffa7ff8","0x400280027ffa7ffc","0x400280037ffa7ffb","0x480280057ffa8000","0x20680017fff7fff","0x89","0x480280047ffa8000","0x480280067ffa8000","0x482680017ffa8000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x58","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff38000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0x39","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x12","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x40780017fff7fff","0xc","0x482480017fec8000","0x1","0x482480017ff18000","0x6b8","0x48127fde7fff8000","0x48127fee7fff8000","0x480680017fff8000","0x0","0x48127fe07fff8000","0x48127fea7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017ff18000","0x3","0x48127ff67fff8000","0x48127ff47fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x1d","0x40780017fff7fff","0xb","0x480080047fec8000","0x48127ff17fff8000","0x482480017ffe8000","0x6a4","0x482480017fe98000","0x8","0x480080067fe88000","0x480080077fe78000","0x10780017fff7fff","0x24","0x40780017fff7fff","0xb","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fe68000","0x3","0x482480017feb8000","0x2d8c","0x48127fe97fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fde7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x16","0x480280047ffa8000","0x48127fe67fff8000","0x482480017ffe8000","0x3494","0x482680017ffa8000","0x8","0x480280067ffa8000","0x480280077ffa8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fde7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x20780017fff7ffa","0x1b","0x1104800180018000","0x1ba5","0x482480017fff8000","0x1ba4","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1e23a","0x40780017fff7fff","0x1","0x480680017fff8000","0x45524332303a207472616e736665722066726f6d2030","0x400080007ffe7fff","0x480a7ff67fff8000","0x48327ffc7ff78000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480a7ff77fff8000","0x20780017fff7ffb","0x1b","0x1104800180018000","0x1b89","0x482480017fff8000","0x1b88","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1e172","0x40780017fff7fff","0x1","0x480680017fff8000","0x45524332303a207472616e7366657220746f2030","0x400080007ffe7fff","0x480a7ff67fff8000","0x48307ffc7ff58000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x400280007ff87fff","0x400380017ff87ffa","0x480280027ff88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff67ffc","0x480280017ff67ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff67ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff67ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff67ffd","0x400280027ff67ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff67fff8000","0x480680017fff8000","0x0","0x482680017ff88000","0x3","0x482680017ff68000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ff97fff","0x400280017ff97ffb","0x400280027ff97ffc","0x400280037ff97ffa","0x480280057ff98000","0x20680017fff7fff","0x354","0x480280047ff98000","0x480280067ff98000","0x482680017ff98000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x321","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff28000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0x2f9","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x2c8","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x48287ffd80017ffb","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0xd","0x400080017ff57fff","0x40780017fff7fff","0x1","0x482480017ff48000","0x2","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff48000","0x2","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc80017fe9","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe80017ff9","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0x26e","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0x258","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x400080007fd87fff","0x400180017fd87ffa","0x480080027fd88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff67ffc","0x480080017ff57ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027ff37ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff67ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff47ffd","0x400080027ff37ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff47fff8000","0x480680017fff8000","0x0","0x482480017fce8000","0x3","0x482480017ff08000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400080007fdc7fff","0x400080017fdc7ffb","0x400080027fdc7ffc","0x400080037fdc7ffa","0x400080047fdc7ff0","0x480080067fdc8000","0x20680017fff7fff","0x20a","0x480080057fdb8000","0x48127fff7fff8000","0x480680017fff8000","0x0","0x482480017ff68000","0x1","0x480680017fff8000","0x53746f726167655772697465","0x400080077fd67fff","0x400080087fd67ffc","0x400080097fd67ffd","0x4000800a7fd67ffe","0x4000800b7fd67feb","0x4800800d7fd68000","0x20680017fff7fff","0x1e8","0x4800800c7fd58000","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x400080007ff47fff","0x400180017ff47ffb","0x480080027ff48000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff07ffc","0x480080017fef7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027fed7ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff07ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017fee7ffd","0x400080027fed7ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff67fff8000","0x480680017fff8000","0x0","0x482480017fea8000","0x3","0x482480017fea8000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x4000800e7fc67fff","0x4000800f7fc67ffb","0x400080107fc67ffc","0x400080117fc67ffa","0x480080137fc68000","0x20680017fff7fff","0x19b","0x480080127fc58000","0x480080147fc48000","0x482480017fc38000","0x15","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x16a","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff28000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0x144","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x115","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x48287ffd7ffb8001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0xd","0x400080017ff57fff","0x40780017fff7fff","0x1","0x482480017ff48000","0x2","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff48000","0x2","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc7fe98001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe7ff98001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0xbd","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0xa9","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x400080007fd87fff","0x400180017fd87ffb","0x480080027fd88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff67ffc","0x480080017ff57ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027ff37ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff67ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff47ffd","0x400080027ff37ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff47fff8000","0x480680017fff8000","0x0","0x402580017fce8001","0x3","0x482480017ff18000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400080007fdd7fff","0x400080017fdd7ffc","0x400080027fdd7ffd","0x400080037fdd7ffb","0x400080047fdd7ff1","0x480080067fdd8000","0x20680017fff7fff","0x64","0x480080057fdc8000","0x48127fff7fff8000","0x480680017fff8000","0x0","0x482480017ff78000","0x1","0x480680017fff8000","0x53746f726167655772697465","0x400080077fd77fff","0x400080087fd77ffc","0x400080097fd77ffd","0x4000800a7fd77ffe","0x4000800b7fd77fec","0x4800800d7fd78000","0x20680017fff7fff","0x4b","0x4800800c7fd68000","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff47fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x19","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x402580017fc68000","0xe","0x1104800180018000","0x1150","0x20680017fff7ffb","0x26","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0xf","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x4800800c7fd68000","0x482480017fff8000","0x4d58","0x482480017fd48000","0x10","0x4800800e7fd38000","0x4800800f7fd28000","0x10780017fff7fff","0xb","0x40780017fff7fff","0x6","0x480080057fd68000","0x482480017fff8000","0x78dc","0x482480017fd48000","0x9","0x480080077fd38000","0x480080087fd28000","0x48127ff27fff8000","0x48127ffb7fff8000","0x480a80017fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x190f","0x482480017fff8000","0x190e","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xa8c0","0x48127ff67fff8000","0x48307ffe7ff68000","0x10780017fff7fff","0xf","0x40780017fff7fff","0x3","0x1104800180018000","0x1901","0x482480017fff8000","0x1900","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xa9ce","0x482480017feb8000","0x2","0x48307ffe7ff28000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f616464204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fcd7fff8000","0x48127fdd7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x18e7","0x482480017fff8000","0x18e6","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xadc0","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017feb8000","0x3","0x48307ffc7ff08000","0x48127fee7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x2b","0x40780017fff7fff","0xb","0x480080047fec8000","0x1104800180018000","0x18ce","0x482480017fff8000","0x18cd","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xb4c8","0x48127feb7fff8000","0x48307ffe7ff88000","0x482480017fe38000","0x8","0x480080067fe28000","0x480080077fe18000","0x10780017fff7fff","0x2b","0x40780017fff7fff","0xb","0x1104800180018000","0x18bc","0x482480017fff8000","0x18bb","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xdb4c","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fe08000","0x3","0x48307ffc7fe58000","0x48127fe37fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x13","0x40780017fff7fff","0x16","0x480080127faf8000","0x1104800180018000","0x18a3","0x482480017fff8000","0x18a2","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xe2b8","0x48127fe07fff8000","0x48307ffe7ff88000","0x482480017fa68000","0x16","0x480080147fa58000","0x480080157fa48000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fd87fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x4800800c7fd58000","0x1104800180018000","0x188b","0x482480017fff8000","0x188a","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1159e","0x48307fff7ff88000","0x482480017fcc8000","0x10","0x4800800e7fcb8000","0x4800800f7fca8000","0x10780017fff7fff","0x14","0x40780017fff7fff","0x6","0x480080057fd58000","0x1104800180018000","0x1877","0x482480017fff8000","0x1876","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x14122","0x48307fff7ff88000","0x482480017fcc8000","0x9","0x480080077fcb8000","0x480080087fca8000","0x48127feb7fff8000","0x48127ffb7fff8000","0x48127fe87fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x185f","0x482480017fff8000","0x185e","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x3","0x482480017fff8000","0x17368","0x48127ff57fff8000","0x48307ffe7ff58000","0x10780017fff7fff","0x11","0x40780017fff7fff","0x3","0x1104800180018000","0x184f","0x482480017fff8000","0x184e","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x3","0x482480017fff8000","0x17476","0x482480017fea8000","0x2","0x48307ffe7ff18000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f737562204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fcc7fff8000","0x48127fdc7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x1833","0x482480017fff8000","0x1832","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x3","0x482480017fff8000","0x17868","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fea8000","0x3","0x48307ffc7fef8000","0x48127fed7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x2f","0x40780017fff7fff","0xb","0x480080047fec8000","0x1104800180018000","0x1818","0x482480017fff8000","0x1817","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x3","0x482480017fff8000","0x17f70","0x48127fea7fff8000","0x48307ffe7ff78000","0x482480017fe28000","0x8","0x480080067fe18000","0x480080077fe08000","0x10780017fff7fff","0x2f","0x40780017fff7fff","0xb","0x1104800180018000","0x1804","0x482480017fff8000","0x1803","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x3","0x482480017fff8000","0x1a5f4","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fdf8000","0x3","0x48307ffc7fe48000","0x48127fe27fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x15","0x40780017fff7fff","0x16","0x480280047ff98000","0x1104800180018000","0x17e9","0x482480017fff8000","0x17e8","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x3","0x482480017fff8000","0x1ad60","0x48127fdf7fff8000","0x48307ffe7ff78000","0x482680017ff98000","0x8","0x480280067ff98000","0x480280077ff98000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fd77fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x3c87bf42ed4f01f11883bf54f43d91d2cbbd5fec26d1df9c74c57ae138800a4","0x400280007ff87fff","0x400380017ff87ffa","0x480280027ff88000","0x400280037ff87fff","0x400380047ff87ffb","0x480280057ff88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff67ffc","0x480280017ff67ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff67ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff67ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff67ffd","0x400280027ff67ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ff88000","0x6","0x482680017ff68000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ff97fff","0x400380017ff97ff7","0x400280027ff97ffc","0x400280037ff97ffb","0x480280057ff98000","0x20680017fff7fff","0x138","0x480280047ff98000","0x480280067ff98000","0x482680017ff98000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x105","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff38000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0xdd","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0xac","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ff17fff8000","0x48127ffb7fff8000","0x482480017ff68000","0x1","0x48127ffb7fff8000","0x48127fed7fff8000","0x48127ff77fff8000","0x4824800180007ffa","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x4","0x10780017fff7fff","0x8","0x40780017fff7fff","0x2","0x482480017ffa8000","0xb4","0x10780017fff7fff","0xa","0x48127ffc7fff8000","0x4824800180007ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x4","0x10780017fff7fff","0x7a","0x48127ffe7fff8000","0x48287ffd80017ffb","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff57fff","0x10780017fff7fff","0xd","0x400080007ff67fff","0x40780017fff7fff","0x1","0x482480017ff58000","0x1","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff58000","0x1","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc80017ff3","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe80017ff9","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0x23","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0xd","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fce7fff8000","0x48127fde7fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x1104800180018000","0xa7","0x208b7fff7fff7ffe","0x1104800180018000","0x16ee","0x482480017fff8000","0x16ed","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xaf14","0x48127ff57fff8000","0x48307ffe7ff58000","0x10780017fff7fff","0x11","0x40780017fff7fff","0x3","0x1104800180018000","0x16de","0x482480017fff8000","0x16dd","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb022","0x482480017fea8000","0x2","0x48307ffe7ff18000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f737562204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fc37fff8000","0x48127fd37fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x16c2","0x482480017fff8000","0x16c1","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xbba8","0x48127ff27fff8000","0x48307ffe7ff68000","0x48127fda7fff8000","0x48127fea7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x1104800180018000","0x16ad","0x482480017fff8000","0x16ac","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb8c4","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fea8000","0x3","0x48307ffc7fef8000","0x48127fed7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x2f","0x40780017fff7fff","0xb","0x480080047fec8000","0x1104800180018000","0x1692","0x482480017fff8000","0x1691","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xbfcc","0x48127fea7fff8000","0x48307ffe7ff78000","0x482480017fe28000","0x8","0x480080067fe18000","0x480080077fe08000","0x10780017fff7fff","0x2f","0x40780017fff7fff","0xb","0x1104800180018000","0x167e","0x482480017fff8000","0x167d","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xe650","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fdf8000","0x3","0x48307ffc7fe48000","0x48127fe27fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x15","0x40780017fff7fff","0x16","0x480280047ff98000","0x1104800180018000","0x1663","0x482480017fff8000","0x1662","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xedbc","0x48127fdf7fff8000","0x48307ffe7ff78000","0x482680017ff98000","0x8","0x480280067ff98000","0x480280077ff98000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fd77fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x20780017fff7ffa","0x1b","0x1104800180018000","0x1646","0x482480017fff8000","0x1645","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xab7c","0x40780017fff7fff","0x1","0x480680017fff8000","0x45524332303a20617070726f76652066726f6d2030","0x400080007ffe7fff","0x480a7ff67fff8000","0x48327ffc7ff78000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480a7ff77fff8000","0x20780017fff7ffb","0x1b","0x1104800180018000","0x162a","0x482480017fff8000","0x1629","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xaab4","0x40780017fff7fff","0x1","0x480680017fff8000","0x45524332303a20617070726f766520746f2030","0x400080007ffe7fff","0x480a7ff67fff8000","0x48307ffc7ff58000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480680017fff8000","0x3c87bf42ed4f01f11883bf54f43d91d2cbbd5fec26d1df9c74c57ae138800a4","0x400280007ff87fff","0x400380017ff87ffa","0x480280027ff88000","0x400280037ff87fff","0x400380047ff87ffb","0x480280057ff88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff67ffc","0x480280017ff67ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff67ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff67ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff67ffd","0x400280027ff67ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x402780017ff88001","0x6","0x482680017ff68000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400280007ff97fff","0x400280017ff97ffc","0x400280027ff97ffd","0x400280037ff97ffb","0x400380047ff97ffc","0x480280067ff98000","0x20680017fff7fff","0x64","0x480280057ff98000","0x48127fff7fff8000","0x480680017fff8000","0x0","0x482480017ff78000","0x1","0x480680017fff8000","0x53746f726167655772697465","0x400280077ff97fff","0x400280087ff97ffc","0x400280097ff97ffd","0x4002800a7ff97ffe","0x4003800b7ff97ffd","0x4802800d7ff98000","0x20680017fff7fff","0x4b","0x4802800c7ff98000","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff47fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x17","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x402780017ff98000","0xe","0x1104800180018000","0xda8","0x20680017fff7ffb","0x26","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0xf","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x4802800c7ff98000","0x482480017fff8000","0x4d58","0x482680017ff98000","0x10","0x4802800e7ff98000","0x4802800f7ff98000","0x10780017fff7fff","0xb","0x40780017fff7fff","0x6","0x480280057ff98000","0x482480017fff8000","0x78dc","0x482680017ff98000","0x9","0x480280077ff98000","0x480280087ff98000","0x48127ff27fff8000","0x48127ffb7fff8000","0x480a80017fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffa7fff","0x400380017ffa7ff8","0x480280037ffa8000","0x20680017fff7fff","0x172","0x480280027ffa8000","0x480280047ffa8000","0x480080027fff8000","0x480680017fff8000","0x3c87bf42ed4f01f11883bf54f43d91d2cbbd5fec26d1df9c74c57ae138800a4","0x400280007ff97fff","0x400280017ff97ffe","0x480280027ff98000","0x400280037ff97fff","0x400380047ff97ffb","0x480280057ff98000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff77ffc","0x480280017ff77ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff77ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff77ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff77ffd","0x400280027ff77ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff37fff8000","0x480680017fff8000","0x0","0x482680017ff98000","0x6","0x482680017ff78000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280057ffa7fff","0x400280067ffa7ffb","0x400280077ffa7ffc","0x400280087ffa7ffa","0x4802800a7ffa8000","0x20680017fff7fff","0x11e","0x480280097ffa8000","0x4802800b7ffa8000","0x482680017ffa8000","0xc","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0xeb","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff28000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0xc3","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x92","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x48287ffd7ffb8001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0xd","0x400080017ff57fff","0x40780017fff7fff","0x1","0x482480017ff48000","0x2","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff48000","0x2","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc7fe98001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe7ff98001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0x38","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0x22","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fd77fff8000","0x48127fe77fff8000","0x48127fc87fff8000","0x480a7ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffe4e","0x20680017fff7ffd","0xd","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x147f","0x482480017fff8000","0x147e","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb234","0x48127ff57fff8000","0x48307ffe7ff58000","0x10780017fff7fff","0x11","0x40780017fff7fff","0x3","0x1104800180018000","0x146f","0x482480017fff8000","0x146e","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb342","0x482480017fea8000","0x2","0x48307ffe7ff18000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f616464204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fcc7fff8000","0x48127fdc7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x1453","0x482480017fff8000","0x1452","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb734","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fea8000","0x3","0x48307ffc7fef8000","0x48127fed7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x2f","0x40780017fff7fff","0xb","0x480080047fec8000","0x1104800180018000","0x1438","0x482480017fff8000","0x1437","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xbe3c","0x48127fea7fff8000","0x48307ffe7ff78000","0x482480017fe28000","0x8","0x480080067fe18000","0x480080077fe08000","0x10780017fff7fff","0x2f","0x40780017fff7fff","0xb","0x1104800180018000","0x1424","0x482480017fff8000","0x1423","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xe4c0","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fdf8000","0x3","0x48307ffc7fe48000","0x48127fe27fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x15","0x40780017fff7fff","0x16","0x480280097ffa8000","0x1104800180018000","0x1409","0x482480017fff8000","0x1408","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xec2c","0x48127fdf7fff8000","0x48307ffe7ff78000","0x482680017ffa8000","0xd","0x4802800b7ffa8000","0x4802800c7ffa8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fd77fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480280027ffa8000","0x1104800180018000","0x13ef","0x482480017fff8000","0x13ee","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1235e","0x480a7ff77fff8000","0x48307ffe7ff78000","0x480a7ff97fff8000","0x482680017ffa8000","0x6","0x480680017fff8000","0x1","0x480280047ffa8000","0x480280057ffa8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffa7fff","0x400380017ffa7ff8","0x480280037ffa8000","0x20680017fff7fff","0x172","0x480280027ffa8000","0x480280047ffa8000","0x480080027fff8000","0x480680017fff8000","0x3c87bf42ed4f01f11883bf54f43d91d2cbbd5fec26d1df9c74c57ae138800a4","0x400280007ff97fff","0x400280017ff97ffe","0x480280027ff98000","0x400280037ff97fff","0x400380047ff97ffb","0x480280057ff98000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff77ffc","0x480280017ff77ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff77ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff77ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff77ffd","0x400280027ff77ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff37fff8000","0x480680017fff8000","0x0","0x482680017ff98000","0x6","0x482680017ff78000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280057ffa7fff","0x400280067ffa7ffb","0x400280077ffa7ffc","0x400280087ffa7ffa","0x4802800a7ffa8000","0x20680017fff7fff","0x11e","0x480280097ffa8000","0x4802800b7ffa8000","0x482680017ffa8000","0xc","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0xeb","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff28000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0xc3","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x92","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x48287ffd80017ffb","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0xd","0x400080017ff57fff","0x40780017fff7fff","0x1","0x482480017ff48000","0x2","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff48000","0x2","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc80017fe9","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe80017ff9","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0x38","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0x22","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fd77fff8000","0x48127fe77fff8000","0x48127fc87fff8000","0x480a7ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffcc2","0x20680017fff7ffd","0xd","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x12f3","0x482480017fff8000","0x12f2","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb234","0x48127ff57fff8000","0x48307ffe7ff58000","0x10780017fff7fff","0x11","0x40780017fff7fff","0x3","0x1104800180018000","0x12e3","0x482480017fff8000","0x12e2","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb342","0x482480017fea8000","0x2","0x48307ffe7ff18000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f737562204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fcc7fff8000","0x48127fdc7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x12c7","0x482480017fff8000","0x12c6","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb734","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fea8000","0x3","0x48307ffc7fef8000","0x48127fed7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x2f","0x40780017fff7fff","0xb","0x480080047fec8000","0x1104800180018000","0x12ac","0x482480017fff8000","0x12ab","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xbe3c","0x48127fea7fff8000","0x48307ffe7ff78000","0x482480017fe28000","0x8","0x480080067fe18000","0x480080077fe08000","0x10780017fff7fff","0x2f","0x40780017fff7fff","0xb","0x1104800180018000","0x1298","0x482480017fff8000","0x1297","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xe4c0","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fdf8000","0x3","0x48307ffc7fe48000","0x48127fe27fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x15","0x40780017fff7fff","0x16","0x480280097ffa8000","0x1104800180018000","0x127d","0x482480017fff8000","0x127c","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xec2c","0x48127fdf7fff8000","0x48307ffe7ff78000","0x482680017ffa8000","0xd","0x4802800b7ffa8000","0x4802800c7ffa8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fd77fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480280027ffa8000","0x1104800180018000","0x1263","0x482480017fff8000","0x1262","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1235e","0x480a7ff77fff8000","0x48307ffe7ff78000","0x480a7ff97fff8000","0x482680017ffa8000","0x6","0x480680017fff8000","0x1","0x480280047ffa8000","0x480280057ffa8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x0","0x480680017fff8000","0x341c1bdfd89f69748aa00b5742b03adbffd79b8e80cab5c50d91cd8c2a79be1","0x480680017fff8000","0x53746f726167655772697465","0x400280007ff47fff","0x400380017ff47ff2","0x400280027ff47ffd","0x400280037ff47ffe","0x400380047ff47ff5","0x480280067ff48000","0x20680017fff7fff","0xe2","0x480280057ff48000","0x48127fff7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0xb6ce5410fca59d078ee9b2a4371a9d684c530d697c64fbef0ae6d5e8f0ac72","0x480680017fff8000","0x53746f726167655772697465","0x400280077ff47fff","0x400280087ff47ffc","0x400280097ff47ffd","0x4002800a7ff47ffe","0x4003800b7ff47ff6","0x4802800d7ff48000","0x20680017fff7fff","0xc0","0x4802800c7ff48000","0x48127fff7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x1f0d4aa99431d246bac9b8e48c33e888245b15e9678f64f9bdfc8823dc8f979","0x480680017fff8000","0x53746f726167655772697465","0x4002800e7ff47fff","0x4002800f7ff47ffc","0x400280107ff47ffd","0x400280117ff47ffe","0x400380127ff47ff7","0x480280147ff48000","0x20680017fff7fff","0x9e","0x480280137ff48000","0x480a7ff17fff8000","0x48127ffe7fff8000","0x480a7ff37fff8000","0x482680017ff48000","0x15","0x480a7ffa7fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x1104800180018000","0xd0","0x20680017fff7ffd","0x7e","0x48127ffa7fff8000","0x20780017fff7ffb","0x1b","0x1104800180018000","0x1211","0x482480017fff8000","0x1210","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x2ea7c","0x40780017fff7fff","0x1","0x480680017fff8000","0x494e56414c49445f4d494e5445525f41444452455353","0x400080007ffe7fff","0x48127fef7fff8000","0x48307ffc7ff58000","0x48127fef7fff8000","0x48127fef7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48127fff7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x1390569bb0a3a722eb4228e8700301347da081211d5c2ded2db22ef389551ab","0x480680017fff8000","0x53746f726167655772697465","0x400080007ff77fff","0x400080017ff77ffc","0x400080027ff77ffd","0x400080037ff77ffe","0x400180047ff77ffb","0x480080067ff78000","0x20680017fff7fff","0x3e","0x480080057ff68000","0x48127ff27fff8000","0x48127ffe7fff8000","0x48127ff27fff8000","0x482480017ff28000","0x7","0x480a7ffc7fff8000","0x1104800180018000","0xecb","0x20680017fff7ffd","0x29","0x48127ffa7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x3fc801c47df4de8d5835f8bfd4d0b8823ba63e5a3f278086901402d680abfc","0x480680017fff8000","0x53746f726167655772697465","0x400080007ff87fff","0x400080017ff87ffc","0x400080027ff87ffd","0x400080037ff87ffe","0x400180047ff87ffd","0x480080067ff88000","0x20680017fff7fff","0xf","0x480080057ff78000","0x48127ff37fff8000","0x48127ffe7fff8000","0x48127ff37fff8000","0x482480017ff38000","0x7","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x480080057ff78000","0x48127ff37fff8000","0x48127ffe7fff8000","0x48127ff37fff8000","0x482480017ff38000","0x9","0x480680017fff8000","0x1","0x480080077ff18000","0x480080087ff08000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2bc0","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480080057ff68000","0x1104800180018000","0x11ac","0x482480017fff8000","0x11ab","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x2bfe8","0x48127feb7fff8000","0x48307ffe7ff78000","0x48127feb7fff8000","0x482480017feb8000","0x9","0x480680017fff8000","0x1","0x480080077fe98000","0x480080087fe88000","0x208b7fff7fff7ffe","0x1104800180018000","0x1198","0x482480017fff8000","0x1197","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x2ec70","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff27fff8000","0x48127ff27fff8000","0x208b7fff7fff7ffe","0x480280137ff48000","0x1104800180018000","0x1184","0x482480017fff8000","0x1183","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0xb","0x482480017fff8000","0x4db5c","0x48307fff7ff88000","0x482680017ff48000","0x17","0x480280157ff48000","0x480280167ff48000","0x10780017fff7fff","0x24","0x4802800c7ff48000","0x1104800180018000","0x1172","0x482480017fff8000","0x1171","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0xb","0x482480017fff8000","0x5071c","0x48307fff7ff88000","0x482680017ff48000","0x10","0x4802800e7ff48000","0x4802800f7ff48000","0x10780017fff7fff","0x12","0x480280057ff48000","0x1104800180018000","0x1160","0x482480017fff8000","0x115f","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0xb","0x482480017fff8000","0x53340","0x48307fff7ff88000","0x482680017ff48000","0x9","0x480280077ff48000","0x480280087ff48000","0x480a7ff17fff8000","0x48127ffb7fff8000","0x480a7ff37fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x20780017fff7ffb","0x1b","0x1104800180018000","0x1144","0x482480017fff8000","0x1143","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1e578","0x40780017fff7fff","0x1","0x480680017fff8000","0x45524332303a206d696e7420746f2030","0x400080007ffe7fff","0x480a7ff77fff8000","0x48327ffc7ff88000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ffa7fff8000","0x480680017fff8000","0x110e2f729c9c2b988559994a3daccd838cf52faf88e18101373e67dd061455a","0x1104800180018000","0xedb","0x20680017fff7ffd","0x2a4","0x48127ffb7fff8000","0x48287ffd7ffe8001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080007ff67fff","0x10780017fff7fff","0xd","0x400080007ff77fff","0x40780017fff7fff","0x1","0x482480017ff68000","0x1","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff68000","0x1","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc7ff68001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe7ff98001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0x24d","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0x237","0x48127ffd7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x110e2f729c9c2b988559994a3daccd838cf52faf88e18101373e67dd061455a","0x480680017fff8000","0x53746f726167655772697465","0x400080007fe57fff","0x400080017fe57ffc","0x400080027fe57ffd","0x400080037fe57ffe","0x400080047fe57ffa","0x480080067fe58000","0x20680017fff7fff","0x20d","0x480080057fe48000","0x480680017fff8000","0x110e2f729c9c2b988559994a3daccd838cf52faf88e18101373e67dd061455a","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ffd8000","0x1","0x480680017fff8000","0x53746f726167655772697465","0x400080077fde7fff","0x400080087fde7ffc","0x400080097fde7ffd","0x4000800a7fde7ffe","0x4000800b7fde7ff4","0x4800800d7fde8000","0x20680017fff7fff","0x1e9","0x4800800c7fdd8000","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x400280007ff97fff","0x400380017ff97ffb","0x480280027ff98000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007fe97ffc","0x480080017fe87ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027fe67ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007fe97ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017fe77ffd","0x400080027fe67ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff67fff8000","0x480680017fff8000","0x0","0x482680017ff98000","0x3","0x482480017fe38000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x4000800e7fce7fff","0x4000800f7fce7ffb","0x400080107fce7ffc","0x400080117fce7ffa","0x480080137fce8000","0x20680017fff7fff","0x19c","0x480080127fcd8000","0x480080147fcc8000","0x482480017fcb8000","0x15","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x16b","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff28000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0x145","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x116","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x48287ffd7ffb8001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0xd","0x400080017ff57fff","0x40780017fff7fff","0x1","0x482480017ff48000","0x2","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff48000","0x2","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc7fe98001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe7ff98001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0xbe","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0xaa","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x400080007fd87fff","0x400180017fd87ffb","0x480080027fd88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff67ffc","0x480080017ff57ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027ff37ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff67ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff47ffd","0x400080027ff37ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff47fff8000","0x480680017fff8000","0x0","0x402580017fce8001","0x3","0x482480017ff18000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400080007fdd7fff","0x400080017fdd7ffc","0x400080027fdd7ffd","0x400080037fdd7ffb","0x400080047fdd7ff1","0x480080067fdd8000","0x20680017fff7fff","0x65","0x480080057fdc8000","0x48127fff7fff8000","0x480680017fff8000","0x0","0x482480017ff78000","0x1","0x480680017fff8000","0x53746f726167655772697465","0x400080077fd77fff","0x400080087fd77ffc","0x400080097fd77ffd","0x4000800a7fd77ffe","0x4000800b7fd77fec","0x4800800d7fd78000","0x20680017fff7fff","0x4c","0x4800800c7fd68000","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff47fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x19","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x402580017fc68000","0xe","0x1104800180018000","0x79d","0x20680017fff7ffb","0x26","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0xf","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x4800800c7fd68000","0x482480017fff8000","0x4d58","0x482480017fd48000","0x10","0x4800800e7fd38000","0x4800800f7fd28000","0x10780017fff7fff","0xb","0x40780017fff7fff","0x6","0x480080057fd68000","0x482480017fff8000","0x78dc","0x482480017fd48000","0x9","0x480080077fd38000","0x480080087fd28000","0x48127ff27fff8000","0x48127ffb7fff8000","0x480a80017fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0xf5c","0x482480017fff8000","0xf5b","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xa8c0","0x48127ff67fff8000","0x48307ffe7ff68000","0x10780017fff7fff","0xf","0x40780017fff7fff","0x3","0x1104800180018000","0xf4e","0x482480017fff8000","0xf4d","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xa9ce","0x482480017feb8000","0x2","0x48307ffe7ff28000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f616464204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fcd7fff8000","0x48127fdd7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0xf34","0x482480017fff8000","0xf33","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xadc0","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017feb8000","0x3","0x48307ffc7ff08000","0x48127fee7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x2b","0x40780017fff7fff","0xb","0x480080047fec8000","0x1104800180018000","0xf1b","0x482480017fff8000","0xf1a","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xb4c8","0x48127feb7fff8000","0x48307ffe7ff88000","0x482480017fe38000","0x8","0x480080067fe28000","0x480080077fe18000","0x10780017fff7fff","0x2b","0x40780017fff7fff","0xb","0x1104800180018000","0xf09","0x482480017fff8000","0xf08","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xdb4c","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fe08000","0x3","0x48307ffc7fe58000","0x48127fe37fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x13","0x40780017fff7fff","0x16","0x480080127fb78000","0x1104800180018000","0xef0","0x482480017fff8000","0xeef","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xe2b8","0x48127fe07fff8000","0x48307ffe7ff88000","0x482480017fae8000","0x16","0x480080147fad8000","0x480080157fac8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fd87fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x4800800c7fdd8000","0x1104800180018000","0xed8","0x482480017fff8000","0xed7","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1159e","0x48307fff7ff88000","0x482480017fd48000","0x10","0x4800800e7fd38000","0x4800800f7fd28000","0x10780017fff7fff","0x14","0x40780017fff7fff","0x7","0x480080057fdd8000","0x1104800180018000","0xec4","0x482480017fff8000","0xec3","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1417c","0x48307fff7ff88000","0x482480017fd48000","0x9","0x480080077fd38000","0x480080087fd28000","0x48127fe47fff8000","0x48127ffb7fff8000","0x480a7ff97fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0xeac","0x482480017fff8000","0xeab","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x16d1e","0x48127ff57fff8000","0x48307ffe7ff58000","0x10780017fff7fff","0x11","0x40780017fff7fff","0x3","0x1104800180018000","0xe9c","0x482480017fff8000","0xe9b","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x16e2c","0x482480017fea8000","0x2","0x48307ffe7ff18000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f616464204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x480a7ff97fff8000","0x48127fdb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0xe80","0x482480017fff8000","0xe7f","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x17a16","0x48127ff37fff8000","0x48307ffe7ff38000","0x480a7ff97fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff27fff8000","0x48127ff27fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x20780017fff7ffb","0x1b","0x1104800180018000","0xe69","0x482480017fff8000","0xe68","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1e578","0x40780017fff7fff","0x1","0x480680017fff8000","0x45524332303a206275726e2066726f6d2030","0x400080007ffe7fff","0x480a7ff77fff8000","0x48327ffc7ff88000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ffa7fff8000","0x480680017fff8000","0x110e2f729c9c2b988559994a3daccd838cf52faf88e18101373e67dd061455a","0x1104800180018000","0xc00","0x20680017fff7ffd","0x2a4","0x48127ffb7fff8000","0x48287ffd80017ffe","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff67fff","0x10780017fff7fff","0xd","0x400080007ff77fff","0x40780017fff7fff","0x1","0x482480017ff68000","0x1","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff68000","0x1","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc80017ff6","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe80017ff9","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0x24d","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0x237","0x48127ffd7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x110e2f729c9c2b988559994a3daccd838cf52faf88e18101373e67dd061455a","0x480680017fff8000","0x53746f726167655772697465","0x400080007fe57fff","0x400080017fe57ffc","0x400080027fe57ffd","0x400080037fe57ffe","0x400080047fe57ffa","0x480080067fe58000","0x20680017fff7fff","0x20d","0x480080057fe48000","0x480680017fff8000","0x110e2f729c9c2b988559994a3daccd838cf52faf88e18101373e67dd061455a","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ffd8000","0x1","0x480680017fff8000","0x53746f726167655772697465","0x400080077fde7fff","0x400080087fde7ffc","0x400080097fde7ffd","0x4000800a7fde7ffe","0x4000800b7fde7ff4","0x4800800d7fde8000","0x20680017fff7fff","0x1e9","0x4800800c7fdd8000","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x400280007ff97fff","0x400380017ff97ffb","0x480280027ff98000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007fe97ffc","0x480080017fe87ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027fe67ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007fe97ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017fe77ffd","0x400080027fe67ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff67fff8000","0x480680017fff8000","0x0","0x482680017ff98000","0x3","0x482480017fe38000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x4000800e7fce7fff","0x4000800f7fce7ffb","0x400080107fce7ffc","0x400080117fce7ffa","0x480080137fce8000","0x20680017fff7fff","0x19c","0x480080127fcd8000","0x480080147fcc8000","0x482480017fcb8000","0x15","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x16b","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff28000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0x145","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x116","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x48287ffd80017ffb","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0xd","0x400080017ff57fff","0x40780017fff7fff","0x1","0x482480017ff48000","0x2","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff48000","0x2","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc80017fe9","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe80017ff9","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0xbe","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0xaa","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x400080007fd87fff","0x400180017fd87ffb","0x480080027fd88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff67ffc","0x480080017ff57ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027ff37ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff67ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff47ffd","0x400080027ff37ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff47fff8000","0x480680017fff8000","0x0","0x402580017fce8001","0x3","0x482480017ff18000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400080007fdd7fff","0x400080017fdd7ffc","0x400080027fdd7ffd","0x400080037fdd7ffb","0x400080047fdd7ff1","0x480080067fdd8000","0x20680017fff7fff","0x65","0x480080057fdc8000","0x48127fff7fff8000","0x480680017fff8000","0x0","0x482480017ff78000","0x1","0x480680017fff8000","0x53746f726167655772697465","0x400080077fd77fff","0x400080087fd77ffc","0x400080097fd77ffd","0x4000800a7fd77ffe","0x4000800b7fd77fec","0x4800800d7fd78000","0x20680017fff7fff","0x4c","0x4800800c7fd68000","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff47fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x19","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480a7ffb7fff8000","0x480680017fff8000","0x0","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x402580017fc68000","0xe","0x1104800180018000","0x4c2","0x20680017fff7ffb","0x26","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0xf","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x4800800c7fd68000","0x482480017fff8000","0x4d58","0x482480017fd48000","0x10","0x4800800e7fd38000","0x4800800f7fd28000","0x10780017fff7fff","0xb","0x40780017fff7fff","0x6","0x480080057fd68000","0x482480017fff8000","0x78dc","0x482480017fd48000","0x9","0x480080077fd38000","0x480080087fd28000","0x48127ff27fff8000","0x48127ffb7fff8000","0x480a80017fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0xc81","0x482480017fff8000","0xc80","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xa8c0","0x48127ff67fff8000","0x48307ffe7ff68000","0x10780017fff7fff","0xf","0x40780017fff7fff","0x3","0x1104800180018000","0xc73","0x482480017fff8000","0xc72","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xa9ce","0x482480017feb8000","0x2","0x48307ffe7ff28000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f737562204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fcd7fff8000","0x48127fdd7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0xc59","0x482480017fff8000","0xc58","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xadc0","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017feb8000","0x3","0x48307ffc7ff08000","0x48127fee7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x2b","0x40780017fff7fff","0xb","0x480080047fec8000","0x1104800180018000","0xc40","0x482480017fff8000","0xc3f","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xb4c8","0x48127feb7fff8000","0x48307ffe7ff88000","0x482480017fe38000","0x8","0x480080067fe28000","0x480080077fe18000","0x10780017fff7fff","0x2b","0x40780017fff7fff","0xb","0x1104800180018000","0xc2e","0x482480017fff8000","0xc2d","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xdb4c","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fe08000","0x3","0x48307ffc7fe58000","0x48127fe37fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x13","0x40780017fff7fff","0x16","0x480080127fb78000","0x1104800180018000","0xc15","0x482480017fff8000","0xc14","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xe2b8","0x48127fe07fff8000","0x48307ffe7ff88000","0x482480017fae8000","0x16","0x480080147fad8000","0x480080157fac8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fd87fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x4800800c7fdd8000","0x1104800180018000","0xbfd","0x482480017fff8000","0xbfc","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1159e","0x48307fff7ff88000","0x482480017fd48000","0x10","0x4800800e7fd38000","0x4800800f7fd28000","0x10780017fff7fff","0x14","0x40780017fff7fff","0x7","0x480080057fdd8000","0x1104800180018000","0xbe9","0x482480017fff8000","0xbe8","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1417c","0x48307fff7ff88000","0x482480017fd48000","0x9","0x480080077fd38000","0x480080087fd28000","0x48127fe47fff8000","0x48127ffb7fff8000","0x480a7ff97fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0xbd1","0x482480017fff8000","0xbd0","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x16d1e","0x48127ff57fff8000","0x48307ffe7ff58000","0x10780017fff7fff","0x11","0x40780017fff7fff","0x3","0x1104800180018000","0xbc1","0x482480017fff8000","0xbc0","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x16e2c","0x482480017fea8000","0x2","0x48307ffe7ff18000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f737562204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x480a7ff97fff8000","0x48127fdb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0xba5","0x482480017fff8000","0xba4","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x17a16","0x48127ff37fff8000","0x48307ffe7ff38000","0x480a7ff97fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff27fff8000","0x48127ff27fff8000","0x208b7fff7fff7ffe","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xa","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x8","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xbb","0x20680017fff7fff","0x8a","0x48307ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xa","0x482480017ffb8000","0x1","0x48127ffb7fff8000","0x480680017fff8000","0x0","0x480080007ff88000","0x10780017fff7fff","0x8","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x60","0xa0680017fff8004","0xe","0x4824800180047ffe","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480280007ffb7ffc","0x480280017ffb7ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400280027ffb7ffd","0x10780017fff7fff","0x4c","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffd","0x480280007ffb7ffd","0x480280017ffb7ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400280027ffb7ffe","0x482680017ffb8000","0x3","0x48127ff67fff8000","0x48127ff67fff8000","0x1104800180018000","0x9c2","0x20680017fff7ffa","0x2a","0x20680017fff7ffd","0xb","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x0","0x48127fd27fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0xc","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x20680017fff7ffc","0xc","0x48127ff37fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x10780017fff7fff","0x47","0x40780017fff7fff","0x4","0x48127fef7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x10780017fff7fff","0x1f","0x40780017fff7fff","0xd","0x48127fec7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127fea7fff8000","0x48127fea7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2e","0x482680017ffb8000","0x3","0x10780017fff7fff","0x5","0x40780017fff7fff","0x34","0x480a7ffb7fff8000","0x48127fc77fff8000","0x48127fc77fff8000","0x48127ffd7fff8000","0x480680017fff8000","0x0","0x48127ffc7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x34","0x4824800180007fcb","0x1","0x20680017fff7fff","0x19","0x480a7ffb7fff8000","0x48127fc67fff8000","0x48127fc67fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff97fff8000","0x480680017fff8000","0x0","0x48127ff87fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x7","0x480a7ffb7fff8000","0x480680017fff8000","0x0","0x48127fbe7fff8000","0x48127fbe7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x3c","0x480a7ffb7fff8000","0x480680017fff8000","0x0","0x48127fbe7fff8000","0x48127fbe7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x400380007ffd7ff6","0x480a7ffc7fff8000","0x482680017ffd8000","0x1","0x20780017fff7ff7","0x21","0x480680017fff8000","0x0","0x400080007ffe7fff","0x400180017ffe7ff8","0x48297ff980007ffa","0x400080027ffd7fff","0x480a7ff47fff8000","0x480a7ff57fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x3","0x1104800180018000","0x48b","0x20680017fff7ffd","0x8","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127ffc7fff8000","0x48127ffc7fff8000","0x10780017fff7fff","0x13","0x48127ffb7fff8000","0x482480017ffb8000","0x3e8","0x480680017fff8000","0x1","0x48127ffb7fff8000","0x48127ffb7fff8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x1","0x400080007ffe7fff","0x480a7ff47fff8000","0x482680017ff58000","0xa0a","0x48127ffb7fff8000","0x482480017ffb8000","0x1","0x20780017fff7ffb","0x7","0x48127ffd7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x6","0x482480017ffd8000","0x64","0x480680017fff8000","0x1","0x400080007ffd7fff","0x48127ffa7fff8000","0x48127ffd7fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0xa70","0x482480017fff8000","0xa6f","0x480080007fff8000","0x480080037fff8000","0x482480017fff8000","0xc62","0xa0680017fff8000","0x8","0x48317ffe80007ff6","0x482480017fff8000","0x100000000000000000000000000000000","0x400280007ff57fff","0x10780017fff7fff","0x7f","0x48317ffe80007ff6","0x400280007ff57fff","0x482680017ff58000","0x1","0x48127ffe7fff8000","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x65","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x48127ffc7fff8000","0x480280007ffc8000","0x48307ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffd7fff8000","0x482480017ffa8000","0x1","0x48127ffa7fff8000","0x480680017fff8000","0x0","0x48127ff77fff8000","0x10780017fff7fff","0x9","0x48127ffd7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x38","0x480080007fff8000","0x48327ff87ff98000","0x48327ffe7ffa8000","0x400280007ff77ffe","0x400280017ff77fff","0x400380027ff77ffb","0x48127ff87fff8000","0x482680017ff78000","0x6","0x480280037ff78000","0x480280047ff78000","0x480280057ff78000","0xa0680017fff8000","0x9","0x4824800180007ffa","0x816","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007fe87fff","0x10780017fff7fff","0x12","0x4824800180007ffa","0x816","0x400080007fe97fff","0x482480017fe98000","0x1","0x48127ffe7fff8000","0x48127ff87fff8000","0x480a7ff87fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127feb7fff8000","0x48127feb7fff8000","0x1104800180018000","0x800000000000010ffffffffffffffffffffffffffffffffffffffffffffffa9","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017fe68000","0x1","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48327ff97ff98000","0x482680017ffa8000","0x1","0x400280007ff77ffe","0x400280017ff77fff","0x400380027ff77ffb","0x48127ff17fff8000","0x482480017ff88000","0x5be","0x482680017ff78000","0x6","0x480680017fff8000","0x0","0x48127ff67fff8000","0x48127ff67fff8000","0x480280037ff78000","0x208b7fff7fff7ffe","0x482680017ff98000","0x1","0x400280007ff77fff","0x400380017ff77ffa","0x400380027ff77ffb","0x48127ffc7fff8000","0x482480017ffc8000","0xad2","0x482680017ff78000","0x6","0x480680017fff8000","0x0","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480280037ff78000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff58000","0x1","0x480a7ff67fff8000","0x480a7ff77fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffd7fff","0x400380017ffd7ffb","0x480280037ffd8000","0x20680017fff7fff","0x7c","0x480280027ffd8000","0x480280047ffd8000","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x480680017fff8000","0x251e864ca2a080f55bce5da2452e8cfcafdbc951a3e7fff5023d558452ec228","0x400280007ffc7ffe","0x400280017ffc7fff","0x480280027ffc8000","0x480080027ffc8000","0x400280037ffc7ffe","0x400280047ffc7fff","0x480280057ffc8000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ffa7ffc","0x480280017ffa7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ffa7ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ffa7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ffa7ffd","0x400280027ffa7ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x482680017ffc8000","0x6","0x482680017ffa8000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280057ffd7fff","0x400280067ffd7ffb","0x400280077ffd7ffc","0x400280087ffd7ffa","0x4802800a7ffd8000","0x20680017fff7fff","0x34","0x480280097ffd8000","0x4802800b7ffd8000","0x482680017ffd8000","0xc","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x48307ffe80007fff","0x20680017fff7fff","0x11","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f4e4c595f555047524144455f474f5645524e4f52","0x400080007ffe7fff","0x48127ff37fff8000","0x48127ff97fff8000","0x48127ff07fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x48127ff37fff8000","0x482480017ff98000","0xb4","0x48127ff07fff8000","0x48127ff57fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x9","0x480280097ffd8000","0x48127ff37fff8000","0x482480017ffe8000","0x456","0x48127ff07fff8000","0x482680017ffd8000","0xd","0x480680017fff8000","0x1","0x4802800b7ffd8000","0x4802800c7ffd8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x16","0x480280027ffd8000","0x1104800180018000","0x94e","0x482480017fff8000","0x94d","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x357a","0x480a7ffa7fff8000","0x48307ffe7ff78000","0x480a7ffc7fff8000","0x482680017ffd8000","0x6","0x480680017fff8000","0x1","0x480280047ffd8000","0x480280057ffd8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480a7ff27fff8000","0x480a7ff37fff8000","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x48127ff77fff8000","0x48127ff67fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffe7d","0x20680017fff7ffd","0x72","0x1104800180018000","0x92a","0x482480017fff8000","0x929","0x48127ff87fff8000","0x48127ff87fff8000","0x480a7ff57fff8000","0x480080007ffc8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff47fff8000","0x48127ff47fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffeab","0x20680017fff7ffc","0x4f","0x480680017fff8000","0x1ac8d354f2e793629cb233a16f10d13cf15b9c45bbc620577c8e1df95ede545","0x400280007ff47fff","0x400280017ff47ffe","0x480280027ff48000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff37ffc","0x480080017ff27ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027ff07ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff37ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff17ffd","0x400080027ff07ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff17fff8000","0x480680017fff8000","0x0","0x482680017ff48000","0x3","0x482480017fed8000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400280007ff67fff","0x400280017ff67ffb","0x400280027ff67ffc","0x400280037ff67ffa","0x400380047ff67ffd","0x480280067ff68000","0x20680017fff7fff","0x10","0x480280057ff68000","0x48127ffc7fff8000","0x48127ffe7fff8000","0x48127ff97fff8000","0x48127fe87fff8000","0x482680017ff68000","0x7","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x480280057ff68000","0x48127ffc7fff8000","0x48127ffe7fff8000","0x48127ff97fff8000","0x48127fe87fff8000","0x482680017ff68000","0x9","0x480680017fff8000","0x1","0x480280077ff68000","0x480280087ff68000","0x208b7fff7fff7ffe","0x1104800180018000","0x8c9","0x482480017fff8000","0x8c8","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x2dbe","0x48127ff37fff8000","0x48307ffe7ff38000","0x48127ff37fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x10780017fff7fff","0xf","0x1104800180018000","0x8ba","0x482480017fff8000","0x8b9","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x3c78","0x48127ff57fff8000","0x48307ffe7ff58000","0x480a7ff57fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ff47fff8000","0x48127ffa7fff8000","0x480a7ff67fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480a7ff27fff8000","0x480a7ff37fff8000","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x48127ff77fff8000","0x48127ff67fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffde6","0x20680017fff7ffd","0x72","0x1104800180018000","0x893","0x482480017fff8000","0x892","0x48127ff87fff8000","0x48127ff87fff8000","0x480a7ff57fff8000","0x480080007ffc8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff47fff8000","0x48127ff47fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffe14","0x20680017fff7ffc","0x4f","0x480680017fff8000","0x2a31bbb25d4dfa03fe73a91cbbab880b7c9cc4461880193ae5819ca6bbfe7cc","0x400280007ff47fff","0x400280017ff47ffe","0x480280027ff48000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff37ffc","0x480080017ff27ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027ff07ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff37ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff17ffd","0x400080027ff07ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff17fff8000","0x480680017fff8000","0x0","0x482680017ff48000","0x3","0x482480017fed8000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400280007ff67fff","0x400280017ff67ffb","0x400280027ff67ffc","0x400280037ff67ffa","0x400380047ff67ffd","0x480280067ff68000","0x20680017fff7fff","0x10","0x480280057ff68000","0x48127ffc7fff8000","0x48127ffe7fff8000","0x48127ff97fff8000","0x48127fe87fff8000","0x482680017ff68000","0x7","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x480280057ff68000","0x48127ffc7fff8000","0x48127ffe7fff8000","0x48127ff97fff8000","0x48127fe87fff8000","0x482680017ff68000","0x9","0x480680017fff8000","0x1","0x480280077ff68000","0x480280087ff68000","0x208b7fff7fff7ffe","0x1104800180018000","0x832","0x482480017fff8000","0x831","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x2dbe","0x48127ff37fff8000","0x48307ffe7ff38000","0x48127ff37fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x10780017fff7fff","0xf","0x1104800180018000","0x823","0x482480017fff8000","0x822","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x3c78","0x48127ff57fff8000","0x48307ffe7ff58000","0x480a7ff57fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ff47fff8000","0x48127ffa7fff8000","0x480a7ff67fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x6","0x10b7ff37fff7fff","0x10780017fff7fff","0x11d","0x10780017fff7fff","0x10c","0x10780017fff7fff","0xfb","0x10780017fff7fff","0xea","0x10780017fff7fff","0xd8","0x10780017fff7fff","0xc6","0x10780017fff7fff","0xb4","0x10780017fff7fff","0xa4","0x10780017fff7fff","0x7a","0x10780017fff7fff","0x50","0x10780017fff7fff","0x26","0x10780017fff7fff","0x13","0x480680017fff8000","0x99cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9","0x400280007ffb7fff","0x400380007ffd7ff6","0x400380017ffd7ff7","0x400380027ffd7ff8","0x400380037ffd7ff9","0x482680017ff28000","0x141e","0x480a7ffa7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x482680017ffd8000","0x4","0x10780017fff7fff","0x103","0x480680017fff8000","0x134692b230b9e1ffa39098904722134159652b09c5bc41d88d6698779d228ff","0x400280007ffb7fff","0x400380007ffd7ff6","0x400380017ffd7ff7","0x400380027ffd7ff8","0x400380037ffd7ff9","0x482680017ff28000","0x13ba","0x480a7ffa7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x482680017ffd8000","0x4","0x10780017fff7fff","0xf2","0x480680017fff8000","0x38a81c7fd04bac40e22e3eab2bcb3a09398bba67d0c5a263c6665c9c0b13a3","0x400280007ffb7fff","0x480a7ff17fff8000","0x480a7ff27fff8000","0x480a7ff47fff8000","0x480a7ff57fff8000","0x480a7ff67fff8000","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x400b7ffa7fff8004","0x402780017ffb8005","0x1","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffd0e","0x20680017fff7ffd","0xb","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x0","0x480a80047fff8000","0x480a80057fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x7633a8d8b49c5c6002a1329e2c9791ea2ced86e06e01e17b5d0d1d5312c792","0x400280007ffb7fff","0x480a7ff17fff8000","0x480a7ff27fff8000","0x480a7ff47fff8000","0x480a7ff57fff8000","0x480a7ff67fff8000","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x400b7ffa7fff8002","0x402780017ffb8003","0x1","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffce6","0x20680017fff7ffd","0xb","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x0","0x480a80027fff8000","0x480a80037fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x34bb683f971572e1b0f230f3dd40f3dbcee94e0b3e3261dd0a91229a1adc4b7","0x400280007ffb7fff","0x480a7ff17fff8000","0x480a7ff27fff8000","0x480a7ff47fff8000","0x480a7ff57fff8000","0x480a7ff67fff8000","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x400b7ffa7fff8000","0x402780017ffb8001","0x1","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffcbe","0x20680017fff7ffd","0xb","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x0","0x480a80007fff8000","0x480a80017fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480680017fff8000","0xd1831486d8c46712712653f17d3414869aa50b4c16836d0b3d4afcfeafa024","0x400280007ffb7fff","0x400380007ffd7ff9","0x482680017ff28000","0x14e6","0x480a7ffa7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x482680017ffd8000","0x1","0x10780017fff7fff","0x6c","0x480680017fff8000","0x9d4a59b844ac9d98627ddba326ab3707a7d7e105fd03c777569d0f61a91f1e","0x400280007ffb7fff","0x400380007ffd7ff7","0x400380017ffd7ff8","0x400380027ffd7ff9","0x482680017ff28000","0x141e","0x480a7ffa7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x482680017ffd8000","0x3","0x10780017fff7fff","0x5c","0x480680017fff8000","0x2842fd3b01bb0858fef6a2da51cdd9f995c7d36d7625fb68dd5d69fcc0a6d76","0x400280007ffb7fff","0x400380007ffd7ff7","0x400380017ffd7ff8","0x400380027ffd7ff9","0x482680017ff28000","0x141e","0x480a7ffa7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x482680017ffd8000","0x3","0x10780017fff7fff","0x4c","0x480680017fff8000","0x2b23b0c08c7b22209aea4100552de1b7876a49f04ee5a4d94f83ad24bc4ec1c","0x400280007ffb7fff","0x400380007ffd7ff7","0x400380017ffd7ff8","0x400380027ffd7ff9","0x482680017ff28000","0x141e","0x480a7ffa7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x482680017ffd8000","0x3","0x10780017fff7fff","0x3c","0x480680017fff8000","0x3ae95723946e49d38f0cf844cef1fb25870e9a74999a4b96271625efa849b4c","0x400280007ffb7fff","0x400380007ffd7ff8","0x400380017ffd7ff9","0x482680017ff28000","0x1482","0x480a7ffa7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x482680017ffd8000","0x2","0x10780017fff7fff","0x2d","0x480680017fff8000","0x2d8a82390cce552844e57407d23a1e48a38c4b979d525b1673171e503e116ab","0x400280007ffb7fff","0x400380007ffd7ff8","0x400380017ffd7ff9","0x482680017ff28000","0x1482","0x480a7ffa7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x482680017ffd8000","0x2","0x10780017fff7fff","0x1e","0x480680017fff8000","0x2143175c365244751ccde24dd8f54f934672d6bc9110175c9e58e1e73705531","0x400280007ffb7fff","0x400380007ffd7ff8","0x400380017ffd7ff9","0x482680017ff28000","0x1482","0x480a7ffa7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x482680017ffd8000","0x2","0x10780017fff7fff","0xf","0x480680017fff8000","0x25e2d538533284b9d61dfe45b9aaa563d33ef8374d9bb26d77a009b8e21f0de","0x400280007ffb7fff","0x400380007ffd7ff8","0x400380017ffd7ff9","0x482680017ff28000","0x14e6","0x480a7ffa7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x482680017ffd8000","0x2","0x480a7ff17fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480a7ff37fff8000","0x480a7ff47fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff77fff8000","0x48127ff67fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffc19","0x20680017fff7ffd","0x9d","0x1104800180018000","0x6c6","0x482480017fff8000","0x6c5","0x48127ff87fff8000","0x48127ff87fff8000","0x480a7ff67fff8000","0x480080007ffc8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff47fff8000","0x48127ff47fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffc47","0x20680017fff7ffc","0x7a","0x480680017fff8000","0x2a31bbb25d4dfa03fe73a91cbbab880b7c9cc4461880193ae5819ca6bbfe7cc","0x400280007ff57fff","0x400280017ff57ffe","0x480280027ff58000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff37ffc","0x480080017ff27ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027ff07ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff37ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff17ffd","0x400080027ff07ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff17fff8000","0x480680017fff8000","0x0","0x482680017ff58000","0x3","0x482480017fed8000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ff77fff","0x400280017ff77ffb","0x400280027ff77ffc","0x400280037ff77ffa","0x480280057ff78000","0x20680017fff7fff","0x3b","0x480280047ff78000","0x480280067ff78000","0x482680017ff78000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x12","0x4824800180007ffc","0x10000000000000000","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480080007ff57fff","0x482480017ffe8000","0xefffffffffffffdeffffffffffffffff","0x480080017ff37fff","0x400080027ff27ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x15","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x482480017ffc8000","0xffffffffffffffff0000000000000000","0x400080017ff77fff","0x482480017ff78000","0x2","0x482480017ffc8000","0x3ca","0x48127ff47fff8000","0x48127fe37fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff47fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f7265553634202d206e6f6e20753634","0x400080007ffe7fff","0x482480017ff08000","0x3","0x48127ff57fff8000","0x48127fed7fff8000","0x48127fdc7fff8000","0x48127ff17fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x480280047ff78000","0x48127ffc7fff8000","0x482480017ffe8000","0x712","0x48127ff97fff8000","0x48127fe87fff8000","0x482680017ff78000","0x8","0x480680017fff8000","0x1","0x480280067ff78000","0x480280077ff78000","0x208b7fff7fff7ffe","0x1104800180018000","0x63a","0x482480017fff8000","0x639","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x346c","0x48127ff37fff8000","0x48307ffe7ff38000","0x48127ff37fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x10780017fff7fff","0xf","0x1104800180018000","0x62b","0x482480017fff8000","0x62a","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x4326","0x48127ff57fff8000","0x48307ffe7ff58000","0x480a7ff67fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ff57fff8000","0x48127ffa7fff8000","0x480a7ff77fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ff98000","0xfffffffffffffffffffffffffffff916","0x400280007ff87fff","0x10780017fff7fff","0x22","0x4825800180007ff9","0x6ea","0x400280007ff87fff","0x482680017ff88000","0x1","0x48127ffe7fff8000","0x48297ffa80007ffb","0x20680017fff7fff","0x4","0x10780017fff7fff","0xf","0x480280007ffa8000","0x400280007ffd7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x482680017ffa8000","0x1","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x482680017ffd8000","0x1","0x1104800180018000","0x800000000000010ffffffffffffffffffffffffffffffffffffffffffffffe5","0x208b7fff7fff7ffe","0x48127ffd7fff8000","0x482480017ffd8000","0x686","0x480680017fff8000","0x0","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff88000","0x1","0x480a7ff97fff8000","0x480680017fff8000","0x1","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x208b7fff7fff7ffe","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffc7fff","0x400380017ffc7ffa","0x480280037ffc8000","0x20680017fff7fff","0x7a","0x480280027ffc8000","0x480280047ffc8000","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x400280007ffb7fff","0x400380017ffb7ffd","0x480280027ffb8000","0x480080027ffd8000","0x400280037ffb7ffe","0x400280047ffb7fff","0x480280057ffb8000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff97ffc","0x480280017ff97ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff97ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff97ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff97ffd","0x400280027ff97ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff37fff8000","0x480680017fff8000","0x0","0x482680017ffb8000","0x6","0x482680017ff98000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280057ffc7fff","0x400280067ffc7ffb","0x400280077ffc7ffc","0x400280087ffc7ffa","0x4802800a7ffc8000","0x20680017fff7fff","0x34","0x480280097ffc8000","0x4802800b7ffc8000","0x482680017ffc8000","0xc","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x48307ffe80007fff","0x20680017fff7fff","0x11","0x40780017fff7fff","0x1","0x480680017fff8000","0x43414c4c45525f49535f4d495353494e475f524f4c45","0x400080007ffe7fff","0x48127ff37fff8000","0x48127ff97fff8000","0x48127ff07fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x48127ff37fff8000","0x482480017ff98000","0xb4","0x48127ff07fff8000","0x48127ff57fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x9","0x480280097ffc8000","0x48127ff37fff8000","0x482480017ffe8000","0x456","0x48127ff07fff8000","0x482680017ffc8000","0xd","0x480680017fff8000","0x1","0x4802800b7ffc8000","0x4802800c7ffc8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x15","0x480280027ffc8000","0x1104800180018000","0x55d","0x482480017fff8000","0x55c","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x3520","0x480a7ff97fff8000","0x48307ffe7ff78000","0x480a7ffb7fff8000","0x482680017ffc8000","0x6","0x480680017fff8000","0x1","0x480280047ffc8000","0x480280057ffc8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x400280007ffa7fff","0x400380017ffa7ffc","0x480280027ffa8000","0x400280037ffa7fff","0x400380047ffa7ffd","0x480280057ffa8000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff87ffc","0x480280017ff87ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff87ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff87ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff87ffd","0x400280027ff87ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ffa8000","0x6","0x482680017ff88000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400380017ffb7ff9","0x400280027ffb7ffc","0x400280037ffb7ffb","0x480280057ffb8000","0x20680017fff7fff","0xd0","0x480280047ffb8000","0x480280067ffb8000","0x482680017ffb8000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x48307ffe80007fff","0x20680017fff7fff","0xa6","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x400080007ff37fff","0x400180017ff37ffc","0x480080027ff38000","0x400080037ff27fff","0x400180047ff27ffd","0x480080057ff28000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007fee7ffc","0x480080017fed7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027feb7ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007fee7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017fec7ffd","0x400080027feb7ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x402580017fe78001","0x6","0x482480017fe88000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400080007feb7fff","0x400080017feb7ffb","0x400080027feb7ffc","0x400080037feb7ffa","0x400080047feb7ffd","0x480080067feb8000","0x20680017fff7fff","0x62","0x480080057fea8000","0x48127fff7fff8000","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400080077fe77fff","0x400080087fe77ffe","0x4800800a7fe78000","0x20680017fff7fff","0x4d","0x480080097fe68000","0x4800800b7fe58000","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff57fff8000","0x48127ffb7fff8000","0x480680017fff8000","0xd","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480080027ff58000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x402580017fd58000","0xc","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffc99","0x20680017fff7ffb","0x26","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0xf","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480080097fe68000","0x48127ff87fff8000","0x482480017ffe8000","0x4fb0","0x480a80017fff8000","0x482480017fe28000","0xd","0x480680017fff8000","0x1","0x4800800b7fe08000","0x4800800c7fdf8000","0x208b7fff7fff7ffe","0x480080057fea8000","0x48127ffc7fff8000","0x482480017ffe8000","0x797c","0x480a80017fff8000","0x482480017fe68000","0x9","0x480680017fff8000","0x1","0x480080077fe48000","0x480080087fe38000","0x208b7fff7fff7ffe","0x1104800180018000","0x45a","0x482480017fff8000","0x459","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xaab4","0x48127fee7fff8000","0x48307ffe7ff48000","0x48127feb7fff8000","0x48127ff07fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x480280047ffb8000","0x1104800180018000","0x444","0x482480017fff8000","0x443","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xae9c","0x48127ff57fff8000","0x48307ffe7ff78000","0x48127ff27fff8000","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x400280007ffa7fff","0x400380017ffa7ffc","0x480280027ffa8000","0x400280037ffa7fff","0x400380047ffa7ffd","0x480280057ffa8000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff87ffc","0x480280017ff87ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff87ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff87ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff87ffd","0x400280027ff87ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ffa8000","0x6","0x482680017ff88000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400380017ffb7ff9","0x400280027ffb7ffc","0x400280037ffb7ffb","0x480280057ffb8000","0x20680017fff7fff","0xd0","0x480280047ffb8000","0x480280067ffb8000","0x482680017ffb8000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x48307ffe80007fff","0x20680017fff7fff","0x17","0x1104800180018000","0x3e5","0x482480017fff8000","0x3e4","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xaab4","0x48127fee7fff8000","0x48307ffe7ff48000","0x48127feb7fff8000","0x48127ff07fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x400080007ff37fff","0x400180017ff37ffc","0x480080027ff38000","0x400080037ff27fff","0x400180047ff27ffd","0x480080057ff28000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007fee7ffc","0x480080017fed7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027feb7ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007fee7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017fec7ffd","0x400080027feb7ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x402580017fe78001","0x6","0x482480017fe88000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400080007feb7fff","0x400080017feb7ffb","0x400080027feb7ffc","0x400080037feb7ffa","0x400080047feb7ffd","0x480080067feb8000","0x20680017fff7fff","0x62","0x480080057fea8000","0x48127fff7fff8000","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400080077fe77fff","0x400080087fe77ffe","0x4800800a7fe78000","0x20680017fff7fff","0x4d","0x480080097fe68000","0x4800800b7fe58000","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff57fff8000","0x48127ffb7fff8000","0x480680017fff8000","0xb","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480080027ff58000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x402580017fd58000","0xc","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffb6b","0x20680017fff7ffb","0x26","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0xf","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480080097fe68000","0x48127ff87fff8000","0x482480017ffe8000","0x4fb0","0x480a80017fff8000","0x482480017fe28000","0xd","0x480680017fff8000","0x1","0x4800800b7fe08000","0x4800800c7fdf8000","0x208b7fff7fff7ffe","0x480080057fea8000","0x48127ffc7fff8000","0x482480017ffe8000","0x797c","0x480a80017fff8000","0x482480017fe68000","0x9","0x480680017fff8000","0x1","0x480080077fe48000","0x480080087fe38000","0x208b7fff7fff7ffe","0x480280047ffb8000","0x1104800180018000","0x32b","0x482480017fff8000","0x32a","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xae9c","0x48127ff57fff8000","0x48307ffe7ff78000","0x48127ff27fff8000","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x2e9f66c6eea14532c94ad25405a4fcb32faa4969559c128d837caa0ec50a655","0x480680017fff8000","0x3711c9d994faf6055172091cb841fd4831aa743e6f3315163b06a122c841846","0x400280007ffb7ffe","0x400280017ffb7fff","0x480280027ffb8000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff97ffc","0x480280017ff97ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff97ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff97ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff97ffd","0x400280027ff97ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ffb8000","0x3","0x482680017ff98000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffc7fff","0x400380017ffc7ffa","0x400280027ffc7ffc","0x400280037ffc7ffb","0x480280057ffc8000","0x20680017fff7fff","0x86","0x480280047ffc8000","0x480280067ffc8000","0x482680017ffc8000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x66","0x48127fff7fff8000","0x20780017fff7ffd","0x1b","0x1104800180018000","0x2da","0x482480017fff8000","0x2d9","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x8","0x482480017fff8000","0x258be","0x40780017fff7fff","0x1","0x480680017fff8000","0x5a45524f5f50524f564953494f4e414c5f474f565f41444d494e","0x400080007ffe7fff","0x48127fef7fff8000","0x48307ffc7ff58000","0x48127fec7fff8000","0x48127ff17fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48127ff87fff8000","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x3711c9d994faf6055172091cb841fd4831aa743e6f3315163b06a122c841846","0x480a7ffd7fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffd72","0x20680017fff7ffd","0x2c","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x3711c9d994faf6055172091cb841fd4831aa743e6f3315163b06a122c841846","0x480680017fff8000","0x3711c9d994faf6055172091cb841fd4831aa743e6f3315163b06a122c841846","0x1104800180018000","0x1dd","0x20680017fff7ffd","0xd","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x251e864ca2a080f55bce5da2452e8cfcafdbc951a3e7fff5023d558452ec228","0x480680017fff8000","0x3711c9d994faf6055172091cb841fd4831aa743e6f3315163b06a122c841846","0x1104800180018000","0x1d1","0x208b7fff7fff7ffe","0x1104800180018000","0x29f","0x482480017fff8000","0x29e","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb496","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff27fff8000","0x48127ff27fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x28c","0x482480017fff8000","0x28b","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x16f08","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff27fff8000","0x48127ff27fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x279","0x482480017fff8000","0x278","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x8","0x482480017fff8000","0x25986","0x40780017fff7fff","0x1","0x480680017fff8000","0x524f4c45535f414c52454144595f494e495449414c495a4544","0x400080007ffe7fff","0x48127ff07fff8000","0x48307ffc7ff58000","0x48127fed7fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480280047ffc8000","0x1104800180018000","0x25f","0x482480017fff8000","0x25e","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x8","0x482480017fff8000","0x25c42","0x48127ff57fff8000","0x48307ffe7ff78000","0x48127ff27fff8000","0x482680017ffc8000","0x8","0x480680017fff8000","0x1","0x480280067ffc8000","0x480280077ffc8000","0x208b7fff7fff7ffe","0xa0680017fff8005","0xe","0x4825800180057ffd","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ffa7ffc","0x480280017ffa7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ffa7ffc","0x10780017fff7fff","0x11","0x480a7ffd7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ffa7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ffa7ffd","0x400280027ffa7ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ffa8000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffc7fff","0x400380017ffc7ffb","0x400280027ffc7ffd","0x400280037ffc7ffc","0x480280057ffc8000","0x20680017fff7fff","0x87","0x480280047ffc8000","0x480280067ffc8000","0x482680017ffc8000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x57","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff48000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0x38","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x11","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x40780017fff7fff","0xc","0x482480017fec8000","0x1","0x482480017ff18000","0x6b8","0x48127fef7fff8000","0x480680017fff8000","0x0","0x48127fe17fff8000","0x48127feb7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017ff18000","0x3","0x48127ff67fff8000","0x48127ff47fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x1d","0x40780017fff7fff","0xb","0x480080047fec8000","0x48127ff17fff8000","0x482480017ffe8000","0x6a4","0x482480017fe98000","0x8","0x480080067fe88000","0x480080077fe78000","0x10780017fff7fff","0x23","0x40780017fff7fff","0xb","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fe68000","0x3","0x482480017feb8000","0x2d8c","0x48127fe97fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x16","0x480280047ffc8000","0x48127fe67fff8000","0x482480017ffe8000","0x3494","0x482680017ffc8000","0x8","0x480280067ffc8000","0x480280077ffc8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xa","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480a7ffc7fff8000","0x10780017fff7fff","0x8","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x98","0x480080007fff8000","0xa0680017fff8000","0x12","0x4824800180007ffe","0x100000000","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480280007ffb7fff","0x482480017ffe8000","0xefffffffffffffde00000000ffffffff","0x480280017ffb7fff","0x400280027ffb7ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x78","0x402780017fff7fff","0x1","0x400280007ffb7ffe","0x482480017ffe8000","0xffffffffffffffffffffffff00000000","0x400280017ffb7fff","0x480680017fff8000","0x0","0x48307ff880007ff9","0x48307ffb7ffe8000","0xa0680017fff8000","0x8","0x482480017ffd8000","0x1","0x48307fff80007ffd","0x400280027ffb7fff","0x10780017fff7fff","0x51","0x48307ffe80007ffd","0x400280027ffb7fff","0x48307ff480007ff5","0x48307ffa7ff38000","0x48307ffb7ff28000","0x48307ff580017ffd","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400280037ffb7fff","0x10780017fff7fff","0x2f","0x400280037ffb7fff","0x48307fef80007ff0","0x48307ffe7ff28000","0xa0680017fff8000","0x8","0x482480017ffd8000","0x1","0x48307fff80007ffd","0x400280047ffb7fff","0x10780017fff7fff","0x11","0x48307ffe80007ffd","0x400280047ffb7fff","0x40780017fff7fff","0x3","0x482680017ffb8000","0x5","0x480680017fff8000","0x0","0x48307fea7fe68000","0x48307ff77fe58000","0x480680017fff8000","0x0","0x48127ff07fff8000","0x48127ff07fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e646578206f7574206f6620626f756e6473","0x400080007ffe7fff","0x482680017ffb8000","0x5","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x4","0x40780017fff7fff","0x1","0x480680017fff8000","0x7533325f737562204f766572666c6f77","0x400080007ffe7fff","0x482680017ffb8000","0x4","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x9","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e646578206f7574206f6620626f756e6473","0x400080007ffe7fff","0x482680017ffb8000","0x3","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0xc","0x482680017ffb8000","0x3","0x480680017fff8000","0x0","0x48127fe67fff8000","0x48127fe67fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x14","0x480a7ffb7fff8000","0x480680017fff8000","0x0","0x48127fe67fff8000","0x48127fe67fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x480680017fff8000","0x2e9f66c6eea14532c94ad25405a4fcb32faa4969559c128d837caa0ec50a655","0x400280007ffa7fff","0x400380017ffa7ffc","0x480280027ffa8000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff87ffc","0x480280017ff87ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff87ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff87ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff87ffd","0x400280027ff87ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ffa8000","0x3","0x482680017ff88000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400380017ffb7ff9","0x400280027ffb7ffc","0x400280037ffb7ffb","0x480280057ffb8000","0x20680017fff7fff","0x8d","0x480280047ffb8000","0x480680017fff8000","0x2e9f66c6eea14532c94ad25405a4fcb32faa4969559c128d837caa0ec50a655","0x400080007ffa7fff","0x400180017ffa7ffc","0x480080027ffa8000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff67ffc","0x480080017ff57ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027ff37ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff67ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff47ffd","0x400080027ff37ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff67fff8000","0x480680017fff8000","0x0","0x480280067ffb8000","0x402580017fef8001","0x3","0x482480017ff08000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400280077ffb7fff","0x400280087ffb7ffb","0x400280097ffb7ffc","0x4002800a7ffb7ffa","0x4003800b7ffb7ffd","0x4802800d7ffb8000","0x20680017fff7fff","0x4c","0x4802800c7ffb8000","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x9","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480a7ffc7fff8000","0x48127ff27fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x402780017ffb8000","0xe","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffff846","0x20680017fff7ffb","0x26","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0xf","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x4802800c7ffb8000","0x48127ffc7fff8000","0x482480017ffe8000","0x4f4c","0x480a80017fff8000","0x482680017ffb8000","0x10","0x480680017fff8000","0x1","0x4802800e7ffb8000","0x4802800f7ffb8000","0x208b7fff7fff7ffe","0x480280047ffb8000","0x1104800180018000","0x12","0x482480017fff8000","0x11","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x7fbc","0x48127ff67fff8000","0x48307ffe7ff88000","0x48127ff37fff8000","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe"],"bytecode_segment_lengths":[332,332,332,332,170,168,168,168,168,296,191,272,272,245,245,245,245,146,125,125,170,115,196,272,375,483,375,346,346,115,196,483,346,346,685,167,167,193,194,379,227,764,339,311,137,193,201,204,985,390,227,396,396,263,731,731,224,66,158,152,151,151,310,194,53,150,281,281,204,193,185,209],"compiler_version":"2.10.0","entry_points_by_type":{"CONSTRUCTOR":[{"builtins":["pedersen","range_check"],"offset":8741,"selector":"0x28ffe4ff0f226a9107253e17a904099aa4f63a02a5621de0576e5aa71bc5194"}],"EXTERNAL":[{"builtins":["pedersen","range_check"],"offset":3936,"selector":"0xb2ef42a25c95687d1a3e7c2584885fd4058d102e05c44f65cf35988451bc8"},{"builtins":["pedersen","range_check","poseidon"],"offset":2002,"selector":"0xc30ffbeb949d3447fd4acd61251803e8ab9c8a777318abb5bd5fbf28015eb"},{"builtins":["pedersen","range_check"],"offset":664,"selector":"0x151e58b29179122a728eab07c8847e5baf5802379c5db3a7d57a8263a7bd1d"},{"builtins":["pedersen","range_check"],"offset":7566,"selector":"0x41b033f4a31df8067c24d1e9b550a2ce75fd4a29e1147af9752174f0e6cb20"},{"builtins":["range_check"],"offset":4577,"selector":"0x4c4fb1ab068f6039d5780c68dd0fa2f8742cceb3426d19667778ca7f3518a9"},{"builtins":["range_check"],"offset":7255,"selector":"0x80aa9fdbfaf9615e4afc7f5f722e265daca5ccc655360fa5ccacf9c267936d"},{"builtins":["pedersen","range_check"],"offset":5330,"selector":"0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e"},{"builtins":["pedersen","range_check"],"offset":4181,"selector":"0x95604234097c6fe6314943092b1aa8fb6ee781cf32ac6d5b78d856f52a540f"},{"builtins":["pedersen","range_check"],"offset":996,"selector":"0xd63a78e4cd7fb4c41bc18d089154af78d400a5e837f270baea6cf8db18c8dd"},{"builtins":["range_check"],"offset":4747,"selector":"0x1557182e4359a1f0c6301278e8f5b35a776ab58d39892581e357578fb287836"},{"builtins":["pedersen","range_check"],"offset":8049,"selector":"0x16cc063b8338363cf388ce7fe1df408bf10f16cd51635d392e21d852fafb683"},{"builtins":["pedersen","range_check"],"offset":3201,"selector":"0x183420eb7aafd9caad318b543d9252c94857340f4768ac83cf4b6472f0bf515"},{"builtins":["pedersen","range_check"],"offset":8395,"selector":"0x1aaf3e6107dd1349c81543ff4221a326814f77dadcc5810807b74f1a49ded4e"},{"builtins":["pedersen","range_check"],"offset":0,"selector":"0x1c67057e2995950900dbf33db0f5fc9904f5a18aae4a3768f721c43efe5d288"},{"builtins":["pedersen","range_check"],"offset":6563,"selector":"0x1d13ab0a76d7407b1d5faccd4b3d8a9efe42f3d3c21766431d4fafb30f45bd4"},{"builtins":["pedersen","range_check"],"offset":5058,"selector":"0x1e888a1026b19c8c0b57c72d63ed1737106aa10034105b980ba117bd0c29fe1"},{"builtins":["pedersen","range_check","poseidon"],"offset":1498,"selector":"0x1fa400a40ac35b4aa2c5383c3bb89afee2a9698b86ebb405cf25a6e63428605"},{"builtins":["range_check"],"offset":4452,"selector":"0x216b05c387bab9ac31918a3e61672f4618601f3c598a2f3f2710f37053e1ea4"},{"builtins":["pedersen","range_check"],"offset":6188,"selector":"0x219209e083275171774dab1df80982e9df2096516f06319c5c6d71ae0a8480c"},{"builtins":["pedersen","range_check"],"offset":3446,"selector":"0x225faa998b63ad3d277e950e8091f07d28a4c45ef6de7f3f7095e89be92d701"},{"builtins":["pedersen","range_check"],"offset":3691,"selector":"0x24643b0aa4f24549ae7cd884195db7950c3a79a96cb7f37bde40549723559d9"},{"builtins":["pedersen","range_check"],"offset":2657,"selector":"0x25a5317fee78a3601253266ed250be22974a6b6eb116c875a2596585df6a400"},{"builtins":["range_check"],"offset":1328,"selector":"0x284a2f635301a0bf3a171bb8e4292667c6c70d23d48b0ae8ec4df336e84bccd"},{"builtins":["pedersen","range_check"],"offset":7370,"selector":"0x2e4263afad30923c891518314c3c95dbe830a16874e8abc5777a9a20b54c76e"},{"builtins":["pedersen","range_check"],"offset":2466,"selector":"0x302e0454f48778e0ca3a2e714a289c4e8d8e03d614b370130abb1a524a47f22"},{"builtins":["pedersen","range_check"],"offset":2170,"selector":"0x30559321b47d576b645ed7bd24089943dd5fd3a359ecdd6fa8f05c1bab67d6b"},{"builtins":["pedersen","range_check","poseidon"],"offset":1834,"selector":"0x338dd2002b6f7ac6471742691de72611381e3fc4ce2b0361c29d42cb2d53a90"},{"builtins":["pedersen","range_check","poseidon"],"offset":1666,"selector":"0x33fe3600cdfaa48261a8c268c66363562da383d5dd26837ba63b66ebbc04e3c"},{"builtins":["pedersen","range_check"],"offset":4862,"selector":"0x35a73cd311a05d46deda634c5ee045db92f811b4e74bca4437fcb5302b7af33"},{"builtins":["range_check"],"offset":4327,"selector":"0x361458367e696363fbcc70777d07ebbd2394e89fd0adcaf147faccd1d294d60"},{"builtins":["pedersen","range_check"],"offset":5705,"selector":"0x3704ffe8fba161be0e994951751a5033b1462b918ff785c0a636be718dfdb68"},{"builtins":["pedersen","range_check"],"offset":2929,"selector":"0x37791de85f8a3be5014988a652f6cf025858f3532706c18f8cf24f2f81800d5"},{"builtins":["pedersen","range_check"],"offset":332,"selector":"0x3a07502a2e0e18ad6178ca530615148b9892d000199dbb29e402c41913c3d1a"},{"builtins":["pedersen","range_check"],"offset":6909,"selector":"0x3b076186c19fe96221e4dfacd40c519f612eae02e0555e4e115a2a6cf2f1c1f"}],"L1_HANDLER":[]},"hints":[[0,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[38,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[42,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[52,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[88,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[90,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[139,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[141,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[170,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[197,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[219,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[240,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[276,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[300,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[315,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[332,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[370,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[374,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[384,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[420,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[422,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[471,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[473,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[502,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[529,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[551,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[572,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[608,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[632,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[647,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[664,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[702,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[706,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[716,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[752,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[754,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[803,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[805,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[834,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[861,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[883,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[904,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[940,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[964,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[979,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[996,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[1034,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[1038,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[1048,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[1084,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[1086,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[1135,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[1137,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[1166,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1193,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[1215,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1236,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1272,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1296,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1311,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1328,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[1347,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1368,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x3336"},"rhs":{"Deref":{"offset":-2,"register":"AP"}}}}]],[1393,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[1401,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"BinOp":{"a":{"offset":-3,"register":"AP"},"b":{"Immediate":"0x0"},"op":"Add"}},"rhs":{"Immediate":"0x10000000000000000"}}}]],[1405,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"value":{"Deref":{"offset":-1,"register":"AP"}},"x":{"offset":0,"register":"AP"},"y":{"offset":1,"register":"AP"}}}]],[1423,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1437,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1467,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1482,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1498,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x23fa"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[1526,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1552,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-4,"register":"AP"}}}}]],[1578,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1603,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1620,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1648,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1666,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x245e"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[1694,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1722,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[1748,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1771,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1788,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1816,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1834,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x245e"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[1862,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1890,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[1916,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1939,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1956,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[1984,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2002,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x245e"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[2030,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2058,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[2084,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2107,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2124,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2152,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2170,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[2218,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[2222,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[2232,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[2248,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2275,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[2293,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[2297,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[2308,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[2335,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[2354,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2393,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2418,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2433,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2449,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2466,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[2495,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2520,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-4,"register":"AP"}}}}]],[2535,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[2539,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[2550,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[2577,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[2581,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2608,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2624,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2640,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2657,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[2695,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[2699,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[2709,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[2725,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2752,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[2772,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[2776,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[2787,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[2814,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[2833,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2872,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2897,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2912,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[2929,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[2967,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[2971,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[2981,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[2997,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3024,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[3044,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[3048,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[3059,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[3086,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[3105,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3144,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3169,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3184,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3201,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[3239,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[3243,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[3253,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[3269,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3296,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[3313,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[3342,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3389,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3414,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3429,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3446,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[3484,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[3488,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[3498,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[3514,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3541,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[3558,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[3587,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3634,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3659,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3674,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3691,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[3729,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[3733,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[3743,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[3759,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3786,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[3803,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[3832,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3879,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3904,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3919,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[3936,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[3974,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[3978,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[3988,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[4004,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4031,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[4048,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[4077,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4124,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4149,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4164,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4181,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[4210,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4237,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[4257,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4278,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4294,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4310,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4327,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[4346,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4367,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x2af8"},"rhs":{"Deref":{"offset":-2,"register":"AP"}}}}]],[4392,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[4396,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4421,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4436,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4452,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[4471,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4492,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x2af8"},"rhs":{"Deref":{"offset":-2,"register":"AP"}}}}]],[4517,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[4521,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4546,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4561,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4577,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[4596,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4617,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x3336"},"rhs":{"Deref":{"offset":-2,"register":"AP"}}}}]],[4642,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[4650,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"BinOp":{"a":{"offset":-3,"register":"AP"},"b":{"Immediate":"0x0"},"op":"Add"}},"rhs":{"Immediate":"0x100"}}}]],[4654,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"value":{"Deref":{"offset":-1,"register":"AP"}},"x":{"offset":0,"register":"AP"},"y":{"offset":1,"register":"AP"}}}]],[4672,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4686,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4716,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4731,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4747,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[4766,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4787,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x6bc6"},"rhs":{"Deref":{"offset":-2,"register":"AP"}}}}]],[4809,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4831,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4846,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4862,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[4900,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[4904,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[4914,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[4930,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[4955,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-4,"register":"AP"}}}}]],[4977,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[5001,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[5026,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[5041,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[5058,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[5096,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[5100,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[5110,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[5145,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[5149,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[5159,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[5175,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[5202,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[5225,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[5249,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[5274,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[5298,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[5313,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[5330,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[5368,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[5372,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[5382,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[5418,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[5420,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[5469,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[5471,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[5500,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[5527,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[5544,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[5562,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[5613,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[5649,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[5673,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[5688,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[5707,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x398"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[5745,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":3,"register":"FP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[5749,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[5759,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":3,"register":"FP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[5795,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":2,"register":"FP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[5799,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[5809,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":2,"register":"FP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[5845,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[5847,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[5896,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[5898,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[5927,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[5954,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[5971,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[6003,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[6072,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[6108,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[6132,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[6156,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[6171,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[6188,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[6226,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[6230,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[6240,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[6276,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[6278,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[6327,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[6329,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[6358,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[6385,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[6402,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[6420,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[6471,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[6507,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[6531,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[6546,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[6563,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[6601,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[6605,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[6615,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[6651,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[6653,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[6702,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[6704,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[6733,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[6760,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[6782,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[6817,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[6853,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[6877,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[6892,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[6909,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[6947,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[6951,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[6961,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[6997,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[6999,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[7048,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[7050,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[7079,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7106,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[7128,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7163,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7199,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7223,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7238,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7255,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[7274,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7295,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x6bc6"},"rhs":{"Deref":{"offset":-2,"register":"AP"}}}}]],[7317,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7339,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7354,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7370,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[7408,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[7412,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[7422,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[7438,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7463,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-4,"register":"AP"}}}}]],[7485,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7509,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7534,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7549,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7568,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x398"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[7606,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":3,"register":"FP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[7610,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[7620,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":3,"register":"FP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[7656,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":2,"register":"FP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[7660,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[7670,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":2,"register":"FP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[7706,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[7708,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[7757,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[7759,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[7788,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7815,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[7832,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[7864,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7933,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7969,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[7993,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[8017,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[8032,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[8049,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[8087,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[8091,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[8101,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[8137,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[8139,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[8188,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[8190,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[8219,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[8246,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[8268,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[8303,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[8339,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[8363,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[8378,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[8395,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[8433,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[8437,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[8447,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[8483,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[8485,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[8534,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[8536,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[8565,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[8592,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[8614,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[8649,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[8685,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[8709,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[8724,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[8741,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x1e6e"},"rhs":{"Deref":{"offset":-6,"register":"FP"}}}}]],[8799,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"BinOp":{"a":{"offset":-2,"register":"AP"},"b":{"Immediate":"0x0"},"op":"Add"}},"rhs":{"Immediate":"0x100"}}}]],[8803,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"value":{"Deref":{"offset":-1,"register":"AP"}},"x":{"offset":0,"register":"AP"},"y":{"offset":1,"register":"AP"}}}]],[8849,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[8851,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[8900,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[8902,[{"DivMod":{"lhs":{"Deref":{"offset":-3,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[8952,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[8956,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[8966,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[9001,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[9005,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[9015,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[9050,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-2,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[9054,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[9064,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-3,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[9100,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"BinOp":{"a":{"offset":-2,"register":"AP"},"b":{"Immediate":"0x0"},"op":"Add"}},"rhs":{"Immediate":"0x10000000000000000"}}}]],[9104,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"value":{"Deref":{"offset":-1,"register":"AP"}},"x":{"offset":0,"register":"AP"},"y":{"offset":1,"register":"AP"}}}]],[9130,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[9157,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[9185,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[9206,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[9231,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[9255,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[9279,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[9303,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[9338,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[9362,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[9377,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[9393,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[9409,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[9430,[{"SystemCall":{"system":{"Deref":{"offset":-6,"register":"FP"}}}}]],[9451,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-6,"register":"FP"},"b":{"Immediate":"0x5"},"op":"Add"}}}}]],[9459,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[9463,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[9473,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-4,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[9507,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[9532,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[9597,[{"SystemCall":{"system":{"Deref":{"offset":-6,"register":"FP"}}}}]],[9618,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-6,"register":"FP"},"b":{"Immediate":"0x5"},"op":"Add"}}}}]],[9626,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[9630,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[9640,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-4,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[9674,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[9699,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[9781,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[9785,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[9795,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-2,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[9953,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[9994,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[9998,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[10009,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[10036,[{"SystemCall":{"system":{"Deref":{"offset":-9,"register":"FP"}}}}]],[10044,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"BinOp":{"a":{"offset":-3,"register":"AP"},"b":{"Immediate":"0x0"},"op":"Add"}},"rhs":{"Immediate":"0x10000000000000000"}}}]],[10048,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"value":{"Deref":{"offset":-1,"register":"AP"}},"x":{"offset":0,"register":"AP"},"y":{"offset":1,"register":"AP"}}}]],[10079,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[10162,[{"SystemCall":{"system":{"Deref":{"offset":-6,"register":"AP"}}}}]],[10182,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-17,"register":"AP"},"b":{"Immediate":"0x5"},"op":"Add"}}}}]],[10190,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"BinOp":{"a":{"offset":-3,"register":"AP"},"b":{"Immediate":"0x0"},"op":"Add"}},"rhs":{"Immediate":"0x10000000000000000"}}}]],[10194,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"value":{"Deref":{"offset":-1,"register":"AP"}},"x":{"offset":0,"register":"AP"},"y":{"offset":1,"register":"AP"}}}]],[10213,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"BinOp":{"a":{"offset":-11,"register":"AP"},"b":{"Deref":{"offset":-6,"register":"AP"}},"op":"Add"}},"rhs":{"Immediate":"0x10000000000000000"}}}]],[10228,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"BinOp":{"a":{"offset":-3,"register":"AP"},"b":{"Deref":{"offset":-2,"register":"AP"}},"op":"Add"}},"rhs":{"Immediate":"0x10000000000000000"}}}]],[10277,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[10279,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[10308,[{"SystemCall":{"system":{"Deref":{"offset":0,"register":"FP"}}}}]],[10387,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[10414,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[10441,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[10613,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[10615,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[10644,[{"SystemCall":{"system":{"Deref":{"offset":0,"register":"FP"}}}}]],[10774,[{"SystemCall":{"system":{"Deref":{"offset":-8,"register":"AP"}}}}]],[10803,[{"SystemCall":{"system":{"Deref":{"offset":-8,"register":"AP"}}}}]],[10860,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[10878,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x10000000000000000"}}}]],[10888,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x10000000000000000"}}}]],[10896,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[10898,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[10928,[{"SystemCall":{"system":{"Deref":{"offset":4,"register":"FP"}}}}]],[10957,[{"SystemCall":{"system":{"Deref":{"offset":-7,"register":"AP"}}}}]],[10961,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[10963,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[10999,[{"SystemCall":{"system":{"Deref":{"offset":3,"register":"FP"}}}}]],[11010,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[11036,[{"SystemCall":{"system":{"Deref":{"offset":0,"register":"FP"}}}}]],[11075,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[11120,[{"SystemCall":{"system":{"Deref":{"offset":-2,"register":"AP"}}}}]],[11207,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[11343,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[11370,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[11459,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[11527,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[11531,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[11542,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[11568,[{"SystemCall":{"system":{"Deref":{"offset":-12,"register":"FP"}}}}]],[11605,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[11625,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[11629,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[11640,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[11667,[{"SystemCall":{"system":{"Deref":{"offset":-21,"register":"AP"}}}}]],[11693,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[11695,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[11723,[{"SystemCall":{"system":{"Deref":{"offset":0,"register":"FP"}}}}]],[11866,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[11870,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[11881,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[11907,[{"SystemCall":{"system":{"Deref":{"offset":-12,"register":"FP"}}}}]],[11957,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[11961,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[11972,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[11999,[{"SystemCall":{"system":{"Deref":{"offset":-20,"register":"AP"}}}}]],[12025,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[12027,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[12055,[{"SystemCall":{"system":{"Deref":{"offset":0,"register":"FP"}}}}]],[12181,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[12201,[{"SystemCall":{"system":{"Deref":{"offset":-4,"register":"FP"}}}}]],[12216,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-4,"register":"FP"},"b":{"Immediate":"0x5"},"op":"Add"}}}}]],[12247,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[12304,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"FP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[12308,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[12319,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[12343,[{"SystemCall":{"system":{"Deref":{"offset":-4,"register":"FP"}}}}]],[12351,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[12353,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[12387,[{"SystemCall":{"system":{"Deref":{"offset":-8,"register":"AP"}}}}]],[12395,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[12397,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[12430,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[12458,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[12500,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[12504,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[12515,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[12541,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[12549,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[12551,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[12585,[{"SystemCall":{"system":{"Deref":{"offset":-8,"register":"AP"}}}}]],[12593,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[12595,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[12629,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[12657,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[12704,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[12708,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[12719,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[12745,[{"SystemCall":{"system":{"Deref":{"offset":-6,"register":"FP"}}}}]],[12753,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[12755,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[12789,[{"SystemCall":{"system":{"Deref":{"offset":-8,"register":"AP"}}}}]],[12797,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[12799,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[12833,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[12861,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[12916,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[12944,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[12964,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[12968,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[12979,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[13006,[{"SystemCall":{"system":{"Deref":{"offset":-7,"register":"FP"}}}}]],[13014,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[13016,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[13050,[{"SystemCall":{"system":{"Deref":{"offset":-8,"register":"AP"}}}}]],[13058,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[13060,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[13083,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[13109,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[13131,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[13151,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[13155,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[13166,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[13194,[{"SystemCall":{"system":{"Deref":{"offset":-36,"register":"AP"}}}}]],[13210,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-42,"register":"AP"},"b":{"Immediate":"0x7"},"op":"Add"}}}}]],[13219,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[13223,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[13234,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[13261,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-58,"register":"AP"},"b":{"Immediate":"0xe"},"op":"Add"}}}}]],[13269,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[13271,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[13305,[{"SystemCall":{"system":{"Deref":{"offset":-8,"register":"AP"}}}}]],[13313,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[13315,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[13338,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[13364,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[13386,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[13406,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[13410,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[13421,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[13449,[{"SystemCall":{"system":{"Deref":{"offset":-35,"register":"AP"}}}}]],[13465,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-41,"register":"AP"},"b":{"Immediate":"0x7"},"op":"Add"}}}}]],[13469,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[13471,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[13504,[{"SystemCall":{"system":{"Deref":{"offset":0,"register":"FP"}}}}]],[13593,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[13616,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[13659,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[13773,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[13798,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[13845,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[13895,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[13899,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[13910,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[13936,[{"SystemCall":{"system":{"Deref":{"offset":-7,"register":"FP"}}}}]],[13944,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[13946,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[13980,[{"SystemCall":{"system":{"Deref":{"offset":-8,"register":"AP"}}}}]],[13988,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[13990,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[14039,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[14065,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[14087,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[14142,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[14188,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[14235,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[14291,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[14319,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[14342,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[14346,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[14357,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[14385,[{"SystemCall":{"system":{"Deref":{"offset":-7,"register":"FP"}}}}]],[14401,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-7,"register":"FP"},"b":{"Immediate":"0x7"},"op":"Add"}}}}]],[14405,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[14407,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[14440,[{"SystemCall":{"system":{"Deref":{"offset":0,"register":"FP"}}}}]],[14508,[{"SystemCall":{"system":{"Deref":{"offset":-6,"register":"FP"}}}}]],[14522,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[14526,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[14537,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[14564,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-6,"register":"FP"},"b":{"Immediate":"0x5"},"op":"Add"}}}}]],[14572,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[14574,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[14608,[{"SystemCall":{"system":{"Deref":{"offset":-8,"register":"AP"}}}}]],[14616,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[14618,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[14641,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[14667,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[14689,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[14765,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[14790,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[14837,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[14904,[{"SystemCall":{"system":{"Deref":{"offset":-6,"register":"FP"}}}}]],[14918,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[14922,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[14933,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[14960,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-6,"register":"FP"},"b":{"Immediate":"0x5"},"op":"Add"}}}}]],[14968,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[14970,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[15004,[{"SystemCall":{"system":{"Deref":{"offset":-8,"register":"AP"}}}}]],[15012,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[15014,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[15037,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[15063,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[15085,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[15161,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[15186,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[15233,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[15307,[{"SystemCall":{"system":{"Deref":{"offset":-12,"register":"FP"}}}}]],[15323,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-12,"register":"FP"},"b":{"Immediate":"0x7"},"op":"Add"}}}}]],[15339,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-12,"register":"FP"},"b":{"Immediate":"0xe"},"op":"Add"}}}}]],[15368,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[15395,[{"SystemCall":{"system":{"Deref":{"offset":-9,"register":"AP"}}}}]],[15421,[{"SystemCall":{"system":{"Deref":{"offset":-8,"register":"AP"}}}}]],[15573,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[15599,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[15625,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[15647,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[15674,[{"SystemCall":{"system":{"Deref":{"offset":-27,"register":"AP"}}}}]],[15692,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-34,"register":"AP"},"b":{"Immediate":"0x7"},"op":"Add"}}}}]],[15701,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[15705,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[15716,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[15743,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-50,"register":"AP"},"b":{"Immediate":"0xe"},"op":"Add"}}}}]],[15751,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[15753,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[15787,[{"SystemCall":{"system":{"Deref":{"offset":-8,"register":"AP"}}}}]],[15795,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[15797,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[15820,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[15846,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[15868,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[15888,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[15892,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[15903,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[15931,[{"SystemCall":{"system":{"Deref":{"offset":-35,"register":"AP"}}}}]],[15947,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-41,"register":"AP"},"b":{"Immediate":"0x7"},"op":"Add"}}}}]],[15951,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[15953,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[15987,[{"SystemCall":{"system":{"Deref":{"offset":0,"register":"FP"}}}}]],[16076,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[16099,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[16142,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[16256,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[16304,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[16330,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[16356,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[16378,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[16405,[{"SystemCall":{"system":{"Deref":{"offset":-27,"register":"AP"}}}}]],[16423,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-34,"register":"AP"},"b":{"Immediate":"0x7"},"op":"Add"}}}}]],[16432,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[16436,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[16447,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[16474,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-50,"register":"AP"},"b":{"Immediate":"0xe"},"op":"Add"}}}}]],[16482,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[16484,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[16518,[{"SystemCall":{"system":{"Deref":{"offset":-8,"register":"AP"}}}}]],[16526,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[16528,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[16551,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[16577,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[16599,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[16619,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[16623,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[16634,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[16662,[{"SystemCall":{"system":{"Deref":{"offset":-35,"register":"AP"}}}}]],[16678,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-41,"register":"AP"},"b":{"Immediate":"0x7"},"op":"Add"}}}}]],[16682,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[16684,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[16718,[{"SystemCall":{"system":{"Deref":{"offset":0,"register":"FP"}}}}]],[16807,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[16830,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[16873,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[16987,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[17065,[{"TestLessThan":{"dst":{"offset":4,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"}}}]],[17069,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":3,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[17079,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":-2,"register":"AP"}},"x":{"offset":-1,"register":"AP"},"y":{"offset":0,"register":"AP"}}}]],[17319,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-10,"register":"FP"}}}}]],[17377,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x816"},"rhs":{"Deref":{"offset":-5,"register":"AP"}}}}]],[17402,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[17452,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[17473,[{"SystemCall":{"system":{"Deref":{"offset":-3,"register":"FP"}}}}]],[17489,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[17493,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[17504,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[17531,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-3,"register":"FP"},"b":{"Immediate":"0x5"},"op":"Add"}}}}]],[17555,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[17621,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[17662,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[17666,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[17677,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[17705,[{"SystemCall":{"system":{"Deref":{"offset":-10,"register":"FP"}}}}]],[17772,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[17813,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[17817,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[17828,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[17856,[{"SystemCall":{"system":{"Deref":{"offset":-10,"register":"FP"}}}}]],[18233,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[18274,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[18278,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[18289,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[18316,[{"SystemCall":{"system":{"Deref":{"offset":-9,"register":"FP"}}}}]],[18324,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"BinOp":{"a":{"offset":-3,"register":"AP"},"b":{"Immediate":"0x0"},"op":"Add"}},"rhs":{"Immediate":"0x10000000000000000"}}}]],[18328,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"value":{"Deref":{"offset":-1,"register":"AP"}},"x":{"offset":0,"register":"AP"},"y":{"offset":1,"register":"AP"}}}]],[18359,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[18427,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Immediate":"0x6ea"},"rhs":{"Deref":{"offset":-7,"register":"FP"}}}}]],[18466,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[18484,[{"SystemCall":{"system":{"Deref":{"offset":-4,"register":"FP"}}}}]],[18498,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[18502,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[18513,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[18540,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-4,"register":"FP"},"b":{"Immediate":"0x5"},"op":"Add"}}}}]],[18564,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[18640,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[18644,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[18655,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[18681,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[18713,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[18717,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[18728,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[18758,[{"SystemCall":{"system":{"Deref":{"offset":-21,"register":"AP"}}}}]],[18767,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-25,"register":"AP"},"b":{"Immediate":"0x7"},"op":"Add"}}}}]],[18772,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[18774,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[18808,[{"SystemCall":{"system":{"Deref":{"offset":0,"register":"FP"}}}}]],[18921,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[18925,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[18936,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[18962,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[19015,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[19019,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[19030,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[19060,[{"SystemCall":{"system":{"Deref":{"offset":-21,"register":"AP"}}}}]],[19069,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-25,"register":"AP"},"b":{"Immediate":"0x7"},"op":"Add"}}}}]],[19074,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[19076,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[19110,[{"SystemCall":{"system":{"Deref":{"offset":0,"register":"FP"}}}}]],[19199,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[19203,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[19214,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[19240,[{"SystemCall":{"system":{"Deref":{"offset":-4,"register":"FP"}}}}]],[19263,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[19360,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[19396,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"FP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[19400,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[19411,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[19435,[{"SystemCall":{"system":{"Deref":{"offset":-4,"register":"FP"}}}}]],[19443,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[19445,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[19479,[{"SystemCall":{"system":{"Deref":{"offset":-8,"register":"AP"}}}}]],[19487,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-3,"register":"AP"}},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[19489,[{"DivMod":{"lhs":{"Deref":{"offset":-4,"register":"AP"}},"quotient":{"offset":3,"register":"AP"},"remainder":{"offset":4,"register":"AP"},"rhs":{"Immediate":"0x100000000000000000000000000000000"}}}]],[19522,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[19550,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[19611,[{"TestLessThan":{"dst":{"offset":0,"register":"AP"},"lhs":{"BinOp":{"a":{"offset":-1,"register":"AP"},"b":{"Immediate":"0x0"},"op":"Add"}},"rhs":{"Immediate":"0x100000000"}}}]],[19615,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"value":{"Deref":{"offset":-1,"register":"AP"}},"x":{"offset":0,"register":"AP"},"y":{"offset":1,"register":"AP"}}}]],[19637,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-2,"register":"AP"}}}}]],[19651,[{"TestLessThan":{"dst":{"offset":-1,"register":"AP"},"lhs":{"Deref":{"offset":0,"register":"AP"}},"rhs":{"Immediate":"0x100000000"}}}]],[19661,[{"TestLessThanOrEqual":{"dst":{"offset":0,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Deref":{"offset":-2,"register":"AP"}}}}]],[19684,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[19705,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[19726,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[19781,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[19785,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[19796,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[19822,[{"SystemCall":{"system":{"Deref":{"offset":-5,"register":"FP"}}}}]],[19831,[{"TestLessThan":{"dst":{"offset":5,"register":"AP"},"lhs":{"Deref":{"offset":-1,"register":"AP"}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"}}}]],[19835,[{"LinearSplit":{"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"scalar":{"Immediate":"0x110000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[19846,[{"LinearSplit":{"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"value":{"Deref":{"offset":4,"register":"AP"}},"x":{"offset":-2,"register":"AP"},"y":{"offset":-1,"register":"AP"}}}]],[19875,[{"SystemCall":{"system":{"BinOp":{"a":{"offset":-5,"register":"FP"},"b":{"Immediate":"0x7"},"op":"Add"}}}}]],[19879,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[19881,[{"AllocSegment":{"dst":{"offset":0,"register":"AP"}}}]],[19915,[{"SystemCall":{"system":{"Deref":{"offset":0,"register":"FP"}}}}]]],"prime":"0x800000000000011000000000000000000000000000000000000000000000001"} ================================================ FILE: crates/cheatnet/src/data/strk_erc20_casm.json ================================================ {"prime":"0x800000000000011000000000000000000000000000000000000000000000001","compiler_version":"2.10.0","bytecode":["0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xab","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x122a","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x7b","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x66","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x5aa2","0x482480017fff8000","0x5aa1","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xca6c","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x23","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127fe87fff8000","0x1104800180018000","0x298d","0x20680017fff7ffd","0xd","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x64","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x55a","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x95","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x1bf8","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x11","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x48127ffb7fff8000","0x482480017ffb8000","0x492","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x5a13","0x482480017fff8000","0x5a12","0x48127ffb7fff8000","0x480080007ffe8000","0xa0680017fff8000","0x9","0x4824800180007ffd","0x32d2","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff57fff","0x10780017fff7fff","0x5c","0x4824800180007ffd","0x32d2","0x400080007ff67fff","0x48127fff7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x6894a7eacac1683e1e290e1df9a86c47bc34cd609052ca3e176955bc0958ee","0x482480017ff38000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffc","0x400280037ffb7ffd","0x480280057ffb8000","0x20680017fff7fff","0x39","0x480280047ffb8000","0x480280067ffb8000","0x482680017ffb8000","0x7","0x48127ffd7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffc","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff57ffc","0x480080017ff47ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff37ffd","0x10780017fff7fff","0x18","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffb","0x480080007ff67ffd","0x480080017ff57ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff47ffe","0x40780017fff7fff","0x1","0x400080007fff7ff7","0x482480017ff38000","0x3","0x482480017ff88000","0x384","0x48127ff67fff8000","0x480680017fff8000","0x0","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4e6f6e20436f6e747261637441646472657373","0x400080007ffe7fff","0x482480017ff18000","0x3","0x48127ff67fff8000","0x48127ff47fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0xa","0x480280047ffb8000","0x48127ffc7fff8000","0x482480017ffe8000","0x712","0x482680017ffb8000","0x8","0x480280067ffb8000","0x480280077ffb8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017ff38000","0x1","0x48127ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff98000","0x1","0x482680017ffa8000","0x21b6","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x15d","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x12d","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x118","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xe4","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xc8","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xa6","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x8a","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x58d1","0x482480017fff8000","0x58d0","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1cc64","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x4c","0x48307ffe80007ffa","0x400080007ff37fff","0x48127fff7fff8000","0x482480017ff28000","0x1","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffb7fff","0x400280017ffb7ffd","0x480280037ffb8000","0x20680017fff7fff","0x25","0x480280027ffb8000","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffd7fff8000","0x480a7ff87fff8000","0x482680017ffb8000","0x5","0x480080027ffb8000","0x48127fcb7fff8000","0x48127fd97fff8000","0x48127fe37fff8000","0x1104800180018000","0x2861","0x20680017fff7ffd","0xe","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x482480017ff78000","0x258","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff97fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x14","0x480280027ffb8000","0x1104800180018000","0x588e","0x482480017fff8000","0x588d","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x19f14","0x480a7ff87fff8000","0x48127ff47fff8000","0x48307ffd7ff68000","0x482680017ffb8000","0x6","0x480280047ffb8000","0x480280057ffb8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x6","0xa0680017fff8000","0x7","0x482680017ffa8000","0xffffffffffffffffffffffffffffe30e","0x400280007ff97fff","0x10780017fff7fff","0x234","0x4825800180007ffa","0x1cf2","0x400280007ff97fff","0x482680017ff98000","0x1","0x48127ffe7fff8000","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x205","0x40137fff7fff8002","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4825800180048002","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x1ef","0x484480017fff8001","0x8000000000000000000000000000000","0x48317fff80008002","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x480080007fef8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x1bb","0x40137fff7fff8003","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4825800180048003","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x1a5","0x484480017fff8001","0x8000000000000000000000000000000","0x48317fff80008003","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x171","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x155","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x133","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x117","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xfc","0x400180007ff68005","0x482480017ff68000","0x1","0x48127ff67fff8000","0x48127ffc7fff8000","0x40137fec7fff8000","0x40137ff77fff8001","0x48307ffd80007ffe","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ffb8000","0x1","0x48127ffb7fff8000","0x480680017fff8000","0x0","0x48127ff87fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xcc","0x400180007fff8004","0x48127ffb7fff8000","0xa0680017fff8000","0x12","0x4825800180008004","0x10000000000000000","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480080007fef7fff","0x482480017ffe8000","0xefffffffffffffdeffffffffffffffff","0x480080017fed7fff","0x400080027fec7ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0xb4","0x402780017fff7fff","0x1","0x400180007ff28004","0x4826800180048000","0xffffffffffffffff0000000000000000","0x400080017ff17fff","0x482480017ff18000","0x2","0x48127ffc7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x82","0x40780017fff7fff","0x1","0x48127ff77fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ffb7fff8000","0x48127ffa7fff8000","0x480080007ff88000","0x1104800180018000","0x275a","0x20680017fff7ffa","0x6b","0x48127ff97fff8000","0x20680017fff7ffc","0x63","0x48127fff7fff8000","0x48307ff980007ffa","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ff27fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x56c4","0x482480017fff8000","0x56c3","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x8","0x482480017fff8000","0x2dc08","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007fea7fff","0x10780017fff7fff","0x2a","0x48307ffe80007ffa","0x400080007feb7fff","0x482480017feb8000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x480a80027fff8000","0x480a80037fff8000","0x480a80007fff8000","0x480a80017fff8000","0x480a80057fff8000","0x480a80047fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x1104800180018000","0x277d","0x20680017fff7ffd","0xd","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x64","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fe78000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48127ff77fff8000","0x482480017ffe8000","0x5be","0x10780017fff7fff","0xf","0x480a7ff87fff8000","0x48127ff77fff8000","0x482480017ff78000","0x8de","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ff87fff8000","0x482480017ffa8000","0x1158","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202336","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017fec8000","0x3","0x482480017ff78000","0x12a2","0x10780017fff7fff","0x5","0x48127ff47fff8000","0x482480017ffa8000","0x187e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202335","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202334","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x1ea0","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1af4","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x213e","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x217a","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x27c4","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202333","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x2ae4","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x305c","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x337c","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x38f4","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x20b2","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x136","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x106","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xf1","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xbd","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xa1","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x7f","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x63","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x5512","0x482480017fff8000","0x5511","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x2503a","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x25","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127fd27fff8000","0x48127fe07fff8000","0x48127fea7fff8000","0x1104800180018000","0x27d9","0x20680017fff7ffd","0xd","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x64","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x136","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x106","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xf1","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xbd","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xa1","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x7f","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x63","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x53c6","0x482480017fff8000","0x53c5","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x2503a","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x25","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127fd27fff8000","0x48127fe07fff8000","0x48127fea7fff8000","0x1104800180018000","0x2734","0x20680017fff7ffd","0xd","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x64","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x136","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x106","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xf1","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xbd","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xa1","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x7f","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x63","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x527a","0x482480017fff8000","0x5279","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x2503a","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x25","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127fd27fff8000","0x48127fe07fff8000","0x48127fea7fff8000","0x1104800180018000","0x2541","0x20680017fff7ffd","0xd","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x64","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x136","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x106","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xf1","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xbd","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xa1","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x7f","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x63","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x512e","0x482480017fff8000","0x512d","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x2503a","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x25","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127fd27fff8000","0x48127fe07fff8000","0x48127fea7fff8000","0x1104800180018000","0x249c","0x20680017fff7ffd","0xd","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x64","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x95","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x1bf8","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x11","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x48127ffb7fff8000","0x482480017ffb8000","0x492","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x507a","0x482480017fff8000","0x5079","0x48127ffb7fff8000","0x480080007ffe8000","0xa0680017fff8000","0x9","0x4824800180007ffd","0x3336","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff57fff","0x10780017fff7fff","0x5c","0x4824800180007ffd","0x3336","0x400080007ff67fff","0x48127fff7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x3fc801c47df4de8d5835f8bfd4d0b8823ba63e5a3f278086901402d680abfc","0x482480017ff38000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffc","0x400280037ffb7ffd","0x480280057ffb8000","0x20680017fff7fff","0x39","0x480280047ffb8000","0x480280067ffb8000","0x482680017ffb8000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x12","0x4824800180007ffc","0x10000000000000000","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480080007ff57fff","0x482480017ffe8000","0xefffffffffffffdeffffffffffffffff","0x480080017ff37fff","0x400080027ff27ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x16","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x482480017ffc8000","0xffffffffffffffff0000000000000000","0x400080017ff77fff","0x40780017fff7fff","0x1","0x400080007fff7ffa","0x482480017ff68000","0x2","0x482480017ffb8000","0x55a","0x48127ff97fff8000","0x480680017fff8000","0x0","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f7265553634202d206e6f6e20753634","0x400080007ffe7fff","0x482480017ff08000","0x3","0x48127ff57fff8000","0x48127ff37fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0xa","0x480280047ffb8000","0x48127ffc7fff8000","0x482480017ffe8000","0x776","0x482680017ffb8000","0x8","0x480280067ffb8000","0x480280077ffb8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017ff38000","0x1","0x48127ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff98000","0x1","0x482680017ffa8000","0x21b6","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0xffffffffffffffffffffffffffffdc06","0x400280007ff87fff","0x10780017fff7fff","0x91","0x4825800180007ffa","0x23fa","0x400280007ff87fff","0x482680017ff88000","0x1","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x1104800180018000","0x2416","0x48127f8e7fff8000","0x20680017fff7ff5","0x7a","0x48127fff7fff8000","0x20680017fff7ff7","0x66","0x48127fff7fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x13","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff77fff8000","0x48127fee7fff8000","0x480a7ff97fff8000","0x482480017ff98000","0x55a","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4fc5","0x482480017fff8000","0x4fc4","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x482480017fff8000","0x6630","0xa0680017fff8000","0x8","0x48307ffe80007ffb","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007fe77fff","0x10780017fff7fff","0x2d","0x48307ffe80007ffb","0x400080007fe87fff","0x482480017fe88000","0x1","0x48127ffe7fff8000","0x480a7ff77fff8000","0x480a7ff97fff8000","0x480a7ffb7fff8000","0x48127fe87fff8000","0x48127fe87fff8000","0x48127fe87fff8000","0x48127fe87fff8000","0x48127fe87fff8000","0x48127fe87fff8000","0x1104800180018000","0x2499","0x20680017fff7ffd","0x10","0x40780017fff7fff","0x1","0x400080007fff7ffe","0x48127ff97fff8000","0x48127ff67fff8000","0x48127ff87fff8000","0x48127ff57fff8000","0x48127ff77fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff97fff8000","0x482480017ff68000","0xc8","0x48127ff87fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff77fff8000","0x482480017fe48000","0x1","0x480a7ff97fff8000","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff77fff8000","0x48127ff07fff8000","0x480a7ff97fff8000","0x482480017ffa8000","0x686","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x480a7ff77fff8000","0x48127ff37fff8000","0x480a7ff97fff8000","0x482480017ffc8000","0x87a","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff77fff8000","0x48127ff77fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff77fff8000","0x482680017ff88000","0x1","0x480a7ff97fff8000","0x482680017ffa8000","0x20ee","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0xffffffffffffffffffffffffffffdba2","0x400280007ff87fff","0x10780017fff7fff","0x91","0x4825800180007ffa","0x245e","0x400280007ff87fff","0x482680017ff88000","0x1","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x1104800180018000","0x236e","0x48127f8e7fff8000","0x20680017fff7ff5","0x7a","0x48127fff7fff8000","0x20680017fff7ff7","0x66","0x48127fff7fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x13","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff77fff8000","0x48127fee7fff8000","0x480a7ff97fff8000","0x482480017ff98000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4f1d","0x482480017fff8000","0x4f1c","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1e1a4","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007fe67fff","0x10780017fff7fff","0x2b","0x48307ffe80007ffa","0x400080007fe77fff","0x482480017fe78000","0x1","0x48127ffe7fff8000","0x480a7ff77fff8000","0x480a7ff97fff8000","0x480a7ffb7fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x1104800180018000","0x24b1","0x20680017fff7ffd","0xe","0x40780017fff7fff","0x1","0x48127ff97fff8000","0x48127ff67fff8000","0x48127ff87fff8000","0x48127ff57fff8000","0x48127ff77fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff97fff8000","0x482480017ff68000","0x64","0x48127ff87fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff77fff8000","0x482480017fe38000","0x1","0x480a7ff97fff8000","0x48127ff37fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff77fff8000","0x48127ff07fff8000","0x480a7ff97fff8000","0x482480017ffa8000","0x6ea","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x480a7ff77fff8000","0x48127ff37fff8000","0x480a7ff97fff8000","0x482480017ffc8000","0x8de","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff77fff8000","0x48127ff77fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff77fff8000","0x482680017ff88000","0x1","0x480a7ff97fff8000","0x482680017ffa8000","0x20ee","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0xffffffffffffffffffffffffffffdba2","0x400280007ff87fff","0x10780017fff7fff","0x91","0x4825800180007ffa","0x245e","0x400280007ff87fff","0x482680017ff88000","0x1","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x1104800180018000","0x22c6","0x48127f8e7fff8000","0x20680017fff7ff5","0x7a","0x48127fff7fff8000","0x20680017fff7ff7","0x66","0x48127fff7fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x13","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff77fff8000","0x48127fee7fff8000","0x480a7ff97fff8000","0x482480017ff98000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4e75","0x482480017fff8000","0x4e74","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x5","0x482480017fff8000","0x1ea28","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007fe67fff","0x10780017fff7fff","0x2b","0x48307ffe80007ffa","0x400080007fe77fff","0x482480017fe78000","0x1","0x48127ffe7fff8000","0x480a7ff77fff8000","0x480a7ff97fff8000","0x480a7ffb7fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x1104800180018000","0x2584","0x20680017fff7ffd","0xe","0x40780017fff7fff","0x1","0x48127ff97fff8000","0x48127ff67fff8000","0x48127ff87fff8000","0x48127ff57fff8000","0x48127ff77fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff97fff8000","0x482480017ff68000","0x64","0x48127ff87fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff77fff8000","0x482480017fe38000","0x1","0x480a7ff97fff8000","0x48127ff37fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff77fff8000","0x48127ff07fff8000","0x480a7ff97fff8000","0x482480017ffa8000","0x6ea","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x480a7ff77fff8000","0x48127ff37fff8000","0x480a7ff97fff8000","0x482480017ffc8000","0x8de","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff77fff8000","0x48127ff77fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff77fff8000","0x482680017ff88000","0x1","0x480a7ff97fff8000","0x482680017ffa8000","0x20ee","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0xffffffffffffffffffffffffffffdba2","0x400280007ff87fff","0x10780017fff7fff","0x91","0x4825800180007ffa","0x245e","0x400280007ff87fff","0x482680017ff88000","0x1","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x1104800180018000","0x221e","0x48127f8e7fff8000","0x20680017fff7ff5","0x7a","0x48127fff7fff8000","0x20680017fff7ff7","0x66","0x48127fff7fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x13","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff77fff8000","0x48127fee7fff8000","0x480a7ff97fff8000","0x482480017ff98000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4dcd","0x482480017fff8000","0x4dcc","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x6","0x482480017fff8000","0x391e8","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007fe67fff","0x10780017fff7fff","0x2b","0x48307ffe80007ffa","0x400080007fe77fff","0x482480017fe78000","0x1","0x48127ffe7fff8000","0x480a7ff77fff8000","0x480a7ff97fff8000","0x480a7ffb7fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x48127fe77fff8000","0x1104800180018000","0x25bf","0x20680017fff7ffd","0xe","0x40780017fff7fff","0x1","0x48127ff97fff8000","0x48127ff67fff8000","0x48127ff87fff8000","0x48127ff57fff8000","0x48127ff77fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff97fff8000","0x482480017ff68000","0x64","0x48127ff87fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff77fff8000","0x482480017fe38000","0x1","0x480a7ff97fff8000","0x48127ff37fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff77fff8000","0x48127ff07fff8000","0x480a7ff97fff8000","0x482480017ffa8000","0x6ea","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x480a7ff77fff8000","0x48127ff37fff8000","0x480a7ff97fff8000","0x482480017ffc8000","0x8de","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff77fff8000","0x48127ff77fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff77fff8000","0x482680017ff88000","0x1","0x480a7ff97fff8000","0x482680017ffa8000","0x20ee","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x112","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0xfd2","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xf6","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x48127ffc7fff8000","0x480280007ffc8000","0x48307ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffd7fff8000","0x482480017ffa8000","0x1","0x48127ffa7fff8000","0x480680017fff8000","0x0","0x480080007ff78000","0x10780017fff7fff","0x9","0x48127ffd7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xc8","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007fee7ffc","0x480080017fed7ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027fec7ffd","0x10780017fff7fff","0xb3","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007fef7ffd","0x480080017fee7ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027fed7ffe","0x482480017fed8000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4cf4","0x482480017fff8000","0x4cf3","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x371e","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x70","0x48307ffe80007ffa","0x400080007ff37fff","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x400280007ff87fff","0x400280017ff87fe5","0x480280027ff88000","0x400280037ff87fff","0x400280047ff87fea","0x480280057ff88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080017fec7ffc","0x480080027feb7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080037fe97ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080017fec7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080027fea7ffd","0x400080037fe97ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x482680017ff88000","0x6","0x482480017fe68000","0x4","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffc","0x400280037ffb7ffa","0x480280057ffb8000","0x20680017fff7fff","0x2d","0x480280047ffb8000","0x480280067ffb8000","0x482680017ffb8000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x40780017fff7fff","0x1","0x480680017fff8000","0x1","0x48307ffd80007fff","0x20680017fff7fff","0x7","0x48127ffb7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x6","0x482480017ffb8000","0x64","0x480680017fff8000","0x1","0x400080007ffb7fff","0x48127ff17fff8000","0x48127ff17fff8000","0x48127ffc7fff8000","0x48127ff47fff8000","0x480680017fff8000","0x0","0x48127ff67fff8000","0x482480017ff58000","0x1","0x208b7fff7fff7ffe","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x482480017ffd8000","0x5dc","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017fec8000","0x3","0x482480017ff88000","0x55a","0x10780017fff7fff","0x5","0x48127ff37fff8000","0x482480017ffa8000","0xa78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0xff0","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xa9","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x1874","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x8d","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x48127ffc7fff8000","0x480280007ffc8000","0x48307ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ff57fff8000","0x482480017ff98000","0x55a","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4bfd","0x482480017fff8000","0x4bfc","0x48127ffa7fff8000","0x480080007ffe8000","0x480080007fff8000","0x482480017fff8000","0x3142","0xa0680017fff8000","0x8","0x48307ffe80007ffb","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007fee7fff","0x10780017fff7fff","0x52","0x48307ffe80007ffb","0x400080007fef7fff","0x480680017fff8000","0x2e9f66c6eea14532c94ad25405a4fcb32faa4969559c128d837caa0ec50a655","0x400280007ff87fff","0x400280017ff87ff4","0x480280027ff88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080017fe97ffc","0x480080027fe87ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080037fe67ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080017fe97ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080027fe77ffd","0x400080037fe67ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff67fff8000","0x480680017fff8000","0x0","0x482680017ff88000","0x3","0x482480017fe38000","0x4","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffc","0x400280037ffb7ffa","0x480280057ffb8000","0x20680017fff7fff","0x12","0x480280047ffb8000","0x40780017fff7fff","0x1","0x480280067ffb8000","0x400080007ffe7fff","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ffb7fff8000","0x482680017ffb8000","0x7","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x482480017ffd8000","0x12c","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017feb8000","0x1","0x48127ff57fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x74e","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xfa","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x122a","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xca","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xb5","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4b17","0x482480017fff8000","0x4b16","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x3782","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x72","0x48307ffe80007ffa","0x400080007ff37fff","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x480680017fff8000","0x3711c9d994faf6055172091cb841fd4831aa743e6f3315163b06a122c841846","0x400280007ff87ffe","0x400280017ff87fff","0x480280027ff88000","0x400280037ff87fff","0x400280047ff87fe9","0x480280057ff88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080017feb7ffc","0x480080027fea7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080037fe87ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080017feb7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080027fe97ffd","0x400080037fe87ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff47fff8000","0x480680017fff8000","0x0","0x482680017ff88000","0x6","0x482480017fe58000","0x4","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffc","0x400280037ffb7ffa","0x480280057ffb8000","0x20680017fff7fff","0x2d","0x480280047ffb8000","0x480280067ffb8000","0x482680017ffb8000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x40780017fff7fff","0x1","0x480680017fff8000","0x1","0x48307ffd80007fff","0x20680017fff7fff","0x7","0x48127ffb7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x6","0x482480017ffb8000","0x64","0x480680017fff8000","0x1","0x400080007ffb7fff","0x48127ff17fff8000","0x48127ff17fff8000","0x48127ffc7fff8000","0x48127ff47fff8000","0x480680017fff8000","0x0","0x48127ff67fff8000","0x482480017ff58000","0x1","0x208b7fff7fff7ffe","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x482480017ffd8000","0x5dc","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x55a","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xfa","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x122a","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xca","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xb5","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4a07","0x482480017fff8000","0x4a06","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x3782","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x72","0x48307ffe80007ffa","0x400080007ff37fff","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x480680017fff8000","0x251e864ca2a080f55bce5da2452e8cfcafdbc951a3e7fff5023d558452ec228","0x400280007ff87ffe","0x400280017ff87fff","0x480280027ff88000","0x400280037ff87fff","0x400280047ff87fe9","0x480280057ff88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080017feb7ffc","0x480080027fea7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080037fe87ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080017feb7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080027fe97ffd","0x400080037fe87ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff47fff8000","0x480680017fff8000","0x0","0x482680017ff88000","0x6","0x482480017fe58000","0x4","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffc","0x400280037ffb7ffa","0x480280057ffb8000","0x20680017fff7fff","0x2d","0x480280047ffb8000","0x480280067ffb8000","0x482680017ffb8000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x40780017fff7fff","0x1","0x480680017fff8000","0x1","0x48307ffd80007fff","0x20680017fff7fff","0x7","0x48127ffb7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x6","0x482480017ffb8000","0x64","0x480680017fff8000","0x1","0x400080007ffb7fff","0x48127ff17fff8000","0x48127ff17fff8000","0x48127ffc7fff8000","0x48127ff47fff8000","0x480680017fff8000","0x0","0x48127ff67fff8000","0x482480017ff58000","0x1","0x208b7fff7fff7ffe","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x482480017ffd8000","0x5dc","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x55a","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xdf","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x122a","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xaf","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x9a","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x48f7","0x482480017fff8000","0x48f6","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x2413a","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x57","0x48307ffe80007ffa","0x400080007ff37fff","0x48127fff7fff8000","0x482480017ff28000","0x1","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffb7fff","0x400280017ffb7ffd","0x480280037ffb8000","0x20680017fff7fff","0x30","0x480280027ffb8000","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffd7fff8000","0x480a7ff87fff8000","0x482680017ffb8000","0x5","0x480680017fff8000","0x3711c9d994faf6055172091cb841fd4831aa743e6f3315163b06a122c841846","0x48127fe17fff8000","0x480680017fff8000","0x7","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127fdb7fff8000","0x480080027ff38000","0x1104800180018000","0x23d1","0x20680017fff7ffd","0xe","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x482480017ff78000","0x258","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff97fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x14","0x480280027ffb8000","0x1104800180018000","0x48a9","0x482480017fff8000","0x48a8","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x213ea","0x480a7ff87fff8000","0x48127ff47fff8000","0x48307ffd7ff68000","0x482680017ffb8000","0x6","0x480280047ffb8000","0x480280057ffb8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x55a","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xdf","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x122a","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xaf","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x9a","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4802","0x482480017fff8000","0x4801","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x24072","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x57","0x48307ffe80007ffa","0x400080007ff37fff","0x48127fff7fff8000","0x482480017ff28000","0x1","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffb7fff","0x400280017ffb7ffd","0x480280037ffb8000","0x20680017fff7fff","0x30","0x480280027ffb8000","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffd7fff8000","0x480a7ff87fff8000","0x482680017ffb8000","0x5","0x480680017fff8000","0x3711c9d994faf6055172091cb841fd4831aa743e6f3315163b06a122c841846","0x48127fe17fff8000","0x480680017fff8000","0x5","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127fdb7fff8000","0x480080027ff38000","0x1104800180018000","0x242f","0x20680017fff7ffd","0xe","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x482480017ff78000","0x258","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff97fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x14","0x480280027ffb8000","0x1104800180018000","0x47b4","0x482480017fff8000","0x47b3","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x21322","0x480a7ff87fff8000","0x48127ff47fff8000","0x48307ffd7ff68000","0x482680017ffb8000","0x6","0x480280047ffb8000","0x480280057ffb8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x55a","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xdf","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x122a","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xaf","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x9a","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x470d","0x482480017fff8000","0x470c","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x2413a","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x57","0x48307ffe80007ffa","0x400080007ff37fff","0x48127fff7fff8000","0x482480017ff28000","0x1","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffb7fff","0x400280017ffb7ffd","0x480280037ffb8000","0x20680017fff7fff","0x30","0x480280027ffb8000","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffd7fff8000","0x480a7ff87fff8000","0x482680017ffb8000","0x5","0x480680017fff8000","0x251e864ca2a080f55bce5da2452e8cfcafdbc951a3e7fff5023d558452ec228","0x48127fe17fff8000","0x480680017fff8000","0x3","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127fdb7fff8000","0x480080027ff38000","0x1104800180018000","0x21e7","0x20680017fff7ffd","0xe","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x482480017ff78000","0x258","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff97fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x14","0x480280027ffb8000","0x1104800180018000","0x46bf","0x482480017fff8000","0x46be","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x213ea","0x480a7ff87fff8000","0x48127ff47fff8000","0x48307ffd7ff68000","0x482680017ffb8000","0x6","0x480280047ffb8000","0x480280057ffb8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x55a","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xdf","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x122a","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xaf","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x9a","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4618","0x482480017fff8000","0x4617","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x24072","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x57","0x48307ffe80007ffa","0x400080007ff37fff","0x48127fff7fff8000","0x482480017ff28000","0x1","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffb7fff","0x400280017ffb7ffd","0x480280037ffb8000","0x20680017fff7fff","0x30","0x480280027ffb8000","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffd7fff8000","0x480a7ff87fff8000","0x482680017ffb8000","0x5","0x480680017fff8000","0x251e864ca2a080f55bce5da2452e8cfcafdbc951a3e7fff5023d558452ec228","0x48127fe17fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127fdb7fff8000","0x480080027ff38000","0x1104800180018000","0x2245","0x20680017fff7ffd","0xe","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x482480017ff78000","0x258","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff97fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x14","0x480280027ffb8000","0x1104800180018000","0x45ca","0x482480017fff8000","0x45c9","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x9","0x482480017fff8000","0x21322","0x480a7ff87fff8000","0x48127ff47fff8000","0x48307ffd7ff68000","0x482680017ffb8000","0x6","0x480280047ffb8000","0x480280057ffb8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x55a","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x7c","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x1810","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x60","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x48127ffc7fff8000","0x480280007ffc8000","0x48307ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ff57fff8000","0x482480017ff98000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x454a","0x482480017fff8000","0x4549","0x48127ffa7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1451e","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007fed7fff","0x10780017fff7fff","0x23","0x48307ffe80007ffa","0x400080007fee7fff","0x482480017fee8000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127ff07fff8000","0x1104800180018000","0x22c8","0x20680017fff7ffd","0xd","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x64","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fea8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x7b2","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x68","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x1bf8","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x11","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x48127ffb7fff8000","0x482480017ffb8000","0x492","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x44c3","0x482480017fff8000","0x44c2","0x48127ffb7fff8000","0x480080007ffe8000","0xa0680017fff8000","0x9","0x4824800180007ffd","0x2af8","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff57fff","0x10780017fff7fff","0x2f","0x4824800180007ffd","0x2af8","0x400080007ff67fff","0x48127fff7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x341c1bdfd89f69748aa00b5742b03adbffd79b8e80cab5c50d91cd8c2a79be1","0x482480017ff38000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffc","0x400280037ffb7ffd","0x480280057ffb8000","0x20680017fff7fff","0x11","0x480280047ffb8000","0x40780017fff7fff","0x1","0x480280067ffb8000","0x400080007ffe7fff","0x48127ffa7fff8000","0x48127ffc7fff8000","0x482680017ffb8000","0x7","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x480280047ffb8000","0x48127ffc7fff8000","0x482480017ffe8000","0x12c","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017ff38000","0x1","0x48127ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff98000","0x1","0x482680017ffa8000","0x21b6","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x68","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x1bf8","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x11","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x48127ffb7fff8000","0x482480017ffb8000","0x492","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4446","0x482480017fff8000","0x4445","0x48127ffb7fff8000","0x480080007ffe8000","0xa0680017fff8000","0x9","0x4824800180007ffd","0x2af8","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff57fff","0x10780017fff7fff","0x2f","0x4824800180007ffd","0x2af8","0x400080007ff67fff","0x48127fff7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0xb6ce5410fca59d078ee9b2a4371a9d684c530d697c64fbef0ae6d5e8f0ac72","0x482480017ff38000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffc","0x400280037ffb7ffd","0x480280057ffb8000","0x20680017fff7fff","0x11","0x480280047ffb8000","0x40780017fff7fff","0x1","0x480280067ffb8000","0x400080007ffe7fff","0x48127ffa7fff8000","0x48127ffc7fff8000","0x482680017ffb8000","0x7","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x480280047ffb8000","0x48127ffc7fff8000","0x482480017ffe8000","0x12c","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017ff38000","0x1","0x48127ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff98000","0x1","0x482680017ffa8000","0x21b6","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x95","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x1bf8","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x11","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x48127ffb7fff8000","0x482480017ffb8000","0x492","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x43c9","0x482480017fff8000","0x43c8","0x48127ffb7fff8000","0x480080007ffe8000","0xa0680017fff8000","0x9","0x4824800180007ffd","0x3336","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff57fff","0x10780017fff7fff","0x5c","0x4824800180007ffd","0x3336","0x400080007ff67fff","0x48127fff7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x1f0d4aa99431d246bac9b8e48c33e888245b15e9678f64f9bdfc8823dc8f979","0x482480017ff38000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffc","0x400280037ffb7ffd","0x480280057ffb8000","0x20680017fff7fff","0x39","0x480280047ffb8000","0x480280067ffb8000","0x482680017ffb8000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x12","0x4824800180007ffc","0x100","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480080007ff57fff","0x482480017ffe8000","0xefffffffffffffde00000000000000ff","0x480080017ff37fff","0x400080027ff27ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x16","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x482480017ffc8000","0xffffffffffffffffffffffffffffff00","0x400080017ff77fff","0x40780017fff7fff","0x1","0x400080007fff7ffa","0x482480017ff68000","0x2","0x482480017ffb8000","0x55a","0x48127ff97fff8000","0x480680017fff8000","0x0","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f72655538202d206e6f6e207538","0x400080007ffe7fff","0x482480017ff08000","0x3","0x48127ff57fff8000","0x48127ff37fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0xa","0x480280047ffb8000","0x48127ffc7fff8000","0x482480017ffe8000","0x776","0x482680017ffb8000","0x8","0x480280067ffb8000","0x480280077ffb8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017ff38000","0x1","0x48127ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff98000","0x1","0x482680017ffa8000","0x21b6","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x5e","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x1bf8","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x11","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x48127ffb7fff8000","0x482480017ffb8000","0x492","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x431f","0x482480017fff8000","0x431e","0x48127ffb7fff8000","0x480080007ffe8000","0xa0680017fff8000","0x9","0x4824800180007ffd","0x6bc6","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff57fff","0x10780017fff7fff","0x25","0x4824800180007ffd","0x6bc6","0x400080007ff67fff","0x482480017ff68000","0x1","0x48127ffe7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x110e2f729c9c2b988559994a3daccd838cf52faf88e18101373e67dd061455a","0x1104800180018000","0x2129","0x20680017fff7ffd","0xf","0x40780017fff7fff","0x1","0x400080007fff7ffd","0x400080017fff7ffe","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x0","0x48127ffb7fff8000","0x482480017ffa8000","0x2","0x208b7fff7fff7ffe","0x48127ffa7fff8000","0x482480017ffa8000","0x12c","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017ff38000","0x1","0x48127ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff98000","0x1","0x482680017ffa8000","0x21b6","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xae","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x128e","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x7e","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x69","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x55a","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x427a","0x482480017fff8000","0x4279","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x482480017fff8000","0x6e82","0xa0680017fff8000","0x8","0x48307ffe80007ffb","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff37fff","0x10780017fff7fff","0x28","0x48307ffe80007ffb","0x400080007ff47fff","0x482480017ff48000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x48127fe87fff8000","0x1104800180018000","0x2142","0x20680017fff7ffd","0x10","0x40780017fff7fff","0x1","0x400080007fff7ffd","0x400080017fff7ffe","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x482480017ff98000","0x2","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x12c","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017ff08000","0x1","0x48127ff57fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x4f6","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa14","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xfa","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x9ec","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xca","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xb5","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x480080007fef8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x81","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x6c","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4185","0x482480017fff8000","0x4184","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x7012","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x29","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x3c87bf42ed4f01f11883bf54f43d91d2cbbd5fec26d1df9c74c57ae138800a4","0x48127fd97fff8000","0x48127fe67fff8000","0x1104800180018000","0x2113","0x20680017fff7ffd","0x10","0x40780017fff7fff","0x1","0x400080007fff7ffd","0x400080017fff7ffe","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x482480017ff98000","0x2","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x12c","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x55a","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0xd98","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x12b6","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x161","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x131","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x11c","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xe8","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xcc","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xaa","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x8e","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x4040","0x482480017fff8000","0x403f","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x21962","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x50","0x48307ffe80007ffa","0x400080007ff37fff","0x48127fff7fff8000","0x482480017ff28000","0x1","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffb7fff","0x400280017ffb7ffd","0x480280037ffb8000","0x20680017fff7fff","0x29","0x480280027ffb8000","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffd7fff8000","0x480a7ff87fff8000","0x482680017ffb8000","0x5","0x480080027ffb8000","0x48127fcb7fff8000","0x48127fd97fff8000","0x48127fe37fff8000","0x1104800180018000","0x208e","0x20680017fff7ffd","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x1","0x400080007ffe7fff","0x48127ff97fff8000","0x48127ff67fff8000","0x482480017ff68000","0x190","0x48127ff77fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff97fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x14","0x480280027ffb8000","0x1104800180018000","0x3ff9","0x482480017fff8000","0x3ff8","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1ec12","0x480a7ff87fff8000","0x48127ff47fff8000","0x48307ffd7ff68000","0x482680017ffb8000","0x6","0x480280047ffb8000","0x480280057ffb8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x4","0xa0680017fff8000","0x7","0x482680017ffa8000","0xfffffffffffffffffffffffffffffc68","0x400280007ff97fff","0x10780017fff7fff","0x1cb","0x4825800180007ffa","0x398","0x400280007ff97fff","0x482680017ff98000","0x1","0x48127ffe7fff8000","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x19c","0x40137fff7fff8003","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4825800180048003","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x186","0x484480017fff8001","0x8000000000000000000000000000000","0x48317fff80008003","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x480080007fef8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x152","0x40137fff7fff8002","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4825800180048002","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x13c","0x484480017fff8001","0x8000000000000000000000000000000","0x48317fff80008002","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x108","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xec","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xca","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xae","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3e95","0x482480017fff8000","0x3e94","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x8","0x482480017fff8000","0x3479c","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x70","0x48307ffe80007ffa","0x400080007ff37fff","0x48127fff7fff8000","0x482480017ff28000","0x1","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffb7fff","0x400280017ffb7ffd","0x480280037ffb8000","0x20680017fff7fff","0x49","0x480280027ffb8000","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffd7fff8000","0x480a7ff87fff8000","0x482680017ffb8000","0x5","0x480a80037fff8000","0x480080027ffa8000","0x40137fd97fff8000","0x40137fe47fff8001","0x480a80007fff8000","0x480a80017fff8000","0x1104800180018000","0x22ba","0x20680017fff7ffd","0x26","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480a80037fff8000","0x480a80027fff8000","0x480a80007fff8000","0x480a80017fff8000","0x1104800180018000","0x1ed5","0x20680017fff7ffd","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x1","0x400080007ffe7fff","0x48127ff97fff8000","0x48127ff67fff8000","0x482480017ff68000","0x190","0x48127ff77fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff97fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x26","0x1104800180018000","0x3e41","0x482480017fff8000","0x3e40","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1eb4a","0x48127ff47fff8000","0x48127ff17fff8000","0x48307ffd7ff18000","0x48127ff27fff8000","0x48127ff37fff8000","0x48127ff37fff8000","0x10780017fff7fff","0x14","0x480280027ffb8000","0x1104800180018000","0x3e2e","0x482480017fff8000","0x3e2d","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x8","0x482480017fff8000","0x31a4c","0x480a7ff87fff8000","0x48127ff47fff8000","0x48307ffd7ff68000","0x482680017ffb8000","0x6","0x480280047ffb8000","0x480280057ffb8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202333","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x1716","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x1a36","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x1fae","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x20c6","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x161","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x131","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x11c","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xe8","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xcc","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xaa","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x8e","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3ce6","0x482480017fff8000","0x3ce5","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xe2a4","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x50","0x48307ffe80007ffa","0x400080007ff37fff","0x48127fff7fff8000","0x482480017ff28000","0x1","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffb7fff","0x400280017ffb7ffd","0x480280037ffb8000","0x20680017fff7fff","0x29","0x480280027ffb8000","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffd7fff8000","0x480a7ff87fff8000","0x482680017ffb8000","0x5","0x480080027ffb8000","0x48127fcb7fff8000","0x48127fd97fff8000","0x48127fe37fff8000","0x1104800180018000","0x2293","0x20680017fff7ffd","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x1","0x400080007ffe7fff","0x48127ff97fff8000","0x48127ff67fff8000","0x482480017ff68000","0x190","0x48127ff77fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff97fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x14","0x480280027ffb8000","0x1104800180018000","0x3c9f","0x482480017fff8000","0x3c9e","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb554","0x480a7ff87fff8000","0x48127ff47fff8000","0x48307ffd7ff68000","0x482680017ffb8000","0x6","0x480280047ffb8000","0x480280057ffb8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x144","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x114","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xff","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xcb","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xaf","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x8d","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x71","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3b6f","0x482480017fff8000","0x3b6e","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x156ee","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x33","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127fd27fff8000","0x48127fe07fff8000","0x48127fea7fff8000","0x1104800180018000","0x220c","0x20680017fff7ffd","0x1b","0x40780017fff7fff","0x1","0x48127ff97fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x1","0x400080007ffc7fff","0x48127ff77fff8000","0x48127ff47fff8000","0x48127ffc7fff8000","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff77fff8000","0x482480017ff68000","0x1","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x2bc","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x144","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x114","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xff","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xcb","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xaf","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x8d","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x71","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3a15","0x482480017fff8000","0x3a14","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x156ee","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x33","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127fd27fff8000","0x48127fe07fff8000","0x48127fea7fff8000","0x1104800180018000","0x223e","0x20680017fff7ffd","0x1b","0x40780017fff7fff","0x1","0x48127ff97fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x1","0x400080007ffc7fff","0x48127ff77fff8000","0x48127ff47fff8000","0x48127ffc7fff8000","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff77fff8000","0x482480017ff68000","0x1","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x2bc","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x5e","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x1bf8","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x11","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x48127ffb7fff8000","0x482480017ffb8000","0x492","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3953","0x482480017fff8000","0x3952","0x48127ffb7fff8000","0x480080007ffe8000","0xa0680017fff8000","0x9","0x4824800180007ffd","0x6bc6","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff57fff","0x10780017fff7fff","0x25","0x4824800180007ffd","0x6bc6","0x400080007ff67fff","0x482480017ff68000","0x1","0x48127ffe7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x110e2f729c9c2b988559994a3daccd838cf52faf88e18101373e67dd061455a","0x1104800180018000","0x175d","0x20680017fff7ffd","0xf","0x40780017fff7fff","0x1","0x400080007fff7ffd","0x400080017fff7ffe","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x0","0x48127ffb7fff8000","0x482480017ffa8000","0x2","0x208b7fff7fff7ffe","0x48127ffa7fff8000","0x482480017ffa8000","0x12c","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017ff38000","0x1","0x48127ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff98000","0x1","0x482680017ffa8000","0x21b6","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xae","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x128e","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x7e","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x69","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x55a","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x38ae","0x482480017fff8000","0x38ad","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x482480017fff8000","0x6e82","0xa0680017fff8000","0x8","0x48307ffe80007ffb","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff37fff","0x10780017fff7fff","0x28","0x48307ffe80007ffb","0x400080007ff47fff","0x482480017ff48000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x48127fe87fff8000","0x1104800180018000","0x1776","0x20680017fff7ffd","0x10","0x40780017fff7fff","0x1","0x400080007fff7ffd","0x400080017fff7ffe","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x482480017ff98000","0x2","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x12c","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017ff08000","0x1","0x48127ff57fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x4f6","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xa14","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x4","0xa0680017fff8000","0x7","0x482680017ffa8000","0xfffffffffffffffffffffffffffffc68","0x400280007ff97fff","0x10780017fff7fff","0x1cb","0x4825800180007ffa","0x398","0x400280007ff97fff","0x482680017ff98000","0x1","0x48127ffe7fff8000","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x19c","0x40137fff7fff8003","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4825800180048003","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x186","0x484480017fff8001","0x8000000000000000000000000000000","0x48317fff80008003","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x480080007fef8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x152","0x40137fff7fff8002","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4825800180048002","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x13c","0x484480017fff8001","0x8000000000000000000000000000000","0x48317fff80008002","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x108","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xec","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xca","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xae","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3750","0x482480017fff8000","0x374f","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x8","0x482480017fff8000","0x3479c","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x70","0x48307ffe80007ffa","0x400080007ff37fff","0x48127fff7fff8000","0x482480017ff28000","0x1","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffb7fff","0x400280017ffb7ffd","0x480280037ffb8000","0x20680017fff7fff","0x49","0x480280027ffb8000","0x480280047ffb8000","0x48127ffb7fff8000","0x48127ffd7fff8000","0x480a7ff87fff8000","0x482680017ffb8000","0x5","0x480a80037fff8000","0x480080027ffa8000","0x40137fd97fff8000","0x40137fe47fff8001","0x480a80007fff8000","0x480a80017fff8000","0x1104800180018000","0x1b75","0x20680017fff7ffd","0x26","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480a80037fff8000","0x480a80027fff8000","0x480a80007fff8000","0x480a80017fff8000","0x1104800180018000","0x1790","0x20680017fff7ffd","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x1","0x400080007ffe7fff","0x48127ff97fff8000","0x48127ff67fff8000","0x482480017ff68000","0x190","0x48127ff77fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff97fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x26","0x1104800180018000","0x36fc","0x482480017fff8000","0x36fb","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1eb4a","0x48127ff47fff8000","0x48127ff17fff8000","0x48307ffd7ff18000","0x48127ff27fff8000","0x48127ff37fff8000","0x48127ff37fff8000","0x10780017fff7fff","0x14","0x480280027ffb8000","0x1104800180018000","0x36e9","0x482480017fff8000","0x36e8","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x8","0x482480017fff8000","0x31a4c","0x480a7ff87fff8000","0x48127ff47fff8000","0x48307ffd7ff68000","0x482680017ffb8000","0x6","0x480280047ffb8000","0x480280057ffb8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202333","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x1716","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x1a36","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x1fae","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x20c6","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x144","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x114","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xff","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xcb","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xaf","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x8d","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x71","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x35a1","0x482480017fff8000","0x35a0","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x156ee","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x33","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127fd27fff8000","0x48127fe07fff8000","0x48127fea7fff8000","0x1104800180018000","0x1c3e","0x20680017fff7ffd","0x1b","0x40780017fff7fff","0x1","0x48127ff97fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x1","0x400080007ffc7fff","0x48127ff77fff8000","0x48127ff47fff8000","0x48127ffc7fff8000","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff77fff8000","0x482480017ff68000","0x1","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x2bc","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x144","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x482480017ffe8000","0x5e6","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x114","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xff","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xcb","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0xaf","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x8d","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x71","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3447","0x482480017fff8000","0x3446","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x156ee","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x33","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127fd27fff8000","0x48127fe07fff8000","0x48127fea7fff8000","0x1104800180018000","0x1c70","0x20680017fff7ffd","0x1b","0x40780017fff7fff","0x1","0x48127ff97fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x1","0x400080007ffc7fff","0x48127ff77fff8000","0x48127ff47fff8000","0x48127ffc7fff8000","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff77fff8000","0x482480017ff68000","0x1","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x2bc","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x1ae","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x7f8","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x834","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0xe7e","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x119e","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x16bc","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0xffffffffffffffffffffffffffffe192","0x400280007ff97fff","0x10780017fff7fff","0x297","0x4825800180007ffa","0x1e6e","0x400280007ff97fff","0x482680017ff98000","0x1","0x48127ffe7fff8000","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x27c","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x48127ffc7fff8000","0x480280007ffc8000","0x48307ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x262","0x482480017ffb8000","0x1","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480080007ff88000","0x48307ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffd7fff8000","0x482480017ffa8000","0x1","0x48127ffa7fff8000","0x480680017fff8000","0x0","0x48127ff77fff8000","0x10780017fff7fff","0x9","0x48127ffd7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x234","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x12","0x4824800180007ffd","0x100","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480080007fe87fff","0x482480017ffe8000","0xefffffffffffffde00000000000000ff","0x480080017fe67fff","0x400080027fe57ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x21c","0x402780017fff7fff","0x1","0x400080007feb7ffd","0x482480017ffd8000","0xffffffffffffffffffffffffffffff00","0x400080017fea7fff","0x482480017fea8000","0x2","0x48127ffc7fff8000","0x48307ff680007ff7","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff48000","0x1","0x48127ff47fff8000","0x480680017fff8000","0x0","0x48127ff17fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x1ea","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x1ce","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48307ff780007ff8","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff58000","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x1ac","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x16","0x480080007ff58003","0x480080017ff48003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ffa","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff07ffd","0x20680017fff7ffe","0x190","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ff58000","0x1","0x48127ffd7fff8000","0x48127ff07fff8000","0x48127ffa7fff8000","0x48307ff580007ff6","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffc7fff8000","0x482480017ff38000","0x1","0x48127ff37fff8000","0x480680017fff8000","0x0","0x480080007ff08000","0x10780017fff7fff","0x9","0x48127ffc7fff8000","0x48127ff37fff8000","0x48127ff37fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x15f","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff17ffc","0x480080017ff07ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027fef7ffd","0x10780017fff7fff","0x14a","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff27ffd","0x480080017ff17ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff07ffe","0x482480017ff08000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x480080007fef8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x116","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0x101","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x480080007fef8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xcd","0x48127ffb7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffd","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007ff37ffc","0x480080017ff27ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027ff17ffd","0x10780017fff7fff","0xb8","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffc","0x480080007ff47ffd","0x480080017ff37ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027ff27ffe","0x482480017ff28000","0x3","0x48127ff97fff8000","0x48307ff480007ff5","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482480017ff28000","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x84","0x480080007fff8000","0x48127ffa7fff8000","0xa0680017fff8000","0x12","0x4824800180007ffd","0x10000000000000000","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480080007ff27fff","0x482480017ffe8000","0xefffffffffffffdeffffffffffffffff","0x480080017ff07fff","0x400080027fef7ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x6c","0x402780017fff7fff","0x1","0x400080007ff57ffd","0x482480017ffd8000","0xffffffffffffffff0000000000000000","0x400080017ff47fff","0x482480017ff48000","0x2","0x48127ffc7fff8000","0x48307ff680007ff7","0x20680017fff7fff","0x4","0x10780017fff7fff","0x12","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x5be","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3212","0x482480017fff8000","0x3211","0x48127ffb7fff8000","0x480080007ffe8000","0x480080007fff8000","0x484480017fff8000","0xc","0x482480017fff8000","0x5dd54","0xa0680017fff8000","0x8","0x48307ffe80007ffa","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x2b","0x48307ffe80007ffa","0x400080007ff37fff","0x482480017ff38000","0x1","0x48127ffe7fff8000","0x480a7ff87fff8000","0x480a7ffb7fff8000","0x48127f917fff8000","0x48127f957fff8000","0x48127f9b7fff8000","0x48127fb67fff8000","0x48127fb67fff8000","0x48127fbb7fff8000","0x48127fc87fff8000","0x48127fd57fff8000","0x48127fe37fff8000","0x1104800180018000","0x1bc1","0x20680017fff7ffd","0xd","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x64","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482480017fef8000","0x1","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017fef8000","0x3","0x482480017ff78000","0x384","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x96a","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202338","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0xc8a","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x11a8","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202337","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff18000","0x3","0x482480017ff88000","0x14c8","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x19e6","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202336","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017fef8000","0x3","0x482480017ff88000","0x1d06","0x10780017fff7fff","0x5","0x48127ff67fff8000","0x482480017ffa8000","0x2224","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202335","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017ff08000","0x3","0x482480017ff88000","0x2260","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x28aa","0x10780017fff7fff","0xb","0x482480017ff08000","0x3","0x482480017ff88000","0x28e6","0x10780017fff7fff","0x5","0x48127ff87fff8000","0x482480017ffa8000","0x2f30","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202334","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482480017fe58000","0x3","0x482480017ff78000","0x307a","0x10780017fff7fff","0x5","0x48127fee7fff8000","0x482480017ffa8000","0x3660","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202333","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202332","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ff57fff8000","0x482480017ff98000","0x3bd8","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x480a7ff87fff8000","0x48127ffa7fff8000","0x482480017ffa8000","0x3e30","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x480a7ff87fff8000","0x482680017ff98000","0x1","0x482680017ffa8000","0x2152","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x1104800180018000","0x1c0c","0x20680017fff7ffd","0xa2","0x48127ffa7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x6894a7eacac1683e1e290e1df9a86c47bc34cd609052ca3e176955bc0958ee","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffc","0x400080027ff87ffd","0x400080037ff87ffe","0x480080057ff88000","0x20680017fff7fff","0x81","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffc","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480080007fec7ffc","0x480080017feb7ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400080027fea7ffd","0x10780017fff7fff","0x5d","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffb","0x480080007fed7ffd","0x480080017fec7ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400080027feb7ffe","0x482480017feb8000","0x3","0x48127ff97fff8000","0x20680017fff7ff6","0x3e","0x48127fff7fff8000","0x20780017fff7ffd","0x14","0x40780017fff7fff","0x4","0x40780017fff7fff","0x1","0x480680017fff8000","0x5a45524f5f41444452455353","0x400080007ffe7fff","0x48127ff77fff8000","0x482480017ff88000","0x2a08","0x48127fe27fff8000","0x48127fed7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48127fff7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x6894a7eacac1683e1e290e1df9a86c47bc34cd609052ca3e176955bc0958ee","0x480680017fff8000","0x53746f726167655772697465","0x400080007ff27fff","0x400080017ff27ffc","0x400080027ff27ffd","0x400080037ff27ffe","0x400180047ff27ffd","0x480080067ff28000","0x20680017fff7fff","0xf","0x480080057ff18000","0x48127ff77fff8000","0x48127ffe7fff8000","0x48127fe27fff8000","0x482480017fed8000","0x7","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x480080057ff18000","0x48127ff77fff8000","0x48127ffe7fff8000","0x48127fe27fff8000","0x482480017fed8000","0x9","0x480680017fff8000","0x1","0x480080077feb8000","0x480080087fea8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x5","0x40780017fff7fff","0x1","0x480680017fff8000","0x4c4f434b494e475f434f4e54524143545f414c52454144595f534554","0x400080007ffe7fff","0x48127ff77fff8000","0x482480017ff78000","0x2ac6","0x48127fe27fff8000","0x48127fed7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x480680017fff8000","0x4e6f6e20436f6e747261637441646472657373","0x400080007ffe7fff","0x482480017fe78000","0x3","0x482480017ff58000","0x28fa","0x48127ff37fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0xc","0x40780017fff7fff","0xc","0x480080047feb8000","0x48127fe77fff8000","0x482480017ffe8000","0x2f9e","0x482480017fe88000","0x8","0x480080067fe78000","0x480080077fe68000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fe27fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x17","0x48127fe27fff8000","0x482480017fe28000","0x5c80","0x48127fe27fff8000","0x48127fe27fff8000","0x480680017fff8000","0x1","0x48127fe27fff8000","0x48127fe27fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x6894a7eacac1683e1e290e1df9a86c47bc34cd609052ca3e176955bc0958ee","0x480680017fff8000","0x53746f7261676552656164","0x400280007ff97fff","0x400380017ff97ff7","0x400280027ff97ffd","0x400280037ff97ffe","0x480280057ff98000","0x20680017fff7fff","0x98","0x480280047ff98000","0x400380067ff98000","0x482680017ff98000","0x7","0x48127ffe7fff8000","0xa0680017fff8004","0xe","0x4825800180048000","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480280007ff67ffc","0x480280017ff67ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400280027ff67ffd","0x10780017fff7fff","0x6d","0x484480017fff8001","0x8000000000000000000000000000000","0x48317fff80008000","0x480280007ff67ffd","0x480280017ff67ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400280027ff67ffe","0x482680017ff68000","0x3","0x48127ff97fff8000","0x20780017fff8000","0x1b","0x1104800180018000","0x3017","0x482480017fff8000","0x3016","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x164d6","0x40780017fff7fff","0x1","0x480680017fff8000","0x4c4f434b494e475f434f4e54524143545f4e4f545f534554","0x400080007ffe7fff","0x48127ff57fff8000","0x48307ffc7ff58000","0x480a7ff87fff8000","0x48127feb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48127ffe7fff8000","0x48127ffe7fff8000","0x480a7ff87fff8000","0x48127ff47fff8000","0x480a7ffa7fff8000","0x480a80007fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x1104800180018000","0x1ba4","0x20680017fff7ffd","0x31","0x40780017fff7fff","0x1","0x400180007fff7ffa","0x400180017fff7ffb","0x400180027fff7ffc","0x400180037fff7ffd","0x48127ff97fff8000","0x480680017fff8000","0x2b7080bbeb1d6f069b6c264f329194905a30d16c6b2dba4f2d59935c2c3896a","0x48127ffd7fff8000","0x482480017ffc8000","0x4","0x480680017fff8000","0x43616c6c436f6e7472616374","0x400080007ff67fff","0x400080017ff67ffb","0x400180027ff68000","0x400080037ff67ffc","0x400080047ff67ffd","0x400080057ff67ffe","0x480080077ff68000","0x20680017fff7fff","0xf","0x480080067ff58000","0x48127ff17fff8000","0x48127ffe7fff8000","0x48127ff17fff8000","0x482480017ff18000","0xa","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x480080067ff58000","0x48127ff17fff8000","0x48127ffe7fff8000","0x48127ff17fff8000","0x482480017ff18000","0xa","0x480680017fff8000","0x1","0x480080087fef8000","0x480080097fee8000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2e7c","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x2fb9","0x482480017fff8000","0x2fb8","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x162e2","0x40780017fff7fff","0x1","0x480680017fff8000","0x4e6f6e20436f6e747261637441646472657373","0x400080007ffe7fff","0x482680017ff68000","0x3","0x48307ffc7fef8000","0x48127fed7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x13","0x480280047ff98000","0x1104800180018000","0x2fa0","0x482480017fff8000","0x2f9f","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x169ea","0x480a7ff67fff8000","0x48307ffe7ff78000","0x482680017ff98000","0x8","0x480280067ff98000","0x480280077ff98000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ff87fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ff88000","0xfffffffffffffffffffffffffffff592","0x400280007ff77fff","0x10780017fff7fff","0x49","0x4825800180007ff8","0xa6e","0x400280007ff77fff","0x482680017ff78000","0x1","0x48127ffe7fff8000","0x20780017fff7ffd","0xe","0x48127ffe7fff8000","0x482480017ffe8000","0xad2","0x480680017fff8000","0x0","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480680017fff8000","0x0","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x208b7fff7fff7ffe","0x48127fff7fff8000","0x48297ff980007ffa","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ff98000","0x1","0x480a7ffa7fff8000","0x480680017fff8000","0x0","0x480280007ff98000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xf","0x400280007ffc7fff","0x48127ff77fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480a7ffb7fff8000","0x482680017ffc8000","0x1","0x4825800180007ffd","0x1","0x1104800180018000","0x800000000000010ffffffffffffffffffffffffffffffffffffffffffffffc4","0x208b7fff7fff7ffe","0x48127ff77fff8000","0x482480017ffa8000","0x6ea","0x480680017fff8000","0x0","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff78000","0x1","0x480a7ff87fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x4","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ff57fff","0x400380017ff57ff3","0x480280037ff58000","0x20680017fff7fff","0x1ed","0x480280027ff58000","0x480280047ff58000","0x480080007fff8000","0x480080017fff8000","0x482680017ff58000","0x5","0x48127ffb7fff8000","0x48317ffd80017ffb","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400280007ff27fff","0x10780017fff7fff","0x1c4","0x400280007ff27fff","0x48127ffd7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x38bb9518f707d6868da0178f4ac498e320441f8f7e11ff8a35ed4ea8286e693","0x482680017ff28000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff77fff","0x400080017ff77ffb","0x400080027ff77ffc","0x400080037ff77ffd","0x480080057ff78000","0x20680017fff7fff","0x19e","0x480080047ff68000","0x48127ffc7fff8000","0x48127ffe7fff8000","0x480a7ff47fff8000","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x402580017fed8002","0x7","0x400180067fed8003","0x1104800180018000","0x1cb2","0x20680017fff7ffd","0x176","0x40780017fff7fff","0x1","0x480680017fff8000","0x537461726b4e6574204d657373616765","0x400080007ffe7fff","0x400180017ffe8003","0x400180027ffe7ff6","0x400080037ffe7ffd","0x48127ffe7fff8000","0x482480017ffd8000","0x4","0x40327ffe80017fff","0x480680017fff8000","0x0","0x48127ff67fff8000","0x4828800180017ffe","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff17fff","0x10780017fff7fff","0x1b","0x400080007ff27fff","0x1104800180018000","0x2ed4","0x482480017fff8000","0x2ed3","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x7","0x482480017fff8000","0x2535a","0x40780017fff7fff","0x1","0x480680017fff8000","0x5265717569726573206174206c65617374206f6e6520656c656d656e74","0x400080007ffe7fff","0x482480017fe98000","0x1","0x48307ffc7ff38000","0x48127fe97fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x155","0x482480017ff18000","0x1","0x48127ffb7fff8000","0x48127ff17fff8000","0x48127ff67fff8000","0x48127ff67fff8000","0x480680017fff8000","0x0","0x1104800180018000","0x1cd0","0x20680017fff7ffc","0x129","0x400080007ffb7fff","0x400180017ffb8001","0x480080027ffb8000","0x480680017fff8000","0x15de4ee18ebfb6453b53db283bb13bc3b7d746ca2d79f8163920e1b68d594ee","0x400080037ff97fff","0x400080047ff97ffe","0x480080057ff98000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff27ffc","0x480080017ff17ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027fef7ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff27ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff07ffd","0x400080027fef7ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff07fff8000","0x480680017fff8000","0x0","0x482480017fef8000","0x6","0x482480017fec8000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x4002800080027fff","0x4002800180027ffb","0x4002800280027ffc","0x4002800380027ffa","0x4802800580028000","0x20680017fff7fff","0xdf","0x4802800480028000","0x4802800680028000","0x4826800180028000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x48307ffe80007fff","0x480680017fff8000","0x0","0x20680017fff7ffe","0x8","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48307ffd80007ffe","0x10780017fff7fff","0x7","0x40780017fff7fff","0x1","0x482480017ffa8000","0x5a","0x48127ffd7fff8000","0x20680017fff7fff","0x1b","0x1104800180018000","0x2e57","0x482480017fff8000","0x2e56","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x5","0x482480017fff8000","0x20f76","0x40780017fff7fff","0x1","0x480680017fff8000","0x5349474e45445f524551554553545f414c52454144595f55534544","0x400080007ffe7fff","0x48127fe87fff8000","0x48307ffc7ff48000","0x48127fe57fff8000","0x48127fea7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480680017fff8000","0x15de4ee18ebfb6453b53db283bb13bc3b7d746ca2d79f8163920e1b68d594ee","0x400080007fef7fff","0x400080017fef7fe3","0x480080027fef8000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007feb7ffc","0x480080017fea7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027fe87ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007feb7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017fe97ffd","0x400080027fe87ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x402580017fe48000","0x3","0x482480017fe58000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400080007fe87fff","0x400080017fe87ffb","0x400080027fe87ffc","0x400080037fe87ffa","0x400080047fe87ffd","0x480080067fe88000","0x20680017fff7fff","0x58","0x480080057fe78000","0x48127ffc7fff8000","0x48127ffe7fff8000","0x482480017fe48000","0x7","0x480a7ff67fff8000","0x48127fd07fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x1104800180018000","0x1c77","0x20680017fff7ffd","0x33","0x4824800180007fff","0x56414c4944","0x48127ffa7fff8000","0x20680017fff7ffe","0x8","0x40780017fff7fff","0x2","0x482480017ffd8000","0x50","0x10780017fff7fff","0x8","0x4824800180007ffd","0x1","0x48127ffe7fff8000","0x20680017fff7ffe","0xe","0x48127fff7fff8000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80007fff8000","0x48127ff47fff8000","0x480a7ff67fff8000","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffd9e","0x208b7fff7fff7ffe","0x1104800180018000","0x2de0","0x482480017fff8000","0x2ddf","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x198d4","0x40780017fff7fff","0x1","0x480680017fff8000","0x5349474e41545552455f56414c49444154494f4e5f4641494c4544","0x400080007ffe7fff","0x48307ffd7ff68000","0x48127ffd7fff8000","0x482480017ffc8000","0x1","0x10780017fff7fff","0x11","0x40780017fff7fff","0x6","0x1104800180018000","0x2dc9","0x482480017fff8000","0x2dc8","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x19c1c","0x48307fff7fee8000","0x48127ff07fff8000","0x48127ff07fff8000","0x48127fea7fff8000","0x48127ffc7fff8000","0x480a80007fff8000","0x48127fe97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480080057fe78000","0x1104800180018000","0x2db2","0x482480017fff8000","0x2db1","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1ddda","0x48127ff57fff8000","0x48307ffe7ff78000","0x480a80007fff8000","0x482480017fdc8000","0x9","0x480680017fff8000","0x1","0x480080077fda8000","0x480080087fd98000","0x208b7fff7fff7ffe","0x4802800480028000","0x1104800180018000","0x2d9d","0x482480017fff8000","0x2d9c","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x5","0x482480017fff8000","0x216e2","0x48127ff57fff8000","0x48307ffe7ff78000","0x48127ff27fff8000","0x4826800180028000","0x8","0x480680017fff8000","0x1","0x4802800680028000","0x4802800780028000","0x208b7fff7fff7ffe","0x1104800180018000","0x2d89","0x482480017fff8000","0x2d88","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x7","0x482480017fff8000","0x247c0","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x11","0x1104800180018000","0x2d78","0x482480017fff8000","0x2d77","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x7","0x482480017fff8000","0x25a9e","0x48127ff37fff8000","0x48307ffe7ff38000","0x48127ff37fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a80027fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480080047ff68000","0x1104800180018000","0x2d5f","0x482480017fff8000","0x2d5e","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x8","0x482480017fff8000","0x2792a","0x48127ff57fff8000","0x48307ffe7ff78000","0x480a7ff47fff8000","0x482480017feb8000","0x8","0x480680017fff8000","0x1","0x480080067fe98000","0x480080077fe88000","0x208b7fff7fff7ffe","0x1104800180018000","0x2d4b","0x482480017fff8000","0x2d4a","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x8","0x482480017fff8000","0x2a2f6","0x40780017fff7fff","0x1","0x480680017fff8000","0x5349474e41545552455f45585049524544","0x400080007ffe7fff","0x482680017ff28000","0x1","0x48307ffc7ff28000","0x480a7ff47fff8000","0x48127fef7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480280027ff58000","0x1104800180018000","0x2d30","0x482480017fff8000","0x2d2f","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x8","0x482480017fff8000","0x2a850","0x480a7ff27fff8000","0x48307ffe7ff78000","0x480a7ff47fff8000","0x482680017ff58000","0x6","0x480680017fff8000","0x1","0x480280047ff58000","0x480280057ff58000","0x208b7fff7fff7ffe","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffa7fff","0x400380017ffa7ff8","0x480280037ffa8000","0x20680017fff7fff","0x8d","0x480280027ffa8000","0x480280047ffa8000","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x1390569bb0a3a722eb4228e8700301347da081211d5c2ded2db22ef389551ab","0x480080007ffc8000","0x480080017ffb8000","0x480080027ffa8000","0x480080037ff98000","0x480080047ff88000","0x480680017fff8000","0x53746f7261676552656164","0x400280057ffa7fff","0x400280067ffa7ff7","0x400280077ffa7ff8","0x400280087ffa7ff9","0x4802800a7ffa8000","0x20680017fff7fff","0x5e","0x480280097ffa8000","0x4802800b7ffa8000","0x482680017ffa8000","0xc","0x48127ffd7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffc","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480280007ff77ffc","0x480280017ff77ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400280027ff77ffd","0x10780017fff7fff","0x33","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffb","0x480280007ff77ffd","0x480280017ff77ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400280027ff77ffe","0x48307ff880007ff2","0x482680017ff78000","0x3","0x48127ff87fff8000","0x20680017fff7ffd","0xc","0x48127ffe7fff8000","0x48127ffe7fff8000","0x480a7ff97fff8000","0x48127ff37fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x1104800180018000","0x1ba4","0x208b7fff7fff7ffe","0x1104800180018000","0x2cd5","0x482480017fff8000","0x2cd4","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1e9d8","0x40780017fff7fff","0x1","0x480680017fff8000","0x4d494e5445525f4f4e4c59","0x400080007ffe7fff","0x48127ff57fff8000","0x48307ffc7ff58000","0x480a7ff97fff8000","0x48127fea7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x2cbc","0x482480017fff8000","0x2cbb","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1e848","0x40780017fff7fff","0x1","0x480680017fff8000","0x4e6f6e20436f6e747261637441646472657373","0x400080007ffe7fff","0x482680017ff78000","0x3","0x48307ffc7fef8000","0x48127fed7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x13","0x480280097ffa8000","0x1104800180018000","0x2ca3","0x482480017fff8000","0x2ca2","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1ef5a","0x480a7ff77fff8000","0x48307ffe7ff78000","0x482680017ffa8000","0xd","0x4802800b7ffa8000","0x4802800c7ffa8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ff97fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480280027ffa8000","0x1104800180018000","0x2c89","0x482480017fff8000","0x2c88","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x21f02","0x480a7ff77fff8000","0x48307ffe7ff78000","0x480a7ff97fff8000","0x482680017ffa8000","0x6","0x480680017fff8000","0x1","0x480280047ffa8000","0x480280057ffa8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffa7fff","0x400380017ffa7ff8","0x480280037ffa8000","0x20680017fff7fff","0x8d","0x480280027ffa8000","0x480280047ffa8000","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x1390569bb0a3a722eb4228e8700301347da081211d5c2ded2db22ef389551ab","0x480080007ffc8000","0x480080017ffb8000","0x480080027ffa8000","0x480080037ff98000","0x480080047ff88000","0x480680017fff8000","0x53746f7261676552656164","0x400280057ffa7fff","0x400280067ffa7ff7","0x400280077ffa7ff8","0x400280087ffa7ff9","0x4802800a7ffa8000","0x20680017fff7fff","0x5e","0x480280097ffa8000","0x4802800b7ffa8000","0x482680017ffa8000","0xc","0x48127ffd7fff8000","0xa0680017fff8004","0xe","0x4824800180047ffc","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480280007ff77ffc","0x480280017ff77ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400280027ff77ffd","0x10780017fff7fff","0x33","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffb","0x480280007ff77ffd","0x480280017ff77ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400280027ff77ffe","0x48307ff880007ff2","0x482680017ff78000","0x3","0x48127ff87fff8000","0x20680017fff7ffd","0xc","0x48127ffe7fff8000","0x48127ffe7fff8000","0x480a7ff97fff8000","0x48127ff37fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x1104800180018000","0x1dd8","0x208b7fff7fff7ffe","0x1104800180018000","0x2c2e","0x482480017fff8000","0x2c2d","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1e9d8","0x40780017fff7fff","0x1","0x480680017fff8000","0x4d494e5445525f4f4e4c59","0x400080007ffe7fff","0x48127ff57fff8000","0x48307ffc7ff58000","0x480a7ff97fff8000","0x48127fea7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x2c15","0x482480017fff8000","0x2c14","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1e848","0x40780017fff7fff","0x1","0x480680017fff8000","0x4e6f6e20436f6e747261637441646472657373","0x400080007ffe7fff","0x482680017ff78000","0x3","0x48307ffc7fef8000","0x48127fed7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x13","0x480280097ffa8000","0x1104800180018000","0x2bfc","0x482480017fff8000","0x2bfb","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1ef5a","0x480a7ff77fff8000","0x48307ffe7ff78000","0x482680017ffa8000","0xd","0x4802800b7ffa8000","0x4802800c7ffa8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ff97fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480280027ffa8000","0x1104800180018000","0x2be2","0x482480017fff8000","0x2be1","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x21f02","0x480a7ff77fff8000","0x48307ffe7ff78000","0x480a7ff97fff8000","0x482680017ffa8000","0x6","0x480680017fff8000","0x1","0x480280047ffa8000","0x480280057ffa8000","0x208b7fff7fff7ffe","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xa","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x8","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x98","0xa0680017fff8004","0xe","0x4824800180047ffe","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480280007ffb7ffc","0x480280017ffb7ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400280027ffb7ffd","0x10780017fff7fff","0x84","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffd","0x480280007ffb7ffd","0x480280017ffb7ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400280027ffb7ffe","0x482680017ffb8000","0x3","0x48127ff67fff8000","0x48127ff67fff8000","0x1104800180018000","0x2021","0x20680017fff7ff8","0x5e","0x20680017fff7ffb","0x46","0x48307ff980007ffa","0x20680017fff7fff","0x4","0x10780017fff7fff","0xa","0x482480017ff88000","0x1","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ff57fff8000","0x10780017fff7fff","0x8","0x48127ff87fff8000","0x48127ff87fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x1b","0x480080007fff8000","0x20680017fff7fff","0x6","0x480680017fff8000","0x1","0x10780017fff7fff","0x4","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x48127fef7fff8000","0x480680017fff8000","0x0","0x48127ff77fff8000","0x48127ff77fff8000","0x480680017fff8000","0x0","0x48127f9e7fff8000","0x48127fee7fff8000","0x48127fee7fff8000","0x48127fee7fff8000","0x48127fee7fff8000","0x48307ff480007ff5","0x208b7fff7fff7ffe","0x40780017fff7fff","0x3","0x48127fef7fff8000","0x480680017fff8000","0x0","0x48127ff77fff8000","0x48127ff77fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x8","0x48127fef7fff8000","0x480680017fff8000","0x0","0x48127fef7fff8000","0x48127fef7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x8","0x48127fef7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127fed7fff8000","0x48127fed7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x56","0x482680017ffb8000","0x3","0x10780017fff7fff","0x5","0x40780017fff7fff","0x5c","0x480a7ffb7fff8000","0x480680017fff8000","0x0","0x48127f9e7fff8000","0x48127f9e7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480a7ff37fff8000","0x480a7ff47fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff77fff8000","0x48127ff67fff8000","0x1104800180018000","0x2063","0x20680017fff7ffd","0x9d","0x1104800180018000","0x2afd","0x482480017fff8000","0x2afc","0x48127ff87fff8000","0x48127ff87fff8000","0x480a7ff67fff8000","0x480080007ffc8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff47fff8000","0x48127ff47fff8000","0x1104800180018000","0x2091","0x20680017fff7ffc","0x7a","0x480680017fff8000","0x1ac8d354f2e793629cb233a16f10d13cf15b9c45bbc620577c8e1df95ede545","0x400280007ff57fff","0x400280017ff57ffe","0x480280027ff58000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff37ffc","0x480080017ff27ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027ff07ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff37ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff17ffd","0x400080027ff07ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff17fff8000","0x480680017fff8000","0x0","0x482680017ff58000","0x3","0x482480017fed8000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ff77fff","0x400280017ff77ffb","0x400280027ff77ffc","0x400280037ff77ffa","0x480280057ff78000","0x20680017fff7fff","0x3b","0x480280047ff78000","0x480280067ff78000","0x482680017ff78000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x12","0x4824800180007ffc","0x10000000000000000","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480080007ff57fff","0x482480017ffe8000","0xefffffffffffffdeffffffffffffffff","0x480080017ff37fff","0x400080027ff27ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x15","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x482480017ffc8000","0xffffffffffffffff0000000000000000","0x400080017ff77fff","0x482480017ff78000","0x2","0x482480017ffc8000","0x3ca","0x48127ff47fff8000","0x48127fe37fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff47fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f7265553634202d206e6f6e20753634","0x400080007ffe7fff","0x482480017ff08000","0x3","0x48127ff57fff8000","0x48127fed7fff8000","0x48127fdc7fff8000","0x48127ff17fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x480280047ff78000","0x48127ffc7fff8000","0x482480017ffe8000","0x712","0x48127ff97fff8000","0x48127fe87fff8000","0x482680017ff78000","0x8","0x480680017fff8000","0x1","0x480280067ff78000","0x480280077ff78000","0x208b7fff7fff7ffe","0x1104800180018000","0x2a71","0x482480017fff8000","0x2a70","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x346c","0x48127ff37fff8000","0x48307ffe7ff38000","0x48127ff37fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x10780017fff7fff","0xf","0x1104800180018000","0x2a62","0x482480017fff8000","0x2a61","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x4326","0x48127ff57fff8000","0x48307ffe7ff58000","0x480a7ff67fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ff57fff8000","0x48127ffa7fff8000","0x480a7ff77fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x4","0x480a7ff37fff8000","0x480a7ff47fff8000","0x480a7ff57fff8000","0x480a7ff77fff8000","0x1104800180018000","0x155b","0x20680017fff7ffd","0x15f","0x48127ffa7fff8000","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400080007ffa7fff","0x400080017ffa7ffe","0x480080037ffa8000","0x20680017fff7fff","0x141","0x480080027ff98000","0x480080047ff88000","0x480080007fff8000","0x48127ffd7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x3fc801c47df4de8d5835f8bfd4d0b8823ba63e5a3f278086901402d680abfc","0x480080007ffc8000","0x480080017ffb8000","0x480080027ffa8000","0x480680017fff8000","0x53746f7261676552656164","0x400080057fef7fff","0x400080067fef7ff9","0x400080077fef7ffa","0x400080087fef7ffb","0x4800800a7fef8000","0x20680017fff7fff","0x110","0x480080097fee8000","0x4800800b7fed8000","0x482480017fec8000","0xc","0x48127ffd7fff8000","0xa0680017fff8000","0x12","0x4824800180007ffc","0x10000000000000000","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480080007fe37fff","0x482480017ffe8000","0xefffffffffffffdeffffffffffffffff","0x480080017fe17fff","0x400080027fe07ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0xe3","0x402780017fff7fff","0x1","0x400080007fe67ffc","0x482480017ffc8000","0xffffffffffffffff0000000000000000","0x400080017fe57fff","0x48127ffd7fff8000","0xa0680017fff8000","0x8","0x48307ff97ff48000","0x4824800180007fff","0x10000000000000000","0x400080027fe17fff","0x10780017fff7fff","0xb9","0x48307ff97ff48001","0x4824800180007fff","0xffffffffffffffff0000000000000000","0x400080027fe17ffe","0x480680017fff8000","0x127500","0x48127ffb7fff8000","0xa0680017fff8000","0x8","0x48307ffd7ffc8000","0x4824800180007fff","0x10000000000000000","0x400080037fdc7fff","0x10780017fff7fff","0x8f","0x48307ffd7ffc8001","0x4824800180007fff","0xffffffffffffffff0000000000000000","0x400080037fdc7ffe","0x482480017fdc8000","0x4","0x48127ffb7fff8000","0x48127fdc7fff8000","0x480a7ff67fff8000","0x48127fef7fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127fef7fff8000","0x40137ff37fff8003","0x1104800180018000","0x2022","0x20680017fff7ffd","0x67","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480a80037fff8000","0x1104800180018000","0x20a9","0x40137ffa7fff8002","0x40137ffb7fff8001","0x40137ffc7fff8000","0x20680017fff7ffd","0x49","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff67fff8000","0x48127ff67fff8000","0x480680017fff8000","0x15","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x1104800180018000","0x2127","0x20680017fff7ffb","0x28","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0x10","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80027fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80027fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80027fff8000","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x48127ff87fff8000","0x482480017ff88000","0x4ef2","0x480a80027fff8000","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x2977","0x482480017fff8000","0x2976","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xaeba","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff27fff8000","0x48127ff27fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x2965","0x482480017fff8000","0x2964","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x10e32","0x40780017fff7fff","0x1","0x480680017fff8000","0x7536345f616464204f766572666c6f77","0x400080007ffe7fff","0x482480017fd38000","0x4","0x48307ffc7ff28000","0x48127fd37fff8000","0x480a7ff67fff8000","0x48127fe67fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x294a","0x482480017fff8000","0x2949","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x110d0","0x40780017fff7fff","0x1","0x480680017fff8000","0x7536345f616464204f766572666c6f77","0x400080007ffe7fff","0x482480017fd88000","0x3","0x48307ffc7ff28000","0x48127fd87fff8000","0x480a7ff67fff8000","0x48127feb7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x292f","0x482480017fff8000","0x292e","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x10e78","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f7265553634202d206e6f6e20753634","0x400080007ffe7fff","0x482480017fd78000","0x3","0x48307ffc7fee8000","0x48127fec7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x15","0x40780017fff7fff","0xc","0x480080097fe28000","0x1104800180018000","0x2914","0x482480017fff8000","0x2913","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x11512","0x48127fd77fff8000","0x48307ffe7ff78000","0x482480017fd88000","0xd","0x4800800b7fd78000","0x4800800c7fd68000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fd27fff8000","0x480a7ff67fff8000","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x480080027ff98000","0x1104800180018000","0x28f9","0x482480017fff8000","0x28f8","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x14532","0x48127fee7fff8000","0x48307ffe7ff78000","0x48127fee7fff8000","0x480a7ff67fff8000","0x482480017fed8000","0x6","0x480680017fff8000","0x1","0x480080047feb8000","0x480080057fea8000","0x208b7fff7fff7ffe","0x1104800180018000","0x28e4","0x482480017fff8000","0x28e3","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x16efe","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x480a7ff67fff8000","0x48127ff17fff8000","0x480680017fff8000","0x1","0x48127ff17fff8000","0x48127ff17fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x3","0x480a7ff37fff8000","0x480a7ff47fff8000","0x480a7ff57fff8000","0x480a7ff77fff8000","0x1104800180018000","0x13e0","0x20680017fff7ffd","0xc7","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480a7ff67fff8000","0x48127ff87fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffdaf","0x20680017fff7ffd","0xa4","0x48127ff97fff8000","0x20680017fff7ffe","0x18","0x1104800180018000","0x28b4","0x482480017fff8000","0x28b3","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x10f68","0x48127ff07fff8000","0x48307ffe7ff78000","0x48127ff07fff8000","0x48127ff07fff8000","0x48127ff07fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x48127ff77fff8000","0x48127ffe7fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x1104800180018000","0x1ed3","0x20680017fff7ffd","0x68","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x1104800180018000","0x1f59","0x40137ffa7fff8002","0x40137ffb7fff8001","0x40137ffc7fff8000","0x20680017fff7ffd","0x49","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff67fff8000","0x48127ff67fff8000","0x480680017fff8000","0x13","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x1104800180018000","0x1fd7","0x20680017fff7ffb","0x28","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0x10","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80027fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80027fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80027fff8000","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x48127ff87fff8000","0x482480017ff88000","0x4ef2","0x480a80027fff8000","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x2827","0x482480017fff8000","0x2826","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xaeba","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff27fff8000","0x48127ff27fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x2815","0x482480017fff8000","0x2814","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x11030","0x48127ff17fff8000","0x48307ffe7ff18000","0x48127ff17fff8000","0x48127ff17fff8000","0x48127ff17fff8000","0x480680017fff8000","0x1","0x48127ff17fff8000","0x48127ff17fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x2801","0x482480017fff8000","0x2800","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x3","0x482480017fff8000","0x1778c","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x480a7ff67fff8000","0x48127ff17fff8000","0x480680017fff8000","0x1","0x48127ff17fff8000","0x48127ff17fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x9","0x480a7ff37fff8000","0x480a7ff47fff8000","0x480a7ff57fff8000","0x480a7ff77fff8000","0x1104800180018000","0x12fd","0x20680017fff7ffd","0x2e0","0x48127ffa7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0xcfc0e4c73ce8e46b07c3167ce01ce17e6c2deaaa5b88b977bbb10abe25c9ad","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffc","0x400080027ff87ffd","0x400080037ff87ffe","0x480080057ff88000","0x20680017fff7fff","0x2bc","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x48307ffe80007fff","0x20680017fff7fff","0x28d","0x48127ffc7fff8000","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400080007ff87fff","0x400080017ff87ffe","0x480080037ff88000","0x20680017fff7fff","0x26f","0x480080027ff78000","0x480080047ff68000","0x480080007fff8000","0x48127fe67fff8000","0x48127ffc7fff8000","0x48127fe67fff8000","0x480a7ff67fff8000","0x482480017ff08000","0x5","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x400180007ff48006","0x400180017ff48007","0x400180027ff48008","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffc9a","0x20680017fff7ffd","0x245","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x40137ff47fff8005","0x1104800180018000","0x203c","0x40137ffa7fff8001","0x40137ffb7fff8002","0x40137ffc7fff8004","0x20680017fff7ffd","0x21e","0x48127ff97fff8000","0x20780017fff8005","0x1c","0x1104800180018000","0x278c","0x482480017fff8000","0x278b","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1f216","0x40780017fff7fff","0x1","0x480680017fff8000","0x554e4b4e4f574e5f494d504c454d454e544154494f4e","0x400080007ffe7fff","0x48127fee7fff8000","0x48307ffc7ff58000","0x480a80017fff8000","0x480a80027fff8000","0x480a80047fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x48127fff7fff8000","0x4829800580018007","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff37fff","0x10780017fff7fff","0x1dd","0x400080007ff47fff","0x48127ffd7fff8000","0x4828800780017ffa","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff07fff","0x10780017fff7fff","0x1b8","0x400080017ff17fff","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x482480017fef8000","0x2","0x48127ffa7fff8000","0x480680017fff8000","0x11","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x1104800180018000","0x1ebb","0x20680017fff7ffb","0x186","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080047fff","0x4002800180047ffe","0x4002800280047ffa","0x4002800380047ffb","0x4002800480047ffc","0x4002800580047ffd","0x4802800780048000","0x20680017fff7fff","0x168","0x4802800680048000","0x4826800180048000","0x8","0x48127ffe7fff8000","0x20780017fff7ffd","0x8","0x48127ff37fff8000","0x482480017ffe8000","0x7b0c","0x48127ffc7fff8000","0x10780017fff7fff","0x42","0x48127fff7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0xcfc0e4c73ce8e46b07c3167ce01ce17e6c2deaaa5b88b977bbb10abe25c9ad","0x480680017fff8000","0x1","0x480680017fff8000","0x53746f726167655772697465","0x400080007ff97fff","0x400080017ff97ffb","0x400080027ff97ffc","0x400080037ff97ffd","0x400080047ff97ffe","0x480080067ff98000","0x20680017fff7fff","0x135","0x480080057ff88000","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127fea7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0xf","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480a7ff87fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x402580017fe88003","0x7","0x1104800180018000","0x1e74","0x20680017fff7ffb","0xfd","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080037fff","0x4002800180037ffe","0x4002800280037ffa","0x4002800380037ffb","0x4002800480037ffc","0x4002800580037ffd","0x4802800780038000","0x20680017fff7fff","0xdf","0x4802800680038000","0x48127ff57fff8000","0x48127ffe7fff8000","0x4826800180038000","0x8","0x40137fff7fff8000","0x20780017fff7ff9","0x67","0x40780017fff7fff","0x1","0x48297ffb80007ffc","0x400080007ffe7fff","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x1104800180018000","0x2049","0x20680017fff7ffd","0x44","0x48127ffc7fff8000","0x480680017fff8000","0x3ea3b9a8522d36784cb325f9c7e2ec3c9f3e6d63031a6c6b8743cc22412f604","0x480680017fff8000","0x4c69627261727943616c6c","0x4002800080007fff","0x4002800180007ffd","0x4003800280007ffa","0x4002800380007ffe","0x4002800480007ffb","0x4002800580007ffc","0x4802800780008000","0x20680017fff7fff","0xc","0x4802800680008000","0x48127fff7fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x0","0x4802800880008000","0x4802800980008000","0x10780017fff7fff","0xb","0x4802800680008000","0x482480017fff8000","0x64","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x20680017fff7ffd","0x7","0x48127ff17fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x34","0x1104800180018000","0x26b5","0x482480017fff8000","0x26b4","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xe8d0","0x40780017fff7fff","0x1","0x480680017fff8000","0x4549435f4c49425f43414c4c5f4641494c4544","0x400080007ffe7fff","0x48127fe87fff8000","0x48307ffc7ff18000","0x480a80017fff8000","0x480a80027fff8000","0x48127fef7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x269b","0x482480017fff8000","0x269a","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x11878","0x48127ff47fff8000","0x48307ffe7ff48000","0x480a80017fff8000","0x480a80027fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff17fff8000","0x48127ff17fff8000","0x208b7fff7fff7ffe","0x48127ffd7fff8000","0x482480017ffd8000","0x3886","0x480a80007fff8000","0x480680017fff8000","0x5265706c616365436c617373","0x400080007ffe7fff","0x400080017ffe7ffd","0x400180027ffe7ff8","0x480080047ffe8000","0x20680017fff7fff","0xe","0x480080037ffd8000","0x48127fff7fff8000","0x482480017ffb8000","0x5","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x10780017fff7fff","0xb","0x480080037ffd8000","0x482480017fff8000","0x64","0x482480017ffb8000","0x7","0x480680017fff8000","0x1","0x480080057ff98000","0x480080067ff88000","0x20680017fff7ffd","0x35","0x48127ff57fff8000","0x48127ffa7fff8000","0x480a80017fff8000","0x480a80027fff8000","0x48127ff87fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x1104800180018000","0x1c99","0x20680017fff7ffd","0x12","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x1104800180018000","0x1d1f","0x208b7fff7fff7ffe","0x1104800180018000","0x2643","0x482480017fff8000","0x2642","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x5b36","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff27fff8000","0x48127ff27fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x2631","0x482480017fff8000","0x2630","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xbab8","0x40780017fff7fff","0x1","0x480680017fff8000","0x5245504c4143455f434c4153535f484153485f4641494c4544","0x400080007ffe7fff","0x48127fec7fff8000","0x48307ffc7ff18000","0x480a80017fff8000","0x480a80027fff8000","0x48127fef7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x4802800680038000","0x1104800180018000","0x2616","0x482480017fff8000","0x2615","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x12214","0x48307fff7ff88000","0x4826800180038000","0xa","0x4802800880038000","0x4802800980038000","0x10780017fff7fff","0x12","0x40780017fff7fff","0x4","0x1104800180018000","0x2603","0x482480017fff8000","0x2602","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x14d48","0x48307fff7fef8000","0x480a80037fff8000","0x48127ff17fff8000","0x48127ff17fff8000","0x48127fea7fff8000","0x48127ffb7fff8000","0x480a80017fff8000","0x480a80027fff8000","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x480080057ff88000","0x1104800180018000","0x25ea","0x482480017fff8000","0x25e9","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x17354","0x48127fe57fff8000","0x48307ffe7ff78000","0x480a80017fff8000","0x480a80027fff8000","0x482480017fec8000","0x9","0x480680017fff8000","0x1","0x480080077fea8000","0x480080087fe98000","0x208b7fff7fff7ffe","0x4802800680048000","0x1104800180018000","0x25d4","0x482480017fff8000","0x25d3","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x19eb0","0x48307fff7ff88000","0x4826800180048000","0xa","0x4802800880048000","0x4802800980048000","0x10780017fff7fff","0x12","0x40780017fff7fff","0x4","0x1104800180018000","0x25c1","0x482480017fff8000","0x25c0","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1c9e4","0x48307fff7fef8000","0x480a80047fff8000","0x48127ff17fff8000","0x48127ff17fff8000","0x48127fea7fff8000","0x48127ffb7fff8000","0x480a80017fff8000","0x480a80027fff8000","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x25a9","0x482480017fff8000","0x25a8","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1eda2","0x40780017fff7fff","0x1","0x480680017fff8000","0x494d504c454d454e544154494f4e5f45585049524544","0x400080007ffe7fff","0x482480017fe78000","0x2","0x48307ffc7ff28000","0x480a80017fff8000","0x480a80027fff8000","0x480a80047fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x258e","0x482480017fff8000","0x258d","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1ef78","0x40780017fff7fff","0x1","0x480680017fff8000","0x4e4f545f454e41424c45445f594554","0x400080007ffe7fff","0x482480017fea8000","0x1","0x48307ffc7ff28000","0x480a80017fff8000","0x480a80027fff8000","0x480a80047fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x2573","0x482480017fff8000","0x2572","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1f40a","0x48127ff17fff8000","0x48307ffe7ff18000","0x480a80017fff8000","0x480a80027fff8000","0x480a80047fff8000","0x480680017fff8000","0x1","0x48127ff17fff8000","0x48127ff17fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x255f","0x482480017fff8000","0x255e","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x3","0x482480017fff8000","0x25cce","0x48127ff17fff8000","0x48307ffe7ff18000","0x48127ff17fff8000","0x48127ff17fff8000","0x48127ff17fff8000","0x480680017fff8000","0x1","0x48127ff17fff8000","0x48127ff17fff8000","0x208b7fff7fff7ffe","0x480080027ff78000","0x1104800180018000","0x254a","0x482480017fff8000","0x2549","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x2c600","0x48127fe17fff8000","0x48307ffe7ff78000","0x48127fe17fff8000","0x480a7ff67fff8000","0x482480017feb8000","0x6","0x480680017fff8000","0x1","0x480080047fe98000","0x480080057fe88000","0x208b7fff7fff7ffe","0x1104800180018000","0x2535","0x482480017fff8000","0x2534","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x2eea0","0x40780017fff7fff","0x1","0x480680017fff8000","0x46494e414c495a4544","0x400080007ffe7fff","0x48127fe37fff8000","0x48307ffc7ff28000","0x48127fe37fff8000","0x480a7ff67fff8000","0x48127fed7fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x480080047ff78000","0x1104800180018000","0x251a","0x482480017fff8000","0x2519","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x2f3b4","0x48127fec7fff8000","0x48307ffe7ff78000","0x48127fec7fff8000","0x480a7ff67fff8000","0x482480017feb8000","0x8","0x480680017fff8000","0x1","0x480080067fe98000","0x480080077fe88000","0x208b7fff7fff7ffe","0x1104800180018000","0x2505","0x482480017fff8000","0x2504","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x31f10","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x480a7ff67fff8000","0x48127ff17fff8000","0x480680017fff8000","0x1","0x48127ff17fff8000","0x48127ff17fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x400280007ff37fff","0x400380017ff37ff5","0x480280027ff38000","0x400280037ff37fff","0x400380047ff37ff6","0x480280057ff38000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff17ffc","0x480280017ff17ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff17ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff17ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff17ffd","0x400280027ff17ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ff38000","0x6","0x482680017ff18000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ff47fff","0x400380017ff47ff2","0x400280027ff47ffc","0x400280037ff47ffb","0x480280057ff48000","0x20680017fff7fff","0x10a","0x480280047ff48000","0x480280067ff48000","0x482680017ff48000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x48307ffe80007fff","0x20680017fff7fff","0xe0","0x48127ffc7fff8000","0x20780017fff7ff6","0x1b","0x1104800180018000","0x24a3","0x482480017fff8000","0x24a2","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x7","0x482480017fff8000","0x1d100","0x40780017fff7fff","0x1","0x480680017fff8000","0x494e56414c49445f4143434f554e545f41444452455353","0x400080007ffe7fff","0x48127feb7fff8000","0x48307ffc7ff58000","0x48127fe87fff8000","0x48127fed7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480680017fff8000","0x2e9f66c6eea14532c94ad25405a4fcb32faa4969559c128d837caa0ec50a655","0x400080007ff27fff","0x400180017ff27ff5","0x480080027ff28000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007fee7ffc","0x480080017fed7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027feb7ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007fee7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017fec7ffd","0x400080027feb7ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff67fff8000","0x480680017fff8000","0x0","0x482480017fe88000","0x3","0x482480017fe88000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400080007feb7fff","0x400080017feb7ffb","0x400080027feb7ffc","0x400080037feb7ffa","0x480080057feb8000","0x20680017fff7fff","0x77","0x480080047fea8000","0x48127ffc7fff8000","0x48127ffe7fff8000","0x48127ff97fff8000","0x482480017fe68000","0x7","0x480080067fe58000","0x1104800180018000","0x1dee","0x20680017fff7ffd","0x5a","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480a7ff57fff8000","0x480a7ff67fff8000","0x1104800180018000","0x1e7a","0x40137ffb7fff8001","0x40137ffc7fff8000","0x20680017fff7ffd","0x45","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff77fff8000","0x48127ff77fff8000","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x1104800180018000","0x1ba0","0x20680017fff7ffb","0x26","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0xf","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x4c36","0x480a80017fff8000","0x480a80007fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x26","0x1104800180018000","0x23f5","0x482480017fff8000","0x23f4","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x13510","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x48127ff27fff8000","0x48127ff37fff8000","0x48127ff37fff8000","0x10780017fff7fff","0x14","0x480080047fea8000","0x1104800180018000","0x23e2","0x482480017fff8000","0x23e1","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x6","0x482480017fff8000","0x19dca","0x48127ff57fff8000","0x48307ffe7ff78000","0x48127ff27fff8000","0x482480017fdf8000","0x8","0x480080067fde8000","0x480080077fdd8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x23c8","0x482480017fff8000","0x23c7","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x7","0x482480017fff8000","0x1d2f4","0x48127fee7fff8000","0x48307ffe7ff48000","0x48127feb7fff8000","0x48127ff07fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x480280047ff48000","0x1104800180018000","0x23b2","0x482480017fff8000","0x23b1","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x7","0x482480017fff8000","0x1d6dc","0x48127ff57fff8000","0x48307ffe7ff78000","0x48127ff27fff8000","0x482680017ff48000","0x8","0x480680017fff8000","0x1","0x480280067ff48000","0x480280077ff48000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x400280007ff37fff","0x400380017ff37ff5","0x480280027ff38000","0x400280037ff37fff","0x400380047ff37ff6","0x480280057ff38000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff17ffc","0x480280017ff17ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff17ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff17ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff17ffd","0x400280027ff17ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ff38000","0x6","0x482680017ff18000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ff47fff","0x400380017ff47ff2","0x400280027ff47ffc","0x400280037ff47ffb","0x480280057ff48000","0x20680017fff7fff","0xee","0x480280047ff48000","0x480280067ff48000","0x482680017ff48000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x48307ffe80007fff","0x20680017fff7fff","0x17","0x1104800180018000","0x2353","0x482480017fff8000","0x2352","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x7","0x482480017fff8000","0x1d22c","0x48127fee7fff8000","0x48307ffe7ff48000","0x48127feb7fff8000","0x48127ff07fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x480680017fff8000","0x2e9f66c6eea14532c94ad25405a4fcb32faa4969559c128d837caa0ec50a655","0x400080007ff37fff","0x400180017ff37ff5","0x480080027ff38000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007fef7ffc","0x480080017fee7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027fec7ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007fef7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017fed7ffd","0x400080027fec7ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff37fff8000","0x480680017fff8000","0x0","0x482480017fe98000","0x3","0x482480017fe98000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400080007fec7fff","0x400080017fec7ffb","0x400080027fec7ffc","0x400080037fec7ffa","0x480080057fec8000","0x20680017fff7fff","0x77","0x480080047feb8000","0x48127ffc7fff8000","0x48127ffe7fff8000","0x48127ff97fff8000","0x482480017fe78000","0x7","0x480080067fe68000","0x1104800180018000","0x1ca2","0x20680017fff7ffd","0x5a","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480a7ff57fff8000","0x480a7ff67fff8000","0x1104800180018000","0x1e47","0x40137ffb7fff8001","0x40137ffc7fff8000","0x20680017fff7ffd","0x45","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff77fff8000","0x48127ff77fff8000","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x1104800180018000","0x1a54","0x20680017fff7ffb","0x26","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0xf","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x4c36","0x480a80017fff8000","0x480a80007fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0x26","0x1104800180018000","0x22a9","0x482480017fff8000","0x22a8","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x13510","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x48127ff27fff8000","0x48127ff37fff8000","0x48127ff37fff8000","0x10780017fff7fff","0x14","0x480080047feb8000","0x1104800180018000","0x2296","0x482480017fff8000","0x2295","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x6","0x482480017fff8000","0x19dca","0x48127ff57fff8000","0x48307ffe7ff78000","0x48127ff27fff8000","0x482480017fe08000","0x8","0x480080067fdf8000","0x480080077fde8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480280047ff48000","0x1104800180018000","0x227b","0x482480017fff8000","0x227a","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x7","0x482480017fff8000","0x1d614","0x48127ff57fff8000","0x48307ffe7ff78000","0x48127ff27fff8000","0x482680017ff48000","0x8","0x480680017fff8000","0x1","0x480280067ff48000","0x480280077ff48000","0x208b7fff7fff7ffe","0x4825800180007ffd","0x3711c9d994faf6055172091cb841fd4831aa743e6f3315163b06a122c841846","0x20680017fff7fff","0x1b","0x1104800180018000","0x2263","0x482480017fff8000","0x2262","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x13c22","0x40780017fff7fff","0x1","0x480680017fff8000","0x474f565f41444d494e5f43414e4e4f545f53454c465f52454d4f5645","0x400080007ffe7fff","0x480a7ff97fff8000","0x48327ffc7ffa8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480a7ffa7fff8000","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffc7fff","0x400280017ffc7ffe","0x480280037ffc8000","0x20680017fff7fff","0x51","0x480280027ffc8000","0x480280047ffc8000","0x48127ffe7fff8000","0x480080007ffe8000","0x480080017ffd8000","0x480080027ffc8000","0x480080037ffb8000","0x480080047ffa8000","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280057ffc7fff","0x400280067ffc7ff9","0x480280087ffc8000","0x20680017fff7fff","0x2d","0x480280077ffc8000","0x480280097ffc8000","0x480080027fff8000","0x48307ff880007fff","0x482680017ffc8000","0xa","0x48127ffb7fff8000","0x20680017fff7ffd","0xb","0x480a7ff97fff8000","0x48127ffe7fff8000","0x480a7ffb7fff8000","0x48127ffb7fff8000","0x480a7ffd7fff8000","0x48127ff07fff8000","0x1104800180018000","0x1d70","0x208b7fff7fff7ffe","0x1104800180018000","0x2221","0x482480017fff8000","0x2220","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0xe3da","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f4e4c595f53454c465f43414e5f52454e4f554e4345","0x400080007ffe7fff","0x480a7ff97fff8000","0x48307ffc7ff58000","0x480a7ffb7fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480280077ffc8000","0x1104800180018000","0x2207","0x482480017fff8000","0x2206","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0xe75e","0x480a7ff97fff8000","0x48307ffe7ff78000","0x480a7ffb7fff8000","0x482680017ffc8000","0xb","0x480680017fff8000","0x1","0x480280097ffc8000","0x4802800a7ffc8000","0x208b7fff7fff7ffe","0x480280027ffc8000","0x1104800180018000","0x21f2","0x482480017fff8000","0x21f1","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x11382","0x480a7ff97fff8000","0x48307ffe7ff78000","0x480a7ffb7fff8000","0x482680017ffc8000","0x6","0x480680017fff8000","0x1","0x480280047ffc8000","0x480280057ffc8000","0x208b7fff7fff7ffe","0xa0680017fff8005","0xe","0x4825800180057ffd","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ffa7ffc","0x480280017ffa7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ffa7ffc","0x10780017fff7fff","0x11","0x480a7ffd7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ffa7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ffa7ffd","0x400280027ffa7ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ffa8000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffc7fff","0x400380017ffc7ffb","0x400280027ffc7ffd","0x400280037ffc7ffc","0x480280057ffc8000","0x20680017fff7fff","0x87","0x480280047ffc8000","0x480280067ffc8000","0x482680017ffc8000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x57","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff48000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0x38","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x11","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x40780017fff7fff","0xc","0x482480017fec8000","0x1","0x482480017ff18000","0x6b8","0x48127fef7fff8000","0x480680017fff8000","0x0","0x48127fe17fff8000","0x48127feb7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017ff18000","0x3","0x48127ff67fff8000","0x48127ff47fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x1d","0x40780017fff7fff","0xb","0x480080047fec8000","0x48127ff17fff8000","0x482480017ffe8000","0x6a4","0x482480017fe98000","0x8","0x480080067fe88000","0x480080077fe78000","0x10780017fff7fff","0x23","0x40780017fff7fff","0xb","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fe68000","0x3","0x482480017feb8000","0x2d8c","0x48127fe97fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x16","0x480280047ffc8000","0x48127fe67fff8000","0x482480017ffe8000","0x3494","0x482680017ffc8000","0x8","0x480280067ffc8000","0x480280077ffc8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x400380007ffa7ffc","0x400380017ffa7ffd","0x480280027ffa8000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff87ffc","0x480280017ff87ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff87ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff87ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff87ffd","0x400280027ff87ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ffa8000","0x3","0x482680017ff88000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400380017ffb7ff9","0x400280027ffb7ffc","0x400280037ffb7ffb","0x480280057ffb8000","0x20680017fff7fff","0x89","0x480280047ffb8000","0x480280067ffb8000","0x482680017ffb8000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x58","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff38000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0x39","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x12","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x40780017fff7fff","0xc","0x482480017fec8000","0x1","0x482480017ff18000","0x6b8","0x48127fde7fff8000","0x48127fee7fff8000","0x480680017fff8000","0x0","0x48127fe07fff8000","0x48127fea7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017ff18000","0x3","0x48127ff67fff8000","0x48127ff47fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x1d","0x40780017fff7fff","0xb","0x480080047fec8000","0x48127ff17fff8000","0x482480017ffe8000","0x6a4","0x482480017fe98000","0x8","0x480080067fe88000","0x480080077fe78000","0x10780017fff7fff","0x24","0x40780017fff7fff","0xb","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fe68000","0x3","0x482480017feb8000","0x2d8c","0x48127fe97fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fde7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x16","0x480280047ffb8000","0x48127fe67fff8000","0x482480017ffe8000","0x3494","0x482680017ffb8000","0x8","0x480280067ffb8000","0x480280077ffb8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fde7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x400380007ff97ffb","0x400380017ff97ffc","0x480280027ff98000","0x400280037ff97fff","0x400380047ff97ffd","0x480280057ff98000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff77ffc","0x480280017ff77ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff77ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff77ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff77ffd","0x400280027ff77ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ff98000","0x6","0x482680017ff78000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffa7fff","0x400380017ffa7ff8","0x400280027ffa7ffc","0x400280037ffa7ffb","0x480280057ffa8000","0x20680017fff7fff","0x89","0x480280047ffa8000","0x480280067ffa8000","0x482680017ffa8000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x58","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff38000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0x39","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x12","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x40780017fff7fff","0xc","0x482480017fec8000","0x1","0x482480017ff18000","0x6b8","0x48127fde7fff8000","0x48127fee7fff8000","0x480680017fff8000","0x0","0x48127fe07fff8000","0x48127fea7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017ff18000","0x3","0x48127ff67fff8000","0x48127ff47fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x1d","0x40780017fff7fff","0xb","0x480080047fec8000","0x48127ff17fff8000","0x482480017ffe8000","0x6a4","0x482480017fe98000","0x8","0x480080067fe88000","0x480080077fe78000","0x10780017fff7fff","0x24","0x40780017fff7fff","0xb","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fe68000","0x3","0x482480017feb8000","0x2d8c","0x48127fe97fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fde7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x16","0x480280047ffa8000","0x48127fe67fff8000","0x482480017ffe8000","0x3494","0x482680017ffa8000","0x8","0x480280067ffa8000","0x480280077ffa8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fde7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x20780017fff7ffa","0x1b","0x1104800180018000","0x1f84","0x482480017fff8000","0x1f83","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1e23a","0x40780017fff7fff","0x1","0x480680017fff8000","0x45524332303a207472616e736665722066726f6d2030","0x400080007ffe7fff","0x480a7ff67fff8000","0x48327ffc7ff78000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480a7ff77fff8000","0x20780017fff7ffb","0x1b","0x1104800180018000","0x1f68","0x482480017fff8000","0x1f67","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1e172","0x40780017fff7fff","0x1","0x480680017fff8000","0x45524332303a207472616e7366657220746f2030","0x400080007ffe7fff","0x480a7ff67fff8000","0x48307ffc7ff58000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x400280007ff87fff","0x400380017ff87ffa","0x480280027ff88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff67ffc","0x480280017ff67ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff67ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff67ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff67ffd","0x400280027ff67ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff67fff8000","0x480680017fff8000","0x0","0x482680017ff88000","0x3","0x482680017ff68000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ff97fff","0x400280017ff97ffb","0x400280027ff97ffc","0x400280037ff97ffa","0x480280057ff98000","0x20680017fff7fff","0x354","0x480280047ff98000","0x480280067ff98000","0x482680017ff98000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x321","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff28000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0x2f9","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x2c8","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x48287ffd80017ffb","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0xd","0x400080017ff57fff","0x40780017fff7fff","0x1","0x482480017ff48000","0x2","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff48000","0x2","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc80017fe9","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe80017ff9","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0x26e","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0x258","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x400080007fd87fff","0x400180017fd87ffa","0x480080027fd88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff67ffc","0x480080017ff57ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027ff37ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff67ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff47ffd","0x400080027ff37ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff47fff8000","0x480680017fff8000","0x0","0x482480017fce8000","0x3","0x482480017ff08000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400080007fdc7fff","0x400080017fdc7ffb","0x400080027fdc7ffc","0x400080037fdc7ffa","0x400080047fdc7ff0","0x480080067fdc8000","0x20680017fff7fff","0x20a","0x480080057fdb8000","0x48127fff7fff8000","0x480680017fff8000","0x0","0x482480017ff68000","0x1","0x480680017fff8000","0x53746f726167655772697465","0x400080077fd67fff","0x400080087fd67ffc","0x400080097fd67ffd","0x4000800a7fd67ffe","0x4000800b7fd67feb","0x4800800d7fd68000","0x20680017fff7fff","0x1e8","0x4800800c7fd58000","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x400080007ff47fff","0x400180017ff47ffb","0x480080027ff48000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff07ffc","0x480080017fef7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027fed7ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff07ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017fee7ffd","0x400080027fed7ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff67fff8000","0x480680017fff8000","0x0","0x482480017fea8000","0x3","0x482480017fea8000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x4000800e7fc67fff","0x4000800f7fc67ffb","0x400080107fc67ffc","0x400080117fc67ffa","0x480080137fc68000","0x20680017fff7fff","0x19b","0x480080127fc58000","0x480080147fc48000","0x482480017fc38000","0x15","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x16a","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff28000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0x144","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x115","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x48287ffd7ffb8001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0xd","0x400080017ff57fff","0x40780017fff7fff","0x1","0x482480017ff48000","0x2","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff48000","0x2","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc7fe98001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe7ff98001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0xbd","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0xa9","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x400080007fd87fff","0x400180017fd87ffb","0x480080027fd88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff67ffc","0x480080017ff57ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027ff37ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff67ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff47ffd","0x400080027ff37ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff47fff8000","0x480680017fff8000","0x0","0x402580017fce8001","0x3","0x482480017ff18000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400080007fdd7fff","0x400080017fdd7ffc","0x400080027fdd7ffd","0x400080037fdd7ffb","0x400080047fdd7ff1","0x480080067fdd8000","0x20680017fff7fff","0x64","0x480080057fdc8000","0x48127fff7fff8000","0x480680017fff8000","0x0","0x482480017ff78000","0x1","0x480680017fff8000","0x53746f726167655772697465","0x400080077fd77fff","0x400080087fd77ffc","0x400080097fd77ffd","0x4000800a7fd77ffe","0x4000800b7fd77fec","0x4800800d7fd78000","0x20680017fff7fff","0x4b","0x4800800c7fd68000","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff47fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x19","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x402580017fc68000","0xe","0x1104800180018000","0x14ab","0x20680017fff7ffb","0x26","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0xf","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x4800800c7fd68000","0x482480017fff8000","0x4d58","0x482480017fd48000","0x10","0x4800800e7fd38000","0x4800800f7fd28000","0x10780017fff7fff","0xb","0x40780017fff7fff","0x6","0x480080057fd68000","0x482480017fff8000","0x78dc","0x482480017fd48000","0x9","0x480080077fd38000","0x480080087fd28000","0x48127ff27fff8000","0x48127ffb7fff8000","0x480a80017fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x1cee","0x482480017fff8000","0x1ced","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xa8c0","0x48127ff67fff8000","0x48307ffe7ff68000","0x10780017fff7fff","0xf","0x40780017fff7fff","0x3","0x1104800180018000","0x1ce0","0x482480017fff8000","0x1cdf","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xa9ce","0x482480017feb8000","0x2","0x48307ffe7ff28000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f616464204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fcd7fff8000","0x48127fdd7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x1cc6","0x482480017fff8000","0x1cc5","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xadc0","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017feb8000","0x3","0x48307ffc7ff08000","0x48127fee7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x2b","0x40780017fff7fff","0xb","0x480080047fec8000","0x1104800180018000","0x1cad","0x482480017fff8000","0x1cac","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xb4c8","0x48127feb7fff8000","0x48307ffe7ff88000","0x482480017fe38000","0x8","0x480080067fe28000","0x480080077fe18000","0x10780017fff7fff","0x2b","0x40780017fff7fff","0xb","0x1104800180018000","0x1c9b","0x482480017fff8000","0x1c9a","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xdb4c","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fe08000","0x3","0x48307ffc7fe58000","0x48127fe37fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x13","0x40780017fff7fff","0x16","0x480080127faf8000","0x1104800180018000","0x1c82","0x482480017fff8000","0x1c81","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xe2b8","0x48127fe07fff8000","0x48307ffe7ff88000","0x482480017fa68000","0x16","0x480080147fa58000","0x480080157fa48000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fd87fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x4800800c7fd58000","0x1104800180018000","0x1c6a","0x482480017fff8000","0x1c69","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1159e","0x48307fff7ff88000","0x482480017fcc8000","0x10","0x4800800e7fcb8000","0x4800800f7fca8000","0x10780017fff7fff","0x14","0x40780017fff7fff","0x6","0x480080057fd58000","0x1104800180018000","0x1c56","0x482480017fff8000","0x1c55","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x14122","0x48307fff7ff88000","0x482480017fcc8000","0x9","0x480080077fcb8000","0x480080087fca8000","0x48127feb7fff8000","0x48127ffb7fff8000","0x48127fe87fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x1c3e","0x482480017fff8000","0x1c3d","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x3","0x482480017fff8000","0x17368","0x48127ff57fff8000","0x48307ffe7ff58000","0x10780017fff7fff","0x11","0x40780017fff7fff","0x3","0x1104800180018000","0x1c2e","0x482480017fff8000","0x1c2d","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x3","0x482480017fff8000","0x17476","0x482480017fea8000","0x2","0x48307ffe7ff18000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f737562204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fcc7fff8000","0x48127fdc7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x1c12","0x482480017fff8000","0x1c11","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x3","0x482480017fff8000","0x17868","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fea8000","0x3","0x48307ffc7fef8000","0x48127fed7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x2f","0x40780017fff7fff","0xb","0x480080047fec8000","0x1104800180018000","0x1bf7","0x482480017fff8000","0x1bf6","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x3","0x482480017fff8000","0x17f70","0x48127fea7fff8000","0x48307ffe7ff78000","0x482480017fe28000","0x8","0x480080067fe18000","0x480080077fe08000","0x10780017fff7fff","0x2f","0x40780017fff7fff","0xb","0x1104800180018000","0x1be3","0x482480017fff8000","0x1be2","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x3","0x482480017fff8000","0x1a5f4","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fdf8000","0x3","0x48307ffc7fe48000","0x48127fe27fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x15","0x40780017fff7fff","0x16","0x480280047ff98000","0x1104800180018000","0x1bc8","0x482480017fff8000","0x1bc7","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x3","0x482480017fff8000","0x1ad60","0x48127fdf7fff8000","0x48307ffe7ff78000","0x482680017ff98000","0x8","0x480280067ff98000","0x480280077ff98000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fd77fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x3c87bf42ed4f01f11883bf54f43d91d2cbbd5fec26d1df9c74c57ae138800a4","0x400280007ff87fff","0x400380017ff87ffa","0x480280027ff88000","0x400280037ff87fff","0x400380047ff87ffb","0x480280057ff88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff67ffc","0x480280017ff67ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff67ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff67ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff67ffd","0x400280027ff67ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ff88000","0x6","0x482680017ff68000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ff97fff","0x400380017ff97ff7","0x400280027ff97ffc","0x400280037ff97ffb","0x480280057ff98000","0x20680017fff7fff","0x138","0x480280047ff98000","0x480280067ff98000","0x482680017ff98000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x105","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff38000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0xdd","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0xac","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ff17fff8000","0x48127ffb7fff8000","0x482480017ff68000","0x1","0x48127ffb7fff8000","0x48127fed7fff8000","0x48127ff77fff8000","0x4824800180007ffa","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x4","0x10780017fff7fff","0x8","0x40780017fff7fff","0x2","0x482480017ffa8000","0xb4","0x10780017fff7fff","0xa","0x48127ffc7fff8000","0x4824800180007ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x4","0x10780017fff7fff","0x7a","0x48127ffe7fff8000","0x48287ffd80017ffb","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff57fff","0x10780017fff7fff","0xd","0x400080007ff67fff","0x40780017fff7fff","0x1","0x482480017ff58000","0x1","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff58000","0x1","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc80017ff3","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe80017ff9","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0x23","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0xd","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fce7fff8000","0x48127fde7fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x1104800180018000","0xa7","0x208b7fff7fff7ffe","0x1104800180018000","0x1acd","0x482480017fff8000","0x1acc","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xaf14","0x48127ff57fff8000","0x48307ffe7ff58000","0x10780017fff7fff","0x11","0x40780017fff7fff","0x3","0x1104800180018000","0x1abd","0x482480017fff8000","0x1abc","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb022","0x482480017fea8000","0x2","0x48307ffe7ff18000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f737562204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fc37fff8000","0x48127fd37fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x1aa1","0x482480017fff8000","0x1aa0","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xbba8","0x48127ff27fff8000","0x48307ffe7ff68000","0x48127fda7fff8000","0x48127fea7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x1104800180018000","0x1a8c","0x482480017fff8000","0x1a8b","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb8c4","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fea8000","0x3","0x48307ffc7fef8000","0x48127fed7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x2f","0x40780017fff7fff","0xb","0x480080047fec8000","0x1104800180018000","0x1a71","0x482480017fff8000","0x1a70","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xbfcc","0x48127fea7fff8000","0x48307ffe7ff78000","0x482480017fe28000","0x8","0x480080067fe18000","0x480080077fe08000","0x10780017fff7fff","0x2f","0x40780017fff7fff","0xb","0x1104800180018000","0x1a5d","0x482480017fff8000","0x1a5c","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xe650","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fdf8000","0x3","0x48307ffc7fe48000","0x48127fe27fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x15","0x40780017fff7fff","0x16","0x480280047ff98000","0x1104800180018000","0x1a42","0x482480017fff8000","0x1a41","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xedbc","0x48127fdf7fff8000","0x48307ffe7ff78000","0x482680017ff98000","0x8","0x480280067ff98000","0x480280077ff98000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fd77fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x20780017fff7ffa","0x1b","0x1104800180018000","0x1a25","0x482480017fff8000","0x1a24","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xab7c","0x40780017fff7fff","0x1","0x480680017fff8000","0x45524332303a20617070726f76652066726f6d2030","0x400080007ffe7fff","0x480a7ff67fff8000","0x48327ffc7ff78000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480a7ff77fff8000","0x20780017fff7ffb","0x1b","0x1104800180018000","0x1a09","0x482480017fff8000","0x1a08","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xaab4","0x40780017fff7fff","0x1","0x480680017fff8000","0x45524332303a20617070726f766520746f2030","0x400080007ffe7fff","0x480a7ff67fff8000","0x48307ffc7ff58000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480680017fff8000","0x3c87bf42ed4f01f11883bf54f43d91d2cbbd5fec26d1df9c74c57ae138800a4","0x400280007ff87fff","0x400380017ff87ffa","0x480280027ff88000","0x400280037ff87fff","0x400380047ff87ffb","0x480280057ff88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff67ffc","0x480280017ff67ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff67ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff67ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff67ffd","0x400280027ff67ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff57fff8000","0x480680017fff8000","0x0","0x402780017ff88001","0x6","0x482680017ff68000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400280007ff97fff","0x400280017ff97ffc","0x400280027ff97ffd","0x400280037ff97ffb","0x400380047ff97ffc","0x480280067ff98000","0x20680017fff7fff","0x64","0x480280057ff98000","0x48127fff7fff8000","0x480680017fff8000","0x0","0x482480017ff78000","0x1","0x480680017fff8000","0x53746f726167655772697465","0x400280077ff97fff","0x400280087ff97ffc","0x400280097ff97ffd","0x4002800a7ff97ffe","0x4003800b7ff97ffd","0x4802800d7ff98000","0x20680017fff7fff","0x4b","0x4802800c7ff98000","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff47fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x17","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x402780017ff98000","0xe","0x1104800180018000","0x1103","0x20680017fff7ffb","0x26","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0xf","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x4802800c7ff98000","0x482480017fff8000","0x4d58","0x482680017ff98000","0x10","0x4802800e7ff98000","0x4802800f7ff98000","0x10780017fff7fff","0xb","0x40780017fff7fff","0x6","0x480280057ff98000","0x482480017fff8000","0x78dc","0x482680017ff98000","0x9","0x480280077ff98000","0x480280087ff98000","0x48127ff27fff8000","0x48127ffb7fff8000","0x480a80017fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffa7fff","0x400380017ffa7ff8","0x480280037ffa8000","0x20680017fff7fff","0x172","0x480280027ffa8000","0x480280047ffa8000","0x480080027fff8000","0x480680017fff8000","0x3c87bf42ed4f01f11883bf54f43d91d2cbbd5fec26d1df9c74c57ae138800a4","0x400280007ff97fff","0x400280017ff97ffe","0x480280027ff98000","0x400280037ff97fff","0x400380047ff97ffb","0x480280057ff98000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff77ffc","0x480280017ff77ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff77ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff77ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff77ffd","0x400280027ff77ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff37fff8000","0x480680017fff8000","0x0","0x482680017ff98000","0x6","0x482680017ff78000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280057ffa7fff","0x400280067ffa7ffb","0x400280077ffa7ffc","0x400280087ffa7ffa","0x4802800a7ffa8000","0x20680017fff7fff","0x11e","0x480280097ffa8000","0x4802800b7ffa8000","0x482680017ffa8000","0xc","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0xeb","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff28000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0xc3","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x92","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x48287ffd7ffb8001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0xd","0x400080017ff57fff","0x40780017fff7fff","0x1","0x482480017ff48000","0x2","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff48000","0x2","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc7fe98001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe7ff98001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0x38","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0x22","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fd77fff8000","0x48127fe77fff8000","0x48127fc87fff8000","0x480a7ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffe4e","0x20680017fff7ffd","0xd","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x185e","0x482480017fff8000","0x185d","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb234","0x48127ff57fff8000","0x48307ffe7ff58000","0x10780017fff7fff","0x11","0x40780017fff7fff","0x3","0x1104800180018000","0x184e","0x482480017fff8000","0x184d","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb342","0x482480017fea8000","0x2","0x48307ffe7ff18000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f616464204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fcc7fff8000","0x48127fdc7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x1832","0x482480017fff8000","0x1831","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb734","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fea8000","0x3","0x48307ffc7fef8000","0x48127fed7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x2f","0x40780017fff7fff","0xb","0x480080047fec8000","0x1104800180018000","0x1817","0x482480017fff8000","0x1816","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xbe3c","0x48127fea7fff8000","0x48307ffe7ff78000","0x482480017fe28000","0x8","0x480080067fe18000","0x480080077fe08000","0x10780017fff7fff","0x2f","0x40780017fff7fff","0xb","0x1104800180018000","0x1803","0x482480017fff8000","0x1802","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xe4c0","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fdf8000","0x3","0x48307ffc7fe48000","0x48127fe27fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x15","0x40780017fff7fff","0x16","0x480280097ffa8000","0x1104800180018000","0x17e8","0x482480017fff8000","0x17e7","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xec2c","0x48127fdf7fff8000","0x48307ffe7ff78000","0x482680017ffa8000","0xd","0x4802800b7ffa8000","0x4802800c7ffa8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fd77fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480280027ffa8000","0x1104800180018000","0x17ce","0x482480017fff8000","0x17cd","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1235e","0x480a7ff77fff8000","0x48307ffe7ff78000","0x480a7ff97fff8000","0x482680017ffa8000","0x6","0x480680017fff8000","0x1","0x480280047ffa8000","0x480280057ffa8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffa7fff","0x400380017ffa7ff8","0x480280037ffa8000","0x20680017fff7fff","0x172","0x480280027ffa8000","0x480280047ffa8000","0x480080027fff8000","0x480680017fff8000","0x3c87bf42ed4f01f11883bf54f43d91d2cbbd5fec26d1df9c74c57ae138800a4","0x400280007ff97fff","0x400280017ff97ffe","0x480280027ff98000","0x400280037ff97fff","0x400380047ff97ffb","0x480280057ff98000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff77ffc","0x480280017ff77ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff77ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff77ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff77ffd","0x400280027ff77ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff37fff8000","0x480680017fff8000","0x0","0x482680017ff98000","0x6","0x482680017ff78000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280057ffa7fff","0x400280067ffa7ffb","0x400280077ffa7ffc","0x400280087ffa7ffa","0x4802800a7ffa8000","0x20680017fff7fff","0x11e","0x480280097ffa8000","0x4802800b7ffa8000","0x482680017ffa8000","0xc","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0xeb","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff28000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0xc3","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x92","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x48287ffd80017ffb","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0xd","0x400080017ff57fff","0x40780017fff7fff","0x1","0x482480017ff48000","0x2","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff48000","0x2","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc80017fe9","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe80017ff9","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0x38","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0x22","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fd77fff8000","0x48127fe77fff8000","0x48127fc87fff8000","0x480a7ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffcc2","0x20680017fff7ffd","0xd","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x16d2","0x482480017fff8000","0x16d1","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb234","0x48127ff57fff8000","0x48307ffe7ff58000","0x10780017fff7fff","0x11","0x40780017fff7fff","0x3","0x1104800180018000","0x16c2","0x482480017fff8000","0x16c1","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb342","0x482480017fea8000","0x2","0x48307ffe7ff18000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f737562204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fcc7fff8000","0x48127fdc7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x16a6","0x482480017fff8000","0x16a5","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb734","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fea8000","0x3","0x48307ffc7fef8000","0x48127fed7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x2f","0x40780017fff7fff","0xb","0x480080047fec8000","0x1104800180018000","0x168b","0x482480017fff8000","0x168a","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xbe3c","0x48127fea7fff8000","0x48307ffe7ff78000","0x482480017fe28000","0x8","0x480080067fe18000","0x480080077fe08000","0x10780017fff7fff","0x2f","0x40780017fff7fff","0xb","0x1104800180018000","0x1677","0x482480017fff8000","0x1676","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xe4c0","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fdf8000","0x3","0x48307ffc7fe48000","0x48127fe27fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x15","0x40780017fff7fff","0x16","0x480280097ffa8000","0x1104800180018000","0x165c","0x482480017fff8000","0x165b","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xec2c","0x48127fdf7fff8000","0x48307ffe7ff78000","0x482680017ffa8000","0xd","0x4802800b7ffa8000","0x4802800c7ffa8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fd77fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480280027ffa8000","0x1104800180018000","0x1642","0x482480017fff8000","0x1641","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x1235e","0x480a7ff77fff8000","0x48307ffe7ff78000","0x480a7ff97fff8000","0x482680017ffa8000","0x6","0x480680017fff8000","0x1","0x480280047ffa8000","0x480280057ffa8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x0","0x480680017fff8000","0x341c1bdfd89f69748aa00b5742b03adbffd79b8e80cab5c50d91cd8c2a79be1","0x480680017fff8000","0x53746f726167655772697465","0x400280007ff47fff","0x400380017ff47ff2","0x400280027ff47ffd","0x400280037ff47ffe","0x400380047ff47ff5","0x480280067ff48000","0x20680017fff7fff","0x11f","0x480280057ff48000","0x48127fff7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0xb6ce5410fca59d078ee9b2a4371a9d684c530d697c64fbef0ae6d5e8f0ac72","0x480680017fff8000","0x53746f726167655772697465","0x400280077ff47fff","0x400280087ff47ffc","0x400280097ff47ffd","0x4002800a7ff47ffe","0x4003800b7ff47ff6","0x4802800d7ff48000","0x20680017fff7fff","0xfd","0x4802800c7ff48000","0x48127fff7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x1f0d4aa99431d246bac9b8e48c33e888245b15e9678f64f9bdfc8823dc8f979","0x480680017fff8000","0x53746f726167655772697465","0x4002800e7ff47fff","0x4002800f7ff47ffc","0x400280107ff47ffd","0x400280117ff47ffe","0x400380127ff47ff7","0x480280147ff48000","0x20680017fff7fff","0xdb","0x480280137ff48000","0x480a7ff17fff8000","0x48127ffe7fff8000","0x480a7ff37fff8000","0x482680017ff48000","0x15","0x480a7ffa7fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x1104800180018000","0x4c3","0x20680017fff7ffd","0xbb","0x48127ffa7fff8000","0x20780017fff7ffb","0x1b","0x1104800180018000","0x15f0","0x482480017fff8000","0x15ef","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0xa","0x482480017fff8000","0x35d7c","0x40780017fff7fff","0x1","0x480680017fff8000","0x494e56414c49445f4d494e5445525f41444452455353","0x400080007ffe7fff","0x48127fef7fff8000","0x48307ffc7ff58000","0x48127fef7fff8000","0x48127fef7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48127fff7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x1390569bb0a3a722eb4228e8700301347da081211d5c2ded2db22ef389551ab","0x480680017fff8000","0x53746f726167655772697465","0x400080007ff77fff","0x400080017ff77ffc","0x400080027ff77ffd","0x400080037ff77ffe","0x400180047ff77ffb","0x480080067ff78000","0x20680017fff7fff","0x7b","0x480080057ff68000","0x48127ff27fff8000","0x48127ffe7fff8000","0x48127ff27fff8000","0x482480017ff28000","0x7","0x480a7ffc7fff8000","0x1104800180018000","0x1226","0x20680017fff7ffd","0x5f","0x48127ffa7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x3fc801c47df4de8d5835f8bfd4d0b8823ba63e5a3f278086901402d680abfc","0x480680017fff8000","0x53746f726167655772697465","0x400080007ff87fff","0x400080017ff87ffc","0x400080027ff87ffd","0x400080037ff87ffe","0x400180047ff87ffd","0x480080067ff88000","0x20680017fff7fff","0x3d","0x480080057ff78000","0x48127ff37fff8000","0x48127ffe7fff8000","0x48127ff37fff8000","0x482480017ff38000","0x7","0x1104800180018000","0x12d9","0x20680017fff7ffd","0x29","0x48127ffa7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x38bb9518f707d6868da0178f4ac498e320441f8f7e11ff8a35ed4ea8286e693","0x480680017fff8000","0x53746f726167655772697465","0x400080007ff87fff","0x400080017ff87ffc","0x400080027ff87ffd","0x400080037ff87ffe","0x400080047ff87ffb","0x480080067ff88000","0x20680017fff7fff","0xf","0x480080057ff78000","0x48127ff37fff8000","0x48127ffe7fff8000","0x48127ff37fff8000","0x482480017ff38000","0x7","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x480080057ff78000","0x48127ff37fff8000","0x48127ffe7fff8000","0x48127ff37fff8000","0x482480017ff38000","0x9","0x480680017fff8000","0x1","0x480080077ff18000","0x480080087ff08000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2bc0","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480080057ff78000","0x1104800180018000","0x1572","0x482480017fff8000","0x1571","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x70a8","0x48127fed7fff8000","0x48307ffe7ff88000","0x48127fed7fff8000","0x482480017fed8000","0x9","0x480680017fff8000","0x1","0x480080077feb8000","0x480080087fea8000","0x208b7fff7fff7ffe","0x1104800180018000","0x1560","0x482480017fff8000","0x155f","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x9c68","0x48127ff37fff8000","0x48307ffe7ff38000","0x48127ff37fff8000","0x48127ff37fff8000","0x480680017fff8000","0x1","0x48127ff37fff8000","0x48127ff37fff8000","0x208b7fff7fff7ffe","0x480080057ff68000","0x1104800180018000","0x154e","0x482480017fff8000","0x154d","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0xa","0x482480017fff8000","0x332e8","0x48127feb7fff8000","0x48307ffe7ff78000","0x48127feb7fff8000","0x482480017feb8000","0x9","0x480680017fff8000","0x1","0x480080077fe98000","0x480080087fe88000","0x208b7fff7fff7ffe","0x1104800180018000","0x153a","0x482480017fff8000","0x1539","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0xa","0x482480017fff8000","0x35f70","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff27fff8000","0x48127ff27fff8000","0x208b7fff7fff7ffe","0x480280137ff48000","0x1104800180018000","0x1526","0x482480017fff8000","0x1525","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0xc","0x482480017fff8000","0x54e5c","0x48307fff7ff88000","0x482680017ff48000","0x17","0x480280157ff48000","0x480280167ff48000","0x10780017fff7fff","0x24","0x4802800c7ff48000","0x1104800180018000","0x1514","0x482480017fff8000","0x1513","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0xc","0x482480017fff8000","0x57a1c","0x48307fff7ff88000","0x482680017ff48000","0x10","0x4802800e7ff48000","0x4802800f7ff48000","0x10780017fff7fff","0x12","0x480280057ff48000","0x1104800180018000","0x1502","0x482480017fff8000","0x1501","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0xc","0x482480017fff8000","0x5a640","0x48307fff7ff88000","0x482680017ff48000","0x9","0x480280077ff48000","0x480280087ff48000","0x480a7ff17fff8000","0x48127ffb7fff8000","0x480a7ff37fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffd7fff","0x400380017ffd7ffb","0x480280037ffd8000","0x20680017fff7fff","0x7c","0x480280027ffd8000","0x480280047ffd8000","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x480680017fff8000","0x251e864ca2a080f55bce5da2452e8cfcafdbc951a3e7fff5023d558452ec228","0x400280007ffc7ffe","0x400280017ffc7fff","0x480280027ffc8000","0x480080027ffc8000","0x400280037ffc7ffe","0x400280047ffc7fff","0x480280057ffc8000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ffa7ffc","0x480280017ffa7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ffa7ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ffa7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ffa7ffd","0x400280027ffa7ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x482680017ffc8000","0x6","0x482680017ffa8000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280057ffd7fff","0x400280067ffd7ffb","0x400280077ffd7ffc","0x400280087ffd7ffa","0x4802800a7ffd8000","0x20680017fff7fff","0x34","0x480280097ffd8000","0x4802800b7ffd8000","0x482680017ffd8000","0xc","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x48307ffe80007fff","0x20680017fff7fff","0x11","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f4e4c595f555047524144455f474f5645524e4f52","0x400080007ffe7fff","0x48127ff37fff8000","0x48127ff97fff8000","0x48127ff07fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x48127ff37fff8000","0x482480017ff98000","0xb4","0x48127ff07fff8000","0x48127ff57fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x9","0x480280097ffd8000","0x48127ff37fff8000","0x482480017ffe8000","0x456","0x48127ff07fff8000","0x482680017ffd8000","0xd","0x480680017fff8000","0x1","0x4802800b7ffd8000","0x4802800c7ffd8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x16","0x480280027ffd8000","0x1104800180018000","0x1466","0x482480017fff8000","0x1465","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x357a","0x480a7ffa7fff8000","0x48307ffe7ff78000","0x480a7ffc7fff8000","0x482680017ffd8000","0x6","0x480680017fff8000","0x1","0x480280047ffd8000","0x480280057ffd8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x3c87bf42ed4f01f11883bf54f43d91d2cbbd5fec26d1df9c74c57ae138800a4","0x400280007ff87fff","0x400380017ff87ffa","0x480280027ff88000","0x400280037ff87fff","0x400380047ff87ffb","0x480280057ff88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff67ffc","0x480280017ff67ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff67ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff67ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff67ffd","0x400280027ff67ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ff88000","0x6","0x482680017ff68000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ff97fff","0x400380017ff97ff7","0x400280027ff97ffc","0x400280037ff97ffb","0x480280057ff98000","0x20680017fff7fff","0x1c6","0x480280047ff98000","0x480280067ff98000","0x482680017ff98000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x193","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff38000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0x16b","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x13a","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x480680017fff8000","0xffffffffffffffffffffffffffffffff","0x48127ffd7fff8000","0x48287ffd80017ffe","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff37fff","0x10780017fff7fff","0xd","0x400080017ff47fff","0x40780017fff7fff","0x1","0x482480017ff38000","0x2","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff38000","0x2","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0xffffffffffffffffffffffffffffffff","0x48287ffc80017fff","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff87fff","0x10780017fff7fff","0xd","0x400080007ff97fff","0x40780017fff7fff","0x5","0x482480017ff48000","0x1","0x482480017ff48000","0x208","0x48127ff87fff8000","0x48127ff37fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48307ffe80017ff8","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff37fff","0x10780017fff7fff","0xdc","0x400080017ff47fff","0x482480017ff48000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff3","0xc6","0x48127ffd7fff8000","0x48307fe680017ffe","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff87fff","0x10780017fff7fff","0xa5","0x400080007ff97fff","0x482480017ff98000","0x1","0x48127ffc7fff8000","0x48307fe280007ffa","0x20680017fff7fff","0x4","0x10780017fff7fff","0x9","0x40780017fff7fff","0x3","0x48127ffa7fff8000","0x482480017ffa8000","0x154","0x10780017fff7fff","0xf","0x48127ffe7fff8000","0x48307fd580017ff7","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0x7d","0x400080007ffa7fff","0x482480017ffa8000","0x1","0x48127ffc7fff8000","0x48287ffd7fdc8001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080007ffb7fff","0x10780017fff7fff","0xd","0x400080007ffc7fff","0x40780017fff7fff","0x1","0x482480017ffb8000","0x1","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ffb8000","0x1","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc7fca8001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe7ff98001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0x23","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0xd","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fb87fff8000","0x48127fc87fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x48127ff87fff8000","0x48127ff87fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffff8fb","0x208b7fff7fff7ffe","0x1104800180018000","0x1320","0x482480017fff8000","0x131f","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xaf14","0x48127ff57fff8000","0x48307ffe7ff58000","0x10780017fff7fff","0x11","0x40780017fff7fff","0x3","0x1104800180018000","0x1310","0x482480017fff8000","0x130f","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb022","0x482480017fea8000","0x2","0x48307ffe7ff18000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f616464204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fad7fff8000","0x48127fbd7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x12f4","0x482480017fff8000","0x12f3","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xbb44","0x482480017ff28000","0x1","0x48307ffe7ff48000","0x10780017fff7fff","0x11","0x40780017fff7fff","0x6","0x1104800180018000","0x12e3","0x482480017fff8000","0x12e2","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xbed2","0x482480017feb8000","0x1","0x48307ffe7fee8000","0x48127fc47fff8000","0x48127fd47fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x1104800180018000","0x12cd","0x482480017fff8000","0x12cc","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xbfb8","0x48127ff57fff8000","0x48307ffe7ff58000","0x10780017fff7fff","0x11","0x40780017fff7fff","0x3","0x1104800180018000","0x12bd","0x482480017fff8000","0x12bc","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xc0c6","0x482480017fe98000","0x2","0x48307ffe7ff18000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f737562204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fca7fff8000","0x48127fda7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x12a1","0x482480017fff8000","0x12a0","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xc580","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fea8000","0x3","0x48307ffc7fef8000","0x48127fed7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x2f","0x40780017fff7fff","0xb","0x480080047fec8000","0x1104800180018000","0x1286","0x482480017fff8000","0x1285","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xcc88","0x48127fea7fff8000","0x48307ffe7ff78000","0x482480017fe28000","0x8","0x480080067fe18000","0x480080077fe08000","0x10780017fff7fff","0x2f","0x40780017fff7fff","0xb","0x1104800180018000","0x1272","0x482480017fff8000","0x1271","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xf30c","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fdf8000","0x3","0x48307ffc7fe48000","0x48127fe27fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x15","0x40780017fff7fff","0x16","0x480280047ff98000","0x1104800180018000","0x1257","0x482480017fff8000","0x1256","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xfa78","0x48127fdf7fff8000","0x48307ffe7ff78000","0x482680017ff98000","0x8","0x480280067ff98000","0x480280077ff98000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fd77fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x480680017fff8000","0x2ab9656e71e13c39f9f290cc5354d2e50a410992032118a1779539be0e4e75","0x400080007ffe7fff","0x400180017ffe7ff9","0x400180027ffe7ffa","0x400180037ffe7ffc","0x400180047ffe7ffd","0x48127ffe7fff8000","0x482480017ffd8000","0x5","0x40327ffe80007fff","0x480680017fff8000","0x0","0x4828800080017fff","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400280007ff67fff","0x10780017fff7fff","0x1a","0x400280007ff67fff","0x1104800180018000","0x1224","0x482480017fff8000","0x1223","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xb9a","0x40780017fff7fff","0x1","0x480680017fff8000","0x5265717569726573206174206c65617374206f6e6520656c656d656e74","0x400080007ffe7fff","0x482680017ff68000","0x1","0x48327ffc7ff78000","0x480a7ff87fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x482680017ff68000","0x1","0x480a7ff77fff8000","0x480a7ff87fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x480680017fff8000","0x0","0x1104800180018000","0x21","0x20680017fff7ffc","0xf","0x400080007ffb7fff","0x400180017ffb8000","0x48127ff97fff8000","0x482480017ff98000","0x190","0x482480017ff98000","0x3","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480080027ff68000","0x208b7fff7fff7ffe","0x1104800180018000","0x11f4","0x482480017fff8000","0x11f3","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x0","0x48127ff37fff8000","0x48307ffe7ff38000","0x48127ff37fff8000","0x480680017fff8000","0x1","0x48127ff47fff8000","0x48127ff47fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x11e4","0x482480017fff8000","0x11e3","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xc62","0xa0680017fff8000","0x8","0x48317ffe80007ff9","0x482480017fff8000","0x100000000000000000000000000000000","0x400280007ff87fff","0x10780017fff7fff","0x3c","0x48317ffe80007ff9","0x400280007ff87fff","0x482680017ff88000","0x1","0x48127ffe7fff8000","0x48297ffb80007ffc","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffe7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x480680017fff8000","0x0","0x480a7ffb7fff8000","0x10780017fff7fff","0x9","0x48127ffe7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xf","0x480080007fff8000","0x400380007ffa7ffd","0x400280017ffa7fff","0x48127ff77fff8000","0x48127ff97fff8000","0x482680017ffa8000","0x3","0x48127ff87fff8000","0x48127ff87fff8000","0x480280027ffa8000","0x1104800180018000","0x800000000000010ffffffffffffffffffffffffffffffffffffffffffffffcb","0x208b7fff7fff7ffe","0x1104800180018000","0x11ab","0x482480017fff8000","0x11aa","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x7b2","0x48127ff27fff8000","0x48307ffe7ff48000","0x480a7ffa7fff8000","0x480680017fff8000","0x0","0x48127ff27fff8000","0x48127ff27fff8000","0x480a7ffd7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff88000","0x1","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x400180007fff7ffb","0x48297ffc80007ffd","0x400080017ffe7fff","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ffa7fff8000","0x482480017ff98000","0x2","0x1104800180018000","0xae5","0x20680017fff7ffd","0x3e","0x48127ffc7fff8000","0x480680017fff8000","0x28420862938116cb3bbdbedee07451ccc54d4e9412dbef71142ad1980a30941","0x480680017fff8000","0x43616c6c436f6e7472616374","0x400280007ff97fff","0x400280017ff97ffd","0x400380027ff97ffa","0x400280037ff97ffe","0x400280047ff97ffb","0x400280057ff97ffc","0x480280077ff98000","0x20680017fff7fff","0x25","0x480280067ff98000","0x480280087ff98000","0x480280097ff98000","0x482680017ff98000","0xa","0x48127ffc7fff8000","0x48307ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xc","0x48127ff17fff8000","0x482480017ffd8000","0x190","0x48127ffb7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480080007ff68000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x52657475726e6564206461746120746f6f2073686f7274","0x400080007ffe7fff","0x48127fef7fff8000","0x48127ffb7fff8000","0x48127ff97fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x480280067ff98000","0x48127ff67fff8000","0x482480017ffe8000","0x3e8","0x482680017ff98000","0xa","0x480680017fff8000","0x1","0x480280087ff98000","0x480280097ff98000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x482480017ffb8000","0x2fa8","0x480a7ff97fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x20780017fff7ffb","0x1b","0x1104800180018000","0x1130","0x482480017fff8000","0x112f","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1e578","0x40780017fff7fff","0x1","0x480680017fff8000","0x45524332303a206d696e7420746f2030","0x400080007ffe7fff","0x480a7ff77fff8000","0x48327ffc7ff88000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ffa7fff8000","0x480680017fff8000","0x110e2f729c9c2b988559994a3daccd838cf52faf88e18101373e67dd061455a","0x1104800180018000","0xec7","0x20680017fff7ffd","0x2a4","0x48127ffb7fff8000","0x48287ffd7ffe8001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080007ff67fff","0x10780017fff7fff","0xd","0x400080007ff77fff","0x40780017fff7fff","0x1","0x482480017ff68000","0x1","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff68000","0x1","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc7ff68001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe7ff98001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0x24d","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0x237","0x48127ffd7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x110e2f729c9c2b988559994a3daccd838cf52faf88e18101373e67dd061455a","0x480680017fff8000","0x53746f726167655772697465","0x400080007fe57fff","0x400080017fe57ffc","0x400080027fe57ffd","0x400080037fe57ffe","0x400080047fe57ffa","0x480080067fe58000","0x20680017fff7fff","0x20d","0x480080057fe48000","0x480680017fff8000","0x110e2f729c9c2b988559994a3daccd838cf52faf88e18101373e67dd061455a","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ffd8000","0x1","0x480680017fff8000","0x53746f726167655772697465","0x400080077fde7fff","0x400080087fde7ffc","0x400080097fde7ffd","0x4000800a7fde7ffe","0x4000800b7fde7ff4","0x4800800d7fde8000","0x20680017fff7fff","0x1e9","0x4800800c7fdd8000","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x400280007ff97fff","0x400380017ff97ffb","0x480280027ff98000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007fe97ffc","0x480080017fe87ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027fe67ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007fe97ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017fe77ffd","0x400080027fe67ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff67fff8000","0x480680017fff8000","0x0","0x482680017ff98000","0x3","0x482480017fe38000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x4000800e7fce7fff","0x4000800f7fce7ffb","0x400080107fce7ffc","0x400080117fce7ffa","0x480080137fce8000","0x20680017fff7fff","0x19c","0x480080127fcd8000","0x480080147fcc8000","0x482480017fcb8000","0x15","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x16b","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff28000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0x145","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x116","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x48287ffd7ffb8001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0xd","0x400080017ff57fff","0x40780017fff7fff","0x1","0x482480017ff48000","0x2","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff48000","0x2","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc7fe98001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe7ff98001","0xa0680017fff7fff","0x7","0x4824800180007fff","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0xbe","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0xaa","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x400080007fd87fff","0x400180017fd87ffb","0x480080027fd88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff67ffc","0x480080017ff57ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027ff37ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff67ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff47ffd","0x400080027ff37ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff47fff8000","0x480680017fff8000","0x0","0x402580017fce8001","0x3","0x482480017ff18000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400080007fdd7fff","0x400080017fdd7ffc","0x400080027fdd7ffd","0x400080037fdd7ffb","0x400080047fdd7ff1","0x480080067fdd8000","0x20680017fff7fff","0x65","0x480080057fdc8000","0x48127fff7fff8000","0x480680017fff8000","0x0","0x482480017ff78000","0x1","0x480680017fff8000","0x53746f726167655772697465","0x400080077fd77fff","0x400080087fd77ffc","0x400080097fd77ffd","0x4000800a7fd77ffe","0x4000800b7fd77fec","0x4800800d7fd78000","0x20680017fff7fff","0x4c","0x4800800c7fd68000","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff47fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x19","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x402580017fc68000","0xe","0x1104800180018000","0x705","0x20680017fff7ffb","0x26","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0xf","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x4800800c7fd68000","0x482480017fff8000","0x4d58","0x482480017fd48000","0x10","0x4800800e7fd38000","0x4800800f7fd28000","0x10780017fff7fff","0xb","0x40780017fff7fff","0x6","0x480080057fd68000","0x482480017fff8000","0x78dc","0x482480017fd48000","0x9","0x480080077fd38000","0x480080087fd28000","0x48127ff27fff8000","0x48127ffb7fff8000","0x480a80017fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0xf48","0x482480017fff8000","0xf47","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xa8c0","0x48127ff67fff8000","0x48307ffe7ff68000","0x10780017fff7fff","0xf","0x40780017fff7fff","0x3","0x1104800180018000","0xf3a","0x482480017fff8000","0xf39","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xa9ce","0x482480017feb8000","0x2","0x48307ffe7ff28000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f616464204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fcd7fff8000","0x48127fdd7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0xf20","0x482480017fff8000","0xf1f","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xadc0","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017feb8000","0x3","0x48307ffc7ff08000","0x48127fee7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x2b","0x40780017fff7fff","0xb","0x480080047fec8000","0x1104800180018000","0xf07","0x482480017fff8000","0xf06","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xb4c8","0x48127feb7fff8000","0x48307ffe7ff88000","0x482480017fe38000","0x8","0x480080067fe28000","0x480080077fe18000","0x10780017fff7fff","0x2b","0x40780017fff7fff","0xb","0x1104800180018000","0xef5","0x482480017fff8000","0xef4","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xdb4c","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fe08000","0x3","0x48307ffc7fe58000","0x48127fe37fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x13","0x40780017fff7fff","0x16","0x480080127fb78000","0x1104800180018000","0xedc","0x482480017fff8000","0xedb","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xe2b8","0x48127fe07fff8000","0x48307ffe7ff88000","0x482480017fae8000","0x16","0x480080147fad8000","0x480080157fac8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fd87fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x4800800c7fdd8000","0x1104800180018000","0xec4","0x482480017fff8000","0xec3","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1159e","0x48307fff7ff88000","0x482480017fd48000","0x10","0x4800800e7fd38000","0x4800800f7fd28000","0x10780017fff7fff","0x14","0x40780017fff7fff","0x7","0x480080057fdd8000","0x1104800180018000","0xeb0","0x482480017fff8000","0xeaf","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1417c","0x48307fff7ff88000","0x482480017fd48000","0x9","0x480080077fd38000","0x480080087fd28000","0x48127fe47fff8000","0x48127ffb7fff8000","0x480a7ff97fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0xe98","0x482480017fff8000","0xe97","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x16d1e","0x48127ff57fff8000","0x48307ffe7ff58000","0x10780017fff7fff","0x11","0x40780017fff7fff","0x3","0x1104800180018000","0xe88","0x482480017fff8000","0xe87","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x16e2c","0x482480017fea8000","0x2","0x48307ffe7ff18000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f616464204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x480a7ff97fff8000","0x48127fdb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0xe6c","0x482480017fff8000","0xe6b","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x17a16","0x48127ff37fff8000","0x48307ffe7ff38000","0x480a7ff97fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff27fff8000","0x48127ff27fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x20780017fff7ffb","0x1b","0x1104800180018000","0xe55","0x482480017fff8000","0xe54","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1e578","0x40780017fff7fff","0x1","0x480680017fff8000","0x45524332303a206275726e2066726f6d2030","0x400080007ffe7fff","0x480a7ff77fff8000","0x48327ffc7ff88000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ffa7fff8000","0x480680017fff8000","0x110e2f729c9c2b988559994a3daccd838cf52faf88e18101373e67dd061455a","0x1104800180018000","0xbec","0x20680017fff7ffd","0x2a4","0x48127ffb7fff8000","0x48287ffd80017ffe","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff67fff","0x10780017fff7fff","0xd","0x400080007ff77fff","0x40780017fff7fff","0x1","0x482480017ff68000","0x1","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff68000","0x1","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc80017ff6","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe80017ff9","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0x24d","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0x237","0x48127ffd7fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x110e2f729c9c2b988559994a3daccd838cf52faf88e18101373e67dd061455a","0x480680017fff8000","0x53746f726167655772697465","0x400080007fe57fff","0x400080017fe57ffc","0x400080027fe57ffd","0x400080037fe57ffe","0x400080047fe57ffa","0x480080067fe58000","0x20680017fff7fff","0x20d","0x480080057fe48000","0x480680017fff8000","0x110e2f729c9c2b988559994a3daccd838cf52faf88e18101373e67dd061455a","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ffd8000","0x1","0x480680017fff8000","0x53746f726167655772697465","0x400080077fde7fff","0x400080087fde7ffc","0x400080097fde7ffd","0x4000800a7fde7ffe","0x4000800b7fde7ff4","0x4800800d7fde8000","0x20680017fff7fff","0x1e9","0x4800800c7fdd8000","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x400280007ff97fff","0x400380017ff97ffb","0x480280027ff98000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007fe97ffc","0x480080017fe87ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027fe67ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007fe97ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017fe77ffd","0x400080027fe67ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff67fff8000","0x480680017fff8000","0x0","0x482680017ff98000","0x3","0x482480017fe38000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x4000800e7fce7fff","0x4000800f7fce7ffb","0x400080107fce7ffc","0x400080117fce7ffa","0x480080137fce8000","0x20680017fff7fff","0x19c","0x480080127fcd8000","0x480080147fcc8000","0x482480017fcb8000","0x15","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x16b","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff28000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0x145","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x116","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x48287ffd80017ffb","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0xd","0x400080017ff57fff","0x40780017fff7fff","0x1","0x482480017ff48000","0x2","0x48127ffb7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x9","0x482480017ff48000","0x2","0x482480017ffb8000","0xa","0x48127ffd7fff8000","0x480680017fff8000","0x1","0x48287ffc80017fe9","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff97fff","0x10780017fff7fff","0xd","0x400080007ffa7fff","0x40780017fff7fff","0x5","0x482480017ff58000","0x1","0x482480017ff58000","0x208","0x48127ff87fff8000","0x48127ff47fff8000","0x10780017fff7fff","0x13","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48307ffe80017ff9","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400080017ff47fff","0x10780017fff7fff","0xbe","0x400080017ff57fff","0x482480017ff58000","0x2","0x48127ffc7fff8000","0x48127ff97fff8000","0x48127ffc7fff8000","0x20680017fff7ff4","0xaa","0x480680017fff8000","0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a","0x400080007fd87fff","0x400180017fd87ffb","0x480080027fd88000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff67ffc","0x480080017ff57ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027ff37ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff67ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff47ffd","0x400080027ff37ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff47fff8000","0x480680017fff8000","0x0","0x402580017fce8001","0x3","0x482480017ff18000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400080007fdd7fff","0x400080017fdd7ffc","0x400080027fdd7ffd","0x400080037fdd7ffb","0x400080047fdd7ff1","0x480080067fdd8000","0x20680017fff7fff","0x65","0x480080057fdc8000","0x48127fff7fff8000","0x480680017fff8000","0x0","0x482480017ff78000","0x1","0x480680017fff8000","0x53746f726167655772697465","0x400080077fd77fff","0x400080087fd77ffc","0x400080097fd77ffd","0x4000800a7fd77ffe","0x4000800b7fd77fec","0x4800800d7fd78000","0x20680017fff7fff","0x4c","0x4800800c7fd68000","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff47fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x19","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480a7ffb7fff8000","0x480680017fff8000","0x0","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x402580017fc68000","0xe","0x1104800180018000","0x42a","0x20680017fff7ffb","0x26","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0xf","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x4800800c7fd68000","0x482480017fff8000","0x4d58","0x482480017fd48000","0x10","0x4800800e7fd38000","0x4800800f7fd28000","0x10780017fff7fff","0xb","0x40780017fff7fff","0x6","0x480080057fd68000","0x482480017fff8000","0x78dc","0x482480017fd48000","0x9","0x480080077fd38000","0x480080087fd28000","0x48127ff27fff8000","0x48127ffb7fff8000","0x480a80017fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0xc6d","0x482480017fff8000","0xc6c","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xa8c0","0x48127ff67fff8000","0x48307ffe7ff68000","0x10780017fff7fff","0xf","0x40780017fff7fff","0x3","0x1104800180018000","0xc5f","0x482480017fff8000","0xc5e","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xa9ce","0x482480017feb8000","0x2","0x48307ffe7ff28000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f737562204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x48127fcd7fff8000","0x48127fdd7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0xc45","0x482480017fff8000","0xc44","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xadc0","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017feb8000","0x3","0x48307ffc7ff08000","0x48127fee7fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x2b","0x40780017fff7fff","0xb","0x480080047fec8000","0x1104800180018000","0xc2c","0x482480017fff8000","0xc2b","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xb4c8","0x48127feb7fff8000","0x48307ffe7ff88000","0x482480017fe38000","0x8","0x480080067fe28000","0x480080077fe18000","0x10780017fff7fff","0x2b","0x40780017fff7fff","0xb","0x1104800180018000","0xc1a","0x482480017fff8000","0xc19","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xdb4c","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fe08000","0x3","0x48307ffc7fe58000","0x48127fe37fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x13","0x40780017fff7fff","0x16","0x480080127fb78000","0x1104800180018000","0xc01","0x482480017fff8000","0xc00","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xe2b8","0x48127fe07fff8000","0x48307ffe7ff88000","0x482480017fae8000","0x16","0x480080147fad8000","0x480080157fac8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127fd87fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x4800800c7fdd8000","0x1104800180018000","0xbe9","0x482480017fff8000","0xbe8","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1159e","0x48307fff7ff88000","0x482480017fd48000","0x10","0x4800800e7fd38000","0x4800800f7fd28000","0x10780017fff7fff","0x14","0x40780017fff7fff","0x7","0x480080057fdd8000","0x1104800180018000","0xbd5","0x482480017fff8000","0xbd4","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x1417c","0x48307fff7ff88000","0x482480017fd48000","0x9","0x480080077fd38000","0x480080087fd28000","0x48127fe47fff8000","0x48127ffb7fff8000","0x480a7ff97fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0xbbd","0x482480017fff8000","0xbbc","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x16d1e","0x48127ff57fff8000","0x48307ffe7ff58000","0x10780017fff7fff","0x11","0x40780017fff7fff","0x3","0x1104800180018000","0xbad","0x482480017fff8000","0xbac","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x16e2c","0x482480017fea8000","0x2","0x48307ffe7ff18000","0x40780017fff7fff","0x1","0x480680017fff8000","0x753235365f737562204f766572666c6f77","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x480a7ff97fff8000","0x48127fdb7fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0xb91","0x482480017fff8000","0xb90","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x17a16","0x48127ff37fff8000","0x48307ffe7ff38000","0x480a7ff97fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff27fff8000","0x48127ff27fff8000","0x208b7fff7fff7ffe","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xa","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x8","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0xbb","0x20680017fff7fff","0x8a","0x48307ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xa","0x482480017ffb8000","0x1","0x48127ffb7fff8000","0x480680017fff8000","0x0","0x480080007ff88000","0x10780017fff7fff","0x8","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x60","0xa0680017fff8004","0xe","0x4824800180047ffe","0x800000000000000000000000000000000000000000000000000000000000000","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8002","0x480280007ffb7ffc","0x480280017ffb7ffc","0x402480017ffb7ffd","0xffffffffffffffeeffffffffffffffff","0x400280027ffb7ffd","0x10780017fff7fff","0x4c","0x484480017fff8001","0x8000000000000000000000000000000","0x48307fff80007ffd","0x480280007ffb7ffd","0x480280017ffb7ffd","0x402480017ffc7ffe","0xf8000000000000000000000000000000","0x400280027ffb7ffe","0x482680017ffb8000","0x3","0x48127ff67fff8000","0x48127ff67fff8000","0x1104800180018000","0x9ae","0x20680017fff7ffa","0x2a","0x20680017fff7ffd","0xb","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x0","0x48127fd27fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x10780017fff7fff","0xc","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x20680017fff7ffc","0xc","0x48127ff37fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x10780017fff7fff","0x47","0x40780017fff7fff","0x4","0x48127fef7fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x10780017fff7fff","0x1f","0x40780017fff7fff","0xd","0x48127fec7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127fea7fff8000","0x48127fea7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2e","0x482680017ffb8000","0x3","0x10780017fff7fff","0x5","0x40780017fff7fff","0x34","0x480a7ffb7fff8000","0x48127fc77fff8000","0x48127fc77fff8000","0x48127ffd7fff8000","0x480680017fff8000","0x0","0x48127ffc7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x34","0x4824800180007fcb","0x1","0x20680017fff7fff","0x19","0x480a7ffb7fff8000","0x48127fc67fff8000","0x48127fc67fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff97fff8000","0x480680017fff8000","0x0","0x48127ff87fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x7","0x480a7ffb7fff8000","0x480680017fff8000","0x0","0x48127fbe7fff8000","0x48127fbe7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x3c","0x480a7ffb7fff8000","0x480680017fff8000","0x0","0x48127fbe7fff8000","0x48127fbe7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x400380007ffd7ff6","0x480a7ffc7fff8000","0x482680017ffd8000","0x1","0x20780017fff7ff7","0x21","0x480680017fff8000","0x0","0x400080007ffe7fff","0x400180017ffe7ff8","0x48297ff980007ffa","0x400080027ffd7fff","0x480a7ff47fff8000","0x480a7ff57fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x48127ff87fff8000","0x482480017ff88000","0x3","0x1104800180018000","0x3f3","0x20680017fff7ffd","0x8","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127ffc7fff8000","0x48127ffc7fff8000","0x10780017fff7fff","0x13","0x48127ffb7fff8000","0x482480017ffb8000","0x3e8","0x480680017fff8000","0x1","0x48127ffb7fff8000","0x48127ffb7fff8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x1","0x400080007ffe7fff","0x480a7ff47fff8000","0x482680017ff58000","0xa0a","0x48127ffb7fff8000","0x482480017ffb8000","0x1","0x20780017fff7ffb","0x7","0x48127ffd7fff8000","0x480680017fff8000","0x0","0x10780017fff7fff","0x6","0x482480017ffd8000","0x64","0x480680017fff8000","0x1","0x400080007ffd7fff","0x48127ffa7fff8000","0x48127ffd7fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0xa5c","0x482480017fff8000","0xa5b","0x480080007fff8000","0x480080037fff8000","0x482480017fff8000","0xc62","0xa0680017fff8000","0x8","0x48317ffe80007ff6","0x482480017fff8000","0x100000000000000000000000000000000","0x400280007ff57fff","0x10780017fff7fff","0x7f","0x48317ffe80007ff6","0x400280007ff57fff","0x482680017ff58000","0x1","0x48127ffe7fff8000","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x65","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x48127ffc7fff8000","0x480280007ffc8000","0x48307ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xb","0x48127ffd7fff8000","0x482480017ffa8000","0x1","0x48127ffa7fff8000","0x480680017fff8000","0x0","0x48127ff77fff8000","0x10780017fff7fff","0x9","0x48127ffd7fff8000","0x48127ffa7fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x38","0x480080007fff8000","0x48327ff87ff98000","0x48327ffe7ffa8000","0x400280007ff77ffe","0x400280017ff77fff","0x400380027ff77ffb","0x48127ff87fff8000","0x482680017ff78000","0x6","0x480280037ff78000","0x480280047ff78000","0x480280057ff78000","0xa0680017fff8000","0x9","0x4824800180007ffa","0x816","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007fe87fff","0x10780017fff7fff","0x12","0x4824800180007ffa","0x816","0x400080007fe97fff","0x482480017fe98000","0x1","0x48127ffe7fff8000","0x48127ff87fff8000","0x480a7ff87fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127ff77fff8000","0x48127feb7fff8000","0x48127feb7fff8000","0x1104800180018000","0x800000000000010ffffffffffffffffffffffffffffffffffffffffffffffa9","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017fe68000","0x1","0x48127ff57fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48327ff97ff98000","0x482680017ffa8000","0x1","0x400280007ff77ffe","0x400280017ff77fff","0x400380027ff77ffb","0x48127ff17fff8000","0x482480017ff88000","0x5be","0x482680017ff78000","0x6","0x480680017fff8000","0x0","0x48127ff67fff8000","0x48127ff67fff8000","0x480280037ff78000","0x208b7fff7fff7ffe","0x482680017ff98000","0x1","0x400280007ff77fff","0x400380017ff77ffa","0x400380027ff77ffb","0x48127ffc7fff8000","0x482480017ffc8000","0xad2","0x482680017ff78000","0x6","0x480680017fff8000","0x0","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480280037ff78000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff58000","0x1","0x480a7ff67fff8000","0x480a7ff77fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480a7ff27fff8000","0x480a7ff37fff8000","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x48127ff77fff8000","0x48127ff67fff8000","0x1104800180018000","0x800000000000010ffffffffffffffffffffffffffffffffffffffffffffff15","0x20680017fff7ffd","0x72","0x1104800180018000","0x9ae","0x482480017fff8000","0x9ad","0x48127ff87fff8000","0x48127ff87fff8000","0x480a7ff57fff8000","0x480080007ffc8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff47fff8000","0x48127ff47fff8000","0x1104800180018000","0x800000000000010ffffffffffffffffffffffffffffffffffffffffffffff43","0x20680017fff7ffc","0x4f","0x480680017fff8000","0x1ac8d354f2e793629cb233a16f10d13cf15b9c45bbc620577c8e1df95ede545","0x400280007ff47fff","0x400280017ff47ffe","0x480280027ff48000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff37ffc","0x480080017ff27ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027ff07ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff37ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff17ffd","0x400080027ff07ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff17fff8000","0x480680017fff8000","0x0","0x482680017ff48000","0x3","0x482480017fed8000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400280007ff67fff","0x400280017ff67ffb","0x400280027ff67ffc","0x400280037ff67ffa","0x400380047ff67ffd","0x480280067ff68000","0x20680017fff7fff","0x10","0x480280057ff68000","0x48127ffc7fff8000","0x48127ffe7fff8000","0x48127ff97fff8000","0x48127fe87fff8000","0x482680017ff68000","0x7","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x480280057ff68000","0x48127ffc7fff8000","0x48127ffe7fff8000","0x48127ff97fff8000","0x48127fe87fff8000","0x482680017ff68000","0x9","0x480680017fff8000","0x1","0x480280077ff68000","0x480280087ff68000","0x208b7fff7fff7ffe","0x1104800180018000","0x94d","0x482480017fff8000","0x94c","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x2dbe","0x48127ff37fff8000","0x48307ffe7ff38000","0x48127ff37fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x10780017fff7fff","0xf","0x1104800180018000","0x93e","0x482480017fff8000","0x93d","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x3c78","0x48127ff57fff8000","0x48307ffe7ff58000","0x480a7ff57fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ff47fff8000","0x48127ffa7fff8000","0x480a7ff67fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480a7ff27fff8000","0x480a7ff37fff8000","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x48127ff77fff8000","0x48127ff67fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffe7e","0x20680017fff7ffd","0x72","0x1104800180018000","0x917","0x482480017fff8000","0x916","0x48127ff87fff8000","0x48127ff87fff8000","0x480a7ff57fff8000","0x480080007ffc8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff47fff8000","0x48127ff47fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffeac","0x20680017fff7ffc","0x4f","0x480680017fff8000","0x2a31bbb25d4dfa03fe73a91cbbab880b7c9cc4461880193ae5819ca6bbfe7cc","0x400280007ff47fff","0x400280017ff47ffe","0x480280027ff48000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff37ffc","0x480080017ff27ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027ff07ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff37ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff17ffd","0x400080027ff07ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff17fff8000","0x480680017fff8000","0x0","0x482680017ff48000","0x3","0x482480017fed8000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400280007ff67fff","0x400280017ff67ffb","0x400280027ff67ffc","0x400280037ff67ffa","0x400380047ff67ffd","0x480280067ff68000","0x20680017fff7fff","0x10","0x480280057ff68000","0x48127ffc7fff8000","0x48127ffe7fff8000","0x48127ff97fff8000","0x48127fe87fff8000","0x482680017ff68000","0x7","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x480280057ff68000","0x48127ffc7fff8000","0x48127ffe7fff8000","0x48127ff97fff8000","0x48127fe87fff8000","0x482680017ff68000","0x9","0x480680017fff8000","0x1","0x480280077ff68000","0x480280087ff68000","0x208b7fff7fff7ffe","0x1104800180018000","0x8b6","0x482480017fff8000","0x8b5","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x2dbe","0x48127ff37fff8000","0x48307ffe7ff38000","0x48127ff37fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x10780017fff7fff","0xf","0x1104800180018000","0x8a7","0x482480017fff8000","0x8a6","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x3c78","0x48127ff57fff8000","0x48307ffe7ff58000","0x480a7ff57fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ff47fff8000","0x48127ffa7fff8000","0x480a7ff67fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x6","0x10b7ff37fff7fff","0x10780017fff7fff","0x11d","0x10780017fff7fff","0x10c","0x10780017fff7fff","0xfb","0x10780017fff7fff","0xea","0x10780017fff7fff","0xd8","0x10780017fff7fff","0xc6","0x10780017fff7fff","0xb4","0x10780017fff7fff","0xa4","0x10780017fff7fff","0x7a","0x10780017fff7fff","0x50","0x10780017fff7fff","0x26","0x10780017fff7fff","0x13","0x480680017fff8000","0x99cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9","0x400280007ffb7fff","0x400380017ffb7ff6","0x400380027ffb7ff7","0x400380007ffd7ff8","0x400380017ffd7ff9","0x482680017ff28000","0x141e","0x480a7ffa7fff8000","0x482680017ffb8000","0x3","0x480a7ffc7fff8000","0x482680017ffd8000","0x2","0x10780017fff7fff","0x103","0x480680017fff8000","0x134692b230b9e1ffa39098904722134159652b09c5bc41d88d6698779d228ff","0x400280007ffb7fff","0x400380017ffb7ff6","0x400380027ffb7ff7","0x400380007ffd7ff8","0x400380017ffd7ff9","0x482680017ff28000","0x13ba","0x480a7ffa7fff8000","0x482680017ffb8000","0x3","0x480a7ffc7fff8000","0x482680017ffd8000","0x2","0x10780017fff7fff","0xf2","0x480680017fff8000","0x38a81c7fd04bac40e22e3eab2bcb3a09398bba67d0c5a263c6665c9c0b13a3","0x400280007ffb7fff","0x480a7ff17fff8000","0x480a7ff27fff8000","0x480a7ff47fff8000","0x480a7ff57fff8000","0x480a7ff67fff8000","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x400b7ffa7fff8004","0x402780017ffb8005","0x1","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffda6","0x20680017fff7ffd","0xb","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x0","0x480a80047fff8000","0x480a80057fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x7633a8d8b49c5c6002a1329e2c9791ea2ced86e06e01e17b5d0d1d5312c792","0x400280007ffb7fff","0x480a7ff17fff8000","0x480a7ff27fff8000","0x480a7ff47fff8000","0x480a7ff57fff8000","0x480a7ff67fff8000","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x400b7ffa7fff8002","0x402780017ffb8003","0x1","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffd7e","0x20680017fff7ffd","0xb","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x0","0x480a80027fff8000","0x480a80037fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x34bb683f971572e1b0f230f3dd40f3dbcee94e0b3e3261dd0a91229a1adc4b7","0x400280007ffb7fff","0x480a7ff17fff8000","0x480a7ff27fff8000","0x480a7ff47fff8000","0x480a7ff57fff8000","0x480a7ff67fff8000","0x480a7ff77fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x400b7ffa7fff8000","0x402780017ffb8001","0x1","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffd56","0x20680017fff7ffd","0xb","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x0","0x480a80007fff8000","0x480a80017fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480680017fff8000","0xd1831486d8c46712712653f17d3414869aa50b4c16836d0b3d4afcfeafa024","0x400280007ffb7fff","0x400380007ffd7ff9","0x482680017ff28000","0x14e6","0x480a7ffa7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x482680017ffd8000","0x1","0x10780017fff7fff","0x6c","0x480680017fff8000","0x9d4a59b844ac9d98627ddba326ab3707a7d7e105fd03c777569d0f61a91f1e","0x400280007ffb7fff","0x400380007ffd7ff7","0x400380017ffd7ff8","0x400380027ffd7ff9","0x482680017ff28000","0x141e","0x480a7ffa7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x482680017ffd8000","0x3","0x10780017fff7fff","0x5c","0x480680017fff8000","0x2842fd3b01bb0858fef6a2da51cdd9f995c7d36d7625fb68dd5d69fcc0a6d76","0x400280007ffb7fff","0x400380007ffd7ff7","0x400380017ffd7ff8","0x400380027ffd7ff9","0x482680017ff28000","0x141e","0x480a7ffa7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x482680017ffd8000","0x3","0x10780017fff7fff","0x4c","0x480680017fff8000","0x2b23b0c08c7b22209aea4100552de1b7876a49f04ee5a4d94f83ad24bc4ec1c","0x400280007ffb7fff","0x400380007ffd7ff7","0x400380017ffd7ff8","0x400380027ffd7ff9","0x482680017ff28000","0x141e","0x480a7ffa7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x482680017ffd8000","0x3","0x10780017fff7fff","0x3c","0x480680017fff8000","0x3ae95723946e49d38f0cf844cef1fb25870e9a74999a4b96271625efa849b4c","0x400280007ffb7fff","0x400380007ffd7ff8","0x400380017ffd7ff9","0x482680017ff28000","0x1482","0x480a7ffa7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x482680017ffd8000","0x2","0x10780017fff7fff","0x2d","0x480680017fff8000","0x2d8a82390cce552844e57407d23a1e48a38c4b979d525b1673171e503e116ab","0x400280007ffb7fff","0x400380007ffd7ff8","0x400380017ffd7ff9","0x482680017ff28000","0x1482","0x480a7ffa7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x482680017ffd8000","0x2","0x10780017fff7fff","0x1e","0x480680017fff8000","0x2143175c365244751ccde24dd8f54f934672d6bc9110175c9e58e1e73705531","0x400280007ffb7fff","0x400380007ffd7ff8","0x400380017ffd7ff9","0x482680017ff28000","0x1482","0x480a7ffa7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x482680017ffd8000","0x2","0x10780017fff7fff","0xf","0x480680017fff8000","0x25e2d538533284b9d61dfe45b9aaa563d33ef8374d9bb26d77a009b8e21f0de","0x400280007ffb7fff","0x400380007ffd7ff8","0x400380017ffd7ff9","0x482680017ff28000","0x14e6","0x480a7ffa7fff8000","0x482680017ffb8000","0x1","0x480a7ffc7fff8000","0x482680017ffd8000","0x2","0x480a7ff17fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x0","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480a7ff37fff8000","0x480a7ff47fff8000","0x480a7ff87fff8000","0x480a7ff97fff8000","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x48127ff77fff8000","0x48127ff67fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffcb1","0x20680017fff7ffd","0x9d","0x1104800180018000","0x74a","0x482480017fff8000","0x749","0x48127ff87fff8000","0x48127ff87fff8000","0x480a7ff67fff8000","0x480080007ffc8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff47fff8000","0x48127ff47fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffcdf","0x20680017fff7ffc","0x7a","0x480680017fff8000","0x2a31bbb25d4dfa03fe73a91cbbab880b7c9cc4461880193ae5819ca6bbfe7cc","0x400280007ff57fff","0x400280017ff57ffe","0x480280027ff58000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff37ffc","0x480080017ff27ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027ff07ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff37ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff17ffd","0x400080027ff07ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff17fff8000","0x480680017fff8000","0x0","0x482680017ff58000","0x3","0x482480017fed8000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ff77fff","0x400280017ff77ffb","0x400280027ff77ffc","0x400280037ff77ffa","0x480280057ff78000","0x20680017fff7fff","0x3b","0x480280047ff78000","0x480280067ff78000","0x482680017ff78000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x12","0x4824800180007ffc","0x10000000000000000","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480080007ff57fff","0x482480017ffe8000","0xefffffffffffffdeffffffffffffffff","0x480080017ff37fff","0x400080027ff27ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x15","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x482480017ffc8000","0xffffffffffffffff0000000000000000","0x400080017ff77fff","0x482480017ff78000","0x2","0x482480017ffc8000","0x3ca","0x48127ff47fff8000","0x48127fe37fff8000","0x48127ff87fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff47fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f7265553634202d206e6f6e20753634","0x400080007ffe7fff","0x482480017ff08000","0x3","0x48127ff57fff8000","0x48127fed7fff8000","0x48127fdc7fff8000","0x48127ff17fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x482480017ff78000","0x1","0x208b7fff7fff7ffe","0x480280047ff78000","0x48127ffc7fff8000","0x482480017ffe8000","0x712","0x48127ff97fff8000","0x48127fe87fff8000","0x482680017ff78000","0x8","0x480680017fff8000","0x1","0x480280067ff78000","0x480280077ff78000","0x208b7fff7fff7ffe","0x1104800180018000","0x6be","0x482480017fff8000","0x6bd","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x346c","0x48127ff37fff8000","0x48307ffe7ff38000","0x48127ff37fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x10780017fff7fff","0xf","0x1104800180018000","0x6af","0x482480017fff8000","0x6ae","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x4326","0x48127ff57fff8000","0x48307ffe7ff58000","0x480a7ff67fff8000","0x48127ff57fff8000","0x48127ff57fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480a7ff57fff8000","0x48127ffa7fff8000","0x480a7ff77fff8000","0x480680017fff8000","0x1","0x48127ff87fff8000","0x48127ff87fff8000","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ff98000","0xfffffffffffffffffffffffffffff916","0x400280007ff87fff","0x10780017fff7fff","0x22","0x4825800180007ff9","0x6ea","0x400280007ff87fff","0x482680017ff88000","0x1","0x48127ffe7fff8000","0x48297ffa80007ffb","0x20680017fff7fff","0x4","0x10780017fff7fff","0xf","0x480280007ffa8000","0x400280007ffd7fff","0x48127ffc7fff8000","0x48127ffc7fff8000","0x482680017ffa8000","0x1","0x480a7ffb7fff8000","0x480a7ffc7fff8000","0x482680017ffd8000","0x1","0x1104800180018000","0x800000000000010ffffffffffffffffffffffffffffffffffffffffffffffe5","0x208b7fff7fff7ffe","0x48127ffd7fff8000","0x482480017ffd8000","0x686","0x480680017fff8000","0x0","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff88000","0x1","0x480a7ff97fff8000","0x480680017fff8000","0x1","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x208b7fff7fff7ffe","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffc7fff","0x400380017ffc7ffa","0x480280037ffc8000","0x20680017fff7fff","0x7a","0x480280027ffc8000","0x480280047ffc8000","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x400280007ffb7fff","0x400380017ffb7ffd","0x480280027ffb8000","0x480080027ffd8000","0x400280037ffb7ffe","0x400280047ffb7fff","0x480280057ffb8000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff97ffc","0x480280017ff97ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff97ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff97ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff97ffd","0x400280027ff97ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff37fff8000","0x480680017fff8000","0x0","0x482680017ffb8000","0x6","0x482680017ff98000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280057ffc7fff","0x400280067ffc7ffb","0x400280077ffc7ffc","0x400280087ffc7ffa","0x4802800a7ffc8000","0x20680017fff7fff","0x34","0x480280097ffc8000","0x4802800b7ffc8000","0x482680017ffc8000","0xc","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x48307ffe80007fff","0x20680017fff7fff","0x11","0x40780017fff7fff","0x1","0x480680017fff8000","0x43414c4c45525f49535f4d495353494e475f524f4c45","0x400080007ffe7fff","0x48127ff37fff8000","0x48127ff97fff8000","0x48127ff07fff8000","0x48127ff57fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x48127ff37fff8000","0x482480017ff98000","0xb4","0x48127ff07fff8000","0x48127ff57fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x9","0x480280097ffc8000","0x48127ff37fff8000","0x482480017ffe8000","0x456","0x48127ff07fff8000","0x482680017ffc8000","0xd","0x480680017fff8000","0x1","0x4802800b7ffc8000","0x4802800c7ffc8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x15","0x480280027ffc8000","0x1104800180018000","0x5e1","0x482480017fff8000","0x5e0","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0x3520","0x480a7ff97fff8000","0x48307ffe7ff78000","0x480a7ffb7fff8000","0x482680017ffc8000","0x6","0x480680017fff8000","0x1","0x480280047ffc8000","0x480280057ffc8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x400280007ffa7fff","0x400380017ffa7ffc","0x480280027ffa8000","0x400280037ffa7fff","0x400380047ffa7ffd","0x480280057ffa8000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff87ffc","0x480280017ff87ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff87ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff87ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff87ffd","0x400280027ff87ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ffa8000","0x6","0x482680017ff88000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400380017ffb7ff9","0x400280027ffb7ffc","0x400280037ffb7ffb","0x480280057ffb8000","0x20680017fff7fff","0xd0","0x480280047ffb8000","0x480280067ffb8000","0x482680017ffb8000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x48307ffe80007fff","0x20680017fff7fff","0xa6","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x400080007ff37fff","0x400180017ff37ffc","0x480080027ff38000","0x400080037ff27fff","0x400180047ff27ffd","0x480080057ff28000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007fee7ffc","0x480080017fed7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027feb7ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007fee7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017fec7ffd","0x400080027feb7ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x402580017fe78001","0x6","0x482480017fe88000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400080007feb7fff","0x400080017feb7ffb","0x400080027feb7ffc","0x400080037feb7ffa","0x400080047feb7ffd","0x480080067feb8000","0x20680017fff7fff","0x62","0x480080057fea8000","0x48127fff7fff8000","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400080077fe77fff","0x400080087fe77ffe","0x4800800a7fe78000","0x20680017fff7fff","0x4d","0x480080097fe68000","0x4800800b7fe58000","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff57fff8000","0x48127ffb7fff8000","0x480680017fff8000","0xd","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480080027ff58000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x402580017fd58000","0xc","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffc99","0x20680017fff7ffb","0x26","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0xf","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480080097fe68000","0x48127ff87fff8000","0x482480017ffe8000","0x4fb0","0x480a80017fff8000","0x482480017fe28000","0xd","0x480680017fff8000","0x1","0x4800800b7fe08000","0x4800800c7fdf8000","0x208b7fff7fff7ffe","0x480080057fea8000","0x48127ffc7fff8000","0x482480017ffe8000","0x797c","0x480a80017fff8000","0x482480017fe68000","0x9","0x480680017fff8000","0x1","0x480080077fe48000","0x480080087fe38000","0x208b7fff7fff7ffe","0x1104800180018000","0x4de","0x482480017fff8000","0x4dd","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xaab4","0x48127fee7fff8000","0x48307ffe7ff48000","0x48127feb7fff8000","0x48127ff07fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x480280047ffb8000","0x1104800180018000","0x4c8","0x482480017fff8000","0x4c7","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xae9c","0x48127ff57fff8000","0x48307ffe7ff78000","0x48127ff27fff8000","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x400280007ffa7fff","0x400380017ffa7ffc","0x480280027ffa8000","0x400280037ffa7fff","0x400380047ffa7ffd","0x480280057ffa8000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff87ffc","0x480280017ff87ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff87ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff87ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff87ffd","0x400280027ff87ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ffa8000","0x6","0x482680017ff88000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400380017ffb7ff9","0x400280027ffb7ffc","0x400280037ffb7ffb","0x480280057ffb8000","0x20680017fff7fff","0xd0","0x480280047ffb8000","0x480280067ffb8000","0x482680017ffb8000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x7","0x48127fff7fff8000","0x480680017fff8000","0x1","0x10780017fff7fff","0x6","0x482480017fff8000","0x64","0x480680017fff8000","0x0","0x480680017fff8000","0x1","0x48307ffe80007fff","0x20680017fff7fff","0x17","0x1104800180018000","0x469","0x482480017fff8000","0x468","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xaab4","0x48127fee7fff8000","0x48307ffe7ff48000","0x48127feb7fff8000","0x48127ff07fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x480680017fff8000","0x52c476292b358ba7d29adb58502341b4cc5437d07f67d3e285e085828bc820","0x400080007ff37fff","0x400180017ff37ffc","0x480080027ff38000","0x400080037ff27fff","0x400180047ff27ffd","0x480080057ff28000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007fee7ffc","0x480080017fed7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027feb7ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007fee7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017fec7ffd","0x400080027feb7ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff27fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x402580017fe78001","0x6","0x482480017fe88000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400080007feb7fff","0x400080017feb7ffb","0x400080027feb7ffc","0x400080037feb7ffa","0x400080047feb7ffd","0x480080067feb8000","0x20680017fff7fff","0x62","0x480080057fea8000","0x48127fff7fff8000","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400080077fe77fff","0x400080087fe77ffe","0x4800800a7fe78000","0x20680017fff7fff","0x4d","0x480080097fe68000","0x4800800b7fe58000","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ff57fff8000","0x48127ffb7fff8000","0x480680017fff8000","0xb","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480080027ff58000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x402580017fd58000","0xc","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffb6b","0x20680017fff7ffb","0x26","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0xf","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x480080097fe68000","0x48127ff87fff8000","0x482480017ffe8000","0x4fb0","0x480a80017fff8000","0x482480017fe28000","0xd","0x480680017fff8000","0x1","0x4800800b7fe08000","0x4800800c7fdf8000","0x208b7fff7fff7ffe","0x480080057fea8000","0x48127ffc7fff8000","0x482480017ffe8000","0x797c","0x480a80017fff8000","0x482480017fe68000","0x9","0x480680017fff8000","0x1","0x480080077fe48000","0x480080087fe38000","0x208b7fff7fff7ffe","0x480280047ffb8000","0x1104800180018000","0x3af","0x482480017fff8000","0x3ae","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xae9c","0x48127ff57fff8000","0x48307ffe7ff78000","0x48127ff27fff8000","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe","0x480680017fff8000","0x2e9f66c6eea14532c94ad25405a4fcb32faa4969559c128d837caa0ec50a655","0x480680017fff8000","0x3711c9d994faf6055172091cb841fd4831aa743e6f3315163b06a122c841846","0x400280007ffb7ffe","0x400280017ffb7fff","0x480280027ffb8000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff97ffc","0x480280017ff97ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff97ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff97ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff97ffd","0x400280027ff97ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ffb8000","0x3","0x482680017ff98000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffc7fff","0x400380017ffc7ffa","0x400280027ffc7ffc","0x400280037ffc7ffb","0x480280057ffc8000","0x20680017fff7fff","0x86","0x480280047ffc8000","0x480280067ffc8000","0x482680017ffc8000","0x7","0x48127ffd7fff8000","0x20680017fff7ffd","0x66","0x48127fff7fff8000","0x20780017fff7ffd","0x1b","0x1104800180018000","0x35e","0x482480017fff8000","0x35d","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x8","0x482480017fff8000","0x258be","0x40780017fff7fff","0x1","0x480680017fff8000","0x5a45524f5f50524f564953494f4e414c5f474f565f41444d494e","0x400080007ffe7fff","0x48127fef7fff8000","0x48307ffc7ff58000","0x48127fec7fff8000","0x48127ff17fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x48127ff87fff8000","0x48127ffe7fff8000","0x48127ff57fff8000","0x48127ffa7fff8000","0x480680017fff8000","0x3711c9d994faf6055172091cb841fd4831aa743e6f3315163b06a122c841846","0x480a7ffd7fff8000","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffd72","0x20680017fff7ffd","0x2c","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x3711c9d994faf6055172091cb841fd4831aa743e6f3315163b06a122c841846","0x480680017fff8000","0x3711c9d994faf6055172091cb841fd4831aa743e6f3315163b06a122c841846","0x1104800180018000","0x261","0x20680017fff7ffd","0xd","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x48127ff97fff8000","0x480680017fff8000","0x251e864ca2a080f55bce5da2452e8cfcafdbc951a3e7fff5023d558452ec228","0x480680017fff8000","0x3711c9d994faf6055172091cb841fd4831aa743e6f3315163b06a122c841846","0x1104800180018000","0x255","0x208b7fff7fff7ffe","0x1104800180018000","0x323","0x482480017fff8000","0x322","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x2","0x482480017fff8000","0xb496","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff27fff8000","0x48127ff27fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x310","0x482480017fff8000","0x30f","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x4","0x482480017fff8000","0x16f08","0x48127ff27fff8000","0x48307ffe7ff28000","0x48127ff27fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff27fff8000","0x48127ff27fff8000","0x208b7fff7fff7ffe","0x1104800180018000","0x2fd","0x482480017fff8000","0x2fc","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x8","0x482480017fff8000","0x25986","0x40780017fff7fff","0x1","0x480680017fff8000","0x524f4c45535f414c52454144595f494e495449414c495a4544","0x400080007ffe7fff","0x48127ff07fff8000","0x48307ffc7ff58000","0x48127fed7fff8000","0x48127ff27fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x480280047ffc8000","0x1104800180018000","0x2e3","0x482480017fff8000","0x2e2","0x480080007fff8000","0x480080007fff8000","0x484480017fff8000","0x8","0x482480017fff8000","0x25c42","0x48127ff57fff8000","0x48307ffe7ff78000","0x48127ff27fff8000","0x482680017ffc8000","0x8","0x480680017fff8000","0x1","0x480280067ffc8000","0x480280077ffc8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x40780017fff7fff","0x1","0x480680017fff8000","0x1bfc207425a47a5dfa1a50a4f5241203f50624ca5fdf5e18755765416b8e288","0x400080007ffe7fff","0x480680017fff8000","0x544f4b454e5f4c4f434b5f414e445f44454c45474154494f4e","0x400080017ffd7fff","0x480680017fff8000","0x312e302e30","0x400080027ffc7fff","0x48127ffc7fff8000","0x482480017ffb8000","0x3","0x480680017fff8000","0x476574457865637574696f6e496e666f","0x400280007ffd7fff","0x400380017ffd7ffb","0x480280037ffd8000","0x20680017fff7fff","0x5c","0x480280027ffd8000","0x480280047ffd8000","0x480080017fff8000","0x480080067fff8000","0x400080007ff97fff","0x48127ff87fff8000","0x482480017ff88000","0x1","0x40327ffe80007fff","0x480680017fff8000","0x0","0x402780017ffd8001","0x5","0x48127ff97fff8000","0x4828800080017ffe","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400280007ffa7fff","0x10780017fff7fff","0x1b","0x400280007ffa7fff","0x1104800180018000","0x2a1","0x482480017fff8000","0x2a0","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0xb9a","0x40780017fff7fff","0x1","0x480680017fff8000","0x5265717569726573206174206c65617374206f6e6520656c656d656e74","0x400080007ffe7fff","0x482680017ffa8000","0x1","0x48307ffc7ff48000","0x480a7ffc7fff8000","0x480a80017fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x482680017ffa8000","0x1","0x48127ffb7fff8000","0x480a7ffc7fff8000","0x48127ff67fff8000","0x48127ff67fff8000","0x480680017fff8000","0x0","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffff09e","0x20680017fff7ffc","0x10","0x400080007ffb7fff","0x400180017ffb8000","0x48127ff97fff8000","0x482480017ff98000","0x190","0x482480017ff98000","0x3","0x480a80017fff8000","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480080027ff58000","0x208b7fff7fff7ffe","0x1104800180018000","0x26f","0x482480017fff8000","0x26e","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x0","0x48127ff37fff8000","0x48307ffe7ff38000","0x48127ff37fff8000","0x480a80017fff8000","0x480680017fff8000","0x1","0x48127ff37fff8000","0x48127ff37fff8000","0x208b7fff7fff7ffe","0x480280027ffd8000","0x1104800180018000","0x25d","0x482480017fff8000","0x25c","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x120c","0x480a7ffa7fff8000","0x48307ffe7ff88000","0x480a7ffc7fff8000","0x482680017ffd8000","0x6","0x480680017fff8000","0x1","0x480280047ffd8000","0x480280057ffd8000","0x208b7fff7fff7ffe","0xa0680017fff8005","0xe","0x4825800180057ffd","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ffa7ffc","0x480280017ffa7ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ffa7ffc","0x10780017fff7fff","0x11","0x480a7ffd7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ffa7ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ffa7ffd","0x400280027ffa7ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ffa8000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffc7fff","0x400380017ffc7ffb","0x400280027ffc7ffd","0x400280037ffc7ffc","0x480280057ffc8000","0x20680017fff7fff","0x87","0x480280047ffc8000","0x480280067ffc8000","0x482680017ffc8000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x57","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x48127ffe7fff8000","0x480680017fff8000","0x0","0x482480017ff48000","0x1","0x482480017ff58000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400080007ff87fff","0x400080017ff87ffb","0x400080027ff87ffc","0x400080037ff87ffd","0x480080057ff88000","0x20680017fff7fff","0x38","0x480080047ff78000","0x480080067ff68000","0x482480017ff58000","0x7","0x48127ffd7fff8000","0xa0680017fff8000","0x16","0x480080007ff88003","0x480080017ff78003","0x4844800180017ffe","0x100000000000000000000000000000000","0x483080017ffd7ff9","0x482480017fff7ffd","0x800000000000010fffffffffffffffff7ffffffffffffef0000000000000001","0x20680017fff7ffc","0x6","0x402480017fff7ffd","0xffffffffffffffffffffffffffffffff","0x10780017fff7fff","0x4","0x402480017ffe7ffd","0xf7ffffffffffffef0000000000000000","0x400080027ff37ffd","0x20680017fff7ffe","0x11","0x402780017fff7fff","0x1","0x400080007ff87ffc","0x40780017fff7fff","0xc","0x482480017fec8000","0x1","0x482480017ff18000","0x6b8","0x48127fef7fff8000","0x480680017fff8000","0x0","0x48127fe17fff8000","0x48127feb7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017ff18000","0x3","0x48127ff67fff8000","0x48127ff47fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x10780017fff7fff","0x1d","0x40780017fff7fff","0xb","0x480080047fec8000","0x48127ff17fff8000","0x482480017ffe8000","0x6a4","0x482480017fe98000","0x8","0x480080067fe88000","0x480080077fe78000","0x10780017fff7fff","0x23","0x40780017fff7fff","0xb","0x40780017fff7fff","0x1","0x480680017fff8000","0x53746f726555313238202d206e6f6e2075313238","0x400080007ffe7fff","0x482480017fe68000","0x3","0x482480017feb8000","0x2d8c","0x48127fe97fff8000","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x16","0x480280047ffc8000","0x48127fe67fff8000","0x482480017ffe8000","0x3494","0x482680017ffc8000","0x8","0x480280067ffc8000","0x480280077ffc8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xa","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480a7ffc7fff8000","0x10780017fff7fff","0x8","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x98","0x480080007fff8000","0xa0680017fff8000","0x12","0x4824800180007ffe","0x100000000","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480280007ffb7fff","0x482480017ffe8000","0xefffffffffffffde00000000ffffffff","0x480280017ffb7fff","0x400280027ffb7ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x78","0x402780017fff7fff","0x1","0x400280007ffb7ffe","0x482480017ffe8000","0xffffffffffffffffffffffff00000000","0x400280017ffb7fff","0x480680017fff8000","0x0","0x48307ff880007ff9","0x48307ffb7ffe8000","0xa0680017fff8000","0x8","0x482480017ffd8000","0x1","0x48307fff80007ffd","0x400280027ffb7fff","0x10780017fff7fff","0x51","0x48307ffe80007ffd","0x400280027ffb7fff","0x48307ff480007ff5","0x48307ffa7ff38000","0x48307ffb7ff28000","0x48307ff580017ffd","0xa0680017fff7fff","0x7","0x482480017fff8000","0x100000000000000000000000000000000","0x400280037ffb7fff","0x10780017fff7fff","0x2f","0x400280037ffb7fff","0x48307fef80007ff0","0x48307ffe7ff28000","0xa0680017fff8000","0x8","0x482480017ffd8000","0x1","0x48307fff80007ffd","0x400280047ffb7fff","0x10780017fff7fff","0x11","0x48307ffe80007ffd","0x400280047ffb7fff","0x40780017fff7fff","0x3","0x482680017ffb8000","0x5","0x480680017fff8000","0x0","0x48307fea7fe68000","0x48307ff77fe58000","0x480680017fff8000","0x0","0x48127ff07fff8000","0x48127ff07fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e646578206f7574206f6620626f756e6473","0x400080007ffe7fff","0x482680017ffb8000","0x5","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x4","0x40780017fff7fff","0x1","0x480680017fff8000","0x7533325f737562204f766572666c6f77","0x400080007ffe7fff","0x482680017ffb8000","0x4","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x9","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e646578206f7574206f6620626f756e6473","0x400080007ffe7fff","0x482680017ffb8000","0x3","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x48127ff97fff8000","0x482480017ff88000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0xc","0x482680017ffb8000","0x3","0x480680017fff8000","0x0","0x48127fe67fff8000","0x48127fe67fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x14","0x480a7ffb7fff8000","0x480680017fff8000","0x0","0x48127fe67fff8000","0x48127fe67fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x40780017fff7fff","0x2","0x480680017fff8000","0x2e9f66c6eea14532c94ad25405a4fcb32faa4969559c128d837caa0ec50a655","0x400280007ffa7fff","0x400380017ffa7ffc","0x480280027ffa8000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480280007ff87ffc","0x480280017ff87ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400280027ff87ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480280007ff87ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480280017ff87ffd","0x400280027ff87ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x480680017fff8000","0x0","0x482680017ffa8000","0x3","0x482680017ff88000","0x3","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400380017ffb7ff9","0x400280027ffb7ffc","0x400280037ffb7ffb","0x480280057ffb8000","0x20680017fff7fff","0x8d","0x480280047ffb8000","0x480680017fff8000","0x2e9f66c6eea14532c94ad25405a4fcb32faa4969559c128d837caa0ec50a655","0x400080007ffa7fff","0x400180017ffa7ffc","0x480080027ffa8000","0xa0680017fff8005","0xe","0x4824800180057ffe","0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00","0x484480017ffe8000","0x110000000000000000","0x48307ffe7fff8003","0x480080007ff67ffc","0x480080017ff57ffc","0x482480017ffb7ffd","0xffffffffffffffeefffffffffffffeff","0x400080027ff37ffc","0x10780017fff7fff","0x11","0x48127ffe7fff8005","0x484480017ffe8000","0x8000000000000000000000000000000","0x48307ffe7fff8003","0x480080007ff67ffd","0x482480017ffc7ffe","0xf0000000000000000000000000000100","0x480080017ff47ffd","0x400080027ff37ff9","0x402480017ffd7ff9","0xffffffffffffffffffffffffffffffff","0x20680017fff7ffd","0x4","0x402780017fff7fff","0x1","0x48127ff67fff8000","0x480680017fff8000","0x0","0x480280067ffb8000","0x402580017fef8001","0x3","0x482480017ff08000","0x3","0x480680017fff8000","0x53746f726167655772697465","0x400280077ffb7fff","0x400280087ffb7ffb","0x400280097ffb7ffc","0x4002800a7ffb7ffa","0x4003800b7ffb7ffd","0x4802800d7ffb8000","0x20680017fff7fff","0x4c","0x4802800c7ffb8000","0x40780017fff7fff","0x1","0x40780017fff7fff","0x1","0x48127ffa7fff8000","0x48127ffc7fff8000","0x480680017fff8000","0x9","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480a7ffc7fff8000","0x48127ff27fff8000","0x480a7ffd7fff8000","0x48127ff57fff8000","0x48127ff47fff8000","0x48127ff47fff8000","0x48127ff37fff8000","0x402780017ffb8000","0xe","0x1104800180018000","0x800000000000010fffffffffffffffffffffffffffffffffffffffffffff7c2","0x20680017fff7ffb","0x26","0x48127ffa7fff8000","0x480680017fff8000","0x456d69744576656e74","0x4002800080007fff","0x4002800180007ffe","0x4002800280007ffa","0x4002800380007ffb","0x4002800480007ffc","0x4002800580007ffd","0x4802800780008000","0x20680017fff7fff","0xf","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0x8","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x480680017fff8000","0x0","0x208b7fff7fff7ffe","0x4802800680008000","0x48127ff57fff8000","0x48127ffe7fff8000","0x480a80017fff8000","0x4826800180008000","0xa","0x480680017fff8000","0x1","0x4802800880008000","0x4802800980008000","0x208b7fff7fff7ffe","0x48127ff97fff8000","0x482480017ff98000","0x2b5c","0x480a80017fff8000","0x480a80007fff8000","0x480680017fff8000","0x1","0x48127ff97fff8000","0x48127ff97fff8000","0x208b7fff7fff7ffe","0x4802800c7ffb8000","0x48127ffc7fff8000","0x482480017ffe8000","0x4f4c","0x480a80017fff8000","0x482680017ffb8000","0x10","0x480680017fff8000","0x1","0x4802800e7ffb8000","0x4802800f7ffb8000","0x208b7fff7fff7ffe","0x480280047ffb8000","0x1104800180018000","0x12","0x482480017fff8000","0x11","0x480080007fff8000","0x480080007fff8000","0x482480017fff8000","0x7fbc","0x48127ff67fff8000","0x48307ffe7ff88000","0x48127ff37fff8000","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe"],"bytecode_segment_lengths":[193,170,371,588,332,332,332,332,170,168,168,168,168,296,191,272,272,245,245,245,245,146,125,125,170,115,196,272,375,483,375,346,346,115,196,483,346,346,685,180,191,98,521,167,167,193,194,379,227,764,339,311,137,193,201,204,985,390,227,396,396,324,152,532,90,91,85,731,731,224,66,158,151,151,310,194,53,150,281,281,204,132,193,185,209],"hints":[[0,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[38,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[42,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[52,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[68,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[95,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[115,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[136,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[161,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[176,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[193,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[212,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[233,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x32d2"},"rhs":{"Deref":{"register":"AP","offset":-2}},"dst":{"register":"AP","offset":0}}}]],[258,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[266,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[270,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[280,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[288,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[302,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[332,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[347,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[363,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[401,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[405,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[415,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[451,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[453,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[502,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[504,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[533,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[560,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[577,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[595,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[642,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[678,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[702,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[717,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[736,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x1cf2"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[774,[{"TestLessThan":{"lhs":{"Deref":{"register":"FP","offset":2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[778,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[788,[{"LinearSplit":{"value":{"Deref":{"register":"FP","offset":2}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[824,[{"TestLessThan":{"lhs":{"Deref":{"register":"FP","offset":3}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[828,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[838,[{"LinearSplit":{"value":{"Deref":{"register":"FP","offset":3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[874,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[876,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[925,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[927,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[988,[{"TestLessThan":{"lhs":{"BinOp":{"op":"Add","a":{"register":"FP","offset":4},"b":{"Immediate":"0x0"}}},"rhs":{"Immediate":"0x10000000000000000"},"dst":{"register":"AP","offset":0}}}]],[992,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-1}},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":0},"y":{"register":"AP","offset":1}}}]],[1036,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1058,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1085,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[1112,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1133,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1167,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1191,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1206,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1242,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1266,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1290,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1305,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1322,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[1360,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[1364,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[1374,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[1410,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[1412,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[1461,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[1463,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[1492,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1519,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[1541,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1562,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1598,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1622,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1637,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1654,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[1692,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[1696,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[1706,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[1742,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[1744,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[1793,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[1795,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[1824,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1851,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[1873,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1894,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1930,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1954,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1969,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[1986,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[2024,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[2028,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[2038,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[2074,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[2076,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[2125,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[2127,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[2156,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2183,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[2205,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2226,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2262,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2286,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2301,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2318,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[2356,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[2360,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[2370,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[2406,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[2408,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[2457,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[2459,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[2488,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2515,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[2537,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2558,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2594,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2618,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2633,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2650,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[2669,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2690,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x3336"},"rhs":{"Deref":{"register":"AP","offset":-2}},"dst":{"register":"AP","offset":0}}}]],[2715,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[2723,[{"TestLessThan":{"lhs":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-3},"b":{"Immediate":"0x0"}}},"rhs":{"Immediate":"0x10000000000000000"},"dst":{"register":"AP","offset":0}}}]],[2727,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-1}},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":0},"y":{"register":"AP","offset":1}}}]],[2745,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2759,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2789,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2804,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2820,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x23fa"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[2848,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2874,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-4}},"dst":{"register":"AP","offset":0}}}]],[2900,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2925,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2942,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2970,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[2988,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x245e"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[3016,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3044,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[3070,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3093,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3110,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3138,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3156,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x245e"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[3184,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3212,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[3238,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3261,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3278,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3306,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3324,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x245e"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[3352,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3380,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[3406,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3429,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3446,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3474,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3492,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[3540,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[3544,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[3554,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[3570,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3597,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[3615,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[3619,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[3630,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[3657,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[3676,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3715,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3740,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3755,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3771,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3788,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[3817,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3842,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-4}},"dst":{"register":"AP","offset":0}}}]],[3857,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[3861,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[3872,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[3899,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[3903,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3930,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3946,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3962,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[3979,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[4017,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[4021,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[4031,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[4047,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4074,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[4094,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[4098,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[4109,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[4136,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[4155,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4194,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4219,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4234,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4251,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[4289,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[4293,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[4303,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[4319,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4346,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[4366,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[4370,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[4381,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[4408,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[4427,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4466,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4491,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4506,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4523,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[4561,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[4565,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[4575,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[4591,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4618,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[4635,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[4664,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4711,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4736,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4751,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4768,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[4806,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[4810,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[4820,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[4836,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4863,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[4880,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[4909,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4956,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4981,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[4996,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5013,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[5051,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[5055,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[5065,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[5081,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5108,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[5125,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[5154,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5201,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5226,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5241,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5258,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[5296,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[5300,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[5310,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[5326,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5353,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[5370,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[5399,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5446,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5471,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5486,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5503,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[5532,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5559,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[5579,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5600,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5616,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5632,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5649,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[5668,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5689,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x2af8"},"rhs":{"Deref":{"register":"AP","offset":-2}},"dst":{"register":"AP","offset":0}}}]],[5714,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[5718,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5743,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5758,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5774,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[5793,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5814,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x2af8"},"rhs":{"Deref":{"register":"AP","offset":-2}},"dst":{"register":"AP","offset":0}}}]],[5839,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[5843,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5868,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5883,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5899,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[5918,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[5939,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x3336"},"rhs":{"Deref":{"register":"AP","offset":-2}},"dst":{"register":"AP","offset":0}}}]],[5964,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[5972,[{"TestLessThan":{"lhs":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-3},"b":{"Immediate":"0x0"}}},"rhs":{"Immediate":"0x100"},"dst":{"register":"AP","offset":0}}}]],[5976,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-1}},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":0},"y":{"register":"AP","offset":1}}}]],[5994,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6008,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6038,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6053,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6069,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[6088,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6109,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x6bc6"},"rhs":{"Deref":{"register":"AP","offset":-2}},"dst":{"register":"AP","offset":0}}}]],[6131,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6153,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6168,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6184,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[6222,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[6226,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[6236,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[6252,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6277,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-4}},"dst":{"register":"AP","offset":0}}}]],[6299,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6323,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6348,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6363,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6380,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[6418,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[6422,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[6432,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[6467,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[6471,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[6481,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[6497,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6524,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[6547,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6571,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6596,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6620,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6635,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6652,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[6690,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[6694,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[6704,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[6740,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[6742,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[6791,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[6793,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[6822,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6849,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[6866,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[6884,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6935,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6971,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[6995,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[7010,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[7029,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x398"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[7067,[{"TestLessThan":{"lhs":{"Deref":{"register":"FP","offset":3}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[7071,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[7081,[{"LinearSplit":{"value":{"Deref":{"register":"FP","offset":3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[7117,[{"TestLessThan":{"lhs":{"Deref":{"register":"FP","offset":2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[7121,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[7131,[{"LinearSplit":{"value":{"Deref":{"register":"FP","offset":2}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[7167,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[7169,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[7218,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[7220,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[7249,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[7276,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[7293,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[7325,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[7394,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[7430,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[7454,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[7478,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[7493,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[7510,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[7548,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[7552,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[7562,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[7598,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[7600,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[7649,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[7651,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[7680,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[7707,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[7724,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[7742,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[7793,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[7829,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[7853,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[7868,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[7885,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[7923,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[7927,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[7937,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[7973,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[7975,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[8024,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[8026,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[8055,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8082,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[8104,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8139,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8175,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8199,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8214,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8231,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[8269,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[8273,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[8283,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[8319,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[8321,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[8370,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[8372,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[8401,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8428,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[8450,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8485,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8521,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8545,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8560,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8577,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[8596,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8617,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x6bc6"},"rhs":{"Deref":{"register":"AP","offset":-2}},"dst":{"register":"AP","offset":0}}}]],[8639,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8661,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8676,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8692,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[8730,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[8734,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[8744,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[8760,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8785,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-4}},"dst":{"register":"AP","offset":0}}}]],[8807,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8831,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8856,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8871,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[8890,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x398"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[8928,[{"TestLessThan":{"lhs":{"Deref":{"register":"FP","offset":3}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[8932,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[8942,[{"LinearSplit":{"value":{"Deref":{"register":"FP","offset":3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[8978,[{"TestLessThan":{"lhs":{"Deref":{"register":"FP","offset":2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[8982,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[8992,[{"LinearSplit":{"value":{"Deref":{"register":"FP","offset":2}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[9028,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[9030,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[9079,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[9081,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[9110,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[9137,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[9154,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[9186,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[9255,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[9291,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[9315,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[9339,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[9354,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[9371,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[9409,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[9413,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[9423,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[9459,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[9461,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[9510,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[9512,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[9541,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[9568,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[9590,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[9625,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[9661,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[9685,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[9700,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[9717,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[9755,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[9759,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[9769,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[9805,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[9807,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[9856,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[9858,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[9887,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[9914,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[9936,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[9971,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10007,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10031,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10046,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10063,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x1e6e"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[10121,[{"TestLessThan":{"lhs":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-2},"b":{"Immediate":"0x0"}}},"rhs":{"Immediate":"0x100"},"dst":{"register":"AP","offset":0}}}]],[10125,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-1}},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":0},"y":{"register":"AP","offset":1}}}]],[10171,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[10173,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[10222,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[10224,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[10274,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[10278,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[10288,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[10323,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[10327,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[10337,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[10372,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-2}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[10376,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[10386,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-3}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[10422,[{"TestLessThan":{"lhs":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-2},"b":{"Immediate":"0x0"}}},"rhs":{"Immediate":"0x10000000000000000"},"dst":{"register":"AP","offset":0}}}]],[10426,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-1}},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":0},"y":{"register":"AP","offset":1}}}]],[10452,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10479,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[10507,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10528,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10553,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10577,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10601,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10625,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10660,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10684,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10699,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10715,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10731,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10767,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-8}}}}]],[10775,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[10779,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[10789,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[10807,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10835,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-14}}}}]],[10864,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10882,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[10940,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-7}}}}]],[10948,[{"TestLessThan":{"lhs":{"Deref":{"register":"FP","offset":0}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[10952,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[10962,[{"LinearSplit":{"value":{"Deref":{"register":"FP","offset":0}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[10985,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[11012,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[11032,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-10}}}}]],[11079,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[11119,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0xa6e"},"rhs":{"Deref":{"register":"FP","offset":-8}},"dst":{"register":"AP","offset":0}}}]],[11197,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[11223,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-11}}}}]],[11234,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x10000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[11255,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-9}}}}]],[11274,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[11290,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000"},"dst":{"register":"AP","offset":-1}}}]],[11308,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[11342,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[11346,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[11357,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[11384,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":2}}}}]],[11433,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[11453,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[11457,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[11468,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[11498,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-24}}}}]],[11552,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[11701,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[11742,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-6}}}}]],[11763,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"FP","offset":-6},"b":{"Immediate":"0x5"}}}}}]],[11771,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[11775,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[11785,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[11819,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[11844,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[11909,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-6}}}}]],[11930,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"FP","offset":-6},"b":{"Immediate":"0x5"}}}}}]],[11938,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[11942,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[11952,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[11986,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[12011,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[12093,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[12097,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[12107,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-2}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[12265,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[12306,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[12310,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[12321,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[12348,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-9}}}}]],[12356,[{"TestLessThan":{"lhs":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-3},"b":{"Immediate":"0x0"}}},"rhs":{"Immediate":"0x10000000000000000"},"dst":{"register":"AP","offset":0}}}]],[12360,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-1}},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":0},"y":{"register":"AP","offset":1}}}]],[12391,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[12474,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-6}}}}]],[12494,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-17},"b":{"Immediate":"0x5"}}}}}]],[12502,[{"TestLessThan":{"lhs":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-3},"b":{"Immediate":"0x0"}}},"rhs":{"Immediate":"0x10000000000000000"},"dst":{"register":"AP","offset":0}}}]],[12506,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-1}},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":0},"y":{"register":"AP","offset":1}}}]],[12525,[{"TestLessThan":{"lhs":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-11},"b":{"Deref":{"register":"AP","offset":-6}}}},"rhs":{"Immediate":"0x10000000000000000"},"dst":{"register":"AP","offset":0}}}]],[12540,[{"TestLessThan":{"lhs":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-3},"b":{"Deref":{"register":"AP","offset":-2}}}},"rhs":{"Immediate":"0x10000000000000000"},"dst":{"register":"AP","offset":0}}}]],[12589,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[12591,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[12620,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":0}}}}]],[12699,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[12726,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[12753,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[12925,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[12927,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[12956,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":0}}}}]],[13086,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-8}}}}]],[13115,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-8}}}}]],[13172,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[13190,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x10000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[13200,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x10000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[13208,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[13210,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[13240,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":4}}}}]],[13269,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-7}}}}]],[13273,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[13275,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[13311,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":3}}}}]],[13322,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[13348,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":0}}}}]],[13387,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[13432,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-2}}}}]],[13519,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[13655,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[13682,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[13771,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[13839,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[13843,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[13854,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[13880,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-12}}}}]],[13917,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[13937,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[13941,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[13952,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[13979,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-21}}}}]],[14005,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[14007,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[14035,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":0}}}}]],[14178,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[14182,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[14193,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[14219,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-12}}}}]],[14269,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[14273,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[14284,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[14311,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-20}}}}]],[14337,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[14339,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[14367,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":0}}}}]],[14493,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[14513,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-4}}}}]],[14528,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"FP","offset":-4},"b":{"Immediate":"0x5"}}}}}]],[14559,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[14616,[{"TestLessThan":{"lhs":{"Deref":{"register":"FP","offset":-3}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[14620,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[14631,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[14655,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-4}}}}]],[14663,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[14665,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[14699,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-8}}}}]],[14707,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[14709,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[14742,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[14770,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[14812,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[14816,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[14827,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[14853,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[14861,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[14863,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[14897,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-8}}}}]],[14905,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[14907,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[14941,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[14969,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[15016,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[15020,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[15031,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[15057,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-6}}}}]],[15065,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[15067,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[15101,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-8}}}}]],[15109,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[15111,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[15145,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[15173,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[15228,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[15256,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[15276,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[15280,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[15291,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[15318,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-7}}}}]],[15326,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[15328,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[15362,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-8}}}}]],[15370,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[15372,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[15395,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[15421,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[15443,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[15463,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[15467,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[15478,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[15506,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-36}}}}]],[15522,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-42},"b":{"Immediate":"0x7"}}}}}]],[15531,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[15535,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[15546,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[15573,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-58},"b":{"Immediate":"0xe"}}}}}]],[15581,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[15583,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[15617,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-8}}}}]],[15625,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[15627,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[15650,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[15676,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[15698,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[15718,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[15722,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[15733,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[15761,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-35}}}}]],[15777,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-41},"b":{"Immediate":"0x7"}}}}}]],[15781,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[15783,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[15816,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":0}}}}]],[15905,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[15928,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[15971,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[16085,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[16110,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[16157,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[16207,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[16211,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[16222,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[16248,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-7}}}}]],[16256,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[16258,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[16292,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-8}}}}]],[16300,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[16302,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[16351,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[16377,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[16399,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[16454,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[16500,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[16547,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[16603,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[16631,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[16654,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[16658,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[16669,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[16697,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-7}}}}]],[16713,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"FP","offset":-7},"b":{"Immediate":"0x7"}}}}}]],[16717,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[16719,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[16752,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":0}}}}]],[16820,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-6}}}}]],[16834,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[16838,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[16849,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[16876,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"FP","offset":-6},"b":{"Immediate":"0x5"}}}}}]],[16884,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[16886,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[16920,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-8}}}}]],[16928,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[16930,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[16953,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[16979,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[17001,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[17077,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[17102,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[17149,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[17216,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-6}}}}]],[17230,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[17234,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[17245,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[17272,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"FP","offset":-6},"b":{"Immediate":"0x5"}}}}}]],[17280,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[17282,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[17316,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-8}}}}]],[17324,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[17326,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[17349,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[17375,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[17397,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[17473,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[17498,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[17545,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[17619,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-12}}}}]],[17635,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"FP","offset":-12},"b":{"Immediate":"0x7"}}}}}]],[17651,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"FP","offset":-12},"b":{"Immediate":"0xe"}}}}}]],[17680,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[17707,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-9}}}}]],[17733,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-8}}}}]],[17758,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-8}}}}]],[17936,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-3}}}}]],[17952,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[17956,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[17967,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[17994,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"FP","offset":-3},"b":{"Immediate":"0x5"}}}}}]],[18018,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[18092,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[18096,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[18107,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[18133,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-7}}}}]],[18141,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[18143,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[18177,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-8}}}}]],[18185,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[18187,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[18212,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[18240,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[18262,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[18279,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[18304,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[18316,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[18342,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[18364,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[18419,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[18502,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[18527,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[18574,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[18618,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[18634,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000"},"dst":{"register":"AP","offset":-1}}}]],[18650,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[18714,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"FP","offset":-7}},"dst":{"register":"AP","offset":0}}}]],[18780,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[18797,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[18824,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-7}}}}]],[18848,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[18896,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[18922,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[18948,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[18970,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[18997,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-27}}}}]],[19015,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-34},"b":{"Immediate":"0x7"}}}}}]],[19024,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[19028,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[19039,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[19066,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-50},"b":{"Immediate":"0xe"}}}}}]],[19074,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[19076,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[19110,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-8}}}}]],[19118,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[19120,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[19143,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[19169,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[19191,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[19211,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[19215,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[19226,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[19254,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-35}}}}]],[19270,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-41},"b":{"Immediate":"0x7"}}}}}]],[19274,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[19276,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[19310,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":0}}}}]],[19399,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[19422,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[19465,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[19579,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[19627,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[19653,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[19679,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[19701,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[19728,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-27}}}}]],[19746,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-34},"b":{"Immediate":"0x7"}}}}}]],[19755,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[19759,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[19770,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[19797,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-50},"b":{"Immediate":"0xe"}}}}}]],[19805,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[19807,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[19841,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-8}}}}]],[19849,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[19851,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[19874,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[19900,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[19922,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":-1}}}]],[19942,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[19946,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[19957,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[19985,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-35}}}}]],[20001,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-41},"b":{"Immediate":"0x7"}}}}}]],[20005,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[20007,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[20041,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":0}}}}]],[20130,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[20153,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[20196,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[20310,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[20388,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x800000000000000000000000000000000000000000000000000000000000000"},"dst":{"register":"AP","offset":4}}}]],[20392,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":3}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[20402,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-2}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-1},"y":{"register":"AP","offset":0}}}]],[20642,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"FP","offset":-10}},"dst":{"register":"AP","offset":0}}}]],[20700,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x816"},"rhs":{"Deref":{"register":"AP","offset":-5}},"dst":{"register":"AP","offset":0}}}]],[20725,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[20775,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[20792,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[20833,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[20837,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[20848,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[20876,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-10}}}}]],[20943,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[20984,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[20988,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[20999,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[21027,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-10}}}}]],[21404,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[21445,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[21449,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[21460,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[21487,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-9}}}}]],[21495,[{"TestLessThan":{"lhs":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-3},"b":{"Immediate":"0x0"}}},"rhs":{"Immediate":"0x10000000000000000"},"dst":{"register":"AP","offset":0}}}]],[21499,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-1}},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":0},"y":{"register":"AP","offset":1}}}]],[21530,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[21598,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x6ea"},"rhs":{"Deref":{"register":"FP","offset":-7}},"dst":{"register":"AP","offset":0}}}]],[21637,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[21655,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-4}}}}]],[21669,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[21673,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[21684,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[21711,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"FP","offset":-4},"b":{"Immediate":"0x5"}}}}}]],[21735,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[21811,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[21815,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[21826,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[21852,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[21884,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[21888,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[21899,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[21929,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-21}}}}]],[21938,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-25},"b":{"Immediate":"0x7"}}}}}]],[21943,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[21945,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[21979,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":0}}}}]],[22092,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[22096,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[22107,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[22133,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[22186,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[22190,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[22201,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[22231,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-21}}}}]],[22240,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-25},"b":{"Immediate":"0x7"}}}}}]],[22245,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[22247,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[22281,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":0}}}}]],[22370,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[22374,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[22385,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[22411,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-4}}}}]],[22434,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[22531,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[22569,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[22587,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-3}}}}]],[22605,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000"},"dst":{"register":"AP","offset":-1}}}]],[22621,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[22699,[{"TestLessThan":{"lhs":{"Deref":{"register":"FP","offset":-3}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[22703,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[22714,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[22738,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-4}}}}]],[22746,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[22748,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[22782,[{"SystemCall":{"system":{"Deref":{"register":"AP","offset":-8}}}}]],[22790,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-3}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[22792,[{"DivMod":{"lhs":{"Deref":{"register":"AP","offset":-4}},"rhs":{"Immediate":"0x100000000000000000000000000000000"},"quotient":{"register":"AP","offset":3},"remainder":{"register":"AP","offset":4}}}]],[22825,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[22853,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[22914,[{"TestLessThan":{"lhs":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-1},"b":{"Immediate":"0x0"}}},"rhs":{"Immediate":"0x100000000"},"dst":{"register":"AP","offset":0}}}]],[22918,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-1}},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":0},"y":{"register":"AP","offset":1}}}]],[22940,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-2}},"dst":{"register":"AP","offset":0}}}]],[22954,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":0}},"rhs":{"Immediate":"0x100000000"},"dst":{"register":"AP","offset":-1}}}]],[22964,[{"TestLessThanOrEqual":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Deref":{"register":"AP","offset":-2}},"dst":{"register":"AP","offset":0}}}]],[22987,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[23008,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[23029,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[23084,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[23088,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[23099,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[23125,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[23134,[{"TestLessThan":{"lhs":{"Deref":{"register":"AP","offset":-1}},"rhs":{"Immediate":"0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"},"dst":{"register":"AP","offset":5}}}]],[23138,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x110000000000000000"},"max_x":{"Immediate":"0xffffffffffffffffffffffffffffffff"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[23149,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":4}},"scalar":{"Immediate":"0x8000000000000000000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":-2},"y":{"register":"AP","offset":-1}}}]],[23178,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"FP","offset":-5},"b":{"Immediate":"0x7"}}}}}]],[23182,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[23184,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[23218,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":0}}}}]]],"entry_points_by_type":{"EXTERNAL":[{"selector":"0xb2ef42a25c95687d1a3e7c2584885fd4058d102e05c44f65cf35988451bc8","offset":5258,"builtins":["pedersen","range_check"]},{"selector":"0xc30ffbeb949d3447fd4acd61251803e8ab9c8a777318abb5bd5fbf28015eb","offset":3324,"builtins":["pedersen","range_check","poseidon"]},{"selector":"0x151e58b29179122a728eab07c8847e5baf5802379c5db3a7d57a8263a7bd1d","offset":1986,"builtins":["pedersen","range_check"]},{"selector":"0x41b033f4a31df8067c24d1e9b550a2ce75fd4a29e1147af9752174f0e6cb20","offset":8888,"builtins":["pedersen","range_check"]},{"selector":"0x4c4fb1ab068f6039d5780c68dd0fa2f8742cceb3426d19667778ca7f3518a9","offset":5899,"builtins":["range_check"]},{"selector":"0x80aa9fdbfaf9615e4afc7f5f722e265daca5ccc655360fa5ccacf9c267936d","offset":8577,"builtins":["range_check"]},{"selector":"0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e","offset":6652,"builtins":["pedersen","range_check"]},{"selector":"0x95604234097c6fe6314943092b1aa8fb6ee781cf32ac6d5b78d856f52a540f","offset":5503,"builtins":["pedersen","range_check"]},{"selector":"0xc357e0791efd20abc629024da92179b10fce7f1b3d10edc62ef40a57984697","offset":363,"builtins":["pedersen","range_check"]},{"selector":"0xd4612fb377c4d51f0397aeb18757d3d580a7f22f58d516141cfcce5333b010","offset":193,"builtins":["range_check"]},{"selector":"0xd63a78e4cd7fb4c41bc18d089154af78d400a5e837f270baea6cf8db18c8dd","offset":2318,"builtins":["pedersen","range_check"]},{"selector":"0x1557182e4359a1f0c6301278e8f5b35a776ab58d39892581e357578fb287836","offset":6069,"builtins":["range_check"]},{"selector":"0x16cc063b8338363cf388ce7fe1df408bf10f16cd51635d392e21d852fafb683","offset":9371,"builtins":["pedersen","range_check"]},{"selector":"0x183420eb7aafd9caad318b543d9252c94857340f4768ac83cf4b6472f0bf515","offset":4523,"builtins":["pedersen","range_check"]},{"selector":"0x1aaf3e6107dd1349c81543ff4221a326814f77dadcc5810807b74f1a49ded4e","offset":9717,"builtins":["pedersen","range_check"]},{"selector":"0x1c67057e2995950900dbf33db0f5fc9904f5a18aae4a3768f721c43efe5d288","offset":1322,"builtins":["pedersen","range_check"]},{"selector":"0x1d13ab0a76d7407b1d5faccd4b3d8a9efe42f3d3c21766431d4fafb30f45bd4","offset":7885,"builtins":["pedersen","range_check"]},{"selector":"0x1e888a1026b19c8c0b57c72d63ed1737106aa10034105b980ba117bd0c29fe1","offset":6380,"builtins":["pedersen","range_check"]},{"selector":"0x1fa400a40ac35b4aa2c5383c3bb89afee2a9698b86ebb405cf25a6e63428605","offset":2820,"builtins":["pedersen","range_check","poseidon"]},{"selector":"0x216b05c387bab9ac31918a3e61672f4618601f3c598a2f3f2710f37053e1ea4","offset":5774,"builtins":["range_check"]},{"selector":"0x219209e083275171774dab1df80982e9df2096516f06319c5c6d71ae0a8480c","offset":7510,"builtins":["pedersen","range_check"]},{"selector":"0x225faa998b63ad3d277e950e8091f07d28a4c45ef6de7f3f7095e89be92d701","offset":4768,"builtins":["pedersen","range_check"]},{"selector":"0x24643b0aa4f24549ae7cd884195db7950c3a79a96cb7f37bde40549723559d9","offset":5013,"builtins":["pedersen","range_check"]},{"selector":"0x25a5317fee78a3601253266ed250be22974a6b6eb116c875a2596585df6a400","offset":3979,"builtins":["pedersen","range_check"]},{"selector":"0x2842cf0fb9cd347687a6bfcf564c97b007cc28c3e25566d4b71c8935857767d","offset":734,"builtins":["pedersen","range_check"]},{"selector":"0x284a2f635301a0bf3a171bb8e4292667c6c70d23d48b0ae8ec4df336e84bccd","offset":2650,"builtins":["range_check"]},{"selector":"0x2e4263afad30923c891518314c3c95dbe830a16874e8abc5777a9a20b54c76e","offset":8692,"builtins":["pedersen","range_check"]},{"selector":"0x302e0454f48778e0ca3a2e714a289c4e8d8e03d614b370130abb1a524a47f22","offset":3788,"builtins":["pedersen","range_check"]},{"selector":"0x30559321b47d576b645ed7bd24089943dd5fd3a359ecdd6fa8f05c1bab67d6b","offset":3492,"builtins":["pedersen","range_check"]},{"selector":"0x338dd2002b6f7ac6471742691de72611381e3fc4ce2b0361c29d42cb2d53a90","offset":3156,"builtins":["pedersen","range_check","poseidon"]},{"selector":"0x33fe3600cdfaa48261a8c268c66363562da383d5dd26837ba63b66ebbc04e3c","offset":2988,"builtins":["pedersen","range_check","poseidon"]},{"selector":"0x35a73cd311a05d46deda634c5ee045db92f811b4e74bca4437fcb5302b7af33","offset":6184,"builtins":["pedersen","range_check"]},{"selector":"0x35ba35ba82b63d56c50ba3393af144616a46a2d2a79d13a5db1635a3386bd48","offset":0,"builtins":["pedersen","range_check"]},{"selector":"0x361458367e696363fbcc70777d07ebbd2394e89fd0adcaf147faccd1d294d60","offset":5649,"builtins":["range_check"]},{"selector":"0x3704ffe8fba161be0e994951751a5033b1462b918ff785c0a636be718dfdb68","offset":7027,"builtins":["pedersen","range_check"]},{"selector":"0x37791de85f8a3be5014988a652f6cf025858f3532706c18f8cf24f2f81800d5","offset":4251,"builtins":["pedersen","range_check"]},{"selector":"0x3a07502a2e0e18ad6178ca530615148b9892d000199dbb29e402c41913c3d1a","offset":1654,"builtins":["pedersen","range_check"]},{"selector":"0x3b076186c19fe96221e4dfacd40c519f612eae02e0555e4e115a2a6cf2f1c1f","offset":8231,"builtins":["pedersen","range_check"]}],"L1_HANDLER":[],"CONSTRUCTOR":[{"selector":"0x28ffe4ff0f226a9107253e17a904099aa4f63a02a5621de0576e5aa71bc5194","offset":10063,"builtins":["pedersen","range_check"]}]}} ================================================ FILE: crates/cheatnet/src/forking/cache.rs ================================================ use anyhow::{Context, Result}; use camino::{Utf8Path, Utf8PathBuf}; use fs2::FileExt; use regex::Regex; use runtime::starknet::context::SerializableBlockInfo; use serde::{Deserialize, Serialize}; use starknet_api::block::{BlockInfo, BlockNumber}; use starknet_api::core::{ClassHash, ContractAddress, Nonce}; use starknet_api::state::StorageKey; use starknet_rust::core::types::ContractClass; use starknet_types_core::felt::Felt; use std::collections::HashMap; use std::fs; use std::fs::{File, OpenOptions}; use std::io::{Read, Seek, Write}; use std::string::ToString; use url::Url; #[must_use] pub fn cache_version() -> String { env!("CARGO_PKG_VERSION").replace('.', "_") } #[derive(Serialize, Deserialize, Debug)] pub struct ForkCacheContent { cache_version: String, storage_at: HashMap>, nonce_at: HashMap, class_hash_at: HashMap, compiled_contract_class: HashMap, block_info: Option, } impl Default for ForkCacheContent { fn default() -> Self { Self { cache_version: cache_version(), storage_at: HashMap::default(), nonce_at: HashMap::default(), class_hash_at: HashMap::default(), compiled_contract_class: HashMap::default(), block_info: Option::default(), } } } impl ForkCacheContent { fn from_str(serialized: &str) -> Self { let cache: Self = serde_json::from_str(serialized).expect("Could not deserialize cache from json"); assert_eq!( cache.cache_version, cache_version(), "Expected the Version {}", cache_version() ); cache } fn extend(&mut self, other: &Self) { // storage_at for (other_contract_address, other_storage) in &other.storage_at { if let Some(self_contract_storage) = self.storage_at.get_mut(other_contract_address) { self_contract_storage.extend(other_storage.clone()); } else { self.storage_at .insert(*other_contract_address, other_storage.clone()); } } self.nonce_at.extend(other.nonce_at.clone()); self.class_hash_at.extend(other.class_hash_at.clone()); self.compiled_contract_class .extend(other.compiled_contract_class.clone()); if other.block_info.is_some() { self.block_info.clone_from(&other.block_info); } } fn compiled_contract_class_map(&self) -> &HashMap { &self.compiled_contract_class } } #[expect(clippy::to_string_trait_impl)] impl ToString for ForkCacheContent { fn to_string(&self) -> String { serde_json::to_string(self).expect("Could not serialize json cache") } } #[derive(Debug)] pub struct ForkCache { fork_cache_content: ForkCacheContent, cache_file: Utf8PathBuf, } impl Drop for ForkCache { fn drop(&mut self) { self.save(); } } trait FileTruncateExtension { fn clear(&mut self) -> Result<()>; } impl FileTruncateExtension for File { fn clear(&mut self) -> Result<()> { self.set_len(0).context("Failed to truncate file")?; self.rewind().context("Failed to rewind file")?; Ok(()) } } impl ForkCache { pub(crate) fn load_or_new( url: &Url, block_number: BlockNumber, cache_dir: &Utf8Path, ) -> Result { let cache_file = cache_file_path_from_fork_config(url, block_number, cache_dir)?; let mut file = OpenOptions::new() .write(true) .read(true) .create(true) .truncate(false) .open(&cache_file) .context("Could not open cache file")?; let mut cache_file_content = String::new(); file.read_to_string(&mut cache_file_content) .context("Could not read cache file")?; // File was just created let fork_cache_content = if cache_file_content.is_empty() { ForkCacheContent::default() } else { ForkCacheContent::from_str(cache_file_content.as_str()) }; Ok(ForkCache { fork_cache_content, cache_file, }) } fn save(&self) { let mut file = OpenOptions::new() .write(true) .read(true) .create(true) .truncate(false) .open(&self.cache_file) .unwrap(); file.lock_exclusive().expect("Could not lock on cache file"); let mut cache_file_content = String::new(); file.read_to_string(&mut cache_file_content) .expect("Should have been able to read the cache"); let output = if cache_file_content.is_empty() { self.fork_cache_content.to_string() } else { let mut fs_fork_cache_content = ForkCacheContent::from_str(&cache_file_content); fs_fork_cache_content.extend(&self.fork_cache_content); fs_fork_cache_content.to_string() }; file.clear().expect("Failed to clear file"); file.write_all(output.as_bytes()) .expect("Could not write cache to file"); fs2::FileExt::unlock(&file).unwrap(); } pub(crate) fn get_storage_at( &self, contract_address: &ContractAddress, key: &StorageKey, ) -> Option { self.fork_cache_content .storage_at .get(contract_address)? .get(key) .copied() } pub(crate) fn cache_get_storage_at( &mut self, contract_address: ContractAddress, key: StorageKey, value: Felt, ) { self.fork_cache_content .storage_at .entry(contract_address) .or_default() .insert(key, value); } pub(crate) fn get_nonce_at(&self, address: &ContractAddress) -> Option { self.fork_cache_content.nonce_at.get(address).copied() } pub(crate) fn cache_get_nonce_at(&mut self, contract_address: ContractAddress, nonce: Nonce) { self.fork_cache_content .nonce_at .insert(contract_address, nonce); } #[must_use] pub fn get_class_hash_at(&self, contract_address: &ContractAddress) -> Option { self.fork_cache_content .class_hash_at .get(contract_address) .copied() } #[must_use] pub fn compiled_contract_class_map(&self) -> &HashMap { self.fork_cache_content.compiled_contract_class_map() } pub(crate) fn cache_get_class_hash_at( &mut self, contract_address: ContractAddress, class_hash: ClassHash, ) { self.fork_cache_content .class_hash_at .insert(contract_address, class_hash); } pub(crate) fn get_compiled_contract_class( &self, class_hash: &ClassHash, ) -> Option<&ContractClass> { self.fork_cache_content .compiled_contract_class .get(class_hash) } pub(crate) fn insert_compiled_contract_class( &mut self, class_hash: ClassHash, contract_class: ContractClass, ) -> &ContractClass { self.fork_cache_content .compiled_contract_class .entry(class_hash) .or_insert(contract_class) } pub(crate) fn get_block_info(&self) -> Option { Some(self.fork_cache_content.block_info.clone()?.into()) } pub(crate) fn cache_get_block_info(&mut self, block_info: BlockInfo) { self.fork_cache_content.block_info = Some(block_info.into()); } } fn cache_file_path_from_fork_config( url: &Url, BlockNumber(block_number): BlockNumber, cache_dir: &Utf8Path, ) -> Result { let re = Regex::new(r"[^a-zA-Z0-9]").unwrap(); // replace non-alphanumeric characters with underscores let sanitized_path = re.replace_all(url.as_str(), "_"); let cache_file_path = cache_dir.join(format!( "{sanitized_path}_{block_number}_v{}.json", cache_version() )); fs::create_dir_all(cache_file_path.parent().unwrap()) .context("Fork cache directory could not be created")?; Ok(cache_file_path) } ================================================ FILE: crates/cheatnet/src/forking/data.rs ================================================ use crate::runtime_extensions::forge_runtime_extension::contracts_data::build_name_selector_map; use starknet_api::core::{ClassHash, EntryPointSelector}; use starknet_rust::core::types::ContractClass; use starknet_rust::core::types::contract::AbiEntry; use std::collections::HashMap; #[derive(Default)] pub struct ForkData { pub abi: HashMap>, pub selectors: HashMap, } impl ForkData { /// Creates a new instance of [`ForkData`] from a given `fork_compiled_contract_class`. #[must_use] pub fn new(fork_compiled_contract_class: &HashMap) -> Self { let abi: HashMap> = fork_compiled_contract_class .iter() .filter_map(|(class_hash, contract_class)| { let ContractClass::Sierra(sierra_class) = contract_class else { return None; }; let abi = serde_json::from_str::>(&sierra_class.abi) .expect("this should be valid ABI"); Some((*class_hash, abi)) }) .collect(); let selectors = abi .values() .flat_map(|abi| build_name_selector_map(abi.clone())) .collect(); Self { abi, selectors } } } ================================================ FILE: crates/cheatnet/src/forking/mod.rs ================================================ pub mod cache; pub mod data; pub mod state; ================================================ FILE: crates/cheatnet/src/forking/state.rs ================================================ use crate::forking::cache::ForkCache; use crate::state::BlockInfoReader; use crate::sync_client::SyncClient; use anyhow::{Context, Result}; use blockifier::execution::contract_class::{ CompiledClassV0, CompiledClassV0Inner, CompiledClassV1, RunnableCompiledClass, }; use blockifier::state::errors::StateError::{self, StateReadError, UndeclaredClassHash}; use blockifier::state::state_api::{StateReader, StateResult}; use cairo_lang_utils::bigint::BigUintAsHex; use cairo_vm::types::program::Program; use camino::Utf8Path; use conversions::{FromConv, IntoConv}; use flate2::read::GzDecoder; use num_bigint::BigUint; use runtime::starknet::context::SerializableGasPrices; use starknet_api::block::{BlockInfo, BlockNumber, BlockTimestamp, StarknetVersion}; use starknet_api::contract_class::SierraVersion; use starknet_api::core::{ChainId, ClassHash, CompiledClassHash, ContractAddress, Nonce}; use starknet_api::state::StorageKey; use starknet_rust::core::types::{ ContractClass as ContractClassStarknet, MaybePreConfirmedBlockWithTxHashes, StarknetError, }; use starknet_rust::core::utils::parse_cairo_short_string; use starknet_rust::providers::ProviderError; use starknet_types_core::felt::Felt; use std::cell::{Ref, RefCell}; use std::collections::HashMap; use std::io::Read; use std::sync::Arc; use universal_sierra_compiler_api::compile_contract_sierra; use url::Url; #[derive(Debug)] pub struct ForkStateReader { client: SyncClient, cache: RefCell, } impl ForkStateReader { pub fn new(url: Url, block_number: BlockNumber, cache_dir: &Utf8Path) -> Result { Ok(ForkStateReader { cache: RefCell::new( ForkCache::load_or_new(&url, block_number, cache_dir) .context("Could not create fork cache")?, ), client: SyncClient::new(url, block_number), }) } pub fn chain_id(&self) -> Result { let id = self.client.chain_id()?; let id = parse_cairo_short_string(&id)?; Ok(ChainId::from(id)) } pub fn compiled_contract_class_map( &self, ) -> Ref<'_, HashMap> { Ref::map(self.cache.borrow(), |cache| { cache.compiled_contract_class_map() }) } } #[expect(clippy::needless_pass_by_value)] fn other_provider_error(boxed: impl ToString) -> Result { let err_str = boxed.to_string(); Err(StateReadError( if err_str.contains("error sending request") { "Unable to reach the node. Check your internet connection and node url".to_string() } else { format!("JsonRpc provider error: {err_str}") }, )) } impl BlockInfoReader for ForkStateReader { fn get_block_info(&mut self) -> StateResult { if let Some(cache_hit) = self.cache.borrow().get_block_info() { return Ok(cache_hit); } match self.client.get_block_with_tx_hashes() { Ok(MaybePreConfirmedBlockWithTxHashes::Block(block)) => { let block_info = BlockInfo { block_number: BlockNumber(block.block_number), sequencer_address: block.sequencer_address.into_(), block_timestamp: BlockTimestamp(block.timestamp), gas_prices: SerializableGasPrices::default().into(), use_kzg_da: true, starknet_version: block .starknet_version .try_into() .unwrap_or(StarknetVersion::LATEST), }; self.cache .borrow_mut() .cache_get_block_info(block_info.clone()); Ok(block_info) } Ok(MaybePreConfirmedBlockWithTxHashes::PreConfirmedBlock(_)) => { unreachable!("Preconfirmed block is not be allowed at the configuration level") } Err(ProviderError::Other(boxed)) => other_provider_error(boxed), Err(err) => Err(StateReadError(format!( "Unable to get block with tx hashes from fork ({err})" ))), } } } impl StateReader for ForkStateReader { fn get_storage_at( &self, contract_address: ContractAddress, key: StorageKey, ) -> StateResult { if let Some(cache_hit) = self.cache.borrow().get_storage_at(&contract_address, &key) { return Ok(cache_hit); } match self .client .get_storage_at(Felt::from_(contract_address), Felt::from_(*key.0.key())) { Ok(value) => { let value_sf = value.into_(); self.cache .borrow_mut() .cache_get_storage_at(contract_address, key, value_sf); Ok(value_sf) } Err(ProviderError::Other(boxed)) => other_provider_error(boxed), Err(ProviderError::StarknetError(StarknetError::ContractNotFound)) => { self.cache.borrow_mut().cache_get_storage_at( contract_address, key, Felt::default(), ); Ok(Felt::default()) } Err(x) => Err(StateReadError(format!( "Unable to get storage at address: {contract_address:?} and key: {key:?} from fork ({x})" ))), } } fn get_nonce_at(&self, contract_address: ContractAddress) -> StateResult { if let Some(cache_hit) = self.cache.borrow().get_nonce_at(&contract_address) { return Ok(cache_hit); } match self.client.get_nonce(Felt::from_(contract_address)) { Ok(nonce) => { let nonce = nonce.into_(); self.cache .borrow_mut() .cache_get_nonce_at(contract_address, nonce); Ok(nonce) } Err(ProviderError::Other(boxed)) => other_provider_error(boxed), Err(ProviderError::StarknetError(StarknetError::ContractNotFound)) => { self.cache .borrow_mut() .cache_get_nonce_at(contract_address, Nonce::default()); Ok(Nonce::default()) } Err(x) => Err(StateReadError(format!( "Unable to get nonce at {contract_address:?} from fork ({x})" ))), } } fn get_class_hash_at(&self, contract_address: ContractAddress) -> StateResult { if let Some(cache_hit) = self.cache.borrow().get_class_hash_at(&contract_address) { return Ok(cache_hit); } match self.client.get_class_hash_at(Felt::from_(contract_address)) { Ok(class_hash) => { let class_hash = class_hash.into_(); self.cache .borrow_mut() .cache_get_class_hash_at(contract_address, class_hash); Ok(class_hash) } Err(ProviderError::StarknetError(StarknetError::ContractNotFound)) => { self.cache .borrow_mut() .cache_get_class_hash_at(contract_address, ClassHash::default()); Ok(ClassHash::default()) } Err(ProviderError::Other(boxed)) => other_provider_error(boxed), Err(x) => Err(StateReadError(format!( "Unable to get class hash at {contract_address:?} from fork ({x})" ))), } } fn get_compiled_class(&self, class_hash: ClassHash) -> StateResult { let mut cache = self.cache.borrow_mut(); let contract_class = { if let Some(cache_hit) = cache.get_compiled_contract_class(&class_hash) { Ok(cache_hit) } else { match self.client.get_class(Felt::from_(class_hash)) { Ok(contract_class) => { Ok(cache.insert_compiled_contract_class(class_hash, contract_class)) } Err(ProviderError::StarknetError(StarknetError::ClassHashNotFound)) => { Err(UndeclaredClassHash(class_hash)) } Err(ProviderError::Other(boxed)) => other_provider_error(boxed), Err(x) => Err(StateReadError(format!( "Unable to get compiled class at {class_hash} from fork ({x})" ))), } } }; match contract_class? { ContractClassStarknet::Sierra(flattened_class) => { let converted_sierra_program: Vec = flattened_class .sierra_program .iter() .map(|field_element| BigUintAsHex { value: BigUint::from_bytes_be(&field_element.to_bytes_be()), }) .collect(); let sierra_contract_class = serde_json::json!({ "sierra_program": converted_sierra_program, "contract_class_version": "", "entry_points_by_type": flattened_class.entry_points_by_type }); let sierra_version = SierraVersion::extract_from_program(&flattened_class.sierra_program) .expect("Unable to extract Sierra version from Sierra program"); match compile_contract_sierra(&sierra_contract_class) { Ok(casm_contract_class_raw) => Ok(RunnableCompiledClass::V1( CompiledClassV1::try_from((casm_contract_class_raw, sierra_version)) .expect("Unable to create RunnableCompiledClass::V1"), )), Err(err) => Err(StateReadError(err.to_string())), } } ContractClassStarknet::Legacy(legacy_class) => { let converted_entry_points = serde_json::from_str( &serde_json::to_string(&legacy_class.entry_points_by_type).unwrap(), ) .unwrap(); let mut decoder = GzDecoder::new(&legacy_class.program[..]); let mut converted_program = String::new(); decoder.read_to_string(&mut converted_program).unwrap(); Ok(RunnableCompiledClass::V0(CompiledClassV0(Arc::new( CompiledClassV0Inner { program: Program::from_bytes(converted_program.as_ref(), None) .expect("Unable to load program from converted_program"), entry_points_by_type: converted_entry_points, }, )))) } } } fn get_compiled_class_hash(&self, _class_hash: ClassHash) -> StateResult { Err(StateReadError( "Unable to get compiled class hash from the fork".to_string(), )) } } ================================================ FILE: crates/cheatnet/src/lib.rs ================================================ use state::CheatnetState; pub mod constants; pub mod forking; pub mod predeployment; #[expect(clippy::result_large_err)] pub mod runtime_extensions; pub mod state; pub mod sync_client; pub mod trace_data; ================================================ FILE: crates/cheatnet/src/predeployment/erc20/constructor_data.rs ================================================ use starknet_api::core::ContractAddress; pub struct ERC20ConstructorData { pub name: String, pub symbol: String, pub decimals: u8, pub total_supply: (u128, u128), // (low, high) pub permitted_minter: ContractAddress, pub upgrade_delay: u64, } ================================================ FILE: crates/cheatnet/src/predeployment/erc20/eth.rs ================================================ use conversions::string::TryFromHexStr; use starknet_api::core::ContractAddress; use crate::predeployment::predeployed_contract::PredeployedContract; use super::constructor_data::ERC20ConstructorData; pub const ETH_CONTRACT_ADDRESS: &str = "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"; #[must_use] pub fn eth_predeployed_contract() -> PredeployedContract { // Compiled with starknet-compile, compiler version: 2.10.0 // Fetched with `starknet_getCompiledCasm` let raw_casm = include_str!("../../data/eth_erc20_casm.json"); let contract_address = ContractAddress::try_from_hex_str(ETH_CONTRACT_ADDRESS).unwrap(); let class_hash = TryFromHexStr::try_from_hex_str( "0x076791ef97c042f81fbf352ad95f39a22554ee8d7927b2ce3c681f3418b5206a", ) .unwrap(); // All storage values are taken from https://voyager.online/contract/0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d#readStorage // Block 747469 let total_supply_low: u128 = 15_000_000_000_000_000_000_000; let permitted_minter = ContractAddress::try_from_hex_str( "0x4c5772d1914fe6ce891b64eb35bf3522aeae1315647314aac58b01137607f3f", ) .unwrap(); let constructor_data = ERC20ConstructorData { name: "ETH".to_string(), symbol: "ETH".to_string(), decimals: 18, total_supply: (total_supply_low, 0), permitted_minter, upgrade_delay: 0, }; PredeployedContract::erc20(contract_address, class_hash, raw_casm, constructor_data) } ================================================ FILE: crates/cheatnet/src/predeployment/erc20/mod.rs ================================================ pub mod constructor_data; pub mod eth; pub mod predeployed_contract; pub mod strk; ================================================ FILE: crates/cheatnet/src/predeployment/erc20/predeployed_contract.rs ================================================ use crate::{ predeployment::predeployed_contract::PredeployedContract, runtime_extensions::forge_runtime_extension::cheatcodes::{ generate_random_felt::generate_random_felt, storage::{map_entry_address, storage_key, variable_address}, }, }; use conversions::felt::FromShortString; use starknet_api::{ core::{ClassHash, ContractAddress}, state::StorageKey, }; use starknet_types_core::felt::Felt; use super::constructor_data::ERC20ConstructorData; impl PredeployedContract { #[must_use] pub fn erc20( contract_address: ContractAddress, class_hash: ClassHash, raw_casm: &str, constructor_data: ERC20ConstructorData, ) -> Self { let ERC20ConstructorData { name, symbol, decimals, total_supply: (total_supply_low, total_supply_high), permitted_minter, upgrade_delay, } = constructor_data; let recipient = generate_random_felt(); let recipient_balance_low_address = map_entry_address("ERC20_balances", &[recipient]); let recipient_balance_high_address = StorageKey(recipient_balance_low_address.try_into().unwrap()) .next_storage_key() .unwrap(); let storage_kv_updates = [ // name ( storage_key(variable_address("ERC20_name")).unwrap(), Felt::from_short_string(&name).unwrap(), ), // symbol ( storage_key(variable_address("ERC20_symbol")).unwrap(), Felt::from_short_string(&symbol).unwrap(), ), // decimals ( storage_key(variable_address("ERC20_decimals")).unwrap(), Felt::from(decimals), ), // total_supply low ( storage_key(variable_address("ERC20_total_supply")).unwrap(), Felt::from(total_supply_low), ), // total_supply high ( storage_key(variable_address("ERC20_total_supply")) .unwrap() .next_storage_key() .unwrap(), Felt::from(total_supply_high), ), // recipient balance low ( storage_key(recipient_balance_low_address).unwrap(), Felt::from(total_supply_low), ), // recipient balance high ( storage_key(**recipient_balance_high_address).unwrap(), Felt::from(total_supply_high), ), // permitted_minter ( storage_key(variable_address("permitted_minter")).unwrap(), **permitted_minter, ), // upgrade_delay ( storage_key(variable_address("upgrade_delay")).unwrap(), Felt::from(upgrade_delay), ), ] .to_vec(); Self::new(contract_address, class_hash, raw_casm, storage_kv_updates) } } ================================================ FILE: crates/cheatnet/src/predeployment/erc20/strk.rs ================================================ use conversions::string::TryFromHexStr; use starknet_api::core::ContractAddress; use crate::predeployment::predeployed_contract::PredeployedContract; use super::constructor_data::ERC20ConstructorData; pub const STRK_CONTRACT_ADDRESS: &str = "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d"; #[must_use] pub fn strk_predeployed_contract() -> PredeployedContract { // Compiled with starknet-compile, compiler version: 2.10.0 // See: https://github.com/starknet-io/starkgate-contracts/blob/c787ec8e727c45499700d01e4eacd4cbc23a36ea/src/cairo/strk/erc20_lockable.cairo let raw_casm = include_str!("../../data/strk_erc20_casm.json"); let contract_address = ContractAddress::try_from_hex_str(STRK_CONTRACT_ADDRESS).unwrap(); let class_hash = TryFromHexStr::try_from_hex_str( "0x04ad3c1dc8413453db314497945b6903e1c766495a1e60492d44da9c2a986e4b", ) .unwrap(); // All storage values are taken from https://voyager.online/contract/0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d#readStorage // Block 747469 let total_supply_low: u128 = 60_000_000_000_000_000_000_000_000; let permitted_minter = ContractAddress::try_from_hex_str( "0x594c1582459ea03f77deaf9eb7e3917d6994a03c13405ba42867f83d85f085d", ) .unwrap(); let constructor_data = ERC20ConstructorData { name: "STRK".to_string(), symbol: "STRK".to_string(), decimals: 18, total_supply: (total_supply_low, 0), permitted_minter, upgrade_delay: 0, }; PredeployedContract::erc20(contract_address, class_hash, raw_casm, constructor_data) } ================================================ FILE: crates/cheatnet/src/predeployment/mod.rs ================================================ pub mod erc20; pub mod predeployed_contract; ================================================ FILE: crates/cheatnet/src/predeployment/predeployed_contract.rs ================================================ use crate::constants::contract_class; use starknet_api::{ contract_class::{ContractClass, SierraVersion}, core::{ClassHash, ContractAddress}, state::StorageKey, }; use starknet_types_core::felt::Felt; pub struct PredeployedContract { pub contract_address: ContractAddress, pub class_hash: ClassHash, pub contract_class: ContractClass, pub storage_kv_updates: Vec<(StorageKey, Felt)>, } impl PredeployedContract { #[must_use] pub fn new( contract_address: ContractAddress, class_hash: ClassHash, raw_casm: &str, storage_kv_updates: Vec<(StorageKey, Felt)>, ) -> Self { let contract_class = contract_class(raw_casm, SierraVersion::LATEST); Self { contract_address, class_hash, contract_class, storage_kv_updates, } } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/cairo1_execution.rs ================================================ use crate::runtime_extensions::call_to_blockifier_runtime_extension::CheatnetState; use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::{ CallInfoWithExecutionData, ContractClassEntryPointExecutionResult, extract_trace_and_register_errors, }; use crate::runtime_extensions::cheatable_starknet_runtime_extension::CheatableStarknetRuntimeExtension; use crate::runtime_extensions::common::get_relocated_vm_trace; use blockifier::execution::contract_class::{CompiledClassV1, TrackedResource}; use blockifier::execution::entry_point::ExecutableCallEntryPoint; use blockifier::execution::entry_point_execution::{ ExecutionRunnerMode, VmExecutionContext, finalize_execution, initialize_execution_context_with_runner_mode, prepare_call_arguments, }; use blockifier::execution::syscalls::vm_syscall_utils::SyscallUsageMap; use blockifier::{ execution::{ contract_class::EntryPointV1, entry_point::EntryPointExecutionContext, errors::EntryPointExecutionError, execution_utils::Args, }, state::state_api::State, }; use cairo_vm::vm::errors::cairo_run_errors::CairoRunError; use cairo_vm::{ hint_processor::hint_processor_definition::HintProcessor, vm::runners::cairo_runner::{CairoArg, CairoRunner}, }; use runtime::{ExtendedRuntime, StarknetRuntime}; // blockifier/src/execution/cairo1_execution.rs:48 (execute_entry_point_call) pub(crate) fn execute_entry_point_call_cairo1( call: ExecutableCallEntryPoint, compiled_class_v1: &CompiledClassV1, state: &mut dyn State, cheatnet_state: &mut CheatnetState, // Added parameter context: &mut EntryPointExecutionContext, ) -> ContractClassEntryPointExecutionResult { let tracked_resource = *context .tracked_resource_stack .last() .expect("Unexpected empty tracked resource."); let entry_point_initial_budget = context.gas_costs().base.entry_point_initial_budget; let class_hash = call.class_hash; let VmExecutionContext { mut runner, mut syscall_handler, initial_syscall_ptr, entry_point, program_extra_data_length, } = initialize_execution_context_with_runner_mode( call, compiled_class_v1, state, context, ExecutionRunnerMode::Tracing, )?; let args = prepare_call_arguments( &syscall_handler.base.call, &mut runner, initial_syscall_ptr, &mut syscall_handler.read_only_segments, &entry_point, entry_point_initial_budget, )?; let n_total_args = args.len(); // region: Modified blockifier code let mut cheatable_runtime = ExtendedRuntime { extension: CheatableStarknetRuntimeExtension { cheatnet_state }, extended_runtime: StarknetRuntime { hint_handler: syscall_handler, panic_traceback: None, }, }; // Execute. cheatable_run_entry_point( &mut runner, &mut cheatable_runtime, &entry_point, &args, program_extra_data_length, ) .inspect_err(|_| { extract_trace_and_register_errors( class_hash, &mut runner, cheatable_runtime.extension.cheatnet_state, ); })?; let trace = get_relocated_vm_trace(&mut runner); // Syscall usage here is flat, meaning it only includes syscalls from current call let syscall_usage = cheatable_runtime .extended_runtime .hint_handler .base .syscalls_usage .clone(); let call_info = finalize_execution( runner, cheatable_runtime.extended_runtime.hint_handler, n_total_args, program_extra_data_length, tracked_resource, )?; if call_info.execution.failed { // fallback to the last pc in the trace if user did not set `panic-backtrace = true` in `Scarb.toml` let pcs = if let Some(panic_traceback) = cheatable_runtime.extended_runtime.panic_traceback { panic_traceback } else { trace .last() .map(|last| vec![last.pc]) .expect("trace should have at least one entry") }; cheatable_runtime .extension .cheatnet_state .register_error(class_hash, pcs); } cheatnet_state .trace_data .set_vm_trace_for_current_call(trace); // TODO(#4250): Investigate if we can simplify our logic given that syscall usage is now present in `CallInfo` let (syscall_usage_vm_resources, syscall_usage_sierra_gas) = match tracked_resource { TrackedResource::CairoSteps => (syscall_usage, SyscallUsageMap::default()), TrackedResource::SierraGas => (SyscallUsageMap::default(), syscall_usage), }; Ok(CallInfoWithExecutionData { call_info, syscall_usage_vm_resources, syscall_usage_sierra_gas, }) // endregion } // crates/blockifier/src/execution/cairo1_execution.rs:236 (run_entry_point) pub fn cheatable_run_entry_point( runner: &mut CairoRunner, hint_processor: &mut dyn HintProcessor, entry_point: &EntryPointV1, args: &Args, program_segment_size: usize, ) -> Result<(), EntryPointExecutionError> { // region: Modified blockifier code // Opposite to blockifier let verify_secure = false; // endregion let args: Vec<&CairoArg> = args.iter().collect(); runner .run_from_entrypoint( entry_point.pc(), &args, verify_secure, Some(program_segment_size), hint_processor, ) .map_err(Box::new)?; // region: Modified blockifier code // Relocate trace to then collect it runner .relocate(true, true) .map_err(CairoRunError::from) .map_err(Box::new)?; // endregion Ok(()) } ================================================ FILE: crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/calls.rs ================================================ use crate::runtime_extensions::call_to_blockifier_runtime_extension::CheatnetState; use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::execution_utils::clear_events_and_messages_from_reverted_call; use blockifier::execution::syscalls::hint_processor::ENTRYPOINT_FAILED_ERROR; use blockifier::execution::{ entry_point::{CallEntryPoint, CallType}, execution_utils::ReadOnlySegment, syscalls::{ hint_processor::{SyscallExecutionError, SyscallHintProcessor, create_retdata_segment}, syscall_base::SyscallResult, }, }; use cairo_vm::vm::vm_core::VirtualMachine; use starknet_api::{ contract_class::EntryPointType, core::{ClassHash, EntryPointSelector}, transaction::fields::Calldata, }; use starknet_types_core::felt::Felt; use super::entry_point::{ExecuteCallEntryPointExtraOptions, execute_call_entry_point}; // blockifier/src/execution/syscalls/hint_processor.rs:541 (execute_inner_call) pub fn execute_inner_call( call: &mut CallEntryPoint, vm: &mut VirtualMachine, syscall_handler: &mut SyscallHintProcessor<'_>, cheatnet_state: &mut CheatnetState, remaining_gas: &mut u64, ) -> SyscallResult { let revert_idx = syscall_handler.base.context.revert_infos.0.len(); // region: Modified blockifier code let call_info = execute_call_entry_point( call, syscall_handler.base.state, cheatnet_state, syscall_handler.base.context, remaining_gas, &ExecuteCallEntryPointExtraOptions::default(), )?; // endregion let mut raw_retdata = call_info.execution.retdata.0.clone(); let failed = call_info.execution.failed; syscall_handler.base.inner_calls.push(call_info); if failed { syscall_handler .base .context .revert(revert_idx, syscall_handler.base.state)?; // Delete events and l2_to_l1_messages from the reverted call. let reverted_call = syscall_handler.base.inner_calls.last_mut().unwrap(); clear_events_and_messages_from_reverted_call(reverted_call); raw_retdata .push(Felt::from_hex(ENTRYPOINT_FAILED_ERROR).map_err(SyscallExecutionError::from)?); return Err(SyscallExecutionError::Revert { error_data: raw_retdata, }); } let retdata_segment = create_retdata_segment(vm, syscall_handler, &raw_retdata)?; Ok(retdata_segment) } // blockifier/src/execution/syscalls/hint_processor.rs:577 (execute_library_call) #[expect(clippy::too_many_arguments)] pub fn execute_library_call( syscall_handler: &mut SyscallHintProcessor<'_>, cheatnet_state: &mut CheatnetState, vm: &mut VirtualMachine, class_hash: ClassHash, call_to_external: bool, entry_point_selector: EntryPointSelector, calldata: Calldata, remaining_gas: &mut u64, ) -> SyscallResult { let entry_point_type = if call_to_external { EntryPointType::External } else { EntryPointType::L1Handler }; let mut entry_point = CallEntryPoint { class_hash: Some(class_hash), code_address: None, entry_point_type, entry_point_selector, calldata, // The call context remains the same in a library call. storage_address: syscall_handler.storage_address(), caller_address: syscall_handler.caller_address(), call_type: CallType::Delegate, initial_gas: *remaining_gas, }; execute_inner_call( &mut entry_point, vm, syscall_handler, cheatnet_state, remaining_gas, ) } ================================================ FILE: crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/cheated_syscalls.rs ================================================ use super::calls::{execute_inner_call, execute_library_call}; use super::execution_info::get_cheated_exec_info_ptr; use super::execution_utils::clear_events_and_messages_from_reverted_call; use crate::runtime_extensions::call_to_blockifier_runtime_extension::CheatnetState; use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::execute_constructor_entry_point; use blockifier::context::TransactionContext; use blockifier::execution::common_hints::ExecutionMode; use blockifier::execution::errors::EntryPointExecutionError; use blockifier::execution::execution_utils::ReadOnlySegment; use blockifier::execution::syscalls::hint_processor::create_retdata_segment; use blockifier::execution::syscalls::hint_processor::{ INVALID_ARGUMENT, SyscallExecutionError, SyscallHintProcessor, }; use blockifier::execution::syscalls::syscall_base::SyscallResult; use blockifier::execution::syscalls::vm_syscall_utils::{ CallContractRequest, CallContractResponse, DeployRequest, DeployResponse, EmptyRequest, GetBlockHashRequest, GetBlockHashResponse, GetExecutionInfoResponse, LibraryCallRequest, LibraryCallResponse, MetaTxV0Request, MetaTxV0Response, StorageReadRequest, StorageReadResponse, StorageWriteRequest, StorageWriteResponse, SyscallSelector, TryExtractRevert, }; use blockifier::execution::{call_info::CallInfo, entry_point::ConstructorContext}; use blockifier::state::errors::StateError; use blockifier::transaction::objects::{ CommonAccountFields, DeprecatedTransactionInfo, TransactionInfo, }; use blockifier::{ execution::entry_point::{ CallEntryPoint, CallType, EntryPointExecutionContext, EntryPointExecutionResult, }, state::state_api::State, }; use cairo_vm::Felt252; use cairo_vm::vm::vm_core::VirtualMachine; use conversions::string::TryFromHexStr; use runtime::starknet::constants::TEST_ADDRESS; use starknet_api::abi::abi_utils::selector_from_name; use starknet_api::contract_class::EntryPointType; use starknet_api::core::EntryPointSelector; use starknet_api::transaction::constants::EXECUTE_ENTRY_POINT_NAME; use starknet_api::transaction::fields::TransactionSignature; use starknet_api::transaction::{TransactionHasher, TransactionOptions, signed_tx_version}; use starknet_api::{ core::{ClassHash, ContractAddress, Nonce, calculate_contract_address}, transaction::{ InvokeTransactionV0, TransactionVersion, fields::{Calldata, Fee}, }, }; use starknet_types_core::felt::Felt; use std::sync::Arc; pub fn get_execution_info_syscall( _request: EmptyRequest, vm: &mut VirtualMachine, syscall_handler: &mut SyscallHintProcessor<'_>, cheatnet_state: &mut CheatnetState, _remaining_gas: &mut u64, ) -> SyscallResult { let execution_info_ptr = syscall_handler.get_or_allocate_execution_info_segment(vm)?; let cheated_data = cheatnet_state.get_cheated_data(syscall_handler.storage_address()); let ptr_cheated_exec_info = get_cheated_exec_info_ptr(vm, execution_info_ptr, &cheated_data); Ok(GetExecutionInfoResponse { execution_info_ptr: ptr_cheated_exec_info, }) } /// Converts `deploy_syscall` failure into catchable [`SyscallExecutionError::Revert`] error. fn convert_deploy_failure_to_revert( syscall_handler: &mut SyscallHintProcessor<'_>, revert_idx: usize, raw_retdata: Vec, call_info_was_added: bool, ) -> SyscallResult { syscall_handler .base .context .revert(revert_idx, syscall_handler.base.state)?; if call_info_was_added && let Some(reverted_call) = syscall_handler.base.inner_calls.last_mut() { clear_events_and_messages_from_reverted_call(reverted_call); } Err(SyscallExecutionError::Revert { error_data: raw_retdata, }) } // blockifier/src/execution/syscalls/mod.rs:222 (deploy_syscall) pub fn deploy_syscall( request: DeployRequest, vm: &mut VirtualMachine, syscall_handler: &mut SyscallHintProcessor<'_>, cheatnet_state: &mut CheatnetState, remaining_gas: &mut u64, ) -> SyscallResult { // Increment the Deploy syscall's linear cost counter by the number of elements in the // constructor calldata syscall_handler.base.increment_syscall_linear_factor_by( &SyscallSelector::Deploy, request.constructor_calldata.0.len(), ); let deployer_address = syscall_handler.base.call.storage_address; let deployer_address_for_calculation = if request.deploy_from_zero { ContractAddress::default() } else { deployer_address }; // region: Modified blockifier code let deployed_contract_address = if let Some(contract_address) = cheatnet_state.next_address_for_deployment() { contract_address } else { calculate_contract_address( request.contract_address_salt, request.class_hash, &request.constructor_calldata, deployer_address_for_calculation, )? }; // endregion let ctor_context = ConstructorContext { class_hash: request.class_hash, code_address: Some(deployed_contract_address), storage_address: deployed_contract_address, caller_address: deployer_address, }; // Check if this deploy comes from the cheatcode // This allows us to make `deploy_syscall` revertible, only when used via the cheatcode let from_cheatcode = cheatnet_state.take_next_syscall_from_cheatcode(); let revert_idx = syscall_handler.base.context.revert_infos.0.len(); let call_info = match execute_deployment( syscall_handler.base.state, cheatnet_state, syscall_handler.base.context, &ctor_context, request.constructor_calldata, remaining_gas, ) { Ok(info) => info, Err(EntryPointExecutionError::ExecutionFailed { error_trace }) if from_cheatcode => { let panic_data = error_trace.last_retdata.0.clone(); return convert_deploy_failure_to_revert( syscall_handler, revert_idx, panic_data, false, ); } Err(err) => return Err(err.into()), }; let failed = call_info.execution.failed; let raw_retdata = call_info.execution.retdata.0.clone(); syscall_handler.base.inner_calls.push(call_info); if failed && from_cheatcode { // This should be unreachable with the current implementation of `execute_deployment` // This check is kept in case of future changes + to be aligned with revert handling elsewhere. return convert_deploy_failure_to_revert(syscall_handler, revert_idx, raw_retdata, true); } let constructor_retdata = create_retdata_segment(vm, syscall_handler, &raw_retdata)?; Ok(DeployResponse { contract_address: deployed_contract_address, constructor_retdata, }) } // blockifier/src/execution/execution_utils.rs:217 (execute_deployment) pub fn execute_deployment( state: &mut dyn State, cheatnet_state: &mut CheatnetState, context: &mut EntryPointExecutionContext, ctor_context: &ConstructorContext, constructor_calldata: Calldata, remaining_gas: &mut u64, ) -> EntryPointExecutionResult { // Address allocation in the state is done before calling the constructor, so that it is // visible from it. let deployed_contract_address = ctor_context.storage_address; let current_class_hash = state.get_class_hash_at(deployed_contract_address)?; if current_class_hash != ClassHash::default() { return Err(StateError::UnavailableContractAddress(deployed_contract_address).into()); } state.set_class_hash_at(deployed_contract_address, ctor_context.class_hash)?; let call_info = execute_constructor_entry_point( state, cheatnet_state, context, ctor_context, constructor_calldata, remaining_gas, )?; Ok(call_info) } // blockifier/src/execution/syscalls/mod.rs:407 (library_call) pub fn library_call_syscall( request: LibraryCallRequest, vm: &mut VirtualMachine, syscall_handler: &mut SyscallHintProcessor<'_>, cheatnet_state: &mut CheatnetState, remaining_gas: &mut u64, ) -> SyscallResult { let call_to_external = true; let retdata_segment = execute_library_call( syscall_handler, cheatnet_state, vm, request.class_hash, call_to_external, request.function_selector, request.calldata, remaining_gas, ) .map_err(|error| match error { SyscallExecutionError::Revert { .. } => error, _ => error.as_lib_call_execution_error( request.class_hash, syscall_handler.storage_address(), request.function_selector, ), })?; Ok(LibraryCallResponse { segment: retdata_segment, }) } // blockifier/src/execution/syscalls/mod.rs:157 (call_contract) pub fn call_contract_syscall( request: CallContractRequest, vm: &mut VirtualMachine, syscall_handler: &mut SyscallHintProcessor<'_>, cheatnet_state: &mut CheatnetState, remaining_gas: &mut u64, ) -> SyscallResult { let storage_address = request.contract_address; let class_hash = syscall_handler .base .state .get_class_hash_at(storage_address)?; let selector = request.function_selector; let mut entry_point = CallEntryPoint { class_hash: None, code_address: Some(storage_address), entry_point_type: EntryPointType::External, entry_point_selector: selector, calldata: request.calldata, storage_address, caller_address: syscall_handler.storage_address(), call_type: CallType::Call, initial_gas: *remaining_gas, }; let retdata_segment = execute_inner_call( &mut entry_point, vm, syscall_handler, cheatnet_state, remaining_gas, ) .map_err(|error| match error { SyscallExecutionError::Revert { .. } => error, _ => error.as_call_contract_execution_error(class_hash, storage_address, selector), })?; // region: Modified blockifier code Ok(CallContractResponse { segment: retdata_segment, }) // endregion } // blockifier/src/execution/syscalls/hint_processor.rs:637 (meta_tx_v0) pub fn meta_tx_v0_syscall( request: MetaTxV0Request, vm: &mut VirtualMachine, syscall_handler: &mut SyscallHintProcessor<'_>, cheatnet_state: &mut CheatnetState, remaining_gas: &mut u64, ) -> SyscallResult { let storage_address = request.contract_address; let selector = request.entry_point_selector; // region: Modified blockifier code let retdata_segment = meta_tx_v0( syscall_handler, vm, cheatnet_state, storage_address, selector, request.calldata, request.signature, remaining_gas, )?; // endregion Ok(MetaTxV0Response { segment: retdata_segment, }) } // blockifier/src/execution/syscalls/syscall_base.rs:278 (meta_tx_v0) #[allow(clippy::result_large_err, clippy::too_many_arguments)] fn meta_tx_v0( syscall_handler: &mut SyscallHintProcessor<'_>, vm: &mut VirtualMachine, cheatnet_state: &mut CheatnetState, contract_address: ContractAddress, entry_point_selector: EntryPointSelector, calldata: Calldata, signature: TransactionSignature, remaining_gas: &mut u64, ) -> SyscallResult { syscall_handler .base .increment_syscall_linear_factor_by(&SyscallSelector::MetaTxV0, calldata.0.len()); if syscall_handler.base.context.execution_mode == ExecutionMode::Validate { //region: Modified blockifier code unreachable!( "`ExecutionMode::Validate` should never occur as execution mode is hardcoded to `Execute`" ); // endregion } if entry_point_selector != selector_from_name(EXECUTE_ENTRY_POINT_NAME) { return Err(SyscallExecutionError::Revert { error_data: vec![Felt252::from_hex(INVALID_ARGUMENT).unwrap()], }); } let mut entry_point = CallEntryPoint { class_hash: None, code_address: Some(contract_address), entry_point_type: EntryPointType::External, entry_point_selector, calldata: calldata.clone(), storage_address: contract_address, caller_address: ContractAddress::default(), call_type: CallType::Call, // NOTE: this value might be overridden later on. initial_gas: *remaining_gas, }; let old_tx_context = syscall_handler.base.context.tx_context.clone(); let only_query = old_tx_context.tx_info.only_query(); // Compute meta-transaction hash. let transaction_hash = InvokeTransactionV0 { max_fee: Fee(0), signature: signature.clone(), contract_address, entry_point_selector, calldata, } .calculate_transaction_hash( &syscall_handler .base .context .tx_context .block_context .chain_info() .chain_id, &signed_tx_version( &TransactionVersion::ZERO, &TransactionOptions { only_query }, ), )?; let class_hash = syscall_handler .base .state .get_class_hash_at(contract_address)?; // Replace `tx_context`. let new_tx_info = TransactionInfo::Deprecated(DeprecatedTransactionInfo { common_fields: CommonAccountFields { transaction_hash, version: TransactionVersion::ZERO, signature, nonce: Nonce(0.into()), sender_address: contract_address, only_query, }, max_fee: Fee(0), }); syscall_handler.base.context.tx_context = Arc::new(TransactionContext { block_context: old_tx_context.block_context.clone(), tx_info: new_tx_info, }); // region: Modified blockifier code // No error should be propagated until we restore the old `tx_context`. let retdata_segment = execute_inner_call( &mut entry_point, vm, syscall_handler, cheatnet_state, remaining_gas, ) .map_err(|error| { SyscallExecutionError::from_self_or_revert(error.try_extract_revert().map_original( |error| { error.as_call_contract_execution_error( class_hash, contract_address, entry_point_selector, ) }, )) })?; // endregion // Restore the old `tx_context`. syscall_handler.base.context.tx_context = old_tx_context; Ok(retdata_segment) } #[expect(clippy::needless_pass_by_value)] pub fn get_block_hash_syscall( request: GetBlockHashRequest, _vm: &mut VirtualMachine, syscall_handler: &mut SyscallHintProcessor<'_>, cheatnet_state: &mut CheatnetState, _remaining_gas: &mut u64, ) -> SyscallResult { let contract_address = syscall_handler.storage_address(); let block_number = request.block_number.0; let block_hash = cheatnet_state.get_block_hash_for_contract( contract_address, block_number, syscall_handler, )?; Ok(GetBlockHashResponse { block_hash }) } #[expect(clippy::needless_pass_by_value)] pub fn storage_read( request: StorageReadRequest, _vm: &mut VirtualMachine, syscall_handler: &mut SyscallHintProcessor<'_>, cheatnet_state: &mut CheatnetState, _remaining_gas: &mut u64, ) -> SyscallResult { let original_storage_address = syscall_handler.base.call.storage_address; maybe_modify_storage_address(syscall_handler, cheatnet_state)?; let value = syscall_handler .base .storage_read(request.address) .inspect_err(|_| { // Restore state on error before bubbling up syscall_handler.base.call.storage_address = original_storage_address; })?; // Restore the original storage_address syscall_handler.base.call.storage_address = original_storage_address; Ok(StorageReadResponse { value }) } #[expect(clippy::needless_pass_by_value)] pub fn storage_write( request: StorageWriteRequest, _vm: &mut VirtualMachine, syscall_handler: &mut SyscallHintProcessor<'_>, cheatnet_state: &mut CheatnetState, _remaining_gas: &mut u64, ) -> SyscallResult { let original_storage_address = syscall_handler.base.call.storage_address; maybe_modify_storage_address(syscall_handler, cheatnet_state)?; syscall_handler .base .storage_write(request.address, request.value) .inspect_err(|_| { // Restore state on error before bubbling up syscall_handler.base.call.storage_address = original_storage_address; })?; // Restore the original storage_address syscall_handler.base.call.storage_address = original_storage_address; Ok(StorageWriteResponse {}) } // This logic is used to modify the storage address to enable using `contract_state_for_testing` // inside `interact_with_state` closure cheatcode. fn maybe_modify_storage_address( syscall_handler: &mut SyscallHintProcessor<'_>, cheatnet_state: &mut CheatnetState, ) -> Result<(), StateError> { let contract_address = syscall_handler.storage_address(); let test_address = TryFromHexStr::try_from_hex_str(TEST_ADDRESS).expect("Failed to parse `TEST_ADDRESS`"); if contract_address != test_address { return Ok(()); } let cheated_data = cheatnet_state.get_cheated_data(contract_address); if let Some(actual_address) = cheated_data.contract_address { let class_hash = syscall_handler .base .state .get_class_hash_at(actual_address) .expect("`get_class_hash_at` should never fail"); if class_hash == ClassHash::default() { return Err(StateError::StateReadError(format!( "Failed to interact with contract state because no contract is deployed at address {actual_address}" ))); } syscall_handler.base.call.storage_address = actual_address; } Ok(()) } ================================================ FILE: crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/deprecated/cairo0_execution.rs ================================================ use crate::runtime_extensions::call_to_blockifier_runtime_extension::CheatnetState; use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::{ CallInfoWithExecutionData, ContractClassEntryPointExecutionResult, extract_trace_and_register_errors, }; use crate::runtime_extensions::deprecated_cheatable_starknet_extension::DeprecatedCheatableStarknetRuntimeExtension; use crate::runtime_extensions::deprecated_cheatable_starknet_extension::runtime::{ DeprecatedExtendedRuntime, DeprecatedStarknetRuntime, }; use blockifier::execution::contract_class::CompiledClassV0; use blockifier::execution::deprecated_entry_point_execution::{ VmExecutionContext, finalize_execution, initialize_execution_context, prepare_call_arguments, }; use blockifier::execution::entry_point::{EntryPointExecutionContext, ExecutableCallEntryPoint}; use blockifier::execution::errors::EntryPointExecutionError; use blockifier::execution::execution_utils::Args; use blockifier::execution::syscalls::vm_syscall_utils::SyscallUsageMap; use blockifier::state::state_api::State; use cairo_vm::hint_processor::hint_processor_definition::HintProcessor; use cairo_vm::vm::runners::cairo_runner::{CairoArg, CairoRunner}; // blockifier/src/execution/deprecated_execution.rs:36 (execute_entry_point_call) pub(crate) fn execute_entry_point_call_cairo0( call: ExecutableCallEntryPoint, compiled_class_v0: CompiledClassV0, state: &mut dyn State, cheatnet_state: &mut CheatnetState, context: &mut EntryPointExecutionContext, ) -> ContractClassEntryPointExecutionResult { let VmExecutionContext { mut runner, mut syscall_handler, initial_syscall_ptr, entry_point_pc, } = initialize_execution_context(&call, compiled_class_v0, state, context)?; let (implicit_args, args) = prepare_call_arguments( &call, &mut runner, initial_syscall_ptr, &mut syscall_handler.read_only_segments, )?; let n_total_args = args.len(); // region: Modified blockifier code let cheatable_extension = DeprecatedCheatableStarknetRuntimeExtension { cheatnet_state }; let mut cheatable_syscall_handler = DeprecatedExtendedRuntime { extension: cheatable_extension, extended_runtime: DeprecatedStarknetRuntime { hint_handler: syscall_handler, }, }; // Execute. cheatable_run_entry_point( &mut runner, &mut cheatable_syscall_handler, entry_point_pc, &args, ) .inspect_err(|_| { extract_trace_and_register_errors( call.class_hash, &mut runner, cheatable_syscall_handler.extension.cheatnet_state, ); })?; let syscall_usage = cheatable_syscall_handler .extended_runtime .hint_handler .syscalls_usage .clone(); let execution_result = finalize_execution( runner, cheatable_syscall_handler.extended_runtime.hint_handler, call, implicit_args, n_total_args, )?; Ok(CallInfoWithExecutionData { call_info: execution_result, syscall_usage_vm_resources: syscall_usage, syscall_usage_sierra_gas: SyscallUsageMap::default(), }) // endregion } // blockifier/src/execution/deprecated_execution.rs:192 (run_entry_point) pub fn cheatable_run_entry_point( runner: &mut CairoRunner, hint_processor: &mut dyn HintProcessor, entry_point_pc: usize, args: &Args, ) -> Result<(), EntryPointExecutionError> { // region: Modified blockifier code // Opposite to blockifier let verify_secure = false; // endregion let program_segment_size = None; // Infer size from program. let args: Vec<&CairoArg> = args.iter().collect(); runner .run_from_entrypoint( entry_point_pc, &args, verify_secure, program_segment_size, hint_processor, ) .map_err(Box::new)?; Ok(()) } ================================================ FILE: crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/deprecated/mod.rs ================================================ pub mod cairo0_execution; ================================================ FILE: crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs ================================================ use super::cairo1_execution::execute_entry_point_call_cairo1; use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::deprecated::cairo0_execution::execute_entry_point_call_cairo0; use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::execution_utils::{exit_error_call, resolve_cheated_data_for_call, update_trace_data}; use crate::runtime_extensions::call_to_blockifier_runtime_extension::rpc::CallSuccess; use crate::runtime_extensions::call_to_blockifier_runtime_extension::CheatnetState; use crate::runtime_extensions::common::get_relocated_vm_trace; #[cfg(feature = "cairo-native")] use crate::runtime_extensions::native::execution::execute_entry_point_call_native; use crate::state::CheatStatus; use blockifier::execution::call_info::{CairoPrimitiveCounterMap, CallExecution, ExtendedExecutionResources, Retdata, StorageAccessTracker}; use blockifier::execution::contract_class::{RunnableCompiledClass, TrackedResource}; use blockifier::execution::entry_point::EntryPointRevertInfo; use blockifier::execution::execution_utils::update_remaining_gas; use blockifier::execution::stack_trace::{ extract_trailing_cairo1_revert_trace, Cairo1RevertHeader, }; use blockifier::execution::syscalls::hint_processor::{ ENTRYPOINT_NOT_FOUND_ERROR, OUT_OF_GAS_ERROR, }; use blockifier::execution::syscalls::vm_syscall_utils::SyscallUsageMap; use blockifier::{ execution::{ call_info::CallInfo, entry_point::{ handle_empty_constructor, CallEntryPoint, CallType, ConstructorContext, EntryPointExecutionContext, EntryPointExecutionResult, FAULTY_CLASS_HASH, }, errors::{EntryPointExecutionError, PreExecutionError}, }, state::state_api::State, }; use cairo_vm::vm::runners::cairo_runner::CairoRunner; use conversions::FromConv; use shared::vm::VirtualMachineExt; use starknet_api::execution_resources::GasAmount; use starknet_api::{ contract_class::EntryPointType, core::ClassHash, transaction::{fields::Calldata, TransactionVersion}, }; use starknet_types_core::felt::Felt; use std::collections::HashSet; pub(crate) type ContractClassEntryPointExecutionResult = Result; pub(crate) struct CallInfoWithExecutionData { pub call_info: CallInfo, pub syscall_usage_vm_resources: SyscallUsageMap, pub syscall_usage_sierra_gas: SyscallUsageMap, } #[derive(Default)] pub struct ExecuteCallEntryPointExtraOptions { pub trace_data_handled_by_revert_call: bool, } // blockifier/src/execution/entry_point (CallEntryPoint::execute) #[expect(clippy::too_many_lines)] pub fn execute_call_entry_point( entry_point: &mut CallEntryPoint, // Instead of 'self' state: &mut dyn State, cheatnet_state: &mut CheatnetState, context: &mut EntryPointExecutionContext, remaining_gas: &mut u64, opts: &ExecuteCallEntryPointExtraOptions, ) -> EntryPointExecutionResult { // region: Modified blockifier code // We skip recursion depth validation here. if !opts.trace_data_handled_by_revert_call { let cheated_data = resolve_cheated_data_for_call(entry_point, cheatnet_state); cheatnet_state .trace_data .enter_nested_call(entry_point.clone(), cheated_data.clone()); } if let Some(cheat_status) = get_mocked_function_cheat_status(entry_point, cheatnet_state) && let CheatStatus::Cheated(ret_data, _) = (*cheat_status).clone() { cheat_status.decrement_cheat_span(); let ret_data_f252: Vec = ret_data.iter().map(|datum| Felt::from_(*datum)).collect(); cheatnet_state.trace_data.update_current_call( ExtendedExecutionResources::default(), u64::default(), SyscallUsageMap::default(), SyscallUsageMap::default(), Ok(CallSuccess { ret_data: ret_data_f252, }), &[], vec![], vec![], ); if !opts.trace_data_handled_by_revert_call { cheatnet_state.trace_data.exit_nested_call(); } let tracked_resource = *context .tracked_resource_stack .last() .expect("Unexpected empty tracked resource."); return Ok(mocked_call_info( entry_point.clone(), ret_data.clone(), tracked_resource, )); } // endregion // Validate contract is deployed. let storage_class_hash = state.get_class_hash_at(entry_point.storage_address)?; if storage_class_hash == ClassHash::default() { return Err( PreExecutionError::UninitializedStorageAddress(entry_point.storage_address).into(), ); } // region: Modified blockifier code let maybe_replacement_class = cheatnet_state .replaced_bytecode_contracts .get(&entry_point.storage_address) .copied(); let class_hash = entry_point .class_hash .or(maybe_replacement_class) .unwrap_or(storage_class_hash); // If not given, take the storage contract class hash. // endregion let compiled_class = state.get_compiled_class(class_hash)?; let current_tracked_resource = compiled_class.get_current_tracked_resource(context); // region: Modified blockifier code cheatnet_state .trace_data .set_class_hash_for_current_call(class_hash); // endregion // Hack to prevent version 0 attack on ready (formerly argent) accounts. if context.tx_context.tx_info.version() == TransactionVersion::ZERO && class_hash == ClassHash(Felt::from_hex(FAULTY_CLASS_HASH).expect("A class hash must be a felt.")) { return Err(PreExecutionError::FraudAttempt.into()); } let contract_class = state.get_compiled_class(class_hash)?; context.revert_infos.0.push(EntryPointRevertInfo::new( entry_point.storage_address, class_hash, context.n_emitted_events, context.n_sent_messages_to_l1, )); context .tracked_resource_stack .push(current_tracked_resource); // Region: Modified blockifier code let entry_point = entry_point.clone().into_executable(class_hash); let result = match contract_class { RunnableCompiledClass::V0(compiled_class_v0) => execute_entry_point_call_cairo0( entry_point.clone(), compiled_class_v0, state, cheatnet_state, context, ), RunnableCompiledClass::V1(compiled_class_v1) => execute_entry_point_call_cairo1( entry_point.clone(), &compiled_class_v1, state, cheatnet_state, context, ), #[cfg(feature = "cairo-native")] RunnableCompiledClass::V1Native(native_compiled_class_v1) => { if context.tracked_resource_stack.last() == Some(&TrackedResource::CairoSteps) { execute_entry_point_call_cairo1( entry_point.clone(), &native_compiled_class_v1.casm(), state, cheatnet_state, context, ) } else { execute_entry_point_call_native( &entry_point, &native_compiled_class_v1, state, cheatnet_state, context, ) } } }; context .tracked_resource_stack .pop() .expect("Unexpected empty tracked resource."); match result { Ok(res) => { if res.call_info.execution.failed && !context.versioned_constants().enable_reverts { let err = EntryPointExecutionError::ExecutionFailed { error_trace: extract_trailing_cairo1_revert_trace( &res.call_info, Cairo1RevertHeader::Execution, ), }; exit_error_call(&err, cheatnet_state, &entry_point); return Err(err); } update_remaining_gas(remaining_gas, &res.call_info); update_trace_data( &res.call_info, &res.syscall_usage_vm_resources, &res.syscall_usage_sierra_gas, cheatnet_state, ); if !opts.trace_data_handled_by_revert_call { cheatnet_state.trace_data.exit_nested_call(); } Ok(res.call_info) } Err(EntryPointExecutionError::PreExecutionError(err)) if context.versioned_constants().enable_reverts => { let error_code = match err { PreExecutionError::EntryPointNotFound(_) | PreExecutionError::NoEntryPointOfTypeFound(_) => ENTRYPOINT_NOT_FOUND_ERROR, PreExecutionError::InsufficientEntryPointGas => OUT_OF_GAS_ERROR, _ => return Err(err.into()), }; Ok(CallInfo { call: entry_point.into(), execution: CallExecution { retdata: Retdata(vec![Felt::from_hex(error_code).unwrap()]), failed: true, gas_consumed: 0, ..CallExecution::default() }, tracked_resource: current_tracked_resource, ..CallInfo::default() }) } Err(err) => { exit_error_call(&err, cheatnet_state, &entry_point); Err(err) } } // endregion } // blockifier/src/execution/entry_point (CallEntryPoint::non_reverting_execute) pub fn non_reverting_execute_call_entry_point( entry_point: &mut CallEntryPoint, // Instead of 'self' state: &mut dyn State, cheatnet_state: &mut CheatnetState, context: &mut EntryPointExecutionContext, remaining_gas: &mut u64, ) -> EntryPointExecutionResult { // Region: Modified blockifier code let cheated_data = resolve_cheated_data_for_call(entry_point, cheatnet_state); cheatnet_state .trace_data .enter_nested_call(entry_point.clone(), cheated_data.clone()); // endregion let execution_result = execute_call_entry_point( entry_point, state, cheatnet_state, context, remaining_gas, &ExecuteCallEntryPointExtraOptions { trace_data_handled_by_revert_call: true, }, ); if let Ok(call_info) = &execution_result { // Update revert gas tracking (for completeness - value will not be used unless the tx // is reverted). context .sierra_gas_revert_tracker .update_with_next_remaining_gas(call_info.tracked_resource, GasAmount(*remaining_gas)); // If the execution of the outer call failed, revert the transaction. if call_info.execution.failed { // Region: Modified blockifier code clear_handled_errors(call_info, cheatnet_state); let err = EntryPointExecutionError::ExecutionFailed { error_trace: extract_trailing_cairo1_revert_trace( call_info, Cairo1RevertHeader::Execution, ), }; // Note: Class hash in the entry point below does not matter, as `exit_error_call` does not update it in the trace. exit_error_call( &err, cheatnet_state, &entry_point .clone() .into_executable(entry_point.class_hash.unwrap_or_default()), ); return Err(err); } cheatnet_state.trace_data.exit_nested_call(); // endregion } execution_result } // blockifier/src/execution/entry_point.rs (execute_constructor_entry_point) pub fn execute_constructor_entry_point( state: &mut dyn State, cheatnet_state: &mut CheatnetState, context: &mut EntryPointExecutionContext, ctor_context: &ConstructorContext, calldata: Calldata, remaining_gas: &mut u64, ) -> EntryPointExecutionResult { // Ensure the class is declared (by reading it). let contract_class = state.get_compiled_class(ctor_context.class_hash)?; let Some(constructor_selector) = contract_class.constructor_selector() else { // Contract has no constructor. cheatnet_state .trace_data .add_deploy_without_constructor_node(); return handle_empty_constructor( contract_class, context, ctor_context, calldata, *remaining_gas, ); }; let mut constructor_call = CallEntryPoint { class_hash: None, code_address: ctor_context.code_address, entry_point_type: EntryPointType::Constructor, entry_point_selector: constructor_selector, calldata, storage_address: ctor_context.storage_address, caller_address: ctor_context.caller_address, call_type: CallType::Call, initial_gas: *remaining_gas, }; // region: Modified blockifier code non_reverting_execute_call_entry_point( &mut constructor_call, state, cheatnet_state, context, remaining_gas, ) // endregion } fn get_mocked_function_cheat_status<'a>( call: &CallEntryPoint, cheatnet_state: &'a mut CheatnetState, ) -> Option<&'a mut CheatStatus>> { if call.call_type == CallType::Delegate { return None; } cheatnet_state .mocked_functions .get_mut(&call.storage_address) .and_then(|contract_functions| contract_functions.get_mut(&call.entry_point_selector)) } fn mocked_call_info( call: CallEntryPoint, ret_data: Vec, tracked_resource: TrackedResource, ) -> CallInfo { CallInfo { call: CallEntryPoint { class_hash: Some(call.class_hash.unwrap_or_default()), ..call }, execution: CallExecution { retdata: Retdata(ret_data), events: vec![], l2_to_l1_messages: vec![], cairo_native: false, failed: false, gas_consumed: 0, }, resources: ExtendedExecutionResources::default(), tracked_resource, inner_calls: vec![], storage_access_tracker: StorageAccessTracker::default(), builtin_counters: CairoPrimitiveCounterMap::default(), syscalls_usage: SyscallUsageMap::default(), } } pub(crate) fn extract_trace_and_register_errors( class_hash: ClassHash, runner: &mut CairoRunner, cheatnet_state: &mut CheatnetState, ) { let trace = get_relocated_vm_trace(runner); cheatnet_state .trace_data .set_vm_trace_for_current_call(trace); let pcs = runner.vm.get_reversed_pc_traceback(); cheatnet_state.register_error(class_hash, pcs); } /// This helper function is used for backtrace to avoid displaying errors that were already handled /// It clears the errors for all contracts that failed with a different panic data than the root call /// Note: This may not be accurate if a panic was initially handled and then the function panicked /// again with the identical panic data pub(crate) fn clear_handled_errors(root_call: &CallInfo, cheatnet_state: &mut CheatnetState) { let contracts_matching_root_error = get_contracts_with_matching_error(root_call); cheatnet_state .encountered_errors .clone() .keys() .for_each(|&class_hash| { if !contracts_matching_root_error.contains(&class_hash) { cheatnet_state.clear_error(class_hash); } }); } /// Collects all contracts that have matching error with the root call fn get_contracts_with_matching_error(root_call: &CallInfo) -> HashSet { let mut contracts_matching_root_error = HashSet::new(); let mut failed_matching_calls: Vec<&CallInfo> = vec![root_call]; while let Some(call_info) = failed_matching_calls.pop() { if let Some(class_hash) = call_info.call.class_hash { contracts_matching_root_error.insert(class_hash); failed_matching_calls.extend(get_inner_calls_with_matching_panic_data( call_info, &root_call.execution.retdata.0, )); } } contracts_matching_root_error } fn get_inner_calls_with_matching_panic_data<'a>( call_info: &'a CallInfo, root_retdata: &[Felt], ) -> Vec<&'a CallInfo> { call_info .inner_calls .iter() .filter(|call| call.execution.failed && root_retdata.starts_with(&call.execution.retdata.0)) .collect() } ================================================ FILE: crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/execution_info.rs ================================================ use crate::state::{CheatedData, CheatedTxInfo}; use cairo_vm::{ types::relocatable::{MaybeRelocatable, Relocatable}, vm::vm_core::VirtualMachine, }; use conversions::serde::SerializedValue; use conversions::{IntoConv, serde::serialize::SerializeToFeltVec}; use starknet_types_core::felt::Felt; fn get_cheated_block_info_ptr( vm: &mut VirtualMachine, original_block_info: &[MaybeRelocatable], cheated_data: &CheatedData, ) -> Relocatable { // create a new segment with replaced block info let ptr_cheated_block_info = vm.add_memory_segment(); let mut new_block_info = original_block_info.to_owned(); if let Some(block_number) = cheated_data.block_number { new_block_info[0] = MaybeRelocatable::Int(block_number.into()); } if let Some(block_timestamp) = cheated_data.block_timestamp { new_block_info[1] = MaybeRelocatable::Int(block_timestamp.into()); } if let Some(sequencer_address) = cheated_data.sequencer_address { new_block_info[2] = MaybeRelocatable::Int(sequencer_address.into_()); } vm.load_data(ptr_cheated_block_info, &new_block_info) .unwrap(); ptr_cheated_block_info } fn get_cheated_tx_info_ptr( vm: &mut VirtualMachine, original_tx_info: &[MaybeRelocatable], cheated_data: &CheatedData, ) -> Relocatable { // create a new segment with replaced tx info let ptr_cheated_tx_info = vm.add_memory_segment(); let mut new_tx_info = original_tx_info.to_owned(); let tx_info_mock = cheated_data.tx_info.clone(); let CheatedTxInfo { version, account_contract_address, max_fee, signature, transaction_hash, chain_id, nonce, resource_bounds, tip, paymaster_data, nonce_data_availability_mode, fee_data_availability_mode, account_deployment_data, proof_facts, } = tx_info_mock; if let Some(version) = version { new_tx_info[0] = MaybeRelocatable::Int(version); } if let Some(account_contract_address) = account_contract_address { new_tx_info[1] = MaybeRelocatable::Int(account_contract_address); } if let Some(max_fee) = max_fee { new_tx_info[2] = MaybeRelocatable::Int(max_fee); } if let Some(signature) = signature { let (signature_start_ptr, signature_end_ptr) = add_vec_memory_segment(&signature, vm); new_tx_info[3] = signature_start_ptr.into(); new_tx_info[4] = signature_end_ptr.into(); } if let Some(transaction_hash) = transaction_hash { new_tx_info[5] = MaybeRelocatable::Int(transaction_hash); } if let Some(chain_id) = chain_id { new_tx_info[6] = MaybeRelocatable::Int(chain_id); } if let Some(nonce) = nonce { new_tx_info[7] = MaybeRelocatable::Int(nonce); } if let Some(resource_bounds) = resource_bounds { let (resource_bounds_start_ptr, resource_bounds_end_ptr) = add_vec_memory_segment( &SerializedValue::new(resource_bounds).serialize_to_vec(), vm, ); new_tx_info[8] = resource_bounds_start_ptr.into(); new_tx_info[9] = resource_bounds_end_ptr.into(); } if let Some(tip) = tip { new_tx_info[10] = MaybeRelocatable::Int(tip); } if let Some(paymaster_data) = paymaster_data { let (paymaster_data_start_ptr, paymaster_data_end_ptr) = add_vec_memory_segment(&paymaster_data, vm); new_tx_info[11] = paymaster_data_start_ptr.into(); new_tx_info[12] = paymaster_data_end_ptr.into(); } if let Some(nonce_data_availability_mode) = nonce_data_availability_mode { new_tx_info[13] = MaybeRelocatable::Int(nonce_data_availability_mode); } if let Some(fee_data_availability_mode) = fee_data_availability_mode { new_tx_info[14] = MaybeRelocatable::Int(fee_data_availability_mode); } if let Some(account_deployment_data) = account_deployment_data { let (account_deployment_data_start_ptr, account_deployment_data_end_ptr) = add_vec_memory_segment(&account_deployment_data, vm); new_tx_info[15] = account_deployment_data_start_ptr.into(); new_tx_info[16] = account_deployment_data_end_ptr.into(); } if let Some(proof_facts) = proof_facts { let (proof_facts_start_ptr, proof_facts_end_ptr) = add_vec_memory_segment(&proof_facts, vm); new_tx_info[17] = proof_facts_start_ptr.into(); new_tx_info[18] = proof_facts_end_ptr.into(); } vm.load_data(ptr_cheated_tx_info, &new_tx_info).unwrap(); ptr_cheated_tx_info } pub fn get_cheated_exec_info_ptr( vm: &mut VirtualMachine, execution_info_ptr: Relocatable, cheated_data: &CheatedData, ) -> Relocatable { let ptr_cheated_exec_info = vm.add_memory_segment(); // Initialize as old exec_info let mut new_exec_info = vm.get_continuous_range(execution_info_ptr, 5).unwrap(); if cheated_data.block_number.is_some() || cheated_data.block_timestamp.is_some() || cheated_data.sequencer_address.is_some() { let data = vm.get_range(execution_info_ptr, 1)[0].clone(); if let MaybeRelocatable::RelocatableValue(block_info_ptr) = data.unwrap().into_owned() { let original_block_info = vm.get_continuous_range(block_info_ptr, 3).unwrap(); let ptr_cheated_block_info = get_cheated_block_info_ptr(vm, &original_block_info, cheated_data); new_exec_info[0] = MaybeRelocatable::RelocatableValue(ptr_cheated_block_info); } } if cheated_data.tx_info.is_mocked() { let data = vm.get_range(execution_info_ptr, 2)[1].clone(); if let MaybeRelocatable::RelocatableValue(tx_info_ptr) = data.unwrap().into_owned() { let original_tx_info = vm.get_continuous_range(tx_info_ptr, 19).unwrap(); let ptr_cheated_tx_info = get_cheated_tx_info_ptr(vm, &original_tx_info, cheated_data); new_exec_info[1] = MaybeRelocatable::RelocatableValue(ptr_cheated_tx_info); } } if let Some(caller_address) = cheated_data.caller_address { new_exec_info[2] = MaybeRelocatable::Int(caller_address.into_()); } if let Some(contract_address) = cheated_data.contract_address { new_exec_info[3] = MaybeRelocatable::Int(contract_address.into_()); } vm.load_data(ptr_cheated_exec_info, &new_exec_info).unwrap(); ptr_cheated_exec_info } fn add_vec_memory_segment(vector: &[Felt], vm: &mut VirtualMachine) -> (Relocatable, Relocatable) { let vector_len = vector.len(); let vector_start_ptr = vm.add_memory_segment(); let vector_end_ptr = (vector_start_ptr + vector_len).unwrap(); let vector: Vec = vector.iter().map(MaybeRelocatable::from).collect(); vm.load_data(vector_start_ptr, &vector).unwrap(); (vector_start_ptr, vector_end_ptr) } ================================================ FILE: crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/execution_utils.rs ================================================ use crate::runtime_extensions::call_to_blockifier_runtime_extension::rpc::{ AddressOrClassHash, from_error, from_non_error, }; use crate::runtime_extensions::common::sum_syscall_usage; use crate::runtime_extensions::forge_runtime_extension::{ get_nested_calls_syscalls_sierra_gas, get_nested_calls_syscalls_vm_resources, }; use crate::state::{CheatedData, CheatnetState}; use blockifier::execution::call_info::CallInfo; use blockifier::execution::entry_point::{CallEntryPoint, CallType, ExecutableCallEntryPoint}; use blockifier::execution::errors::EntryPointExecutionError; use blockifier::execution::syscalls::vm_syscall_utils::SyscallUsageMap; pub(crate) fn resolve_cheated_data_for_call( entry_point: &mut CallEntryPoint, cheatnet_state: &mut CheatnetState, ) -> CheatedData { if let CallType::Delegate = entry_point.call_type { // When a delegate call is made directly by a test contract, `top_cheated_data()` will have a default value (all fields set to `None`). // Therefore, we need to get cheated data for the current contract, which is the test contract. if cheatnet_state.trace_data.current_call_stack.size() == 1 { cheatnet_state.get_cheated_data(entry_point.storage_address) } else { cheatnet_state .trace_data .current_call_stack .top_cheated_data() .clone() } } else { let contract_address = entry_point.storage_address; let cheated_data = cheatnet_state.create_cheated_data(contract_address); cheatnet_state.update_cheats(&contract_address); cheated_data } } pub(crate) fn update_trace_data( call_info: &CallInfo, syscall_usage_vm_resources: &SyscallUsageMap, syscall_usage_sierra_gas: &SyscallUsageMap, cheatnet_state: &mut CheatnetState, ) { let nested_syscall_usage_vm_resources = get_nested_calls_syscalls_vm_resources(&cheatnet_state.trace_data.current_call_stack.top()); let nested_syscall_usage_sierra_gas = get_nested_calls_syscalls_sierra_gas(&cheatnet_state.trace_data.current_call_stack.top()); let syscall_usage_vm_resources = sum_syscall_usage( nested_syscall_usage_vm_resources, syscall_usage_vm_resources, ); let syscall_usage_sierra_gas = sum_syscall_usage(nested_syscall_usage_sierra_gas, syscall_usage_sierra_gas); let signature = cheatnet_state .get_cheated_data(call_info.call.storage_address) .tx_info .signature .unwrap_or_default(); cheatnet_state.trace_data.update_current_call( call_info.resources.clone(), call_info.execution.gas_consumed, syscall_usage_vm_resources, syscall_usage_sierra_gas, from_non_error(call_info), &call_info.execution.l2_to_l1_messages, signature, call_info.execution.events.clone(), ); } /// Clears `events` and `l2_to_l1_messages` from a reverted call and all its inner calls that did not fail. /// This is part of `execute_inner_call` function in Blockifier. /// pub(crate) fn clear_events_and_messages_from_reverted_call(reverted_call: &mut CallInfo) { let mut stack: Vec<&mut CallInfo> = vec![reverted_call]; while let Some(call_info) = stack.pop() { call_info.execution.events.clear(); call_info.execution.l2_to_l1_messages.clear(); // Add inner calls that did not fail to the stack. // The events and l2_to_l1_messages of the failed calls were already cleared. stack.extend( call_info .inner_calls .iter_mut() .filter(|call_info| !call_info.execution.failed), ); } } pub(crate) fn exit_error_call( error: &EntryPointExecutionError, cheatnet_state: &mut CheatnetState, entry_point: &ExecutableCallEntryPoint, ) { let identifier = match entry_point.call_type { CallType::Call => AddressOrClassHash::ContractAddress(entry_point.storage_address), CallType::Delegate => AddressOrClassHash::ClassHash(entry_point.class_hash), }; let trace_data = &mut cheatnet_state.trace_data; // In case of a revert, clear all events and messages emitted by the current call. trace_data.clear_current_call_events_and_messages(); trace_data.update_current_call_result(from_error(error, &identifier)); trace_data.exit_nested_call(); } ================================================ FILE: crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/mod.rs ================================================ pub mod cairo1_execution; pub mod calls; pub mod cheated_syscalls; pub mod deprecated; pub mod entry_point; pub mod execution_info; pub mod execution_utils; pub mod syscall_hooks; ================================================ FILE: crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/syscall_hooks.rs ================================================ use crate::runtime_extensions::forge_runtime_extension::cheatcodes::spy_messages_to_l1::MessageToL1; use crate::{ runtime_extensions::forge_runtime_extension::cheatcodes::spy_events::Event, state::CheatnetState, }; use blockifier::execution::call_info::OrderedL2ToL1Message; use blockifier::execution::{ call_info::OrderedEvent, deprecated_syscalls::hint_processor::DeprecatedSyscallHintProcessor, syscalls::hint_processor::SyscallHintProcessor, }; use starknet_api::core::ContractAddress; pub trait SyscallHintProcessorExt { fn contract_address(&self) -> ContractAddress; fn last_event(&self) -> &OrderedEvent; fn last_l2_to_l1_message(&self) -> &OrderedL2ToL1Message; } impl SyscallHintProcessorExt for SyscallHintProcessor<'_> { fn contract_address(&self) -> ContractAddress { self.base .call .code_address .unwrap_or(self.base.call.storage_address) } fn last_event(&self) -> &OrderedEvent { self.base.events.last().unwrap() } fn last_l2_to_l1_message(&self) -> &OrderedL2ToL1Message { self.base.l2_to_l1_messages.last().unwrap() } } impl SyscallHintProcessorExt for DeprecatedSyscallHintProcessor<'_> { fn contract_address(&self) -> ContractAddress { self.storage_address } fn last_event(&self) -> &OrderedEvent { self.events.last().unwrap() } fn last_l2_to_l1_message(&self) -> &OrderedL2ToL1Message { self.l2_to_l1_messages.last().unwrap() } } pub fn emit_event_hook( syscall_handler: &impl SyscallHintProcessorExt, cheatnet_state: &mut CheatnetState, ) { let contract_address = syscall_handler.contract_address(); let last_event = syscall_handler.last_event(); cheatnet_state .detected_events .push(Event::from_ordered_event(last_event, contract_address)); } pub fn send_message_to_l1_syscall_hook( syscall_handler: &impl SyscallHintProcessorExt, cheatnet_state: &mut CheatnetState, ) { let contract_address = syscall_handler.contract_address(); let last_message = syscall_handler.last_l2_to_l1_message(); cheatnet_state .detected_messages_to_l1 .push(MessageToL1::from_ordered_message( last_message, contract_address, )); } ================================================ FILE: crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/mod.rs ================================================ use std::marker::PhantomData; use crate::state::CheatnetState; use blockifier::execution::entry_point::{CallEntryPoint, CallType}; use blockifier::execution::syscalls::hint_processor::{ OUT_OF_GAS_ERROR, SyscallHintProcessor, create_retdata_segment, }; use blockifier::execution::syscalls::syscall_base::SyscallResult; use blockifier::execution::syscalls::syscall_executor::SyscallExecutor; use blockifier::execution::syscalls::vm_syscall_utils::{ CallContractRequest, CallContractResponse, LibraryCallRequest, LibraryCallResponse, RevertData, SelfOrRevert, SingleSegmentResponse, SyscallExecutorBaseError, SyscallRequestWrapper, SyscallSelector, }; use blockifier::execution::syscalls::vm_syscall_utils::{ SyscallRequest, SyscallResponse, SyscallResponseWrapper, }; use blockifier::utils::u64_from_usize; use cairo_vm::vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}; use runtime::{ExtendedRuntime, ExtensionLogic, SyscallHandlingResult}; use starknet_api::contract_class::EntryPointType; use starknet_api::core::ContractAddress; use starknet_api::execution_resources::GasAmount; use starknet_types_core::felt::Felt; use crate::runtime_extensions::call_to_blockifier_runtime_extension::rpc::{ AddressOrClassHash, call_entry_point, }; use super::cheatable_starknet_runtime_extension::{ CheatableStarknetRuntime, CheatableStarknetRuntimeError, }; use conversions::string::TryFromHexStr; use runtime::starknet::constants::TEST_ADDRESS; pub mod execution; pub mod panic_parser; pub mod rpc; pub struct CallToBlockifierExtension<'a> { pub lifetime: &'a PhantomData<()>, } pub type CallToBlockifierRuntime<'a> = ExtendedRuntime>; impl<'a> ExtensionLogic for CallToBlockifierExtension<'a> { type Runtime = CheatableStarknetRuntime<'a>; fn override_system_call( &mut self, selector: SyscallSelector, vm: &mut VirtualMachine, extended_runtime: &mut Self::Runtime, ) -> Result { // Warning: Do not add a default (`_`) arm here. // This match must remain exhaustive so that if a new syscall is introduced, // we will explicitly add support for it. match selector { // We execute contract calls and library calls with modified blockifier // This is redirected to drop ForgeRuntimeExtension // and to enable executing outer calls in tests as non-revertible. SyscallSelector::CallContract => { self.execute_syscall(vm, call_contract_syscall, selector, extended_runtime)?; Ok(SyscallHandlingResult::Handled) } SyscallSelector::LibraryCall => { self.execute_syscall(vm, library_call_syscall, selector, extended_runtime)?; Ok(SyscallHandlingResult::Handled) } SyscallSelector::DelegateCall | SyscallSelector::DelegateL1Handler | SyscallSelector::Deploy | SyscallSelector::EmitEvent | SyscallSelector::GetBlockHash | SyscallSelector::GetBlockNumber | SyscallSelector::GetBlockTimestamp | SyscallSelector::GetCallerAddress | SyscallSelector::GetClassHashAt | SyscallSelector::GetContractAddress | SyscallSelector::GetExecutionInfo | SyscallSelector::GetSequencerAddress | SyscallSelector::GetTxInfo | SyscallSelector::GetTxSignature | SyscallSelector::Keccak | SyscallSelector::KeccakRound | SyscallSelector::Sha256ProcessBlock | SyscallSelector::LibraryCallL1Handler | SyscallSelector::MetaTxV0 | SyscallSelector::ReplaceClass | SyscallSelector::Secp256k1Add | SyscallSelector::Secp256k1GetPointFromX | SyscallSelector::Secp256k1GetXy | SyscallSelector::Secp256k1Mul | SyscallSelector::Secp256k1New | SyscallSelector::Secp256r1Add | SyscallSelector::Secp256r1GetPointFromX | SyscallSelector::Secp256r1GetXy | SyscallSelector::Secp256r1Mul | SyscallSelector::Secp256r1New | SyscallSelector::SendMessageToL1 | SyscallSelector::StorageRead | SyscallSelector::StorageWrite => Ok(SyscallHandlingResult::Forwarded), } } } fn call_contract_syscall( request: CallContractRequest, vm: &mut VirtualMachine, syscall_handler: &mut SyscallHintProcessor, cheatnet_state: &mut CheatnetState, remaining_gas: &mut u64, ) -> SyscallResult { let contract_address = request.contract_address; let entry_point = CallEntryPoint { class_hash: None, code_address: Some(contract_address), entry_point_type: EntryPointType::External, entry_point_selector: request.function_selector, calldata: request.calldata, storage_address: contract_address, caller_address: TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap(), call_type: CallType::Call, initial_gas: *remaining_gas, }; let res = call_entry_point( syscall_handler, cheatnet_state, entry_point, &AddressOrClassHash::ContractAddress(contract_address), remaining_gas, )?; let segment = create_retdata_segment(vm, syscall_handler, &res.ret_data)?; Ok(CallContractResponse { segment }) } fn library_call_syscall( request: LibraryCallRequest, vm: &mut VirtualMachine, syscall_handler: &mut SyscallHintProcessor, cheatnet_state: &mut CheatnetState, remaining_gas: &mut u64, ) -> SyscallResult { let class_hash = request.class_hash; let entry_point = CallEntryPoint { class_hash: Some(class_hash), code_address: None, entry_point_type: EntryPointType::External, entry_point_selector: request.function_selector, calldata: request.calldata, storage_address: TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap(), caller_address: ContractAddress::default(), call_type: CallType::Delegate, initial_gas: *remaining_gas, }; let res = call_entry_point( syscall_handler, cheatnet_state, entry_point, &AddressOrClassHash::ClassHash(class_hash), remaining_gas, )?; let segment = create_retdata_segment(vm, syscall_handler, &res.ret_data)?; Ok(LibraryCallResponse { segment }) } impl CallToBlockifierExtension<'_> { // crates/blockifier/src/execution/syscalls/vm_syscall_utils.rs:677 (execute_syscall) #[expect(clippy::unused_self)] fn execute_syscall( &mut self, vm: &mut VirtualMachine, execute_callback: ExecuteCallback, selector: SyscallSelector, extended_runtime: &mut CheatableStarknetRuntime, ) -> Result<(), Error> where Request: SyscallRequest + std::fmt::Debug, Response: SyscallResponse + std::fmt::Debug, Error: CheatableStarknetRuntimeError, ExecuteCallback: FnOnce( Request, &mut VirtualMachine, &mut SyscallHintProcessor<'_>, &mut CheatnetState, &mut u64, // Remaining gas. ) -> Result, { // region: Modified blockifier code let syscall_handler = &mut extended_runtime.extended_runtime.hint_handler; let cheatnet_state = &mut *extended_runtime.extension.cheatnet_state; // Increment, since the selector was peeked into before syscall_handler.syscall_ptr += 1; syscall_handler.increment_syscall_count_by(&selector, 1); // endregion let syscall_gas_cost = syscall_handler .get_gas_cost_from_selector(&selector) .map_err(|error| SyscallExecutorBaseError::GasCost { error, selector })?; let SyscallRequestWrapper { gas_counter, request, } = SyscallRequestWrapper::::read(vm, syscall_handler.get_mut_syscall_ptr())?; let syscall_gas_cost = syscall_gas_cost.get_syscall_cost(u64_from_usize(request.get_linear_factor_length())); let syscall_base_cost = syscall_handler.get_syscall_base_gas_cost(); // Sanity check for preventing underflow. assert!( syscall_gas_cost >= syscall_base_cost, "Syscall gas cost must be greater than base syscall gas cost" ); // Refund `SYSCALL_BASE_GAS_COST` as it was pre-charged. // Note: It is pre-charged by the compiler: https://github.com/starkware-libs/sequencer/blob/v0.15.0-rc.2/crates/blockifier/src/blockifier_versioned_constants.rs#L1057 let required_gas = syscall_gas_cost - syscall_base_cost; if gas_counter < required_gas { let out_of_gas_error = Felt::from_hex(OUT_OF_GAS_ERROR).map_err(SyscallExecutorBaseError::from)?; let response: SyscallResponseWrapper = SyscallResponseWrapper::Failure { gas_counter, revert_data: RevertData::new_normal(vec![out_of_gas_error]), }; response.write(vm, syscall_handler.get_mut_syscall_ptr())?; return Ok(()); } let mut remaining_gas = gas_counter - required_gas; // TODO(#3681) syscall_handler.update_revert_gas_with_next_remaining_gas(GasAmount(remaining_gas)); let original_response = execute_callback( request, vm, syscall_handler, cheatnet_state, &mut remaining_gas, ); let response = match original_response { Ok(response) => SyscallResponseWrapper::Success { gas_counter: remaining_gas, response, }, Err(error) => match error.try_extract_revert() { SelfOrRevert::Revert(data) => SyscallResponseWrapper::Failure { gas_counter: remaining_gas, revert_data: data, }, SelfOrRevert::Original(err) => return Err(err), }, }; response.write(vm, &mut syscall_handler.syscall_ptr)?; Ok(()) } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/panic_parser.rs ================================================ use conversions::byte_array::ByteArray; use regex::Regex; use starknet_types_core::felt::Felt; use std::sync::LazyLock; // Regex used to extract panic data from panicking proxy contract static RE_PROXY_PREFIX: LazyLock = LazyLock::new(|| { Regex::new(r"[\s\S]*Execution failed\. Failure reason:\nError in contract \(.+\):\n([\s\S]*)\.") .unwrap() }); static RE_HEX: LazyLock = LazyLock::new(|| Regex::new("0x[0-9a-fA-F]+").unwrap()); // CairoVM returns felts padded to 64 characters after 0x, unlike the spec's 63. // This regex (0x[a-fA-F0-9]{0,64}) handles the padded form and is different from the spec. static RE_ENTRYPOINT: LazyLock = LazyLock::new(|| { Regex::new(r"Entry point EntryPointSelector\((0x[a-fA-F0-9]{0,64})\) not found in contract\.") .unwrap() }); enum PanicDataFormat { ByteArray(Vec), Felts(Vec), EntryPoint(Vec), } impl From for Vec { fn from(value: PanicDataFormat) -> Self { match value { PanicDataFormat::ByteArray(v) | PanicDataFormat::Felts(v) | PanicDataFormat::EntryPoint(v) => v, } } } fn parse_byte_array(s: &str) -> Option { if !s.starts_with('\"') { return None; } let inner = s.trim_matches('"'); let felts = ByteArray::from(inner).serialize_with_magic(); Some(PanicDataFormat::ByteArray(felts)) } fn parse_felts(s: &str) -> Option { // Matches `panic_data` when a proxy contract panics, either: // - with a single Felt "0x" // - with an array of Felts "(" // The difference comes from the `format_panic_data` implementation in `blockifier`. // https://github.com/starkware-libs/sequencer/blob/8211fbf1e2660884c4a9e67ddd93680495afde12/crates/starknet_api/src/execution_utils.rs if !(s.starts_with("0x") || s.starts_with('(')) { return None; } let felts: Vec = RE_HEX .find_iter(s) .map(|m| Felt::from_hex(m.as_str()).expect("Invalid felt in panic data")) .collect(); Some(PanicDataFormat::Felts(felts)) } fn parse_entrypoint(s: &str) -> Option { // These felts were chosen from `CairoHintProcessor` in order to be consistent with `cairo-test`: // https://github.com/starkware-libs/cairo/blob/2ad7718591a8d2896fec2b435c509ee5a3da9fad/crates/cairo-lang-runner/src/casm_run/mod.rs#L1055-L1057 if RE_ENTRYPOINT.captures(s).is_some() { return Some(PanicDataFormat::EntryPoint(vec![ Felt::from_bytes_be_slice("ENTRYPOINT_NOT_FOUND".as_bytes()), Felt::from_bytes_be_slice("ENTRYPOINT_FAILED".as_bytes()), ])); } None } /// Tries to extract panic data from a raw Starknet error string. pub fn try_extract_panic_data(err: &str) -> Option> { let captures = RE_PROXY_PREFIX.captures(err)?; let raw = captures.get(1)?.as_str(); parse_byte_array(raw) .or_else(|| parse_felts(raw)) .or_else(|| parse_entrypoint(err)) .map(Into::into) } #[cfg(test)] mod test { use super::*; use cairo_lang_utils::byte_array::BYTE_ARRAY_MAGIC; use conversions::{felt::FromShortString, string::TryFromHexStr}; use indoc::indoc; use starknet_types_core::felt::Felt; use test_case::test_case; #[test_case(indoc!(r" Error at pc=0:366: Got an exception while executing a hint: Execution failed. Failure reason: Error in contract (contract address: 0x0033be52b9269700771b680f5905b305864f46e78bfbe79428f4bf7a933fb02f, class hash: 0x031b4bdf7360269d8bc059935f2e44d3ad487cbb781ff57527fd4b5ec13bf659, selector: 0x032e90fe8c4355e4732f08747d73146ef03dcd019ec3498c089dce91cf40aadc): 0x1. " ), Some(&vec![Felt::from(1)]); "non ascii felt")] #[test_case(indoc!(r" Error at pc=0:366: Got an exception while executing a hint: Execution failed. Failure reason: Error in contract (contract address: 0x0033be52b9269700771b680f5905b305864f46e78bfbe79428f4bf7a933fb02f, class hash: 0x031b4bdf7360269d8bc059935f2e44d3ad487cbb781ff57527fd4b5ec13bf659, selector: 0x032e90fe8c4355e4732f08747d73146ef03dcd019ec3498c089dce91cf40aadc): 0x41 ('A'). " ), Some(&vec![Felt::from(65)]); "ascii felt")] #[test_case(indoc!(r" Error at pc=0:366: Got an exception while executing a hint: Execution failed. Failure reason: Error in contract (contract address: 0x0033be52b9269700771b680f5905b305864f46e78bfbe79428f4bf7a933fb02f, class hash: 0x031b4bdf7360269d8bc059935f2e44d3ad487cbb781ff57527fd4b5ec13bf659, selector: 0x032e90fe8c4355e4732f08747d73146ef03dcd019ec3498c089dce91cf40aadc): (0x1, 0x41 ('A'), 0x2, 0x42 ('B')). " ), Some(&vec![Felt::from(1), Felt::from(65), Felt::from(2), Felt::from(66)]); "mixed felts")] #[test_case(indoc!(r" Got an exception while executing a hint: Execution failed. Failure reason: Error in contract (contract address: 0x03cda836debfed3f83aa981d7a31733da3ae4f903dde9d833509d2f985d52241, class hash: 0x07ca8b953cb041ee517951d34880631e537682103870b9b018a7b493363b9b63, selector: 0x00a4695e9e8c278609a8e9362d5abe9852a904da970c7de84f0456c777d21137): (0x54687265652073616420746967657273206174652077686561742e2054776f ('Three sad tigers ate wheat. Two'), 0x2074696765727320776572652066756c6c2e20546865206f74686572207469 (' tigers were full. The other ti'), 0x676572206e6f7420736f206d756368 ('ger not so much')). " ), Some(&vec![Felt::from_hex_unchecked("0x54687265652073616420746967657273206174652077686561742e2054776f"), Felt::from_hex_unchecked("0x2074696765727320776572652066756c6c2e20546865206f74686572207469"), Felt::from_hex_unchecked("0x676572206e6f7420736f206d756368")]); "felt array")] fn extracting_plain_panic_data(data: &str, expected: Option<&Vec>) { assert_eq!(try_extract_panic_data(data), expected.cloned()); } #[allow(clippy::needless_pass_by_value)] #[test_case(indoc!( r#" Error at pc=0:107: Got an exception while executing a hint: Execution failed. Failure reason: Error in contract (contract address: 0x03cda836debfed3f83aa981d7a31733da3ae4f903dde9d833509d2f985d52241, class hash: 0x07ca8b953cb041ee517951d34880631e537682103870b9b018a7b493363b9b63, selector: 0x00a4695e9e8c278609a8e9362d5abe9852a904da970c7de84f0456c777d21137): "wow message is exactly 31 chars". "# ), Some(vec![ Felt::try_from_hex_str(&format!("0x{BYTE_ARRAY_MAGIC}")).unwrap(), Felt::from(1), Felt::from_short_string("wow message is exactly 31 chars").unwrap(), Felt::from(0), Felt::from(0), ]); "exactly 31 chars" )] #[test_case(indoc!( r#" Error at pc=0:107: Got an exception while executing a hint: Execution failed. Failure reason: Error in contract (contract address: 0x03cda836debfed3f83aa981d7a31733da3ae4f903dde9d833509d2f985d52241, class hash: 0x07ca8b953cb041ee517951d34880631e537682103870b9b018a7b493363b9b63, selector: 0x00a4695e9e8c278609a8e9362d5abe9852a904da970c7de84f0456c777d21137): "". "# ), Some(vec![ Felt::try_from_hex_str(&format!("0x{BYTE_ARRAY_MAGIC}")).unwrap(), Felt::from(0), Felt::from(0), Felt::from(0), ]); "empty string" )] #[test_case(indoc!( r#" Error at pc=0:107: Got an exception while executing a hint: Execution failed. Failure reason: Error in contract (contract address: 0x03cda836debfed3f83aa981d7a31733da3ae4f903dde9d833509d2f985d52241, class hash: 0x07ca8b953cb041ee517951d34880631e537682103870b9b018a7b493363b9b63, selector: 0x00a4695e9e8c278609a8e9362d5abe9852a904da970c7de84f0456c777d21137): "A very long and multiline thing is also being parsed, and can also can be very long as you can see". "# ), Some(vec![ Felt::try_from_hex_str(&format!("0x{BYTE_ARRAY_MAGIC}")).unwrap(), Felt::from(3), Felt::from_short_string("A very long and multiline\nthing").unwrap(), Felt::from_short_string(" is also being parsed, and can\n").unwrap(), Felt::from_short_string("also can be very long as you ca").unwrap(), Felt::from_short_string("n see").unwrap(), Felt::from(5), ]); "long string" )] #[test_case("Custom Hint Error: Invalid trace: \"PANIC DATA\"", None; "invalid")] fn extracting_string_panic_data(data: &str, expected: Option>) { assert_eq!(try_extract_panic_data(data), expected); } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/rpc.rs ================================================ use super::CheatnetState; use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::{ ExecuteCallEntryPointExtraOptions, clear_handled_errors, execute_call_entry_point, }; use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::execution_utils::clear_events_and_messages_from_reverted_call; use crate::runtime_extensions::{ call_to_blockifier_runtime_extension::panic_parser::try_extract_panic_data, common::create_execute_calldata, }; use blockifier::execution::call_info::{CallExecution, ExecutionSummary, Retdata}; use blockifier::execution::contract_class::TrackedResource; use blockifier::execution::syscalls::hint_processor::{ ENTRYPOINT_FAILED_ERROR_FELT, SyscallExecutionError, }; use blockifier::execution::syscalls::vm_syscall_utils::SyscallExecutorBaseError; use blockifier::execution::{ call_info::CallInfo, entry_point::CallType, errors::{EntryPointExecutionError, PreExecutionError}, syscalls::hint_processor::SyscallHintProcessor, }; use blockifier::execution::{ entry_point::CallEntryPoint, syscalls::vm_syscall_utils::SyscallUsageMap, }; use blockifier::state::errors::StateError; use cairo_vm::vm::errors::hint_errors::HintError; use conversions::{byte_array::ByteArray, serde::serialize::CairoSerialize, string::IntoHexStr}; use shared::utils::build_readable_text; use starknet_api::core::EntryPointSelector; use starknet_api::{ contract_class::EntryPointType, core::{ClassHash, ContractAddress}, }; use starknet_types_core::felt::Felt; #[derive(Clone, Debug, Default)] pub struct UsedResources { pub syscall_usage: SyscallUsageMap, pub execution_summary: ExecutionSummary, pub l1_handler_payload_lengths: Vec, } #[derive(Debug, CairoSerialize)] pub struct CallSuccess { pub ret_data: Vec, } impl From for SyscallExecutionError { fn from(value: CallFailure) -> Self { match value { CallFailure::Recoverable { panic_data } => Self::Revert { error_data: panic_data, }, // TODO(#3307): // `SyscallExecutorBaseError::Hint` is chosen arbitrary to enable conversion by `try_extract_revert` // in `execute_syscall` function. // Ideally, we should pass the actual received error instead of a string. CallFailure::Unrecoverable { msg } => Self::SyscallExecutorBase( SyscallExecutorBaseError::Hint(HintError::CustomHint(Box::from(msg.to_string()))), ), } } } pub type CallResult = Result; /// Enum representing a possible call failure and its type. /// `Recoverable` - Meant to be caught by the user. /// `Unrecoverable` - Equivalent of `panic!` in rust. #[derive(Debug, Clone, CairoSerialize)] pub enum CallFailure { Recoverable { panic_data: Vec }, Unrecoverable { msg: ByteArray }, } pub enum AddressOrClassHash { ContractAddress(ContractAddress), ClassHash(ClassHash), } impl CallFailure { /// Maps blockifier-type error, to one that can be put into memory as panic-data (or re-raised) #[must_use] pub fn from_execution_error( err: &EntryPointExecutionError, starknet_identifier: &AddressOrClassHash, ) -> Self { match err { EntryPointExecutionError::ExecutionFailed { error_trace } => { let err_data = error_trace.last_retdata.clone().0; let err_data_str = build_readable_text(err_data.as_slice()).unwrap_or_default(); if err_data_str.contains("Failed to deserialize param #") || err_data_str.contains("Input too long for arguments") { CallFailure::Unrecoverable { msg: ByteArray::from(err_data_str.as_str()), } } else { CallFailure::Recoverable { panic_data: err_data, } } } EntryPointExecutionError::PreExecutionError(PreExecutionError::EntryPointNotFound( selector, )) => { let selector_hash = selector.into_hex_string(); let msg = match starknet_identifier { AddressOrClassHash::ContractAddress(address) => format!( "Entry point selector {selector_hash} not found in contract {}", address.into_hex_string() ), AddressOrClassHash::ClassHash(class_hash) => format!( "Entry point selector {selector_hash} not found for class hash {}", class_hash.into_hex_string() ), }; let panic_data_felts = ByteArray::from(msg.as_str()).serialize_with_magic(); CallFailure::Recoverable { panic_data: panic_data_felts, } } EntryPointExecutionError::PreExecutionError( PreExecutionError::UninitializedStorageAddress(contract_address), ) => { let address_str = contract_address.into_hex_string(); let msg = format!("Contract not deployed at address: {address_str}"); let panic_data_felts = ByteArray::from(msg.as_str()).serialize_with_magic(); CallFailure::Recoverable { panic_data: panic_data_felts, } } EntryPointExecutionError::StateError(StateError::StateReadError(msg)) => { CallFailure::Unrecoverable { msg: ByteArray::from(msg.as_str()), } } error => { let error_string = error.to_string(); if let Some(panic_data) = try_extract_panic_data(&error_string) { CallFailure::Recoverable { panic_data } } else { CallFailure::Unrecoverable { msg: ByteArray::from(error_string.as_str()), } } } } } } pub fn from_non_error(call_info: &CallInfo) -> Result { let return_data = &call_info.execution.retdata.0; if call_info.execution.failed { return Err(CallFailure::Recoverable { panic_data: return_data.clone(), }); } Ok(CallSuccess { ret_data: return_data.clone(), }) } pub fn from_error( err: &EntryPointExecutionError, starknet_identifier: &AddressOrClassHash, ) -> Result { Err(CallFailure::from_execution_error(err, starknet_identifier)) } pub fn call_l1_handler( syscall_handler: &mut SyscallHintProcessor, cheatnet_state: &mut CheatnetState, contract_address: &ContractAddress, entry_point_selector: EntryPointSelector, calldata: &[Felt], ) -> Result { let calldata = create_execute_calldata(calldata); let mut remaining_gas = i64::MAX as u64; let entry_point = CallEntryPoint { class_hash: None, code_address: Some(*contract_address), entry_point_type: EntryPointType::L1Handler, entry_point_selector, calldata, storage_address: *contract_address, caller_address: ContractAddress::default(), call_type: CallType::Call, initial_gas: remaining_gas, }; call_entry_point( syscall_handler, cheatnet_state, entry_point, &AddressOrClassHash::ContractAddress(*contract_address), &mut remaining_gas, ) } pub fn call_entry_point( syscall_handler: &mut SyscallHintProcessor, cheatnet_state: &mut CheatnetState, mut entry_point: CallEntryPoint, starknet_identifier: &AddressOrClassHash, remaining_gas: &mut u64, ) -> Result { let revert_idx = syscall_handler.base.context.revert_infos.0.len(); let result = execute_call_entry_point( &mut entry_point, syscall_handler.base.state, cheatnet_state, syscall_handler.base.context, remaining_gas, &ExecuteCallEntryPointExtraOptions { trace_data_handled_by_revert_call: false, }, ) .map_err(|err| CallFailure::from_execution_error(&err, starknet_identifier)); let call_info = match result { Ok(call_info) => call_info, Err(CallFailure::Recoverable { panic_data }) => { build_failed_call_info(syscall_handler, cheatnet_state, entry_point, &panic_data) } Err(err) => { return Err(err); } }; let mut raw_retdata = call_info.execution.retdata.0.clone(); let failed = call_info.execution.failed; syscall_handler.base.inner_calls.push(call_info.clone()); if failed { clear_handled_errors(&call_info, cheatnet_state); syscall_handler .base .context .revert(revert_idx, syscall_handler.base.state) .expect("Failed to revert state"); // Delete events and l2_to_l1_messages from the reverted call. let reverted_call = syscall_handler.base.inner_calls.last_mut().unwrap(); clear_events_and_messages_from_reverted_call(reverted_call); raw_retdata.push(ENTRYPOINT_FAILED_ERROR_FELT); return Err(CallFailure::Recoverable { panic_data: raw_retdata, }); } Ok(CallSuccess { ret_data: raw_retdata, }) } fn build_failed_call_info( syscall_handler: &mut SyscallHintProcessor, cheatnet_state: &CheatnetState, entry_point: CallEntryPoint, panic_data: &[Felt], ) -> CallInfo { let storage_class_hash = syscall_handler .base .state .get_class_hash_at(entry_point.storage_address) .expect("There should be a class hash at the storage address"); let maybe_replacement_class = cheatnet_state .replaced_bytecode_contracts .get(&entry_point.storage_address) .copied(); let class_hash = entry_point .class_hash .or(maybe_replacement_class) .unwrap_or(storage_class_hash); let current_tracked_resource = syscall_handler .base .state .get_compiled_class(class_hash) .map_or(TrackedResource::SierraGas, |compiled_class| { compiled_class.get_current_tracked_resource(syscall_handler.base.context) }); CallInfo { call: entry_point.into_executable(class_hash).into(), execution: CallExecution { retdata: Retdata(panic_data.to_vec()), failed: true, ..CallExecution::default() }, tracked_resource: current_tracked_resource, ..CallInfo::default() } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/cheatable_starknet_runtime_extension.rs ================================================ use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::{ cheated_syscalls, syscall_hooks, }; use crate::state::CheatnetState; use anyhow::Result; use blockifier::execution::syscalls::hint_processor::OUT_OF_GAS_ERROR; use blockifier::execution::syscalls::hint_processor::SyscallHintProcessor; use blockifier::execution::syscalls::syscall_executor::SyscallExecutor; use blockifier::execution::syscalls::vm_syscall_utils::{ RevertData, SelfOrRevert, SyscallExecutorBaseError, SyscallRequest, SyscallRequestWrapper, SyscallResponse, SyscallResponseWrapper, SyscallSelector, TryExtractRevert, }; use blockifier::utils::u64_from_usize; use cairo_vm::{ types::relocatable::Relocatable, vm::{ errors::{hint_errors::HintError, vm_errors::VirtualMachineError}, vm_core::VirtualMachine, }, }; use runtime::{ExtendedRuntime, ExtensionLogic, StarknetRuntime, SyscallHandlingResult}; use starknet_api::execution_resources::GasAmount; use starknet_types_core::felt::Felt; pub struct CheatableStarknetRuntimeExtension<'a> { pub cheatnet_state: &'a mut CheatnetState, } pub type CheatableStarknetRuntime<'a> = ExtendedRuntime>; impl<'a> ExtensionLogic for CheatableStarknetRuntimeExtension<'a> { type Runtime = StarknetRuntime<'a>; fn override_system_call( &mut self, selector: SyscallSelector, vm: &mut VirtualMachine, extended_runtime: &mut Self::Runtime, ) -> Result { let syscall_handler = &mut extended_runtime.hint_handler; // Warning: Do not add a default (`_`) arm here. // This match must remain exhaustive so that if a new syscall is introduced, // we will explicitly add support for it. match selector { SyscallSelector::GetExecutionInfo => Ok(self .execute_syscall( syscall_handler, vm, cheated_syscalls::get_execution_info_syscall, SyscallSelector::GetExecutionInfo, ) .map(|()| SyscallHandlingResult::Handled)?), SyscallSelector::CallContract => Ok(self .execute_syscall( syscall_handler, vm, cheated_syscalls::call_contract_syscall, SyscallSelector::CallContract, ) .map(|()| SyscallHandlingResult::Handled)?), SyscallSelector::LibraryCall => Ok(self .execute_syscall( syscall_handler, vm, cheated_syscalls::library_call_syscall, SyscallSelector::LibraryCall, ) .map(|()| SyscallHandlingResult::Handled)?), SyscallSelector::Deploy => Ok(self .execute_syscall( syscall_handler, vm, cheated_syscalls::deploy_syscall, SyscallSelector::Deploy, ) .map(|()| SyscallHandlingResult::Handled)?), SyscallSelector::GetBlockHash => Ok(self .execute_syscall( syscall_handler, vm, cheated_syscalls::get_block_hash_syscall, SyscallSelector::GetBlockHash, ) .map(|()| SyscallHandlingResult::Handled)?), SyscallSelector::StorageRead => Ok(self .execute_syscall( syscall_handler, vm, cheated_syscalls::storage_read, SyscallSelector::StorageRead, ) .map(|()| SyscallHandlingResult::Handled)?), SyscallSelector::StorageWrite => Ok(self .execute_syscall( syscall_handler, vm, cheated_syscalls::storage_write, SyscallSelector::StorageWrite, ) .map(|()| SyscallHandlingResult::Handled)?), SyscallSelector::MetaTxV0 => Ok(self .execute_syscall( syscall_handler, vm, cheated_syscalls::meta_tx_v0_syscall, SyscallSelector::MetaTxV0, ) .map(|()| SyscallHandlingResult::Handled)?), SyscallSelector::DelegateCall | SyscallSelector::DelegateL1Handler | SyscallSelector::EmitEvent | SyscallSelector::GetBlockNumber | SyscallSelector::GetBlockTimestamp | SyscallSelector::GetCallerAddress | SyscallSelector::GetClassHashAt | SyscallSelector::GetContractAddress | SyscallSelector::GetSequencerAddress | SyscallSelector::GetTxInfo | SyscallSelector::GetTxSignature | SyscallSelector::Keccak | SyscallSelector::KeccakRound | SyscallSelector::Sha256ProcessBlock | SyscallSelector::LibraryCallL1Handler | SyscallSelector::ReplaceClass | SyscallSelector::Secp256k1Add | SyscallSelector::Secp256k1GetPointFromX | SyscallSelector::Secp256k1GetXy | SyscallSelector::Secp256k1Mul | SyscallSelector::Secp256k1New | SyscallSelector::Secp256r1Add | SyscallSelector::Secp256r1GetPointFromX | SyscallSelector::Secp256r1GetXy | SyscallSelector::Secp256r1Mul | SyscallSelector::Secp256r1New | SyscallSelector::SendMessageToL1 => Ok(SyscallHandlingResult::Forwarded), } } fn handle_system_call_signal( &mut self, selector: SyscallSelector, _vm: &mut VirtualMachine, extended_runtime: &mut Self::Runtime, ) { let syscall_handler = &extended_runtime.hint_handler; match selector { SyscallSelector::EmitEvent => { syscall_hooks::emit_event_hook(syscall_handler, self.cheatnet_state); } SyscallSelector::SendMessageToL1 => { syscall_hooks::send_message_to_l1_syscall_hook( syscall_handler, self.cheatnet_state, ); } _ => {} } } } pub fn felt_from_ptr_immutable( vm: &VirtualMachine, ptr: &Relocatable, ) -> Result { let felt = vm.get_integer(*ptr)?.into_owned(); Ok(felt) } pub trait CheatableStarknetRuntimeError: TryExtractRevert + From {} impl CheatableStarknetRuntimeError for T where T: TryExtractRevert + From { } impl CheatableStarknetRuntimeExtension<'_> { // crates/blockifier/src/execution/syscalls/vm_syscall_utils.rs:677 (execute_syscall) fn execute_syscall( &mut self, syscall_handler: &mut SyscallHintProcessor, vm: &mut VirtualMachine, execute_callback: ExecuteCallback, selector: SyscallSelector, ) -> Result<(), Error> where Request: SyscallRequest + std::fmt::Debug, Response: SyscallResponse + std::fmt::Debug, Error: CheatableStarknetRuntimeError, ExecuteCallback: FnOnce( Request, &mut VirtualMachine, &mut SyscallHintProcessor<'_>, &mut CheatnetState, &mut u64, // Remaining gas. ) -> Result, { // Increment, since the selector was peeked into before syscall_handler.syscall_ptr += 1; syscall_handler.increment_syscall_count_by(&selector, 1); let syscall_gas_cost = syscall_handler .get_gas_cost_from_selector(&selector) .map_err(|error| SyscallExecutorBaseError::GasCost { error, selector })?; let SyscallRequestWrapper { gas_counter, request, } = SyscallRequestWrapper::::read(vm, &mut syscall_handler.syscall_ptr)?; let syscall_gas_cost = syscall_gas_cost.get_syscall_cost(u64_from_usize(request.get_linear_factor_length())); let syscall_base_cost = syscall_handler.get_syscall_base_gas_cost(); // Sanity check for preventing underflow. assert!( syscall_gas_cost >= syscall_base_cost, "Syscall gas cost must be greater than base syscall gas cost" ); // Refund `SYSCALL_BASE_GAS_COST` as it was pre-charged. // Note: It is pre-charged by the compiler: https://github.com/starkware-libs/sequencer/blob/v0.15.0-rc.2/crates/blockifier/src/blockifier_versioned_constants.rs#L1057 let required_gas = syscall_gas_cost - syscall_base_cost; if gas_counter < required_gas { // Out of gas failure. let out_of_gas_error = Felt::from_hex(OUT_OF_GAS_ERROR) .expect("Failed to parse OUT_OF_GAS_ERROR hex string"); let response: SyscallResponseWrapper = SyscallResponseWrapper::Failure { gas_counter, revert_data: RevertData::new_normal(vec![out_of_gas_error]), }; response.write(vm, &mut syscall_handler.syscall_ptr)?; return Ok(()); } // Execute. let mut remaining_gas = gas_counter - required_gas; // TODO(#3681) syscall_handler.update_revert_gas_with_next_remaining_gas(GasAmount(remaining_gas)); let original_response = execute_callback( request, vm, syscall_handler, self.cheatnet_state, &mut remaining_gas, ); let response = match original_response { Ok(response) => SyscallResponseWrapper::Success { gas_counter: remaining_gas, response, }, Err(error) => match error.try_extract_revert() { SelfOrRevert::Revert(data) => SyscallResponseWrapper::Failure { gas_counter: remaining_gas, revert_data: data, }, SelfOrRevert::Original(err) => return Err(err), }, }; response.write(vm, &mut syscall_handler.syscall_ptr)?; Ok(()) } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/common.rs ================================================ use blockifier::blockifier_versioned_constants::VersionedConstants; use blockifier::execution::syscalls::vm_syscall_utils::{SyscallSelector, SyscallUsageMap}; use blockifier::utils::u64_from_usize; use cairo_vm::vm::runners::cairo_runner::CairoRunner; use cairo_vm::vm::trace::trace_entry::RelocatedTraceEntry; use starknet_api::transaction::fields::Calldata; use starknet_types_core::felt::Felt; #[must_use] pub fn create_execute_calldata(calldata: &[Felt]) -> Calldata { Calldata(calldata.to_vec().into()) } #[must_use] pub fn sum_syscall_usage(mut a: SyscallUsageMap, b: &SyscallUsageMap) -> SyscallUsageMap { for (key, value) in b { a.entry(*key).or_default().call_count += value.call_count; a.entry(*key).or_default().linear_factor += value.linear_factor; } a } #[must_use] pub fn get_syscalls_gas_consumed( syscall_usage: &SyscallUsageMap, versioned_constants: &VersionedConstants, ) -> u64 { syscall_usage .iter() .map(|(selector, usage)| { let syscall_gas_costs = &versioned_constants.os_constants.gas_costs.syscalls; let syscall_gas_cost = syscall_gas_costs .get_syscall_gas_cost(selector) .unwrap_or_else(|_| { panic!("Failed to get syscall gas cost for selector {selector:?}") }); // `linear_factor` is relevant only for `deploy` and `meta_tx_v0` syscalls, for other syscalls it is 0 // `base_syscall_cost` makes an assert that `linear_factor` is 0 // Hence to get base cost for `deploy` we use `get_syscall_cost` with 0 as `linear_length` // For other syscalls we use `base_syscall_cost`, which is also an additional check that `linear_factor` is always 0 then let base_cost = match selector { SyscallSelector::Deploy | SyscallSelector::MetaTxV0 => { syscall_gas_cost.get_syscall_cost(0) } _ => syscall_gas_cost.base_syscall_cost(), }; // We want to calculate `base_cost * call_count + linear_cost * linear_factor` // And it is achieved by calculating `base_cost * (call_count - 1) + base_cost + linear_cost * linear_factor` // There is a field name `linear_factor` used in both `SyscallUsage` and `SyscallGasCost` // In `get_syscall_cost` function `linear_length` parameter is calldata length, hence `SyscallUsage.linear_factor` u64_from_usize(usage.call_count - 1) * base_cost + syscall_gas_cost.get_syscall_cost(u64_from_usize(usage.linear_factor)) }) .sum() } #[must_use] pub fn get_relocated_vm_trace(cairo_runner: &mut CairoRunner) -> Vec { // if vm execution failed, the trace is not relocated so we need to relocate it if cairo_runner.relocated_trace.is_none() { cairo_runner .relocate(true, true) .expect("relocation should not fail"); } cairo_runner .relocated_trace .clone() .expect("relocated trace should be present") } ================================================ FILE: crates/cheatnet/src/runtime_extensions/deprecated_cheatable_starknet_extension/mod.rs ================================================ use crate::state::CheatnetState; use blockifier::execution::common_hints::HintExecutionResult; use blockifier::execution::deprecated_syscalls::hint_processor::{ DeprecatedSyscallExecutionError, DeprecatedSyscallHintProcessor, }; use blockifier::execution::deprecated_syscalls::{ CallContractRequest, DeployRequest, DeployResponse, DeprecatedSyscallResult, GetBlockNumberResponse, GetBlockTimestampResponse, GetContractAddressResponse, LibraryCallRequest, SyscallRequest, SyscallResponse, WriteResponseResult, }; use blockifier::execution::entry_point::{CallEntryPoint, CallType, ConstructorContext}; use blockifier::execution::execution_utils::{ ReadOnlySegment, execute_deployment, write_maybe_relocatable, }; use blockifier::execution::syscalls::vm_syscall_utils::SyscallSelector; use conversions::FromConv; use ::runtime::SyscallHandlingResult; use cairo_vm::types::relocatable::{MaybeRelocatable, Relocatable}; use cairo_vm::vm::errors::hint_errors::HintError; use cairo_vm::vm::vm_core::VirtualMachine; use num_traits::ToPrimitive; use starknet_api::block::{BlockNumber, BlockTimestamp}; use starknet_api::contract_class::EntryPointType; use starknet_api::core::{ ClassHash, ContractAddress, EntryPointSelector, calculate_contract_address, }; use starknet_api::transaction::fields::Calldata; use starknet_types_core::felt::Felt; use self::runtime::{ DeprecatedExtendedRuntime, DeprecatedExtensionLogic, DeprecatedStarknetRuntime, }; use super::call_to_blockifier_runtime_extension::execution::entry_point::non_reverting_execute_call_entry_point; use super::call_to_blockifier_runtime_extension::execution::syscall_hooks; pub mod runtime; #[derive(Debug)] // crates/blockifier/src/execution/deprecated_syscalls/mod.rs:147 (SingleSegmentResponse) // It is created here because fields in the original structure are private // so we cannot create it in call_contract_syscall pub struct SingleSegmentResponse { pub(crate) segment: ReadOnlySegment, } // crates/blockifier/src/execution/deprecated_syscalls/mod.rs:151 (SyscallResponse for SingleSegmentResponse) impl SyscallResponse for SingleSegmentResponse { fn write(self, vm: &mut VirtualMachine, ptr: &mut Relocatable) -> WriteResponseResult { write_maybe_relocatable(vm, ptr, self.segment.length)?; write_maybe_relocatable(vm, ptr, self.segment.start_ptr)?; Ok(()) } } pub struct DeprecatedCheatableStarknetRuntimeExtension<'a> { pub cheatnet_state: &'a mut CheatnetState, } pub type DeprecatedCheatableStarknetRuntime<'a> = DeprecatedExtendedRuntime>; impl<'a> DeprecatedExtensionLogic for DeprecatedCheatableStarknetRuntimeExtension<'a> { type Runtime = DeprecatedStarknetRuntime<'a>; #[allow(clippy::too_many_lines)] fn override_system_call( &mut self, selector: SyscallSelector, vm: &mut VirtualMachine, extended_runtime: &mut Self::Runtime, ) -> Result { let syscall_handler = &mut extended_runtime.hint_handler; let contract_address = syscall_handler.storage_address; // Warning: Do not add a default (`_`) arm here. // This match must remain exhaustive so that if a new syscall is introduced, // we will explicitly add support for it. match selector { SyscallSelector::GetCallerAddress => { if let Some(caller_address) = self .cheatnet_state .get_cheated_caller_address(contract_address) { // Increment, since the selector was peeked into before syscall_handler.syscall_ptr += 1; increment_syscall_count(syscall_handler, selector); let response = GetContractAddressResponse { address: caller_address, }; response.write(vm, &mut syscall_handler.syscall_ptr)?; Ok(SyscallHandlingResult::Handled) } else { Ok(SyscallHandlingResult::Forwarded) } } SyscallSelector::GetBlockNumber => { if let Some(block_number) = self .cheatnet_state .get_cheated_block_number(contract_address) { syscall_handler.syscall_ptr += 1; increment_syscall_count(syscall_handler, selector); let response = GetBlockNumberResponse { block_number: BlockNumber(block_number.to_u64().unwrap()), }; response.write(vm, &mut syscall_handler.syscall_ptr)?; Ok(SyscallHandlingResult::Handled) } else { Ok(SyscallHandlingResult::Forwarded) } } SyscallSelector::GetBlockTimestamp => { if let Some(block_timestamp) = self .cheatnet_state .get_cheated_block_timestamp(contract_address) { syscall_handler.syscall_ptr += 1; increment_syscall_count(syscall_handler, selector); let response = GetBlockTimestampResponse { block_timestamp: BlockTimestamp(block_timestamp.to_u64().unwrap()), }; response.write(vm, &mut syscall_handler.syscall_ptr)?; Ok(SyscallHandlingResult::Handled) } else { Ok(SyscallHandlingResult::Forwarded) } } SyscallSelector::GetSequencerAddress => { if let Some(sequencer_address) = self .cheatnet_state .get_cheated_sequencer_address(contract_address) { syscall_handler.syscall_ptr += 1; increment_syscall_count(syscall_handler, selector); syscall_handler.verify_not_in_validate_mode("get_sequencer_address")?; let response = GetContractAddressResponse { address: sequencer_address, }; response.write(vm, &mut syscall_handler.syscall_ptr)?; Ok(SyscallHandlingResult::Handled) } else { Ok(SyscallHandlingResult::Forwarded) } } SyscallSelector::DelegateCall => { syscall_handler.syscall_ptr += 1; increment_syscall_count(syscall_handler, selector); self.execute_syscall(vm, delegate_call, syscall_handler)?; Ok(SyscallHandlingResult::Handled) } SyscallSelector::LibraryCall => { syscall_handler.syscall_ptr += 1; increment_syscall_count(syscall_handler, selector); self.execute_syscall(vm, library_call, syscall_handler)?; Ok(SyscallHandlingResult::Handled) } SyscallSelector::CallContract => { syscall_handler.syscall_ptr += 1; increment_syscall_count(syscall_handler, selector); self.execute_syscall(vm, call_contract, syscall_handler)?; Ok(SyscallHandlingResult::Handled) } SyscallSelector::Deploy => { syscall_handler.syscall_ptr += 1; increment_syscall_count(syscall_handler, selector); self.execute_syscall(vm, deploy, syscall_handler)?; Ok(SyscallHandlingResult::Handled) } SyscallSelector::DelegateL1Handler | SyscallSelector::EmitEvent | SyscallSelector::GetBlockHash | SyscallSelector::GetClassHashAt | SyscallSelector::GetContractAddress | SyscallSelector::GetExecutionInfo | SyscallSelector::GetTxInfo | SyscallSelector::GetTxSignature | SyscallSelector::Keccak | SyscallSelector::KeccakRound | SyscallSelector::Sha256ProcessBlock | SyscallSelector::LibraryCallL1Handler | SyscallSelector::MetaTxV0 | SyscallSelector::ReplaceClass | SyscallSelector::Secp256k1Add | SyscallSelector::Secp256k1GetPointFromX | SyscallSelector::Secp256k1GetXy | SyscallSelector::Secp256k1Mul | SyscallSelector::Secp256k1New | SyscallSelector::Secp256r1Add | SyscallSelector::Secp256r1GetPointFromX | SyscallSelector::Secp256r1GetXy | SyscallSelector::Secp256r1Mul | SyscallSelector::Secp256r1New | SyscallSelector::SendMessageToL1 | SyscallSelector::StorageRead | SyscallSelector::StorageWrite => Ok(SyscallHandlingResult::Forwarded), } } fn post_syscall_hook( &mut self, selector: &SyscallSelector, extended_runtime: &mut Self::Runtime, ) { let syscall_handler = &extended_runtime.hint_handler; match selector { SyscallSelector::EmitEvent => { syscall_hooks::emit_event_hook(syscall_handler, self.cheatnet_state); } SyscallSelector::SendMessageToL1 => { syscall_hooks::send_message_to_l1_syscall_hook( syscall_handler, self.cheatnet_state, ); } _ => {} } } } impl DeprecatedCheatableStarknetRuntimeExtension<'_> { // crates/blockifier/src/execution/deprecated_syscalls/hint_processor.rs:233 fn execute_syscall( &mut self, vm: &mut VirtualMachine, execute_callback: ExecuteCallback, syscall_handler: &mut DeprecatedSyscallHintProcessor, ) -> HintExecutionResult where Request: SyscallRequest, Response: SyscallResponse, ExecuteCallback: FnOnce( Request, &mut VirtualMachine, &mut DeprecatedSyscallHintProcessor, &mut CheatnetState, ) -> DeprecatedSyscallResult, { let request = Request::read(vm, &mut syscall_handler.syscall_ptr)?; let response = execute_callback(request, vm, syscall_handler, self.cheatnet_state)?; response.write(vm, &mut syscall_handler.syscall_ptr)?; Ok(()) } } // crates/blockifier/src/execution/deprecated_syscalls/hint_processor.rs:264 fn increment_syscall_count( syscall_handler: &mut DeprecatedSyscallHintProcessor, selector: SyscallSelector, ) { syscall_handler .syscalls_usage .entry(selector) .or_default() .increment_call_count(); } //blockifier/src/execution/deprecated_syscalls/mod.rs:303 (deploy) fn deploy( request: DeployRequest, _vm: &mut VirtualMachine, syscall_handler: &mut DeprecatedSyscallHintProcessor<'_>, _cheatnet_state: &mut CheatnetState, ) -> DeprecatedSyscallResult { let deployer_address = syscall_handler.storage_address; let deployer_address_for_calculation = if request.deploy_from_zero { ContractAddress::default() } else { deployer_address }; let deployed_contract_address = calculate_contract_address( request.contract_address_salt, request.class_hash, &request.constructor_calldata, deployer_address_for_calculation, )?; // Increment the Deploy syscall's linear cost counter by the number of elements in the // constructor calldata. let syscall_usage = syscall_handler .syscalls_usage .get_mut(&SyscallSelector::Deploy) .expect("syscalls_usage entry for Deploy must be initialized"); syscall_usage.linear_factor += request.constructor_calldata.0.len(); let ctor_context = ConstructorContext { class_hash: request.class_hash, code_address: Some(deployed_contract_address), storage_address: deployed_contract_address, caller_address: deployer_address, }; let mut remaining_gas = syscall_handler .context .gas_costs() .base .default_initial_gas_cost; let call_info = execute_deployment( syscall_handler.state, syscall_handler.context, ctor_context, request.constructor_calldata, &mut remaining_gas, )?; syscall_handler.inner_calls.push(call_info); Ok(DeployResponse { contract_address: deployed_contract_address, }) } //blockifier/src/execution/deprecated_syscalls/mod.rs:182 (call_contract) fn call_contract( request: CallContractRequest, vm: &mut VirtualMachine, syscall_handler: &mut DeprecatedSyscallHintProcessor<'_>, cheatnet_state: &mut CheatnetState, ) -> DeprecatedSyscallResult { let storage_address = request.contract_address; // Check that the call is legal if in Validate execution mode. if syscall_handler.is_validate_mode() && syscall_handler.storage_address != storage_address { return Err( DeprecatedSyscallExecutionError::InvalidSyscallInExecutionMode { syscall_name: "call_contract".to_string(), execution_mode: syscall_handler.execution_mode(), }, ); } let mut entry_point = CallEntryPoint { class_hash: None, code_address: Some(storage_address), entry_point_type: EntryPointType::External, entry_point_selector: request.function_selector, calldata: request.calldata, storage_address, caller_address: syscall_handler.storage_address, call_type: CallType::Call, initial_gas: syscall_handler .context .gas_costs() .base .default_initial_gas_cost, }; let retdata_segment = execute_inner_call(&mut entry_point, vm, syscall_handler, cheatnet_state)?; Ok(SingleSegmentResponse { segment: retdata_segment, }) } // blockifier/src/execution/deprecated_syscalls/mod.rs:209 (delegate_call) fn delegate_call( request: CallContractRequest, vm: &mut VirtualMachine, syscall_handler: &mut DeprecatedSyscallHintProcessor<'_>, cheatnet_state: &mut CheatnetState, ) -> DeprecatedSyscallResult { let call_to_external = true; let storage_address = request.contract_address; let class_hash = syscall_handler.state.get_class_hash_at(storage_address)?; let retdata_segment = execute_library_call( syscall_handler, cheatnet_state, vm, class_hash, Some(storage_address), call_to_external, request.function_selector, request.calldata, )?; Ok(SingleSegmentResponse { segment: retdata_segment, }) } // blockifier/src/execution/deprecated_syscalls/mod.rs:537 (library_call) fn library_call( request: LibraryCallRequest, vm: &mut VirtualMachine, syscall_handler: &mut DeprecatedSyscallHintProcessor<'_>, cheatnet_state: &mut CheatnetState, ) -> DeprecatedSyscallResult { let call_to_external = true; let retdata_segment = execute_library_call( syscall_handler, cheatnet_state, vm, request.class_hash, None, call_to_external, request.function_selector, request.calldata, )?; Ok(SingleSegmentResponse { segment: retdata_segment, }) } // blockifier/src/execution/deprecated_syscalls/hint_processor.rs:393 (execute_inner_call) fn execute_inner_call( call: &mut CallEntryPoint, vm: &mut VirtualMachine, syscall_handler: &mut DeprecatedSyscallHintProcessor<'_>, cheatnet_state: &mut CheatnetState, ) -> DeprecatedSyscallResult { let mut remaining_gas = call.initial_gas; // region: Modified blockifier code let call_info = non_reverting_execute_call_entry_point( call, syscall_handler.state, cheatnet_state, syscall_handler.context, &mut remaining_gas, )?; // endregion let retdata = &call_info.execution.retdata.0; let retdata: Vec = retdata .iter() .map(|&x| MaybeRelocatable::from(Felt::from_(x))) .collect(); let retdata_segment_start_ptr = syscall_handler.read_only_segments.allocate(vm, &retdata)?; syscall_handler.inner_calls.push(call_info); Ok(ReadOnlySegment { start_ptr: retdata_segment_start_ptr, length: retdata.len(), }) } // blockifier/src/execution/deprecated_syscalls/hint_processor.rs:409 (execute_library_call) #[expect(clippy::too_many_arguments)] fn execute_library_call( syscall_handler: &mut DeprecatedSyscallHintProcessor<'_>, cheatnet_state: &mut CheatnetState, vm: &mut VirtualMachine, class_hash: ClassHash, code_address: Option, call_to_external: bool, entry_point_selector: EntryPointSelector, calldata: Calldata, ) -> DeprecatedSyscallResult { let entry_point_type = if call_to_external { EntryPointType::External } else { EntryPointType::L1Handler }; let mut entry_point = CallEntryPoint { class_hash: Some(class_hash), code_address, entry_point_type, entry_point_selector, calldata, // The call context remains the same in a library call. storage_address: syscall_handler.storage_address, caller_address: syscall_handler.caller_address, call_type: CallType::Delegate, initial_gas: syscall_handler .context .gas_costs() .base .default_initial_gas_cost, }; execute_inner_call(&mut entry_point, vm, syscall_handler, cheatnet_state) } ================================================ FILE: crates/cheatnet/src/runtime_extensions/deprecated_cheatable_starknet_extension/runtime.rs ================================================ use crate::runtime_extensions::cheatable_starknet_runtime_extension::felt_from_ptr_immutable; use anyhow::Result; use blockifier::execution::{ deprecated_syscalls::hint_processor::DeprecatedSyscallHintProcessor, hint_code, syscalls::vm_syscall_utils::SyscallSelector, }; use cairo_vm::{ hint_processor::{ builtin_hint_processor::{ builtin_hint_processor_definition::HintProcessorData, hint_utils::get_ptr_from_var_name, }, hint_processor_definition::{HintProcessor, HintProcessorLogic, HintReference}, }, serde::deserialize_program::ApTracking, types::{exec_scope::ExecutionScopes, relocatable::Relocatable}, vm::{ errors::{hint_errors::HintError, vm_errors::VirtualMachineError}, runners::cairo_runner::{ResourceTracker, RunResources}, vm_core::VirtualMachine, }, }; use runtime::{SyscallHandlingResult, SyscallPtrAccess}; use starknet_types_core::felt::Felt; use std::sync::Arc; use std::{any::Any, collections::HashMap}; pub struct DeprecatedStarknetRuntime<'a> { pub hint_handler: DeprecatedSyscallHintProcessor<'a>, } impl SyscallPtrAccess for DeprecatedStarknetRuntime<'_> { fn get_mut_syscall_ptr(&mut self) -> &mut Relocatable { &mut self.hint_handler.syscall_ptr } } impl ResourceTracker for DeprecatedStarknetRuntime<'_> { fn consumed(&self) -> bool { self.hint_handler.context.vm_run_resources.consumed() } fn consume_step(&mut self) { self.hint_handler.context.vm_run_resources.consume_step(); } fn get_n_steps(&self) -> Option { self.hint_handler.context.vm_run_resources.get_n_steps() } fn run_resources(&self) -> &RunResources { self.hint_handler.context.vm_run_resources.run_resources() } } impl HintProcessorLogic for DeprecatedStarknetRuntime<'_> { fn execute_hint( &mut self, vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, hint_data: &Box, ) -> Result<(), HintError> { self.hint_handler.execute_hint(vm, exec_scopes, hint_data) } fn compile_hint( &self, hint_code: &str, ap_tracking_data: &ApTracking, reference_ids: &HashMap, references: &[HintReference], accessible_scopes: &[String], constants: Arc>, ) -> Result, VirtualMachineError> { self.hint_handler.compile_hint( hint_code, ap_tracking_data, reference_ids, references, accessible_scopes, constants, ) } } pub struct DeprecatedExtendedRuntime { pub extension: Extension, pub extended_runtime: ::Runtime, } impl HintProcessorLogic for DeprecatedExtendedRuntime { fn execute_hint( &mut self, vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, hint_data: &Box, ) -> Result<(), HintError> { let hint = hint_data .downcast_ref::() .ok_or(HintError::WrongHintData)?; if hint_code::SYSCALL_HINTS.contains(hint.code.as_str()) { return self.execute_syscall_hint( vm, exec_scopes, hint_data, &hint.ids_data, &hint.ap_tracking, ); } self.extended_runtime .execute_hint(vm, exec_scopes, hint_data) } fn compile_hint( &self, hint_code: &str, ap_tracking_data: &ApTracking, reference_ids: &HashMap, references: &[HintReference], accessible_scopes: &[String], constants: Arc>, ) -> Result, VirtualMachineError> { self.extended_runtime.compile_hint( hint_code, ap_tracking_data, reference_ids, references, accessible_scopes, constants, ) } } impl DeprecatedExtendedRuntime { fn execute_syscall_hint( &mut self, vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, hint_data: &Box, ids_data: &HashMap, ap_tracking: &ApTracking, ) -> Result<(), HintError> { let initial_syscall_ptr = get_ptr_from_var_name("syscall_ptr", vm, ids_data, ap_tracking)?; let selector = SyscallSelector::try_from(felt_from_ptr_immutable(vm, &initial_syscall_ptr)?)?; if let SyscallHandlingResult::Handled = self.extension .override_system_call(selector, vm, &mut self.extended_runtime)? { Ok(()) } else { self.extended_runtime .execute_hint(vm, exec_scopes, hint_data)?; self.extension .post_syscall_hook(&selector, &mut self.extended_runtime); Ok(()) } } } impl SyscallPtrAccess for DeprecatedExtendedRuntime { fn get_mut_syscall_ptr(&mut self) -> &mut Relocatable { self.extended_runtime.get_mut_syscall_ptr() } } impl ResourceTracker for DeprecatedExtendedRuntime { fn consumed(&self) -> bool { self.extended_runtime.consumed() } fn consume_step(&mut self) { self.extended_runtime.consume_step(); } fn get_n_steps(&self) -> Option { self.extended_runtime.get_n_steps() } fn run_resources(&self) -> &RunResources { self.extended_runtime.run_resources() } } pub trait DeprecatedExtensionLogic { type Runtime: HintProcessor + SyscallPtrAccess; fn override_system_call( &mut self, _selector: SyscallSelector, _vm: &mut VirtualMachine, _extended_runtime: &mut Self::Runtime, ) -> Result { Ok(SyscallHandlingResult::Forwarded) } fn post_syscall_hook( &mut self, _selector: &SyscallSelector, _extended_runtime: &mut Self::Runtime, ); } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_config_extension/config.rs ================================================ use conversions::{byte_array::ByteArray, serde::deserialize::CairoDeserialize}; use serde::{ Deserialize, Deserializer, de::{self, MapAccess, Visitor}, }; use starknet_api::execution_resources::{GasAmount, GasVector}; use starknet_types_core::felt::Felt; use std::str::FromStr; use std::{fmt, num::NonZeroU32}; use url::Url; // available gas #[derive(Debug, Clone, Copy, CairoDeserialize, PartialEq)] pub struct RawAvailableResourceBoundsConfig { pub l1_gas: usize, pub l1_data_gas: usize, pub l2_gas: usize, } impl RawAvailableResourceBoundsConfig { #[must_use] pub fn to_gas_vector(&self) -> GasVector { GasVector { l1_gas: GasAmount(self.l1_gas as u64), l1_data_gas: GasAmount(self.l1_data_gas as u64), l2_gas: GasAmount(self.l2_gas as u64), } } #[must_use] pub fn is_zero(&self) -> bool { self.to_gas_vector() == GasVector::ZERO } } // fork #[derive(Debug, Clone, CairoDeserialize, PartialEq)] pub enum BlockId { BlockTag, BlockHash(Felt), BlockNumber(u64), } impl<'de> Deserialize<'de> for BlockId { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct BlockIdVisitor; impl<'de> Visitor<'de> for BlockIdVisitor { type Value = BlockId; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a map with exactly one of: tag, hash, or number") } fn visit_map(self, mut map: A) -> Result where A: MapAccess<'de>, { let mut block_id = None; while let Some(key) = map.next_key::()? { if block_id.is_some() { return Err(de::Error::custom( "block_id must contain exactly one key: 'tag', 'hash', or 'number'", )); } block_id = Some(match key.as_str() { "tag" => { let tag = map.next_value::()?; if tag != "latest" { return Err(de::Error::custom( "block_id.tag can only be equal to latest", )); } BlockId::BlockTag } "hash" => BlockId::BlockHash( Felt::from_str(&map.next_value::()?) .map_err(de::Error::custom)?, ), "number" => BlockId::BlockNumber( map.next_value::()? .parse() .map_err(de::Error::custom)?, ), unknown => { return Err(de::Error::unknown_field( unknown, &["tag", "hash", "number"], )); } }); } block_id.ok_or_else(|| de::Error::missing_field("block_id")) } } deserializer.deserialize_map(BlockIdVisitor) } } #[derive(Debug, Clone, CairoDeserialize, PartialEq)] pub struct InlineForkConfig { pub url: Url, pub block: BlockId, } #[derive(Debug, Clone, CairoDeserialize, PartialEq)] pub struct OverriddenForkConfig { pub name: ByteArray, pub block: BlockId, } #[derive(Debug, Clone, CairoDeserialize, PartialEq)] pub enum RawForkConfig { Inline(InlineForkConfig), Named(ByteArray), Overridden(OverriddenForkConfig), } // fuzzer #[derive(Debug, Clone, CairoDeserialize, PartialEq)] pub struct RawFuzzerConfig { pub runs: Option, pub seed: Option, } // should panic #[derive(Debug, Clone, CairoDeserialize)] pub enum Expected { ShortString(Felt), ByteArray(ByteArray), Array(Vec), Any, } #[derive(Debug, Clone, CairoDeserialize)] pub struct RawShouldPanicConfig { pub expected: Expected, } // ignore #[derive(Debug, Clone, CairoDeserialize)] pub struct RawIgnoreConfig { pub is_ignored: bool, } // disable strk predeployment #[derive(Debug, Clone, CairoDeserialize)] pub struct RawPredeployedContractsConfig { pub is_disabled: bool, } // config #[derive(Debug, Default, Clone)] pub struct RawForgeConfig { pub fork: Option, pub available_gas: Option, pub ignore: Option, pub should_panic: Option, pub fuzzer: Option, pub disable_predeployed_contracts: Option, } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_config_extension.rs ================================================ use cairo_vm::vm::vm_core::VirtualMachine; use config::RawForgeConfig; use conversions::serde::deserialize::BufferReader; use runtime::{CheatcodeHandlingResult, EnhancedHintError, ExtensionLogic, StarknetRuntime}; pub mod config; pub struct ForgeConfigExtension<'a> { pub config: &'a mut RawForgeConfig, } // This runtime extension provides an implementation logic for snforge configuration run. impl<'a> ExtensionLogic for ForgeConfigExtension<'a> { type Runtime = StarknetRuntime<'a>; fn handle_cheatcode( &mut self, selector: &str, mut input_reader: BufferReader<'_>, _extended_runtime: &mut Self::Runtime, _vm: &VirtualMachine, ) -> Result { macro_rules! config_cheatcode { ( $prop:ident) => {{ self.config.$prop = Some(input_reader.read()?); Ok(CheatcodeHandlingResult::from_serializable(())) }}; } match selector { "set_config_fork" => config_cheatcode!(fork), "set_config_available_gas" => config_cheatcode!(available_gas), "set_config_ignore" => config_cheatcode!(ignore), "set_config_disable_contracts" => config_cheatcode!(disable_predeployed_contracts), "set_config_should_panic" => config_cheatcode!(should_panic), "set_config_fuzzer" => config_cheatcode!(fuzzer), "is_config_mode" => Ok(CheatcodeHandlingResult::from_serializable(true)), _ => Ok(CheatcodeHandlingResult::Forwarded), } } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/cheat_account_contract_address.rs ================================================ use crate::{ runtime_extensions::forge_runtime_extension::cheatcodes::cheat_execution_info::{ CheatArguments, ExecutionInfoMockOperations, Operation, TxInfoMockOperations, }, state::{CheatSpan, CheatnetState}, }; use starknet_api::core::ContractAddress; impl CheatnetState { pub fn cheat_account_contract_address( &mut self, target: ContractAddress, account_contract_address: ContractAddress, span: CheatSpan, ) { self.cheat_execution_info(ExecutionInfoMockOperations { tx_info: TxInfoMockOperations { account_contract_address: Operation::Start(CheatArguments { value: account_contract_address.into(), span, target, }), ..Default::default() }, ..Default::default() }); } pub fn start_cheat_account_contract_address( &mut self, target: ContractAddress, account_contract_address: ContractAddress, ) { self.cheat_account_contract_address( target, account_contract_address, CheatSpan::Indefinite, ); } pub fn stop_cheat_account_contract_address(&mut self, target: ContractAddress) { self.cheat_execution_info(ExecutionInfoMockOperations { tx_info: TxInfoMockOperations { account_contract_address: Operation::Stop(target), ..Default::default() }, ..Default::default() }); } pub fn start_cheat_account_contract_address_global( &mut self, account_contract_address: ContractAddress, ) { self.cheat_execution_info(ExecutionInfoMockOperations { tx_info: TxInfoMockOperations { account_contract_address: Operation::StartGlobal(account_contract_address.into()), ..Default::default() }, ..Default::default() }); } pub fn stop_cheat_account_contract_address_global( &mut self, account_contract_address: ContractAddress, ) { self.cheat_execution_info(ExecutionInfoMockOperations { tx_info: TxInfoMockOperations { account_contract_address: Operation::StartGlobal(account_contract_address.into()), ..Default::default() }, ..Default::default() }); } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/cheat_block_hash.rs ================================================ use crate::CheatnetState; use crate::runtime_extensions::forge_runtime_extension::cheatcodes::cheat_execution_info::{ CheatArguments, Operation, }; use crate::state::CheatSpan; use blockifier::execution::syscalls::hint_processor::SyscallHintProcessor; use blockifier::execution::syscalls::syscall_base::SyscallResult; use starknet_api::block::{BlockHash, BlockNumber}; use starknet_api::core::ContractAddress; use starknet_api::hash::StarkHash; use starknet_types_core::felt::Felt; use std::num::NonZeroUsize; impl CheatnetState { pub fn cheat_block_hash(&mut self, block_number: u64, operation: Operation) { match operation { Operation::Start(args) => { let CheatArguments { value, span, target, } = args; self.block_hash_contracts .insert((target, block_number), (span, value)); } Operation::Stop(contract_address) => { self.block_hash_contracts .remove(&(contract_address, block_number)); if let Some((_, except)) = self.global_block_hash.get_mut(&block_number) { except.push(contract_address); } } Operation::StartGlobal(block_hash) => { self.global_block_hash .insert(block_number, (block_hash, vec![])); self.block_hash_contracts .retain(|(_, bn), _| *bn != block_number); } Operation::StopGlobal => { self.global_block_hash.remove(&block_number); self.block_hash_contracts .retain(|(_, bn), _| *bn != block_number); } Operation::Retain => { unreachable!("Retain operation isn't used for this cheat") } } } pub fn start_cheat_block_hash( &mut self, contract_address: ContractAddress, block_number: u64, block_hash: Felt, ) { self.cheat_block_hash( block_number, Operation::Start(CheatArguments { value: block_hash, span: CheatSpan::Indefinite, target: contract_address, }), ); } pub fn stop_cheat_block_hash(&mut self, contract_address: ContractAddress, block_number: u64) { self.cheat_block_hash(block_number, Operation::Stop(contract_address)); } pub fn start_cheat_block_hash_global(&mut self, block_number: u64, block_hash: Felt) { self.cheat_block_hash(block_number, Operation::StartGlobal(block_hash)); } pub fn stop_cheat_block_hash_global(&mut self, block_number: u64) { self.cheat_block_hash(block_number, Operation::StopGlobal); } pub fn get_cheated_block_hash_for_contract( &mut self, contract_address: ContractAddress, block_number: u64, ) -> Option { if let Some((cheat_span, block_hash)) = self .block_hash_contracts .get(&(contract_address, block_number)) .copied() { match cheat_span { CheatSpan::TargetCalls(n) if n.get() == 1 => { self.block_hash_contracts .remove(&(contract_address, block_number)); } CheatSpan::TargetCalls(num) => { let calls_number = num.get() - 1; self.block_hash_contracts.insert( (contract_address, block_number), ( CheatSpan::TargetCalls( NonZeroUsize::new(calls_number) .expect("`NonZeroUsize` should not be zero after decrement"), ), block_hash, ), ); } CheatSpan::Indefinite => {} } return Some(BlockHash(StarkHash::from(block_hash))); } if let Some((block_hash, except)) = self.global_block_hash.get(&block_number) && !except.contains(&contract_address) { return Some(BlockHash(StarkHash::from(*block_hash))); } None } pub fn get_block_hash_for_contract( &mut self, contract_address: ContractAddress, block_number: u64, syscall_handler: &mut SyscallHintProcessor, ) -> SyscallResult { let cheated_block_hash = self.get_cheated_block_hash_for_contract(contract_address, block_number); if let Some(cheated_block_hash) = cheated_block_hash { Ok(cheated_block_hash) } else { Ok(BlockHash( syscall_handler .base .get_block_hash(BlockNumber(block_number))?, )) } } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/cheat_block_number.rs ================================================ use super::cheat_execution_info::{ BlockInfoMockOperations, CheatArguments, ExecutionInfoMockOperations, Operation, }; use crate::CheatnetState; use crate::state::CheatSpan; use starknet_api::core::ContractAddress; impl CheatnetState { pub fn cheat_block_number( &mut self, contract_address: ContractAddress, block_number: u64, span: CheatSpan, ) { self.cheat_execution_info(ExecutionInfoMockOperations { block_info: BlockInfoMockOperations { block_number: Operation::Start(CheatArguments { value: block_number, span, target: contract_address, }), ..Default::default() }, ..Default::default() }); } pub fn start_cheat_block_number_global(&mut self, block_number: u64) { self.cheat_execution_info(ExecutionInfoMockOperations { block_info: BlockInfoMockOperations { block_number: Operation::StartGlobal(block_number), ..Default::default() }, ..Default::default() }); } pub fn start_cheat_block_number( &mut self, contract_address: ContractAddress, block_number: u64, ) { self.cheat_block_number(contract_address, block_number, CheatSpan::Indefinite); } pub fn stop_cheat_block_number(&mut self, contract_address: ContractAddress) { self.cheat_execution_info(ExecutionInfoMockOperations { block_info: BlockInfoMockOperations { block_number: Operation::Stop(contract_address), ..Default::default() }, ..Default::default() }); } pub fn stop_cheat_block_number_global(&mut self) { self.cheat_execution_info(ExecutionInfoMockOperations { block_info: BlockInfoMockOperations { block_number: Operation::StopGlobal, ..Default::default() }, ..Default::default() }); } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/cheat_block_timestamp.rs ================================================ use super::cheat_execution_info::{ BlockInfoMockOperations, CheatArguments, ExecutionInfoMockOperations, Operation, }; use crate::CheatnetState; use crate::state::CheatSpan; use starknet_api::core::ContractAddress; impl CheatnetState { pub fn cheat_block_timestamp( &mut self, contract_address: ContractAddress, timestamp: u64, span: CheatSpan, ) { self.cheat_execution_info(ExecutionInfoMockOperations { block_info: BlockInfoMockOperations { block_timestamp: Operation::Start(CheatArguments { value: timestamp, span, target: contract_address, }), ..Default::default() }, ..Default::default() }); } pub fn start_cheat_block_timestamp_global(&mut self, timestamp: u64) { self.cheat_execution_info(ExecutionInfoMockOperations { block_info: BlockInfoMockOperations { block_timestamp: Operation::StartGlobal(timestamp), ..Default::default() }, ..Default::default() }); } pub fn start_cheat_block_timestamp( &mut self, contract_address: ContractAddress, timestamp: u64, ) { self.cheat_block_timestamp(contract_address, timestamp, CheatSpan::Indefinite); } pub fn stop_cheat_block_timestamp(&mut self, contract_address: ContractAddress) { self.cheat_execution_info(ExecutionInfoMockOperations { block_info: BlockInfoMockOperations { block_timestamp: Operation::Stop(contract_address), ..Default::default() }, ..Default::default() }); } pub fn stop_cheat_block_timestamp_global(&mut self) { self.cheat_execution_info(ExecutionInfoMockOperations { block_info: BlockInfoMockOperations { block_timestamp: Operation::StopGlobal, ..Default::default() }, ..Default::default() }); } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/cheat_caller_address.rs ================================================ use super::cheat_execution_info::{CheatArguments, ExecutionInfoMockOperations, Operation}; use crate::CheatnetState; use crate::state::CheatSpan; use starknet_api::core::ContractAddress; impl CheatnetState { pub fn cheat_caller_address( &mut self, target: ContractAddress, caller_address: ContractAddress, span: CheatSpan, ) { self.cheat_execution_info(ExecutionInfoMockOperations { caller_address: Operation::Start(CheatArguments { value: caller_address, span, target, }), ..Default::default() }); } pub fn start_cheat_caller_address_global(&mut self, caller_address: ContractAddress) { self.cheat_execution_info(ExecutionInfoMockOperations { caller_address: Operation::StartGlobal(caller_address), ..Default::default() }); } pub fn start_cheat_caller_address( &mut self, target: ContractAddress, caller_address: ContractAddress, ) { self.cheat_caller_address(target, caller_address, CheatSpan::Indefinite); } pub fn stop_cheat_caller_address(&mut self, target: ContractAddress) { self.cheat_execution_info(ExecutionInfoMockOperations { caller_address: Operation::Stop(target), ..Default::default() }); } pub fn stop_cheat_caller_address_global(&mut self) { self.cheat_execution_info(ExecutionInfoMockOperations { caller_address: Operation::StopGlobal, ..Default::default() }); } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/cheat_execution_info.rs ================================================ use crate::{ CheatnetState, state::{CheatSpan, CheatStatus}, }; use conversions::serde::{deserialize::CairoDeserialize, serialize::CairoSerialize}; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; #[derive(CairoDeserialize, Clone, Debug)] pub struct CheatArguments { pub value: T, pub span: CheatSpan, pub target: ContractAddress, } #[derive(CairoDeserialize, Clone, Default, Debug)] pub enum Operation { StartGlobal(T), Start(CheatArguments), Stop(ContractAddress), StopGlobal, #[default] Retain, } #[derive(CairoDeserialize, CairoSerialize, Clone, Default, Debug, Eq, PartialEq)] pub struct ResourceBounds { pub resource: Felt, pub max_amount: u64, pub max_price_per_unit: u128, } #[derive(Clone, Default, Debug, PartialEq, Eq)] pub struct TxInfoMock { pub version: CheatStatus, pub account_contract_address: CheatStatus, pub max_fee: CheatStatus, pub signature: CheatStatus>, pub transaction_hash: CheatStatus, pub chain_id: CheatStatus, pub nonce: CheatStatus, pub resource_bounds: CheatStatus>, pub tip: CheatStatus, pub paymaster_data: CheatStatus>, pub nonce_data_availability_mode: CheatStatus, pub fee_data_availability_mode: CheatStatus, pub account_deployment_data: CheatStatus>, pub proof_facts: CheatStatus>, } #[derive(Clone, Default, Debug, PartialEq, Eq)] pub struct BlockInfoMock { pub block_number: CheatStatus, pub block_timestamp: CheatStatus, pub sequencer_address: CheatStatus, } #[derive(Clone, Default, Debug)] pub struct ExecutionInfoMock { pub block_info: BlockInfoMock, pub tx_info: TxInfoMock, pub caller_address: CheatStatus, pub contract_address: CheatStatus, } #[derive(CairoDeserialize, Clone, Default, Debug)] pub struct TxInfoMockOperations { pub version: Operation, pub account_contract_address: Operation, pub max_fee: Operation, pub signature: Operation>, pub transaction_hash: Operation, pub chain_id: Operation, pub nonce: Operation, pub resource_bounds: Operation>, pub tip: Operation, pub paymaster_data: Operation>, pub nonce_data_availability_mode: Operation, pub fee_data_availability_mode: Operation, pub account_deployment_data: Operation>, pub proof_facts: Operation>, } #[derive(CairoDeserialize, Clone, Default, Debug)] pub struct BlockInfoMockOperations { pub block_number: Operation, pub block_timestamp: Operation, pub sequencer_address: Operation, } #[derive(CairoDeserialize, Clone, Default, Debug)] pub struct ExecutionInfoMockOperations { pub block_info: BlockInfoMockOperations, pub tx_info: TxInfoMockOperations, pub caller_address: Operation, pub contract_address: Operation, } macro_rules! for_all_fields { ($macro:ident!) => { $macro!(caller_address); $macro!(contract_address); $macro!(block_info.block_number); $macro!(block_info.block_timestamp); $macro!(block_info.sequencer_address); $macro!(tx_info.version); $macro!(tx_info.account_contract_address); $macro!(tx_info.max_fee); $macro!(tx_info.signature); $macro!(tx_info.transaction_hash); $macro!(tx_info.chain_id); $macro!(tx_info.nonce); $macro!(tx_info.resource_bounds); $macro!(tx_info.tip); $macro!(tx_info.paymaster_data); $macro!(tx_info.nonce_data_availability_mode); $macro!(tx_info.fee_data_availability_mode); $macro!(tx_info.account_deployment_data); $macro!(tx_info.proof_facts); }; } impl CheatnetState { pub fn get_cheated_execution_info_for_contract( &mut self, target: ContractAddress, ) -> &mut ExecutionInfoMock { self.cheated_execution_info_contracts .entry(target) .or_insert_with(|| self.global_cheated_execution_info.clone()) } pub fn cheat_execution_info(&mut self, execution_info_mock: ExecutionInfoMockOperations) { macro_rules! cheat { ($($path:ident).+) => { match execution_info_mock.$($path).+ { Operation::Retain => {} Operation::Start(CheatArguments { value, span, target, }) => { let cheated_info = self.get_cheated_execution_info_for_contract(target); cheated_info.$($path).+ = CheatStatus::Cheated(value, span); } Operation::Stop(target) => { let cheated_info = self.get_cheated_execution_info_for_contract(target); cheated_info.$($path).+ = CheatStatus::Uncheated; } Operation::StartGlobal(value) => { self.global_cheated_execution_info.$($path).+ = CheatStatus::Cheated(value.clone(), CheatSpan::Indefinite); for val in self.cheated_execution_info_contracts.values_mut() { val.$($path).+ = CheatStatus::Cheated(value.clone(), CheatSpan::Indefinite); } } Operation::StopGlobal => { self.global_cheated_execution_info.$($path).+ = CheatStatus::Uncheated; for val in self.cheated_execution_info_contracts.values_mut() { val.$($path).+ = CheatStatus::Uncheated; } } }; }; } for_all_fields!(cheat!); } pub fn progress_cheated_execution_info(&mut self, address: ContractAddress) { let mocks = self.get_cheated_execution_info_for_contract(address); macro_rules! decrement { ($($path:ident).+) => { mocks.$($path).+.decrement_cheat_span(); }; } for_all_fields!(decrement!); } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/cheat_sequencer_address.rs ================================================ use super::cheat_execution_info::{ BlockInfoMockOperations, CheatArguments, ExecutionInfoMockOperations, Operation, }; use crate::CheatnetState; use crate::state::CheatSpan; use starknet_api::core::ContractAddress; impl CheatnetState { pub fn cheat_sequencer_address( &mut self, contract_address: ContractAddress, sequencer_address: ContractAddress, span: CheatSpan, ) { self.cheat_execution_info(ExecutionInfoMockOperations { block_info: BlockInfoMockOperations { sequencer_address: Operation::Start(CheatArguments { value: sequencer_address, span, target: contract_address, }), ..Default::default() }, ..Default::default() }); } pub fn start_cheat_sequencer_address( &mut self, contract_address: ContractAddress, sequencer_address: ContractAddress, ) { self.cheat_sequencer_address(contract_address, sequencer_address, CheatSpan::Indefinite); } pub fn start_cheat_sequencer_address_global(&mut self, sequencer_address: ContractAddress) { self.cheat_execution_info(ExecutionInfoMockOperations { block_info: BlockInfoMockOperations { sequencer_address: Operation::StartGlobal(sequencer_address), ..Default::default() }, ..Default::default() }); } pub fn stop_cheat_sequencer_address(&mut self, contract_address: ContractAddress) { self.cheat_execution_info(ExecutionInfoMockOperations { block_info: BlockInfoMockOperations { sequencer_address: Operation::Stop(contract_address), ..Default::default() }, ..Default::default() }); } pub fn stop_cheat_sequencer_address_global(&mut self) { self.cheat_execution_info(ExecutionInfoMockOperations { block_info: BlockInfoMockOperations { sequencer_address: Operation::StopGlobal, ..Default::default() }, ..Default::default() }); } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/declare.rs ================================================ use crate::constants::get_current_sierra_version; use crate::runtime_extensions::forge_runtime_extension::{ cheatcodes::{CheatcodeError, EnhancedHintError}, contracts_data::ContractsData, }; use anyhow::{Context, Result}; use blockifier::execution::contract_class::{CompiledClassV1, RunnableCompiledClass}; #[cfg(feature = "cairo-native")] use blockifier::execution::native::contract_class::NativeCompiledClassV1; use blockifier::state::{errors::StateError, state_api::State}; use conversions::IntoConv; use conversions::serde::serialize::CairoSerialize; use scarb_api::StarknetContractArtifacts; use starknet_api::core::{ClassHash, CompiledClassHash}; use starknet_rust::core::types::contract::SierraClass; #[derive(CairoSerialize)] pub enum DeclareResult { Success(ClassHash), AlreadyDeclared(ClassHash), } pub fn declare( state: &mut dyn State, contract_name: &str, contracts_data: &ContractsData, ) -> Result { let contract_artifact = contracts_data .get_artifacts(contract_name) .with_context(|| format!("Failed to get contract artifact for name = {contract_name}.")) .map_err(EnhancedHintError::from)?; let contract_class = get_contract_class(contract_artifact); let class_hash = *contracts_data .get_class_hash(contract_name) .expect("Failed to get class hash"); match state.get_compiled_class(class_hash) { Err(StateError::UndeclaredClassHash(_)) => { // Class is undeclared; declare it. state .set_contract_class(class_hash, contract_class) .map_err(EnhancedHintError::from)?; // NOTE: Compiled class hash is being set to 0 here // because it is currently only used in verification // and we haven't found a way to calculate it easily state .set_compiled_class_hash(class_hash, CompiledClassHash::default()) .unwrap_or_else(|err| panic!("Failed to set compiled class hash: {err:?}")); Ok(DeclareResult::Success(class_hash)) } Err(error) => Err(CheatcodeError::Unrecoverable(EnhancedHintError::State( error, ))), Ok(_) => { // Class is already declared, cannot redeclare // (i.e., make sure the leaf is uninitialized). Ok(DeclareResult::AlreadyDeclared(class_hash)) } } } pub fn get_class_hash(sierra_class: &SierraClass) -> Result { Ok(sierra_class.class_hash()?.into_()) } fn get_contract_class(contract_artifact: &StarknetContractArtifacts) -> RunnableCompiledClass { let contract_class = CompiledClassV1::try_from((contract_artifact.casm.clone(), get_current_sierra_version())) .expect("Failed to read contract class from json"); #[cfg(feature = "cairo-native")] return match &contract_artifact.executor { None => RunnableCompiledClass::V1(contract_class), Some(executor) => RunnableCompiledClass::V1Native(NativeCompiledClassV1::new( executor.clone(), contract_class, )), }; #[cfg(not(feature = "cairo-native"))] RunnableCompiledClass::V1(contract_class) } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/generate_random_felt.rs ================================================ use num_bigint::{BigUint, RandBigInt}; use starknet_types_core::felt::Felt; #[must_use] pub fn generate_random_felt() -> Felt { let mut rng = rand::thread_rng(); let random_number: BigUint = rng.gen_biguint(251); Felt::from(random_number) } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/get_class_hash.rs ================================================ use crate::runtime_extensions::forge_runtime_extension::cheatcodes::{ CheatcodeError, EnhancedHintError, }; use blockifier::state::state_api::State; use starknet_api::core::{ClassHash, ContractAddress}; /// Gets the class hash at the given address. pub fn get_class_hash( state: &mut dyn State, contract_address: ContractAddress, ) -> Result { match state.get_class_hash_at(contract_address) { Ok(class_hash) => Ok(class_hash), Err(e) => Err(CheatcodeError::Unrecoverable(EnhancedHintError::State(e))), } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/l1_handler_execute.rs ================================================ use crate::{ runtime_extensions::call_to_blockifier_runtime_extension::rpc::{CallResult, call_l1_handler}, state::CheatnetState, }; use blockifier::execution::syscalls::hint_processor::SyscallHintProcessor; use starknet_api::core::{ContractAddress, EntryPointSelector}; use starknet_types_core::felt::Felt; pub fn l1_handler_execute( syscall_handler: &mut SyscallHintProcessor, cheatnet_state: &mut CheatnetState, contract_address: ContractAddress, function_selector: EntryPointSelector, from_address: Felt, payload: &[Felt], ) -> CallResult { let mut calldata = vec![from_address]; calldata.extend_from_slice(payload); call_l1_handler( syscall_handler, cheatnet_state, &contract_address, function_selector, calldata.as_slice(), ) } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/mock_call.rs ================================================ use crate::CheatnetState; use crate::state::{CheatSpan, CheatStatus}; use starknet_api::core::{ContractAddress, EntryPointSelector}; use starknet_types_core::felt::Felt; use std::collections::hash_map::Entry; impl CheatnetState { pub fn mock_call( &mut self, contract_address: ContractAddress, function_selector: EntryPointSelector, ret_data: &[Felt], span: CheatSpan, ) { let contract_mocked_functions = self.mocked_functions.entry(contract_address).or_default(); contract_mocked_functions.insert( function_selector, CheatStatus::Cheated(ret_data.to_vec(), span), ); } pub fn start_mock_call( &mut self, contract_address: ContractAddress, function_selector: EntryPointSelector, ret_data: &[Felt], ) { self.mock_call( contract_address, function_selector, ret_data, CheatSpan::Indefinite, ); } pub fn stop_mock_call( &mut self, contract_address: ContractAddress, function_selector: EntryPointSelector, ) { if let Entry::Occupied(mut e) = self.mocked_functions.entry(contract_address) { let contract_mocked_functions = e.get_mut(); contract_mocked_functions.remove(&function_selector); } } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/mod.rs ================================================ use crate::runtime_extensions::call_to_blockifier_runtime_extension::rpc::CallFailure; use cairo_vm::vm::errors::hint_errors::HintError; use runtime::EnhancedHintError; use starknet_types_core::felt::Felt; pub mod cheat_account_contract_address; pub mod cheat_block_hash; pub mod cheat_block_number; pub mod cheat_block_timestamp; pub mod cheat_caller_address; pub mod cheat_execution_info; pub mod cheat_sequencer_address; pub mod declare; pub mod generate_random_felt; pub mod get_class_hash; pub mod l1_handler_execute; pub mod mock_call; pub mod precalculate_address; pub mod replace_bytecode; pub mod spy_events; pub mod spy_messages_to_l1; pub mod storage; /// A structure used for returning cheatcode errors in tests #[derive(Debug)] pub enum CheatcodeError { Recoverable(Vec), // Return error result in cairo Unrecoverable(EnhancedHintError), // Fail whole test } impl From for CheatcodeError { fn from(error: EnhancedHintError) -> Self { CheatcodeError::Unrecoverable(error) } } impl From for CheatcodeError { fn from(value: CallFailure) -> Self { match value { CallFailure::Recoverable { panic_data } => CheatcodeError::Recoverable(panic_data), CallFailure::Unrecoverable { msg } => CheatcodeError::Unrecoverable( HintError::CustomHint(Box::from(msg.to_string())).into(), ), } } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/precalculate_address.rs ================================================ use crate::CheatnetState; use crate::runtime_extensions::common::create_execute_calldata; use conversions::IntoConv; use runtime::starknet::constants::TEST_ADDRESS; use starknet_api::core::{ClassHash, ContractAddress, calculate_contract_address}; use starknet_types_core::felt::Felt; impl CheatnetState { #[must_use] pub fn precalculate_address( &self, class_hash: &ClassHash, calldata: &[Felt], ) -> ContractAddress { let salt = self.get_salt(); let execute_calldata = create_execute_calldata(calldata); let deployer_address = Felt::from_hex(TEST_ADDRESS).unwrap(); calculate_contract_address( salt, *class_hash, &execute_calldata, deployer_address.into_(), ) .unwrap() } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/replace_bytecode.rs ================================================ use crate::CheatnetState; use conversions::serde::serialize::CairoSerialize; use starknet_api::core::{ClassHash, ContractAddress}; impl CheatnetState { pub fn replace_class_for_contract( &mut self, contract_address: ContractAddress, class_hash: ClassHash, ) { self.replaced_bytecode_contracts .insert(contract_address, class_hash); } } #[derive(CairoSerialize)] pub enum ReplaceBytecodeError { ContractNotDeployed, UndeclaredClassHash, } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/spy_events.rs ================================================ use crate::CheatnetState; use blockifier::execution::call_info::OrderedEvent; use conversions::{FromConv, serde::serialize::CairoSerialize}; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; /// Represents an emitted event. It is used in the `CheatnetState` to keep track of events /// emitted in the `cheatnet::src::rpc::call_contract` #[derive(CairoSerialize, Debug, PartialEq, Clone)] pub struct Event { pub from: ContractAddress, pub keys: Vec, pub data: Vec, } impl Event { #[must_use] pub fn from_ordered_event( ordered_event: &OrderedEvent, contract_address: ContractAddress, ) -> Self { Self { from: contract_address, keys: ordered_event .event .keys .iter() .map(|key| Felt::from_(key.0)) .collect(), data: ordered_event .event .data .0 .iter() .map(|el| Felt::from_(*el)) .collect(), } } } impl CheatnetState { pub fn get_events(&mut self, event_offset: usize) -> Vec { self.detected_events[event_offset..].to_vec() } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/spy_messages_to_l1.rs ================================================ use crate::state::CheatnetState; use blockifier::execution::call_info::OrderedL2ToL1Message; use conversions::serde::serialize::CairoSerialize; use starknet_api::core::{ContractAddress, EthAddress}; use starknet_types_core::felt::Felt; #[derive(CairoSerialize, Clone)] pub struct MessageToL1 { from_address: ContractAddress, to_address: EthAddress, payload: Vec, } impl MessageToL1 { #[must_use] pub fn from_ordered_message( ordered_message: &OrderedL2ToL1Message, from_address: ContractAddress, ) -> MessageToL1 { Self { from_address, to_address: EthAddress::try_from(ordered_message.message.to_address) .expect("this address should be valid"), payload: ordered_message .message .payload .clone() .0 .into_iter() .map(conversions::IntoConv::into_) .collect(), } } } impl CheatnetState { #[must_use] pub fn get_messages_to_l1(&self, message_offset: usize) -> Vec { self.detected_messages_to_l1[message_offset..].to_vec() } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/storage.rs ================================================ use blockifier::state::state_api::State; use conversions::{FromConv, IntoConv}; use starknet_api::core::{ContractAddress, EntryPointSelector, PatriciaKey}; use starknet_api::hash::StarkHash; use starknet_api::state::StorageKey; use starknet_rust::core::crypto::pedersen_hash; use starknet_rust::core::utils::get_selector_from_name; use starknet_types_core::felt::Felt; use starknet_types_core::felt::NonZeroFelt; /// /// # Arguments /// /// * `blockifier_state`: Blockifier state reader /// * `target`: The address of the contract we want to target /// * `storage_address`: Storage address of the felt value we want to store /// * `value`: A felt value to write at `storage_address` /// /// returns: Result<(), Error> - a result containing the error if `store` failed /// pub fn store( state: &mut dyn State, target: ContractAddress, storage_address: Felt, value: Felt, ) -> Result<(), anyhow::Error> { state.set_storage_at(target, storage_key(storage_address)?, value.into_())?; Ok(()) } /// /// # Arguments /// /// * `blockifier_state`: Blockifier state reader /// * `target`: The address of the contract we want to target /// * `storage_address`: Storage address of the felt value we want to load /// /// returns: Result, Error> - a result containing the read data /// pub fn load( state: &mut dyn State, target: ContractAddress, storage_address: Felt, ) -> Result { Ok(state .get_storage_at(target, storage_key(storage_address)?)? .into_()) } /// The address after hashing with pedersen, needs to be taken with a specific modulo value (2^251 - 256) /// For details see: /// #[must_use] fn normalize_storage_address(address: Felt) -> Felt { let modulus = NonZeroFelt::from_felt_unchecked(Felt::from(2).pow(251_u128) - Felt::from(256)); address.mod_floor(&modulus) } #[must_use] pub fn calculate_variable_address(selector: Felt, key: Option<&[Felt]>) -> Felt { let mut address: Felt = selector.into_(); match key { None => address.into_(), Some(key) => { for key_part in key { address = pedersen_hash(&address, &((*key_part).into_())); } normalize_storage_address(address).into_() } } } #[must_use] pub fn variable_address(var_name: &str) -> Felt { calculate_variable_address(selector_from_name(var_name).into_(), None) } #[must_use] pub fn selector_from_name(name: &str) -> EntryPointSelector { let selector = get_selector_from_name(name).unwrap(); selector.into_() } pub fn storage_key(storage_address: Felt) -> Result { Ok(StorageKey(PatriciaKey::try_from(StarkHash::from_( storage_address, ))?)) } #[must_use] pub fn map_entry_address(var_name: &str, key: &[Felt]) -> Felt { calculate_variable_address(selector_from_name(var_name).into_(), Some(key)) } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/contracts_data.rs ================================================ use super::cheatcodes::declare::get_class_hash; use anyhow::Result; use bimap::BiMap; use camino::Utf8PathBuf; use conversions::IntoConv; use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}; use runtime::starknet::constants::TEST_CONTRACT_CLASS_HASH; use scarb_api::StarknetContractArtifacts; use starknet_api::core::{ClassHash, EntryPointSelector}; use starknet_rust::core::types::contract::{AbiEntry, SierraClass}; use starknet_rust::core::utils::get_selector_from_name; use starknet_types_core::felt::Felt; use std::collections::HashMap; type ContractName = String; type FunctionName = String; #[derive(Debug, Clone, PartialEq, Default)] pub struct ContractsData { pub contracts: HashMap, pub class_hashes: BiMap, pub selectors: HashMap, } #[derive(Debug, Clone, PartialEq)] pub struct ContractData { pub artifacts: StarknetContractArtifacts, pub class_hash: ClassHash, source_sierra_path: Utf8PathBuf, } impl ContractsData { pub fn try_from( contracts: HashMap, ) -> Result { let parsed_contracts: HashMap = contracts .par_iter() .map(|(name, (artifact, _))| { Ok((name.clone(), serde_json::from_str(&artifact.sierra)?)) }) .collect::>()?; let class_hashes: Vec<(ContractName, ClassHash)> = parsed_contracts .par_iter() .map(|(name, sierra_class)| Ok((name.clone(), get_class_hash(sierra_class)?))) .collect::>()?; let class_hashes = BiMap::from_iter(class_hashes); let contracts = contracts .into_iter() .map(|(name, (artifacts, source_sierra_path))| { let class_hash = *class_hashes.get_by_left(&name).unwrap(); ( name, ContractData { artifacts, class_hash, source_sierra_path, }, ) }) .collect(); let selectors = parsed_contracts .into_par_iter() .map(|(_, sierra_class)| build_name_selector_map(sierra_class.abi)) .flatten() .collect(); Ok(ContractsData { contracts, class_hashes, selectors, }) } #[must_use] pub fn get_artifacts(&self, contract_name: &str) -> Option<&StarknetContractArtifacts> { self.contracts .get(contract_name) .map(|contract| &contract.artifacts) } #[must_use] pub fn get_class_hash(&self, contract_name: &str) -> Option<&ClassHash> { self.contracts .get(contract_name) .map(|contract| &contract.class_hash) } #[must_use] pub fn get_source_sierra_path(&self, contract_name: &str) -> Option<&Utf8PathBuf> { self.contracts .get(contract_name) .map(|contract| &contract.source_sierra_path) } #[must_use] pub fn get_contract_name(&self, class_hash: &ClassHash) -> Option<&ContractName> { self.class_hashes.get_by_right(class_hash) } #[must_use] pub fn get_function_name( &self, entry_point_selector: &EntryPointSelector, ) -> Option<&FunctionName> { self.selectors.get(entry_point_selector) } #[must_use] pub fn is_fork_class_hash(&self, class_hash: &ClassHash) -> bool { if class_hash.0 == Felt::from_hex_unchecked(TEST_CONTRACT_CLASS_HASH) { false } else { !self.class_hashes.contains_right(class_hash) } } } #[must_use] pub fn build_name_selector_map(abi: Vec) -> HashMap { let mut selector_map = HashMap::new(); for abi_entry in abi { match abi_entry { AbiEntry::Interface(abi_interface) => { for abi_entry in abi_interface.items { add_simple_abi_entry_to_mapping(abi_entry, &mut selector_map); } } _ => add_simple_abi_entry_to_mapping(abi_entry, &mut selector_map), } } selector_map } fn add_simple_abi_entry_to_mapping( abi_entry: AbiEntry, selector_map: &mut HashMap, ) { match abi_entry { AbiEntry::Function(abi_function) | AbiEntry::L1Handler(abi_function) => { selector_map.insert( get_selector_from_name(&abi_function.name).unwrap().into_(), abi_function.name, ); } AbiEntry::Constructor(abi_constructor) => { selector_map.insert( get_selector_from_name(&abi_constructor.name) .unwrap() .into_(), abi_constructor.name, ); } _ => {} } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/file_operations.rs ================================================ use anyhow::{Result, anyhow}; use conversions::felt::TryInferFormat; use conversions::{ byte_array::ByteArray, serde::serialize::SerializeToFeltVec, string::TryFromDecStr, }; use flatten_serde_json::flatten; use runtime::EnhancedHintError; use serde_json::{Map, Value}; use starknet_types_core::felt::Felt; use std::fs::read_to_string; pub(super) fn read_txt(path: String) -> Result, EnhancedHintError> { Ok(read_to_string(&path)? .lines() .filter(|line| !line.is_empty()) .map(str::trim) .map(Felt::infer_format_and_parse) .collect::, _>>() .map_err(|_| EnhancedHintError::FileParsing { path })? .into_iter() .flatten() .collect()) } pub(super) fn read_json(path: String) -> Result, EnhancedHintError> { let content = read_to_string(&path)?; let json: Map = serde_json::from_str(&content) .map_err(|e| anyhow!("Parse JSON error: {e} , in file {path}"))?; let data = flatten(&json); let mut result = vec![]; let mut keys: Vec<_> = data.keys().collect(); keys.sort_by_key(|a| a.to_lowercase()); keys.into_iter() .try_for_each(|key| value_into_vec(data.get(key).unwrap(), &mut result)) .map_err(|_| EnhancedHintError::FileParsing { path })?; Ok(result) } fn value_into_vec(value: &Value, output: &mut Vec) -> Result<()> { match value { Value::Array(vec) => { output.push(vec.len().into()); for value in vec { value_into_vec(value, output)?; } Ok(()) } Value::Number(num) => { output.push(Felt::try_from_dec_str(&num.to_string())?); Ok(()) } Value::String(string) => { output.extend(ByteArray::from(string.as_str()).serialize_to_vec()); Ok(()) } _ => { unreachable!( "flatten_serde_json::flatten leaves only numbers string and array of numbers and strings" ); } } } #[cfg(test)] mod tests { use super::read_json; use conversions::{byte_array::ByteArray, serde::serialize::SerializeToFeltVec}; use starknet_types_core::felt::Felt; use std::fs; use tempfile::TempDir; fn create_file(content: impl AsRef<[u8]>) -> (TempDir, String) { let temp = TempDir::new().unwrap(); let file = format!("{}/file.json", temp.path().to_string_lossy()); fs::write(&file, content).unwrap(); (temp, file) } #[test] fn test_json_values_sorted_by_keys() { let string = r#" { "name": "Joh", "age": 43, "a": { "b": 1, "c": 2 }, "ab": 12 }"#; let (_temp, file_path) = create_file(string); let result = read_json(file_path).unwrap(); let mut expected_result = vec![Felt::from(1), Felt::from(2), Felt::from(12), Felt::from(43)]; expected_result.extend(ByteArray::from("Joh").serialize_to_vec()); assert_eq!(result, expected_result); let string = r#" { "ad": "string", "test": ["1",2,"3",4] }"#; let (_temp, file_path) = create_file(string); let result = read_json(file_path).unwrap(); let mut expected_result = ByteArray::from("string").serialize_to_vec(); expected_result.push(Felt::from(4)); expected_result.extend(ByteArray::from("1").serialize_to_vec()); expected_result.push(Felt::from(2)); expected_result.extend(ByteArray::from("3").serialize_to_vec()); expected_result.push(Felt::from(4)); assert_eq!(result, expected_result); } #[test] fn test_json_values_sorted_by_keys_invalid_data() { let string = r" [1,2,'3232']"; let (_temp, file_path) = create_file(string); let result = read_json(file_path); assert!(result.is_err()); let string = r#" { "test": 'invalid json format' }"#; let (_temp, file_path) = create_file(string); let result = read_json(file_path); assert!(result.is_err()); } } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/fuzzer.rs ================================================ use anyhow::ensure; use num_bigint::RandBigInt; use rand::prelude::StdRng; use starknet_types_core::felt::Felt; use std::sync::{Arc, Mutex}; pub(crate) fn generate_arg( fuzzer_rng: Option>>, min_value: Felt, max_value: Felt, ) -> anyhow::Result { let min_big_int = if min_value > (Felt::MAX + Felt::from(i128::MIN)) && min_value > max_value { // Negative value x is serialized as P + x, where P is the STARK prime number // hence to deserialize and get the actual x we need to subtract P (== Felt::MAX + 1) min_value.to_bigint() - Felt::MAX.to_bigint() - 1 } else { min_value.to_bigint() }; let max_big_int = max_value.to_bigint(); ensure!( min_big_int <= max_big_int, format!( "`generate_arg` cheatcode: `min_value` must be <= `max_value`, provided values after deserialization: {min_big_int} and {max_big_int}" ) ); let value = if let Some(fuzzer_rng) = fuzzer_rng { fuzzer_rng .lock() .expect("Failed to acquire lock on fuzzer_rng") .gen_bigint_range(&min_big_int, &(max_big_int + 1)) } else { // `generate_arg` cheatcode can be also used outside the fuzzer context rand::thread_rng().gen_bigint_range(&min_big_int, &(max_big_int + 1)) }; Ok(Felt::from(value)) } ================================================ FILE: crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs ================================================ use self::contracts_data::ContractsData; use crate::runtime_extensions::call_to_blockifier_runtime_extension::rpc::UsedResources; use crate::runtime_extensions::common::sum_syscall_usage; use crate::runtime_extensions::forge_runtime_extension::cheatcodes::replace_bytecode::ReplaceBytecodeError; use crate::runtime_extensions::{ call_to_blockifier_runtime_extension::{CallToBlockifierRuntime, rpc::CallFailure}, common::get_relocated_vm_trace, forge_runtime_extension::cheatcodes::{ CheatcodeError, declare::declare, generate_random_felt::generate_random_felt, get_class_hash::get_class_hash, l1_handler_execute::l1_handler_execute, storage::{calculate_variable_address, load, store}, }, }; use crate::trace_data::{CallTrace, CallTraceNode, GasReportData}; use anyhow::{Context, Result, anyhow}; use blockifier::blockifier_versioned_constants::VersionedConstants; use blockifier::bouncer::extended_execution_resources_to_gas; use blockifier::context::TransactionContext; use blockifier::execution::call_info::{ CallInfo, CallSummary, ChargedResources, EventSummary, ExecutionSummary, ExtendedExecutionResources, OrderedEvent, }; use blockifier::execution::contract_class::TrackedResource; use blockifier::execution::syscalls::vm_syscall_utils::{SyscallSelector, SyscallUsageMap}; use blockifier::state::errors::StateError; use blockifier::utils::u64_from_usize; use cairo_vm::vm::runners::cairo_runner::CairoRunner; use cairo_vm::vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}; use conversions::byte_array::ByteArray; use conversions::felt::{ToShortString, TryInferFormat}; use conversions::serde::SerializedValue; use conversions::serde::deserialize::BufferReader; use conversions::serde::serialize::{CairoSerialize, SerializeToFeltVec}; use data_transformer::cairo_types::CairoU256; use rand::prelude::StdRng; use runtime::{ CheatcodeHandlingResult, EnhancedHintError, ExtendedRuntime, ExtensionLogic, SyscallHandlingResult, }; use scarb_oracle_hint_service::OracleHintService; use starknet_api::execution_resources::GasAmount; use starknet_api::versioned_constants_logic::VersionedConstantsTrait; use starknet_api::{contract_class::EntryPointType::L1Handler, core::ClassHash}; use starknet_rust::signers::SigningKey; use starknet_types_core::felt::Felt; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::rc::Rc; use std::sync::{Arc, Mutex}; pub mod cheatcodes; pub mod contracts_data; mod file_operations; mod fuzzer; pub type ForgeRuntime<'a> = ExtendedRuntime>; pub struct ForgeExtension<'a> { pub environment_variables: &'a HashMap, pub contracts_data: &'a ContractsData, pub fuzzer_rng: Option>>, pub oracle_hint_service: OracleHintService, } // This runtime extension provides an implementation logic for functions from snforge_std library. impl<'a> ExtensionLogic for ForgeExtension<'a> { type Runtime = CallToBlockifierRuntime<'a>; // `generic_array` which is a transitive dependency of multiple packages we depend on // now, shows a deprecation warning at asking to upgrade to version 1.x #[expect(clippy::too_many_lines)] fn handle_cheatcode( &mut self, selector: &str, mut input_reader: BufferReader<'_>, extended_runtime: &mut Self::Runtime, vm: &VirtualMachine, ) -> Result { if let Some(oracle_selector) = self .oracle_hint_service .accept_cheatcode(selector.as_bytes()) { let output = self .oracle_hint_service .execute_cheatcode(oracle_selector, input_reader.into_remaining()); let mut reader = BufferReader::new(&output); let deserialized: Result, ByteArray> = reader.read()?; let converted = deserialized .map_err(|error| EnhancedHintError::OracleError { error }) .map(|r| r.serialize_to_vec())?; return Ok(CheatcodeHandlingResult::Handled(converted)); } match selector { "is_config_mode" => Ok(CheatcodeHandlingResult::from_serializable(false)), "cheat_execution_info" => { let execution_info = input_reader.read()?; extended_runtime .extended_runtime .extension .cheatnet_state .cheat_execution_info(execution_info); Ok(CheatcodeHandlingResult::from_serializable(())) } "mock_call" => { let contract_address = input_reader.read()?; let function_selector = input_reader.read()?; let span = input_reader.read()?; let ret_data: Vec<_> = input_reader.read()?; extended_runtime .extended_runtime .extension .cheatnet_state .mock_call(contract_address, function_selector, &ret_data, span); Ok(CheatcodeHandlingResult::from_serializable(())) } "stop_mock_call" => { let contract_address = input_reader.read()?; let function_selector = input_reader.read()?; extended_runtime .extended_runtime .extension .cheatnet_state .stop_mock_call(contract_address, function_selector); Ok(CheatcodeHandlingResult::from_serializable(())) } "replace_bytecode" => { let contract = input_reader.read()?; let class = input_reader.read()?; let is_undeclared = match extended_runtime .extended_runtime .extended_runtime .hint_handler .base .state .get_compiled_class(class) { Err(StateError::UndeclaredClassHash(_)) => true, Err(err) => return Err(err.into()), _ => false, }; let res = if extended_runtime .extended_runtime .extended_runtime .hint_handler .base .state .get_class_hash_at(contract)? == ClassHash::default() { Err(ReplaceBytecodeError::ContractNotDeployed) } else if is_undeclared { Err(ReplaceBytecodeError::UndeclaredClassHash) } else { extended_runtime .extended_runtime .extension .cheatnet_state .replace_class_for_contract(contract, class); Ok(()) }; Ok(CheatcodeHandlingResult::from_serializable(res)) } "declare" => { let state = &mut extended_runtime .extended_runtime .extended_runtime .hint_handler .base .state; let contract_name: String = input_reader.read::()?.to_string(); handle_declare_result(declare(*state, &contract_name, self.contracts_data)) } // Internal cheatcode used to pass a contract address when calling `deploy_at`. "set_deploy_at_address" => { let contract_address = input_reader.read()?; let state = &mut *extended_runtime.extended_runtime.extension.cheatnet_state; state.set_next_deploy_at_address(contract_address); Ok(CheatcodeHandlingResult::from_serializable(())) } // Internal cheatcode to mark next syscall as coming from cheatcode. "set_next_syscall_from_cheatcode" => { let state = &mut *extended_runtime.extended_runtime.extension.cheatnet_state; state.set_next_syscall_from_cheatcode(); Ok(CheatcodeHandlingResult::from_serializable(())) } "precalculate_address" => { let class_hash = input_reader.read()?; let calldata: Vec<_> = input_reader.read()?; let contract_address = extended_runtime .extended_runtime .extension .cheatnet_state .precalculate_address(&class_hash, &calldata); Ok(CheatcodeHandlingResult::from_serializable(contract_address)) } // Internal cheatcode to guarantee unique salts for each deployment // when deploying via a method of the `ContractClass` struct. "get_salt" => { let state = &mut *extended_runtime.extended_runtime.extension.cheatnet_state; let salt = state.get_salt(); state.increment_deploy_salt_base(); Ok(CheatcodeHandlingResult::from_serializable(salt)) } "var" => { let name: String = input_reader.read::()?.to_string(); let env_var = self .environment_variables .get(&name) .with_context(|| format!("Failed to read from env var = {name}"))?; let parsed_env_var = Felt::infer_format_and_parse(env_var) .map_err(|_| anyhow!("Failed to parse value = {env_var} to felt"))?; Ok(CheatcodeHandlingResult::Handled(parsed_env_var)) } "get_class_hash" => { let contract_address = input_reader.read()?; let state = &mut extended_runtime .extended_runtime .extended_runtime .hint_handler .base .state; match get_class_hash(*state, contract_address) { Ok(class_hash) => Ok(CheatcodeHandlingResult::from_serializable(class_hash)), Err(CheatcodeError::Recoverable(_)) => unreachable!(), Err(CheatcodeError::Unrecoverable(err)) => Err(err), } } "l1_handler_execute" => { let contract_address = input_reader.read()?; let function_selector = input_reader.read()?; let from_address = input_reader.read()?; let payload: Vec<_> = input_reader.read()?; let cheatnet_runtime = &mut extended_runtime.extended_runtime; let syscall_handler = &mut cheatnet_runtime.extended_runtime.hint_handler; match l1_handler_execute( syscall_handler, cheatnet_runtime.extension.cheatnet_state, contract_address, function_selector, from_address, &payload, ) { Ok(_) => Ok(CheatcodeHandlingResult::from_serializable(0_u8)), Err(CallFailure::Recoverable { panic_data }) => Ok( CheatcodeHandlingResult::from_serializable(Err::<(), _>(panic_data)), ), Err(CallFailure::Unrecoverable { msg }) => Err(EnhancedHintError::from( HintError::CustomHint(Box::from(msg.to_string())), )), } } "read_txt" => { let file_path: String = input_reader.read::()?.to_string(); let parsed_content = file_operations::read_txt(file_path)?; Ok(CheatcodeHandlingResult::Handled(parsed_content)) } "read_json" => { let file_path: String = input_reader.read::()?.to_string(); let parsed_content = file_operations::read_json(file_path)?; Ok(CheatcodeHandlingResult::Handled(parsed_content)) } "spy_events" => { let events_offset = extended_runtime .extended_runtime .extension .cheatnet_state .detected_events .len(); Ok(CheatcodeHandlingResult::from_serializable(events_offset)) } "get_events" => { let events_offset = input_reader.read()?; let events = extended_runtime .extended_runtime .extension .cheatnet_state .get_events(events_offset); Ok(CheatcodeHandlingResult::from_serializable(events)) } "spy_messages_to_l1" => { let messages_offset = extended_runtime .extended_runtime .extension .cheatnet_state .detected_messages_to_l1 .len(); Ok(CheatcodeHandlingResult::from_serializable(messages_offset)) } "get_messages_to_l1" => { let messages_offset = input_reader.read()?; let messages = extended_runtime .extended_runtime .extension .cheatnet_state .get_messages_to_l1(messages_offset); Ok(CheatcodeHandlingResult::from_serializable(messages)) } "generate_stark_keys" => { let key_pair = SigningKey::from_random(); Ok(CheatcodeHandlingResult::from_serializable(( key_pair.secret_scalar(), key_pair.verifying_key().scalar(), ))) } "stark_sign_message" => { let private_key = input_reader.read()?; let message_hash = input_reader.read()?; if private_key == Felt::from(0_u8) { return Ok(CheatcodeHandlingResult::from_serializable(Err::<(), _>( SignError::InvalidSecretKey, ))); } let key_pair = SigningKey::from_secret_scalar(private_key); let result = if let Ok(signature) = key_pair.sign(&message_hash) { Ok((signature.r, signature.s)) } else { Err(SignError::HashOutOfRange) }; Ok(CheatcodeHandlingResult::from_serializable(result)) } "generate_ecdsa_keys" => { let curve: Felt = input_reader.read()?; let curve = curve.to_short_string().ok(); let (signing_key_bytes, x_coordinate_bytes, y_coordinate_bytes) = { let extract_coordinates_from_verifying_key = |verifying_key: Box<[u8]>| { let verifying_key = verifying_key.iter().as_slice(); ( verifying_key[1..33].try_into().unwrap(), verifying_key[33..65].try_into().unwrap(), ) }; match curve.as_deref() { Some("Secp256k1") => { let signing_key = k256::ecdsa::SigningKey::random( &mut k256::elliptic_curve::rand_core::OsRng, ); let verifying_key = signing_key .verifying_key() .to_encoded_point(false) .to_bytes(); let (x_coordinate, y_coordinate) = extract_coordinates_from_verifying_key(verifying_key); ( signing_key.to_bytes().as_slice()[0..32].try_into().unwrap(), x_coordinate, y_coordinate, ) } Some("Secp256r1") => { let signing_key = p256::ecdsa::SigningKey::random( &mut p256::elliptic_curve::rand_core::OsRng, ); let verifying_key = signing_key .verifying_key() .to_encoded_point(false) .to_bytes(); let (x_coordinate, y_coordinate) = extract_coordinates_from_verifying_key(verifying_key); ( signing_key.to_bytes().as_slice()[0..32].try_into().unwrap(), x_coordinate, y_coordinate, ) } _ => return Ok(CheatcodeHandlingResult::Forwarded), } }; Ok(CheatcodeHandlingResult::from_serializable(( CairoU256::from_bytes(&signing_key_bytes), CairoU256::from_bytes(&x_coordinate_bytes), // bytes of public_key's x-coordinate CairoU256::from_bytes(&y_coordinate_bytes), // bytes of public_key's y-coordinate ))) } "ecdsa_sign_message" => { let curve: Felt = input_reader.read()?; let curve = curve.to_short_string().ok(); let secret_key: CairoU256 = input_reader.read()?; let msg_hash: CairoU256 = input_reader.read()?; let result = { match curve.as_deref() { Some("Secp256k1") => { if let Ok(signing_key) = k256::ecdsa::SigningKey::from_slice(&secret_key.to_be_bytes()) { let signature: k256::ecdsa::Signature = k256::ecdsa::signature::hazmat::PrehashSigner::sign_prehash( &signing_key, &msg_hash.to_be_bytes(), ) .unwrap(); let signature = signature.normalize_s().unwrap_or(signature); Ok(signature.split_bytes()) } else { Err(SignError::InvalidSecretKey) } } Some("Secp256r1") => { if let Ok(signing_key) = p256::ecdsa::SigningKey::from_slice(&secret_key.to_be_bytes()) { let signature: p256::ecdsa::Signature = p256::ecdsa::signature::hazmat::PrehashSigner::sign_prehash( &signing_key, &msg_hash.to_be_bytes(), ) .unwrap(); let signature = signature.normalize_s().unwrap_or(signature); Ok(signature.split_bytes()) } else { Err(SignError::InvalidSecretKey) } } _ => return Ok(CheatcodeHandlingResult::Forwarded), } }; let result = result.map(|(r_bytes, s_bytes)| { let r_bytes: [u8; 32] = r_bytes.as_slice()[0..32].try_into().unwrap(); let s_bytes: [u8; 32] = s_bytes.as_slice()[0..32].try_into().unwrap(); ( CairoU256::from_bytes(&r_bytes), CairoU256::from_bytes(&s_bytes), ) }); Ok(CheatcodeHandlingResult::from_serializable(result)) } "get_call_trace" => { let call_trace = &extended_runtime .extended_runtime .extension .cheatnet_state .trace_data .current_call_stack .borrow_full_trace(); Ok(CheatcodeHandlingResult::from_serializable(call_trace)) } "store" => { let state = &mut extended_runtime .extended_runtime .extended_runtime .hint_handler .base .state; let target = input_reader.read()?; let storage_address = input_reader.read()?; store(*state, target, storage_address, input_reader.read()?) .context("Failed to store")?; Ok(CheatcodeHandlingResult::from_serializable(())) } "load" => { let state = &mut extended_runtime .extended_runtime .extended_runtime .hint_handler .base .state; let target = input_reader.read()?; let storage_address = input_reader.read()?; let loaded = load(*state, target, storage_address).context("Failed to load")?; Ok(CheatcodeHandlingResult::from_serializable(loaded)) } "map_entry_address" => { let map_selector = input_reader.read()?; let keys: Vec<_> = input_reader.read()?; let map_entry_address = calculate_variable_address(map_selector, Some(&keys)); Ok(CheatcodeHandlingResult::from_serializable( map_entry_address, )) } "generate_random_felt" => Ok(CheatcodeHandlingResult::from_serializable( generate_random_felt(), )), "generate_arg" => { let min_value = input_reader.read()?; let max_value = input_reader.read()?; Ok(CheatcodeHandlingResult::from_serializable( fuzzer::generate_arg(self.fuzzer_rng.clone(), min_value, max_value)?, )) } "save_fuzzer_arg" => { let arg = input_reader.read::()?.to_string(); extended_runtime .extended_runtime .extension .cheatnet_state // Skip first character, which is a snapshot symbol '@' .update_fuzzer_args(arg[1..].to_string()); Ok(CheatcodeHandlingResult::from_serializable(())) } "set_block_hash" => { let block_number = input_reader.read()?; let operation = input_reader.read()?; extended_runtime .extended_runtime .extension .cheatnet_state .cheat_block_hash(block_number, operation); Ok(CheatcodeHandlingResult::from_serializable(())) } "get_current_vm_step" => { // Each contract call is executed in separate VM, hence all VM steps // are calculated as sum of steps from calls + current VM steps. // Since syscalls are added to VM resources after the execution, we need // to include them manually here. let top_call = extended_runtime .extended_runtime .extension .cheatnet_state .trace_data .current_call_stack .top(); let vm_steps_from_inner_calls = calculate_vm_steps_from_calls(&top_call); let top_call_syscalls = &extended_runtime .extended_runtime .extended_runtime .hint_handler .base .syscalls_usage; let vm_steps_from_syscalls = &VersionedConstants::latest_constants() .get_additional_os_syscall_resources(top_call_syscalls) .n_steps; let total_vm_steps = vm_steps_from_inner_calls + vm_steps_from_syscalls + vm.get_current_step(); Ok(CheatcodeHandlingResult::from_serializable(total_vm_steps)) } _ => Ok(CheatcodeHandlingResult::Forwarded), } } fn override_system_call( &mut self, selector: SyscallSelector, _vm: &mut VirtualMachine, _extended_runtime: &mut Self::Runtime, ) -> Result { match selector { SyscallSelector::ReplaceClass => Err(HintError::CustomHint(Box::from( "Replace class can't be used in tests", ))), _ => Ok(SyscallHandlingResult::Forwarded), } } } #[derive(CairoSerialize)] enum SignError { InvalidSecretKey, HashOutOfRange, } fn handle_declare_result( declare_result: Result, ) -> Result { let result = match declare_result { Ok(data) => Ok(data), Err(CheatcodeError::Recoverable(panic_data)) => Err(panic_data), Err(CheatcodeError::Unrecoverable(err)) => return Err(err), }; Ok(CheatcodeHandlingResult::from_serializable(result)) } pub fn add_resources_to_top_call( runtime: &mut ForgeRuntime, resources: &ExtendedExecutionResources, tracked_resource: &TrackedResource, ) { let versioned_constants = runtime .extended_runtime .extended_runtime .extended_runtime .hint_handler .base .context .tx_context .block_context .versioned_constants(); let top_call = runtime .extended_runtime .extended_runtime .extension .cheatnet_state .trace_data .current_call_stack .top(); let mut top_call = top_call.borrow_mut(); match tracked_resource { TrackedResource::CairoSteps => top_call.used_execution_resources += resources, TrackedResource::SierraGas => { let builtin_gas_costs = versioned_constants.os_constants.gas_costs.builtins; top_call.gas_consumed += extended_execution_resources_to_gas( resources, &builtin_gas_costs, versioned_constants, ) .0; } } } pub fn update_top_call_resources( runtime: &mut ForgeRuntime, top_call_tracked_resource: TrackedResource, ) { // call representing the test code let top_call = runtime .extended_runtime .extended_runtime .extension .cheatnet_state .trace_data .current_call_stack .top(); let all_execution_resources = add_execution_resources(top_call.clone()); let all_sierra_gas_consumed = add_sierra_gas_resources(&top_call); // Below syscall usages are cumulative, meaning they include syscalls from their inner calls. let nested_calls_syscalls_vm_resources = get_nested_calls_syscalls_vm_resources(&top_call); let nested_calls_syscalls_sierra_gas = get_nested_calls_syscalls_sierra_gas(&top_call); let mut top_call = top_call.borrow_mut(); top_call.used_execution_resources = all_execution_resources; top_call.gas_consumed = all_sierra_gas_consumed; // Syscall usage here is flat, meaning it only includes syscalls from current call (in this case the top-level call) let top_call_syscalls = runtime .extended_runtime .extended_runtime .extended_runtime .hint_handler .base .syscalls_usage .clone(); let mut total_syscalls_vm_resources = nested_calls_syscalls_vm_resources.clone(); let mut total_syscalls_sierra_gas = nested_calls_syscalls_sierra_gas.clone(); // Based on the tracked resource of top call, we add the syscall usage to respective totals. match top_call_tracked_resource { TrackedResource::CairoSteps => { total_syscalls_vm_resources = sum_syscall_usage(total_syscalls_vm_resources, &top_call_syscalls); } TrackedResource::SierraGas => { total_syscalls_sierra_gas = sum_syscall_usage(total_syscalls_sierra_gas, &top_call_syscalls); } } top_call.used_syscalls_vm_resources = total_syscalls_vm_resources; top_call.used_syscalls_sierra_gas = total_syscalls_sierra_gas; } /// Calculates the total syscall usage from nested calls where the tracked resource is Cairo steps. pub fn get_nested_calls_syscalls_vm_resources(trace: &Rc>) -> SyscallUsageMap { // Only sum 1-level since these include syscalls from inner calls trace .borrow() .nested_calls .iter() .filter_map(CallTraceNode::extract_entry_point_call) .fold(SyscallUsageMap::new(), |syscalls, trace| { sum_syscall_usage(syscalls, &trace.borrow().used_syscalls_vm_resources) }) } /// Calculates the total syscall usage from nested calls where the tracked resource is Sierra gas. pub fn get_nested_calls_syscalls_sierra_gas(trace: &Rc>) -> SyscallUsageMap { // Only sum 1-level since these include syscalls from inner calls trace .borrow() .nested_calls .iter() .filter_map(CallTraceNode::extract_entry_point_call) .fold(SyscallUsageMap::new(), |syscalls, trace| { sum_syscall_usage(syscalls, &trace.borrow().used_syscalls_sierra_gas) }) } // Only top-level is considered relevant since we can't have l1 handlers deeper than 1 level of nesting fn get_l1_handlers_payloads_lengths(inner_calls: &[CallInfo]) -> Vec { inner_calls .iter() .filter_map(|call_info| { if call_info.call.entry_point_type == L1Handler { return Some(call_info.call.calldata.0.len()); } None }) .collect() } pub fn update_top_call_l1_resources(runtime: &mut ForgeRuntime) { let all_l2_l1_message_sizes = runtime .extended_runtime .extended_runtime .extended_runtime .hint_handler .base .l2_to_l1_messages .iter() .map(|ordered_message| ordered_message.message.payload.0.len()) .collect(); // call representing the test code let top_call = runtime .extended_runtime .extended_runtime .extension .cheatnet_state .trace_data .current_call_stack .top(); top_call.borrow_mut().used_l1_resources.l2_l1_message_sizes = all_l2_l1_message_sizes; } pub fn update_top_call_vm_trace(runtime: &mut ForgeRuntime, cairo_runner: &mut CairoRunner) { let trace_data = &mut runtime .extended_runtime .extended_runtime .extension .cheatnet_state .trace_data; if trace_data.is_vm_trace_needed { trace_data.current_call_stack.top().borrow_mut().vm_trace = Some(get_relocated_vm_trace(cairo_runner)); } } pub fn compute_and_store_execution_summary(trace: &Rc>) { let execution_summary = if trace.borrow().nested_calls.is_empty() { get_execution_summary_without_nested_calls(trace) } else { let mut nested_calls_summaries = vec![]; for nested_call in &trace.borrow().nested_calls { if let CallTraceNode::EntryPointCall(nested_call) = nested_call { compute_and_store_execution_summary(nested_call); nested_calls_summaries.push( nested_call .borrow() .gas_report_data .as_ref() .expect("Gas report data must be set after calling `compute_and_store_execution_summary`") .execution_summary .clone()); } } let mut current_call_summary = get_execution_summary_without_nested_calls(trace) + nested_calls_summaries.into_iter().sum(); // vm_resources and gas_consumed of a call already contain the resources of its inner calls. current_call_summary.charged_resources.extended_vm_resources = trace.borrow().used_execution_resources.clone(); current_call_summary.charged_resources.gas_consumed = GasAmount(trace.borrow().gas_consumed); current_call_summary }; trace.borrow_mut().gas_report_data = Some(GasReportData::new(execution_summary.clone())); } // Based on blockifier/src/execution/call_info.rs (summarize) fn get_execution_summary_without_nested_calls(trace: &Rc>) -> ExecutionSummary { let current_call = trace.borrow(); ExecutionSummary { charged_resources: ChargedResources { extended_vm_resources: current_call.used_execution_resources.clone(), gas_consumed: GasAmount(current_call.gas_consumed), }, l2_to_l1_payload_lengths: current_call.used_l1_resources.l2_l1_message_sizes.clone(), event_summary: { let mut event_summary = EventSummary { n_events: current_call.events.len(), ..Default::default() }; for OrderedEvent { event, .. } in ¤t_call.events { event_summary.total_event_data_size += u64_from_usize(event.data.0.len()); event_summary.total_event_keys += u64_from_usize(event.keys.len()); } event_summary }, // Fields below are not relevant for partial gas calculation. call_summary: CallSummary::default(), executed_class_hashes: HashSet::default(), visited_storage_entries: HashSet::default(), } } fn add_sierra_gas_resources(top_call: &Rc>) -> u64 { let mut gas_consumed = top_call.borrow().gas_consumed; for nested_call in &top_call.borrow().nested_calls { if let CallTraceNode::EntryPointCall(nested_call) = nested_call { gas_consumed += &nested_call.borrow().gas_consumed; } } gas_consumed } #[expect(clippy::needless_pass_by_value)] fn add_execution_resources(top_call: Rc>) -> ExtendedExecutionResources { let mut execution_resources = top_call.borrow().used_execution_resources.clone(); for nested_call in &top_call.borrow().nested_calls { match nested_call { CallTraceNode::EntryPointCall(nested_call) => { execution_resources += &nested_call.borrow().used_execution_resources; } CallTraceNode::DeployWithoutConstructor => {} } } execution_resources } #[must_use] pub fn get_all_used_resources( call_info: &CallInfo, trace: &Rc>, transaction_context: &TransactionContext, ) -> UsedResources { let versioned_constants = transaction_context.block_context.versioned_constants(); let summary = call_info.summarize(versioned_constants); let l1_handler_payload_lengths = get_l1_handlers_payloads_lengths(&call_info.inner_calls); // Syscalls are used only for `--detailed-resources` output. let top_call_syscalls = trace.borrow().get_total_used_syscalls(); UsedResources { syscall_usage: top_call_syscalls, execution_summary: summary, l1_handler_payload_lengths, } } fn calculate_vm_steps_from_calls(top_call: &Rc>) -> usize { top_call .borrow() .nested_calls .iter() .fold(0, |acc, node| match node { CallTraceNode::EntryPointCall(call_trace) => { acc + call_trace .borrow() .used_execution_resources .vm_resources .n_steps } CallTraceNode::DeployWithoutConstructor => acc, }) } ================================================ FILE: crates/cheatnet/src/runtime_extensions/mod.rs ================================================ pub mod call_to_blockifier_runtime_extension; pub mod cheatable_starknet_runtime_extension; pub mod common; pub mod deprecated_cheatable_starknet_extension; pub mod forge_config_extension; pub mod forge_runtime_extension; #[cfg(feature = "cairo-native")] mod native; ================================================ FILE: crates/cheatnet/src/runtime_extensions/native/call.rs ================================================ use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::{ ExecuteCallEntryPointExtraOptions, execute_call_entry_point, }; use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::execution_utils::clear_events_and_messages_from_reverted_call; use crate::runtime_extensions::native::native_syscall_handler::BaseSyscallResult; use crate::state::CheatnetState; use blockifier::execution::entry_point::CallEntryPoint; use blockifier::execution::syscalls::hint_processor::{ ENTRYPOINT_FAILED_ERROR, SyscallExecutionError, }; use blockifier::execution::syscalls::syscall_base::SyscallHandlerBase; use starknet_types_core::felt::Felt; // Based on https://github.com/software-mansion-labs/sequencer/blob/57447e3e8897d4e7ce7f3ec8d23af58d5b6bf1a7/crates/blockifier/src/execution/syscalls/syscall_base.rs#L435 pub fn execute_inner_call( // region: Modified blockifier code syscall_handler_base: &mut SyscallHandlerBase, cheatnet_state: &mut CheatnetState, call: &mut CallEntryPoint, remaining_gas: &mut u64, ) -> BaseSyscallResult> { // endregion let revert_idx = syscall_handler_base.context.revert_infos.0.len(); // region: Modified blockifier code let call_info = execute_call_entry_point( call, syscall_handler_base.state, cheatnet_state, syscall_handler_base.context, remaining_gas, &ExecuteCallEntryPointExtraOptions { trace_data_handled_by_revert_call: false, }, )?; // endregion let mut raw_retdata = call_info.execution.retdata.0.clone(); let failed = call_info.execution.failed; syscall_handler_base.inner_calls.push(call_info); if failed { syscall_handler_base .context .revert(revert_idx, syscall_handler_base.state)?; // Delete events and l2_to_l1_messages from the reverted call. let reverted_call = syscall_handler_base.inner_calls.last_mut().unwrap(); clear_events_and_messages_from_reverted_call(reverted_call); raw_retdata .push(Felt::from_hex(ENTRYPOINT_FAILED_ERROR).map_err(SyscallExecutionError::from)?); return Err(SyscallExecutionError::Revert { error_data: raw_retdata, }); } Ok(raw_retdata) } ================================================ FILE: crates/cheatnet/src/runtime_extensions/native/deploy.rs ================================================ use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::cheated_syscalls::execute_deployment; use crate::runtime_extensions::native::native_syscall_handler::BaseSyscallResult; use crate::state::CheatnetState; use blockifier::execution::call_info::CallInfo; use blockifier::execution::entry_point::ConstructorContext; use blockifier::execution::syscalls::syscall_base::SyscallHandlerBase; use blockifier::execution::syscalls::vm_syscall_utils::SyscallSelector; use starknet_api::core::{ClassHash, ContractAddress, calculate_contract_address}; use starknet_api::transaction::fields::{Calldata, ContractAddressSalt}; #[expect(clippy::match_bool)] // Copied from blockifer/src/execution/syscalls/syscall_base.rs pub fn deploy( syscall_handler_base: &mut SyscallHandlerBase, cheatnet_state: &mut CheatnetState, class_hash: ClassHash, contract_address_salt: ContractAddressSalt, constructor_calldata: Calldata, deploy_from_zero: bool, remaining_gas: &mut u64, ) -> BaseSyscallResult<(ContractAddress, CallInfo)> { syscall_handler_base .increment_syscall_linear_factor_by(&SyscallSelector::Deploy, constructor_calldata.0.len()); // region: Modified blockifer code // removed code // endregion let deployer_address = syscall_handler_base.call.storage_address; let deployer_address_for_calculation = match deploy_from_zero { true => ContractAddress::default(), false => deployer_address, }; let deployed_contract_address = calculate_contract_address( contract_address_salt, class_hash, &constructor_calldata, deployer_address_for_calculation, )?; let ctor_context = ConstructorContext { class_hash, code_address: Some(deployed_contract_address), storage_address: deployed_contract_address, caller_address: deployer_address, }; // region: Modified blockifer code let call_info = execute_deployment( syscall_handler_base.state, cheatnet_state, syscall_handler_base.context, &ctor_context, constructor_calldata, remaining_gas, )?; // endregion Ok((deployed_contract_address, call_info)) } ================================================ FILE: crates/cheatnet/src/runtime_extensions/native/execution.rs ================================================ use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::{ CallInfoWithExecutionData, ContractClassEntryPointExecutionResult, }; use crate::runtime_extensions::native::native_syscall_handler::CheatableNativeSyscallHandler; use crate::state::CheatnetState; use blockifier::execution::call_info::{ CairoPrimitiveCounterMap, CallExecution, CallInfo, OpcodeName, Retdata, cairo_primitive_counter_map, }; use blockifier::execution::contract_class::TrackedResource; use blockifier::execution::entry_point::{ EntryPointExecutionContext, EntryPointExecutionResult, ExecutableCallEntryPoint, }; use blockifier::execution::errors::{ EntryPointExecutionError, PostExecutionError, PreExecutionError, }; use blockifier::execution::native::contract_class::NativeCompiledClassV1; use blockifier::execution::native::syscall_handler::NativeSyscallHandler; use blockifier::state::state_api::State; use blockifier::transaction::objects::ExecutionResourcesTraits; use blockifier::utils::add_maps; use cairo_native::execution_result::{BuiltinStats, ContractExecutionResult}; use cairo_native::utils::BuiltinCosts; use cairo_vm::types::builtin_name::BuiltinName; use std::collections::HashMap; use std::default::Default; pub(crate) fn execute_entry_point_call_native( call: &ExecutableCallEntryPoint, native_compiled_class_v1: &NativeCompiledClassV1, state: &mut dyn State, cheatnet_state: &mut CheatnetState, // Added parameter context: &mut EntryPointExecutionContext, ) -> ContractClassEntryPointExecutionResult { let mut syscall_handler = CheatableNativeSyscallHandler { cheatnet_state, native_syscall_handler: &mut NativeSyscallHandler::new(call.clone(), state, context), }; let call_info = execute_entry_point_call(call, native_compiled_class_v1, &mut syscall_handler)?; let syscall_usage = &syscall_handler.native_syscall_handler.base.syscalls_usage; Ok(CallInfoWithExecutionData { call_info, // Native execution doesn't support VM resources. // If we got to this point, it means tracked resources are SierraGas. syscall_usage_vm_resources: HashMap::default(), syscall_usage_sierra_gas: syscall_usage.clone(), }) } // Based on https://github.com/software-mansion-labs/sequencer/blob/b6d1c0b354d84225ab9c47f8ff28663d22e84d19/crates/blockifier/src/execution/native/entry_point_execution.rs#L20 fn execute_entry_point_call( call: &ExecutableCallEntryPoint, compiled_class: &NativeCompiledClassV1, // region: Modified blockifier code syscall_handler: &mut CheatableNativeSyscallHandler, // endregion ) -> EntryPointExecutionResult { let entry_point = compiled_class.get_entry_point(&call.type_and_selector())?; let gas_costs = &syscall_handler .native_syscall_handler .base .context .gas_costs(); let builtin_costs = BuiltinCosts { r#const: 1, pedersen: gas_costs.builtins.pedersen, bitwise: gas_costs.builtins.bitwise, ecop: gas_costs.builtins.ecop, poseidon: gas_costs.builtins.poseidon, add_mod: gas_costs.builtins.add_mod, mul_mod: gas_costs.builtins.mul_mod, blake: gas_costs.builtins.blake, }; // Pre-charge entry point's initial budget to ensure sufficient gas for executing a minimal // entry point code. When redepositing is used, the entry point is aware of this pre-charge // and adjusts the gas counter accordingly if a smaller amount of gas is required. let initial_budget = syscall_handler .native_syscall_handler .base .context .gas_costs() .base .entry_point_initial_budget; let call_initial_gas = syscall_handler .native_syscall_handler .base .call .initial_gas .checked_sub(initial_budget) .ok_or(PreExecutionError::InsufficientEntryPointGas)?; let execution_result = compiled_class.executor.run( entry_point.selector.0, &syscall_handler .native_syscall_handler .base .call .calldata .0 .clone(), call_initial_gas, Some(builtin_costs), &mut *syscall_handler, ); syscall_handler.native_syscall_handler.finalize(); let call_result = execution_result.map_err(EntryPointExecutionError::NativeUnexpectedError)?; // TODO(#3790) consider modifying this so it doesn't use take internally if let Some(error) = syscall_handler.unrecoverable_error() { return Err(EntryPointExecutionError::NativeUnrecoverableError( Box::new(error), )); } create_callinfo(call_result, syscall_handler) } // Copied from https://github.com/software-mansion-labs/sequencer/blob/b6d1c0b354d84225ab9c47f8ff28663d22e84d19/crates/blockifier/src/execution/native/entry_point_execution.rs#L73 fn create_callinfo( call_result: ContractExecutionResult, syscall_handler: &mut CheatableNativeSyscallHandler<'_>, ) -> Result { let remaining_gas = call_result.remaining_gas; if remaining_gas > syscall_handler.native_syscall_handler.base.call.initial_gas { return Err(PostExecutionError::MalformedReturnData { error_message: format!( "Unexpected remaining gas. Used gas is greater than initial gas: {} > {}", remaining_gas, syscall_handler.native_syscall_handler.base.call.initial_gas ), } .into()); } let gas_consumed = syscall_handler.native_syscall_handler.base.call.initial_gas - remaining_gas; let vm_resources = CallInfo::summarize_vm_resources( syscall_handler .native_syscall_handler .base .inner_calls .iter(), ); // Retrieve the builtin counts from the syscall handler let version_constants = syscall_handler .native_syscall_handler .base .context .versioned_constants(); let syscall_builtins = version_constants .get_additional_os_syscall_resources( &syscall_handler.native_syscall_handler.base.syscalls_usage, ) .filter_unused_builtins() .prover_builtins(); let mut entry_point_primitive_counters = builtin_stats_to_primitive_counters(call_result.builtin_stats); add_maps( &mut entry_point_primitive_counters, &cairo_primitive_counter_map(syscall_builtins), ); Ok(CallInfo { call: syscall_handler .native_syscall_handler .base .call .clone() .into(), execution: CallExecution { retdata: Retdata(call_result.return_values), events: syscall_handler.native_syscall_handler.base.events.clone(), cairo_native: true, l2_to_l1_messages: syscall_handler .native_syscall_handler .base .l2_to_l1_messages .clone(), failed: call_result.failure_flag, gas_consumed, }, resources: vm_resources, inner_calls: syscall_handler .native_syscall_handler .base .inner_calls .clone(), storage_access_tracker: syscall_handler .native_syscall_handler .base .storage_access_tracker .clone(), tracked_resource: TrackedResource::SierraGas, builtin_counters: entry_point_primitive_counters, syscalls_usage: syscall_handler .native_syscall_handler .base .syscalls_usage .clone(), }) } // Copied from https://github.com/starkware-libs/sequencer/blob/blockifier-v0.18.0-rc.1/crates/blockifier/src/execution/native/entry_point_execution.rs#L130 fn builtin_stats_to_primitive_counters(stats: BuiltinStats) -> CairoPrimitiveCounterMap { let builtins = [ (BuiltinName::range_check, stats.range_check), (BuiltinName::pedersen, stats.pedersen), (BuiltinName::bitwise, stats.bitwise), (BuiltinName::ec_op, stats.ec_op), (BuiltinName::poseidon, stats.poseidon), (BuiltinName::range_check96, stats.range_check96), (BuiltinName::add_mod, stats.add_mod), (BuiltinName::mul_mod, stats.mul_mod), ]; let opcodes = [(OpcodeName::blake, stats.blake)]; builtins .into_iter() .map(|(builtin_name, count)| (builtin_name.into(), count)) .chain( opcodes .into_iter() .map(|(opcode_name, count): (OpcodeName, _)| (opcode_name.into(), count)), ) .filter(|(_, count)| *count > 0) .collect() } ================================================ FILE: crates/cheatnet/src/runtime_extensions/native/mod.rs ================================================ mod call; mod deploy; pub mod execution; pub mod native_syscall_handler; ================================================ FILE: crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs ================================================ use crate::runtime_extensions::forge_runtime_extension::cheatcodes::spy_events::Event; use crate::runtime_extensions::forge_runtime_extension::cheatcodes::spy_messages_to_l1::MessageToL1; use crate::runtime_extensions::native::call::execute_inner_call; use crate::runtime_extensions::native::deploy::deploy; use crate::state::CheatnetState; use blockifier::execution::call_info::Retdata; use blockifier::execution::common_hints::ExecutionMode; use blockifier::execution::entry_point::{CallEntryPoint, CallType}; use blockifier::execution::errors::EntryPointExecutionError; use blockifier::execution::native::syscall_handler::NativeSyscallHandler; use blockifier::execution::syscalls::hint_processor::{OUT_OF_GAS_ERROR, SyscallExecutionError}; use blockifier::execution::syscalls::vm_syscall_utils::{ SelfOrRevert, SyscallExecutorBaseError, SyscallSelector, TryExtractRevert, }; use blockifier::utils::u64_from_usize; use cairo_native::starknet::{ BlockInfo, ExecutionInfo, ExecutionInfoV2, ExecutionInfoV3, ResourceBounds, Secp256k1Point, Secp256r1Point, StarknetSyscallHandler, SyscallResult, TxV2Info, TxV3Info, U256, }; use num_traits::ToPrimitive; use starknet_api::block::BlockNumber; use starknet_api::contract_class::EntryPointType; use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector}; use starknet_api::execution_resources::GasAmount; use starknet_api::transaction::fields::{Calldata, ContractAddressSalt}; use starknet_types_core::felt::Felt; use std::sync::Arc; pub struct CheatableNativeSyscallHandler<'a> { pub native_syscall_handler: &'a mut NativeSyscallHandler<'a>, pub cheatnet_state: &'a mut CheatnetState, } pub type BaseSyscallResult = Result; impl CheatableNativeSyscallHandler<'_> { // TODO(#3790) consider modifying this so it doesn't use take pub fn unrecoverable_error(&mut self) -> Option { self.native_syscall_handler.unrecoverable_error.take() } // Copied from https://github.com/software-mansion-labs/sequencer/blob/b6d1c0b354d84225ab9c47f8ff28663d22e84d19/crates/blockifier/src/execution/native/syscall_handler.rs#L80 /// Handles all gas-related logics, syscall usage counting and perform additional checks. In /// native, we need to explicitly call this method at the beginning of each syscall. fn pre_execute_syscall( &mut self, remaining_gas: &mut u64, total_gas_cost: u64, selector: SyscallSelector, ) -> SyscallResult<()> { if self.native_syscall_handler.unrecoverable_error.is_some() { // An unrecoverable error was found in a previous syscall, we return immediately to // accelerate the end of the execution. The returned data is not important return Err(vec![]); } // Keccak syscall usages' increments are handled inside its implementation. if !matches!(selector, SyscallSelector::Keccak) { self.native_syscall_handler .base .increment_syscall_count_by(selector, 1); } // Refund `SYSCALL_BASE_GAS_COST` as it was pre-charged. let required_gas = total_gas_cost - self .native_syscall_handler .gas_costs() .base .syscall_base_gas_cost; if *remaining_gas < required_gas { // Out of gas failure. return Err(vec![ Felt::from_hex(OUT_OF_GAS_ERROR) .expect("Failed to parse OUT_OF_GAS_ERROR hex string"), ]); } *remaining_gas -= required_gas; // To support sierra gas charge for blockifier revert flow, we track the remaining gas left // before executing a syscall if the current tracked resource is gas. // 1. If the syscall does not run Cairo code (i.e. not library call, not call contract, and // not a deploy), any failure will not run in the OS, so no need to charge - the value // before entering the callback is good enough to charge. // 2. If the syscall runs Cairo code, but the tracked resource is steps (and not gas), the // additional charge of reverted cairo steps will cover the inner cost, and the outer // cost we track here will be the additional reverted gas. // 3. If the syscall runs Cairo code and the tracked resource is gas, either the inner // failure will be a Cairo1 revert (and the gas consumed on the call info will override // the current tracked value), or we will pass through another syscall before failing - // and by induction (we will reach this point again), the gas will be charged correctly. self.native_syscall_handler .base .context .update_revert_gas_with_next_remaining_gas(GasAmount(*remaining_gas)); Ok(()) } // Based on https://github.com/software-mansion-labs/sequencer/blob/b6d1c0b354d84225ab9c47f8ff28663d22e84d19/crates/blockifier/src/execution/native/syscall_handler.rs#L153 fn execute_inner_call( &mut self, entry_point: &mut CallEntryPoint, remaining_gas: &mut u64, class_hash: ClassHash, error_wrapper_fn: impl Fn( SyscallExecutionError, ClassHash, ContractAddress, EntryPointSelector, ) -> SyscallExecutionError, ) -> SyscallResult { let entry_point_clone = entry_point.clone(); let raw_data = execute_inner_call( &mut self.native_syscall_handler.base, self.cheatnet_state, entry_point, remaining_gas, ) .map_err(|e| { self.handle_error( remaining_gas, SyscallExecutionError::from_self_or_revert(e.try_extract_revert().map_original( |error| { error_wrapper_fn( error, class_hash, entry_point_clone.storage_address, entry_point_clone.entry_point_selector, ) }, )), ) })?; Ok(Retdata(raw_data)) } // Copied from https://github.com/software-mansion-labs/sequencer/blob/b6d1c0b354d84225ab9c47f8ff28663d22e84d19/crates/blockifier/src/execution/native/syscall_handler.rs#L124 fn handle_error(&mut self, remaining_gas: &mut u64, error: SyscallExecutionError) -> Vec { // In case of more than one inner call and because each inner call has their own // syscall handler, if there is an unrecoverable error at call `n` it will create a // `NativeExecutionError`. When rolling back, each call from `n-1` to `1` will also // store the result of a previous `NativeExecutionError` in a `NativeExecutionError` // creating multiple wraps around the same error. This function is meant to prevent that. fn unwrap_native_error(error: SyscallExecutionError) -> SyscallExecutionError { match error { SyscallExecutionError::EntryPointExecutionError( EntryPointExecutionError::NativeUnrecoverableError(e), ) => *e, _ => error, } } match error.try_extract_revert() { SelfOrRevert::Revert(revert_error) => revert_error.error_data, SelfOrRevert::Original(error) => { assert!( self.native_syscall_handler.unrecoverable_error.is_none(), "Trying to set an unrecoverable error twice in Native Syscall Handler" ); self.native_syscall_handler.unrecoverable_error = Some(unwrap_native_error(error)); *remaining_gas = 0; vec![] } } } } impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> { fn get_block_hash( &mut self, block_number: u64, remaining_gas: &mut u64, ) -> SyscallResult { self.pre_execute_syscall( remaining_gas, self.native_syscall_handler .gas_costs() .syscalls .get_block_hash .base_syscall_cost(), SyscallSelector::GetBlockHash, )?; let block_hash = self.cheatnet_state.get_cheated_block_hash_for_contract( self.native_syscall_handler.base.call.storage_address, block_number, ); if let Some(block_hash) = block_hash { Ok(block_hash.0) } else { match self .native_syscall_handler .base .get_block_hash(BlockNumber(block_number)) { Ok(value) => Ok(value), Err(e) => Err(self.handle_error(remaining_gas, e)), } } } fn get_execution_info(&mut self, remaining_gas: &mut u64) -> SyscallResult { self.native_syscall_handler .get_execution_info(remaining_gas) } #[expect(clippy::too_many_lines)] fn get_execution_info_v2(&mut self, remaining_gas: &mut u64) -> SyscallResult { // We don't need to call pre_execute_syscall here because the call to `get_execution_info_v2` // on the native syscall handler is does that internally, and we don't want to do it twice. let original_data = self .native_syscall_handler .get_execution_info_v2(remaining_gas)?; let cheated_data = self .cheatnet_state .get_cheated_data(self.native_syscall_handler.base.call.storage_address); let block_number = cheated_data .block_number .unwrap_or(original_data.block_info.block_number); let block_timestamp = cheated_data .block_timestamp .unwrap_or(original_data.block_info.block_timestamp); let sequencer_address = cheated_data.sequencer_address.map_or( original_data.block_info.sequencer_address, std::convert::Into::into, ); let version = cheated_data .tx_info .version .unwrap_or(original_data.tx_info.version); let account_contract_address = cheated_data .tx_info .account_contract_address .unwrap_or(original_data.tx_info.account_contract_address); let max_fee = cheated_data .tx_info .max_fee .map_or(original_data.tx_info.max_fee, |max_fee| { max_fee.to_u128().unwrap() }); let signature = cheated_data .tx_info .signature .unwrap_or(original_data.tx_info.signature); let transaction_hash = cheated_data .tx_info .transaction_hash .unwrap_or(original_data.tx_info.transaction_hash); let chain_id = cheated_data .tx_info .chain_id .unwrap_or(original_data.tx_info.chain_id); let nonce = cheated_data .tx_info .nonce .unwrap_or(original_data.tx_info.nonce); // TODO(#3790) implement conversions let resource_bounds = cheated_data.tx_info.resource_bounds.map_or( original_data.tx_info.resource_bounds, |rb| { rb.iter() .map(|item| ResourceBounds { resource: item.resource, max_amount: item.max_amount, max_price_per_unit: item.max_price_per_unit, }) .collect() }, ); let tip = cheated_data .tx_info .tip .map_or(original_data.tx_info.tip, |tip| tip.to_u128().unwrap()); let paymaster_data = cheated_data .tx_info .paymaster_data .unwrap_or(original_data.tx_info.paymaster_data); let nonce_data_availability_mode = cheated_data .tx_info .nonce_data_availability_mode .map_or(original_data.tx_info.nonce_data_availability_mode, |mode| { mode.to_u32().unwrap() }); let fee_data_availability_mode = cheated_data .tx_info .fee_data_availability_mode .map_or(original_data.tx_info.fee_data_availability_mode, |mode| { mode.to_u32().unwrap() }); let account_deployment_data = cheated_data .tx_info .account_deployment_data .unwrap_or(original_data.tx_info.account_deployment_data); let caller_address = cheated_data .caller_address .map_or(original_data.caller_address, std::convert::Into::into); let contract_address = cheated_data .contract_address .map_or(original_data.contract_address, std::convert::Into::into); let entry_point_selector = original_data.entry_point_selector; Ok(ExecutionInfoV2 { block_info: BlockInfo { block_number, block_timestamp, sequencer_address, }, tx_info: TxV2Info { version, account_contract_address, max_fee, signature, transaction_hash, chain_id, nonce, resource_bounds, tip, paymaster_data, nonce_data_availability_mode, fee_data_availability_mode, account_deployment_data, }, caller_address, contract_address, entry_point_selector, }) } #[expect(clippy::too_many_lines)] fn get_execution_info_v3(&mut self, remaining_gas: &mut u64) -> SyscallResult { // We don't need to call pre_execute_syscall here because the call to `get_execution_info_v3` // on the native syscall handler is does that internally, and we don't want to do it twice. let original_data = self .native_syscall_handler .get_execution_info_v3(remaining_gas)?; let cheated_data = self .cheatnet_state .get_cheated_data(self.native_syscall_handler.base.call.storage_address); let block_number = cheated_data .block_number .unwrap_or(original_data.block_info.block_number); let block_timestamp = cheated_data .block_timestamp .unwrap_or(original_data.block_info.block_timestamp); let sequencer_address = cheated_data.sequencer_address.map_or( original_data.block_info.sequencer_address, std::convert::Into::into, ); let version = cheated_data .tx_info .version .unwrap_or(original_data.tx_info.version); let account_contract_address = cheated_data .tx_info .account_contract_address .unwrap_or(original_data.tx_info.account_contract_address); let max_fee = cheated_data .tx_info .max_fee .map_or(original_data.tx_info.max_fee, |max_fee| { max_fee.to_u128().unwrap() }); let signature = cheated_data .tx_info .signature .unwrap_or(original_data.tx_info.signature); let transaction_hash = cheated_data .tx_info .transaction_hash .unwrap_or(original_data.tx_info.transaction_hash); let chain_id = cheated_data .tx_info .chain_id .unwrap_or(original_data.tx_info.chain_id); let nonce = cheated_data .tx_info .nonce .unwrap_or(original_data.tx_info.nonce); // TODO(#3790) implement conversions let resource_bounds = cheated_data.tx_info.resource_bounds.map_or( original_data.tx_info.resource_bounds, |rb| { rb.iter() .map(|item| ResourceBounds { resource: item.resource, max_amount: item.max_amount, max_price_per_unit: item.max_price_per_unit, }) .collect() }, ); let tip = cheated_data .tx_info .tip .map_or(original_data.tx_info.tip, |tip| tip.to_u128().unwrap()); let paymaster_data = cheated_data .tx_info .paymaster_data .unwrap_or(original_data.tx_info.paymaster_data); let nonce_data_availability_mode = cheated_data .tx_info .nonce_data_availability_mode .map_or(original_data.tx_info.nonce_data_availability_mode, |mode| { mode.to_u32().unwrap() }); let fee_data_availability_mode = cheated_data .tx_info .fee_data_availability_mode .map_or(original_data.tx_info.fee_data_availability_mode, |mode| { mode.to_u32().unwrap() }); let account_deployment_data = cheated_data .tx_info .account_deployment_data .unwrap_or(original_data.tx_info.account_deployment_data); let caller_address = cheated_data .caller_address .map_or(original_data.caller_address, std::convert::Into::into); let contract_address = cheated_data .contract_address .map_or(original_data.contract_address, std::convert::Into::into); let entry_point_selector = original_data.entry_point_selector; Ok(ExecutionInfoV3 { block_info: BlockInfo { block_number, block_timestamp, sequencer_address, }, tx_info: TxV3Info { version, account_contract_address, max_fee, signature, transaction_hash, chain_id, nonce, resource_bounds, tip, paymaster_data, nonce_data_availability_mode, fee_data_availability_mode, account_deployment_data, proof_facts: original_data.tx_info.proof_facts, }, caller_address, contract_address, entry_point_selector, }) } // Based on https://github.com/software-mansion-labs/sequencer/blob/b6d1c0b354d84225ab9c47f8ff28663d22e84d19/crates/blockifier/src/execution/native/syscall_handler.rs#L322 fn deploy( &mut self, class_hash: Felt, contract_address_salt: Felt, calldata: &[Felt], deploy_from_zero: bool, remaining_gas: &mut u64, ) -> SyscallResult<(Felt, Vec)> { // The cost of deploying a contract is the base cost plus the linear cost of the calldata // len. let total_gas_cost = self .native_syscall_handler .gas_costs() .syscalls .deploy .get_syscall_cost(u64_from_usize(calldata.len())); self.pre_execute_syscall(remaining_gas, total_gas_cost, SyscallSelector::Deploy)?; // region: Modified blockifier code let (deployed_contract_address, call_info) = deploy( &mut self.native_syscall_handler.base, self.cheatnet_state, ClassHash(class_hash), ContractAddressSalt(contract_address_salt), Calldata(Arc::new(calldata.to_vec())), deploy_from_zero, remaining_gas, ) // endregion .map_err(|err| self.handle_error(remaining_gas, err))?; let constructor_retdata = call_info.execution.retdata.0[..].to_vec(); self.native_syscall_handler.base.inner_calls.push(call_info); Ok((Felt::from(deployed_contract_address), constructor_retdata)) } fn replace_class(&mut self, class_hash: Felt, remaining_gas: &mut u64) -> SyscallResult<()> { self.native_syscall_handler .replace_class(class_hash, remaining_gas) } // Based on from https://github.com/software-mansion-labs/sequencer/blob/b6d1c0b354d84225ab9c47f8ff28663d22e84d19/crates/blockifier/src/execution/native/syscall_handler.rs#L399 fn library_call( &mut self, class_hash: Felt, function_selector: Felt, calldata: &[Felt], remaining_gas: &mut u64, ) -> SyscallResult> { self.pre_execute_syscall( remaining_gas, self.native_syscall_handler .gas_costs() .syscalls .library_call .base_syscall_cost(), SyscallSelector::LibraryCall, )?; let class_hash = ClassHash(class_hash); let wrapper_calldata = Calldata(Arc::new(calldata.to_vec())); let selector = EntryPointSelector(function_selector); let mut entry_point = CallEntryPoint { class_hash: Some(class_hash), code_address: None, entry_point_type: EntryPointType::External, entry_point_selector: selector, calldata: wrapper_calldata, // The call context remains the same in a library call. storage_address: self.native_syscall_handler.base.call.storage_address, caller_address: self.native_syscall_handler.base.call.caller_address, call_type: CallType::Delegate, initial_gas: *remaining_gas, }; let error_wrapper_function = |e: SyscallExecutionError, class_hash: ClassHash, storage_address: ContractAddress, selector: EntryPointSelector| { e.as_lib_call_execution_error(class_hash, storage_address, selector) }; Ok(self .execute_inner_call( &mut entry_point, remaining_gas, class_hash, error_wrapper_function, )? .0) } // Based on https://github.com/software-mansion-labs/sequencer/blob/b6d1c0b354d84225ab9c47f8ff28663d22e84d19/crates/blockifier/src/execution/native/syscall_handler.rs#L444 fn call_contract( &mut self, address: Felt, entry_point_selector: Felt, calldata: &[Felt], remaining_gas: &mut u64, ) -> SyscallResult> { self.pre_execute_syscall( remaining_gas, self.native_syscall_handler .gas_costs() .syscalls .call_contract .base_syscall_cost(), SyscallSelector::CallContract, )?; let contract_address = ContractAddress::try_from(address) .map_err(|error| self.handle_error(remaining_gas, error.into()))?; let class_hash = self .native_syscall_handler .base .state .get_class_hash_at(contract_address) .map_err(|e| self.handle_error(remaining_gas, e.into()))?; if self.native_syscall_handler.base.context.execution_mode == ExecutionMode::Validate && self.native_syscall_handler.base.call.storage_address != contract_address { let err = SyscallExecutorBaseError::InvalidSyscallInExecutionMode { syscall_name: "call_contract".to_string(), execution_mode: self.native_syscall_handler.base.context.execution_mode, }; return Err(self.handle_error(remaining_gas, err.into())); } let selector = EntryPointSelector(entry_point_selector); // TODO(#3790) restore blocking // self // .native_syscall_handler // .base // .maybe_block_direct_execute_call(selector) // .map_err(|e| self.handle_error(remaining_gas, e))?; let wrapper_calldata = Calldata(Arc::new(calldata.to_vec())); let mut entry_point = CallEntryPoint { class_hash: None, code_address: Some(contract_address), entry_point_type: EntryPointType::External, entry_point_selector: selector, calldata: wrapper_calldata, storage_address: contract_address, caller_address: self.native_syscall_handler.base.call.storage_address, call_type: CallType::Call, initial_gas: *remaining_gas, }; let error_wrapper_function = |e: SyscallExecutionError, class_hash: ClassHash, storage_address: ContractAddress, selector: EntryPointSelector| { e.as_call_contract_execution_error(class_hash, storage_address, selector) }; Ok(self .execute_inner_call( &mut entry_point, remaining_gas, class_hash, error_wrapper_function, )? .0) } fn storage_read( &mut self, address_domain: u32, address: Felt, remaining_gas: &mut u64, ) -> SyscallResult { self.native_syscall_handler .storage_read(address_domain, address, remaining_gas) } fn storage_write( &mut self, address_domain: u32, address: Felt, value: Felt, remaining_gas: &mut u64, ) -> SyscallResult<()> { self.native_syscall_handler .storage_write(address_domain, address, value, remaining_gas) } fn emit_event( &mut self, keys: &[Felt], data: &[Felt], remaining_gas: &mut u64, ) -> SyscallResult<()> { let syscall_result = self .native_syscall_handler .emit_event(keys, data, remaining_gas); if syscall_result.is_ok() { let contract_address = self .native_syscall_handler .base .call // TODO(#3790) why we default to code_address?? .code_address .unwrap_or(self.native_syscall_handler.base.call.storage_address); let event = self .native_syscall_handler .base .events .last() .expect("Event must have been emitted"); self.cheatnet_state .detected_events .push(Event::from_ordered_event(event, contract_address)); } syscall_result } fn send_message_to_l1( &mut self, to_address: Felt, payload: &[Felt], remaining_gas: &mut u64, ) -> SyscallResult<()> { let syscall_result = self.native_syscall_handler .send_message_to_l1(to_address, payload, remaining_gas); if syscall_result.is_ok() { let contract_address = self .native_syscall_handler .base .call // TODO(#3790) why we default to code_address?? .code_address .unwrap_or(self.native_syscall_handler.base.call.storage_address); let message = self .native_syscall_handler .base .l2_to_l1_messages .last() .expect("Message must have been sent"); self.cheatnet_state .detected_messages_to_l1 .push(MessageToL1::from_ordered_message(message, contract_address)); } syscall_result } fn keccak(&mut self, input: &[u64], remaining_gas: &mut u64) -> SyscallResult { self.native_syscall_handler.keccak(input, remaining_gas) } fn secp256k1_new( &mut self, x: U256, y: U256, remaining_gas: &mut u64, ) -> SyscallResult> { self.native_syscall_handler .secp256k1_new(x, y, remaining_gas) } fn secp256k1_add( &mut self, p0: Secp256k1Point, p1: Secp256k1Point, remaining_gas: &mut u64, ) -> SyscallResult { self.native_syscall_handler .secp256k1_add(p0, p1, remaining_gas) } fn secp256k1_mul( &mut self, p: Secp256k1Point, m: U256, remaining_gas: &mut u64, ) -> SyscallResult { self.native_syscall_handler .secp256k1_mul(p, m, remaining_gas) } fn secp256k1_get_point_from_x( &mut self, x: U256, y_parity: bool, remaining_gas: &mut u64, ) -> SyscallResult> { self.native_syscall_handler .secp256k1_get_point_from_x(x, y_parity, remaining_gas) } fn secp256k1_get_xy( &mut self, p: Secp256k1Point, remaining_gas: &mut u64, ) -> SyscallResult<(U256, U256)> { self.native_syscall_handler .secp256k1_get_xy(p, remaining_gas) } fn secp256r1_new( &mut self, x: U256, y: U256, remaining_gas: &mut u64, ) -> SyscallResult> { self.native_syscall_handler .secp256r1_new(x, y, remaining_gas) } fn secp256r1_add( &mut self, p0: Secp256r1Point, p1: Secp256r1Point, remaining_gas: &mut u64, ) -> SyscallResult { self.native_syscall_handler .secp256r1_add(p0, p1, remaining_gas) } fn secp256r1_mul( &mut self, p: Secp256r1Point, m: U256, remaining_gas: &mut u64, ) -> SyscallResult { self.native_syscall_handler .secp256r1_mul(p, m, remaining_gas) } fn secp256r1_get_point_from_x( &mut self, x: U256, y_parity: bool, remaining_gas: &mut u64, ) -> SyscallResult> { self.native_syscall_handler .secp256r1_get_point_from_x(x, y_parity, remaining_gas) } fn secp256r1_get_xy( &mut self, p: Secp256r1Point, remaining_gas: &mut u64, ) -> SyscallResult<(U256, U256)> { self.native_syscall_handler .secp256r1_get_xy(p, remaining_gas) } fn sha256_process_block( &mut self, state: &mut [u32; 8], block: &[u32; 16], remaining_gas: &mut u64, ) -> SyscallResult<()> { self.native_syscall_handler .sha256_process_block(state, block, remaining_gas) } fn get_class_hash_at( &mut self, contract_address: Felt, remaining_gas: &mut u64, ) -> SyscallResult { self.native_syscall_handler .get_class_hash_at(contract_address, remaining_gas) } // TODO(#3790) Support cheating meta_tx_v0 fn meta_tx_v0( &mut self, address: Felt, entry_point_selector: Felt, calldata: &[Felt], signature: &[Felt], remaining_gas: &mut u64, ) -> SyscallResult> { self.native_syscall_handler.meta_tx_v0( address, entry_point_selector, calldata, signature, remaining_gas, ) } } ================================================ FILE: crates/cheatnet/src/state.rs ================================================ use crate::constants::build_test_entry_point; use crate::forking::state::ForkStateReader; use crate::predeployment::erc20::eth::eth_predeployed_contract; use crate::predeployment::erc20::strk::strk_predeployed_contract; use crate::predeployment::predeployed_contract::PredeployedContract; use crate::runtime_extensions::forge_runtime_extension::cheatcodes::cheat_execution_info::{ ExecutionInfoMock, ResourceBounds, }; use crate::runtime_extensions::forge_runtime_extension::cheatcodes::spy_events::Event; use crate::runtime_extensions::forge_runtime_extension::cheatcodes::spy_messages_to_l1::MessageToL1; use crate::trace_data::{CallTrace, NotEmptyCallStack, TraceData}; use blockifier::execution::contract_class::RunnableCompiledClass; use blockifier::state::errors::StateError::UndeclaredClassHash; use blockifier::state::state_api::{StateReader, StateResult}; use cairo_vm::Felt252; use conversions::serde::deserialize::CairoDeserialize; use conversions::string::TryFromHexStr; use indexmap::IndexMap; use runtime::starknet::constants::TEST_CONTRACT_CLASS_HASH; use runtime::starknet::context::SerializableBlockInfo; use runtime::starknet::state::DictStateReader; use starknet_api::block::BlockInfo; use starknet_api::core::{ChainId, EntryPointSelector}; use starknet_api::transaction::fields::ContractAddressSalt; use starknet_api::{ core::{ClassHash, CompiledClassHash, ContractAddress, Nonce}, state::StorageKey, }; use starknet_types_core::felt::Felt; use std::cell::RefCell; use std::collections::HashMap; use std::num::NonZeroUsize; use std::rc::Rc; // Specifies the duration of the cheat #[derive(CairoDeserialize, Copy, Clone, Debug, PartialEq, Eq)] pub enum CheatSpan { Indefinite, TargetCalls(NonZeroUsize), } #[derive(Debug)] pub struct ExtendedStateReader { pub dict_state_reader: DictStateReader, pub fork_state_reader: Option, } impl ExtendedStateReader { pub fn predeploy_contracts(&mut self) { // We consider contract as deployed solely based on the fact that the test used forking let is_fork = self.fork_state_reader.is_some(); if !is_fork { let contracts = vec![strk_predeployed_contract(), eth_predeployed_contract()]; for contract in contracts { self.predeploy_contract(contract); } } } fn predeploy_contract(&mut self, contract: PredeployedContract) { let PredeployedContract { contract_address, class_hash, contract_class, storage_kv_updates, } = contract; self.dict_state_reader .address_to_class_hash .insert(contract_address, class_hash); self.dict_state_reader .class_hash_to_class .insert(class_hash, contract_class); for (key, value) in &storage_kv_updates { let entry = (contract_address, *key); self.dict_state_reader.storage_view.insert(entry, *value); } } } pub trait BlockInfoReader { fn get_block_info(&mut self) -> StateResult; } impl BlockInfoReader for ExtendedStateReader { fn get_block_info(&mut self) -> StateResult { if let Some(ref mut fork_state_reader) = self.fork_state_reader { return fork_state_reader.get_block_info(); } Ok(SerializableBlockInfo::default().into()) } } impl StateReader for ExtendedStateReader { fn get_storage_at( &self, contract_address: ContractAddress, key: StorageKey, ) -> StateResult { self.dict_state_reader .get_storage_at(contract_address, key) .or_else(|_| { self.fork_state_reader .as_ref() .map_or(Ok(Felt252::default()), { |reader| reader.get_storage_at(contract_address, key) }) }) } fn get_nonce_at(&self, contract_address: ContractAddress) -> StateResult { self.dict_state_reader .get_nonce_at(contract_address) .or_else(|_| { self.fork_state_reader .as_ref() .map_or(Ok(Nonce::default()), { |reader| reader.get_nonce_at(contract_address) }) }) } fn get_class_hash_at(&self, contract_address: ContractAddress) -> StateResult { self.dict_state_reader .get_class_hash_at(contract_address) .or_else(|_| { self.fork_state_reader .as_ref() .map_or(Ok(ClassHash::default()), { |reader| reader.get_class_hash_at(contract_address) }) }) } fn get_compiled_class(&self, class_hash: ClassHash) -> StateResult { self.dict_state_reader .get_compiled_class(class_hash) .or_else(|_| { self.fork_state_reader .as_ref() .map_or(Err(UndeclaredClassHash(class_hash)), |reader| { reader.get_compiled_class(class_hash) }) }) } fn get_compiled_class_hash(&self, class_hash: ClassHash) -> StateResult { Ok(self .dict_state_reader .get_compiled_class_hash(class_hash) .unwrap_or_default()) } } impl ExtendedStateReader { pub fn get_chain_id(&self) -> anyhow::Result> { self.fork_state_reader .as_ref() .map(ForkStateReader::chain_id) .transpose() } } #[derive(Clone, Default, Debug, PartialEq, Eq)] pub enum CheatStatus { Cheated(T, CheatSpan), #[default] Uncheated, } impl CheatStatus { pub fn decrement_cheat_span(&mut self) { if let CheatStatus::Cheated(_, CheatSpan::TargetCalls(n)) = self { let calls_number = n.get() - 1; if calls_number == 0 { *self = CheatStatus::Uncheated; } else { *n = NonZeroUsize::new(calls_number) .expect("`NonZeroUsize` should not be zero after decrement"); } } } pub fn as_value(&self) -> Option where T: Clone, { match self { Self::Cheated(value, _span) => Some(value.clone()), Self::Uncheated => None, } } } #[derive(Clone, Default, Debug, PartialEq, Eq)] pub struct CheatedTxInfo { pub version: Option, pub account_contract_address: Option, pub max_fee: Option, pub signature: Option>, pub transaction_hash: Option, pub chain_id: Option, pub nonce: Option, pub resource_bounds: Option>, pub tip: Option, pub paymaster_data: Option>, pub nonce_data_availability_mode: Option, pub fee_data_availability_mode: Option, pub account_deployment_data: Option>, pub proof_facts: Option>, } impl CheatedTxInfo { #[must_use] pub fn is_mocked(&self) -> bool { self != &CheatedTxInfo::default() } } #[derive(Clone, Default, Debug)] pub struct CheatedData { pub block_number: Option, pub block_timestamp: Option, pub caller_address: Option, pub contract_address: Option, pub sequencer_address: Option, pub tx_info: CheatedTxInfo, } pub struct CheatnetState { pub cheated_execution_info_contracts: HashMap, pub global_cheated_execution_info: ExecutionInfoMock, pub mocked_functions: HashMap>>>, pub replaced_bytecode_contracts: HashMap, pub detected_events: Vec, pub detected_messages_to_l1: Vec, pub deploy_salt_base: u32, pub next_deploy_at_address: Option, pub next_syscall_from_cheatcode: bool, pub block_info: BlockInfo, pub trace_data: TraceData, pub encountered_errors: EncounteredErrors, pub fuzzer_args: Vec, pub block_hash_contracts: HashMap<(ContractAddress, u64), (CheatSpan, Felt)>, pub global_block_hash: HashMap)>, } pub type EncounteredErrors = IndexMap>; impl Default for CheatnetState { fn default() -> Self { let mut test_code_entry_point = build_test_entry_point(); test_code_entry_point.class_hash = ClassHash(TryFromHexStr::try_from_hex_str(TEST_CONTRACT_CLASS_HASH).unwrap()); let test_call = Rc::new(RefCell::new(CallTrace { entry_point: test_code_entry_point.into(), ..CallTrace::default_successful_call() })); Self { cheated_execution_info_contracts: HashMap::default(), global_cheated_execution_info: ExecutionInfoMock::default(), mocked_functions: HashMap::default(), replaced_bytecode_contracts: HashMap::default(), detected_events: vec![], detected_messages_to_l1: vec![], deploy_salt_base: 0, next_deploy_at_address: None, next_syscall_from_cheatcode: false, block_info: SerializableBlockInfo::default().into(), trace_data: TraceData { current_call_stack: NotEmptyCallStack::from(test_call), is_vm_trace_needed: false, }, encountered_errors: IndexMap::default(), fuzzer_args: Vec::default(), block_hash_contracts: HashMap::default(), global_block_hash: HashMap::default(), } } } impl CheatnetState { #[must_use] pub fn create_cheated_data(&mut self, contract_address: ContractAddress) -> CheatedData { let execution_info = self.get_cheated_execution_info_for_contract(contract_address); CheatedData { block_number: execution_info.block_info.block_number.as_value(), block_timestamp: execution_info.block_info.block_timestamp.as_value(), caller_address: execution_info.caller_address.as_value(), contract_address: execution_info.contract_address.as_value(), sequencer_address: execution_info.block_info.sequencer_address.as_value(), tx_info: CheatedTxInfo { version: execution_info.tx_info.version.as_value(), account_contract_address: execution_info .tx_info .account_contract_address .as_value(), max_fee: execution_info.tx_info.max_fee.as_value(), signature: execution_info.tx_info.signature.as_value(), transaction_hash: execution_info.tx_info.transaction_hash.as_value(), chain_id: execution_info.tx_info.chain_id.as_value(), nonce: execution_info.tx_info.nonce.as_value(), resource_bounds: execution_info.tx_info.resource_bounds.as_value(), tip: execution_info.tx_info.tip.as_value(), paymaster_data: execution_info.tx_info.paymaster_data.as_value(), nonce_data_availability_mode: execution_info .tx_info .nonce_data_availability_mode .as_value(), fee_data_availability_mode: execution_info .tx_info .fee_data_availability_mode .as_value(), account_deployment_data: execution_info.tx_info.account_deployment_data.as_value(), proof_facts: execution_info.tx_info.proof_facts.as_value(), }, } } pub fn get_cheated_data(&mut self, contract_address: ContractAddress) -> CheatedData { let current_call_stack = &mut self.trace_data.current_call_stack; // case of cheating the test address itself if current_call_stack.size() == 1 { self.create_cheated_data(contract_address) // do not update the cheats, as the test address cannot be called from the outside } else { current_call_stack.top_cheated_data() } } pub fn increment_deploy_salt_base(&mut self) { self.deploy_salt_base += 1; } pub fn set_next_deploy_at_address(&mut self, address: ContractAddress) { self.next_deploy_at_address = Some(address); } pub fn next_address_for_deployment(&mut self) -> Option { self.next_deploy_at_address.take() } pub fn set_next_syscall_from_cheatcode(&mut self) { self.next_syscall_from_cheatcode = true; } pub fn take_next_syscall_from_cheatcode(&mut self) -> bool { std::mem::take(&mut self.next_syscall_from_cheatcode) } #[must_use] pub fn get_salt(&self) -> ContractAddressSalt { ContractAddressSalt(Felt::from(self.deploy_salt_base)) } #[must_use] pub fn get_cheated_block_number(&mut self, address: ContractAddress) -> Option { self.get_cheated_execution_info_for_contract(address) .block_info .block_number .as_value() } #[must_use] pub fn get_cheated_block_timestamp(&mut self, address: ContractAddress) -> Option { self.get_cheated_execution_info_for_contract(address) .block_info .block_timestamp .as_value() } #[must_use] pub fn get_cheated_sequencer_address( &mut self, address: ContractAddress, ) -> Option { self.get_cheated_execution_info_for_contract(address) .block_info .sequencer_address .as_value() } #[must_use] pub fn get_cheated_caller_address( &mut self, address: ContractAddress, ) -> Option { self.get_cheated_execution_info_for_contract(address) .caller_address .as_value() } pub fn update_cheats(&mut self, address: &ContractAddress) { self.progress_cheated_execution_info(*address); } pub fn update_fuzzer_args(&mut self, arg: String) { self.fuzzer_args.push(arg); } pub fn register_error(&mut self, class_hash: ClassHash, pcs: Vec) { self.encountered_errors.insert(class_hash, pcs); } pub fn clear_error(&mut self, class_hash: ClassHash) { self.encountered_errors.shift_remove(&class_hash); } } ================================================ FILE: crates/cheatnet/src/sync_client.rs ================================================ use starknet_api::block::BlockNumber; use starknet_rust::core::types::{ BlockId, ContractClass, GetStorageAtResult, MaybePreConfirmedBlockWithTxHashes, }; use starknet_rust::providers::jsonrpc::HttpTransport; use starknet_rust::providers::{JsonRpcClient, Provider, ProviderError}; use starknet_types_core::felt::Felt; use tokio::runtime::Runtime; use url::Url; #[derive(Debug)] pub struct SyncClient { client: JsonRpcClient, block_id: BlockId, runtime: Runtime, } impl SyncClient { #[must_use] pub fn new(url: Url, block_number: BlockNumber) -> Self { Self { client: JsonRpcClient::new(HttpTransport::new(url)), block_id: BlockId::Number(block_number.0), runtime: Runtime::new().expect("Could not instantiate Runtime"), } } pub fn chain_id(&self) -> Result { self.sync(self.client.chain_id()) } pub fn get_block_with_tx_hashes( &self, ) -> Result { self.sync(self.client.get_block_with_tx_hashes(self.block_id)) } pub fn get_storage_at(&self, contract_address: Felt, key: Felt) -> Result { self.sync( self.client .get_storage_at(contract_address, key, self.block_id, None), ) .map(|storage_response| match storage_response { GetStorageAtResult::Value(value) => value, GetStorageAtResult::ValueWithMetadata(result_with_metadata) => { result_with_metadata.value } }) } pub fn get_nonce(&self, contract_address: Felt) -> Result { self.sync(self.client.get_nonce(self.block_id, contract_address)) } pub fn get_class_hash_at(&self, contract_address: Felt) -> Result { self.sync( self.client .get_class_hash_at(self.block_id, contract_address), ) } pub fn get_class(&self, class_hash: Felt) -> Result { self.sync(self.client.get_class(self.block_id, class_hash)) } fn sync(&self, future: F) -> F::Output { self.runtime.block_on(future) } } ================================================ FILE: crates/cheatnet/src/trace_data.rs ================================================ use crate::runtime_extensions::call_to_blockifier_runtime_extension::rpc::{ CallResult, CallSuccess, }; use crate::runtime_extensions::common::sum_syscall_usage; use crate::state::CheatedData; use blockifier::blockifier_versioned_constants::VersionedConstants; use blockifier::execution::call_info::{ ExecutionSummary, ExtendedExecutionResources, OrderedEvent, OrderedL2ToL1Message, }; use blockifier::execution::entry_point::CallEntryPoint; use blockifier::execution::syscalls::vm_syscall_utils::SyscallUsageMap; use cairo_annotations::trace_data::L1Resources; use cairo_vm::vm::trace::trace_entry::RelocatedTraceEntry; use conversions::serde::serialize::{BufferWriter, CairoSerialize}; use starknet_api::core::ClassHash; use starknet_api::execution_resources::GasVector; use starknet_api::transaction::fields::GasVectorComputationMode; use starknet_api::versioned_constants_logic::VersionedConstantsTrait; use starknet_types_core::felt::Felt; use std::cell::{OnceCell, Ref, RefCell}; use std::rc::Rc; #[derive(Debug)] pub struct TraceData { pub current_call_stack: NotEmptyCallStack, pub is_vm_trace_needed: bool, } #[derive(Debug)] pub struct NotEmptyCallStack(Vec); #[derive(Clone, Debug)] struct CallStackElement { call_trace: Rc>, cheated_data: CheatedData, } /// Tree structure representing trace of a call. #[derive(Debug)] pub struct CallTrace { // only these are serialized pub entry_point: CallEntryPoint, pub nested_calls: Vec, pub result: CallResult, // serialize end // These also include resources used by internal calls pub used_execution_resources: ExtendedExecutionResources, pub used_l1_resources: L1Resources, pub used_syscalls_vm_resources: SyscallUsageMap, pub used_syscalls_sierra_gas: SyscallUsageMap, pub vm_trace: Option>, pub gas_consumed: u64, pub events: Vec, pub signature: Vec, // This is updated only once after the entire test execution. pub gas_report_data: Option, } /// Enum representing a node of a trace of a call. #[derive(Clone, Debug)] pub enum CallTraceNode { EntryPointCall(Rc>), DeployWithoutConstructor, } #[derive(Clone, Debug)] pub struct GasReportData { pub execution_summary: ExecutionSummary, partial_gas_usage: OnceCell, } impl TraceData { pub fn enter_nested_call(&mut self, entry_point: CallEntryPoint, cheated_data: CheatedData) { let new_call = Rc::new(RefCell::new(CallTrace { entry_point, ..CallTrace::default_successful_call() })); let current_call = self.current_call_stack.top(); current_call .borrow_mut() .nested_calls .push(CallTraceNode::EntryPointCall(new_call.clone())); self.current_call_stack.push(new_call, cheated_data); } pub fn set_class_hash_for_current_call(&mut self, class_hash: ClassHash) { let current_call = self.current_call_stack.top(); current_call.borrow_mut().entry_point.class_hash = Some(class_hash); } pub fn set_vm_trace_for_current_call(&mut self, vm_trace: Vec) { let current_call = self.current_call_stack.top(); current_call.borrow_mut().vm_trace = Some(vm_trace); } pub fn update_current_call_result(&mut self, result: CallResult) { let current_call = self.current_call_stack.top(); current_call.borrow_mut().result = result; } pub fn clear_current_call_events_and_messages(&mut self) { let current_call = self.current_call_stack.top(); current_call.borrow_mut().events.clear(); current_call .borrow_mut() .used_l1_resources .l2_l1_message_sizes .clear(); } #[expect(clippy::too_many_arguments)] pub fn update_current_call( &mut self, execution_resources: ExtendedExecutionResources, gas_consumed: u64, used_syscalls_vm_resources: SyscallUsageMap, used_syscalls_sierra_gas: SyscallUsageMap, result: CallResult, l2_to_l1_messages: &[OrderedL2ToL1Message], signature: Vec, events: Vec, ) { let current_call = self.current_call_stack.top(); let mut current_call = current_call.borrow_mut(); current_call.used_execution_resources = execution_resources; current_call.gas_consumed = gas_consumed; current_call.used_syscalls_vm_resources = used_syscalls_vm_resources; current_call.used_syscalls_sierra_gas = used_syscalls_sierra_gas; current_call.used_l1_resources.l2_l1_message_sizes = l2_to_l1_messages .iter() .map(|ordered_message| ordered_message.message.payload.0.len()) .collect(); current_call.result = result; current_call.signature = signature; current_call.events = events; } pub fn exit_nested_call(&mut self) { self.current_call_stack.pop(); } pub fn add_deploy_without_constructor_node(&mut self) { let current_call = self.current_call_stack.top(); current_call .borrow_mut() .nested_calls .push(CallTraceNode::DeployWithoutConstructor); } } impl NotEmptyCallStack { pub fn from(elem: Rc>) -> Self { NotEmptyCallStack(vec![CallStackElement { call_trace: elem, cheated_data: CheatedData::default(), }]) } pub fn push(&mut self, elem: Rc>, cheated_data: CheatedData) { self.0.push(CallStackElement { call_trace: elem, cheated_data, }); } pub fn top(&mut self) -> Rc> { let top_val = self.0.last().unwrap(); top_val.call_trace.clone() } pub fn top_cheated_data(&mut self) -> CheatedData { let top_val = self.0.last().unwrap(); top_val.cheated_data.clone() } fn pop(&mut self) -> CallStackElement { assert!(self.0.len() > 1, "You cannot make NotEmptyCallStack empty"); self.0.pop().unwrap() } #[must_use] pub fn size(&self) -> usize { self.0.len() } #[must_use] pub fn borrow_full_trace(&self) -> Ref<'_, CallTrace> { self.0.first().unwrap().call_trace.borrow() } } impl CallTrace { pub(crate) fn default_successful_call() -> Self { Self { entry_point: CallEntryPoint::default(), used_execution_resources: ExtendedExecutionResources::default(), used_l1_resources: L1Resources::default(), used_syscalls_vm_resources: SyscallUsageMap::default(), used_syscalls_sierra_gas: SyscallUsageMap::default(), nested_calls: vec![], result: Ok(CallSuccess { ret_data: vec![] }), vm_trace: None, gas_consumed: u64::default(), events: vec![], signature: vec![], gas_report_data: None, } } #[must_use] pub fn get_total_used_syscalls(&self) -> SyscallUsageMap { sum_syscall_usage( self.used_syscalls_vm_resources.clone(), &self.used_syscalls_sierra_gas, ) } } impl CallTraceNode { #[must_use] pub fn extract_entry_point_call(&self) -> Option<&Rc>> { if let CallTraceNode::EntryPointCall(trace) = self { Some(trace) } else { None } } } impl GasReportData { #[must_use] pub fn new(execution_summary: ExecutionSummary) -> Self { Self { execution_summary, partial_gas_usage: OnceCell::new(), } } pub fn get_gas(&self) -> GasVector { *self.partial_gas_usage.get_or_init(|| { self.execution_summary.clone().to_partial_gas_vector( VersionedConstants::latest_constants(), &GasVectorComputationMode::All, ) }) } } impl CairoSerialize for CallTrace { fn serialize(&self, output: &mut BufferWriter) { self.entry_point.serialize(output); let visible_calls: Vec<_> = self .nested_calls .iter() .filter_map(CallTraceNode::extract_entry_point_call) .collect(); visible_calls.serialize(output); self.result.serialize(output); } } ================================================ FILE: crates/cheatnet/tests/builtins/mod.rs ================================================ mod panic_call; mod segment_arena; ================================================ FILE: crates/cheatnet/tests/builtins/panic_call.rs ================================================ use crate::common::assertions::assert_panic; use crate::common::call_contract; use crate::common::{deploy_contract, state::create_cached_state}; use blockifier::execution::syscalls::hint_processor::ENTRYPOINT_FAILED_ERROR_FELT; use cairo_lang_utils::byte_array::BYTE_ARRAY_MAGIC; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::storage::selector_from_name; use cheatnet::state::CheatnetState; use conversions::IntoConv; use conversions::felt::FromShortString; use conversions::string::TryFromHexStr; use starknet_types_core::felt::Felt; use test_case::test_case; #[test] fn call_contract_failed() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract(&mut cached_state, &mut cheatnet_state, "PanicCall", &[]); let selector = selector_from_name("panic_call"); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[Felt::from(420)], ); assert_panic( output, &[ Felt::from_short_string("Input too long for arguments").unwrap(), ENTRYPOINT_FAILED_ERROR_FELT, ], ); } #[test] fn call_contract_panic() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract(&mut cached_state, &mut cheatnet_state, "PanicCall", &[]); let selector = selector_from_name("panic_call"); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_panic( output, &[ Felt::from_short_string("shortstring").unwrap(), Felt::from(0), Felt::MAX, Felt::from_short_string("shortstring2").unwrap(), ENTRYPOINT_FAILED_ERROR_FELT, ], ); } #[test] fn call_proxied_contract_bytearray_panic() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let proxy = deploy_contract( &mut cached_state, &mut cheatnet_state, "ByteArrayPanickingContractProxy", &[], ); let bytearray_panicking_contract = deploy_contract( &mut cached_state, &mut cheatnet_state, "ByteArrayPanickingContract", &[], ); let selector = selector_from_name("call_bytearray_panicking_contract"); let output = call_contract( &mut cached_state, &mut cheatnet_state, &proxy, selector, &[bytearray_panicking_contract.into_()], ); assert_panic( output, &[ Felt::try_from_hex_str(&format!("0x{BYTE_ARRAY_MAGIC}")).unwrap(), Felt::from(2), Felt::from_short_string("This is a very long\n and multi ").unwrap(), Felt::from_short_string("line string, that will for sure").unwrap(), Felt::from_short_string(" saturate the pending_word").unwrap(), Felt::from(26), ENTRYPOINT_FAILED_ERROR_FELT, ENTRYPOINT_FAILED_ERROR_FELT, ], ); } #[test_case(&[Felt::from(1), Felt::from(1)], &[Felt::from(1)])] #[test_case(&[Felt::from(1), Felt::from(65)], &[Felt::from(65)])] #[test_case(&[Felt::from(4), Felt::from(1), Felt::from(65), Felt::from(2), Felt::from(66)], &[Felt::from(1), Felt::from(65), Felt::from(2), Felt::from(66)])] fn call_proxied_contract_felts_panic(input: &[Felt], expected_panic: &[Felt]) { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let proxy = deploy_contract( &mut cached_state, &mut cheatnet_state, "ByteArrayPanickingContractProxy", &[], ); let bytearray_panicking_contract = deploy_contract( &mut cached_state, &mut cheatnet_state, "ByteArrayPanickingContract", &[], ); let selector_felts = selector_from_name("call_felts_panicking_contract"); let mut contract_call_args = vec![bytearray_panicking_contract.into_()]; contract_call_args.extend_from_slice(input); let output = call_contract( &mut cached_state, &mut cheatnet_state, &proxy, selector_felts, &contract_call_args, ); assert_panic( output, &[ expected_panic, &[ENTRYPOINT_FAILED_ERROR_FELT, ENTRYPOINT_FAILED_ERROR_FELT], ] .concat(), ); } ================================================ FILE: crates/cheatnet/tests/builtins/segment_arena.rs ================================================ use crate::common::assertions::assert_success; use crate::common::call_contract; use crate::common::{deploy_contract, state::create_cached_state}; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::storage::selector_from_name; use cheatnet::state::CheatnetState; #[test] fn segment_arena_simple() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract( &mut cached_state, &mut cheatnet_state, "SegmentArenaUser", &[], ); let selector = selector_from_name("interface_function"); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &[]); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/cheat_account_contract_address.rs ================================================ use std::num::NonZeroUsize; use crate::{cheatcodes::test_environment::TestEnvironment, common::assertions::assert_success}; use cheatnet::state::CheatSpan; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; trait CheatAccountContractAddressTrait { fn cheat_account_contract_address( &mut self, target: ContractAddress, new_address: u128, span: CheatSpan, ); fn start_cheat_account_contract_address(&mut self, target: ContractAddress, new_address: u128); fn stop_cheat_account_contract_address(&mut self, target: ContractAddress); } impl CheatAccountContractAddressTrait for TestEnvironment { fn cheat_account_contract_address( &mut self, target: ContractAddress, new_address: u128, span: CheatSpan, ) { self.cheatnet_state.cheat_account_contract_address( target, ContractAddress::from(new_address), span, ); } fn start_cheat_account_contract_address(&mut self, target: ContractAddress, new_address: u128) { self.cheatnet_state .start_cheat_account_contract_address(target, ContractAddress::from(new_address)); } fn stop_cheat_account_contract_address(&mut self, target: ContractAddress) { self.cheatnet_state .stop_cheat_account_contract_address(target); } } #[test] fn cheat_account_contract_address_simple() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatTxInfoChecker", &[]); let output = test_env.call_contract(&contract_address, "get_account_contract_address", &[]); assert_success(output, &[Felt::from(0)]); test_env.start_cheat_account_contract_address(contract_address, 123); let output = test_env.call_contract(&contract_address, "get_account_contract_address", &[]); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_account_contract_address_stop() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatTxInfoChecker", &[]); test_env.start_cheat_account_contract_address(contract_address, 123); assert_success( test_env.call_contract(&contract_address, "get_account_contract_address", &[]), &[Felt::from(123)], ); test_env.stop_cheat_account_contract_address(contract_address); assert_success( test_env.call_contract(&contract_address, "get_account_contract_address", &[]), &[Felt::from(0)], ); } #[test] fn cheat_account_contract_address_simple_with_span() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatTxInfoChecker", &[]); test_env.cheat_account_contract_address( contract_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); assert_success( test_env.call_contract(&contract_address, "get_account_contract_address", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_account_contract_address", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_account_contract_address", &[]), &[Felt::from(0)], ); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/cheat_block_hash.rs ================================================ use super::test_environment::TestEnvironment; use crate::common::assertions::assert_success; use crate::common::get_contracts; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::cheat_execution_info::{ CheatArguments, Operation, }; use cheatnet::state::CheatSpan; use conversions::IntoConv; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; use std::num::NonZeroUsize; const DEFAULT_BLOCK_HASH: u64 = 0; const BLOCK_NUMBER: u64 = 123; trait CheatBlockHashTrait { fn cheat_block_hash( &mut self, contract_address: ContractAddress, block_number: u64, block_hash: Felt, span: CheatSpan, ); fn start_cheat_block_hash( &mut self, contract_address: ContractAddress, block_number: u64, block_hash: Felt, ); fn stop_cheat_block_hash(&mut self, contract_address: ContractAddress, block_number: u64); } impl CheatBlockHashTrait for TestEnvironment { fn cheat_block_hash( &mut self, contract_address: ContractAddress, block_number: u64, block_hash: Felt, span: CheatSpan, ) { self.cheatnet_state.cheat_block_hash( block_number, Operation::Start(CheatArguments { value: block_hash, span, target: contract_address, }), ); } fn start_cheat_block_hash( &mut self, contract_address: ContractAddress, block_number: u64, block_hash: Felt, ) { self.cheatnet_state .start_cheat_block_hash(contract_address, block_number, block_hash); } fn stop_cheat_block_hash(&mut self, contract_address: ContractAddress, block_number: u64) { self.cheatnet_state .stop_cheat_block_hash(contract_address, block_number); } } #[test] fn cheat_block_hash_simple() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockHashChecker", &[]); let output = test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]); assert_success(output, &[Felt::from(0)]); test_env.start_cheat_block_hash(contract_address, BLOCK_NUMBER, Felt::from(123)); let output = test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_block_hash_in_constructor() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("ConstructorCheatBlockHashChecker", &contracts_data); let precalculated_address = test_env.precalculate_address(&class_hash, &[BLOCK_NUMBER.into()]); test_env.start_cheat_block_hash(precalculated_address, BLOCK_NUMBER, Felt::from(123)); let contract_address = test_env.deploy_wrapper(&class_hash, &[BLOCK_NUMBER.into()]); assert_eq!(precalculated_address, contract_address); let output = test_env.call_contract(&contract_address, "get_stored_block_hash", &[]); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_block_hash_stop() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockHashChecker", &[]); test_env.start_cheat_block_hash(contract_address, BLOCK_NUMBER, Felt::from(123)); assert_success( test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(123)], ); test_env.stop_cheat_block_hash(contract_address, BLOCK_NUMBER); assert_success( test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(DEFAULT_BLOCK_HASH)], ); } #[test] fn cheat_block_hash_double() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockHashChecker", &[]); test_env.start_cheat_block_hash(contract_address, BLOCK_NUMBER, Felt::from(123)); test_env.start_cheat_block_hash(contract_address, BLOCK_NUMBER, Felt::from(123)); assert_success( test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(123)], ); test_env.stop_cheat_block_hash(contract_address, BLOCK_NUMBER); assert_success( test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(DEFAULT_BLOCK_HASH)], ); } #[test] fn cheat_block_hash_proxy() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockHashChecker", &[]); let proxy_address = test_env.deploy("CheatBlockHashCheckerProxy", &[]); let proxy_selector = "get_cheated_block_hash"; test_env.start_cheat_block_hash(contract_address, BLOCK_NUMBER, Felt::from(123)); assert_success( test_env.call_contract( &proxy_address, proxy_selector, &[contract_address.into_(), BLOCK_NUMBER.into()], ), &[Felt::from(123)], ); test_env.stop_cheat_block_hash(contract_address, BLOCK_NUMBER); assert_success( test_env.call_contract( &proxy_address, proxy_selector, &[contract_address.into_(), BLOCK_NUMBER.into()], ), &[Felt::from(DEFAULT_BLOCK_HASH)], ); } #[test] fn cheat_block_hash_library_call() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatBlockHashChecker", &contracts_data); let lib_call_address = test_env.deploy("CheatBlockHashCheckerLibCall", &[]); let lib_call_selector = "get_block_hash_with_lib_call"; test_env.start_cheat_block_hash(lib_call_address, BLOCK_NUMBER, Felt::from(123)); assert_success( test_env.call_contract( &lib_call_address, lib_call_selector, &[class_hash.into_(), BLOCK_NUMBER.into()], ), &[Felt::from(123)], ); test_env.stop_cheat_block_hash(lib_call_address, BLOCK_NUMBER); assert_success( test_env.call_contract( &lib_call_address, lib_call_selector, &[class_hash.into_(), BLOCK_NUMBER.into()], ), &[Felt::from(DEFAULT_BLOCK_HASH)], ); } #[test] fn cheat_block_hash_all_simple() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockHashChecker", &[]); test_env .cheatnet_state .start_cheat_block_hash_global(BLOCK_NUMBER, Felt::from(123)); let output = test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_block_hash_all_then_one() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockHashChecker", &[]); test_env .cheatnet_state .start_cheat_block_hash_global(BLOCK_NUMBER, Felt::from(321)); test_env.start_cheat_block_hash(contract_address, BLOCK_NUMBER, Felt::from(123)); let output = test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_block_hash_one_then_all() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockHashChecker", &[]); test_env.start_cheat_block_hash(contract_address, BLOCK_NUMBER, Felt::from(123)); test_env .cheatnet_state .start_cheat_block_hash_global(BLOCK_NUMBER, Felt::from(321)); let output = test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]); assert_success(output, &[Felt::from(321)]); } #[test] fn cheat_block_hash_all_stop() { let mut test_env = TestEnvironment::new(); let cheat_block_hash_checker = test_env.declare("CheatBlockHashChecker", &get_contracts()); let contract_address = test_env.deploy_wrapper(&cheat_block_hash_checker, &[]); test_env .cheatnet_state .start_cheat_block_hash_global(BLOCK_NUMBER, Felt::from(123)); assert_success( test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(123)], ); test_env .cheatnet_state .stop_cheat_block_hash_global(BLOCK_NUMBER); assert_success( test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(DEFAULT_BLOCK_HASH)], ); let contract_address = test_env.deploy_wrapper(&cheat_block_hash_checker, &[]); assert_success( test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(DEFAULT_BLOCK_HASH)], ); } #[test] fn cheat_block_hash_multiple() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatBlockHashChecker", &contracts_data); let contract_address1 = test_env.deploy_wrapper(&class_hash, &[]); let contract_address2 = test_env.deploy_wrapper(&class_hash, &[]); test_env.cheatnet_state.start_cheat_block_hash( contract_address1, BLOCK_NUMBER, Felt::from(123), ); test_env.cheatnet_state.start_cheat_block_hash( contract_address2, BLOCK_NUMBER, Felt::from(123), ); assert_success( test_env.call_contract(&contract_address1, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address2, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(123)], ); test_env .cheatnet_state .stop_cheat_block_hash(contract_address1, BLOCK_NUMBER); test_env .cheatnet_state .stop_cheat_block_hash(contract_address2, BLOCK_NUMBER); assert_success( test_env.call_contract(&contract_address1, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(DEFAULT_BLOCK_HASH)], ); assert_success( test_env.call_contract(&contract_address2, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(DEFAULT_BLOCK_HASH)], ); } #[test] fn cheat_block_hash_simple_with_span() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockHashChecker", &[]); test_env.cheat_block_hash( contract_address, BLOCK_NUMBER, Felt::from(123), CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); assert_success( test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(DEFAULT_BLOCK_HASH)], ); } #[test] fn cheat_block_hash_proxy_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatBlockHashCheckerProxy", &contracts_data); let contract_address_1 = test_env.deploy_wrapper(&class_hash, &[]); let contract_address_2 = test_env.deploy_wrapper(&class_hash, &[]); test_env.cheat_block_hash( contract_address_1, BLOCK_NUMBER, Felt::from(123), CheatSpan::TargetCalls(NonZeroUsize::new(1).unwrap()), ); let output = test_env.call_contract( &contract_address_1, "call_proxy", &[contract_address_2.into_(), BLOCK_NUMBER.into()], ); assert_success(output, &[123.into(), DEFAULT_BLOCK_HASH.into()]); } #[test] fn cheat_block_hash_in_constructor_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("ConstructorCheatBlockHashChecker", &contracts_data); let precalculated_address = test_env.precalculate_address(&class_hash, &[BLOCK_NUMBER.into()]); test_env.cheat_block_hash( precalculated_address, BLOCK_NUMBER, Felt::from(123), CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); let contract_address = test_env.deploy_wrapper(&class_hash, &[BLOCK_NUMBER.into()]); assert_eq!(precalculated_address, contract_address); assert_success( test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(DEFAULT_BLOCK_HASH)], ); assert_success( test_env.call_contract(&contract_address, "get_stored_block_hash", &[]), &[Felt::from(123)], ); } #[test] fn cheat_block_hash_no_constructor_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatBlockHashChecker", &contracts_data); let precalculated_address = test_env.precalculate_address(&class_hash, &[]); test_env.cheat_block_hash( precalculated_address, BLOCK_NUMBER, Felt::from(123), CheatSpan::TargetCalls(NonZeroUsize::new(1).unwrap()), ); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); assert_eq!(precalculated_address, contract_address); assert_success( test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(DEFAULT_BLOCK_HASH)], ); } #[test] fn cheat_block_hash_override_span() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockHashChecker", &[]); test_env.cheat_block_hash( contract_address, BLOCK_NUMBER, Felt::from(123), CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); assert_success( test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(123)], ); test_env.cheat_block_hash( contract_address, BLOCK_NUMBER, Felt::from(321), CheatSpan::Indefinite, ); assert_success( test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(321)], ); assert_success( test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(321)], ); test_env.stop_cheat_block_hash(contract_address, BLOCK_NUMBER); assert_success( test_env.call_contract(&contract_address, "get_block_hash", &[BLOCK_NUMBER.into()]), &[Felt::from(DEFAULT_BLOCK_HASH)], ); } #[test] fn cheat_block_hash_library_call_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatBlockHashChecker", &contracts_data); let contract_address = test_env.deploy("CheatBlockHashCheckerLibCall", &[]); test_env.cheat_block_hash( contract_address, BLOCK_NUMBER, Felt::from(123), CheatSpan::TargetCalls(NonZeroUsize::new(1).unwrap()), ); let lib_call_selector = "get_block_hash_with_lib_call"; assert_success( test_env.call_contract( &contract_address, lib_call_selector, &[class_hash.into_(), BLOCK_NUMBER.into()], ), &[Felt::from(123)], ); assert_success( test_env.call_contract( &contract_address, lib_call_selector, &[class_hash.into_(), BLOCK_NUMBER.into()], ), &[Felt::from(DEFAULT_BLOCK_HASH)], ); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/cheat_block_number.rs ================================================ use crate::{common::assertions::assert_success, common::get_contracts}; use cheatnet::state::CheatSpan; use conversions::IntoConv; use runtime::starknet::context::DEFAULT_BLOCK_NUMBER; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; use std::num::NonZeroUsize; use super::test_environment::TestEnvironment; trait CheatBlockNumberTrait { fn cheat_block_number( &mut self, contract_address: ContractAddress, block_number: u64, span: CheatSpan, ); fn start_cheat_block_number(&mut self, contract_address: ContractAddress, block_number: u64); fn stop_cheat_block_number(&mut self, contract_address: ContractAddress); } impl CheatBlockNumberTrait for TestEnvironment { fn cheat_block_number( &mut self, contract_address: ContractAddress, block_number: u64, span: CheatSpan, ) { self.cheatnet_state .cheat_block_number(contract_address, block_number, span); } fn start_cheat_block_number(&mut self, contract_address: ContractAddress, block_number: u64) { self.cheatnet_state .start_cheat_block_number(contract_address, block_number); } fn stop_cheat_block_number(&mut self, contract_address: ContractAddress) { self.cheatnet_state .stop_cheat_block_number(contract_address); } } #[test] fn cheat_block_number_simple() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockNumberChecker", &[]); test_env.start_cheat_block_number(contract_address, 123); let output = test_env.call_contract(&contract_address, "get_block_number", &[]); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_block_number_with_other_syscall() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockNumberChecker", &[]); test_env.start_cheat_block_number(contract_address, 123); let output = test_env.call_contract(&contract_address, "get_block_number_and_emit_event", &[]); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_block_number_in_constructor() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("ConstructorCheatBlockNumberChecker", &contracts_data); let precalculated_address = test_env.precalculate_address(&class_hash, &[]); test_env.start_cheat_block_number(precalculated_address, 123); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); assert_eq!(precalculated_address, contract_address); let output = test_env.call_contract(&contract_address, "get_stored_block_number", &[]); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_block_number_stop() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockNumberChecker", &[]); test_env.start_cheat_block_number(contract_address, 123); assert_success( test_env.call_contract(&contract_address, "get_block_number", &[]), &[Felt::from(123)], ); test_env.stop_cheat_block_number(contract_address); assert_success( test_env.call_contract(&contract_address, "get_block_number", &[]), &[Felt::from(DEFAULT_BLOCK_NUMBER)], ); } #[test] fn cheat_block_number_double() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockNumberChecker", &[]); test_env.start_cheat_block_number(contract_address, 123); test_env.start_cheat_block_number(contract_address, 123); assert_success( test_env.call_contract(&contract_address, "get_block_number", &[]), &[Felt::from(123)], ); test_env.stop_cheat_block_number(contract_address); assert_success( test_env.call_contract(&contract_address, "get_block_number", &[]), &[Felt::from(DEFAULT_BLOCK_NUMBER)], ); } #[test] fn cheat_block_number_proxy() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockNumberChecker", &[]); let proxy_address = test_env.deploy("CheatBlockNumberCheckerProxy", &[]); let proxy_selector = "get_cheated_block_number"; test_env.start_cheat_block_number(contract_address, 123); assert_success( test_env.call_contract(&proxy_address, proxy_selector, &[contract_address.into_()]), &[Felt::from(123)], ); test_env.stop_cheat_block_number(contract_address); assert_success( test_env.call_contract(&proxy_address, proxy_selector, &[contract_address.into_()]), &[Felt::from(DEFAULT_BLOCK_NUMBER)], ); } #[test] fn cheat_block_number_library_call() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatBlockNumberChecker", &contracts_data); let lib_call_address = test_env.deploy("CheatBlockNumberCheckerLibCall", &[]); let lib_call_selector = "get_block_number_with_lib_call"; test_env.start_cheat_block_number(lib_call_address, 123); assert_success( test_env.call_contract(&lib_call_address, lib_call_selector, &[class_hash.into_()]), &[Felt::from(123)], ); test_env.stop_cheat_block_number(lib_call_address); assert_success( test_env.call_contract(&lib_call_address, lib_call_selector, &[class_hash.into_()]), &[Felt::from(DEFAULT_BLOCK_NUMBER)], ); } #[test] fn cheat_block_number_all_simple() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockNumberChecker", &[]); test_env.cheatnet_state.start_cheat_block_number_global(123); let output = test_env.call_contract(&contract_address, "get_block_number", &[]); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_block_number_all_then_one() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockNumberChecker", &[]); test_env.cheatnet_state.start_cheat_block_number_global(321); test_env.start_cheat_block_number(contract_address, 123); let output = test_env.call_contract(&contract_address, "get_block_number", &[]); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_block_number_one_then_all() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockNumberChecker", &[]); test_env.start_cheat_block_number(contract_address, 123); test_env.cheatnet_state.start_cheat_block_number_global(321); let output = test_env.call_contract(&contract_address, "get_block_number", &[]); assert_success(output, &[Felt::from(321)]); } #[test] fn cheat_block_number_all_stop() { let mut test_env = TestEnvironment::new(); let cheat_block_number_checker = test_env.declare("CheatBlockNumberChecker", &get_contracts()); let contract_address = test_env.deploy_wrapper(&cheat_block_number_checker, &[]); test_env.cheatnet_state.start_cheat_block_number_global(123); assert_success( test_env.call_contract(&contract_address, "get_block_number", &[]), &[Felt::from(123)], ); test_env.cheatnet_state.stop_cheat_block_number_global(); assert_success( test_env.call_contract(&contract_address, "get_block_number", &[]), &[Felt::from(DEFAULT_BLOCK_NUMBER)], ); let contract_address = test_env.deploy_wrapper(&cheat_block_number_checker, &[]); assert_success( test_env.call_contract(&contract_address, "get_block_number", &[]), &[Felt::from(DEFAULT_BLOCK_NUMBER)], ); } #[test] fn cheat_block_number_multiple() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatBlockNumberChecker", &contracts_data); let contract_address1 = test_env.deploy_wrapper(&class_hash, &[]); let contract_address2 = test_env.deploy_wrapper(&class_hash, &[]); test_env .cheatnet_state .start_cheat_block_number(contract_address1, 123); test_env .cheatnet_state .start_cheat_block_number(contract_address2, 123); assert_success( test_env.call_contract(&contract_address1, "get_block_number", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address2, "get_block_number", &[]), &[Felt::from(123)], ); test_env .cheatnet_state .stop_cheat_block_number(contract_address1); test_env .cheatnet_state .stop_cheat_block_number(contract_address2); assert_success( test_env.call_contract(&contract_address1, "get_block_number", &[]), &[Felt::from(DEFAULT_BLOCK_NUMBER)], ); assert_success( test_env.call_contract(&contract_address2, "get_block_number", &[]), &[Felt::from(DEFAULT_BLOCK_NUMBER)], ); } #[test] fn cheat_block_number_simple_with_span() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockNumberChecker", &[]); test_env.cheat_block_number( contract_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); assert_success( test_env.call_contract(&contract_address, "get_block_number", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_block_number", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_block_number", &[]), &[Felt::from(DEFAULT_BLOCK_NUMBER)], ); } #[test] fn cheat_block_number_proxy_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatBlockNumberCheckerProxy", &contracts_data); let contract_address_1 = test_env.deploy_wrapper(&class_hash, &[]); let contract_address_2 = test_env.deploy_wrapper(&class_hash, &[]); test_env.cheat_block_number( contract_address_1, 123, CheatSpan::TargetCalls(NonZeroUsize::new(1).unwrap()), ); let output = test_env.call_contract( &contract_address_1, "call_proxy", &[contract_address_2.into_()], ); assert_success(output, &[123.into(), DEFAULT_BLOCK_NUMBER.into()]); } #[test] fn cheat_block_number_in_constructor_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("ConstructorCheatBlockNumberChecker", &contracts_data); let precalculated_address = test_env.precalculate_address(&class_hash, &[]); test_env.cheat_block_number( precalculated_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); assert_eq!(precalculated_address, contract_address); assert_success( test_env.call_contract(&contract_address, "get_block_number", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_block_number", &[]), &[Felt::from(DEFAULT_BLOCK_NUMBER)], ); assert_success( test_env.call_contract(&contract_address, "get_stored_block_number", &[]), &[Felt::from(123)], ); } #[test] fn cheat_block_number_no_constructor_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatBlockNumberChecker", &contracts_data); let precalculated_address = test_env.precalculate_address(&class_hash, &[]); test_env.cheat_block_number( precalculated_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(1).unwrap()), ); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); assert_eq!(precalculated_address, contract_address); assert_success( test_env.call_contract(&contract_address, "get_block_number", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_block_number", &[]), &[Felt::from(DEFAULT_BLOCK_NUMBER)], ); } #[test] fn cheat_block_number_override_span() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockNumberChecker", &[]); test_env.cheat_block_number( contract_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); assert_success( test_env.call_contract(&contract_address, "get_block_number", &[]), &[Felt::from(123)], ); test_env.cheat_block_number(contract_address, 321, CheatSpan::Indefinite); assert_success( test_env.call_contract(&contract_address, "get_block_number", &[]), &[Felt::from(321)], ); assert_success( test_env.call_contract(&contract_address, "get_block_number", &[]), &[Felt::from(321)], ); test_env.stop_cheat_block_number(contract_address); assert_success( test_env.call_contract(&contract_address, "get_block_number", &[]), &[Felt::from(DEFAULT_BLOCK_NUMBER)], ); } #[test] fn cheat_block_number_library_call_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatBlockNumberChecker", &contracts_data); let contract_address = test_env.deploy("CheatBlockNumberCheckerLibCall", &[]); test_env.cheat_block_number( contract_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(1).unwrap()), ); let lib_call_selector = "get_block_number_with_lib_call"; assert_success( test_env.call_contract(&contract_address, lib_call_selector, &[class_hash.into_()]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, lib_call_selector, &[class_hash.into_()]), &[Felt::from(DEFAULT_BLOCK_NUMBER)], ); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/cheat_block_timestamp.rs ================================================ use crate::{common::assertions::assert_success, common::get_contracts}; use cheatnet::state::CheatSpan; use conversions::IntoConv; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; use std::num::NonZeroUsize; use super::test_environment::TestEnvironment; const DEFAULT_BLOCK_TIMESTAMP: u64 = 0; trait CheatBlockTimestampTrait { fn cheat_block_timestamp( &mut self, contract_address: ContractAddress, timestamp: u64, span: CheatSpan, ); fn start_cheat_block_timestamp(&mut self, contract_address: ContractAddress, timestamp: u64); fn stop_cheat_block_timestamp(&mut self, contract_address: ContractAddress); } impl CheatBlockTimestampTrait for TestEnvironment { fn cheat_block_timestamp( &mut self, contract_address: ContractAddress, timestamp: u64, span: CheatSpan, ) { self.cheatnet_state .cheat_block_timestamp(contract_address, timestamp, span); } fn start_cheat_block_timestamp(&mut self, contract_address: ContractAddress, timestamp: u64) { self.cheatnet_state .start_cheat_block_timestamp(contract_address, timestamp); } fn stop_cheat_block_timestamp(&mut self, contract_address: ContractAddress) { self.cheatnet_state .stop_cheat_block_timestamp(contract_address); } } #[test] fn cheat_block_timestamp_simple() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockTimestampChecker", &[]); test_env.start_cheat_block_timestamp(contract_address, 123); let output = test_env.call_contract(&contract_address, "get_block_timestamp", &[]); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_block_timestamp_with_other_syscall() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockTimestampChecker", &[]); test_env.start_cheat_block_timestamp(contract_address, 123); let selector = "get_block_timestamp_and_emit_event"; let output = test_env.call_contract(&contract_address, selector, &[]); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_block_timestamp_in_constructor() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("ConstructorCheatBlockTimestampChecker", &contracts_data); let precalculated_address = test_env.precalculate_address(&class_hash, &[]); test_env.start_cheat_block_timestamp(precalculated_address, 123); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); assert_eq!(precalculated_address, contract_address); let output = test_env.call_contract(&contract_address, "get_stored_block_timestamp", &[]); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_block_timestamp_stop() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockTimestampChecker", &[]); test_env.start_cheat_block_timestamp(contract_address, 123); assert_success( test_env.call_contract(&contract_address, "get_block_timestamp", &[]), &[Felt::from(123)], ); test_env.stop_cheat_block_timestamp(contract_address); assert_success( test_env.call_contract(&contract_address, "get_block_timestamp", &[]), &[Felt::from(DEFAULT_BLOCK_TIMESTAMP)], ); } #[test] fn cheat_block_timestamp_double() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockTimestampChecker", &[]); test_env.start_cheat_block_timestamp(contract_address, 123); test_env.start_cheat_block_timestamp(contract_address, 123); assert_success( test_env.call_contract(&contract_address, "get_block_timestamp", &[]), &[Felt::from(123)], ); test_env.stop_cheat_block_timestamp(contract_address); assert_success( test_env.call_contract(&contract_address, "get_block_timestamp", &[]), &[Felt::from(DEFAULT_BLOCK_TIMESTAMP)], ); } #[test] fn cheat_block_timestamp_proxy() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockTimestampChecker", &[]); let proxy_address = test_env.deploy("CheatBlockTimestampCheckerProxy", &[]); let proxy_selector = "get_cheated_block_timestamp"; test_env.start_cheat_block_timestamp(contract_address, 123); assert_success( test_env.call_contract(&proxy_address, proxy_selector, &[contract_address.into_()]), &[Felt::from(123)], ); test_env.stop_cheat_block_timestamp(contract_address); assert_success( test_env.call_contract(&proxy_address, proxy_selector, &[contract_address.into_()]), &[Felt::from(DEFAULT_BLOCK_TIMESTAMP)], ); } #[test] fn cheat_block_timestamp_library_call() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatBlockTimestampChecker", &contracts_data); let lib_call_address = test_env.deploy("CheatBlockTimestampCheckerLibCall", &[]); let lib_call_selector = "get_block_timestamp_with_lib_call"; test_env.start_cheat_block_timestamp(lib_call_address, 123); assert_success( test_env.call_contract(&lib_call_address, lib_call_selector, &[class_hash.into_()]), &[Felt::from(123)], ); test_env.stop_cheat_block_timestamp(lib_call_address); assert_success( test_env.call_contract(&lib_call_address, lib_call_selector, &[class_hash.into_()]), &[Felt::from(DEFAULT_BLOCK_TIMESTAMP)], ); } #[test] fn cheat_block_timestamp_all_simple() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockTimestampChecker", &[]); test_env .cheatnet_state .start_cheat_block_timestamp_global(123); let output = test_env.call_contract(&contract_address, "get_block_timestamp", &[]); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_block_timestamp_all_then_one() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockTimestampChecker", &[]); test_env .cheatnet_state .start_cheat_block_timestamp_global(321); test_env.start_cheat_block_timestamp(contract_address, 123); let output = test_env.call_contract(&contract_address, "get_block_timestamp", &[]); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_block_timestamp_one_then_all() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockTimestampChecker", &[]); test_env.start_cheat_block_timestamp(contract_address, 123); test_env .cheatnet_state .start_cheat_block_timestamp_global(321); let output = test_env.call_contract(&contract_address, "get_block_timestamp", &[]); assert_success(output, &[Felt::from(321)]); } #[test] fn cheat_block_timestamp_all_stop() { let mut test_env = TestEnvironment::new(); let cheat_block_timestamp_checker = test_env.declare("CheatBlockTimestampChecker", &get_contracts()); let contract_address = test_env.deploy_wrapper(&cheat_block_timestamp_checker, &[]); test_env .cheatnet_state .start_cheat_block_timestamp_global(123); assert_success( test_env.call_contract(&contract_address, "get_block_timestamp", &[]), &[Felt::from(123)], ); test_env.cheatnet_state.stop_cheat_block_timestamp_global(); assert_success( test_env.call_contract(&contract_address, "get_block_timestamp", &[]), &[Felt::from(DEFAULT_BLOCK_TIMESTAMP)], ); let contract_address = test_env.deploy_wrapper(&cheat_block_timestamp_checker, &[]); assert_success( test_env.call_contract(&contract_address, "get_block_timestamp", &[]), &[Felt::from(DEFAULT_BLOCK_TIMESTAMP)], ); } #[test] fn cheat_block_timestamp_multiple() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatBlockTimestampChecker", &contracts_data); let contract_address1 = test_env.deploy_wrapper(&class_hash, &[]); let contract_address2 = test_env.deploy_wrapper(&class_hash, &[]); test_env .cheatnet_state .start_cheat_block_timestamp(contract_address1, 123); test_env .cheatnet_state .start_cheat_block_timestamp(contract_address2, 123); assert_success( test_env.call_contract(&contract_address1, "get_block_timestamp", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address2, "get_block_timestamp", &[]), &[Felt::from(123)], ); test_env .cheatnet_state .stop_cheat_block_timestamp(contract_address1); test_env .cheatnet_state .stop_cheat_block_timestamp(contract_address2); assert_success( test_env.call_contract(&contract_address1, "get_block_timestamp", &[]), &[Felt::from(DEFAULT_BLOCK_TIMESTAMP)], ); assert_success( test_env.call_contract(&contract_address2, "get_block_timestamp", &[]), &[Felt::from(DEFAULT_BLOCK_TIMESTAMP)], ); } #[test] fn cheat_block_timestamp_simple_with_span() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockTimestampChecker", &[]); test_env.cheat_block_timestamp( contract_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); assert_success( test_env.call_contract(&contract_address, "get_block_timestamp", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_block_timestamp", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_block_timestamp", &[]), &[Felt::from(DEFAULT_BLOCK_TIMESTAMP)], ); } #[test] fn cheat_block_timestamp_proxy_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatBlockTimestampCheckerProxy", &contracts_data); let contract_address_1 = test_env.deploy_wrapper(&class_hash, &[]); let contract_address_2 = test_env.deploy_wrapper(&class_hash, &[]); test_env.cheat_block_timestamp( contract_address_1, 123, CheatSpan::TargetCalls(NonZeroUsize::new(1).unwrap()), ); let output = test_env.call_contract( &contract_address_1, "call_proxy", &[contract_address_2.into_()], ); assert_success(output, &[123.into(), DEFAULT_BLOCK_TIMESTAMP.into()]); } #[test] fn cheat_block_timestamp_in_constructor_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("ConstructorCheatBlockTimestampChecker", &contracts_data); let precalculated_address = test_env.precalculate_address(&class_hash, &[]); test_env.cheat_block_timestamp( precalculated_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); assert_eq!(precalculated_address, contract_address); assert_success( test_env.call_contract(&contract_address, "get_block_timestamp", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_block_timestamp", &[]), &[Felt::from(DEFAULT_BLOCK_TIMESTAMP)], ); assert_success( test_env.call_contract(&contract_address, "get_stored_block_timestamp", &[]), &[Felt::from(123)], ); } #[test] fn cheat_block_timestamp_no_constructor_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatBlockTimestampChecker", &contracts_data); let precalculated_address = test_env.precalculate_address(&class_hash, &[]); test_env.cheat_block_timestamp( precalculated_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(1).unwrap()), ); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); assert_eq!(precalculated_address, contract_address); assert_success( test_env.call_contract(&contract_address, "get_block_timestamp", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_block_timestamp", &[]), &[Felt::from(DEFAULT_BLOCK_TIMESTAMP)], ); } #[test] fn cheat_block_timestamp_override_span() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockTimestampChecker", &[]); test_env.cheat_block_timestamp( contract_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); assert_success( test_env.call_contract(&contract_address, "get_block_timestamp", &[]), &[Felt::from(123)], ); test_env.cheat_block_timestamp(contract_address, 321, CheatSpan::Indefinite); assert_success( test_env.call_contract(&contract_address, "get_block_timestamp", &[]), &[Felt::from(321)], ); assert_success( test_env.call_contract(&contract_address, "get_block_timestamp", &[]), &[Felt::from(321)], ); test_env.stop_cheat_block_timestamp(contract_address); assert_success( test_env.call_contract(&contract_address, "get_block_timestamp", &[]), &[Felt::from(DEFAULT_BLOCK_TIMESTAMP)], ); } #[test] fn cheat_block_timestamp_library_call_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatBlockTimestampChecker", &contracts_data); let contract_address = test_env.deploy("CheatBlockTimestampCheckerLibCall", &[]); test_env.cheat_block_timestamp( contract_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(1).unwrap()), ); let lib_call_selector = "get_block_timestamp_with_lib_call"; assert_success( test_env.call_contract(&contract_address, lib_call_selector, &[class_hash.into_()]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, lib_call_selector, &[class_hash.into_()]), &[Felt::from(DEFAULT_BLOCK_TIMESTAMP)], ); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/cheat_caller_address.rs ================================================ use crate::common::assertions::assert_success; use crate::common::get_contracts; use cairo_lang_starknet_classes::keccak::starknet_keccak; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::spy_events::Event; use cheatnet::state::CheatSpan; use conversions::IntoConv; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; use std::num::NonZeroUsize; use tempfile::TempDir; use super::test_environment::TestEnvironment; use crate::common::state::create_fork_cached_state_at; use conversions::string::TryFromHexStr; use runtime::starknet::constants::TEST_ADDRESS; trait CheatCallerAddressTrait { fn cheat_caller_address( &mut self, contract_address: ContractAddress, new_address: u128, span: CheatSpan, ); fn start_cheat_caller_address(&mut self, contract_address: ContractAddress, new_address: u128); fn stop_cheat_caller_address(&mut self, contract_address: ContractAddress); } impl CheatCallerAddressTrait for TestEnvironment { fn cheat_caller_address( &mut self, contract_address: ContractAddress, new_address: u128, span: CheatSpan, ) { self.cheatnet_state.cheat_caller_address( contract_address, ContractAddress::from(new_address), span, ); } fn start_cheat_caller_address(&mut self, contract_address: ContractAddress, new_address: u128) { self.cheatnet_state .start_cheat_caller_address(contract_address, ContractAddress::from(new_address)); } fn stop_cheat_caller_address(&mut self, contract_address: ContractAddress) { self.cheatnet_state .stop_cheat_caller_address(contract_address); } } #[test] fn cheat_caller_address_simple() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatCallerAddressChecker", &[]); test_env.start_cheat_caller_address(contract_address, 123); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::from(123)], ); } #[test] fn cheat_caller_address_with_other_syscall() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatCallerAddressChecker", &[]); test_env.start_cheat_caller_address(contract_address, 123); assert_success( test_env.call_contract(&contract_address, "get_caller_address_and_emit_event", &[]), &[Felt::from(123)], ); } #[test] fn cheat_caller_address_in_constructor() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("ConstructorCheatCallerAddressChecker", &contracts_data); let precalculated_address = test_env.precalculate_address(&class_hash, &[]); test_env.start_cheat_caller_address(precalculated_address, 123); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); assert_eq!(precalculated_address, contract_address); assert_success( test_env.call_contract(&contract_address, "get_stored_caller_address", &[]), &[Felt::from(123)], ); } #[test] fn cheat_caller_address_stop() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatCallerAddressChecker", &[]); test_env.start_cheat_caller_address(contract_address, 123); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::from(123)], ); test_env.stop_cheat_caller_address(contract_address); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap()], ); } #[test] fn cheat_caller_address_double() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatCallerAddressChecker", &[]); test_env.start_cheat_caller_address(contract_address, 111); test_env.start_cheat_caller_address(contract_address, 222); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::from(222)], ); test_env.stop_cheat_caller_address(contract_address); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap()], ); } #[test] fn cheat_caller_address_proxy() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatCallerAddressChecker", &[]); let proxy_address = test_env.deploy("CheatCallerAddressCheckerProxy", &[]); test_env.start_cheat_caller_address(contract_address, 123); let selector = "get_cheated_caller_address"; assert_success( test_env.call_contract(&proxy_address, selector, &[contract_address.into_()]), &[Felt::from(123)], ); test_env.stop_cheat_caller_address(contract_address); assert_success( test_env.call_contract(&proxy_address, selector, &[contract_address.into_()]), &[proxy_address.into_()], ); } #[test] fn cheat_caller_address_library_call() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatCallerAddressChecker", &contracts_data); let lib_call_address = test_env.deploy("CheatCallerAddressCheckerLibCall", &[]); test_env.start_cheat_caller_address(lib_call_address, 123); let lib_call_selector = "get_caller_address_with_lib_call"; assert_success( test_env.call_contract(&lib_call_address, lib_call_selector, &[class_hash.into_()]), &[Felt::from(123)], ); test_env.stop_cheat_caller_address(lib_call_address); assert_success( test_env.call_contract(&lib_call_address, lib_call_selector, &[class_hash.into_()]), &[TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap()], ); } #[test] fn cheat_caller_address_all() { let mut test_env = TestEnvironment::new(); let cheat_caller_address_checker = test_env.declare("CheatCallerAddressChecker", &get_contracts()); let contract_address = test_env.deploy_wrapper(&cheat_caller_address_checker, &[]); test_env .cheatnet_state .start_cheat_caller_address_global(123_u8.into()); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::from(123)], ); test_env.cheatnet_state.stop_cheat_caller_address_global(); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap()], ); let contract_address = test_env.deploy_wrapper(&cheat_caller_address_checker, &[]); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap()], ); } #[test] fn cheat_caller_address_multiple() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatCallerAddressChecker", &contracts_data); let contract_address1 = test_env.deploy_wrapper(&class_hash, &[]); let contract_address2 = test_env.deploy_wrapper(&class_hash, &[]); test_env.start_cheat_caller_address(contract_address1, 123); test_env.start_cheat_caller_address(contract_address2, 123); assert_success( test_env.call_contract(&contract_address1, "get_caller_address", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address2, "get_caller_address", &[]), &[Felt::from(123)], ); test_env .cheatnet_state .stop_cheat_caller_address(contract_address1); test_env .cheatnet_state .stop_cheat_caller_address(contract_address2); assert_success( test_env.call_contract(&contract_address1, "get_caller_address", &[]), &[TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap()], ); assert_success( test_env.call_contract(&contract_address2, "get_caller_address", &[]), &[TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap()], ); } #[test] fn cheat_caller_address_all_then_one() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatCallerAddressChecker", &[]); test_env .cheatnet_state .start_cheat_caller_address_global(111_u8.into()); test_env.start_cheat_caller_address(contract_address, 222); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::from(222)], ); } #[test] fn cheat_caller_address_one_then_all() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatCallerAddressChecker", &[]); test_env.start_cheat_caller_address(contract_address, 111); test_env .cheatnet_state .start_cheat_caller_address_global(222_u8.into()); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::from(222)], ); } #[test] fn cheat_caller_address_cairo0_callback() { let temp_dir = TempDir::new().unwrap(); let cached_state = create_fork_cached_state_at(53_631, temp_dir.path().to_str().unwrap()); let mut test_env = TestEnvironment::new(); test_env.cached_state = cached_state; let contract_address = test_env.deploy("Cairo1Contract_v1", &[]); test_env.start_cheat_caller_address(contract_address, 123); let expected_caller_address = Felt::from(123); assert_success( test_env.call_contract( &contract_address, "start", &[ // cairo 0 callback contract address Felt::try_from_hex_str( "0x18783f6c124c3acc504f300cb6b3a33def439681744d027be8d7fd5d3551565", ) .unwrap(), expected_caller_address, ], ), &[], ); let events = test_env.cheatnet_state.get_events(0); // make sure end() was called by cairo0 contract assert_eq!( events[0], Event { from: contract_address, keys: vec![starknet_keccak("End".as_ref()).into()], data: vec![expected_caller_address] }, "Wrong event" ); } #[test] fn cheat_caller_address_simple_with_span() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatCallerAddressChecker", &[]); test_env.cheat_caller_address( contract_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap()], ); } #[test] fn cheat_caller_address_proxy_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatCallerAddressCheckerProxy", &contracts_data); let contract_address_1 = test_env.deploy_wrapper(&class_hash, &[]); let contract_address_2 = test_env.deploy_wrapper(&class_hash, &[]); test_env.cheat_caller_address( contract_address_1, 123, CheatSpan::TargetCalls(NonZeroUsize::new(1).unwrap()), ); let output = test_env.call_contract( &contract_address_1, "call_proxy", &[contract_address_2.into_()], ); assert_success(output, &[123.into(), contract_address_2.into_()]); } #[test] fn cheat_caller_address_override_span() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatCallerAddressChecker", &[]); test_env.cheat_caller_address( contract_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::from(123)], ); test_env.cheat_caller_address(contract_address, 321, CheatSpan::Indefinite); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::from(321)], ); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::from(321)], ); test_env.stop_cheat_caller_address(contract_address); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap()], ); } #[test] fn cheat_caller_address_constructor_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("ConstructorCheatCallerAddressChecker", &contracts_data); let precalculated_address = test_env.precalculate_address(&class_hash, &[]); test_env.cheat_caller_address( precalculated_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(3).unwrap()), ); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); assert_eq!(precalculated_address, contract_address); assert_success( test_env.call_contract(&contract_address, "get_stored_caller_address", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap()], ); } #[test] fn cheat_caller_address_library_call_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatCallerAddressChecker", &contracts_data); let contract_address = test_env.deploy("CheatCallerAddressCheckerLibCall", &[]); test_env.cheat_caller_address( contract_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(1).unwrap()), ); let lib_call_selector = "get_caller_address_with_lib_call"; assert_success( test_env.call_contract(&contract_address, lib_call_selector, &[class_hash.into_()]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, lib_call_selector, &[class_hash.into_()]), &[TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap()], ); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/cheat_execution_info.rs ================================================ use super::test_environment::TestEnvironment; use crate::common::{assertions::assert_success, get_contracts, recover_data}; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::cheat_execution_info::ResourceBounds; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::cheat_execution_info::{ CheatArguments, ExecutionInfoMockOperations, Operation, TxInfoMockOperations, }; use cheatnet::state::CheatSpan; use conversions::IntoConv; use conversions::serde::deserialize::{BufferReader, CairoDeserialize}; use starknet_api::{core::ContractAddress, transaction::TransactionHash}; use starknet_types_core::felt::Felt; use std::num::NonZeroUsize; trait CheatTransactionHashTrait { fn cheat_transaction_hash( &mut self, contract_address: ContractAddress, transaction_hash: Felt, span: CheatSpan, ); fn start_cheat_transaction_hash( &mut self, contract_address: ContractAddress, transaction_hash: Felt, ); fn stop_cheat_transaction_hash(&mut self, contract_address: ContractAddress); fn start_cheat_transaction_hash_global(&mut self, transaction_hash: Felt); fn stop_cheat_transaction_hash_global(&mut self); } impl CheatTransactionHashTrait for TestEnvironment { fn cheat_transaction_hash( &mut self, contract_address: ContractAddress, transaction_hash: Felt, span: CheatSpan, ) { let mut execution_info_mock = ExecutionInfoMockOperations::default(); execution_info_mock.tx_info.transaction_hash = Operation::Start(CheatArguments { value: transaction_hash, span, target: contract_address, }); self.cheatnet_state .cheat_execution_info(execution_info_mock); } fn start_cheat_transaction_hash( &mut self, contract_address: ContractAddress, transaction_hash: Felt, ) { let mut execution_info_mock = ExecutionInfoMockOperations::default(); execution_info_mock.tx_info.transaction_hash = Operation::Start(CheatArguments { value: transaction_hash, span: CheatSpan::Indefinite, target: contract_address, }); self.cheatnet_state .cheat_execution_info(execution_info_mock); } fn stop_cheat_transaction_hash(&mut self, contract_address: ContractAddress) { let mut execution_info_mock = ExecutionInfoMockOperations::default(); execution_info_mock.tx_info.transaction_hash = Operation::Stop(contract_address); self.cheatnet_state .cheat_execution_info(execution_info_mock); } fn start_cheat_transaction_hash_global(&mut self, transaction_hash: Felt) { let mut execution_info_mock = ExecutionInfoMockOperations::default(); execution_info_mock.tx_info.transaction_hash = Operation::StartGlobal(transaction_hash); self.cheatnet_state .cheat_execution_info(execution_info_mock); } fn stop_cheat_transaction_hash_global(&mut self) { let mut execution_info_mock = ExecutionInfoMockOperations::default(); execution_info_mock.tx_info.transaction_hash = Operation::StopGlobal; self.cheatnet_state .cheat_execution_info(execution_info_mock); } } trait CheatTransactionInfoTrait { fn cheat_transaction_info(&mut self, tx_info_mock: TxInfoMockOperations); } impl CheatTransactionInfoTrait for TestEnvironment { fn cheat_transaction_info(&mut self, tx_info_mock: TxInfoMockOperations) { let execution_info_mock_operations = ExecutionInfoMockOperations { tx_info: tx_info_mock, ..Default::default() }; self.cheatnet_state .cheat_execution_info(execution_info_mock_operations); } } trait TxInfoTrait { fn assert_tx_info(&mut self, contract_address: &ContractAddress, expected_tx_info: &TxInfo); fn get_tx_info(&mut self, contract_address: &ContractAddress) -> TxInfo; } impl TxInfoTrait for TestEnvironment { fn assert_tx_info(&mut self, contract_address: &ContractAddress, expected_tx_info: &TxInfo) { let tx_info = self.get_tx_info(contract_address); assert_eq!(tx_info, *expected_tx_info); } fn get_tx_info(&mut self, contract_address: &ContractAddress) -> TxInfo { let call_result = self.call_contract(contract_address, "get_tx_info", &[]); let data = recover_data(call_result); TxInfo::deserialize(&data) } } #[derive(CairoDeserialize, Clone, Default, Debug, PartialEq)] struct TxInfo { pub version: Felt, pub account_contract_address: Felt, pub max_fee: Felt, pub signature: Vec, pub transaction_hash: Felt, pub chain_id: Felt, pub nonce: Felt, pub resource_bounds: Vec, pub tip: Felt, pub paymaster_data: Vec, pub nonce_data_availability_mode: Felt, pub fee_data_availability_mode: Felt, pub account_deployment_data: Vec, pub proof_facts: Vec, } impl TxInfo { fn apply_mock_fields(tx_info_mock: &TxInfoMockOperations, tx_info: &Self) -> Self { macro_rules! clone_field { ($field:ident) => { if let Operation::Start(CheatArguments { value, span: CheatSpan::Indefinite, target: _contract_address, }) = tx_info_mock.$field.clone() { value } else { tx_info.$field.clone() } }; } Self { version: clone_field!(version), account_contract_address: clone_field!(account_contract_address), max_fee: clone_field!(max_fee), signature: clone_field!(signature), transaction_hash: clone_field!(transaction_hash), chain_id: clone_field!(chain_id), nonce: clone_field!(nonce), resource_bounds: clone_field!(resource_bounds), tip: clone_field!(tip), paymaster_data: clone_field!(paymaster_data), nonce_data_availability_mode: clone_field!(nonce_data_availability_mode), fee_data_availability_mode: clone_field!(fee_data_availability_mode), account_deployment_data: clone_field!(account_deployment_data), proof_facts: clone_field!(proof_facts), } } fn deserialize(data: &[Felt]) -> Self { BufferReader::new(data).read().unwrap() } } #[test] fn cheat_transaction_hash_simple() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatTxInfoChecker", &[]); let tx_info_before = test_env.get_tx_info(&contract_address); let transaction_hash = Felt::from(123); let mut expected_tx_info = tx_info_before.clone(); expected_tx_info.transaction_hash = transaction_hash; test_env.start_cheat_transaction_hash(contract_address, transaction_hash); test_env.assert_tx_info(&contract_address, &expected_tx_info); } #[test] fn start_cheat_execution_info_multiple_times() { fn operation_start(contract_address: ContractAddress, value: T) -> Operation { Operation::Start(CheatArguments { value, span: CheatSpan::Indefinite, target: contract_address, }) } let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatTxInfoChecker", &[]); let tx_info_before = test_env.get_tx_info(&contract_address); let initial_tx_info_mock = TxInfoMockOperations { version: operation_start(contract_address, Felt::from(13)), account_contract_address: operation_start(contract_address, Felt::from(66)), max_fee: operation_start(contract_address, Felt::from(77)), signature: operation_start(contract_address, vec![Felt::from(88), Felt::from(89)]), transaction_hash: operation_start(contract_address, Felt::from(123)), chain_id: operation_start(contract_address, Felt::from(22)), nonce: operation_start(contract_address, Felt::from(33)), resource_bounds: operation_start( contract_address, vec![ ResourceBounds { resource: Felt::from(111), max_amount: 222, max_price_per_unit: 333, }, ResourceBounds { resource: Felt::from(444), max_amount: 555, max_price_per_unit: 666, }, ], ), tip: operation_start(contract_address, Felt::from(777)), paymaster_data: operation_start( contract_address, vec![ Felt::from(11), Felt::from(22), Felt::from(33), Felt::from(44), ], ), nonce_data_availability_mode: operation_start(contract_address, Felt::from(55)), fee_data_availability_mode: operation_start(contract_address, Felt::from(66)), account_deployment_data: operation_start( contract_address, vec![Felt::from(777), Felt::from(888), Felt::from(999)], ), proof_facts: operation_start( contract_address, vec![Felt::from(13), Felt::from(14), Felt::from(15)], ), }; let expected_tx_info = TxInfo::apply_mock_fields(&initial_tx_info_mock, &tx_info_before); test_env.cheat_transaction_info(initial_tx_info_mock.clone()); test_env.assert_tx_info(&contract_address, &expected_tx_info); let tx_info_mock = TxInfoMockOperations { version: Operation::Retain, max_fee: Operation::Retain, transaction_hash: Operation::Retain, nonce: Operation::Retain, tip: Operation::Retain, nonce_data_availability_mode: Operation::Retain, account_deployment_data: Operation::Retain, proof_facts: Operation::Retain, ..initial_tx_info_mock }; let expected_tx_info = TxInfo::apply_mock_fields(&tx_info_mock, &expected_tx_info); test_env.cheat_transaction_info(tx_info_mock); test_env.assert_tx_info(&contract_address, &expected_tx_info); let tx_info_mock = TxInfoMockOperations { account_contract_address: Operation::Retain, signature: Operation::Retain, chain_id: Operation::Retain, resource_bounds: Operation::Retain, paymaster_data: Operation::Retain, fee_data_availability_mode: Operation::Retain, ..initial_tx_info_mock }; let expected_tx_info = TxInfo::apply_mock_fields(&tx_info_mock, &expected_tx_info); test_env.cheat_transaction_info(tx_info_mock); test_env.assert_tx_info(&contract_address, &expected_tx_info); } #[test] fn cheat_transaction_hash_start_stop() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatTxInfoChecker", &[]); let tx_info_before = test_env.get_tx_info(&contract_address); let transaction_hash = Felt::from(123); let mut expected_tx_info = tx_info_before.clone(); expected_tx_info.transaction_hash = transaction_hash; test_env.start_cheat_transaction_hash(contract_address, transaction_hash); test_env.assert_tx_info(&contract_address, &expected_tx_info); test_env.stop_cheat_transaction_hash(contract_address); test_env.assert_tx_info(&contract_address, &tx_info_before); } #[test] fn cheat_transaction_hash_stop_no_effect() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatTxInfoChecker", &[]); let tx_info_before = test_env.get_tx_info(&contract_address); test_env.stop_cheat_transaction_hash(contract_address); test_env.assert_tx_info(&contract_address, &tx_info_before); } #[test] fn cheat_transaction_hash_with_other_syscall() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatTxInfoChecker", &[]); let transaction_hash = Felt::from(123); test_env.start_cheat_transaction_hash(contract_address, transaction_hash); let output = test_env.call_contract(&contract_address, "get_tx_hash_and_emit_event", &[]); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_transaction_hash_in_constructor() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("TxHashChecker", &contracts_data); let precalculated_address = test_env.precalculate_address(&class_hash, &[]); let transaction_hash = Felt::from(123); test_env.start_cheat_transaction_hash(precalculated_address, transaction_hash); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); assert_eq!(precalculated_address, contract_address); let output = test_env.call_contract(&contract_address, "get_stored_tx_hash", &[]); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_transaction_hash_proxy() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatTxInfoChecker", &[]); let transaction_hash = Felt::from(123); test_env.start_cheat_transaction_hash(contract_address, transaction_hash); let output = test_env.call_contract(&contract_address, "get_transaction_hash", &[]); assert_success(output, &[Felt::from(123)]); let proxy_address = test_env.deploy("TxHashCheckerProxy", &[]); let output = test_env.call_contract( &proxy_address, "get_checkers_tx_hash", &[contract_address.into_()], ); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_transaction_hash_library_call() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatTxInfoChecker", &contracts_data); let lib_call_address = test_env.deploy("CheatTxInfoCheckerLibCall", &[]); let transaction_hash = Felt::from(123); test_env.start_cheat_transaction_hash(lib_call_address, transaction_hash); let output = test_env.call_contract( &lib_call_address, "get_tx_hash_with_lib_call", &[class_hash.into_()], ); assert_success(output, &[Felt::from(123)]); } #[test] fn cheat_transaction_hash_all_simple() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatTxInfoChecker", &[]); let tx_info_before = test_env.get_tx_info(&contract_address); let transaction_hash = Felt::from(123); let mut expected_tx_info = tx_info_before.clone(); expected_tx_info.transaction_hash = transaction_hash; test_env.start_cheat_transaction_hash_global(transaction_hash); test_env.assert_tx_info(&contract_address, &expected_tx_info); } #[test] fn cheat_transaction_hash_all_then_one() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatTxInfoChecker", &[]); let tx_info_before = test_env.get_tx_info(&contract_address); let transaction_hash = Felt::from(321); let mut expected_tx_info = tx_info_before.clone(); expected_tx_info.transaction_hash = transaction_hash; test_env.start_cheat_transaction_hash_global(Felt::from(123)); test_env.start_cheat_transaction_hash(contract_address, transaction_hash); test_env.assert_tx_info(&contract_address, &expected_tx_info); } #[test] fn cheat_transaction_hash_one_then_all() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatTxInfoChecker", &[]); let tx_info_before = test_env.get_tx_info(&contract_address); let transaction_hash = Felt::from(321); let mut expected_tx_info = tx_info_before.clone(); expected_tx_info.transaction_hash = transaction_hash; test_env.start_cheat_transaction_hash(contract_address, Felt::from(123)); test_env.start_cheat_transaction_hash_global(transaction_hash); test_env.assert_tx_info(&contract_address, &expected_tx_info); } #[test] fn cheat_transaction_hash_all_stop() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatTxInfoChecker", &[]); let tx_info_before = test_env.get_tx_info(&contract_address); let transaction_hash = Felt::from(123); let expected_tx_info = tx_info_before.clone(); test_env.start_cheat_transaction_hash_global(transaction_hash); test_env.stop_cheat_transaction_hash_global(); test_env.assert_tx_info(&contract_address, &expected_tx_info); } #[test] fn cheat_transaction_hash_multiple() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatTxInfoChecker", &contracts_data); let contract_address_1 = test_env.deploy_wrapper(&class_hash, &[]); let contract_address_2 = test_env.deploy_wrapper(&class_hash, &[]); let tx_info_before_1 = test_env.get_tx_info(&contract_address_1); let tx_info_before_2 = test_env.get_tx_info(&contract_address_2); let transaction_hash = Felt::from(123); let mut expected_tx_info_1 = tx_info_before_1.clone(); let mut expected_tx_info_2 = tx_info_before_2.clone(); expected_tx_info_1.transaction_hash = transaction_hash; expected_tx_info_2.transaction_hash = transaction_hash; test_env.start_cheat_transaction_hash(contract_address_1, transaction_hash); test_env.start_cheat_transaction_hash(contract_address_2, transaction_hash); test_env.assert_tx_info(&contract_address_1, &expected_tx_info_1); test_env.assert_tx_info(&contract_address_2, &expected_tx_info_2); test_env.stop_cheat_transaction_hash_global(); test_env.assert_tx_info(&contract_address_1, &tx_info_before_1); test_env.assert_tx_info(&contract_address_2, &tx_info_before_2); } #[test] fn cheat_transaction_hash_simple_with_span() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatTxInfoChecker", &[]); let tx_info_before = test_env.get_tx_info(&contract_address); let transaction_hash = Felt::from(123); let mut expected_tx_info = tx_info_before.clone(); expected_tx_info.transaction_hash = transaction_hash; test_env.cheat_transaction_hash( contract_address, transaction_hash, CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); test_env.assert_tx_info(&contract_address, &expected_tx_info); test_env.assert_tx_info(&contract_address, &expected_tx_info); test_env.assert_tx_info(&contract_address, &tx_info_before); } #[test] fn cheat_transaction_hash_proxy_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("TxHashCheckerProxy", &contracts_data); let contract_address_1 = test_env.deploy_wrapper(&class_hash, &[]); let contract_address_2 = test_env.deploy_wrapper(&class_hash, &[]); test_env.cheat_transaction_hash( contract_address_1, Felt::from(123), CheatSpan::TargetCalls(NonZeroUsize::new(1).unwrap()), ); let output = test_env.call_contract( &contract_address_1, "call_proxy", &[contract_address_2.into_()], ); assert_success(output, &[123.into(), TransactionHash::default().0.into_()]); } #[test] fn cheat_transaction_hash_in_constructor_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("TxHashChecker", &contracts_data); let precalculated_address = test_env.precalculate_address(&class_hash, &[]); test_env.cheat_transaction_hash( precalculated_address, Felt::from(123), CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); assert_eq!(precalculated_address, contract_address); assert_success( test_env.call_contract(&contract_address, "get_transaction_hash", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_transaction_hash", &[]), &[TransactionHash::default().0.into_()], ); assert_success( test_env.call_contract(&contract_address, "get_stored_tx_hash", &[]), &[Felt::from(123)], ); } #[test] fn cheat_transaction_hash_no_constructor_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatTxInfoChecker", &contracts_data); let precalculated_address = test_env.precalculate_address(&class_hash, &[]); test_env.cheat_transaction_hash( precalculated_address, Felt::from(123), CheatSpan::TargetCalls(NonZeroUsize::new(1).unwrap()), ); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); assert_eq!(precalculated_address, contract_address); assert_success( test_env.call_contract(&contract_address, "get_transaction_hash", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_transaction_hash", &[]), &[TransactionHash::default().0.into_()], ); } #[test] fn cheat_transaction_hash_override_span() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatTxInfoChecker", &[]); let tx_info_before = test_env.get_tx_info(&contract_address); let transaction_hash = Felt::from(123); let mut expected_tx_info = tx_info_before.clone(); expected_tx_info.transaction_hash = transaction_hash; test_env.cheat_transaction_hash(contract_address, transaction_hash, CheatSpan::Indefinite); test_env.assert_tx_info(&contract_address, &expected_tx_info); let transaction_hash = Felt::from(321); expected_tx_info.transaction_hash = transaction_hash; test_env.cheat_transaction_hash( contract_address, transaction_hash, CheatSpan::TargetCalls(NonZeroUsize::new(1).unwrap()), ); test_env.assert_tx_info(&contract_address, &expected_tx_info); test_env.assert_tx_info(&contract_address, &tx_info_before); } #[test] fn cheat_transaction_hash_library_call_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatTxInfoChecker", &contracts_data); let contract_address = test_env.deploy("CheatTxInfoCheckerLibCall", &[]); let tx_info_before = test_env.get_tx_info(&contract_address); test_env.cheat_transaction_hash( contract_address, Felt::from(123), CheatSpan::TargetCalls(NonZeroUsize::new(1).unwrap()), ); let lib_call_selector = "get_tx_hash_with_lib_call"; assert_success( test_env.call_contract(&contract_address, lib_call_selector, &[class_hash.into_()]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, lib_call_selector, &[class_hash.into_()]), &[tx_info_before.transaction_hash], ); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/cheat_sequencer_address.rs ================================================ use super::test_environment::TestEnvironment; use crate::{common::assertions::assert_success, common::get_contracts}; use cheatnet::state::CheatSpan; use conversions::IntoConv; use conversions::string::TryFromHexStr; use runtime::starknet::context::SEQUENCER_ADDRESS; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; use std::num::NonZeroUsize; trait CheatSequencerAddressTrait { fn cheat_sequencer_address( &mut self, contract_address: ContractAddress, sequencer_address: u128, span: CheatSpan, ); fn start_cheat_sequencer_address( &mut self, contract_address: ContractAddress, sequencer_address: u128, ); fn stop_cheat_sequencer_address(&mut self, contract_address: ContractAddress); } impl CheatSequencerAddressTrait for TestEnvironment { fn cheat_sequencer_address( &mut self, contract_address: ContractAddress, sequencer_address: u128, span: CheatSpan, ) { self.cheatnet_state.cheat_sequencer_address( contract_address, ContractAddress::from(sequencer_address), span, ); } fn start_cheat_sequencer_address( &mut self, contract_address: ContractAddress, sequencer_address: u128, ) { self.cheatnet_state.start_cheat_sequencer_address( contract_address, ContractAddress::from(sequencer_address), ); } fn stop_cheat_sequencer_address(&mut self, contract_address: ContractAddress) { self.cheatnet_state .stop_cheat_sequencer_address(contract_address); } } #[test] fn cheat_sequencer_address_simple() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatSequencerAddressChecker", &[]); test_env.start_cheat_sequencer_address(contract_address, 123); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[Felt::from(123)], ); } #[test] fn cheat_sequencer_address_with_other_syscall() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatSequencerAddressChecker", &[]); test_env.start_cheat_sequencer_address(contract_address, 123); assert_success( test_env.call_contract(&contract_address, "get_seq_addr_and_emit_event", &[]), &[Felt::from(123)], ); } #[test] fn cheat_sequencer_address_in_constructor() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("ConstructorCheatSequencerAddressChecker", &contracts_data); let precalculated_address = test_env.precalculate_address(&class_hash, &[]); test_env.start_cheat_sequencer_address(precalculated_address, 123); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); assert_eq!(precalculated_address, contract_address); assert_success( test_env.call_contract(&contract_address, "get_stored_sequencer_address", &[]), &[Felt::from(123)], ); } #[test] fn cheat_sequencer_address_stop() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatSequencerAddressChecker", &[]); test_env.start_cheat_sequencer_address(contract_address, 123); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[Felt::from(123)], ); test_env .cheatnet_state .stop_cheat_sequencer_address(contract_address); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[TryFromHexStr::try_from_hex_str(SEQUENCER_ADDRESS).unwrap()], ); } #[test] fn cheat_sequencer_address_double() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatSequencerAddressChecker", &[]); test_env.start_cheat_sequencer_address(contract_address, 111); test_env.start_cheat_sequencer_address(contract_address, 222); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[Felt::from(222)], ); test_env.stop_cheat_sequencer_address(contract_address); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[TryFromHexStr::try_from_hex_str(SEQUENCER_ADDRESS).unwrap()], ); } #[test] fn cheat_sequencer_address_proxy() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatSequencerAddressChecker", &[]); let proxy_address = test_env.deploy("CheatSequencerAddressCheckerProxy", &[]); test_env.start_cheat_sequencer_address(contract_address, 123); let selector = "get_cheated_sequencer_address"; assert_success( test_env.call_contract(&proxy_address, selector, &[contract_address.into_()]), &[Felt::from(123)], ); test_env.stop_cheat_sequencer_address(contract_address); assert_success( test_env.call_contract(&proxy_address, selector, &[contract_address.into_()]), &[TryFromHexStr::try_from_hex_str(SEQUENCER_ADDRESS).unwrap()], ); } #[test] fn cheat_sequencer_address_library_call() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatSequencerAddressChecker", &contracts_data); let lib_call_address = test_env.deploy("CheatSequencerAddressCheckerLibCall", &[]); let lib_call_selector = "get_sequencer_address_with_lib_call"; test_env.start_cheat_sequencer_address(lib_call_address, 123); assert_success( test_env.call_contract(&lib_call_address, lib_call_selector, &[class_hash.into_()]), &[Felt::from(123)], ); test_env.stop_cheat_sequencer_address(lib_call_address); assert_success( test_env.call_contract(&lib_call_address, lib_call_selector, &[class_hash.into_()]), &[TryFromHexStr::try_from_hex_str(SEQUENCER_ADDRESS).unwrap()], ); } #[test] fn cheat_sequencer_address_all_simple() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatSequencerAddressChecker", &[]); test_env .cheatnet_state .start_cheat_sequencer_address_global(123_u8.into()); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[Felt::from(123)], ); } #[test] fn cheat_sequencer_address_all_then_one() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatSequencerAddressChecker", &[]); test_env .cheatnet_state .start_cheat_sequencer_address_global(111_u8.into()); test_env.start_cheat_sequencer_address(contract_address, 222); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[Felt::from(222)], ); } #[test] fn cheat_sequencer_address_one_then_all() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatSequencerAddressChecker", &[]); test_env.start_cheat_sequencer_address(contract_address, 111); test_env .cheatnet_state .start_cheat_sequencer_address_global(222_u8.into()); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[Felt::from(222)], ); } #[test] fn cheat_sequencer_address_all_stop() { let mut test_env = TestEnvironment::new(); let cheat_sequencer_address_checker = test_env.declare("CheatSequencerAddressChecker", &get_contracts()); let contract_address = test_env.deploy_wrapper(&cheat_sequencer_address_checker, &[]); test_env .cheatnet_state .start_cheat_sequencer_address_global(123_u8.into()); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[Felt::from(123)], ); test_env .cheatnet_state .stop_cheat_sequencer_address_global(); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[TryFromHexStr::try_from_hex_str(SEQUENCER_ADDRESS).unwrap()], ); let contract_address = test_env.deploy_wrapper(&cheat_sequencer_address_checker, &[]); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[TryFromHexStr::try_from_hex_str(SEQUENCER_ADDRESS).unwrap()], ); } #[test] fn cheat_sequencer_address_multiple() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatSequencerAddressChecker", &contracts_data); let contract_address1 = test_env.deploy_wrapper(&class_hash, &[]); let contract_address2 = test_env.deploy_wrapper(&class_hash, &[]); test_env.start_cheat_sequencer_address(contract_address1, 123); test_env.start_cheat_sequencer_address(contract_address2, 123); assert_success( test_env.call_contract(&contract_address1, "get_sequencer_address", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address2, "get_sequencer_address", &[]), &[Felt::from(123)], ); test_env .cheatnet_state .stop_cheat_sequencer_address(contract_address1); test_env .cheatnet_state .stop_cheat_sequencer_address(contract_address2); assert_success( test_env.call_contract(&contract_address1, "get_sequencer_address", &[]), &[TryFromHexStr::try_from_hex_str(SEQUENCER_ADDRESS).unwrap()], ); assert_success( test_env.call_contract(&contract_address2, "get_sequencer_address", &[]), &[TryFromHexStr::try_from_hex_str(SEQUENCER_ADDRESS).unwrap()], ); } #[test] fn cheat_sequencer_address_simple_with_span() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatSequencerAddressChecker", &[]); test_env.cheat_sequencer_address( contract_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[TryFromHexStr::try_from_hex_str(SEQUENCER_ADDRESS).unwrap()], ); } #[test] fn cheat_sequencer_address_proxy_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatSequencerAddressCheckerProxy", &contracts_data); let contract_address_1 = test_env.deploy_wrapper(&class_hash, &[]); let contract_address_2 = test_env.deploy_wrapper(&class_hash, &[]); test_env.cheat_sequencer_address( contract_address_1, 123, CheatSpan::TargetCalls(NonZeroUsize::new(1).unwrap()), ); let output = test_env.call_contract( &contract_address_1, "call_proxy", &[contract_address_2.into_()], ); assert_success( output, &[ 123.into(), TryFromHexStr::try_from_hex_str(SEQUENCER_ADDRESS).unwrap(), ], ); } #[test] fn cheat_sequencer_address_in_constructor_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("ConstructorCheatSequencerAddressChecker", &contracts_data); let precalculated_address = test_env .cheatnet_state .precalculate_address(&class_hash, &[]); test_env.cheat_sequencer_address( precalculated_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); assert_eq!(precalculated_address, contract_address); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[TryFromHexStr::try_from_hex_str(SEQUENCER_ADDRESS).unwrap()], ); assert_success( test_env.call_contract(&contract_address, "get_stored_sequencer_address", &[]), &[Felt::from(123)], ); } #[test] fn cheat_sequencer_address_no_constructor_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatSequencerAddressChecker", &contracts_data); let precalculated_address = test_env .cheatnet_state .precalculate_address(&class_hash, &[]); test_env.cheat_sequencer_address( precalculated_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(1).unwrap()), ); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); assert_eq!(precalculated_address, contract_address); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[TryFromHexStr::try_from_hex_str(SEQUENCER_ADDRESS).unwrap()], ); } #[test] fn cheat_sequencer_address_override_span() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatSequencerAddressChecker", &[]); test_env.cheat_sequencer_address( contract_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[Felt::from(123)], ); test_env.cheat_sequencer_address(contract_address, 321, CheatSpan::Indefinite); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[Felt::from(321)], ); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[Felt::from(321)], ); test_env.stop_cheat_sequencer_address(contract_address); assert_success( test_env.call_contract(&contract_address, "get_sequencer_address", &[]), &[TryFromHexStr::try_from_hex_str(SEQUENCER_ADDRESS).unwrap()], ); } #[test] fn cheat_sequencer_address_library_call_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("CheatSequencerAddressChecker", &contracts_data); let contract_address = test_env.deploy("CheatSequencerAddressCheckerLibCall", &[]); test_env.cheat_sequencer_address( contract_address, 123, CheatSpan::TargetCalls(NonZeroUsize::new(1).unwrap()), ); let lib_call_selector = "get_sequencer_address_with_lib_call"; assert_success( test_env.call_contract(&contract_address, lib_call_selector, &[class_hash.into_()]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, lib_call_selector, &[class_hash.into_()]), &[TryFromHexStr::try_from_hex_str(SEQUENCER_ADDRESS).unwrap()], ); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/declare.rs ================================================ use crate::common::assertions::ClassHashAssert; use crate::common::{get_contracts, state::create_cached_state}; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::CheatcodeError; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::declare::{ DeclareResult, declare, }; use runtime::EnhancedHintError; #[test] fn declare_simple() { let contract_name = "HelloStarknet"; let mut cached_state = create_cached_state(); let contracts_data = get_contracts(); let class_hash = declare(&mut cached_state, contract_name, &contracts_data) .unwrap() .unwrap_success(); let expected_class_hash = contracts_data.get_class_hash(contract_name).unwrap(); assert_eq!(class_hash, *expected_class_hash); } #[test] fn declare_multiple() { let contract_names = vec!["HelloStarknet", "ConstructorSimple"]; let mut cached_state = create_cached_state(); let contracts_data = get_contracts(); for contract_name in contract_names { let class_hash = declare(&mut cached_state, contract_name, &contracts_data) .unwrap() .unwrap_success(); let expected_class_hash = contracts_data.get_class_hash(contract_name).unwrap(); assert_eq!(class_hash, *expected_class_hash); } } #[test] fn declare_same_contract() { let contract_name = "HelloStarknet"; let mut cached_state = create_cached_state(); let contracts_data = get_contracts(); let class_hash = declare(&mut cached_state, contract_name, &contracts_data) .unwrap() .unwrap_success(); let expected_class_hash = contracts_data.get_class_hash(contract_name).unwrap(); assert_eq!(class_hash, *expected_class_hash); let output = declare(&mut cached_state, contract_name, &contracts_data); assert!( matches!(output, Ok(DeclareResult::AlreadyDeclared(class_hash)) if class_hash == *expected_class_hash) ); } #[test] fn declare_non_existent() { let contract_name = "GoodbyeStarknet"; let mut cached_state = create_cached_state(); let contracts_data = get_contracts(); let output = declare(&mut cached_state, contract_name, &contracts_data); assert!(match output { Err(CheatcodeError::Unrecoverable(EnhancedHintError::Anyhow(msg))) => { let msg = msg.to_string(); msg.contains("Failed") && msg.contains(contract_name) } _ => false, }); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/generate_random_felt.rs ================================================ use starknet_types_core::felt::Felt; use std::collections::HashSet; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::generate_random_felt::generate_random_felt; #[test] fn test_generate_random_felt_range_and_uniqueness() { let mut random_values = vec![]; let max_felt: Felt = Felt::MAX; for _ in 0..10 { let random_value = generate_random_felt(); assert!(random_value < max_felt, "Value out of range"); random_values.push(random_value); } let unique_values: HashSet<_> = random_values.iter().collect(); assert!( unique_values.len() > 1, "Random values should not all be identical." ); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/get_class_hash.rs ================================================ use crate::common::assertions::ClassHashAssert; use crate::common::get_contracts; use crate::common::state::create_cached_state; use crate::common::{call_contract, deploy}; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::declare::declare; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::get_class_hash::get_class_hash; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::storage::selector_from_name; use cheatnet::state::CheatnetState; use conversions::IntoConv; #[test] fn get_class_hash_simple() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contracts_data = get_contracts(); let class_hash = declare(&mut cached_state, "HelloStarknet", &contracts_data) .unwrap() .unwrap_success(); let contract_address = deploy(&mut cached_state, &mut cheatnet_state, &class_hash, &[]); assert_eq!( class_hash, get_class_hash(&mut cached_state, contract_address).unwrap() ); } #[test] fn get_class_hash_upgrade() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contracts_data = get_contracts(); let class_hash = declare(&mut cached_state, "GetClassHashCheckerUpg", &contracts_data) .unwrap() .unwrap_success(); let contract_address = deploy(&mut cached_state, &mut cheatnet_state, &class_hash, &[]); assert_eq!( class_hash, get_class_hash(&mut cached_state, contract_address).unwrap() ); let hello_starknet_class_hash = declare(&mut cached_state, "HelloStarknet", &contracts_data) .unwrap() .unwrap_success(); let selector = selector_from_name("upgrade"); call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[hello_starknet_class_hash.into_()], ) .unwrap(); assert_eq!( hello_starknet_class_hash, get_class_hash(&mut cached_state, contract_address).unwrap() ); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/library_call.rs ================================================ use crate::cheatcodes::test_environment::TestEnvironment; use crate::common::assertions::assert_success; use crate::common::get_contracts; use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::rpc::CallSuccess; use cheatnet::state::CheatSpan; use conversions::string::TryFromHexStr; use runtime::starknet::constants::TEST_ADDRESS; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; #[test] fn global_cheat_works_with_library_call_from_test() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("ExampleContract", &contracts_data); let cheated_caller_address = 123_u8; test_env .cheatnet_state .start_cheat_caller_address_global(cheated_caller_address.into()); assert_success( test_env.library_call_contract(&class_hash, "get_caller_address", &[]), &[Felt::from(cheated_caller_address)], ); assert_success( test_env.library_call_contract(&class_hash, "get_caller_address", &[]), &[Felt::from(cheated_caller_address)], ); test_env.cheatnet_state.stop_cheat_caller_address_global(); assert_success( test_env.library_call_contract(&class_hash, "get_caller_address", &[]), &[ContractAddress::default().into()], ); } #[test] fn cheat_with_finite_span_works_with_library_call_from_test() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("ExampleContract", &contracts_data); let cheated_caller_address = 123_u8; test_env.cheatnet_state.cheat_caller_address( ContractAddress::try_from_hex_str(TEST_ADDRESS).unwrap(), cheated_caller_address.into(), CheatSpan::TargetCalls(1.try_into().unwrap()), ); assert_success( test_env.library_call_contract(&class_hash, "get_caller_address", &[]), &[Felt::from(cheated_caller_address)], ); // Library call doesn't trigger the cheat removal because it's not a contract call // hence after second library call the cheated address is still in effect. assert_success( test_env.library_call_contract(&class_hash, "get_caller_address", &[]), &[Felt::from(cheated_caller_address)], ); test_env .cheatnet_state .stop_cheat_caller_address(ContractAddress::try_from_hex_str(TEST_ADDRESS).unwrap()); assert_success( test_env.library_call_contract(&class_hash, "get_caller_address", &[]), &[ContractAddress::default().into()], ); } #[test] fn cheat_with_indefinite_span_works_with_library_call_from_test() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("ExampleContract", &contracts_data); let cheated_caller_address = 123_u8; test_env.cheatnet_state.cheat_caller_address( ContractAddress::try_from_hex_str(TEST_ADDRESS).unwrap(), cheated_caller_address.into(), CheatSpan::Indefinite, ); assert_success( test_env.library_call_contract(&class_hash, "get_caller_address", &[]), &[Felt::from(cheated_caller_address)], ); assert_success( test_env.library_call_contract(&class_hash, "get_caller_address", &[]), &[Felt::from(cheated_caller_address)], ); test_env .cheatnet_state .stop_cheat_caller_address(ContractAddress::try_from_hex_str(TEST_ADDRESS).unwrap()); assert_success( test_env.library_call_contract(&class_hash, "get_caller_address", &[]), &[ContractAddress::default().into()], ); } #[test] fn global_cheat_works_with_library_call_from_actual_contract() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let contract_address = test_env.deploy("ExampleContractLibraryCall", &[]); let class_hash = test_env.declare("ExampleContract", &contracts_data); let cheated_caller_address = 123_u8; let set_class_hash_result = test_env.call_contract(&contract_address, "set_class_hash", &[class_hash.into()]); assert!(matches!(set_class_hash_result, Ok(CallSuccess { .. }))); test_env .cheatnet_state .start_cheat_caller_address_global(cheated_caller_address.into()); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::from(cheated_caller_address)], ); test_env.cheatnet_state.stop_cheat_caller_address_global(); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::try_from_hex_str(TEST_ADDRESS).unwrap()], ); } #[test] fn cheat_with_finite_span_works_with_library_call_from_actual_contract() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let contract_address = test_env.deploy("ExampleContractLibraryCall", &[]); let class_hash = test_env.declare("ExampleContract", &contracts_data); let set_class_hash_result = test_env.call_contract(&contract_address, "set_class_hash", &[class_hash.into()]); assert!(matches!(set_class_hash_result, Ok(CallSuccess { .. }))); let cheated_caller_address = 123_u8; test_env.cheatnet_state.cheat_caller_address( contract_address, cheated_caller_address.into(), CheatSpan::TargetCalls(1.try_into().unwrap()), ); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::from(cheated_caller_address)], ); // We made one contract call, so the cheat should be removed now. assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::try_from_hex_str(TEST_ADDRESS).unwrap()], ); } #[test] fn cheat_with_indefinite_span_works_with_library_call_from_actual_contract() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let contract_address = test_env.deploy("ExampleContractLibraryCall", &[]); let class_hash = test_env.declare("ExampleContract", &contracts_data); let set_class_hash_result = test_env.call_contract(&contract_address, "set_class_hash", &[class_hash.into()]); assert!(matches!(set_class_hash_result, Ok(CallSuccess { .. }))); let cheated_caller_address = 123_u8; test_env.cheatnet_state.cheat_caller_address( contract_address, cheated_caller_address.into(), CheatSpan::Indefinite, ); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::from(cheated_caller_address)], ); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::from(cheated_caller_address)], ); test_env .cheatnet_state .stop_cheat_caller_address(contract_address); assert_success( test_env.call_contract(&contract_address, "get_caller_address", &[]), &[Felt::try_from_hex_str(TEST_ADDRESS).unwrap()], ); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/load.rs ================================================ use crate::common::get_contracts; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::storage::{ load, map_entry_address, variable_address, }; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; use super::test_environment::TestEnvironment; trait LoadTrait { fn load(&mut self, target: ContractAddress, storage_address: Felt) -> Felt; } impl LoadTrait for TestEnvironment { fn load(&mut self, target: ContractAddress, storage_address: Felt) -> Felt { load(&mut self.cached_state, target, storage_address).unwrap() } } #[test] fn load_simple_state() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("HelloStarknet", &contracts_data); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); test_env .call_contract(&contract_address, "increase_balance", &[Felt::from(420)]) .unwrap(); let balance_value = test_env.load(contract_address, variable_address("balance")); assert_eq!( balance_value, Felt::from(420), "Wrong data value was returned: {balance_value}" ); } #[test] fn load_state_map_simple_value() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("MapSimpleValueSimpleKey", &contracts_data); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); let map_key = Felt::from(420); let inserted_value = Felt::from(69); test_env .call_contract(&contract_address, "insert", &[map_key, inserted_value]) .unwrap(); let var_address = map_entry_address("values", &[map_key]); let map_value = test_env.load(contract_address, var_address); assert_eq!( map_value, inserted_value, "Wrong data value was returned: {map_value}" ); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/meta_tx_v0.rs ================================================ use crate::common::assertions::assert_success; use conversions::IntoConv; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; use super::test_environment::TestEnvironment; #[test] fn meta_tx_v0_with_cheat_caller_address() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatCallerAddressCheckerMetaTxV0", &[]); test_env .cheatnet_state .start_cheat_caller_address(contract_address, ContractAddress::from(123_u32)); let meta_contract = test_env.deploy("MetaTxV0Test", &[]); let signature = vec![]; let meta_result = test_env.call_contract( &meta_contract, "execute_meta_tx_v0", &[contract_address.into_(), signature.len().into()] .into_iter() .chain(signature) .collect::>(), ); assert_success(meta_result, &[Felt::from(123)]); } #[test] fn meta_tx_v0_with_cheat_block_number() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockNumberCheckerMetaTxV0", &[]); test_env .cheatnet_state .start_cheat_block_number(contract_address, 999_u64); let meta_contract = test_env.deploy("MetaTxV0Test", &[]); let signature = vec![]; let meta_result = test_env.call_contract( &meta_contract, "execute_meta_tx_v0", &[contract_address.into_(), signature.len().into()] .into_iter() .chain(signature) .collect::>(), ); assert_success(meta_result, &[Felt::from(999)]); } #[test] fn meta_tx_v0_with_cheat_block_timestamp() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockTimestampCheckerMetaTxV0", &[]); test_env .cheatnet_state .start_cheat_block_timestamp(contract_address, 1_234_567_890_u64); let meta_contract = test_env.deploy("MetaTxV0Test", &[]); let signature = vec![]; let meta_result = test_env.call_contract( &meta_contract, "execute_meta_tx_v0", &[contract_address.into_(), signature.len().into()] .into_iter() .chain(signature) .collect::>(), ); assert_success(meta_result, &[Felt::from(1_234_567_890_u64)]); } #[test] fn meta_tx_v0_with_cheat_sequencer_address() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatSequencerAddressCheckerMetaTxV0", &[]); test_env .cheatnet_state .start_cheat_sequencer_address(contract_address, ContractAddress::from(777_u32)); let meta_contract = test_env.deploy("MetaTxV0Test", &[]); let signature = vec![]; let meta_result = test_env.call_contract( &meta_contract, "execute_meta_tx_v0", &[contract_address.into_(), signature.len().into()] .into_iter() .chain(signature) .collect::>(), ); assert_success(meta_result, &[Felt::from(777)]); } #[test] fn meta_tx_v0_with_cheat_block_hash() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("CheatBlockHashCheckerMetaTxV0", &[]); let block_number = 100; test_env .cheatnet_state .start_cheat_block_hash(contract_address, block_number, Felt::from(555)); let meta_contract = test_env.deploy("MetaTxV0Test", &[]); let signature = vec![]; let meta_result = test_env.call_contract( &meta_contract, "execute_meta_tx_v0_get_block_hash", &[ contract_address.into_(), block_number.into(), signature.len().into(), ] .into_iter() .chain(signature) .collect::>(), ); assert_success(meta_result, &[Felt::from(555)]); } #[test] fn meta_tx_v0_verify_tx_context_modification() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("TxInfoCheckerMetaTxV0", &[]); // Call __execute__ directly, should return original version (3) let direct_execute_result = test_env.call_contract(&contract_address, "__execute__", &[]); let meta_contract = test_env.deploy("MetaTxV0Test", &[]); let signature = vec![]; let meta_result = test_env.call_contract( &meta_contract, "execute_meta_tx_v0", &[contract_address.into_(), signature.len().into()] .into_iter() .chain(signature.clone()) .collect::>(), ); assert_success(meta_result, &[Felt::ZERO]); assert_success(direct_execute_result, &[Felt::from(3_u32)]); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/mock_call.rs ================================================ use super::test_environment::TestEnvironment; use crate::common::assertions::ClassHashAssert; use crate::common::recover_data; use crate::common::state::create_cached_state; use crate::common::{call_contract, deploy}; use crate::{ common::assertions::assert_success, common::{deploy_contract, get_contracts}, }; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::declare::declare; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::storage::selector_from_name; use cheatnet::state::{CheatSpan, CheatnetState}; use conversions::IntoConv; use starknet_api::core::ContractAddress; use starknet_rust::core::utils::get_selector_from_name; use starknet_types_core::felt::Felt; use std::num::NonZeroUsize; trait MockCallTrait { fn mock_call( &mut self, contract_address: &ContractAddress, function_name: &str, ret_data: &[u128], span: CheatSpan, ); fn stop_mock_call(&mut self, contract_address: &ContractAddress, function_name: &str); } impl MockCallTrait for TestEnvironment { fn mock_call( &mut self, contract_address: &ContractAddress, function_name: &str, ret_data: &[u128], span: CheatSpan, ) { let ret_data: Vec = ret_data.iter().map(|x| Felt::from(*x)).collect(); let function_selector = get_selector_from_name(function_name).unwrap(); self.cheatnet_state.mock_call( *contract_address, function_selector.into_(), &ret_data, span, ); } fn stop_mock_call(&mut self, contract_address: &ContractAddress, function_name: &str) { let function_selector = get_selector_from_name(function_name).unwrap(); self.cheatnet_state .stop_mock_call(*contract_address, function_selector.into_()); } } #[test] fn mock_call_simple() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract( &mut cached_state, &mut cheatnet_state, "MockChecker", &[Felt::from(420)], ); let selector = selector_from_name("get_thing"); let ret_data = [Felt::from(123)]; cheatnet_state.start_mock_call(contract_address, selector_from_name("get_thing"), &ret_data); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &ret_data); } #[test] fn mock_call_stop() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract( &mut cached_state, &mut cheatnet_state, "MockChecker", &[Felt::from(420)], ); let selector = selector_from_name("get_thing"); let ret_data = [Felt::from(123)]; cheatnet_state.start_mock_call(contract_address, selector_from_name("get_thing"), &ret_data); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &ret_data); cheatnet_state.stop_mock_call(contract_address, selector_from_name("get_thing")); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &[Felt::from(420)]); } #[test] fn mock_call_stop_no_start() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract( &mut cached_state, &mut cheatnet_state, "MockChecker", &[Felt::from(420)], ); let selector = selector_from_name("get_thing"); cheatnet_state.stop_mock_call(contract_address, selector_from_name("get_thing")); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &[Felt::from(420)]); } #[test] fn mock_call_double() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract( &mut cached_state, &mut cheatnet_state, "MockChecker", &[Felt::from(420)], ); let selector = selector_from_name("get_thing"); let ret_data = [Felt::from(123)]; cheatnet_state.start_mock_call(contract_address, selector, &ret_data); let ret_data = [Felt::from(999)]; cheatnet_state.start_mock_call(contract_address, selector, &ret_data); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &ret_data); cheatnet_state.stop_mock_call(contract_address, selector); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &[Felt::from(420)]); } #[test] fn mock_call_double_call() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract( &mut cached_state, &mut cheatnet_state, "MockChecker", &[Felt::from(420)], ); let selector = selector_from_name("get_thing"); let ret_data = [Felt::from(123)]; cheatnet_state.start_mock_call(contract_address, selector_from_name("get_thing"), &ret_data); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &ret_data); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &ret_data); } #[test] fn mock_call_proxy() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract( &mut cached_state, &mut cheatnet_state, "MockChecker", &[Felt::from(420)], ); let selector = selector_from_name("get_thing"); let ret_data = [Felt::from(123)]; cheatnet_state.start_mock_call(contract_address, selector_from_name("get_thing"), &ret_data); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &ret_data); let proxy_address = deploy_contract( &mut cached_state, &mut cheatnet_state, "MockCheckerProxy", &[], ); let proxy_selector = selector_from_name("get_thing_from_contract"); let output = call_contract( &mut cached_state, &mut cheatnet_state, &proxy_address, proxy_selector, &[contract_address.into_()], ); assert_success(output, &ret_data); } #[test] fn mock_call_proxy_with_other_syscall() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract( &mut cached_state, &mut cheatnet_state, "MockChecker", &[Felt::from(420)], ); let selector = selector_from_name("get_thing"); let ret_data = [Felt::from(123)]; cheatnet_state.start_mock_call(contract_address, selector_from_name("get_thing"), &ret_data); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &ret_data); let proxy_address = deploy_contract( &mut cached_state, &mut cheatnet_state, "MockCheckerProxy", &[], ); let proxy_selector = selector_from_name("get_thing_from_contract_and_emit_event"); let output = call_contract( &mut cached_state, &mut cheatnet_state, &proxy_address, proxy_selector, &[contract_address.into_()], ); assert_success(output, &ret_data); } #[test] fn mock_call_inner_call_no_effect() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract( &mut cached_state, &mut cheatnet_state, "MockChecker", &[Felt::from(420)], ); let selector = selector_from_name("get_thing"); let ret_data = [Felt::from(123)]; cheatnet_state.start_mock_call(contract_address, selector_from_name("get_thing"), &ret_data); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &ret_data); let selector = selector_from_name("get_thing_wrapper"); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &[Felt::from(420)]); } #[test] fn mock_call_library_call_no_effect() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contracts_data = get_contracts(); let class_hash = declare(&mut cached_state, "MockChecker", &contracts_data) .unwrap() .unwrap_success(); let contract_address = deploy( &mut cached_state, &mut cheatnet_state, &class_hash, &[Felt::from(420)], ); let lib_call_address = deploy_contract( &mut cached_state, &mut cheatnet_state, "MockCheckerLibCall", &[], ); let ret_data = [Felt::from(123)]; cheatnet_state.start_mock_call( contract_address, selector_from_name("get_constant_thing"), &ret_data, ); let lib_call_selector = selector_from_name("get_constant_thing_with_lib_call"); let output = call_contract( &mut cached_state, &mut cheatnet_state, &lib_call_address, lib_call_selector, &[class_hash.into_()], ); assert_success(output, &[Felt::from(13)]); } #[test] fn mock_call_before_deployment() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contracts_data = get_contracts(); let class_hash = declare(&mut cached_state, "MockChecker", &contracts_data) .unwrap() .unwrap_success(); let precalculated_address = cheatnet_state.precalculate_address(&class_hash, &[Felt::from(420)]); let selector = selector_from_name("get_thing"); let ret_data = [Felt::from(123)]; cheatnet_state.start_mock_call( precalculated_address, selector_from_name("get_thing"), &ret_data, ); let contract_address = deploy( &mut cached_state, &mut cheatnet_state, &class_hash, &[Felt::from(420)], ); assert_eq!(precalculated_address, contract_address); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &ret_data); } #[test] fn mock_call_not_implemented() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract( &mut cached_state, &mut cheatnet_state, "MockChecker", &[Felt::from(420)], ); let selector = selector_from_name("get_thing_not_implemented"); let ret_data = [Felt::from(123), Felt::from(123), Felt::from(123)]; cheatnet_state.start_mock_call( contract_address, selector_from_name("get_thing_not_implemented"), &ret_data, ); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &ret_data); } #[test] fn mock_call_in_constructor() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contracts_data = get_contracts(); let class_hash = declare(&mut cached_state, "HelloStarknet", &contracts_data) .unwrap() .unwrap_success(); let balance_contract_address = deploy(&mut cached_state, &mut cheatnet_state, &class_hash, &[]); let ret_data = [Felt::from(223)]; cheatnet_state.start_mock_call( balance_contract_address, selector_from_name("get_balance"), &ret_data, ); let class_hash = declare(&mut cached_state, "ConstructorMockChecker", &contracts_data) .unwrap() .unwrap_success(); let contract_address = deploy( &mut cached_state, &mut cheatnet_state, &class_hash, &[balance_contract_address.into_()], ); let selector = selector_from_name("get_constructor_balance"); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); let output_data = recover_data(output); assert_eq!(output_data.len(), 1); assert_eq!(output_data.first().unwrap().clone(), Felt::from(223)); } #[test] fn mock_call_two_methods() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract( &mut cached_state, &mut cheatnet_state, "MockChecker", &[Felt::from(420)], ); let selector1 = selector_from_name("get_thing"); let selector2 = selector_from_name("get_constant_thing"); let ret_data = [Felt::from(123)]; cheatnet_state.start_mock_call(contract_address, selector_from_name("get_thing"), &ret_data); cheatnet_state.start_mock_call( contract_address, selector_from_name("get_constant_thing"), &ret_data, ); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector1, &[], ); assert_success(output, &ret_data); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector2, &[], ); assert_success(output, &ret_data); } #[test] fn mock_call_nonexisting_contract() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let selector = selector_from_name("get_thing"); let ret_data = [Felt::from(123)]; let contract_address = ContractAddress::from(218_u8); cheatnet_state.start_mock_call(contract_address, selector_from_name("get_thing"), &ret_data); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &ret_data); } #[test] fn mock_call_simple_with_span() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("MockChecker", &[Felt::from(420)]); test_env.mock_call( &contract_address, "get_thing", &[123], CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); assert_success( test_env.call_contract(&contract_address, "get_thing", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_thing", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract(&contract_address, "get_thing", &[]), &[Felt::from(420)], ); } #[test] fn mock_call_proxy_with_span() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("MockChecker", &[Felt::from(420)]); let proxy_address = test_env.deploy("MockCheckerProxy", &[]); test_env.mock_call( &contract_address, "get_thing", &[123], CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); assert_success( test_env.call_contract(&contract_address, "get_thing", &[]), &[Felt::from(123)], ); assert_success( test_env.call_contract( &proxy_address, "get_thing_from_contract", &[contract_address.into_()], ), &[Felt::from(123)], ); assert_success( test_env.call_contract( &proxy_address, "get_thing_from_contract", &[contract_address.into_()], ), &[Felt::from(420)], ); } #[test] fn mock_call_in_constructor_with_span() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let balance_address = test_env.deploy("HelloStarknet", &[]); let class_hash = test_env.declare("ConstructorMockChecker", &contracts_data); let precalculated_address = test_env .cheatnet_state .precalculate_address(&class_hash, &[balance_address.into_()]); test_env.mock_call( &balance_address, "get_balance", &[111], CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); let contract_address = test_env.deploy_wrapper(&class_hash, &[balance_address.into_()]); assert_eq!(precalculated_address, contract_address); assert_success( test_env.call_contract(&contract_address, "get_constructor_balance", &[]), &[Felt::from(111)], ); assert_success( test_env.call_contract(&balance_address, "get_balance", &[]), &[Felt::from(111)], ); assert_success( test_env.call_contract(&balance_address, "get_balance", &[]), &[Felt::from(0)], ); } #[test] fn mock_call_twice_in_function() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("MockChecker", &contracts_data); let precalculated_address = test_env .cheatnet_state .precalculate_address(&class_hash, &[111.into()]); test_env.mock_call( &precalculated_address, "get_thing", &[222], CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); let contract_address = test_env.deploy_wrapper(&class_hash, &[111.into()]); assert_eq!(precalculated_address, contract_address); assert_success( test_env.call_contract(&contract_address, "get_thing", &[]), &[222.into()], ); assert_success( test_env.call_contract(&contract_address, "get_thing_twice", &[]), &[222.into(), 111.into()], ); assert_success( test_env.call_contract(&contract_address, "get_thing", &[]), &[111.into()], ); } #[test] fn mock_call_override_span() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("MockChecker", &[111.into()]); test_env.mock_call( &contract_address, "get_thing", &[222], CheatSpan::TargetCalls(NonZeroUsize::new(2).unwrap()), ); assert_success( test_env.call_contract(&contract_address, "get_thing", &[]), &[Felt::from(222)], ); test_env.mock_call( &contract_address, "get_thing", &[333], CheatSpan::Indefinite, ); assert_success( test_env.call_contract(&contract_address, "get_thing", &[]), &[Felt::from(333)], ); assert_success( test_env.call_contract(&contract_address, "get_thing", &[]), &[Felt::from(333)], ); test_env.stop_mock_call(&contract_address, "get_thing"); assert_success( test_env.call_contract(&contract_address, "get_thing", &[]), &[111.into()], ); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/mod.rs ================================================ mod test_environment; mod cheat_account_contract_address; mod cheat_block_hash; mod cheat_block_number; mod cheat_block_timestamp; mod cheat_caller_address; mod cheat_execution_info; mod cheat_sequencer_address; mod declare; mod generate_random_felt; mod get_class_hash; mod library_call; mod load; mod meta_tx_v0; mod mock_call; mod multiple_writes_same_storage; mod precalculate_address; mod replace_bytecode; mod spy_events; mod store; ================================================ FILE: crates/cheatnet/tests/cheatcodes/multiple_writes_same_storage.rs ================================================ use crate::common::assertions::assert_success; use crate::common::get_contracts; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::storage::{ load, store, variable_address, }; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; use super::test_environment::TestEnvironment; trait StoreTrait { fn store(&mut self, target: ContractAddress, storage_address: Felt, value: u128); fn load(&mut self, target: ContractAddress, storage_address: Felt) -> Felt; } impl StoreTrait for TestEnvironment { fn store(&mut self, target: ContractAddress, storage_address: Felt, value: u128) { store( &mut self.cached_state, target, storage_address, Felt::from(value), ) .unwrap(); } fn load(&mut self, target: ContractAddress, storage_address: Felt) -> Felt { load(&mut self.cached_state, target, storage_address).unwrap() } } #[test] fn same_storage_access_call_contract() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let hello_class_hash = test_env.declare("HelloStarknet", &contracts_data); let contract_address_a = test_env.deploy_wrapper(&hello_class_hash, &[]); let contract_address_b = test_env.deploy_wrapper(&hello_class_hash, &[]); assert_ne!(contract_address_b, contract_address_a); test_env .call_contract(&contract_address_a, "increase_balance", &[Felt::from(420)]) .unwrap(); let balance_value_a = test_env.call_contract(&contract_address_a, "get_balance", &[]); assert_success(balance_value_a, &[Felt::from(420)]); let balance_value_b = test_env.call_contract(&contract_address_b, "get_balance", &[]); assert_success(balance_value_b, &[Felt::from(0)]); test_env .call_contract(&contract_address_b, "increase_balance", &[Felt::from(42)]) .unwrap(); let balance_value_b = test_env.call_contract(&contract_address_b, "get_balance", &[]); assert_success(balance_value_b, &[Felt::from(42)]); let balance_value_a = test_env.call_contract(&contract_address_a, "get_balance", &[]); assert_success(balance_value_a, &[Felt::from(420)]); } #[test] fn same_storage_access_store() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let hello_class_hash = test_env.declare("HelloStarknet", &contracts_data); let contract_address_a = test_env.deploy_wrapper(&hello_class_hash, &[]); let contract_address_b = test_env.deploy_wrapper(&hello_class_hash, &[]); assert_ne!(contract_address_b, contract_address_a); test_env.store(contract_address_a, variable_address("balance"), 450); let balance_value_a = test_env.load(contract_address_a, variable_address("balance")); assert_eq!(balance_value_a, Felt::from(450)); let balance_value_b = test_env.load(contract_address_b, variable_address("balance")); assert_eq!(balance_value_b, Felt::from(0)); test_env.store(contract_address_b, variable_address("balance"), 42); let balance_value_b = test_env.load(contract_address_b, variable_address("balance")); assert_eq!(balance_value_b, Felt::from(42)); let balance_value_a = test_env.load(contract_address_a, variable_address("balance")); assert_eq!(balance_value_a, Felt::from(450)); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/precalculate_address.rs ================================================ use crate::{cheatcodes::test_environment::TestEnvironment, common::get_contracts}; #[test] fn precalculate_address_simple() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("HelloStarknet", &contracts_data); let precalculated1 = test_env.precalculate_address(&class_hash, &[]); let actual1 = test_env.deploy_wrapper(&class_hash, &[]); let precalculated2 = test_env.precalculate_address(&class_hash, &[]); let actual2 = test_env.deploy_wrapper(&class_hash, &[]); assert_eq!(precalculated1, actual1); assert_eq!(precalculated2, actual2); assert_ne!(actual1, actual2); } #[test] fn precalculate_address_calldata() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("ConstructorSimple", &contracts_data); let precalculated1 = test_env.precalculate_address(&class_hash, &[111]); let precalculated2 = test_env.precalculate_address(&class_hash, &[222]); let actual1 = test_env.deploy_wrapper(&class_hash, &[111.into()]); let precalculated2_post_deploy = test_env.precalculate_address(&class_hash, &[222]); let actual2 = test_env.deploy_wrapper(&class_hash, &[222.into()]); assert_eq!(precalculated1, actual1); assert_ne!(precalculated1, precalculated2); assert_ne!(precalculated2, precalculated2_post_deploy); assert_eq!(precalculated2_post_deploy, actual2); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/replace_bytecode.rs ================================================ use crate::{ cheatcodes::test_environment::TestEnvironment, common::{assertions::assert_success, get_contracts, state::create_fork_cached_state_at}, }; use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::rpc::CallSuccess; use conversions::string::TryFromHexStr; use num_traits::Zero; use starknet_api::core::{ClassHash, ContractAddress}; use starknet_types_core::felt::Felt; use tempfile::TempDir; trait ReplaceBytecodeTrait { fn replace_class_for_contract( &mut self, contract_address: ContractAddress, class_hash: ClassHash, ); } impl ReplaceBytecodeTrait for TestEnvironment { fn replace_class_for_contract( &mut self, contract_address: ContractAddress, class_hash: ClassHash, ) { self.cheatnet_state .replace_class_for_contract(contract_address, class_hash); } } #[test] fn fork() { let cache_dir = TempDir::new().unwrap(); let mut test_env = TestEnvironment::new(); test_env.cached_state = create_fork_cached_state_at(53_300, cache_dir.path().to_str().unwrap()); let contracts_data = get_contracts(); let class_hash = test_env.declare("ReplaceInFork", &contracts_data); let contract = TryFromHexStr::try_from_hex_str( "0x06fdb5ef99e9def44484a3f8540bc42333e321e9b24a397d6bc0c8860bb7da8f", ) .unwrap(); let output = test_env.call_contract(&contract, "get_owner", &[]); assert!(matches!( output, Ok(CallSuccess { ret_data, .. }) if ret_data != [Felt::zero()], )); test_env.replace_class_for_contract(contract, class_hash); let output = test_env.call_contract(&contract, "get_owner", &[]); assert_success(output, &[Felt::zero()]); } #[test] fn override_entrypoint() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash_a = test_env.declare("ReplaceBytecodeA", &contracts_data); let class_hash_b = test_env.declare("ReplaceBytecodeB", &contracts_data); let contract_address = test_env.deploy_wrapper(&class_hash_a, &[]); let output = test_env.call_contract(&contract_address, "get_const", &[]); assert_success(output, &[Felt::from(2137)]); test_env.replace_class_for_contract(contract_address, class_hash_b); let output = test_env.call_contract(&contract_address, "get_const", &[]); assert_success(output, &[Felt::from(420)]); } #[test] fn keep_storage() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash_a = test_env.declare("ReplaceBytecodeA", &contracts_data); let class_hash_b = test_env.declare("ReplaceBytecodeB", &contracts_data); let contract_address = test_env.deploy_wrapper(&class_hash_a, &[]); let output = test_env.call_contract(&contract_address, "set", &[456.into()]); assert_success(output, &[]); let output = test_env.call_contract(&contract_address, "get", &[]); assert_success(output, &[Felt::from(456)]); test_env.replace_class_for_contract(contract_address, class_hash_b); let output = test_env.call_contract(&contract_address, "get", &[]); assert_success(output, &[Felt::from(556)]); } #[test] fn allow_setting_original_class() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash_a = test_env.declare("ReplaceBytecodeA", &contracts_data); let class_hash_b = test_env.declare("ReplaceBytecodeB", &contracts_data); let contract_address = test_env.deploy_wrapper(&class_hash_a, &[]); test_env.replace_class_for_contract(contract_address, class_hash_b); test_env.replace_class_for_contract(contract_address, class_hash_a); let output = test_env.call_contract(&contract_address, "get_const", &[]); assert_success(output, &[Felt::from(2137)]); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/spy_events.rs ================================================ use crate::cheatcodes::test_environment::TestEnvironment; use crate::common::get_contracts; use crate::common::state::create_fork_cached_state_at; use crate::common::{call_contract, deploy_contract}; use cairo_lang_starknet_classes::keccak::starknet_keccak; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::spy_events::Event; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::storage::selector_from_name; use cheatnet::state::CheatnetState; use conversions::IntoConv; use conversions::string::TryFromHexStr; use starknet_types_core::felt::Felt; use std::vec; use tempfile::TempDir; trait SpyTrait { fn get_events(&mut self, id: usize) -> Vec; } impl SpyTrait for TestEnvironment { fn get_events(&mut self, events_offset: usize) -> Vec { self.cheatnet_state.get_events(events_offset) } } #[test] fn spy_events_zero_offset() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("SpyEventsChecker", &[]); test_env .call_contract(&contract_address, "emit_one_event", &[Felt::from(123)]) .unwrap(); let events = test_env.get_events(0); assert_eq!(events.len(), 1, "There should be one event"); assert_eq!( events[0], Event { from: contract_address, keys: vec![starknet_keccak("FirstEvent".as_ref()).into()], data: vec![Felt::from(123)] }, "Wrong event" ); test_env .call_contract(&contract_address, "emit_one_event", &[Felt::from(123)]) .unwrap(); let length = test_env.get_events(0).len(); assert_eq!(length, 2, "There should be one more new event"); } #[test] fn spy_events_some_offset() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("SpyEventsChecker", &[]); test_env .call_contract(&contract_address, "emit_one_event", &[Felt::from(123)]) .unwrap(); test_env .call_contract(&contract_address, "emit_one_event", &[Felt::from(123)]) .unwrap(); test_env .call_contract(&contract_address, "emit_one_event", &[Felt::from(123)]) .unwrap(); let events = test_env.get_events(2); assert_eq!( events.len(), 1, "There should be only one event fetched after accounting for an offset of 2" ); assert_eq!( events[0], Event { from: contract_address, keys: vec![starknet_keccak("FirstEvent".as_ref()).into()], data: vec![Felt::from(123)] }, "Wrong event" ); test_env .call_contract(&contract_address, "emit_one_event", &[Felt::from(123)]) .unwrap(); let length = test_env.get_events(2).len(); assert_eq!(length, 2, "There should be one more new event"); } #[test] fn check_events_order() { let mut test_env = TestEnvironment::new(); let spy_events_checker_address = test_env.deploy("SpyEventsChecker", &[]); let spy_events_order_checker_address = test_env.deploy("SpyEventsOrderChecker", &[]); test_env .call_contract( &spy_events_order_checker_address, "emit_and_call_another", &[ Felt::from(123), Felt::from(234), Felt::from(345), spy_events_checker_address.into_(), ], ) .unwrap(); let events = test_env.get_events(0); assert_eq!(events.len(), 3, "There should be three events"); assert_eq!( events[0], Event { from: spy_events_order_checker_address, keys: vec![starknet_keccak("SecondEvent".as_ref()).into()], data: vec![Felt::from(123)] }, "Wrong first event" ); assert_eq!( events[1], Event { from: spy_events_checker_address, keys: vec![starknet_keccak("FirstEvent".as_ref()).into()], data: vec![Felt::from(234)] }, "Wrong second event" ); assert_eq!( events[2], Event { from: spy_events_order_checker_address, keys: vec![starknet_keccak("ThirdEvent".as_ref()).into()], data: vec![Felt::from(345)] }, "Wrong third event" ); } #[test] fn library_call_emits_event() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("SpyEventsChecker", &contracts_data); let contract_address = test_env.deploy("SpyEventsLibCall", &[]); test_env .call_contract( &contract_address, "call_lib_call", &[Felt::from(123), class_hash.into_()], ) .unwrap(); let events = test_env.get_events(0); assert_eq!(events.len(), 1, "There should be one event"); assert_eq!( events[0], Event { from: contract_address, keys: vec![starknet_keccak("FirstEvent".as_ref()).into()], data: vec![Felt::from(123)] }, "Wrong event" ); } #[test] fn event_emitted_in_constructor() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("ConstructorSpyEventsChecker", &[Felt::from(123)]); let events = test_env.get_events(0); assert_eq!(events.len(), 1, "There should be one event"); assert_eq!( events[0], Event { from: contract_address, keys: vec![starknet_keccak("FirstEvent".as_ref()).into()], data: vec![Felt::from(123)] }, "Wrong event" ); } #[test] fn test_nested_calls() { let mut test_env = TestEnvironment::new(); let spy_events_checker_address = test_env.deploy("SpyEventsChecker", &[]); let contracts_data = get_contracts(); let class_hash = test_env.declare("SpyEventsCheckerProxy", &contracts_data); let spy_events_checker_proxy_address = test_env.deploy_wrapper(&class_hash, &[spy_events_checker_address.into_()]); let spy_events_checker_top_proxy_address = test_env.deploy_wrapper(&class_hash, &[spy_events_checker_proxy_address.into_()]); test_env .call_contract( &spy_events_checker_top_proxy_address, "emit_one_event", &[Felt::from(123)], ) .unwrap(); let events = test_env.get_events(0); assert_eq!(events.len(), 3, "There should be three events"); assert_eq!( events[0], Event { from: spy_events_checker_top_proxy_address, keys: vec![starknet_keccak("FirstEvent".as_ref()).into()], data: vec![Felt::from(123)] }, "Wrong first event" ); assert_eq!( events[1], Event { from: spy_events_checker_proxy_address, keys: vec![starknet_keccak("FirstEvent".as_ref()).into()], data: vec![Felt::from(123)] }, "Wrong second event" ); assert_eq!( events[2], Event { from: spy_events_checker_address, keys: vec![starknet_keccak("FirstEvent".as_ref()).into()], data: vec![Felt::from(123)] }, "Wrong third event" ); } #[test] fn use_multiple_spies() { let mut test_env = TestEnvironment::new(); let spy_events_checker_address = test_env.deploy("SpyEventsChecker", &[]); let contracts_data = get_contracts(); let class_hash = test_env.declare("SpyEventsCheckerProxy", &contracts_data); let spy_events_checker_proxy_address = test_env.deploy_wrapper(&class_hash, &[spy_events_checker_address.into_()]); let spy_events_checker_top_proxy_address = test_env.deploy_wrapper(&class_hash, &[spy_events_checker_proxy_address.into_()]); // Events emitted in the order of: // - spy_events_checker_top_proxy_address, // - spy_events_checker_proxy_address, // - spy_events_checker_address test_env .call_contract( &spy_events_checker_top_proxy_address, "emit_one_event", &[Felt::from(123)], ) .unwrap(); let events1 = test_env.get_events(0); let events2 = test_env.get_events(1); let events3 = test_env.get_events(2); assert_eq!(events1.len(), 3, "There should be one event"); assert_eq!(events2.len(), 2, "There should be one event"); assert_eq!(events3.len(), 1, "There should be one event"); assert_eq!( events1[0], Event { from: spy_events_checker_top_proxy_address, keys: vec![starknet_keccak("FirstEvent".as_ref()).into()], data: vec![Felt::from(123)] }, "Wrong spy_events_checker_top_proxy event" ); assert_eq!( events2[0], Event { from: spy_events_checker_proxy_address, keys: vec![starknet_keccak("FirstEvent".as_ref()).into()], data: vec![Felt::from(123)] }, "Wrong spy_events_checker_proxy event" ); assert_eq!( events3[0], Event { from: spy_events_checker_address, keys: vec![starknet_keccak("FirstEvent".as_ref()).into()], data: vec![Felt::from(123)] }, "Wrong spy_events_checker event" ); } #[test] fn test_emitted_by_emit_events_syscall() { let mut test_env = TestEnvironment::new(); let contract_address = test_env.deploy("SpyEventsChecker", &[]); test_env .call_contract( &contract_address, "emit_event_syscall", &[Felt::from(123), Felt::from(456)], ) .unwrap(); let events = test_env.get_events(0); assert_eq!(events.len(), 1, "There should be one event"); assert_eq!( events[0], Event { from: contract_address, keys: vec![Felt::from(123)], data: vec![Felt::from(456)] }, "Wrong spy_events_checker event" ); } #[test] fn capture_cairo0_event() { let temp_dir = TempDir::new().unwrap(); let mut cached_state = create_fork_cached_state_at(53_626, temp_dir.path().to_str().unwrap()); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract( &mut cached_state, &mut cheatnet_state, "SpyEventsCairo0", &[], ); let selector = selector_from_name("test_cairo0_event_collection"); let cairo0_contract_address = Felt::try_from_hex_str("0x2c77ca97586968c6651a533bd5f58042c368b14cf5f526d2f42f670012e10ac") .unwrap(); call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[cairo0_contract_address], ) .unwrap(); let events = cheatnet_state.get_events(0); assert_eq!(events.len(), 1, "There should be one event"); assert_eq!( events[0], Event { from: cairo0_contract_address.into_(), keys: vec![starknet_keccak("my_event".as_ref()).into()], data: vec![Felt::from(123_456_789)] }, "Wrong spy_events_checker event" ); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/store.rs ================================================ use crate::common::assertions::assert_success; use crate::common::get_contracts; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::storage::{ map_entry_address, store, variable_address, }; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; use super::test_environment::TestEnvironment; trait StoreTrait { fn store(&mut self, target: ContractAddress, storage_address: Felt, value: u128); } impl StoreTrait for TestEnvironment { fn store(&mut self, target: ContractAddress, storage_address: Felt, value: u128) { store( &mut self.cached_state, target, storage_address, Felt::from(value), ) .unwrap(); } } #[test] fn store_simple_state() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("HelloStarknet", &contracts_data); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); test_env.store(contract_address, variable_address("balance"), 666); assert_success( test_env.call_contract(&contract_address, "get_balance", &[]), &[Felt::from(666)], ); } #[test] fn store_state_map_simple_value() { let mut test_env = TestEnvironment::new(); let contracts_data = get_contracts(); let class_hash = test_env.declare("MapSimpleValueSimpleKey", &contracts_data); let contract_address = test_env.deploy_wrapper(&class_hash, &[]); let map_key = Felt::from(420); let inserted_value = 69; let entry_address = map_entry_address("values", &[map_key]); test_env.store(contract_address, entry_address, inserted_value); assert_success( test_env.call_contract(&contract_address, "read", &[map_key]), &[inserted_value.into()], ); } ================================================ FILE: crates/cheatnet/tests/cheatcodes/test_environment.rs ================================================ use crate::common::assertions::ClassHashAssert; use crate::common::{call_contract, deploy, library_call_contract}; use crate::common::{deploy_contract, state::create_cached_state}; use blockifier::state::cached_state::CachedState; use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::rpc::CallResult; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::declare::declare; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::storage::selector_from_name; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; use cheatnet::state::{CheatnetState, ExtendedStateReader}; use starknet_api::core::ClassHash; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; pub struct TestEnvironment { pub cached_state: CachedState, pub cheatnet_state: CheatnetState, } impl TestEnvironment { pub fn new() -> Self { let cached_state = create_cached_state(); Self { cached_state, cheatnet_state: CheatnetState::default(), } } pub fn declare(&mut self, contract_name: &str, contracts_data: &ContractsData) -> ClassHash { declare(&mut self.cached_state, contract_name, contracts_data) .unwrap() .unwrap_success() } pub fn deploy(&mut self, contract_name: &str, calldata: &[Felt]) -> ContractAddress { deploy_contract( &mut self.cached_state, &mut self.cheatnet_state, contract_name, calldata, ) } pub fn deploy_wrapper(&mut self, class_hash: &ClassHash, calldata: &[Felt]) -> ContractAddress { deploy( &mut self.cached_state, &mut self.cheatnet_state, class_hash, calldata, ) } pub fn call_contract( &mut self, contract_address: &ContractAddress, selector: &str, calldata: &[Felt], ) -> CallResult { call_contract( &mut self.cached_state, &mut self.cheatnet_state, contract_address, selector_from_name(selector), calldata, ) } pub fn library_call_contract( &mut self, class_hash: &ClassHash, selector: &str, calldata: &[Felt], ) -> CallResult { library_call_contract( &mut self.cached_state, &mut self.cheatnet_state, class_hash, selector_from_name(selector), calldata, ) } pub fn precalculate_address( &mut self, class_hash: &ClassHash, calldata: &[u128], ) -> ContractAddress { let calldata = calldata.iter().map(|x| Felt::from(*x)).collect::>(); self.cheatnet_state .precalculate_address(class_hash, &calldata) } } ================================================ FILE: crates/cheatnet/tests/common/assertions.rs ================================================ use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::rpc::{ CallFailure, CallResult, CallSuccess, }; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::declare::DeclareResult; use conversions::byte_array::ByteArray; use starknet_api::core::ClassHash; use starknet_types_core::felt::Felt; #[inline] pub fn assert_success(call_contract_output: CallResult, expected_data: &[Felt]) { assert!(matches!( call_contract_output, Ok(CallSuccess { ret_data, .. }) if ret_data == expected_data, )); } #[inline] pub fn assert_panic(call_contract_output: CallResult, expected_data: &[Felt]) { assert!(matches!( call_contract_output, Err( CallFailure::Recoverable { panic_data, .. } ) if panic_data == expected_data )); } #[inline] pub fn assert_error(call_contract_output: CallResult, expected_data: impl Into) { assert!(matches!( call_contract_output, Err( CallFailure::Unrecoverable { msg, .. } ) if msg == expected_data.into(), )); } pub trait ClassHashAssert { fn unwrap_success(self) -> ClassHash; } impl ClassHashAssert for DeclareResult { fn unwrap_success(self) -> ClassHash { match self { DeclareResult::Success(class_hash) => class_hash, DeclareResult::AlreadyDeclared(class_hash) => { panic!("Class hash {class_hash} is already declared") } } } } ================================================ FILE: crates/cheatnet/tests/common/cache.rs ================================================ use glob::glob; use serde_json::{Map, Value}; use std::fs; use std::path::PathBuf; use std::str::FromStr; pub fn read_cache(file_pattern: &str) -> Map { let mut cache_files = glob(file_pattern).unwrap().filter_map(Result::ok); let cache_file = match (cache_files.next(), cache_files.next()) { (None, None) => panic!("Cache file not found"), (Some(cache_file), None) => cache_file, _ => panic!("Multiple matching cache files found"), }; let cache_content = fs::read_to_string(cache_file).expect("Could not read cache"); let parsed_cache_content: Value = serde_json::from_str(&cache_content).expect("Could not parse cache"); parsed_cache_content .as_object() .expect("Parsed cache is not an object") .clone() } pub fn purge_cache(directory: &str) { fs::remove_dir_all(PathBuf::from_str(directory).expect("Could not parse cache path")) .expect("Could not remove cache directory"); } ================================================ FILE: crates/cheatnet/tests/common/mod.rs ================================================ use assertions::ClassHashAssert; use blockifier::execution::call_info::CallInfo; use blockifier::execution::contract_class::TrackedResource; use blockifier::execution::entry_point::{ CallEntryPoint, CallType, ConstructorContext, EntryPointExecutionContext, EntryPointExecutionResult, }; use blockifier::execution::execution_utils::ReadOnlySegments; use blockifier::execution::syscalls::hint_processor::SyscallHintProcessor; use blockifier::state::state_api::State; use cairo_lang_casm::hints::Hint; use cairo_vm::types::relocatable::Relocatable; use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::execution::cheated_syscalls; use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::{ ExecuteCallEntryPointExtraOptions, execute_call_entry_point, }; use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::rpc::{ AddressOrClassHash, CallSuccess, call_entry_point, }; use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::rpc::{ CallFailure, CallResult, }; use cheatnet::runtime_extensions::common::create_execute_calldata; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::declare::declare; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; use cheatnet::state::CheatnetState; use conversions::IntoConv; use conversions::string::TryFromHexStr; use foundry_ui::UI; use runtime::starknet::constants::TEST_ADDRESS; use runtime::starknet::context::build_context; use scarb_api::metadata::metadata_for_dir; use scarb_api::{ CompilationOpts, get_contracts_artifacts_and_source_sierra_paths, target_dir_for_workspace, }; use starknet_api::contract_class::EntryPointType; use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector}; use starknet_api::transaction::fields::Calldata; use starknet_rust::core::utils::get_selector_from_name; use starknet_types_core::felt::Felt; use std::collections::HashMap; use std::sync::Arc; pub mod assertions; pub mod cache; pub mod state; // Helper struct to return both: our custom call result wrapper and actual call info (unless unrecoverable error), allowing tests to check both pub struct CallResultExtended { pub call_result: CallResult, pub call_info: Option, } fn build_syscall_hint_processor<'a>( call_entry_point: &CallEntryPoint, state: &'a mut dyn State, entry_point_execution_context: &'a mut EntryPointExecutionContext, hints: &'a HashMap, ) -> SyscallHintProcessor<'a> { let class_hash = call_entry_point.class_hash.unwrap_or_default(); let call_entry_point = call_entry_point.clone().into_executable(class_hash); SyscallHintProcessor::new( state, entry_point_execution_context, Relocatable { segment_index: 0, offset: 0, }, call_entry_point, hints, ReadOnlySegments::default(), ) } pub fn recover_data(output: CallResult) -> Vec { match output { Ok(CallSuccess { ret_data, .. }) => ret_data, Err(failure_type) => match failure_type { CallFailure::Recoverable { panic_data, .. } => panic_data, CallFailure::Unrecoverable { msg, .. } => panic!("Call failed with message: {msg}"), }, } } pub fn get_contracts() -> ContractsData { let scarb_metadata = metadata_for_dir("tests/contracts").unwrap(); let target_dir = target_dir_for_workspace(&scarb_metadata).join("dev"); let package = scarb_metadata.packages.first().unwrap(); let ui = UI::default(); let contracts = get_contracts_artifacts_and_source_sierra_paths( &target_dir, package, &ui, CompilationOpts { use_test_target_contracts: false, #[cfg(feature = "cairo-native")] run_native: true, }, ) .unwrap(); ContractsData::try_from(contracts).unwrap() } pub fn deploy_contract( state: &mut dyn State, cheatnet_state: &mut CheatnetState, contract_name: &str, calldata: &[Felt], ) -> ContractAddress { let contracts_data = get_contracts(); let class_hash = declare(state, contract_name, &contracts_data) .unwrap() .unwrap_success(); let mut entry_point_execution_context = build_context( &cheatnet_state.block_info, None, &TrackedResource::SierraGas, ); let hints = HashMap::new(); let mut syscall_hint_processor = build_syscall_hint_processor( &CallEntryPoint::default(), state, &mut entry_point_execution_context, &hints, ); let (contract_address, _) = deploy_helper( &mut syscall_hint_processor, cheatnet_state, &class_hash, None, calldata, ); contract_address } pub fn deploy( state: &mut dyn State, cheatnet_state: &mut CheatnetState, class_hash: &ClassHash, calldata: &[Felt], ) -> ContractAddress { let mut entry_point_execution_context = build_context( &cheatnet_state.block_info, None, &TrackedResource::SierraGas, ); let hints = HashMap::new(); let mut syscall_hint_processor = build_syscall_hint_processor( &CallEntryPoint::default(), state, &mut entry_point_execution_context, &hints, ); let (contract_address, _) = deploy_helper( &mut syscall_hint_processor, cheatnet_state, class_hash, None, calldata, ); contract_address } fn deploy_helper( syscall_handler: &mut SyscallHintProcessor, cheatnet_state: &mut CheatnetState, class_hash: &ClassHash, contract_address: Option, calldata: &[Felt], ) -> (ContractAddress, Vec) { let contract_address = contract_address .unwrap_or_else(|| cheatnet_state.precalculate_address(class_hash, calldata)); let calldata = Calldata(Arc::new(calldata.to_vec())); let ctor_context = ConstructorContext { class_hash: *class_hash, code_address: Some(contract_address), storage_address: contract_address, caller_address: TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap(), }; let call_info = cheated_syscalls::execute_deployment( syscall_handler.base.state, cheatnet_state, syscall_handler.base.context, &ctor_context, calldata, &mut (i64::MAX as u64), ) .unwrap(); cheatnet_state.increment_deploy_salt_base(); let retdata = call_info.execution.retdata.0.clone(); syscall_handler.base.inner_calls.push(call_info); (contract_address, retdata) } // This does contract call without the transaction layer. This way `call_contract` can return data and modify state. // `call` and `invoke` on the transactional layer use such method under the hood. pub fn call_contract( state: &mut dyn State, cheatnet_state: &mut CheatnetState, contract_address: &ContractAddress, entry_point_selector: EntryPointSelector, calldata: &[Felt], ) -> CallResult { let calldata = create_execute_calldata(calldata); let entry_point = CallEntryPoint { class_hash: None, code_address: Some(*contract_address), entry_point_type: EntryPointType::External, entry_point_selector, calldata, storage_address: *contract_address, caller_address: TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap(), call_type: CallType::Call, initial_gas: i64::MAX as u64, }; call_entry_point_extended_result( state, cheatnet_state, entry_point, &AddressOrClassHash::ContractAddress(*contract_address), ) .call_result } // This executes a library call as from a test contract. // `entry_point` below should match `library_call_syscall` entry point, except for `selector` and `calldata`: // https://github.com/foundry-rs/starknet-foundry/blob/421a339168a9e0b6502eac4fdc4fdeb0598c72b7/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/mod.rs#L151 pub fn library_call_contract( state: &mut dyn State, cheatnet_state: &mut CheatnetState, class_hash: &ClassHash, entry_point_selector: EntryPointSelector, calldata: &[Felt], ) -> CallResult { let calldata = create_execute_calldata(calldata); let entry_point = CallEntryPoint { class_hash: Some(*class_hash), code_address: None, entry_point_type: EntryPointType::External, entry_point_selector, calldata, storage_address: TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap(), caller_address: ContractAddress::default(), call_type: CallType::Delegate, initial_gas: i64::MAX as u64, }; call_entry_point_extended_result( state, cheatnet_state, entry_point, &AddressOrClassHash::ClassHash(*class_hash), ) .call_result } pub fn call_contract_extended_result( state: &mut dyn State, cheatnet_state: &mut CheatnetState, contract_address: &ContractAddress, entry_point_selector: EntryPointSelector, calldata: &[Felt], ) -> CallResultExtended { let calldata = create_execute_calldata(calldata); let entry_point = CallEntryPoint { class_hash: None, code_address: Some(*contract_address), entry_point_type: EntryPointType::External, entry_point_selector, calldata, storage_address: *contract_address, caller_address: TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap(), call_type: CallType::Call, initial_gas: i64::MAX as u64, }; call_entry_point_extended_result( state, cheatnet_state, entry_point, &AddressOrClassHash::ContractAddress(*contract_address), ) } fn call_entry_point_extended_result( state: &mut dyn State, cheatnet_state: &mut CheatnetState, entry_point: CallEntryPoint, address_or_class_hash: &AddressOrClassHash, ) -> CallResultExtended { let mut entry_point_execution_context = build_context( &cheatnet_state.block_info, None, &TrackedResource::SierraGas, ); let hints = HashMap::new(); let mut syscall_hint_processor = build_syscall_hint_processor( &entry_point, state, &mut entry_point_execution_context, &hints, ); let call_result = call_entry_point( &mut syscall_hint_processor, cheatnet_state, entry_point, address_or_class_hash, &mut (i64::MAX as u64), ); let call_info = syscall_hint_processor.base.inner_calls.first().cloned(); CallResultExtended { call_result, call_info, } } // Calls an entry point via `execute_call_entry_point` directly, bypassing the `call_entry_point` wrapper. // Unlike `call_entry_point`, this layer does **not** revert state mutations on failure. // Therefore, events, messages, and storage changes persist even when the call fails. pub fn execute_entry_point_without_revert( state: &mut dyn State, cheatnet_state: &mut CheatnetState, contract_address: &ContractAddress, entry_point_selector: EntryPointSelector, calldata: &[Felt], tracked_resource: TrackedResource, ) -> EntryPointExecutionResult { let calldata = create_execute_calldata(calldata); let mut entry_point = CallEntryPoint { class_hash: None, code_address: Some(*contract_address), entry_point_type: EntryPointType::External, entry_point_selector, calldata, storage_address: *contract_address, caller_address: TryFromHexStr::try_from_hex_str(TEST_ADDRESS).unwrap(), call_type: CallType::Call, initial_gas: i64::MAX as u64, }; let mut entry_point_execution_context = build_context(&cheatnet_state.block_info, None, &tracked_resource); let hints = HashMap::new(); let syscall_hint_processor = build_syscall_hint_processor( &entry_point, state, &mut entry_point_execution_context, &hints, ); execute_call_entry_point( &mut entry_point, syscall_hint_processor.base.state, cheatnet_state, syscall_hint_processor.base.context, &mut (i64::MAX as u64), &ExecuteCallEntryPointExtraOptions::default(), ) } #[must_use] pub fn selector_from_name(name: &str) -> EntryPointSelector { let selector = get_selector_from_name(name).unwrap(); selector.into_() } ================================================ FILE: crates/cheatnet/tests/common/state.rs ================================================ use blockifier::state::cached_state::CachedState; use cheatnet::constants::build_testing_state; use cheatnet::forking::state::ForkStateReader; use cheatnet::state::ExtendedStateReader; use shared::test_utils::node_url::node_rpc_url; use starknet_api::block::BlockNumber; pub fn create_cached_state() -> CachedState { CachedState::new(ExtendedStateReader { dict_state_reader: build_testing_state(), fork_state_reader: None, }) } pub fn create_fork_cached_state(cache_dir: &str) -> CachedState { create_fork_cached_state_at(54_060, cache_dir) } pub fn create_fork_cached_state_at( block_number: u64, cache_dir: &str, ) -> CachedState { let node_url = node_rpc_url(); CachedState::new(ExtendedStateReader { dict_state_reader: build_testing_state(), fork_state_reader: Some( ForkStateReader::new(node_url, BlockNumber(block_number), cache_dir.into()).unwrap(), ), }) } ================================================ FILE: crates/cheatnet/tests/contracts/Scarb.toml ================================================ [package] name = "cheatnet_testing_contracts" version = "0.1.0" # TODO(#4091): Add edition once Scarb version in .tool-versions is at least 2.17 # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest [[target.starknet-contract]] [dependencies] starknet = "2.4.0" [tool.snforge] ================================================ FILE: crates/cheatnet/tests/contracts/src/bytearray_string_panic_call.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait IByteArrayPanickingContract { fn do_panic(self: @TContractState); fn do_panic_felts(self: @TContractState, data: Array); } #[starknet::contract] mod ByteArrayPanickingContract { use core::panics::panic_with_byte_array; #[storage] struct Storage {} #[abi(embed_v0)] impl Impl of super::IByteArrayPanickingContract { fn do_panic(self: @ContractState) { let data = "This is a very long\n and multi line string, that will for sure saturate the pending_word"; panic_with_byte_array(@data); } fn do_panic_felts(self: @ContractState, data: Array) { panic(data); } } } #[starknet::interface] trait IByteArrayPanickingContractProxy { fn call_bytearray_panicking_contract(self: @TContractState, contract_address: ContractAddress); fn call_felts_panicking_contract( self: @TContractState, contract_address: ContractAddress, data: Array, ); } #[starknet::contract] mod ByteArrayPanickingContractProxy { use starknet::ContractAddress; use super::{IByteArrayPanickingContractDispatcher, IByteArrayPanickingContractDispatcherTrait}; #[storage] struct Storage {} #[abi(embed_v0)] impl Impl of super::IByteArrayPanickingContractProxy { fn call_bytearray_panicking_contract( self: @ContractState, contract_address: ContractAddress, ) { IByteArrayPanickingContractDispatcher { contract_address }.do_panic(); } fn call_felts_panicking_contract( self: @ContractState, contract_address: ContractAddress, data: Array, ) { IByteArrayPanickingContractDispatcher { contract_address }.do_panic_felts(data); } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_block_hash/checker.cairo ================================================ #[starknet::interface] trait ICheatBlockHashChecker { fn get_block_hash(ref self: TContractState, block_number: u64) -> felt252; } #[starknet::contract] mod CheatBlockHashChecker { use core::starknet::SyscallResultTrait; use starknet::syscalls::get_block_hash_syscall; #[storage] struct Storage {} #[abi(embed_v0)] impl CheatBlockHashChecker of super::ICheatBlockHashChecker { fn get_block_hash(ref self: ContractState, block_number: u64) -> felt252 { let block_hash = get_block_hash_syscall(block_number).unwrap_syscall(); block_hash } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_block_hash/checker_library_call.cairo ================================================ use starknet::ClassHash; #[starknet::interface] trait ICheatBlockHashChecker { fn get_block_hash(ref self: TContractState, block_number: u64) -> felt252; } #[starknet::interface] trait ICheatBlockHashCheckerLibCall { fn get_block_hash_with_lib_call( ref self: TContractState, class_hash: ClassHash, block_number: u64, ) -> felt252; fn get_block_hash(ref self: TContractState, block_number: u64) -> felt252; } #[starknet::contract] mod CheatBlockHashCheckerLibCall { use core::starknet::SyscallResultTrait; use starknet::ClassHash; use starknet::syscalls::get_block_hash_syscall; use super::{ICheatBlockHashCheckerDispatcherTrait, ICheatBlockHashCheckerLibraryDispatcher}; #[storage] struct Storage {} #[abi(embed_v0)] impl ICheatBlockHashCheckerLibCall of super::ICheatBlockHashCheckerLibCall { fn get_block_hash_with_lib_call( ref self: ContractState, class_hash: ClassHash, block_number: u64, ) -> felt252 { let cheat_block_hash_checker = ICheatBlockHashCheckerLibraryDispatcher { class_hash }; cheat_block_hash_checker.get_block_hash(block_number) } fn get_block_hash(ref self: ContractState, block_number: u64) -> felt252 { let block_hash = get_block_hash_syscall(block_number).unwrap_syscall(); block_hash } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_block_hash/checker_meta_tx_v0.cairo ================================================ #[starknet::interface] trait ICheatBlockHashCheckerMetaTxV0 { fn __execute__(ref self: TContractState, block_number: u64) -> felt252; } #[starknet::contract(account)] mod CheatBlockHashCheckerMetaTxV0 { use starknet::SyscallResultTrait; use starknet::syscalls::get_block_hash_syscall; #[storage] struct Storage {} #[abi(embed_v0)] impl ICheatBlockHashCheckerMetaTxV0 of super::ICheatBlockHashCheckerMetaTxV0 { fn __execute__(ref self: ContractState, block_number: u64) -> felt252 { let block_hash = get_block_hash_syscall(block_number).unwrap_syscall(); block_hash } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_block_hash/checker_proxy.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait ICheatBlockHashChecker { fn get_block_hash(ref self: TContractState, block_number: u64) -> felt252; } #[starknet::interface] trait ICheatBlockHashCheckerProxy { fn get_cheated_block_hash( self: @TContractState, address: ContractAddress, block_number: u64, ) -> felt252; fn get_block_hash(self: @TContractState, block_number: u64) -> felt252; fn call_proxy( self: @TContractState, address: ContractAddress, block_number: u64, ) -> (felt252, felt252); } #[starknet::contract] mod CheatBlockHashCheckerProxy { use starknet::syscalls::get_block_hash_syscall; use starknet::{ContractAddress, SyscallResultTrait, get_contract_address}; use super::{ ICheatBlockHashCheckerDispatcher, ICheatBlockHashCheckerDispatcherTrait, ICheatBlockHashCheckerProxyDispatcher, ICheatBlockHashCheckerProxyDispatcherTrait, }; #[storage] struct Storage {} #[abi(embed_v0)] impl ICheatBlockHashCheckerProxy of super::ICheatBlockHashCheckerProxy { fn get_cheated_block_hash( self: @ContractState, address: ContractAddress, block_number: u64, ) -> felt252 { let cheat_block_hash_checker = ICheatBlockHashCheckerDispatcher { contract_address: address, }; cheat_block_hash_checker.get_block_hash(block_number) } fn get_block_hash(self: @ContractState, block_number: u64) -> felt252 { let block_hash = get_block_hash_syscall(block_number).unwrap_syscall(); block_hash } fn call_proxy( self: @ContractState, address: ContractAddress, block_number: u64, ) -> (felt252, felt252) { let dispatcher = ICheatBlockHashCheckerProxyDispatcher { contract_address: address }; let hash = self.get_block_hash(block_number); let res = dispatcher.get_cheated_block_hash(get_contract_address(), block_number); (hash, res) } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_block_hash/constructor_checker.cairo ================================================ #[starknet::interface] trait IConstructorCheatBlockHashChecker { fn get_stored_block_hash(ref self: TContractState) -> felt252; fn get_block_hash(ref self: TContractState, block_number: u64) -> felt252; } #[starknet::contract] mod ConstructorCheatBlockHashChecker { use starknet::SyscallResultTrait; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; use starknet::syscalls::get_block_hash_syscall; #[storage] struct Storage { blk_hash: felt252, } #[constructor] fn constructor(ref self: ContractState, block_number: u64) { let blk_hash = get_block_hash_syscall(block_number).unwrap_syscall(); self.blk_hash.write(blk_hash); } #[abi(embed_v0)] impl IConstructorCheatBlockHashChecker of super::IConstructorCheatBlockHashChecker< ContractState, > { fn get_stored_block_hash(ref self: ContractState) -> felt252 { self.blk_hash.read() } fn get_block_hash(ref self: ContractState, block_number: u64) -> felt252 { let block_hash = get_block_hash_syscall(block_number).unwrap_syscall(); block_hash } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_block_hash.cairo ================================================ mod checker; mod checker_library_call; mod checker_meta_tx_v0; mod checker_proxy; mod constructor_checker; ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_block_number/checker.cairo ================================================ #[starknet::interface] trait ICheatBlockNumberChecker { fn get_block_number(ref self: TContractState) -> u64; fn get_block_number_and_emit_event(ref self: TContractState) -> u64; } #[starknet::contract] mod CheatBlockNumberChecker { use core::box::BoxTrait; #[storage] struct Storage { balance: felt252, } #[event] #[derive(Drop, starknet::Event)] enum Event { BlockNumberEmitted: BlockNumberEmitted, } #[derive(Drop, starknet::Event)] struct BlockNumberEmitted { block_number: u64, } #[abi(embed_v0)] impl ICheatBlockNumberChecker of super::ICheatBlockNumberChecker { fn get_block_number(ref self: ContractState) -> u64 { starknet::get_block_info().unbox().block_number } fn get_block_number_and_emit_event(ref self: ContractState) -> u64 { let block_number = starknet::get_block_info().unbox().block_number; self.emit(Event::BlockNumberEmitted(BlockNumberEmitted { block_number })); block_number } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_block_number/checker_library_call.cairo ================================================ use starknet::ClassHash; #[starknet::interface] trait ICheatBlockNumberChecker { fn get_block_number(ref self: TContractState) -> u64; } #[starknet::interface] trait ICheatBlockNumberCheckerLibCall { fn get_block_number_with_lib_call(ref self: TContractState, class_hash: ClassHash) -> u64; fn get_block_number(ref self: TContractState) -> u64; } #[starknet::contract] mod CheatBlockNumberCheckerLibCall { use starknet::ClassHash; use super::{ICheatBlockNumberCheckerDispatcherTrait, ICheatBlockNumberCheckerLibraryDispatcher}; #[storage] struct Storage {} #[abi(embed_v0)] impl ICheatBlockNumberCheckerLibCall of super::ICheatBlockNumberCheckerLibCall { fn get_block_number_with_lib_call(ref self: ContractState, class_hash: ClassHash) -> u64 { let cheat_block_number_checker = ICheatBlockNumberCheckerLibraryDispatcher { class_hash, }; cheat_block_number_checker.get_block_number() } fn get_block_number(ref self: ContractState) -> u64 { starknet::get_block_info().unbox().block_number } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_block_number/checker_meta_tx_v0.cairo ================================================ #[starknet::interface] trait ICheatBlockNumberCheckerMetaTxV0 { fn __execute__(ref self: TContractState) -> felt252; } #[starknet::contract(account)] mod CheatBlockNumberCheckerMetaTxV0 { #[storage] struct Storage {} #[abi(embed_v0)] impl ICheatBlockNumberCheckerMetaTxV0 of super::ICheatBlockNumberCheckerMetaTxV0< ContractState, > { fn __execute__(ref self: ContractState) -> felt252 { let block_number = starknet::get_block_number(); block_number.into() } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_block_number/checker_proxy.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait ICheatBlockNumberChecker { fn get_block_number(self: @TContractState) -> u64; } #[starknet::interface] trait ICheatBlockNumberCheckerProxy { fn get_cheated_block_number(self: @TContractState, address: ContractAddress) -> u64; fn get_block_number(self: @TContractState) -> u64; fn call_proxy(self: @TContractState, address: ContractAddress) -> (u64, u64); } #[starknet::contract] mod CheatBlockNumberCheckerProxy { use starknet::{ContractAddress, get_contract_address}; use super::{ ICheatBlockNumberCheckerDispatcher, ICheatBlockNumberCheckerDispatcherTrait, ICheatBlockNumberCheckerProxyDispatcher, ICheatBlockNumberCheckerProxyDispatcherTrait, }; #[storage] struct Storage {} #[abi(embed_v0)] impl ICheatBlockNumberCheckerProxy of super::ICheatBlockNumberCheckerProxy { fn get_cheated_block_number(self: @ContractState, address: ContractAddress) -> u64 { let cheat_block_number_checker = ICheatBlockNumberCheckerDispatcher { contract_address: address, }; cheat_block_number_checker.get_block_number() } fn get_block_number(self: @ContractState) -> u64 { starknet::get_block_info().unbox().block_number } fn call_proxy(self: @ContractState, address: ContractAddress) -> (u64, u64) { let dispatcher = ICheatBlockNumberCheckerProxyDispatcher { contract_address: address }; let block_number = self.get_block_number(); let res = dispatcher.get_cheated_block_number(get_contract_address()); (block_number, res) } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_block_number/constructor_checker.cairo ================================================ #[starknet::interface] trait IConstructorCheatBlockNumberChecker { fn get_stored_block_number(ref self: TContractState) -> u64; fn get_block_number(ref self: TContractState) -> u64; } #[starknet::contract] mod ConstructorCheatBlockNumberChecker { use core::box::BoxTrait; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { blk_nb: u64, } #[constructor] fn constructor(ref self: ContractState) { let block_numb = starknet::get_block_info().unbox().block_number; self.blk_nb.write(block_numb); } #[abi(embed_v0)] impl IConstructorCheatBlockNumberChecker of super::IConstructorCheatBlockNumberChecker< ContractState, > { fn get_stored_block_number(ref self: ContractState) -> u64 { self.blk_nb.read() } fn get_block_number(ref self: ContractState) -> u64 { starknet::get_block_info().unbox().block_number } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_block_number.cairo ================================================ mod checker; mod checker_library_call; mod checker_meta_tx_v0; mod checker_proxy; mod constructor_checker; ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_block_timestamp/checker.cairo ================================================ #[starknet::interface] trait ICheatBlockTimestampChecker { fn get_block_timestamp(ref self: TContractState) -> u64; fn get_block_timestamp_and_emit_event(ref self: TContractState) -> u64; fn get_block_timestamp_and_number(ref self: TContractState) -> (u64, u64); } #[starknet::contract] mod CheatBlockTimestampChecker { use core::box::BoxTrait; #[storage] struct Storage {} #[event] #[derive(Drop, starknet::Event)] enum Event { BlockTimestampEmitted: BlockTimestampEmitted, } #[derive(Drop, starknet::Event)] struct BlockTimestampEmitted { block_timestamp: u64, } #[abi(embed_v0)] impl CheatBlockTimestampChecker of super::ICheatBlockTimestampChecker { fn get_block_timestamp(ref self: ContractState) -> u64 { starknet::get_block_info().unbox().block_timestamp } fn get_block_timestamp_and_emit_event(ref self: ContractState) -> u64 { let block_timestamp = starknet::get_block_info().unbox().block_timestamp; self.emit(Event::BlockTimestampEmitted(BlockTimestampEmitted { block_timestamp })); block_timestamp } fn get_block_timestamp_and_number(ref self: ContractState) -> (u64, u64) { let block_info = starknet::get_block_info().unbox(); (block_info.block_timestamp, block_info.block_number) } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_block_timestamp/checker_library_call.cairo ================================================ use starknet::ClassHash; #[starknet::interface] trait ICheatBlockTimestampChecker { fn get_block_timestamp(ref self: TContractState) -> u64; } #[starknet::interface] trait ICheatBlockTimestampCheckerLibCall { fn get_block_timestamp_with_lib_call(ref self: TContractState, class_hash: ClassHash) -> u64; fn get_block_timestamp(ref self: TContractState) -> u64; } #[starknet::contract] mod CheatBlockTimestampCheckerLibCall { use starknet::ClassHash; use super::{ ICheatBlockTimestampCheckerDispatcherTrait, ICheatBlockTimestampCheckerLibraryDispatcher, }; #[storage] struct Storage {} #[abi(embed_v0)] impl ICheatBlockTimestampCheckerLibCall of super::ICheatBlockTimestampCheckerLibCall< ContractState, > { fn get_block_timestamp_with_lib_call( ref self: ContractState, class_hash: ClassHash, ) -> u64 { let cheat_block_timestamp_checker = ICheatBlockTimestampCheckerLibraryDispatcher { class_hash, }; cheat_block_timestamp_checker.get_block_timestamp() } fn get_block_timestamp(ref self: ContractState) -> u64 { starknet::get_block_info().unbox().block_timestamp } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_block_timestamp/checker_meta_tx_v0.cairo ================================================ #[starknet::interface] trait ICheatBlockTimestampCheckerMetaTxV0 { fn __execute__(ref self: TContractState) -> felt252; } #[starknet::contract(account)] mod CheatBlockTimestampCheckerMetaTxV0 { #[storage] struct Storage {} #[abi(embed_v0)] impl ICheatBlockTimestampCheckerMetaTxV0 of super::ICheatBlockTimestampCheckerMetaTxV0< ContractState, > { fn __execute__(ref self: ContractState) -> felt252 { let block_timestamp = starknet::get_block_timestamp(); block_timestamp.into() } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_block_timestamp/checker_proxy.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait ICheatBlockTimestampChecker { fn get_block_timestamp(self: @TContractState) -> u64; } #[starknet::interface] trait ICheatBlockTimestampCheckerProxy { fn get_cheated_block_timestamp(self: @TContractState, address: ContractAddress) -> u64; fn get_block_timestamp(self: @TContractState) -> u64; fn call_proxy(self: @TContractState, address: ContractAddress) -> (u64, u64); } #[starknet::contract] mod CheatBlockTimestampCheckerProxy { use starknet::{ContractAddress, get_contract_address}; use super::{ ICheatBlockTimestampCheckerDispatcher, ICheatBlockTimestampCheckerDispatcherTrait, ICheatBlockTimestampCheckerProxyDispatcher, ICheatBlockTimestampCheckerProxyDispatcherTrait, }; #[storage] struct Storage {} #[abi(embed_v0)] impl ICheatBlockTimestampCheckerProxy of super::ICheatBlockTimestampCheckerProxy< ContractState, > { fn get_cheated_block_timestamp(self: @ContractState, address: ContractAddress) -> u64 { let cheat_block_timestamp_checker = ICheatBlockTimestampCheckerDispatcher { contract_address: address, }; cheat_block_timestamp_checker.get_block_timestamp() } fn get_block_timestamp(self: @ContractState) -> u64 { starknet::get_block_info().unbox().block_timestamp } fn call_proxy(self: @ContractState, address: ContractAddress) -> (u64, u64) { let dispatcher = ICheatBlockTimestampCheckerProxyDispatcher { contract_address: address, }; let timestamp = self.get_block_timestamp(); let res = dispatcher.get_cheated_block_timestamp(get_contract_address()); (timestamp, res) } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_block_timestamp/constructor_checker.cairo ================================================ #[starknet::interface] trait IConstructorCheatBlockTimestampChecker { fn get_stored_block_timestamp(ref self: TContractState) -> u64; fn get_block_timestamp(ref self: TContractState) -> u64; } #[starknet::contract] mod ConstructorCheatBlockTimestampChecker { use core::box::BoxTrait; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { blk_timestamp: u64, } #[constructor] fn constructor(ref self: ContractState) { let blk_timestamp = starknet::get_block_info().unbox().block_timestamp; self.blk_timestamp.write(blk_timestamp); } #[abi(embed_v0)] impl IConstructorCheatBlockTimestampChecker of super::IConstructorCheatBlockTimestampChecker< ContractState, > { fn get_stored_block_timestamp(ref self: ContractState) -> u64 { self.blk_timestamp.read() } fn get_block_timestamp(ref self: ContractState) -> u64 { starknet::get_block_info().unbox().block_timestamp } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_block_timestamp.cairo ================================================ mod checker; mod checker_library_call; mod checker_meta_tx_v0; mod checker_proxy; mod constructor_checker; ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_caller_address/checker.cairo ================================================ #[starknet::interface] trait ICheatCallerAddressChecker { fn get_caller_address(ref self: TContractState) -> felt252; fn get_caller_address_and_emit_event(ref self: TContractState) -> felt252; } #[starknet::contract] mod CheatCallerAddressChecker { use core::option::Option; use core::traits::Into; #[storage] struct Storage { balance: felt252, } #[event] #[derive(Drop, starknet::Event)] enum Event { CallerAddressEmitted: CallerAddressEmitted, } #[derive(Drop, starknet::Event)] struct CallerAddressEmitted { caller_address: felt252, } #[abi(embed_v0)] impl ICheatCallerAddressChecker of super::ICheatCallerAddressChecker { fn get_caller_address(ref self: ContractState) -> felt252 { starknet::get_caller_address().into() } fn get_caller_address_and_emit_event(ref self: ContractState) -> felt252 { let caller_address = starknet::get_caller_address().into(); self.emit(Event::CallerAddressEmitted(CallerAddressEmitted { caller_address })); caller_address } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_caller_address/checker_cairo0.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait ICairo1Contract { fn start( ref self: TContractState, cairo0_address: ContractAddress, expected_caller_address: ContractAddress, ); fn end(ref self: TContractState); } // 0x18783f6c124c3acc504f300cb6b3a33def439681744d027be8d7fd5d3551565 #[starknet::interface] trait ICairo0Contract { // this function only job is to call `ICairo1Contract::end()` // `contract_address` is `Cairo1Contract_v1` address fn callback(ref self: TContractState, contract_address: felt252); } #[starknet::contract] mod Cairo1Contract_v1 { use core::traits::Into; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; use starknet::{ContractAddress, get_caller_address, get_contract_address}; use super::ICairo0ContractDispatcherTrait; #[storage] struct Storage { expected_caller_address: ContractAddress, } #[event] #[derive(Drop, starknet::Event)] enum Event { End: EndCalled, } #[derive(Drop, starknet::Event)] struct EndCalled { expected_caller_address: felt252, } #[abi(embed_v0)] impl ICairo1ContractImpl of super::ICairo1Contract { fn start( ref self: ContractState, cairo0_address: ContractAddress, expected_caller_address: ContractAddress, ) { let contract_address = get_contract_address(); let cairo0_contract = super::ICairo0ContractDispatcher { contract_address: cairo0_address, }; self.expected_caller_address.write(expected_caller_address); assert(expected_caller_address == get_caller_address(), 'address should be cheated'); cairo0_contract.callback(contract_address.into()); assert(expected_caller_address == get_caller_address(), 'address should be cheated'); } fn end(ref self: ContractState) { let expected_caller_address = self.expected_caller_address.read(); assert(expected_caller_address == get_caller_address(), 'should be same'); self .emit( Event::End( EndCalled { expected_caller_address: expected_caller_address.into() }, ), ); } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_caller_address/checker_library_call.cairo ================================================ use starknet::ClassHash; #[starknet::interface] trait ICheatCallerAddressChecker { fn get_caller_address(ref self: TContractState) -> felt252; } #[starknet::interface] trait ICheatCallerAddressCheckerLibCall { fn get_caller_address_with_lib_call(ref self: TContractState, class_hash: ClassHash) -> felt252; fn get_caller_address(ref self: TContractState) -> felt252; } #[starknet::contract] mod CheatCallerAddressCheckerLibCall { use starknet::ClassHash; use super::{ ICheatCallerAddressCheckerDispatcherTrait, ICheatCallerAddressCheckerLibraryDispatcher, }; #[storage] struct Storage {} #[abi(embed_v0)] impl ICheatCallerAddressCheckerLibCall of super::ICheatCallerAddressCheckerLibCall< ContractState, > { fn get_caller_address_with_lib_call( ref self: ContractState, class_hash: ClassHash, ) -> felt252 { let cheat_caller_address_checker = ICheatCallerAddressCheckerLibraryDispatcher { class_hash, }; cheat_caller_address_checker.get_caller_address() } fn get_caller_address(ref self: ContractState) -> felt252 { starknet::get_caller_address().into() } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_caller_address/checker_meta_tx_v0.cairo ================================================ #[starknet::interface] trait ICheatCallerAddressCheckerMetaTxV0 { fn __execute__(ref self: TContractState) -> felt252; } #[starknet::contract(account)] mod CheatCallerAddressCheckerMetaTxV0 { #[storage] struct Storage {} #[abi(embed_v0)] impl ICheatCallerAddressCheckerMetaTxV0 of super::ICheatCallerAddressCheckerMetaTxV0< ContractState, > { fn __execute__(ref self: ContractState) -> felt252 { starknet::get_caller_address().into() } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_caller_address/checker_proxy.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait ICheatCallerAddressChecker { fn get_caller_address(self: @TContractState) -> felt252; fn get_caller_address_and_emit_event(self: @TContractState) -> felt252; } #[starknet::interface] trait ICheatCallerAddressCheckerProxy { fn get_cheated_caller_address(self: @TContractState, address: ContractAddress) -> felt252; fn get_caller_address(self: @TContractState) -> felt252; fn call_proxy(self: @TContractState, address: ContractAddress) -> (felt252, felt252); } #[starknet::contract] mod CheatCallerAddressCheckerProxy { use starknet::{ContractAddress, get_caller_address, get_contract_address}; use super::{ ICheatCallerAddressCheckerDispatcher, ICheatCallerAddressCheckerDispatcherTrait, ICheatCallerAddressCheckerProxyDispatcher, ICheatCallerAddressCheckerProxyDispatcherTrait, }; #[storage] struct Storage {} #[abi(embed_v0)] impl ICheatCallerAddressCheckerProxy of super::ICheatCallerAddressCheckerProxy { fn get_cheated_caller_address(self: @ContractState, address: ContractAddress) -> felt252 { let cheat_caller_address_checker = ICheatCallerAddressCheckerDispatcher { contract_address: address, }; cheat_caller_address_checker.get_caller_address() } fn get_caller_address(self: @ContractState) -> felt252 { starknet::get_caller_address().into() } fn call_proxy(self: @ContractState, address: ContractAddress) -> (felt252, felt252) { let dispatcher = ICheatCallerAddressCheckerProxyDispatcher { contract_address: address, }; let caller_address: felt252 = get_caller_address().into(); let res = dispatcher.get_cheated_caller_address(get_contract_address()); (caller_address, res) } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_caller_address/constructor_checker.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait IConstructorCheatCallerAddressChecker { fn get_stored_caller_address(ref self: TContractState) -> ContractAddress; fn get_caller_address(ref self: TContractState) -> felt252; } #[starknet::contract] mod ConstructorCheatCallerAddressChecker { use starknet::ContractAddress; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { caller_address: ContractAddress, } #[constructor] fn constructor(ref self: ContractState) { let address = starknet::get_caller_address(); self.caller_address.write(address); } #[abi(embed_v0)] impl IConstructorCheatCallerAddressChecker of super::IConstructorCheatCallerAddressChecker< ContractState, > { fn get_stored_caller_address(ref self: ContractState) -> ContractAddress { self.caller_address.read() } fn get_caller_address(ref self: ContractState) -> felt252 { starknet::get_caller_address().into() } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_caller_address.cairo ================================================ mod checker; mod checker_cairo0; mod checker_library_call; mod checker_meta_tx_v0; mod checker_proxy; mod constructor_checker; ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_sequencer_address/checker.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait ICheatSequencerAddressChecker { fn get_sequencer_address(ref self: TContractState) -> ContractAddress; fn get_seq_addr_and_emit_event(ref self: TContractState) -> ContractAddress; } #[starknet::contract] mod CheatSequencerAddressChecker { use starknet::ContractAddress; #[storage] struct Storage {} #[event] #[derive(Drop, starknet::Event)] enum Event { SequencerAddressEmitted: SequencerAddressEmitted, } #[derive(Drop, starknet::Event)] struct SequencerAddressEmitted { sequencer_address: ContractAddress, } #[abi(embed_v0)] impl ICheatSequencerAddressChecker of super::ICheatSequencerAddressChecker { fn get_sequencer_address(ref self: ContractState) -> ContractAddress { starknet::get_block_info().unbox().sequencer_address } fn get_seq_addr_and_emit_event(ref self: ContractState) -> ContractAddress { let sequencer_address = starknet::get_block_info().unbox().sequencer_address; self .emit( Event::SequencerAddressEmitted(SequencerAddressEmitted { sequencer_address }), ); sequencer_address } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_sequencer_address/checker_library_call.cairo ================================================ use starknet::{ClassHash, ContractAddress}; #[starknet::interface] trait ICheatSequencerAddressChecker { fn get_sequencer_address(self: @TContractState) -> ContractAddress; } #[starknet::interface] trait ICheatSequencerAddressCheckerLibCall { fn get_sequencer_address_with_lib_call( self: @TContractState, class_hash: ClassHash, ) -> ContractAddress; fn get_sequencer_address(self: @TContractState) -> ContractAddress; } #[starknet::contract] mod CheatSequencerAddressCheckerLibCall { use starknet::{ClassHash, ContractAddress}; use super::{ ICheatSequencerAddressCheckerDispatcherTrait, ICheatSequencerAddressCheckerLibraryDispatcher, }; #[storage] struct Storage {} #[abi(embed_v0)] impl ICheatSequencerAddressCheckerLibCall of super::ICheatSequencerAddressCheckerLibCall< ContractState, > { fn get_sequencer_address_with_lib_call( self: @ContractState, class_hash: ClassHash, ) -> ContractAddress { let sequencer_address_checker = ICheatSequencerAddressCheckerLibraryDispatcher { class_hash, }; sequencer_address_checker.get_sequencer_address() } fn get_sequencer_address(self: @ContractState) -> ContractAddress { starknet::get_block_info().unbox().sequencer_address } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_sequencer_address/checker_meta_tx_v0.cairo ================================================ #[starknet::interface] trait ICheatSequencerAddressCheckerMetaTxV0 { fn __execute__(ref self: TContractState) -> felt252; } #[starknet::contract(account)] mod CheatSequencerAddressCheckerMetaTxV0 { #[storage] struct Storage {} #[abi(embed_v0)] impl ICheatSequencerAddressCheckerMetaTxV0 of super::ICheatSequencerAddressCheckerMetaTxV0< ContractState, > { fn __execute__(ref self: ContractState) -> felt252 { let sequencer_address = starknet::get_block_info().unbox().sequencer_address; sequencer_address.into() } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_sequencer_address/checker_proxy.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait ICheatSequencerAddressChecker { fn get_sequencer_address(self: @TContractState) -> ContractAddress; } #[starknet::interface] trait ICheatSequencerAddressCheckerProxy { fn get_cheated_sequencer_address( self: @TContractState, address: ContractAddress, ) -> ContractAddress; fn get_sequencer_address(self: @TContractState) -> ContractAddress; fn call_proxy( self: @TContractState, address: ContractAddress, ) -> (ContractAddress, ContractAddress); } #[starknet::contract] mod CheatSequencerAddressCheckerProxy { use starknet::{ContractAddress, get_contract_address}; use super::{ ICheatSequencerAddressCheckerDispatcher, ICheatSequencerAddressCheckerDispatcherTrait, ICheatSequencerAddressCheckerProxyDispatcher, ICheatSequencerAddressCheckerProxyDispatcherTrait, }; #[storage] struct Storage {} #[abi(embed_v0)] impl ICheatSequencerAddressCheckerProxy of super::ICheatSequencerAddressCheckerProxy< ContractState, > { fn get_cheated_sequencer_address( self: @ContractState, address: ContractAddress, ) -> ContractAddress { let sequencer_address_checker = ICheatSequencerAddressCheckerDispatcher { contract_address: address, }; sequencer_address_checker.get_sequencer_address() } fn get_sequencer_address(self: @ContractState) -> ContractAddress { starknet::get_block_info().unbox().sequencer_address } fn call_proxy( self: @ContractState, address: ContractAddress, ) -> (ContractAddress, ContractAddress) { let dispatcher = ICheatSequencerAddressCheckerProxyDispatcher { contract_address: address, }; let sequencer_address = self.get_sequencer_address(); let res = dispatcher.get_cheated_sequencer_address(get_contract_address()); (sequencer_address, res) } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_sequencer_address/constructor_checker.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait IConstructorCheatSequencerAddressChecker { fn get_stored_sequencer_address(ref self: TContractState) -> ContractAddress; fn get_sequencer_address(self: @TContractState) -> ContractAddress; } #[starknet::contract] mod ConstructorCheatSequencerAddressChecker { use core::box::BoxTrait; use starknet::ContractAddress; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { seq_addr: ContractAddress, } #[constructor] fn constructor(ref self: ContractState) { let sequencer_address = starknet::get_block_info().unbox().sequencer_address; self.seq_addr.write(sequencer_address); } #[abi(embed_v0)] impl IConstructorCheatSequencerAddressChecker of super::IConstructorCheatSequencerAddressChecker< ContractState, > { fn get_stored_sequencer_address(ref self: ContractState) -> ContractAddress { self.seq_addr.read() } fn get_sequencer_address(self: @ContractState) -> ContractAddress { starknet::get_block_info().unbox().sequencer_address } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_sequencer_address.cairo ================================================ mod checker; mod checker_library_call; mod checker_meta_tx_v0; mod checker_proxy; mod constructor_checker; ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_tx_info/constructor_tx_hash_checker.cairo ================================================ #[starknet::interface] trait ITxHashChecker { fn get_stored_tx_hash(self: @TContractState) -> felt252; fn get_transaction_hash(self: @TContractState) -> felt252; } #[starknet::contract] mod TxHashChecker { use core::box::BoxTrait; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { stored_tx_hash: felt252, } #[constructor] fn constructor(ref self: ContractState) { let tx_hash = starknet::get_tx_info().unbox().transaction_hash; self.stored_tx_hash.write(tx_hash); } #[abi(embed_v0)] impl ITxHashChecker of super::ITxHashChecker { fn get_stored_tx_hash(self: @ContractState) -> felt252 { self.stored_tx_hash.read() } fn get_transaction_hash(self: @ContractState) -> felt252 { starknet::get_tx_info().unbox().transaction_hash } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_tx_info/tx_hash_checker_proxy.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait ICheatTxInfoChecker { fn get_transaction_hash(self: @TContractState) -> felt252; } #[starknet::interface] trait ITxHashCheckerProxy { fn get_checkers_tx_hash(self: @TContractState, address: ContractAddress) -> felt252; fn get_transaction_hash(self: @TContractState) -> felt252; fn call_proxy(self: @TContractState, address: ContractAddress) -> (felt252, felt252); } #[starknet::contract] mod TxHashCheckerProxy { use starknet::{ContractAddress, get_contract_address}; use super::{ ICheatTxInfoCheckerDispatcher, ICheatTxInfoCheckerDispatcherTrait, ITxHashCheckerProxyDispatcher, ITxHashCheckerProxyDispatcherTrait, }; #[storage] struct Storage {} #[abi(embed_v0)] impl ITxHashCheckerProxy of super::ITxHashCheckerProxy { fn get_checkers_tx_hash(self: @ContractState, address: ContractAddress) -> felt252 { let tx_info_checker = ICheatTxInfoCheckerDispatcher { contract_address: address }; tx_info_checker.get_transaction_hash() } fn get_transaction_hash(self: @ContractState) -> felt252 { starknet::get_tx_info().unbox().transaction_hash } fn call_proxy(self: @ContractState, address: ContractAddress) -> (felt252, felt252) { let dispatcher = ITxHashCheckerProxyDispatcher { contract_address: address }; let tx_hash = self.get_transaction_hash(); let res = dispatcher.get_checkers_tx_hash(get_contract_address()); (tx_hash, res) } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_tx_info/tx_info_checker.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait ICheatTxInfoChecker { fn get_account_contract_address(ref self: TContractState) -> ContractAddress; fn get_transaction_hash(self: @TContractState) -> felt252; fn get_tx_hash_and_emit_event(ref self: TContractState) -> felt252; fn get_tx_info(self: @TContractState) -> starknet::info::v3::TxInfo; } #[starknet::contract] mod CheatTxInfoChecker { use starknet::syscalls::get_execution_info_v3_syscall; use starknet::{ContractAddress, SyscallResultTrait, get_tx_info}; #[storage] struct Storage {} #[event] #[derive(Drop, starknet::Event)] enum Event { TxHashEmitted: TxHashEmitted, } #[derive(Drop, starknet::Event)] struct TxHashEmitted { tx_hash: felt252, } #[abi(embed_v0)] impl ICheatTxInfoChecker of super::ICheatTxInfoChecker { fn get_transaction_hash(self: @ContractState) -> felt252 { starknet::get_tx_info().unbox().transaction_hash } fn get_tx_hash_and_emit_event(ref self: ContractState) -> felt252 { let tx_hash = starknet::get_tx_info().unbox().transaction_hash; self.emit(Event::TxHashEmitted(TxHashEmitted { tx_hash })); tx_hash } fn get_tx_info(self: @ContractState) -> starknet::info::v3::TxInfo { let execution_info = get_execution_info_v3_syscall().unwrap_syscall().unbox(); execution_info.tx_info.unbox() } fn get_account_contract_address(ref self: ContractState) -> ContractAddress { let tx_info = get_tx_info(); tx_info.account_contract_address } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_tx_info/tx_info_checker_library_call.cairo ================================================ use starknet::ClassHash; #[starknet::interface] trait ICheatTxInfoChecker { fn get_transaction_hash(self: @TContractState) -> felt252; } #[starknet::interface] trait ICheatTxInfoCheckerLibCall { fn get_tx_hash_with_lib_call(self: @TContractState, class_hash: ClassHash) -> felt252; fn get_tx_info(self: @TContractState) -> starknet::info::v3::TxInfo; } #[starknet::contract] mod CheatTxInfoCheckerLibCall { use starknet::syscalls::get_execution_info_v3_syscall; use starknet::{ClassHash, SyscallResultTrait}; use super::{ICheatTxInfoCheckerDispatcherTrait, ICheatTxInfoCheckerLibraryDispatcher}; #[storage] struct Storage {} #[abi(embed_v0)] impl ICheatTxInfoCheckerLibCall of super::ICheatTxInfoCheckerLibCall { fn get_tx_hash_with_lib_call(self: @ContractState, class_hash: ClassHash) -> felt252 { let tx_info_checker = ICheatTxInfoCheckerLibraryDispatcher { class_hash }; tx_info_checker.get_transaction_hash() } fn get_tx_info(self: @ContractState) -> starknet::info::v3::TxInfo { let execution_info = get_execution_info_v3_syscall().unwrap_syscall().unbox(); execution_info.tx_info.unbox() } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_tx_info/tx_info_checker_meta_tx_v0.cairo ================================================ #[starknet::interface] trait ITxInfoCheckerMetaTxV0 { fn __execute__(ref self: TContractState) -> felt252; } #[starknet::contract(account)] mod TxInfoCheckerMetaTxV0 { use starknet::get_execution_info; #[storage] struct Storage {} #[abi(embed_v0)] impl ITxInfoCheckerMetaTxV0 of super::ITxInfoCheckerMetaTxV0 { fn __execute__(ref self: ContractState) -> felt252 { let execution_info = get_execution_info().unbox(); let tx_info = execution_info.tx_info.unbox(); tx_info.version } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/cheat_tx_info.cairo ================================================ mod constructor_tx_hash_checker; mod tx_hash_checker_proxy; mod tx_info_checker; mod tx_info_checker_library_call; mod tx_info_checker_meta_tx_v0; ================================================ FILE: crates/cheatnet/tests/contracts/src/common/constructor_simple.cairo ================================================ #[starknet::interface] trait IConstructorSimple { fn add_to_number(ref self: TContractState, number: felt252) -> felt252; fn get_number(self: @TContractState) -> felt252; } #[starknet::contract] mod ConstructorSimple { use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { number: felt252, } #[constructor] fn constructor(ref self: ContractState, number: felt252) { self.number.write(number); } #[abi(embed_v0)] impl ConstructorSimpleImpl of super::IConstructorSimple { fn add_to_number(ref self: ContractState, number: felt252) -> felt252 { let new_number = self.number.read() + number; self.number.write(new_number); new_number } fn get_number(self: @ContractState) -> felt252 { self.number.read() } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/common/constructor_simple2.cairo ================================================ #[starknet::interface] trait IConstructorSimple2 { fn add_to_number(ref self: TContractState, number: felt252) -> felt252; fn get_number(self: @TContractState) -> felt252; } #[starknet::contract] mod ConstructorSimple2 { use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { number: felt252, } #[constructor] fn constructor(ref self: ContractState, number: felt252, number2: felt252) { self.number.write(number + number2); } #[abi(embed_v0)] impl ConstructorSimple2Impl of super::IConstructorSimple2 { fn add_to_number(ref self: ContractState, number: felt252) -> felt252 { let new_number = self.number.read() + number; self.number.write(new_number); new_number } fn get_number(self: @ContractState) -> felt252 { self.number.read() } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/common/hello_starknet.cairo ================================================ #[starknet::interface] trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; } #[starknet::contract] mod HelloStarknet { use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { balance: felt252, } #[abi(embed_v0)] impl HelloStarknetImpl of super::IHelloStarknet { fn increase_balance(ref self: ContractState, amount: felt252) { assert(amount != 0, 'Amount cannot be 0'); self.balance.write(self.balance.read() + amount); } fn get_balance(self: @ContractState) -> felt252 { self.balance.read() } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/common.cairo ================================================ mod constructor_simple; mod constructor_simple2; mod hello_starknet; ================================================ FILE: crates/cheatnet/tests/contracts/src/events/constructor_spy_events_checker.cairo ================================================ #[starknet::contract] mod ConstructorSpyEventsChecker { #[storage] struct Storage {} #[event] #[derive(Drop, starknet::Event)] enum Event { FirstEvent: FirstEvent, } #[derive(Drop, starknet::Event)] struct FirstEvent { some_data: felt252, } #[constructor] fn constructor(ref self: ContractState, data: felt252) { self.emit(Event::FirstEvent(FirstEvent { some_data: data })); } } ================================================ FILE: crates/cheatnet/tests/contracts/src/events/spy_events_cairo0.cairo ================================================ use starknet::ContractAddress; // 0x2c77ca97586968c6651a533bd5f58042c368b14cf5f526d2f42f670012e10ac #[starknet::interface] trait ICairo0Contract { // this function only job is to emit `my_event` with single felt252 value fn emit_one_cairo0_event(ref self: TContractState, contract_address: felt252); } #[starknet::interface] trait ISpyEventsCairo0 { fn test_cairo0_event_collection(ref self: TContractState, cairo0_address: ContractAddress); } #[starknet::contract] mod SpyEventsCairo0 { use core::traits::Into; use starknet::ContractAddress; use super::ICairo0ContractDispatcherTrait; #[storage] struct Storage {} #[abi(embed_v0)] impl ISpyEventsCairo0 of super::ISpyEventsCairo0 { fn test_cairo0_event_collection(ref self: ContractState, cairo0_address: ContractAddress) { let cairo0_contract = super::ICairo0ContractDispatcher { contract_address: cairo0_address, }; cairo0_contract.emit_one_cairo0_event(123456789); } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/events/spy_events_checker.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait ISpyEventsChecker { fn do_not_emit(ref self: TContractState); fn emit_one_event(ref self: TContractState, some_data: felt252); fn emit_two_events( ref self: TContractState, some_data: felt252, some_more_data: ContractAddress, ); fn emit_three_events( ref self: TContractState, some_data: felt252, some_more_data: ContractAddress, even_more_data: u256, ); fn emit_event_syscall(ref self: TContractState, some_key: felt252, some_data: felt252); } #[starknet::contract] mod SpyEventsChecker { use starknet::{ContractAddress, SyscallResultTrait}; #[storage] struct Storage {} #[event] #[derive(Drop, starknet::Event)] enum Event { FirstEvent: FirstEvent, SecondEvent: SecondEvent, ThirdEvent: ThirdEvent, } #[derive(Drop, starknet::Event)] struct FirstEvent { some_data: felt252, } #[derive(Drop, starknet::Event)] struct SecondEvent { some_data: felt252, #[key] some_more_data: ContractAddress, } #[derive(Drop, starknet::Event)] struct ThirdEvent { some_data: felt252, some_more_data: ContractAddress, even_more_data: u256, } #[abi(embed_v0)] impl ISpyEventsChecker of super::ISpyEventsChecker { fn do_not_emit(ref self: ContractState) {} fn emit_one_event(ref self: ContractState, some_data: felt252) { self.emit(Event::FirstEvent(FirstEvent { some_data })); } fn emit_two_events( ref self: ContractState, some_data: felt252, some_more_data: ContractAddress, ) { self.emit(Event::FirstEvent(FirstEvent { some_data })); self.emit(Event::SecondEvent(SecondEvent { some_data, some_more_data })); } fn emit_three_events( ref self: ContractState, some_data: felt252, some_more_data: ContractAddress, even_more_data: u256, ) { self.emit(Event::FirstEvent(FirstEvent { some_data })); self.emit(Event::SecondEvent(SecondEvent { some_data, some_more_data })); self.emit(Event::ThirdEvent(ThirdEvent { some_data, some_more_data, even_more_data })); } fn emit_event_syscall(ref self: ContractState, some_key: felt252, some_data: felt252) { starknet::syscalls::emit_event_syscall( array![some_key].span(), array![some_data].span(), ) .unwrap_syscall(); } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/events/spy_events_checker_proxy.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait ISpyEventsChecker { fn do_not_emit(ref self: TContractState); fn emit_one_event(ref self: TContractState, some_data: felt252); fn emit_two_events( ref self: TContractState, some_data: felt252, some_more_data: ContractAddress, ); fn emit_three_events( ref self: TContractState, some_data: felt252, some_more_data: ContractAddress, even_more_data: u256, ); } #[starknet::contract] mod SpyEventsCheckerProxy { use starknet::ContractAddress; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; use super::{ISpyEventsCheckerDispatcher, ISpyEventsCheckerDispatcherTrait}; #[storage] struct Storage { proxied_address: ContractAddress, } #[event] #[derive(Drop, starknet::Event)] enum Event { FirstEvent: FirstEvent, SecondEvent: SecondEvent, ThirdEvent: ThirdEvent, } #[derive(Drop, starknet::Event)] struct FirstEvent { some_data: felt252, } #[derive(Drop, starknet::Event)] struct SecondEvent { some_data: felt252, #[key] some_more_data: ContractAddress, } #[derive(Drop, starknet::Event)] struct ThirdEvent { some_data: felt252, some_more_data: ContractAddress, even_more_data: u256, } #[constructor] fn constructor(ref self: ContractState, proxied_address: ContractAddress) { self.proxied_address.write(proxied_address); } #[abi(embed_v0)] impl ISpyEventsChecker of super::ISpyEventsChecker { fn do_not_emit(ref self: ContractState) { let spy_events_checker = ISpyEventsCheckerDispatcher { contract_address: self.proxied_address.read(), }; spy_events_checker.do_not_emit(); } fn emit_one_event(ref self: ContractState, some_data: felt252) { self.emit(Event::FirstEvent(FirstEvent { some_data })); let spy_events_checker = ISpyEventsCheckerDispatcher { contract_address: self.proxied_address.read(), }; spy_events_checker.emit_one_event(some_data); } fn emit_two_events( ref self: ContractState, some_data: felt252, some_more_data: ContractAddress, ) { self.emit(Event::FirstEvent(FirstEvent { some_data })); self.emit(Event::SecondEvent(SecondEvent { some_data, some_more_data })); let spy_events_checker = ISpyEventsCheckerDispatcher { contract_address: self.proxied_address.read(), }; spy_events_checker.emit_two_events(some_data, some_more_data); } fn emit_three_events( ref self: ContractState, some_data: felt252, some_more_data: ContractAddress, even_more_data: u256, ) { self.emit(Event::FirstEvent(FirstEvent { some_data })); self.emit(Event::SecondEvent(SecondEvent { some_data, some_more_data })); self.emit(Event::ThirdEvent(ThirdEvent { some_data, some_more_data, even_more_data })); let spy_events_checker = ISpyEventsCheckerDispatcher { contract_address: self.proxied_address.read(), }; spy_events_checker.emit_three_events(some_data, some_more_data, even_more_data); } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/events/spy_events_lib_call.cairo ================================================ use starknet::{ClassHash, ContractAddress}; #[starknet::interface] trait ISpyEventsLibCall { fn call_lib_call(ref self: TContractState, data: felt252, class_hash: ClassHash); } #[starknet::contract] mod SpyEventsLibCall { use starknet::ClassHash; #[starknet::interface] trait ISpyEventsChecker { fn emit_one_event(ref self: TContractState, some_data: felt252); } #[storage] struct Storage {} #[abi(embed_v0)] impl ISpyEventsLibCallImpl of super::ISpyEventsLibCall { fn call_lib_call(ref self: ContractState, data: felt252, class_hash: ClassHash) { let spy_events_checker = ISpyEventsCheckerLibraryDispatcher { class_hash }; spy_events_checker.emit_one_event(data); } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/events/spy_events_order_checker.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait ISpyEventsOrderChecker { fn emit_and_call_another( ref self: TContractState, first_data: felt252, second_data: felt252, third_data: felt252, another_contract_address: ContractAddress, ); } #[starknet::contract] mod SpyEventsOrderChecker { use starknet::ContractAddress; #[starknet::interface] trait ISpyEventsChecker { fn emit_one_event(ref self: TContractState, some_data: felt252); } #[storage] struct Storage {} #[event] #[derive(Drop, starknet::Event)] enum Event { SecondEvent: SecondEvent, ThirdEvent: ThirdEvent, } #[derive(Drop, starknet::Event)] struct SecondEvent { data: felt252, } #[derive(Drop, starknet::Event)] struct ThirdEvent { data: felt252, } #[abi(embed_v0)] impl ISpyEventsOrderCheckerImpl of super::ISpyEventsOrderChecker { fn emit_and_call_another( ref self: ContractState, first_data: felt252, second_data: felt252, third_data: felt252, another_contract_address: ContractAddress, ) { self.emit(Event::SecondEvent(SecondEvent { data: first_data })); let spy_events_checker = ISpyEventsCheckerDispatcher { contract_address: another_contract_address, }; spy_events_checker.emit_one_event(second_data); self.emit(Event::ThirdEvent(ThirdEvent { data: third_data })); } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/events.cairo ================================================ mod constructor_spy_events_checker; mod spy_events_cairo0; mod spy_events_checker; mod spy_events_checker_proxy; mod spy_events_lib_call; mod spy_events_order_checker; ================================================ FILE: crates/cheatnet/tests/contracts/src/get_class_hash/get_class_hash_checker.cairo ================================================ use starknet::ClassHash; #[starknet::interface] trait IUpgradeable { fn upgrade(ref self: T, class_hash: ClassHash); } #[starknet::contract] mod GetClassHashCheckerUpg { use starknet::ClassHash; #[storage] struct Storage { inner: felt252, } #[abi(embed_v0)] impl IUpgradeableImpl of super::IUpgradeable { fn upgrade(ref self: ContractState, class_hash: ClassHash) { _upgrade(class_hash); } } fn _upgrade(class_hash: ClassHash) { match starknet::syscalls::replace_class_syscall(class_hash) { Result::Ok(()) => {}, Result::Err(e) => panic(e), }; } } ================================================ FILE: crates/cheatnet/tests/contracts/src/get_class_hash.cairo ================================================ mod get_class_hash_checker; ================================================ FILE: crates/cheatnet/tests/contracts/src/lib.cairo ================================================ mod bytearray_string_panic_call; mod cheat_block_hash; mod cheat_block_number; mod cheat_block_timestamp; mod cheat_caller_address; mod cheat_sequencer_address; mod cheat_tx_info; mod common; mod events; mod get_class_hash; mod library_calls; mod meta_tx_v0; mod mock; mod panic_call; mod replace_bytecode; mod revert; mod segment_arena_user; mod starknet; mod store_load; mod tracked_resources; ================================================ FILE: crates/cheatnet/tests/contracts/src/library_calls.cairo ================================================ #[starknet::interface] pub trait IExampleContract { fn set_class_hash(ref self: T, class_hash: starknet::ClassHash); fn get_caller_address(ref self: T) -> starknet::ContractAddress; } // contract A #[starknet::contract] pub mod ExampleContract { #[storage] struct Storage {} #[abi(embed_v0)] impl IExampleContractImpl of super::IExampleContract { fn set_class_hash(ref self: ContractState, class_hash: starknet::ClassHash) {} fn get_caller_address(ref self: ContractState) -> starknet::ContractAddress { starknet::get_caller_address() } } } // contract B to make library call to the class of contract A #[starknet::contract] pub mod ExampleContractLibraryCall { use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; use super::{IExampleContractDispatcherTrait, IExampleContractLibraryDispatcher}; #[storage] struct Storage { lib_class_hash: starknet::ClassHash, } #[abi(embed_v0)] impl ExampleContract of super::IExampleContract { #[abi(embed_v0)] fn set_class_hash(ref self: ContractState, class_hash: starknet::ClassHash) { self.lib_class_hash.write(class_hash); } fn get_caller_address(ref self: ContractState) -> starknet::ContractAddress { IExampleContractLibraryDispatcher { class_hash: self.lib_class_hash.read() } .get_caller_address() } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/meta_tx_v0/checker.cairo ================================================ #[starknet::interface] trait IMetaTxV0Test { fn execute_meta_tx_v0( self: @TContractState, target: starknet::ContractAddress, signature: Span, ) -> felt252; fn execute_meta_tx_v0_get_block_hash( self: @TContractState, target: starknet::ContractAddress, block_number: u64, signature: Span, ) -> felt252; } #[starknet::contract] mod MetaTxV0Test { use starknet::syscalls::meta_tx_v0_syscall; #[storage] struct Storage {} #[abi(embed_v0)] impl IMetaTxV0Test of super::IMetaTxV0Test { fn execute_meta_tx_v0( self: @ContractState, target: starknet::ContractAddress, signature: Span, ) -> felt252 { let selector = selector!("__execute__"); let calldata = array![]; let result = meta_tx_v0_syscall(target, selector, calldata.span(), signature).unwrap(); *result.at(0) } fn execute_meta_tx_v0_get_block_hash( self: @ContractState, target: starknet::ContractAddress, block_number: u64, signature: Span, ) -> felt252 { let selector = selector!("__execute__"); let calldata = array![block_number.into()]; let result = meta_tx_v0_syscall(target, selector, calldata.span(), signature).unwrap(); *result.at(0) } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/meta_tx_v0.cairo ================================================ mod checker; ================================================ FILE: crates/cheatnet/tests/contracts/src/mock/constructor_mock_checker.cairo ================================================ #[starknet::interface] trait IConstructorMockChecker { fn get_constructor_balance(ref self: TContractState) -> felt252; } #[starknet::contract] mod ConstructorMockChecker { use starknet::ContractAddress; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[starknet::interface] trait IHelloStarknet { fn get_balance(self: @TContractState) -> felt252; } #[storage] struct Storage { constructor_balance: felt252, } #[constructor] fn constructor(ref self: ContractState, balance_contract_address: ContractAddress) { let hello_starknet = IHelloStarknetDispatcher { contract_address: balance_contract_address, }; self.constructor_balance.write(hello_starknet.get_balance()); } #[abi(embed_v0)] impl IConstructorMockCheckerImpl of super::IConstructorMockChecker { fn get_constructor_balance(ref self: ContractState) -> felt252 { self.constructor_balance.read() } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/mock/mock_checker.cairo ================================================ #[derive(Serde, Drop)] struct StructThing { item_one: felt252, item_two: felt252, } #[starknet::interface] trait IMockChecker { fn get_thing(ref self: TContractState) -> felt252; fn get_thing_wrapper(ref self: TContractState) -> felt252; fn get_constant_thing(ref self: TContractState) -> felt252; fn get_struct_thing(ref self: TContractState) -> StructThing; fn get_arr_thing(ref self: TContractState) -> Array; fn get_thing_twice(ref self: TContractState) -> (felt252, felt252); } #[starknet::contract] mod MockChecker { use core::array::ArrayTrait; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; use super::{IMockChecker, IMockCheckerDispatcher, IMockCheckerDispatcherTrait, StructThing}; #[storage] struct Storage { stored_thing: felt252, } #[constructor] fn constructor(ref self: ContractState, arg1: felt252) { self.stored_thing.write(arg1) } #[abi(embed_v0)] impl IMockCheckerImpl of super::IMockChecker { fn get_thing(ref self: ContractState) -> felt252 { self.stored_thing.read() } fn get_thing_wrapper(ref self: ContractState) -> felt252 { self.get_thing() } fn get_constant_thing(ref self: ContractState) -> felt252 { 13 } fn get_struct_thing(ref self: ContractState) -> StructThing { StructThing { item_one: 12, item_two: 21 } } fn get_arr_thing(ref self: ContractState) -> Array { array![StructThing { item_one: 12, item_two: 21 }] } fn get_thing_twice(ref self: ContractState) -> (felt252, felt252) { let contract_address = starknet::get_contract_address(); let dispatcher = IMockCheckerDispatcher { contract_address }; let a = dispatcher.get_thing(); let b = dispatcher.get_thing(); (a, b) } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/mock/mock_checker_library_call.cairo ================================================ use starknet::ClassHash; #[starknet::interface] trait IMockChecker { fn get_constant_thing(ref self: TContractState) -> felt252; } #[starknet::interface] trait IMockCheckerLibCall { fn get_constant_thing_with_lib_call(ref self: TContractState, class_hash: ClassHash) -> felt252; } #[starknet::contract] mod MockCheckerLibCall { use starknet::ClassHash; use super::{IMockCheckerDispatcherTrait, IMockCheckerLibraryDispatcher}; #[storage] struct Storage {} #[abi(embed_v0)] impl IMockCheckerLibCall of super::IMockCheckerLibCall { fn get_constant_thing_with_lib_call( ref self: ContractState, class_hash: ClassHash, ) -> felt252 { let mock_checker = IMockCheckerLibraryDispatcher { class_hash }; mock_checker.get_constant_thing() } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/mock/mock_checker_proxy.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait IMockChecker { fn get_thing(ref self: TContractState) -> felt252; } #[starknet::interface] trait IMockCheckerProxy { fn get_thing_from_contract(ref self: TContractState, address: ContractAddress) -> felt252; fn get_thing_from_contract_and_emit_event( ref self: TContractState, address: ContractAddress, ) -> felt252; } #[starknet::contract] mod MockCheckerProxy { use starknet::ContractAddress; use super::{IMockCheckerDispatcher, IMockCheckerDispatcherTrait}; #[storage] struct Storage {} #[event] #[derive(Drop, starknet::Event)] enum Event { ThingEmitted: ThingEmitted, } #[derive(Drop, starknet::Event)] struct ThingEmitted { thing: felt252, } #[abi(embed_v0)] impl IMockCheckerProxy of super::IMockCheckerProxy { fn get_thing_from_contract(ref self: ContractState, address: ContractAddress) -> felt252 { let dispatcher = IMockCheckerDispatcher { contract_address: address }; dispatcher.get_thing() } fn get_thing_from_contract_and_emit_event( ref self: ContractState, address: ContractAddress, ) -> felt252 { let dispatcher = IMockCheckerDispatcher { contract_address: address }; let thing = dispatcher.get_thing(); self.emit(Event::ThingEmitted(ThingEmitted { thing })); thing } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/mock.cairo ================================================ mod constructor_mock_checker; mod mock_checker; mod mock_checker_library_call; mod mock_checker_proxy; ================================================ FILE: crates/cheatnet/tests/contracts/src/panic_call.cairo ================================================ #[starknet::contract] mod PanicCall { #[storage] struct Storage {} #[external(v0)] fn panic_call(ref self: ContractState) { panic( array![ 'shortstring', 0, 0x800000000000011000000000000000000000000000000000000000000000000, 'shortstring2', ], ); } } ================================================ FILE: crates/cheatnet/tests/contracts/src/replace_bytecode/replace_bytecode_a.cairo ================================================ #[starknet::interface] trait IReplaceBytecodeA { fn get(self: @TContractState) -> felt252; fn set(ref self: TContractState, value: felt252); fn get_const(self: @TContractState) -> felt252; } #[starknet::contract] mod ReplaceBytecodeA { use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { value: felt252, } #[abi(embed_v0)] impl IReplaceBytecodeA of super::IReplaceBytecodeA { fn get(self: @ContractState) -> felt252 { self.value.read() } fn set(ref self: ContractState, value: felt252) { self.value.write(value); } fn get_const(self: @ContractState) -> felt252 { 2137 } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/replace_bytecode/replace_bytecode_b.cairo ================================================ #[starknet::interface] trait IReplaceBytecodeB { fn get(self: @TContractState) -> felt252; fn set(ref self: TContractState, value: felt252); fn get_const(self: @TContractState) -> felt252; } #[starknet::contract] mod ReplaceBytecodeB { use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { value: felt252, } #[abi(embed_v0)] impl IReplaceBytecodeB of super::IReplaceBytecodeB { fn get(self: @ContractState) -> felt252 { self.value.read() + 100 } fn set(ref self: ContractState, value: felt252) { self.value.write(value + 100); } fn get_const(self: @ContractState) -> felt252 { 420 } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/replace_bytecode/replace_fork.cairo ================================================ #[starknet::interface] trait IReplaceInFork { fn get_owner(self: @TContractState) -> felt252; } #[starknet::contract] mod ReplaceInFork { #[storage] struct Storage {} #[abi(embed_v0)] impl IReplaceInFork of super::IReplaceInFork { fn get_owner(self: @ContractState) -> felt252 { 0 } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/replace_bytecode.cairo ================================================ mod replace_bytecode_a; mod replace_bytecode_b; mod replace_fork; ================================================ FILE: crates/cheatnet/tests/contracts/src/revert.cairo ================================================ #[starknet::contract] mod Revert { use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; use starknet::{StorageAddress, SyscallResultTrait, syscalls}; #[storage] struct Storage { storage_var: felt252, } #[external(v0)] fn modify_in_nested_call_and_handle_panic( ref self: ContractState, contract_address: starknet::ContractAddress, new_class_hash: starknet::ClassHash, ) { match syscalls::call_contract_syscall( contract_address, selector!("modify_contract_var_and_panic"), array![new_class_hash.into()].span(), ) { Result::Ok(_) => core::panic_with_felt252('expected revert'), Result::Err(errors) => { let mut error_span = errors.span(); assert(*error_span.pop_back().unwrap() == 'ENTRYPOINT_FAILED', 'unexpected error'); }, } assert(self.storage_var.read() == 0, 'value should not change'); } #[external(v0)] fn modify_in_top_and_nested_calls_and_panic(ref self: ContractState, key: StorageAddress) { let storage_before = syscalls::storage_read_syscall(0, key).unwrap_syscall(); assert(storage_before == 0, 'incorrect storage before'); // Call `modify_specific_storage` without panic. syscalls::call_contract_syscall( starknet::get_contract_address(), selector!("modify_specific_storage"), array![key.into(), 99, 0].span(), ) .unwrap_syscall(); let storage_after = syscalls::storage_read_syscall(0, key).unwrap_syscall(); assert(storage_after == 99, 'incorrect storage after'); let dummy_span = array![1, 1].span(); syscalls::emit_event_syscall(dummy_span, dummy_span).unwrap_syscall(); syscalls::send_message_to_l1_syscall(91, dummy_span).unwrap_syscall(); // Call `modify_specific_storage` with panic. syscalls::call_contract_syscall( starknet::get_contract_address(), selector!("modify_specific_storage"), array![key.into(), 88, 1].span(), ) .unwrap_syscall(); assert(false, 'unreachable'); } #[external(v0)] fn modify_contract_var_and_panic(ref self: ContractState, class_hash: starknet::ClassHash) { let dummy_span = array![0].span(); syscalls::emit_event_syscall(dummy_span, dummy_span).unwrap_syscall(); syscalls::replace_class_syscall(class_hash).unwrap_syscall(); syscalls::send_message_to_l1_syscall(17, dummy_span).unwrap_syscall(); self.storage_var.write(987); assert(self.storage_var.read() == 987, 'value should change'); core::panic_with_felt252('modify_contract_var_and_panic'); } #[external(v0)] fn modify_specific_storage( ref self: ContractState, key: StorageAddress, new_value: felt252, should_panic: bool, ) { let address_domain = 0; syscalls::storage_write_syscall(address_domain, key, new_value).unwrap_syscall(); let dummy_span = array![0].span(); syscalls::emit_event_syscall(dummy_span, dummy_span).unwrap_syscall(); syscalls::send_message_to_l1_syscall(19, dummy_span).unwrap_syscall(); if should_panic { core::panic_with_felt252('modify_specific_storage'); } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/segment_arena_user.cairo ================================================ #[starknet::contract] mod SegmentArenaUser { use core::dict::Felt252Dict; #[storage] struct Storage {} #[external(v0)] fn interface_function(ref self: ContractState) { let _felt_dict: Felt252Dict = Default::default(); } } ================================================ FILE: crates/cheatnet/tests/contracts/src/starknet/block_info_checker_library_call.cairo ================================================ use starknet::{ClassHash, ContractAddress}; #[starknet::interface] trait IBlockInfoChecker { fn read_block_number(ref self: TContractState) -> u64; fn read_block_timestamp(ref self: TContractState) -> u64; fn read_sequencer_address(ref self: TContractState) -> ContractAddress; } #[starknet::interface] trait IBlockInfoCheckerLibCall { fn read_block_number_with_lib_call(ref self: TContractState, class_hash: ClassHash) -> u64; fn read_block_timestamp_with_lib_call(ref self: TContractState, class_hash: ClassHash) -> u64; fn read_sequencer_address_with_lib_call( ref self: TContractState, class_hash: ClassHash, ) -> ContractAddress; } #[starknet::contract] mod BlockInfoCheckerLibCall { use starknet::{ClassHash, ContractAddress}; use super::{IBlockInfoCheckerDispatcherTrait, IBlockInfoCheckerLibraryDispatcher}; #[storage] struct Storage {} #[abi(embed_v0)] impl IBlockInfoCheckerLibCall of super::IBlockInfoCheckerLibCall { fn read_block_number_with_lib_call(ref self: ContractState, class_hash: ClassHash) -> u64 { let block_info_checker = IBlockInfoCheckerLibraryDispatcher { class_hash }; block_info_checker.read_block_number() } fn read_block_timestamp_with_lib_call( ref self: ContractState, class_hash: ClassHash, ) -> u64 { let block_info_checker = IBlockInfoCheckerLibraryDispatcher { class_hash }; block_info_checker.read_block_timestamp() } fn read_sequencer_address_with_lib_call( ref self: ContractState, class_hash: ClassHash, ) -> ContractAddress { let block_info_checker = IBlockInfoCheckerLibraryDispatcher { class_hash }; block_info_checker.read_sequencer_address() } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/starknet/block_info_checker_proxy.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait IBlockInfoChecker { fn read_block_number(self: @TContractState) -> u64; fn read_block_timestamp(self: @TContractState) -> u64; fn read_sequencer_address(self: @TContractState) -> ContractAddress; } #[starknet::interface] trait IBlockInfoCheckerProxy { fn read_block_number(ref self: TContractState, address: ContractAddress) -> u64; fn read_block_timestamp(ref self: TContractState, address: ContractAddress) -> u64; fn read_sequencer_address( ref self: TContractState, address: ContractAddress, ) -> ContractAddress; } #[starknet::contract] mod BlockInfoCheckerProxy { use starknet::ContractAddress; use super::{IBlockInfoCheckerDispatcher, IBlockInfoCheckerDispatcherTrait}; #[storage] struct Storage {} #[abi(embed_v0)] impl IBlockInfoCheckerProxy of super::IBlockInfoCheckerProxy { fn read_block_number(ref self: ContractState, address: ContractAddress) -> u64 { let block_info_checker = IBlockInfoCheckerDispatcher { contract_address: address }; block_info_checker.read_block_number() } fn read_block_timestamp(ref self: ContractState, address: ContractAddress) -> u64 { let block_info_checker = IBlockInfoCheckerDispatcher { contract_address: address }; block_info_checker.read_block_timestamp() } fn read_sequencer_address( ref self: ContractState, address: ContractAddress, ) -> ContractAddress { let block_info_checker = IBlockInfoCheckerDispatcher { contract_address: address }; block_info_checker.read_sequencer_address() } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/starknet/blocker.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait IBlocker { fn write_block(ref self: TContractState); fn read_block_number(self: @TContractState) -> u64; fn read_block_timestamp(self: @TContractState) -> u64; fn read_sequencer_address(self: @TContractState) -> ContractAddress; fn read_block_hash(self: @TContractState) -> felt252; } #[starknet::contract] mod Blocker { use core::array::ArrayTrait; use core::box::BoxTrait; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; use starknet::syscalls::get_block_hash_syscall; use starknet::{ContractAddress, SyscallResultTrait, get_block_info}; #[storage] struct Storage { block_number: u64, block_timestamp: u64, block_hash: felt252, sequencer_address: ContractAddress, } #[abi(embed_v0)] impl IBlockerImpl of super::IBlocker { fn write_block(ref self: ContractState) { let block_info = get_block_info().unbox(); self.block_number.write(block_info.block_number); self.block_timestamp.write(block_info.block_timestamp); self.sequencer_address.write(block_info.sequencer_address); let block_hash = get_block_hash_syscall(block_info.block_number - 10).unwrap_syscall(); self.block_hash.write(block_hash); } fn read_block_number(self: @ContractState) -> u64 { self.block_number.read() } fn read_block_timestamp(self: @ContractState) -> u64 { self.block_timestamp.read() } fn read_sequencer_address(self: @ContractState) -> ContractAddress { self.sequencer_address.read() } fn read_block_hash(self: @ContractState) -> felt252 { self.block_hash.read() } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/starknet/forking_checker.cairo ================================================ use starknet::{ClassHash, ContractAddress}; #[starknet::interface] trait IHelloStarknet { fn get_balance(self: @TContractState) -> felt252; } #[starknet::interface] trait IForkingChecker { fn get_balance_call_contract( ref self: TContractState, contract_address: ContractAddress, ) -> felt252; fn get_balance_library_call(ref self: TContractState, class_hash: ClassHash) -> felt252; fn set_balance(ref self: TContractState, new_balance: felt252); } #[starknet::contract] mod ForkingChecker { use core::option::OptionTrait; use starknet::storage::StoragePointerWriteAccess; use starknet::{ClassHash, ContractAddress}; use super::{ IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait, IHelloStarknetLibraryDispatcher, }; #[storage] struct Storage { balance: felt252, } #[constructor] fn constructor(ref self: ContractState, contract_address: Option) { if contract_address.is_some() { let hello_starknet = IHelloStarknetDispatcher { contract_address: contract_address.unwrap(), }; self.balance.write(hello_starknet.get_balance()); } } #[abi(embed_v0)] impl IForkingCheckerImpl of super::IForkingChecker { fn get_balance_call_contract( ref self: ContractState, contract_address: ContractAddress, ) -> felt252 { let hello_starknet = IHelloStarknetDispatcher { contract_address }; hello_starknet.get_balance() } fn get_balance_library_call(ref self: ContractState, class_hash: ClassHash) -> felt252 { let hello_starknet = IHelloStarknetLibraryDispatcher { class_hash }; hello_starknet.get_balance() } fn set_balance(ref self: ContractState, new_balance: felt252) { self.balance.write(new_balance); } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/starknet/noncer.cairo ================================================ #[starknet::interface] trait INoncer { fn write_nonce(ref self: TContractState); fn read_nonce(self: @TContractState) -> felt252; } #[starknet::contract] mod Noncer { use core::array::ArrayTrait; use core::box::BoxTrait; use starknet::get_tx_info; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { nonce: felt252, } #[abi(embed_v0)] impl INoncerImpl of super::INoncer { fn write_nonce(ref self: ContractState) { let tx_info = get_tx_info().unbox(); let nonce = tx_info.nonce; self.nonce.write(nonce); } fn read_nonce(self: @ContractState) -> felt252 { self.nonce.read() } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/starknet/timestamper.cairo ================================================ #[starknet::interface] trait ITimestamper { fn write_timestamp(ref self: TContractState); fn read_timestamp(self: @TContractState) -> u64; } #[starknet::contract] mod Timestamper { use core::array::ArrayTrait; use starknet::get_block_timestamp; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { time: u64, } #[abi(embed_v0)] impl ITimestamperImpl of super::ITimestamper { fn write_timestamp(ref self: ContractState) { let time = get_block_timestamp(); self.time.write(time); } fn read_timestamp(self: @ContractState) -> u64 { self.time.read() } } } ================================================ FILE: crates/cheatnet/tests/contracts/src/starknet.cairo ================================================ mod block_info_checker_library_call; mod block_info_checker_proxy; mod blocker; mod forking_checker; mod noncer; mod timestamper; ================================================ FILE: crates/cheatnet/tests/contracts/src/store_load/map_simple_value_simple_key.cairo ================================================ #[starknet::contract] mod MapSimpleValueSimpleKey { use starknet::storage::{Map, StorageMapReadAccess, StoragePathEntry, StoragePointerWriteAccess}; #[storage] struct Storage { values: Map, } #[external(v0)] fn insert(ref self: ContractState, key: felt252, value: felt252) { self.values.entry(key).write(value); } #[external(v0)] fn read(self: @ContractState, key: felt252) -> felt252 { self.values.read(key) } } ================================================ FILE: crates/cheatnet/tests/contracts/src/store_load.cairo ================================================ mod map_simple_value_simple_key; ================================================ FILE: crates/cheatnet/tests/contracts/src/tracked_resources.cairo ================================================ // Address of a simple proxy contract that works as a wrapper on the `call_contract_syscall` // Deployed to the Sepolia network and compiled with Sierra version 1.6.0 const PROXY_CONTRACT_ADDRESS: felt252 = 0x004a053601baaed3231638627631caed753b6527484cde2ed2b5b7d57854a902; #[starknet::contract] mod TrackedResources { use starknet::{SyscallResultTrait, get_contract_address, syscalls}; #[storage] struct Storage {} #[external(v0)] fn call_twice(ref self: ContractState) { // Call through proxy syscalls::call_contract_syscall( super::PROXY_CONTRACT_ADDRESS.try_into().unwrap(), selector!("call_single"), array![get_contract_address().try_into().unwrap(), selector!("call_internal"), 0] .span(), ) .unwrap_syscall(); // Call itself directly syscalls::call_contract_syscall( get_contract_address(), selector!("call_internal"), array![].span(), ) .unwrap_syscall(); } #[external(v0)] fn call_internal(ref self: ContractState) { dummy_computations(); } fn dummy_computations() { 1_u8 >= 1_u8; 1_u8 & 1_u8; core::pedersen::pedersen(1, 2); core::poseidon::hades_permutation(0, 0, 0); core::keccak::keccak_u256s_le_inputs(array![1].span()); syscalls::get_block_hash_syscall(0x100).unwrap_syscall(); syscalls::emit_event_syscall(array![1].span(), array![2].span()).unwrap_syscall(); } } ================================================ FILE: crates/cheatnet/tests/main.rs ================================================ use scarb_api::ScarbCommand; mod builtins; mod cheatcodes; pub(crate) mod common; mod starknet; // Build testing contracts before executing the tests #[cfg(test)] #[ctor::ctor] fn init() { use camino::Utf8PathBuf; let contracts_path = Utf8PathBuf::from("tests").join("contracts"); let output = ScarbCommand::new() .current_dir(contracts_path) .arg("build") .command() .output() .unwrap(); if !output.status.success() { let stderr = String::from_utf8(output.stderr).expect("Decoding scarb stderr failed"); let stdout = String::from_utf8(output.stdout).expect("Decoding scarb stdout failed"); panic!("scarb build failed,\nstderr: \n{stderr}\nstdout: \n{stdout}"); } } ================================================ FILE: crates/cheatnet/tests/starknet/block.rs ================================================ use crate::common::call_contract; use crate::common::{ assertions::assert_success, deploy_contract, recover_data, state::create_cached_state, }; use blockifier::state::state_api::State; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::storage::selector_from_name; use cheatnet::state::CheatnetState; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; fn check_block( state: &mut dyn State, cheatnet_state: &mut CheatnetState, contract_address: &ContractAddress, ) -> (Felt, Felt, Felt, Felt) { let write_block = selector_from_name("write_block"); let read_block_number = selector_from_name("read_block_number"); let read_block_timestamp = selector_from_name("read_block_timestamp"); let read_sequencer_address = selector_from_name("read_sequencer_address"); let read_block_hash = selector_from_name("read_block_hash"); let output = call_contract(state, cheatnet_state, contract_address, write_block, &[]); assert_success(output, &[]); let output = call_contract( state, cheatnet_state, contract_address, read_block_number, &[], ); let block_number = &recover_data(output)[0]; let output = call_contract( state, cheatnet_state, contract_address, read_block_timestamp, &[], ); let block_timestamp = &recover_data(output)[0]; let output = call_contract( state, cheatnet_state, contract_address, read_sequencer_address, &[], ); let sequencer_address = &recover_data(output)[0]; let output = call_contract( state, cheatnet_state, contract_address, read_block_hash, &[], ); let block_hash = &recover_data(output)[0]; ( *block_number, *block_timestamp, *sequencer_address, *block_hash, ) } #[test] fn block_does_not_decrease() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract(&mut cached_state, &mut cheatnet_state, "Blocker", &[]); let (old_block_number, old_block_timestamp, old_sequencer_address, old_block_hash) = check_block(&mut cached_state, &mut cheatnet_state, &contract_address); let (new_block_number, new_block_timestamp, new_sequencer_address, new_block_hash) = check_block(&mut cached_state, &mut cheatnet_state, &contract_address); assert!(old_block_number <= new_block_number); assert!(old_block_timestamp <= new_block_timestamp); assert_eq!(old_sequencer_address, new_sequencer_address); assert_eq!(new_block_hash, old_block_hash); } ================================================ FILE: crates/cheatnet/tests/starknet/cheat_fork.rs ================================================ use crate::common::call_contract; use crate::common::state::create_fork_cached_state; use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::rpc::CallSuccess; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::storage::selector_from_name; use cheatnet::state::CheatnetState; use conversions::string::TryFromHexStr; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; use tempfile::TempDir; use test_case::test_case; const CAIRO0_TESTER_ADDRESS: &str = "0x7fec0c04dde6b1cfa7994359313f8b67edd0d8e40e28424437702d3ee48c2a4"; #[test_case("return_caller_address"; "when common call")] #[test_case("return_proxied_caller_address"; "when library call")] fn cheat_caller_address_cairo0_contract(selector: &str) { let cache_dir = TempDir::new().unwrap(); let mut cached_fork_state = create_fork_cached_state(cache_dir.path().to_str().unwrap()); let mut cheatnet_state = CheatnetState::default(); let contract_address = ContractAddress::try_from_hex_str(CAIRO0_TESTER_ADDRESS).unwrap(); let selector = selector_from_name(selector); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[], ); let Ok(CallSuccess { ret_data }) = output else { panic!("Wrong call output") }; let caller = &ret_data[0]; cheatnet_state.start_cheat_caller_address(contract_address, ContractAddress::from(123_u128)); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[], ); let Ok(CallSuccess { ret_data }) = output else { panic!("Wrong call output") }; let cheated_caller_address = &ret_data[0]; cheatnet_state.stop_cheat_caller_address(contract_address); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[], ); let Ok(CallSuccess { ret_data }) = output else { panic!("Wrong call output") }; let uncheated_caller_address = &ret_data[0]; assert_eq!(cheated_caller_address, &Felt::from(123)); assert_eq!(uncheated_caller_address, caller); } #[test_case("return_block_number"; "when common call")] #[test_case("return_proxied_block_number"; "when library call")] fn cheat_block_number_cairo0_contract(selector: &str) { let cache_dir = TempDir::new().unwrap(); let mut cached_fork_state = create_fork_cached_state(cache_dir.path().to_str().unwrap()); let mut cheatnet_state = CheatnetState::default(); let contract_address = ContractAddress::try_from_hex_str(CAIRO0_TESTER_ADDRESS).unwrap(); let selector = selector_from_name(selector); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[], ); let Ok(CallSuccess { ret_data }) = output else { panic!("Wrong call output") }; let block_number = &ret_data[0]; cheatnet_state.start_cheat_block_number(contract_address, 123); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[], ); let Ok(CallSuccess { ret_data }) = output else { panic!("Wrong call output") }; let cheated_block_number = &ret_data[0]; cheatnet_state.stop_cheat_block_number(contract_address); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[], ); let Ok(CallSuccess { ret_data }) = output else { panic!("Wrong call output") }; let uncheated_block_number = &ret_data[0]; assert_eq!(cheated_block_number, &Felt::from(123)); assert_eq!(uncheated_block_number, block_number); } #[test_case("return_block_timestamp"; "when common call")] #[test_case("return_proxied_block_timestamp"; "when library call")] fn cheat_block_timestamp_cairo0_contract(selector: &str) { let cache_dir = TempDir::new().unwrap(); let mut cached_fork_state = create_fork_cached_state(cache_dir.path().to_str().unwrap()); let mut cheatnet_state = CheatnetState::default(); let contract_address = ContractAddress::try_from_hex_str(CAIRO0_TESTER_ADDRESS).unwrap(); let selector = selector_from_name(selector); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[], ); let Ok(CallSuccess { ret_data }) = output else { panic!("Wrong call output") }; let block_timestamp = &ret_data[0]; cheatnet_state.start_cheat_block_timestamp(contract_address, 123); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[], ); let Ok(CallSuccess { ret_data }) = output else { panic!("Wrong call output") }; let cheated_block_timestamp = &ret_data[0]; cheatnet_state.stop_cheat_block_timestamp(contract_address); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[], ); let Ok(CallSuccess { ret_data }) = output else { panic!("Wrong call output") }; let uncheated_block_timestamp = &ret_data[0]; assert_eq!(cheated_block_timestamp, &Felt::from(123)); assert_eq!(uncheated_block_timestamp, block_timestamp); } ================================================ FILE: crates/cheatnet/tests/starknet/execution.rs ================================================ use crate::common::state::{create_cached_state, create_fork_cached_state_at}; use crate::common::{ call_contract_extended_result, deploy_contract, execute_entry_point_without_revert, selector_from_name, }; use blockifier::execution::contract_class::TrackedResource; use blockifier::execution::syscalls::hint_processor::ENTRYPOINT_FAILED_ERROR_FELT; use blockifier::state::state_api::StateReader; use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::rpc::CallFailure; use cheatnet::state::CheatnetState; use conversions::IntoConv; use conversions::felt::FromShortString; use starknet_api::felt; use starknet_api::state::StorageKey; use starknet_types_core::felt::Felt; use tempfile::TempDir; #[test] fn test_state_reverted_in_nested_call() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract(&mut cached_state, &mut cheatnet_state, "Revert", &[]); // Mock contract just to get a class hash, it can be replaced with any other declared contract let mock_contract = deploy_contract( &mut cached_state, &mut cheatnet_state, "MockCheckerLibCall", &[], ); let mock_class_hash = cached_state.get_class_hash_at(mock_contract).unwrap(); let res = execute_entry_point_without_revert( &mut cached_state, &mut cheatnet_state, &contract_address, selector_from_name("modify_in_nested_call_and_handle_panic"), &[contract_address.into_(), mock_class_hash.into_()], TrackedResource::SierraGas, ) .unwrap(); assert!(!res.execution.failed); let [inner_call] = &res.inner_calls[..] else { panic!("Expected one inner call, got {:?}", res.inner_calls); }; assert_eq!( inner_call.execution.retdata.0, &[Felt::from_short_string("modify_contract_var_and_panic").unwrap()] ); assert!(inner_call.execution.events.is_empty()); assert!(inner_call.execution.l2_to_l1_messages.is_empty()); } #[test] fn test_state_not_reverted_in_top_call_when_raw_execution() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); // Mock contract just to get a class hash, it can be replaced with any other declared contract let mock_contract = deploy_contract( &mut cached_state, &mut cheatnet_state, "MockCheckerLibCall", &[], ); let mock_class_hash = cached_state.get_class_hash_at(mock_contract).unwrap(); let contract_address = deploy_contract(&mut cached_state, &mut cheatnet_state, "Revert", &[]); // Call via `execute_call_entry_point` directly (no revert on failure) to confirm // that state mutations (events, messages) survive a failed call at this layer. let res = execute_entry_point_without_revert( &mut cached_state, &mut cheatnet_state, &contract_address, selector_from_name("modify_contract_var_and_panic"), &[mock_class_hash.into_()], TrackedResource::SierraGas, ) .unwrap(); assert!(res.execution.failed); assert!(res.inner_calls.is_empty()); assert!(!res.execution.events.is_empty()); assert!(!res.execution.l2_to_l1_messages.is_empty()); } #[test] fn test_state_reverted_in_top_call_when_call_entry_point() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); // Mock contract just to get a class hash, it can be replaced with any other declared contract let mock_contract = deploy_contract( &mut cached_state, &mut cheatnet_state, "MockCheckerLibCall", &[], ); let mock_class_hash = cached_state.get_class_hash_at(mock_contract).unwrap(); let contract_address = deploy_contract(&mut cached_state, &mut cheatnet_state, "Revert", &[]); // Call via `call_entry_point`, so the same way contract calls are executed in test bodies. // On failure, it should revert all state mutations. let res = call_contract_extended_result( &mut cached_state, &mut cheatnet_state, &contract_address, selector_from_name("modify_contract_var_and_panic"), &[mock_class_hash.into_()], ) .call_info .expect("Call info should be present"); assert!(res.execution.failed); assert!(res.inner_calls.is_empty()); assert!(res.execution.events.is_empty()); assert!(res.execution.l2_to_l1_messages.is_empty()); assert_eq!( res.execution.retdata.0, &[Felt::from_short_string("modify_contract_var_and_panic").unwrap()] ); } #[test] fn test_state_reverted_only_in_failed_nested_call_when_raw_execution() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract(&mut cached_state, &mut cheatnet_state, "Revert", &[]); let storage_key = StorageKey::try_from(felt!(123_u64)).unwrap(); // Call via `execute_call_entry_point` directly (no revert on failure) to confirm // that state mutations (storage, events, messages) survive a failed call at this layer. let res = execute_entry_point_without_revert( &mut cached_state, &mut cheatnet_state, &contract_address, selector_from_name("modify_in_top_and_nested_calls_and_panic"), &[(*storage_key.0).into_()], TrackedResource::SierraGas, ) .unwrap(); assert!(res.execution.failed); let [inner_call, failed_inner_call] = &res.inner_calls[..] else { panic!("Expected two inner calls, got {:?}", res.inner_calls); }; // Successful inner call state was not reverted. assert!(!inner_call.execution.failed); assert!(!inner_call.execution.events.is_empty()); assert!(!inner_call.execution.l2_to_l1_messages.is_empty()); // Failed inner call state was reverted by `execute_inner_call` function. assert!(failed_inner_call.execution.failed); assert!(failed_inner_call.execution.events.is_empty()); assert!(failed_inner_call.execution.l2_to_l1_messages.is_empty()); assert!(!res.execution.events.is_empty()); assert!(!res.execution.l2_to_l1_messages.is_empty()); assert_eq!( res.execution.retdata.0, &[ Felt::from_short_string("modify_specific_storage").unwrap(), ENTRYPOINT_FAILED_ERROR_FELT ] ); let storage_value = cached_state .get_storage_at(contract_address, storage_key) .unwrap(); assert_eq!( storage_value, felt!(99_u8), "Storage should be 99 without revert" ); } #[test] fn test_state_reverted_in_top_and_nested_calls_when_call_entry_point() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract(&mut cached_state, &mut cheatnet_state, "Revert", &[]); let storage_key = StorageKey::try_from(felt!(123_u64)).unwrap(); // Call via `call_entry_point`, so the same way contract calls are executed in test bodies. // On failure, it should revert all state mutations. let res = call_contract_extended_result( &mut cached_state, &mut cheatnet_state, &contract_address, selector_from_name("modify_in_top_and_nested_calls_and_panic"), &[(*storage_key.0).into_()], ); let res_call_info = res.call_info.expect("Call info should be present"); assert!(res_call_info.execution.failed); let [inner_call, failed_inner_call] = &res_call_info.inner_calls[..] else { panic!( "Expected two inner calls, got {:?}", res_call_info.inner_calls ); }; // Now state in all nested calls should be reverted. assert!(!inner_call.execution.failed); assert!(inner_call.execution.events.is_empty()); assert!(inner_call.execution.l2_to_l1_messages.is_empty()); assert!(failed_inner_call.execution.failed); assert!(failed_inner_call.execution.events.is_empty()); assert!(failed_inner_call.execution.l2_to_l1_messages.is_empty()); assert!(res_call_info.execution.events.is_empty()); assert!(res_call_info.execution.l2_to_l1_messages.is_empty()); assert_eq!( res_call_info.execution.retdata.0, &[ Felt::from_short_string("modify_specific_storage").unwrap(), ENTRYPOINT_FAILED_ERROR_FELT ] ); let CallFailure::Recoverable { panic_data } = res.call_result.as_ref().unwrap_err() else { panic!("Expected Recoverable error, got {:?}", res.call_result); }; assert_eq!( panic_data, &[ Felt::from_short_string("modify_specific_storage").unwrap(), ENTRYPOINT_FAILED_ERROR_FELT, ENTRYPOINT_FAILED_ERROR_FELT ] ); let storage_value = cached_state .get_storage_at(contract_address, storage_key) .unwrap(); assert_eq!( storage_value, felt!(0_u8), "Storage should be 0 after revert" ); } #[test] fn test_tracked_resources() { let cache_dir = TempDir::new().unwrap(); let mut cached_state = create_fork_cached_state_at(782_878, cache_dir.path().to_str().unwrap()); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract( &mut cached_state, &mut cheatnet_state, "TrackedResources", &[], ); let main_call_info = execute_entry_point_without_revert( &mut cached_state, &mut cheatnet_state, &contract_address, selector_from_name("call_twice"), &[], TrackedResource::SierraGas, ) .unwrap(); // `call_twice` from the `TrackedResources` contract assert!(!main_call_info.execution.failed); assert_eq!(main_call_info.inner_calls.len(), 2); assert_eq!(main_call_info.tracked_resource, TrackedResource::SierraGas); assert_ne!(main_call_info.execution.gas_consumed, 0); // `call_single` from the forked proxy contract let first_inner_call = main_call_info.inner_calls.first().unwrap(); assert_eq!( first_inner_call.tracked_resource, TrackedResource::CairoSteps ); assert_eq!(first_inner_call.execution.gas_consumed, 0); assert_ne!(first_inner_call.resources.vm_resources.n_steps, 0); assert_eq!(first_inner_call.inner_calls.len(), 1); // `call_internal` from the `TrackedResources` contract let inner_inner_call = first_inner_call.inner_calls.first().unwrap(); assert_eq!( inner_inner_call.tracked_resource, TrackedResource::CairoSteps ); assert_eq!(inner_inner_call.execution.gas_consumed, 0); assert_ne!(inner_inner_call.resources.vm_resources.n_steps, 0); // `call_internal` from the `TrackedResources` contract let second_inner_call = main_call_info.inner_calls.last().unwrap(); assert_eq!( second_inner_call.tracked_resource, TrackedResource::SierraGas ); assert_ne!(second_inner_call.execution.gas_consumed, 0); assert_eq!(second_inner_call.resources.vm_resources.n_steps, 0); } ================================================ FILE: crates/cheatnet/tests/starknet/forking.rs ================================================ use crate::common::assertions::{assert_error, assert_panic, assert_success}; use crate::common::cache::{purge_cache, read_cache}; use crate::common::state::{create_fork_cached_state, create_fork_cached_state_at}; use crate::common::{call_contract, deploy_contract}; use blockifier::execution::syscalls::hint_processor::ENTRYPOINT_FAILED_ERROR_FELT; use blockifier::state::cached_state::CachedState; use camino::Utf8Path; use cheatnet::constants::build_testing_state; use cheatnet::forking::cache::cache_version; use cheatnet::forking::state::ForkStateReader; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::storage::selector_from_name; use cheatnet::state::{BlockInfoReader, CheatnetState, ExtendedStateReader}; use conversions::byte_array::ByteArray; use conversions::string::TryFromHexStr; use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; use serde_json::Value; use starknet_api::block::BlockNumber; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; use tempfile::TempDir; #[test] fn fork_simple() { let cache_dir = TempDir::new().unwrap(); let mut cached_fork_state = create_fork_cached_state(cache_dir.path().to_str().unwrap()); let mut cheatnet_state = CheatnetState::default(); let contract_address = ContractAddress::try_from_hex_str( "0x202de98471a4fae6bcbabb96cab00437d381abc58b02509043778074d6781e9", ) .unwrap(); let selector = selector_from_name("get_balance"); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &[Felt::from(0)]); let selector = selector_from_name("increase_balance"); call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[Felt::from(100)], ) .unwrap(); let selector = selector_from_name("get_balance"); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &[Felt::from(100)]); } #[test] fn try_calling_nonexistent_contract() { let cache_dir = TempDir::new().unwrap(); let mut cached_fork_state = create_fork_cached_state(cache_dir.path().to_str().unwrap()); let mut cheatnet_state = CheatnetState::default(); let contract_address = ContractAddress::from(1_u8); let selector = selector_from_name("get_balance"); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[], ); let msg = "Contract not deployed at address: 0x1"; let mut panic_data_felts = ByteArray::from(msg).serialize_with_magic(); panic_data_felts.push(ENTRYPOINT_FAILED_ERROR_FELT); assert_panic(output, &panic_data_felts); } #[test] fn test_forking_at_block_number() { let cache_dir = TempDir::new().unwrap(); { let mut cheatnet_state = CheatnetState::default(); let mut cached_state_before_delopy = create_fork_cached_state_at(50_000, cache_dir.path().to_str().unwrap()); let mut cached_state_after_deploy = create_fork_cached_state_at(53_681, cache_dir.path().to_str().unwrap()); let contract_address = ContractAddress::try_from_hex_str( "0x202de98471a4fae6bcbabb96cab00437d381abc58b02509043778074d6781e9", ) .unwrap(); let selector = selector_from_name("get_balance"); let output = call_contract( &mut cached_state_before_delopy, &mut cheatnet_state, &contract_address, selector, &[], ); let msg = "Contract not deployed at address: 0x202de98471a4fae6bcbabb96cab00437d381abc58b02509043778074d6781e9"; let mut panic_data_felts = ByteArray::from(msg).serialize_with_magic(); panic_data_felts.push(ENTRYPOINT_FAILED_ERROR_FELT); assert_panic(output, &panic_data_felts); let selector = selector_from_name("get_balance"); let output = call_contract( &mut cached_state_after_deploy, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &[Felt::from(0)]); } purge_cache(cache_dir.path().to_str().unwrap()); } #[test] fn call_forked_contract_from_other_contract() { let cache_dir = TempDir::new().unwrap(); let mut cached_fork_state = create_fork_cached_state(cache_dir.path().to_str().unwrap()); let mut cheatnet_state = CheatnetState::default(); let forked_contract_address = Felt::try_from_hex_str("0x202de98471a4fae6bcbabb96cab00437d381abc58b02509043778074d6781e9") .unwrap(); let contract_address = deploy_contract( &mut cached_fork_state, &mut cheatnet_state, "ForkingChecker", &[Felt::from(1)], ); let selector = selector_from_name("get_balance_call_contract"); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[forked_contract_address], ); assert_success(output, &[Felt::from(0)]); } #[test] fn library_call_on_forked_class_hash() { let cache_dir = TempDir::new().unwrap(); let mut cached_fork_state = create_fork_cached_state(cache_dir.path().to_str().unwrap()); let mut cheatnet_state = CheatnetState::default(); let forked_class_hash = Felt::try_from_hex_str( "0x06a7eb29ee38b0a0b198e39ed6ad458d2e460264b463351a0acfc05822d61550", ) .unwrap(); let contract_address = deploy_contract( &mut cached_fork_state, &mut cheatnet_state, "ForkingChecker", &[Felt::from(1)], ); let selector = selector_from_name("get_balance_library_call"); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[forked_class_hash], ); assert_success(output, &[Felt::from(0)]); call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector_from_name("set_balance"), &[Felt::from(100)], ) .unwrap(); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[forked_class_hash], ); assert_success(output, &[Felt::from(100)]); } #[test] fn call_forked_contract_from_constructor() { let cache_dir = TempDir::new().unwrap(); let mut cached_fork_state = create_fork_cached_state(cache_dir.path().to_str().unwrap()); let mut cheatnet_state = CheatnetState::default(); let forked_class_hash = Felt::try_from_hex_str( "0x06a7eb29ee38b0a0b198e39ed6ad458d2e460264b463351a0acfc05822d61550", ) .unwrap(); let forked_contract_address = Felt::try_from_hex_str("0x202de98471a4fae6bcbabb96cab00437d381abc58b02509043778074d6781e9") .unwrap(); let contract_address = deploy_contract( &mut cached_fork_state, &mut cheatnet_state, "ForkingChecker", &[Felt::from(0), forked_contract_address], ); let selector = selector_from_name("get_balance_library_call"); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[forked_class_hash], ); assert_success(output, &[Felt::from(0)]); } #[test] fn call_forked_contract_get_block_info_via_proxy() { let cache_dir = TempDir::new().unwrap(); let mut cached_fork_state = create_fork_cached_state_at(53_655, cache_dir.path().to_str().unwrap()); let block_info = cached_fork_state.state.get_block_info().unwrap(); let mut cheatnet_state = CheatnetState { block_info, ..Default::default() }; let forked_contract_address = Felt::try_from_hex_str("0x3d80c579ad7d83ff46634abe8f91f9d2080c5c076d4f0f59dd810f9b3f01164") .unwrap(); let contract_address = deploy_contract( &mut cached_fork_state, &mut cheatnet_state, "BlockInfoCheckerProxy", &[], ); let selector = selector_from_name("read_block_number"); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[forked_contract_address], ); assert_success(output, &[Felt::from(53_655)]); let selector = selector_from_name("read_block_timestamp"); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[forked_contract_address], ); assert_success(output, &[Felt::from(1_711_548_115)]); let selector = selector_from_name("read_sequencer_address"); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[forked_contract_address], ); assert_success( output, &[Felt::try_from_hex_str( "0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8", ) .unwrap()], ); } #[test] fn call_forked_contract_get_block_info_via_libcall() { let cache_dir = TempDir::new().unwrap(); let mut cached_fork_state = create_fork_cached_state_at(53_669, cache_dir.path().to_str().unwrap()); let block_info = cached_fork_state.state.get_block_info().unwrap(); let mut cheatnet_state = CheatnetState { block_info, ..Default::default() }; let forked_class_hash = Felt::try_from_hex_str( "0x04947e141416a51b57a59bc8786b5c0e02751d33e46383fa9cebbf9cf6f30844", ) .unwrap(); let contract_address = deploy_contract( &mut cached_fork_state, &mut cheatnet_state, "BlockInfoCheckerLibCall", &[], ); let selector = selector_from_name("read_block_number_with_lib_call"); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[forked_class_hash], ); assert_success(output, &[Felt::from(53_669)]); let selector = selector_from_name("read_block_timestamp_with_lib_call"); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[forked_class_hash], ); assert_success(output, &[Felt::from(1_711_551_518)]); let selector = selector_from_name("read_sequencer_address_with_lib_call"); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[forked_class_hash], ); assert_success( output, &[Felt::try_from_hex_str( "0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8", ) .unwrap()], ); } #[test] fn using_specified_block_nb_is_cached() { let cache_dir = TempDir::new().unwrap(); let run_test = || { let mut cached_state = create_fork_cached_state_at(53_669, cache_dir.path().to_str().unwrap()); let _ = cached_state.state.get_block_info().unwrap(); let mut cheatnet_state = CheatnetState::default(); let contract_address = ContractAddress::try_from_hex_str( "0x202de98471a4fae6bcbabb96cab00437d381abc58b02509043778074d6781e9", ) .unwrap(); let selector = selector_from_name("get_balance"); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &[Felt::from(0)]); }; let assert_cache = || { // Assertions let cache = read_cache( cache_dir .path() .join(format!("*v{}.json", cache_version())) .to_str() .unwrap(), ); assert_eq!( cache["storage_at"].as_object().unwrap() ["0x202de98471a4fae6bcbabb96cab00437d381abc58b02509043778074d6781e9"] .as_object() .unwrap()["0x206f38f7e4f15e87567361213c28f235cccdaa1d7fd34c9db1dfe9489c6a091"], "0x0" ); assert_eq!( cache["class_hash_at"].as_object().unwrap()["0x202de98471a4fae6bcbabb96cab00437d381abc58b02509043778074d6781e9"], "0x6a7eb29ee38b0a0b198e39ed6ad458d2e460264b463351a0acfc05822d61550" ); match cache["compiled_contract_class"].as_object().unwrap()["0x6a7eb29ee38b0a0b198e39ed6ad458d2e460264b463351a0acfc05822d61550"] { Value::Object(_) => {} _ => panic!("The compiled_contract_class entry is not an object"), } assert_eq!( cache["block_info"].as_object().unwrap()["block_number"] .as_u64() .unwrap(), 53_669 ); assert_eq!( cache["block_info"].as_object().unwrap()["block_timestamp"] .as_u64() .unwrap(), 1_711_551_518 ); assert_eq!( cache["block_info"].as_object().unwrap()["sequencer_address"], "0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8" ); }; // 1st run - check whether cache is written run_test(); assert_cache(); // 2nd run - check whether cache still the same after, as after the 1st run_test(); assert_cache(); purge_cache(cache_dir.path().to_str().unwrap()); } #[test] fn test_cache_merging() { fn run_test(cache_dir: &str, contract_address: &str, balance: u64) { let mut cached_state = create_fork_cached_state_at(53_680, cache_dir); let _ = cached_state.state.get_block_info().unwrap(); let mut cheatnet_state = CheatnetState::default(); let contract_address = ContractAddress::try_from_hex_str(contract_address).unwrap(); let selector = selector_from_name("get_balance"); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &[Felt::from(balance)]); } let cache_dir = TempDir::new().unwrap(); let contract_1_address = "0x202de98471a4fae6bcbabb96cab00437d381abc58b02509043778074d6781e9"; let contract_2_address = "0x4e6e4924e5db5ffe394484860a8f60e5c292d1937fd80040b312aeea921be11"; let assert_cache = || { // Assertions let cache = read_cache( cache_dir .path() .join(format!("*v{}.json", cache_version())) .to_str() .unwrap(), ); let contract_1_class_hash = "0x6a7eb29ee38b0a0b198e39ed6ad458d2e460264b463351a0acfc05822d61550"; let contract_2_class_hash = "0x2b08ef708af3e6263e02ce541a0099e7c30bac5a8d3d13e42c25c787fa4163"; let balance_storage_address = "0x206f38f7e4f15e87567361213c28f235cccdaa1d7fd34c9db1dfe9489c6a091"; assert_eq!( cache["storage_at"].as_object().unwrap()[contract_1_address] .as_object() .unwrap()[balance_storage_address], "0x0" ); assert_eq!( cache["storage_at"].as_object().unwrap()[contract_2_address] .as_object() .unwrap()[balance_storage_address], "0x0" ); assert_eq!( cache["class_hash_at"].as_object().unwrap()[contract_1_address], contract_1_class_hash ); assert_eq!( cache["class_hash_at"].as_object().unwrap()[contract_2_address], contract_2_class_hash ); match cache["compiled_contract_class"].as_object().unwrap()[contract_1_class_hash] { Value::Object(_) => {} _ => panic!("The compiled_contract_class entry is not an object"), } match cache["compiled_contract_class"].as_object().unwrap()[contract_2_class_hash] { Value::Object(_) => {} _ => panic!("The compiled_contract_class entry is not an object"), } assert_eq!( cache["block_info"].as_object().unwrap()["block_number"] .as_u64() .unwrap(), 53_680 ); assert_eq!( cache["block_info"].as_object().unwrap()["block_timestamp"] .as_u64() .unwrap(), 1_711_554_206 ); assert_eq!( cache["block_info"].as_object().unwrap()["sequencer_address"], "0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8" ); }; let cache_dir_str = cache_dir.path().to_str().unwrap(); run_test(cache_dir_str, contract_1_address, 0); run_test(cache_dir_str, contract_2_address, 0); assert_cache(); purge_cache(cache_dir.path().to_str().unwrap()); // Parallel execution [ (cache_dir_str, contract_1_address, 0), (cache_dir_str, contract_2_address, 0), ] .par_iter() .for_each(|param_tpl| run_test(param_tpl.0, param_tpl.1, param_tpl.2)); assert_cache(); } #[test] fn test_cached_block_info_merging() { fn run_test(cache_dir: &str, balance: u64, call_get_block_info: bool) { let mut cached_state = create_fork_cached_state_at(53_680, cache_dir); if call_get_block_info { let _ = cached_state.state.get_block_info().unwrap(); } let mut cheatnet_state = CheatnetState::default(); let contract_address = ContractAddress::try_from_hex_str( "0x202de98471a4fae6bcbabb96cab00437d381abc58b02509043778074d6781e9", ) .unwrap(); let selector = selector_from_name("get_balance"); let output = call_contract( &mut cached_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_success(output, &[Felt::from(balance)]); } let cache_dir = TempDir::new().unwrap(); let assert_cached_block_info = |is_block_info_cached: bool| { // Assertions let cache = read_cache( cache_dir .path() .join(format!("*v{}.json", cache_version())) .to_str() .unwrap(), ); if is_block_info_cached { assert_eq!( cache["block_info"].as_object().unwrap()["block_number"] .as_u64() .unwrap(), 53_680 ); assert_eq!( cache["block_info"].as_object().unwrap()["block_timestamp"] .as_u64() .unwrap(), 1_711_554_206 ); assert_eq!( cache["block_info"].as_object().unwrap()["sequencer_address"], "0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8" ); } else { assert_eq!(cache["block_info"].as_object(), None); } }; let cache_dir_str = cache_dir.path().to_str().unwrap(); run_test(cache_dir_str, 0, false); assert_cached_block_info(false); run_test(cache_dir_str, 0, true); assert_cached_block_info(true); run_test(cache_dir_str, 0, false); assert_cached_block_info(true); } #[test] fn test_calling_nonexistent_url() { let temp_dir = TempDir::new().unwrap(); let nonexistent_url = "http://nonexistent-node-address.com".parse().unwrap(); let mut cached_fork_state = CachedState::new(ExtendedStateReader { dict_state_reader: build_testing_state(), fork_state_reader: Some( ForkStateReader::new( nonexistent_url, BlockNumber(1), Utf8Path::from_path(temp_dir.path()).unwrap(), ) .unwrap(), ), }); let mut cheatnet_state = CheatnetState::default(); let contract_address = ContractAddress::try_from_hex_str( "0x202de98471a4fae6bcbabb96cab00437d381abc58b02509043778074d6781e9", ) .unwrap(); let selector = selector_from_name("get_balance"); let output = call_contract( &mut cached_fork_state, &mut cheatnet_state, &contract_address, selector, &[], ); assert_error( output, "Unable to reach the node. Check your internet connection and node url", ); } ================================================ FILE: crates/cheatnet/tests/starknet/mod.rs ================================================ // Testing whether Cheatnet's behavior is consistent with Starknet's mod block; mod cheat_fork; mod execution; mod forking; mod nonce; mod timestamp; ================================================ FILE: crates/cheatnet/tests/starknet/nonce.rs ================================================ use crate::common::assertions::ClassHashAssert; use crate::common::{call_contract, deploy}; use crate::{ common::assertions::assert_success, common::{deploy_contract, get_contracts, recover_data, state::create_cached_state}, }; use blockifier::state::state_api::State; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::declare::declare; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::storage::selector_from_name; use cheatnet::state::CheatnetState; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; // We've decided that the nonce should not change in tests // and should remain 0 at all times, this may be revised in the future. fn check_nonce( state: &mut dyn State, cheatnet_state: &mut CheatnetState, contract_address: &ContractAddress, ) -> Felt { let write_nonce = selector_from_name("write_nonce"); let read_nonce = selector_from_name("read_nonce"); let output = call_contract(state, cheatnet_state, contract_address, write_nonce, &[]); assert_success(output, &[]); let output = call_contract(state, cheatnet_state, contract_address, read_nonce, &[]); recover_data(output)[0] } #[test] fn nonce_transactions() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract(&mut cached_state, &mut cheatnet_state, "Noncer", &[]); let old_nonce = check_nonce(&mut cached_state, &mut cheatnet_state, &contract_address); let new_nonce = check_nonce(&mut cached_state, &mut cheatnet_state, &contract_address); assert_eq!(old_nonce, Felt::from(0)); assert_eq!(old_nonce, new_nonce); } #[test] fn nonce_declare_deploy() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract(&mut cached_state, &mut cheatnet_state, "Noncer", &[]); let contracts_data = get_contracts(); let nonce1 = check_nonce(&mut cached_state, &mut cheatnet_state, &contract_address); let class_hash = declare(&mut cached_state, "HelloStarknet", &contracts_data) .unwrap() .unwrap_success(); let nonce2 = check_nonce(&mut cached_state, &mut cheatnet_state, &contract_address); deploy(&mut cached_state, &mut cheatnet_state, &class_hash, &[]); let nonce3 = check_nonce(&mut cached_state, &mut cheatnet_state, &contract_address); assert_eq!(nonce1, Felt::from(0)); assert_eq!(nonce1, nonce2); assert_eq!(nonce2, nonce3); } ================================================ FILE: crates/cheatnet/tests/starknet/timestamp.rs ================================================ use crate::common::call_contract; use crate::{ common::assertions::assert_success, common::{deploy_contract, recover_data, state::create_cached_state}, }; use blockifier::state::state_api::State; use cheatnet::runtime_extensions::forge_runtime_extension::cheatcodes::storage::selector_from_name; use cheatnet::state::CheatnetState; use starknet_api::core::ContractAddress; use starknet_types_core::felt::Felt; fn check_timestamp( state: &mut dyn State, cheatnet_state: &mut CheatnetState, contract_address: &ContractAddress, ) -> Felt { let write_timestamp = selector_from_name("write_timestamp"); let read_timestamp = selector_from_name("read_timestamp"); let output = call_contract( state, cheatnet_state, contract_address, write_timestamp, &[], ); assert_success(output, &[]); let output = call_contract(state, cheatnet_state, contract_address, read_timestamp, &[]); recover_data(output)[0] } #[test] fn timestamp_does_not_decrease() { let mut cached_state = create_cached_state(); let mut cheatnet_state = CheatnetState::default(); let contract_address = deploy_contract(&mut cached_state, &mut cheatnet_state, "Timestamper", &[]); let old_timestamp = check_timestamp(&mut cached_state, &mut cheatnet_state, &contract_address); let new_timestamp = check_timestamp(&mut cached_state, &mut cheatnet_state, &contract_address); assert!(old_timestamp <= new_timestamp); } ================================================ FILE: crates/configuration/Cargo.toml ================================================ [package] name = "configuration" version = "1.0.0" edition.workspace = true [features] testing = [] [dependencies] anyhow.workspace = true serde_json.workspace = true serde.workspace = true camino.workspace = true toml.workspace = true tempfile.workspace = true [dev-dependencies] url.workspace = true ================================================ FILE: crates/configuration/src/core.rs ================================================ use crate::Config; use anyhow::anyhow; use serde_json::Number; use std::env; fn resolve_env_variables(config: serde_json::Value) -> anyhow::Result { match config { serde_json::Value::Object(map) => { let val = map .into_iter() .map(|(k, v)| -> anyhow::Result<(String, serde_json::Value)> { Ok((k, resolve_env_variables(v)?)) }) .collect::>>()?; Ok(serde_json::Value::Object(val)) } serde_json::Value::Array(val) => { let val = val .into_iter() .map(resolve_env_variables) .collect::>>()?; Ok(serde_json::Value::Array(val)) } serde_json::Value::String(val) if val.starts_with('$') => resolve_env_variable(&val), val => Ok(val), } } fn resolve_env_variable(var: &str) -> anyhow::Result { assert!(var.starts_with('$')); let mut initial_value = &var[1..]; if initial_value.starts_with('{') && initial_value.ends_with('}') { initial_value = &initial_value[1..initial_value.len() - 1]; } let value = env::var(initial_value)?; if let Ok(value) = value.parse::() { return Ok(serde_json::Value::Number(value)); } if let Ok(value) = value.parse::() { return Ok(serde_json::Value::Bool(value)); } Ok(serde_json::Value::String(value)) } fn get_with_ownership(config: serde_json::Value, key: &str) -> Option { match config { serde_json::Value::Object(mut map) => map.remove(key), _ => None, } } fn get_profile( raw_config: serde_json::Value, tool: &str, profile: &str, ) -> Option { let profile_name = profile; let tool_config = get_with_ownership(raw_config, tool) .unwrap_or(serde_json::Value::Object(serde_json::Map::new())); get_with_ownership(tool_config, profile_name) } pub enum Profile { None, Default, Some(String), } pub fn load_config( raw_config: serde_json::Value, profile: Profile, ) -> anyhow::Result { let raw_config_json = match profile { Profile::None => raw_config, Profile::Default => get_profile(raw_config, T::tool_name(), "default") .unwrap_or_else(|| serde_json::Value::Object(serde_json::Map::new())), Profile::Some(profile) => get_profile(raw_config, T::tool_name(), &profile) .ok_or_else(|| anyhow!("Profile [{profile}] not found in config"))?, }; T::from_raw(resolve_env_variables(raw_config_json)?) } ================================================ FILE: crates/configuration/src/lib.rs ================================================ use crate::core::Profile; use anyhow::{Context, Result, anyhow}; use camino::Utf8PathBuf; use std::fs::File; use std::{env, fs}; use toml::Table; pub mod core; pub mod test_utils; pub const CONFIG_FILENAME: &str = "snfoundry.toml"; /// Configuration not associated with any specific package pub trait Config { #[must_use] fn tool_name() -> &'static str; fn from_raw(config: serde_json::Value) -> Result where Self: Sized; } #[must_use] pub fn resolve_config_file() -> Utf8PathBuf { find_config_file().unwrap_or_else(|_| { let path = Utf8PathBuf::from(CONFIG_FILENAME); File::create(&path).expect("creating file in current directory should be possible"); path.canonicalize_utf8() .expect("path canonicalize in current directory should be possible") }) } pub fn load_config( path: Option<&Utf8PathBuf>, profile: Option<&str>, ) -> Result { let config_path = path .as_ref() .and_then(|p| search_config_upwards_relative_to(p).ok()) .or_else(|| find_config_file().ok()); match config_path { Some(path) => { let raw_config_toml = fs::read_to_string(path) .context("Failed to read snfoundry.toml config file")? .parse::() .context("Failed to parse snfoundry.toml config file")?; let raw_config_json = serde_json::to_value(raw_config_toml) .context("Conversion from TOML value to JSON value should not fail.")?; core::load_config( raw_config_json, profile.map_or_else(|| Profile::Default, |p| Profile::Some(p.to_string())), ) } None => Ok(T::default()), } } pub fn search_config_upwards_relative_to(current_dir: &Utf8PathBuf) -> Result { current_dir .ancestors() .find(|path| fs::metadata(path.join(CONFIG_FILENAME)).is_ok()) .map(|path| path.join(CONFIG_FILENAME)) .ok_or_else(|| { anyhow!( "Failed to find snfoundry.toml - not found in current nor any parent directories" ) }) } pub fn find_config_file() -> Result { search_config_upwards_relative_to(&Utf8PathBuf::try_from( env::current_dir().expect("Failed to get current directory"), )?) } #[cfg(test)] mod tests { use super::*; use crate::test_utils::copy_config_to_tempdir; use serde::{Deserialize, Serialize}; use std::fs::{self, File}; use tempfile::tempdir; use url::Url; #[test] fn find_config_in_current_dir() { let tempdir = copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", None); let path = search_config_upwards_relative_to( &Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap(), ) .unwrap(); assert_eq!(path, tempdir.path().join(CONFIG_FILENAME)); } #[test] fn find_config_in_parent_dir() { let tempdir = copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", Some("childdir")); let path = search_config_upwards_relative_to( &Utf8PathBuf::try_from(tempdir.path().to_path_buf().join("childdir")).unwrap(), ) .unwrap(); assert_eq!(path, tempdir.path().join(CONFIG_FILENAME)); } #[test] fn find_config_in_parent_dir_two_levels() { let tempdir = copy_config_to_tempdir( "tests/data/stubtool_snfoundry.toml", Some("childdir1/childdir2"), ); let path = search_config_upwards_relative_to( &Utf8PathBuf::try_from(tempdir.path().to_path_buf().join("childdir1/childdir2")) .unwrap(), ) .unwrap(); assert_eq!(path, tempdir.path().join(CONFIG_FILENAME)); } #[test] fn find_config_in_parent_dir_available_in_multiple_parents() { let tempdir = copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", Some("childdir1")); fs::copy( "tests/data/stubtool_snfoundry.toml", tempdir.path().join("childdir1").join(CONFIG_FILENAME), ) .expect("Failed to copy config file to temp dir"); let path = search_config_upwards_relative_to( &Utf8PathBuf::try_from(tempdir.path().to_path_buf().join("childdir1")).unwrap(), ) .unwrap(); assert_eq!(path, tempdir.path().join("childdir1").join(CONFIG_FILENAME)); } #[test] fn no_config_in_current_nor_parent_dir() { let tempdir = tempdir().expect("Failed to create a temporary directory"); assert!( search_config_upwards_relative_to( &Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap() ) .is_err(), "Failed to find snfoundry.toml - not found in current nor any parent directories" ); } #[derive(Debug, Default, Serialize, Deserialize)] pub struct StubConfig { pub url: Option, #[serde(default)] pub account: String, } impl Config for StubConfig { fn tool_name() -> &'static str { "stubtool" } fn from_raw(config: serde_json::Value) -> Result { Ok(serde_json::from_value::(config)?) } } #[test] fn load_config_happy_case_with_profile() { let tempdir = copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", None); let config = load_config::( Some(&Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()), Some(&String::from("profile1")), ) .unwrap(); assert_eq!(config.account, String::from("user3")); assert_eq!( config.url, Some(Url::parse("http://127.0.0.1:5050/rpc").unwrap()) ); } #[test] fn load_config_happy_case_default_profile() { let tempdir = copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", None); let config = load_config::( Some(&Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()), None, ) .unwrap(); assert_eq!(config.account, String::from("user1")); assert_eq!( config.url, Some(Url::parse("http://127.0.0.1:5055/rpc").unwrap()) ); } #[test] fn load_config_invalid_url() { let tempdir = copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", None); let err = load_config::( Some(&Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()), Some(&String::from("profile6")), ) .unwrap_err(); assert!( err.to_string() .contains("relative URL without a base: \"invalid_url\"") ); } #[test] fn load_config_not_found() { let tempdir = tempdir().expect("Failed to create a temporary directory"); let config = load_config::( Some(&Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()), None, ) .unwrap(); assert_eq!(config.account, String::new()); assert_eq!(config.url, None); } #[derive(Debug, Default, Serialize, Deserialize)] pub struct StubComplexConfig { #[serde(default)] pub url: String, #[serde(default)] pub account: i32, #[serde(default)] pub nested: StubComplexConfigNested, } #[derive(Debug, Default, Serialize, Deserialize)] pub struct StubComplexConfigNested { #[serde( default, rename(serialize = "list-example", deserialize = "list-example") )] list_example: Vec, #[serde(default, rename(serialize = "url-nested", deserialize = "url-nested"))] url_nested: f32, #[serde(default, rename(serialize = "url-alt", deserialize = "url-alt"))] url_alt: String, } impl Config for StubComplexConfig { fn tool_name() -> &'static str { "stubtool" } fn from_raw(config: serde_json::Value) -> Result { Ok(serde_json::from_value::(config)?) } } #[test] fn empty_config_works() { let temp_dir = tempdir().expect("Failed to create a temporary directory"); File::create(temp_dir.path().join(CONFIG_FILENAME)).unwrap(); load_config::( Some(&Utf8PathBuf::try_from(temp_dir.path().to_path_buf()).unwrap()), None, ) .unwrap(); } #[test] #[expect(clippy::float_cmp)] fn resolve_env_vars() { let tempdir = copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", Some("childdir1")); fs::copy( "tests/data/stubtool_snfoundry.toml", tempdir.path().join("childdir1").join(CONFIG_FILENAME), ) .expect("Failed to copy config file to temp dir"); // missing env variables if load_config::( Some(&Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()), Some(&String::from("with-envs")), ) .is_ok() { panic!("Expected failure"); } // Present env variables // SAFETY: These values are only read here and are not modified by other tests. unsafe { env::set_var("VALUE_STRING123132", "nfsaufbnsailfbsbksdabfnkl"); env::set_var("VALUE_STRING123142", "nfsasnsidnnsailfbsbksdabdkdkl"); env::set_var("VALUE_INT123132", "321312"); env::set_var("VALUE_FLOAT123132", "321.312"); env::set_var("VALUE_BOOL1231321", "true"); env::set_var("VALUE_BOOL1231322", "false"); }; let config = load_config::( Some(&Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()), Some(&String::from("with-envs")), ) .unwrap(); assert_eq!(config.url, String::from("nfsaufbnsailfbsbksdabfnkl")); assert_eq!(config.account, 321_312); assert_eq!(config.nested.list_example, vec![true, false]); assert_eq!(config.nested.url_nested, 321.312); assert_eq!( config.nested.url_alt, String::from("nfsasnsidnnsailfbsbksdabdkdkl") ); } } ================================================ FILE: crates/configuration/src/test_utils.rs ================================================ use crate::CONFIG_FILENAME; use std::fs; use tempfile::{TempDir, tempdir}; #[must_use] pub fn copy_config_to_tempdir(src_path: &str, additional_path: Option<&str>) -> TempDir { let temp_dir = tempdir().expect("Failed to create a temporary directory"); if let Some(dir) = additional_path { let path = temp_dir.path().join(dir); fs::create_dir_all(path).expect("Failed to create directories in temp dir"); } let temp_dir_file_path = temp_dir.path().join(CONFIG_FILENAME); fs::copy(src_path, temp_dir_file_path).expect("Failed to copy config file to temp dir"); temp_dir } ================================================ FILE: crates/configuration/tests/data/stubtool_snfoundry.toml ================================================ [stubtool.default] url = "http://127.0.0.1:5055/rpc" accounts-file = "../account-file" account = "user1" [stubtool.profile1] url = "http://127.0.0.1:5050/rpc" account = "user3" [stubtool.profile2] url = "http://127.0.0.1:5055/rpc" accounts-file = "../account-file" account = "user100" [stubtool.profile3] url = "http://127.0.0.1:5055/rpc" account = "/path/to/account.json" keystore = "../keystore" [stubtool.profile4] url = "http://127.0.0.1:5055/rpc" accounts-file = "../account-file" account = "user3" [stubtool.profile5] url = "http://127.0.0.1:5055/rpc" [stubtool.profile6] url = "invalid_url" account = "user8" [stubtool.with-envs] url = "$VALUE_STRING123132" account = "$VALUE_INT123132" [stubtool.with-envs.nested] list-example = [ "$VALUE_BOOL1231321", "$VALUE_BOOL1231322" ] url-nested = "$VALUE_FLOAT123132" url-alt = "${VALUE_STRING123142}" ================================================ FILE: crates/conversions/Cargo.toml ================================================ [package] name = "conversions" version = "1.0.0" edition.workspace = true [features] testing = [] [dependencies] anyhow.workspace = true blockifier.workspace = true starknet_api.workspace = true starknet-types-core.workspace = true cairo-lang-utils.workspace = true cairo-vm.workspace = true starknet-rust.workspace = true thiserror.workspace = true serde_json.workspace = true serde.workspace = true num-traits.workspace = true itertools.workspace = true cairo-serde-macros = { path = "cairo-serde-macros" } [dev-dependencies] test-case.workspace = true ================================================ FILE: crates/conversions/cairo-serde-macros/Cargo.toml ================================================ [package] name = "cairo-serde-macros" version = "1.0.0" edition.workspace = true [lib] proc-macro = true [dependencies] syn = "2.0.114" quote = "1.0.45" proc-macro2 = "1.0.105" ================================================ FILE: crates/conversions/cairo-serde-macros/src/cairo_deserialize.rs ================================================ use proc_macro2::TokenStream; use quote::{quote, quote_spanned}; use syn::spanned::Spanned; use syn::{Data, DeriveInput, Fields, GenericParam, Generics, parse_macro_input, parse_quote}; // works by calling `CairoDeserialize::deserialize(reader)` on all fields of struct // for enums by reading 1 felt that is then matched on to determine which variant should be used pub fn derive_deserialize(item: proc_macro::TokenStream) -> proc_macro::TokenStream { let span = item.clone().into(); let mut input = parse_macro_input!(item as DeriveInput); let name = input.ident; let generics = &mut input.generics; let data = &input.data; add_trait_bounds(generics); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let body = create_func_body(data, &span); quote! { impl #impl_generics conversions::serde::deserialize::CairoDeserialize for #name #ty_generics #where_clause { fn deserialize(reader: &mut conversions::serde::deserialize::BufferReader<'_>) -> conversions::serde::deserialize::BufferReadResult { #body } } } .into() } fn add_trait_bounds(generics: &mut Generics) { for param in &mut generics.params { if let GenericParam::Type(type_param) = param { type_param.bounds.push(parse_quote!( conversions::serde::deserialize::CairoDeserialize )); } } } // generate code for struct/enum fields (named and tuple) fn call_trait_on_field(fields: &Fields) -> TokenStream { match fields { Fields::Named(fields) => { let recurse = fields.named.iter().map(|f| { let name = &f.ident; quote_spanned! {f.span() => #name: conversions::serde::deserialize::CairoDeserialize::deserialize(reader)?, } }); quote! { {#(#recurse)*} } } Fields::Unnamed(fields) => { let recurse = fields.unnamed.iter().map(|f| { quote_spanned! {f.span()=> conversions::serde::deserialize::CairoDeserialize::deserialize(reader)? } }); quote! { (#(#recurse),*) } } Fields::Unit => TokenStream::new(), } } // creates code for `CairoDeserialize::deserialize` body fn create_func_body(data: &Data, span: &TokenStream) -> TokenStream { match data { Data::Struct(data) => match &data.fields { Fields::Named(_) | Fields::Unnamed(_) => { let fields = call_trait_on_field(&data.fields); quote! { Result::Ok(Self #fields ) } } Fields::Unit => { quote!(Result::Ok(Self)) } }, Data::Enum(data) => { // generate match arms by matching on next integer literals (discriminator) // then generate trait calls for variants fields let arms = data.variants.iter().enumerate().map(|(i, variant)| { let name = &variant.ident; let fields = call_trait_on_field(&variant.fields); let lit = syn::parse_str::(&i.to_string()).unwrap(); quote! { #lit => Self::#name #fields } }); quote! { let variant: usize = reader.read()?; let this = match variant { #(#arms,)* _ => Result::Err(conversions::serde::deserialize::BufferReadError::ParseFailed)?, }; Result::Ok(this) } } // can not determine which variant should be used // use enum instead Data::Union(_) => syn::Error::new_spanned( span, "conversions::serde::deserialize::CairoDeserialize can be derived only on structs and enums", ) .into_compile_error(), } } ================================================ FILE: crates/conversions/cairo-serde-macros/src/cairo_serialize.rs ================================================ use proc_macro2::TokenStream; use quote::{ToTokens, quote, quote_spanned}; use syn::spanned::Spanned; use syn::{Data, DeriveInput, Fields, GenericParam, Generics, parse_macro_input, parse_quote}; // works by calling `CairoSerialize::serialize(writer)` on all fields of struct // for enums by writing 1 felt that is number of variant, then variant members pub fn derive_serialize(item: proc_macro::TokenStream) -> proc_macro::TokenStream { let span = item.clone().into(); let mut input = parse_macro_input!(item as DeriveInput); let name = input.ident; let generics = &mut input.generics; let data = &input.data; add_trait_bounds(generics); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let body = create_func_body(data, &span); quote! { impl #impl_generics conversions::serde::serialize::CairoSerialize for #name #ty_generics #where_clause { fn serialize(&self, writer: &mut conversions::serde::serialize::BufferWriter) { #body } } } .into() } fn add_trait_bounds(generics: &mut Generics) { for param in &mut generics.params { if let GenericParam::Type(type_param) = param { type_param .bounds .push(parse_quote!(conversions::serde::serialize::CairoSerialize)); } } } #[derive(Copy, Clone)] enum Item { Struct, Enum, } impl Item { fn get_prefix(self) -> TokenStream { match self { Self::Struct => quote! (self.), Self::Enum => quote!(), } } } // generate code for struct/enum fields (named and tuple) fn call_trait_on_field(fields: &Fields, item: Item) -> TokenStream { let prefix = item.get_prefix(); match fields { Fields::Named(fields) => { let recurse = fields.named.iter().map(|f| { let name = &f.ident; quote_spanned! {f.span() => conversions::serde::serialize::CairoSerialize::serialize(& #prefix #name, writer); } }); quote! { {#(#recurse)*} } } Fields::Unnamed(unnamed_fields) => { let recurse = unnamed_fields.unnamed.iter().enumerate().map(|(i, f)| { let name = match item { Item::Struct => { let prop = syn::parse_str::(&i.to_string()).unwrap(); quote! { #prefix #prop } } Item::Enum => syn::parse_str::(&format!("field_{i}")) .unwrap() .to_token_stream(), }; quote_spanned! {f.span()=> conversions::serde::serialize::CairoSerialize::serialize(& #name, writer); } }); quote! { #(#recurse),* } } Fields::Unit => TokenStream::new(), } } fn destruct_fields(fields: &Fields) -> TokenStream { match fields { Fields::Named(fields) => { let recurse = fields.named.iter().map(|f| { let name = &f.ident; quote_spanned! {f.span() => #name } }); quote! { {#(#recurse),*} } } Fields::Unnamed(fields) => { let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| { let name = syn::parse_str::(&format!("field_{i}")).unwrap(); quote_spanned! {f.span() => #name } }); quote! { (#(#recurse),*) } } Fields::Unit => TokenStream::new(), } } // creates code for `CairoSerialize::serialize` body fn create_func_body(data: &Data, span: &TokenStream) -> TokenStream { match data { Data::Struct(data) => { call_trait_on_field(&data.fields, Item::Struct) }, Data::Enum(data) => { // generate match arms by matching on next integer literals (discriminator) // then generate trait calls for variants fields let arms = data.variants.iter().enumerate().map(|(i,variant)| { let name = &variant.ident; let calls = call_trait_on_field(&variant.fields, Item::Enum); let destructurization = destruct_fields(&variant.fields); let lit = syn::parse_str::(&format!("{i}_u32")).unwrap(); quote! { Self::#name #destructurization => { conversions::serde::serialize::CairoSerialize::serialize(&#lit, writer); #calls } } }); // empty match does not work with references // and `self` is behind reference if data.variants.is_empty() { quote! {} }else{ quote! { match self { #(#arms,)* }; } } } // can not determine which variant should be used // use enum instead Data::Union(_) => syn::Error::new_spanned( span, "conversions::serde::serialize::CairoSerialize can be derived only on structs and enums", ) .into_compile_error(), } } ================================================ FILE: crates/conversions/cairo-serde-macros/src/lib.rs ================================================ mod cairo_deserialize; mod cairo_serialize; #[proc_macro_derive(CairoDeserialize)] pub fn derive_deserialize(item: proc_macro::TokenStream) -> proc_macro::TokenStream { cairo_deserialize::derive_deserialize(item) } #[proc_macro_derive(CairoSerialize)] pub fn derive_serialize(item: proc_macro::TokenStream) -> proc_macro::TokenStream { cairo_serialize::derive_serialize(item) } ================================================ FILE: crates/conversions/src/byte_array.rs ================================================ use crate as conversions; // trick for CairoDeserialize macro use crate::serde::deserialize::{BufferReadError, BufferReadResult, BufferReader}; use crate::{serde::serialize::SerializeToFeltVec, string::TryFromHexStr}; use cairo_lang_utils::byte_array::{BYTE_ARRAY_MAGIC, BYTES_IN_WORD}; use cairo_serde_macros::{CairoDeserialize, CairoSerialize}; use starknet_types_core::felt::Felt; use std::fmt; #[derive(CairoDeserialize, CairoSerialize, Clone, Debug, PartialEq)] pub struct ByteArray { words: Vec, pending_word: Felt, pending_word_len: usize, } impl From<&str> for ByteArray { fn from(value: &str) -> Self { let chunks = value.as_bytes().chunks_exact(BYTES_IN_WORD); let remainder = chunks.remainder(); let pending_word_len = remainder.len(); let words = chunks.map(Felt::from_bytes_be_slice).collect(); let pending_word = Felt::from_bytes_be_slice(remainder); Self { words, pending_word, pending_word_len, } } } impl ByteArray { #[must_use] pub fn serialize_with_magic(&self) -> Vec { let mut result = self.serialize_to_vec(); result.insert( 0, Felt::try_from_hex_str(&format!("0x{BYTE_ARRAY_MAGIC}")).unwrap(), ); result } pub fn deserialize_with_magic(value: &[Felt]) -> BufferReadResult { if value.first() == Some(&Felt::try_from_hex_str(&format!("0x{BYTE_ARRAY_MAGIC}")).unwrap()) { BufferReader::new(&value[1..]).read() } else { Err(BufferReadError::ParseFailed) } } } fn extend_full_word_bytes(out: &mut Vec, word: &Felt) { let buf = word.to_bytes_be(); out.extend_from_slice(&buf[1..32]); } fn extend_pending_word_bytes(out: &mut Vec, word: &Felt, len: usize) { let buf = word.to_bytes_be(); out.extend_from_slice(&buf[(32 - len)..32]); } impl fmt::Display for ByteArray { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut bytes = Vec::new(); for word in &self.words { extend_full_word_bytes(&mut bytes, word); } extend_pending_word_bytes(&mut bytes, &self.pending_word, self.pending_word_len); for b in bytes { match b { // Printable ASCII characters 0x20..=0x7E => write!(f, "{}", b as char)?, // Common whitespace characters b'\n' => writeln!(f)?, b'\r' => write!(f, "\r")?, b'\t' => write!(f, "\t")?, // Escape all other bytes to avoid panics (important for fuzz tests) _ => write!(f, "\\x{b:02x}")?, } } Ok(()) } } #[cfg(test)] mod tests { use super::*; use test_case::test_case; #[test] fn test_fmt_empty() { let array = ByteArray::from(""); assert_eq!(array.to_string(), ""); } #[test] fn test_fmt_single_word() { let array = ByteArray::from("Hello"); assert_eq!(array.to_string(), "Hello"); } #[test] fn test_fmt_multiple_words() { let array = ByteArray::from("Hello World! This is a test."); assert_eq!(array.to_string(), "Hello World! This is a test."); } #[test] fn test_fmt_with_pending_word() { let array = ByteArray::from("abc"); assert_eq!(array.to_string(), "abc"); } #[test] fn test_fmt_special_chars() { let special_chars = "!@#$%^&*()_+-=[]{}|;:,.<>?"; let array = ByteArray::from(special_chars); assert_eq!(array.to_string(), special_chars); } #[test_case("Hello\0World", "Hello\\x00World"; "single null byte")] #[test_case("\0\0", "\\x00\\x00"; "two null bytes")] #[test_case("\x01\x02ABC", "\\x01\\x02ABC"; "control chars 0x01 0x02")] #[test_case("\x07Bell", "\\x07Bell"; "bell character")] #[test_case("\x1fEnd", "\\x1fEnd"; "unit separator")] #[test_case("Line1\nLine2", "Line1\nLine2"; "newline preserved")] #[test_case("Col1\tCol2", "Col1\tCol2"; "tab preserved")] #[test_case("CR\rLF", "CR\rLF"; "carriage return preserved")] #[test_case("A\x00B\x01C", "A\\x00B\\x01C"; "mixed printable and escaped")] #[test_case("\x7f", "\\x7f"; "delete character")] fn test_fmt_escaping_non_printable_bytes(input: &str, expected: &str) { let array = ByteArray::from(input); let output = array.to_string(); assert_eq!(output, expected); } #[test] fn test_fmt_mixed_ascii() { let mixed = "Hello\tWorld\n123 !@#"; let array = ByteArray::from(mixed); assert_eq!(array.to_string(), mixed); } #[test] fn test_fmt_with_newlines() { let with_newlines = "First line\nSecond line\r\nThird line"; let array = ByteArray::from(with_newlines); assert_eq!(array.to_string(), with_newlines); } #[test] fn test_fmt_multiple_newlines() { let multiple_newlines = "Line1\n\n\nLine2\n\nLine3"; let array = ByteArray::from(multiple_newlines); assert_eq!(array.to_string(), multiple_newlines); } } ================================================ FILE: crates/conversions/src/class_hash.rs ================================================ use crate::{FromConv, IntoConv, from_thru_felt}; use conversions::padded_felt::PaddedFelt; use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce}; use starknet_types_core::felt::Felt; impl FromConv for ClassHash { fn from_(value: Felt) -> ClassHash { ClassHash(value.into_()) } } from_thru_felt!(ContractAddress, ClassHash); from_thru_felt!(Nonce, ClassHash); from_thru_felt!(EntryPointSelector, ClassHash); from_thru_felt!(PaddedFelt, ClassHash); ================================================ FILE: crates/conversions/src/contract_address.rs ================================================ use crate::{FromConv, from_thru_felt}; use conversions::padded_felt::PaddedFelt; use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce, PatriciaKey}; use starknet_api::hash::StarkHash; use starknet_types_core::felt::Felt; impl FromConv for ContractAddress { fn from_(value: Felt) -> ContractAddress { ContractAddress(PatriciaKey::try_from(StarkHash::from_(value)).unwrap()) } } from_thru_felt!(ClassHash, ContractAddress); from_thru_felt!(Nonce, ContractAddress); from_thru_felt!(EntryPointSelector, ContractAddress); from_thru_felt!(PaddedFelt, ContractAddress); ================================================ FILE: crates/conversions/src/entrypoint_selector.rs ================================================ use crate::{FromConv, IntoConv, from_thru_felt}; use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce}; use starknet_types_core::felt::Felt; impl FromConv for EntryPointSelector { fn from_(value: Felt) -> EntryPointSelector { EntryPointSelector(value.into_()) } } from_thru_felt!(ContractAddress, EntryPointSelector); from_thru_felt!(Nonce, EntryPointSelector); from_thru_felt!(ClassHash, EntryPointSelector); ================================================ FILE: crates/conversions/src/eth_address.rs ================================================ use crate::FromConv; use starknet_api::core::EthAddress; use starknet_types_core::felt::Felt; impl FromConv for EthAddress { fn from_(value: Felt) -> EthAddress { EthAddress::try_from(value).expect("Conversion of felt to EthAddress failed") } } impl FromConv for Felt { fn from_(value: EthAddress) -> Felt { value.into() } } ================================================ FILE: crates/conversions/src/felt.rs ================================================ use crate::{ FromConv, IntoConv, byte_array::ByteArray, serde::serialize::SerializeToFeltVec, string::{TryFromDecStr, TryFromHexStr}, }; use anyhow::{Context, Result, anyhow, bail}; use conversions::padded_felt::PaddedFelt; use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce}; use starknet_api::transaction::fields::ContractAddressSalt; use starknet_types_core::felt::Felt; use std::vec; impl FromConv for Felt { fn from_(value: ClassHash) -> Felt { value.0.into_() } } impl FromConv for Felt { fn from_(value: ContractAddress) -> Felt { (*value.0.key()).into_() } } impl FromConv for Felt { fn from_(value: ContractAddressSalt) -> Felt { value.0.into_() } } impl FromConv for Felt { fn from_(value: Nonce) -> Felt { value.0.into_() } } impl FromConv for Felt { fn from_(value: EntryPointSelector) -> Felt { value.0.into_() } } impl FromConv for Felt { fn from_(value: PaddedFelt) -> Felt { value.0.into_() } } impl TryFromDecStr for T where T: FromConv, { fn try_from_dec_str(value: &str) -> Result { if value.starts_with('-') { bail!("Value must not start with -") } Felt::from_dec_str(value) .map(T::from_) .with_context(|| anyhow!("Invalid value for string")) } } impl TryFromHexStr for T where T: FromConv, { fn try_from_hex_str(value: &str) -> Result { if !value.starts_with("0x") { bail!("Value must start with 0x"); } Felt::from_hex(value) .map(T::from_) .with_context(|| anyhow!("Invalid value for string")) } } pub trait FromShortString: Sized { fn from_short_string(short_string: &str) -> Result; } impl FromShortString for Felt { fn from_short_string(short_string: &str) -> Result { if short_string.len() <= 31 && short_string.is_ascii() { Ok(Felt::from_bytes_be_slice(short_string.as_bytes())) } else { bail!("Value must be ascii and less than 32 bytes long.") } } } #[derive(Debug)] pub struct ToStrErr; pub trait ToShortString: Sized { fn to_short_string(&self) -> Result; } impl ToShortString for Felt { fn to_short_string(&self) -> Result { let mut as_string = String::default(); let mut is_end = false; for byte in self.to_biguint().to_bytes_be() { if byte == 0 { is_end = true; } else if is_end { return Err(ToStrErr); } else if byte.is_ascii_graphic() || byte.is_ascii_whitespace() { as_string.push(byte as char); } else { return Err(ToStrErr); } } Ok(as_string) } } pub trait TryInferFormat: Sized { /// Parses value from `hex string`, `dec string`, `quoted cairo shortstring `and `quoted cairo string` fn infer_format_and_parse(value: &str) -> Result>; } fn resolve(value: &str) -> String { value[1..value.len() - 1].replace("\\n", "\n") } impl TryInferFormat for Felt { fn infer_format_and_parse(value: &str) -> Result> { if value.starts_with('\'') && value.ends_with('\'') { let value = resolve(value).replace("\\'", "'"); Felt::from_short_string(&value).map(|felt| vec![felt]) } else if value.starts_with('"') && value.ends_with('"') { let value = resolve(value).replace("\\\"", "\""); Ok(ByteArray::from(value.as_str()).serialize_to_vec()) } else { Felt::try_from_hex_str(value) .or_else(|_| Felt::try_from_dec_str(value)) .map(|felt| vec![felt]) } } } #[cfg(test)] mod tests { use super::*; #[test] fn short_string_happy_case() { let felt = Felt::from_hex("0x616263646566").unwrap(); assert_eq!(felt.to_short_string().unwrap(), "abcdef"); } #[test] fn short_string_31_characters() { let felt = Felt::from_hex("0x4142434445464748494a4b4c4d4e4f505152535455565758595a3132333435") .unwrap(); assert_eq!( felt.to_short_string().unwrap(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ12345" ); } #[test] fn short_string_too_long() { let felt = Felt::from_hex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") .unwrap(); assert!(felt.to_short_string().is_err()); } #[test] fn short_string_empty() { let felt = Felt::from_hex("0x0").unwrap(); assert_eq!(felt.to_short_string().unwrap(), ""); } #[test] fn short_string_with_whitespace() { let felt = Felt::from_hex("0x48656C6C6F20576F726C64").unwrap(); assert_eq!(felt.to_short_string().unwrap(), "Hello World"); } #[test] fn short_string_special_chars() { let felt = Felt::from_hex("0x4021233F2A2B5B5D").unwrap(); assert_eq!(felt.to_short_string().unwrap(), "@!#?*+[]"); } #[test] fn short_string_with_numbers() { let felt = Felt::from_hex("0x313233343536373839").unwrap(); assert_eq!(felt.to_short_string().unwrap(), "123456789"); } #[test] fn short_string_non_ascii() { let felt = Felt::from_hex("0x80").unwrap(); assert!(felt.to_short_string().is_err()); } #[test] fn short_string_null_byte() { let felt = Felt::from_hex("0x00616263").unwrap(); assert_eq!(felt.to_short_string().unwrap(), "abc"); } #[test] fn short_string_null_byte_middle() { let felt = Felt::from_hex("0x61006263").unwrap(); assert!(felt.to_short_string().is_err()); } #[test] fn short_string_null_byte_end() { let felt = Felt::from_hex("0x61626300").unwrap(); assert_eq!(felt.to_short_string().unwrap(), "abc"); } } ================================================ FILE: crates/conversions/src/lib.rs ================================================ use std::convert::Infallible; pub mod byte_array; pub mod class_hash; pub mod contract_address; pub mod entrypoint_selector; pub mod eth_address; pub mod felt; pub mod non_zero_felt; pub mod non_zero_u128; pub mod non_zero_u64; pub mod nonce; pub mod padded_felt; pub mod primitive; pub mod serde; pub mod string; extern crate self as conversions; pub trait FromConv: Sized { fn from_(value: T) -> Self; } impl FromConv for T { fn from_(value: T) -> Self { value } } pub trait IntoConv: Sized { fn into_(self) -> T; } // FromConv implies IntoConv impl IntoConv for T where U: FromConv, { #[inline] fn into_(self: T) -> U { U::from_(self) } } pub trait TryFromConv: Sized { type Error; fn try_from_(value: T) -> Result; } pub trait TryIntoConv: Sized { type Error; fn try_into_(self) -> Result; } // TryFromConv implies TryIntoConv impl TryIntoConv for T where U: TryFromConv, { type Error = U::Error; #[inline] fn try_into_(self) -> Result { U::try_from_(self) } } // Infallible conversions are semantically equivalent to fallible conversions // with an uninhabited error type. impl TryFromConv for T where U: IntoConv, { type Error = Infallible; #[inline] fn try_from_(value: U) -> Result { Ok(U::into_(value)) } } #[macro_export] macro_rules! from_thru_felt { ($from:ty, $to:ty) => { impl FromConv<$from> for $to { fn from_(value: $from) -> Self { Self::from_(Felt::from_(value)) } } }; } ================================================ FILE: crates/conversions/src/non_zero_felt.rs ================================================ use crate::FromConv; use starknet_types_core::felt::{Felt, NonZeroFelt}; use std::num::{NonZeroU64, NonZeroU128}; impl FromConv for NonZeroFelt { fn from_(value: NonZeroU64) -> Self { NonZeroFelt::try_from(Felt::from(value.get())).unwrap_or_else(|_| { unreachable!( "NonZeroU64 is always greater than 0, so it should be convertible to NonZeroFelt" ) }) } } impl FromConv for NonZeroFelt { fn from_(value: NonZeroU128) -> Self { NonZeroFelt::try_from(Felt::from(value.get())).unwrap_or_else(|_| { unreachable!( "NonZeroU128 is always greater than 0, so it should be convertible to NonZeroFelt" ) }) } } ================================================ FILE: crates/conversions/src/non_zero_u128.rs ================================================ use crate::TryFromConv; use starknet_types_core::felt::{Felt, NonZeroFelt}; use std::num::{NonZero, NonZeroU128}; impl TryFromConv for NonZeroU128 { type Error = String; fn try_from_(value: NonZeroFelt) -> Result { let value: u128 = Felt::from(value) .try_into() .map_err(|_| "felt was too large to fit in u128")?; Ok(NonZero::new(value) .unwrap_or_else(|| unreachable!("non zero felt is always greater than 0"))) } } ================================================ FILE: crates/conversions/src/non_zero_u64.rs ================================================ use crate::TryFromConv; use starknet_types_core::felt::{Felt, NonZeroFelt}; use std::num::{NonZero, NonZeroU64}; impl TryFromConv for NonZeroU64 { type Error = String; fn try_from_(value: NonZeroFelt) -> Result { let value: u64 = Felt::from(value) .try_into() .map_err(|_| "felt was too large to fit in u64")?; Ok(NonZero::new(value) .unwrap_or_else(|| unreachable!("non zero felt is always greater than 0"))) } } ================================================ FILE: crates/conversions/src/nonce.rs ================================================ use crate::{FromConv, IntoConv, from_thru_felt}; use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce}; use starknet_types_core::felt::Felt; impl FromConv for Nonce { fn from_(value: Felt) -> Nonce { Nonce(value.into_()) } } from_thru_felt!(ClassHash, Nonce); from_thru_felt!(ContractAddress, Nonce); from_thru_felt!(EntryPointSelector, Nonce); ================================================ FILE: crates/conversions/src/padded_felt.rs ================================================ use crate::FromConv; use cairo_serde_macros::CairoSerialize; use conversions::from_thru_felt; use serde::{Deserialize, Serialize, Serializer}; use starknet_api::core::{ClassHash, ContractAddress}; use starknet_types_core::felt::Felt; use std::fmt; use std::fmt::{Formatter, LowerHex}; #[derive(Clone, Copy, Debug, PartialEq, Deserialize, CairoSerialize)] pub struct PaddedFelt(pub Felt); impl FromConv for PaddedFelt { fn from_(value: Felt) -> Self { Self(value) } } impl Serialize for PaddedFelt { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&format!("{:#066x}", &self.0)) } } impl LowerHex for PaddedFelt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{:#066x}", self.0) } } from_thru_felt!(ClassHash, PaddedFelt); from_thru_felt!(ContractAddress, PaddedFelt); ================================================ FILE: crates/conversions/src/primitive.rs ================================================ use super::TryFromConv; use starknet_types_core::felt::Felt; use thiserror; #[derive(Debug, thiserror::Error)] pub enum PrimitiveConversionError { #[error("Felt overflow")] Overflow, } #[macro_export] macro_rules! impl_try_from_felt { ($to:ty) => { impl TryFromConv for $to { type Error = PrimitiveConversionError; fn try_from_(value: Felt) -> Result<$to, Self::Error> { if value.ge(&Felt::from(<$to>::MAX)) { Err(PrimitiveConversionError::Overflow) } else { Ok(<$to>::from_le_bytes( value.to_bytes_le()[..size_of::<$to>()].try_into().unwrap(), )) } } } }; } impl_try_from_felt!(u64); impl_try_from_felt!(u128); ================================================ FILE: crates/conversions/src/serde/deserialize/deserialize_impl.rs ================================================ use super::{BufferReadError, BufferReadResult, BufferReader, CairoDeserialize}; use crate::{IntoConv, byte_array::ByteArray}; use num_traits::cast::ToPrimitive; use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce}; use starknet_rust::{core::types::U256, providers::Url}; use starknet_types_core::felt::{Felt, NonZeroFelt}; use std::num::NonZero; impl CairoDeserialize for Url { fn deserialize(reader: &mut BufferReader<'_>) -> BufferReadResult { let url: String = reader.read::()?.to_string(); Url::parse(&url).map_err(|_| BufferReadError::ParseFailed) } } impl CairoDeserialize for Felt { fn deserialize(reader: &mut BufferReader<'_>) -> BufferReadResult { reader.read_felt() } } impl CairoDeserialize for Vec where T: CairoDeserialize, { fn deserialize(reader: &mut BufferReader<'_>) -> BufferReadResult { let length: Felt = reader.read()?; let length = length.to_usize().ok_or(BufferReadError::ParseFailed)?; let mut result = Vec::with_capacity(length); for _ in 0..length { result.push(reader.read()?); } Ok(result) } } impl CairoDeserialize for Option where T: CairoDeserialize, { fn deserialize(reader: &mut BufferReader<'_>) -> BufferReadResult { let variant: Felt = reader.read()?; let variant = variant.to_usize().ok_or(BufferReadError::ParseFailed)?; match variant { 0 => Ok(Some(reader.read()?)), 1 => Ok(None), _ => Err(BufferReadError::ParseFailed), } } } impl CairoDeserialize for Result { fn deserialize(reader: &mut BufferReader<'_>) -> BufferReadResult { let variant: Felt = reader.read()?; let variant = variant.to_usize().ok_or(BufferReadError::ParseFailed)?; match variant { 0 => Ok(Ok(reader.read()?)), 1 => Ok(Err(reader.read()?)), _ => Err(BufferReadError::ParseFailed), } } } impl CairoDeserialize for bool { fn deserialize(reader: &mut BufferReader<'_>) -> BufferReadResult { let num: usize = reader.read()?; match num { 0 => Ok(false), 1 => Ok(true), _ => Err(BufferReadError::ParseFailed), } } } impl CairoDeserialize for NonZeroFelt { fn deserialize(reader: &mut BufferReader<'_>) -> BufferReadResult { let felt = reader.read::()?; NonZeroFelt::try_from(felt).map_err(|_| BufferReadError::ParseFailed) } } macro_rules! impl_deserialize_for_nonzero_num_type { ($type:ty) => { impl CairoDeserialize for NonZero<$type> { fn deserialize(reader: &mut BufferReader<'_>) -> BufferReadResult { let val = <$type>::deserialize(reader)?; NonZero::new(val).ok_or(BufferReadError::ParseFailed) } } }; } macro_rules! impl_deserialize_for_felt_type { ($type:ty) => { impl CairoDeserialize for $type { fn deserialize(reader: &mut BufferReader<'_>) -> BufferReadResult { Felt::deserialize(reader).map(IntoConv::into_) } } }; } macro_rules! impl_deserialize_for_num_type { ($type:ty) => { impl CairoDeserialize for $type { fn deserialize(reader: &mut BufferReader<'_>) -> BufferReadResult { let felt = Felt::deserialize(reader)?; felt.try_into().map_err(|_| BufferReadError::ParseFailed) } } }; } impl_deserialize_for_felt_type!(ClassHash); impl_deserialize_for_felt_type!(ContractAddress); impl_deserialize_for_felt_type!(Nonce); impl_deserialize_for_felt_type!(EntryPointSelector); impl_deserialize_for_nonzero_num_type!(u32); impl_deserialize_for_nonzero_num_type!(u64); impl_deserialize_for_nonzero_num_type!(u128); impl_deserialize_for_nonzero_num_type!(usize); impl_deserialize_for_num_type!(u8); impl_deserialize_for_num_type!(u16); impl_deserialize_for_num_type!(u32); impl_deserialize_for_num_type!(u64); impl_deserialize_for_num_type!(u128); impl_deserialize_for_num_type!(U256); impl_deserialize_for_num_type!(usize); impl_deserialize_for_num_type!(i8); impl_deserialize_for_num_type!(i16); impl_deserialize_for_num_type!(i32); impl_deserialize_for_num_type!(i64); impl_deserialize_for_num_type!(i128); ================================================ FILE: crates/conversions/src/serde/deserialize.rs ================================================ use starknet_types_core::felt::Felt; use thiserror::Error; pub use cairo_serde_macros::CairoDeserialize; mod deserialize_impl; #[derive(Error, Debug)] pub enum BufferReadError { #[error("Read out of bounds")] EndOfBuffer, #[error("Failed to parse while reading")] ParseFailed, } pub type BufferReadResult = Result; pub struct BufferReader<'a> { buffer: &'a [Felt], } pub trait CairoDeserialize: Sized { fn deserialize(reader: &mut BufferReader<'_>) -> BufferReadResult; } impl<'a> BufferReader<'a> { #[must_use] pub fn new(buffer: &'a [Felt]) -> Self { Self { buffer } } pub fn read_felt(&mut self) -> BufferReadResult { let [head, tail @ ..] = self.buffer else { return Err(BufferReadError::EndOfBuffer); }; self.buffer = tail; Ok(*head) } #[must_use] pub fn into_remaining(self) -> &'a [Felt] { self.buffer } pub fn read(&mut self) -> BufferReadResult where T: CairoDeserialize, { T::deserialize(self) } } ================================================ FILE: crates/conversions/src/serde/serialize/serialize_impl.rs ================================================ use super::{BufferWriter, CairoSerialize}; use crate::{IntoConv, byte_array::ByteArray}; use blockifier::execution::entry_point::{CallEntryPoint, CallType}; use starknet_api::core::EthAddress; use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce}; use starknet_api::transaction::fields::{Calldata, ContractAddressSalt}; use starknet_rust::core::types::{ ContractErrorData, ContractExecutionError, TransactionExecutionErrorData, }; use starknet_types_core::felt::Felt; use std::{ cell::{Ref, RefCell}, rc::Rc, sync::Arc, }; use starknet_api::contract_class::EntryPointType; impl CairoSerialize for CallEntryPoint { fn serialize(&self, output: &mut BufferWriter) { self.entry_point_type.serialize(output); self.entry_point_selector.serialize(output); self.calldata.serialize(output); self.storage_address.serialize(output); self.caller_address.serialize(output); self.call_type.serialize(output); } } impl CairoSerialize for ContractErrorData { fn serialize(&self, output: &mut BufferWriter) { self.revert_error.serialize(output); } } // TODO(#3129) impl CairoSerialize for ContractExecutionError { fn serialize(&self, output: &mut BufferWriter) { match &self { // We need to add 0 and 1 because of enum variants serialization ContractExecutionError::Nested(inner) => { 0.serialize(output); inner.class_hash.serialize(output); inner.contract_address.serialize(output); inner.selector.serialize(output); inner.error.serialize(output); } ContractExecutionError::Message(msg) => { 1.serialize(output); ByteArray::from(msg.as_str()).serialize(output); } } } } impl CairoSerialize for TransactionExecutionErrorData { fn serialize(&self, output: &mut BufferWriter) { self.transaction_index.serialize(output); self.execution_error.serialize(output); } } impl CairoSerialize for anyhow::Error { fn serialize(&self, output: &mut BufferWriter) { ByteArray::from(self.to_string().as_str()).serialize(output); } } impl CairoSerialize for Calldata { fn serialize(&self, output: &mut BufferWriter) { self.0.serialize(output); } } impl CairoSerialize for EntryPointType { fn serialize(&self, output: &mut BufferWriter) { match self { EntryPointType::Constructor => output.write_felt(0.into()), EntryPointType::External => output.write_felt(1.into()), EntryPointType::L1Handler => output.write_felt(2.into()), } } } impl CairoSerialize for CallType { fn serialize(&self, output: &mut BufferWriter) { match self { CallType::Call => output.write_felt(0.into()), CallType::Delegate => output.write_felt(1.into()), } } } impl CairoSerialize for bool { fn serialize(&self, output: &mut BufferWriter) { if *self { Felt::from(1).serialize(output); } else { Felt::from(0).serialize(output); } } } impl CairoSerialize for Arc where T: CairoSerialize, { fn serialize(&self, output: &mut BufferWriter) { T::serialize(self, output); } } impl CairoSerialize for Rc where T: CairoSerialize, { fn serialize(&self, output: &mut BufferWriter) { T::serialize(self, output); } } impl CairoSerialize for RefCell where T: CairoSerialize, { fn serialize(&self, output: &mut BufferWriter) { self.borrow().serialize(output); } } impl CairoSerialize for Ref<'_, T> where T: CairoSerialize, { fn serialize(&self, output: &mut BufferWriter) { T::serialize(self, output); } } impl CairoSerialize for Vec where T: CairoSerialize, { fn serialize(&self, output: &mut BufferWriter) { self.len().serialize(output); for e in self { e.serialize(output); } } } impl CairoSerialize for Result { fn serialize(&self, output: &mut BufferWriter) { match self { Ok(val) => { output.write_felt(Felt::from(0)); val.serialize(output); } Err(err) => { output.write_felt(Felt::from(1)); err.serialize(output); } } } } impl CairoSerialize for Option { fn serialize(&self, output: &mut BufferWriter) { match self { Some(val) => { output.write_felt(Felt::from(0)); val.serialize(output); } None => output.write_felt(Felt::from(1)), } } } impl CairoSerialize for &T where T: CairoSerialize + ?Sized, { fn serialize(&self, output: &mut BufferWriter) { T::serialize(self, output); } } macro_rules! impl_serialize_for_felt_type { ($type:ty) => { impl CairoSerialize for $type { fn serialize(&self, output: &mut BufferWriter) { output.write_felt(self.clone().into_()); } } }; } macro_rules! impl_serialize_for_num_type { ($type:ty) => { impl CairoSerialize for $type { fn serialize(&self, output: &mut BufferWriter) { Felt::from(*self).serialize(output); } } }; } macro_rules! impl_serialize_for_tuple { ($($ty:ident),*) => { impl<$( $ty ),*> CairoSerialize for ( $( $ty, )* ) where $( $ty: CairoSerialize, )* { #[allow(non_snake_case)] #[allow(unused_variables)] fn serialize(&self, output: &mut BufferWriter) { let ( $( $ty, )* ) = self; $( $ty.serialize(output); )* } } }; } impl_serialize_for_felt_type!(Felt); impl_serialize_for_felt_type!(ClassHash); impl_serialize_for_felt_type!(ContractAddress); impl_serialize_for_felt_type!(ContractAddressSalt); impl_serialize_for_felt_type!(Nonce); impl_serialize_for_felt_type!(EntryPointSelector); impl_serialize_for_felt_type!(EthAddress); impl_serialize_for_num_type!(u8); impl_serialize_for_num_type!(u16); impl_serialize_for_num_type!(u32); impl_serialize_for_num_type!(u64); impl_serialize_for_num_type!(u128); impl_serialize_for_num_type!(usize); impl_serialize_for_num_type!(i8); impl_serialize_for_num_type!(i16); impl_serialize_for_num_type!(i32); impl_serialize_for_num_type!(i64); impl_serialize_for_num_type!(i128); impl_serialize_for_tuple!(); impl_serialize_for_tuple!(A); impl_serialize_for_tuple!(A, B); impl_serialize_for_tuple!(A, B, C); impl_serialize_for_tuple!(A, B, C, D); // cairo serde supports tuples in range 0 - 4 only ================================================ FILE: crates/conversions/src/serde/serialize.rs ================================================ use starknet_types_core::felt::Felt; pub use cairo_serde_macros::CairoSerialize; mod serialize_impl; pub struct BufferWriter { output: Vec, } impl BufferWriter { fn new() -> Self { Self { output: vec![] } } pub fn write_felt(&mut self, felt: Felt) { self.output.push(felt); } pub fn write(&mut self, serializable: T) where T: CairoSerialize, { serializable.serialize(self); } #[must_use] pub fn to_vec(self) -> Vec { self.output } } pub trait CairoSerialize { fn serialize(&self, output: &mut BufferWriter); } pub trait SerializeToFeltVec { fn serialize_to_vec(&self) -> Vec; } impl SerializeToFeltVec for T where T: CairoSerialize, { fn serialize_to_vec(&self) -> Vec { let mut buffer = BufferWriter::new(); self.serialize(&mut buffer); buffer.to_vec() } } ================================================ FILE: crates/conversions/src/serde/serialized_value.rs ================================================ use conversions::serde::deserialize::{BufferReadResult, BufferReader, CairoDeserialize}; use conversions::serde::serialize::{BufferWriter, CairoSerialize}; use starknet_types_core::felt::Felt; /// Represents an already serialized Vec of values. /// /// Use this to omit adding extra felt for the length of the vector during serialization. #[derive(Debug)] pub struct SerializedValue(pub Vec) where T: CairoSerialize; impl SerializedValue where T: CairoSerialize, { #[must_use] pub fn new(vec: Vec) -> Self { Self(vec) } } impl CairoSerialize for SerializedValue where T: CairoSerialize, { fn serialize(&self, output: &mut BufferWriter) { for e in &self.0 { e.serialize(output); } } } impl CairoDeserialize for SerializedValue { fn deserialize(reader: &mut BufferReader<'_>) -> BufferReadResult { let mut result: Vec = Vec::new(); while let Ok(r) = reader.read_felt() { result.push(r); } Ok(Self::new(result)) } } ================================================ FILE: crates/conversions/src/serde.rs ================================================ pub mod deserialize; pub mod serialize; pub mod serialized_value; pub use serialized_value::SerializedValue; ================================================ FILE: crates/conversions/src/string.rs ================================================ use crate::IntoConv; use anyhow::Result; use starknet_types_core::felt::Felt; pub trait TryFromDecStr { fn try_from_dec_str(str: &str) -> Result where Self: Sized; } pub trait TryFromHexStr { fn try_from_hex_str(str: &str) -> Result where Self: Sized; } pub trait IntoDecStr { fn into_dec_string(self) -> String; } pub trait IntoHexStr { fn into_hex_string(self) -> String; } pub trait IntoPaddedHexStr { fn into_padded_hex_str(self) -> String; } impl IntoDecStr for T where T: IntoConv, { fn into_dec_string(self) -> String { self.into_().to_string() } } impl IntoHexStr for T where T: IntoConv, { fn into_hex_string(self) -> String { self.into_().to_hex_string() } } impl IntoPaddedHexStr for T where T: IntoConv, { fn into_padded_hex_str(self) -> String { self.into_().to_fixed_hex_string() } } ================================================ FILE: crates/conversions/tests/derive_cairo_deserialize.rs ================================================ use conversions::serde::deserialize::{BufferReader, CairoDeserialize}; use starknet_types_core::felt::Felt; macro_rules! from_felts { ($($exprs:expr),*) => { CairoDeserialize::deserialize(&mut BufferReader::new(&[ $( Felt::from($exprs) ),* ])).unwrap() }; } #[test] fn work_on_struct() { #[derive(CairoDeserialize, Debug, PartialEq, Eq)] struct Foo { a: Felt, } let value: Foo = from_felts!(123); assert_eq!(value, Foo { a: Felt::from(123) }); } #[test] fn work_on_empty_struct() { #[derive(CairoDeserialize, Debug, PartialEq, Eq)] struct Foo {} let value: Foo = from_felts!(); assert_eq!(value, Foo {}); } #[test] fn work_on_tuple_struct() { #[derive(CairoDeserialize, Debug, PartialEq, Eq)] struct Foo(Felt); let value: Foo = from_felts!(123); assert_eq!(value, Foo(Felt::from(123))); } #[test] fn work_on_empty_tuple_struct() { #[derive(CairoDeserialize, Debug, PartialEq, Eq)] struct Foo(); let value: Foo = from_felts!(); assert_eq!(value, Foo()); } #[test] fn work_on_unit_struct() { #[derive(CairoDeserialize, Debug, PartialEq, Eq)] struct Foo; let value: Foo = from_felts!(); assert_eq!(value, Foo); } #[test] fn work_on_enum() { #[derive(CairoDeserialize, Debug, PartialEq, Eq)] enum Foo { A, B(Felt), C { a: Felt }, } let value: Foo = from_felts!(0); assert_eq!(value, Foo::A); let value: Foo = from_felts!(1, 123); assert_eq!(value, Foo::B(Felt::from(123))); let value: Foo = from_felts!(2, 123); assert_eq!(value, Foo::C { a: Felt::from(123) }); } #[test] #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: ParseFailed")] #[allow(unreachable_code)] fn fail_on_empty_enum() { #[derive(CairoDeserialize, Debug, PartialEq, Eq)] enum Foo {} let _: Foo = from_felts!(0); } #[test] fn work_with_nested() { #[derive(CairoDeserialize, Debug, PartialEq, Eq)] enum Foo { A, B(Felt), C { a: Bar }, } #[derive(CairoDeserialize, Debug, PartialEq, Eq)] struct Bar { a: Felt, } let value: Foo = from_felts!(2, 123); assert_eq!( value, Foo::C { a: Bar { a: Felt::from(123) } } ); } #[test] #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: EndOfBuffer")] fn fail_on_too_short_data() { #[derive(CairoDeserialize, Debug, PartialEq, Eq)] struct Foo { a: Felt, b: Felt, } let _: Foo = from_felts!(123); } ================================================ FILE: crates/conversions/tests/derive_cairo_serialize.rs ================================================ use conversions::serde::serialize::{CairoSerialize, SerializeToFeltVec}; use starknet_types_core::felt::Felt; macro_rules! from_felts { ($($exprs:expr),*) => { &[ $( Felt::from($exprs) ),* ] }; } #[test] fn work_on_struct() { #[derive(CairoSerialize, Debug, PartialEq, Eq)] struct Foo { a: Felt, } let value = from_felts!(123); assert_eq!( value, Foo { a: Felt::from(123) }.serialize_to_vec().as_slice() ); } #[test] fn work_on_empty_struct() { #[derive(CairoSerialize, Debug, PartialEq, Eq)] struct Foo {} let value: &[Felt] = from_felts!(); assert_eq!(value, Foo {}.serialize_to_vec().as_slice()); } #[test] fn work_on_tuple_struct() { #[derive(CairoSerialize, Debug, PartialEq, Eq)] struct Foo(Felt); let value = from_felts!(123); assert_eq!(value, Foo(Felt::from(123)).serialize_to_vec().as_slice()); } #[test] fn work_on_empty_tuple_struct() { #[derive(CairoSerialize, Debug, PartialEq, Eq)] struct Foo(); let value: &[Felt] = from_felts!(); assert_eq!(value, Foo().serialize_to_vec().as_slice()); } #[test] fn work_on_unit_struct() { #[derive(CairoSerialize, Debug, PartialEq, Eq)] struct Foo; let value: &[Felt] = from_felts!(); assert_eq!(value, Foo.serialize_to_vec().as_slice()); } #[test] fn work_on_enum() { #[derive(CairoSerialize, Debug, PartialEq, Eq)] enum Foo { A, B(Felt), C { a: Felt }, } let value = from_felts!(0); assert_eq!(value, Foo::A.serialize_to_vec().as_slice()); let value = from_felts!(1, 123); assert_eq!(value, Foo::B(Felt::from(123)).serialize_to_vec().as_slice()); let value = from_felts!(2, 123); assert_eq!( value, Foo::C { a: Felt::from(123) }.serialize_to_vec().as_slice() ); } #[test] fn work_on_empty_enum() { #[derive(CairoSerialize, Debug, PartialEq, Eq)] #[expect(dead_code)] enum Foo {} } #[test] fn work_with_nested() { #[derive(CairoSerialize, Debug, PartialEq, Eq)] #[expect(dead_code)] enum Foo { A, B(Felt), C { a: Bar }, } #[derive(CairoSerialize, Debug, PartialEq, Eq)] struct Bar { a: Felt, } let value = from_felts!(2, 123); assert_eq!( value, Foo::C { a: Bar { a: Felt::from(123) } } .serialize_to_vec() .as_slice() ); } ================================================ FILE: crates/conversions/tests/e2e/class_hash.rs ================================================ #[cfg(test)] mod tests_class_hash { use cairo_vm::utils::PRIME_STR; use conversions::string::{IntoDecStr, TryFromDecStr, TryFromHexStr}; use conversions::{FromConv, IntoConv}; use starknet_api::core::ClassHash; use starknet_api::core::{ContractAddress, EntryPointSelector, Nonce}; use starknet_api::hash::StarkHash; use starknet_types_core::felt::Felt; #[test] fn test_class_hash_conversions_happy_case() { let felt = Felt::from_bytes_be(&[1u8; 32]); let class_hash = ClassHash(felt); assert_eq!(class_hash, ContractAddress::from_(class_hash).into_()); assert_eq!(class_hash, Felt::from_(class_hash).into_()); assert_eq!(class_hash, Felt::from_(class_hash).into_()); assert_eq!(class_hash, Nonce::from_(class_hash).into_()); assert_eq!(class_hash, EntryPointSelector::from_(class_hash).into_()); assert_eq!(class_hash, StarkHash::from_(class_hash).into_()); assert_eq!( class_hash, ClassHash::try_from_dec_str(&class_hash.into_dec_string()).unwrap() ); } #[test] fn test_class_hash_conversions_zero() { let felt = Felt::ZERO; let class_hash = ClassHash(felt); assert_eq!(class_hash, ContractAddress::from_(class_hash).into_()); assert_eq!(class_hash, Felt::from_(class_hash).into_()); assert_eq!(class_hash, Felt::from_(class_hash).into_()); assert_eq!(class_hash, Nonce::from_(class_hash).into_()); assert_eq!(class_hash, EntryPointSelector::from_(class_hash).into_()); assert_eq!(class_hash, StarkHash::from_(class_hash).into_()); assert_eq!( class_hash, ClassHash::try_from_dec_str(&class_hash.into_dec_string()).unwrap() ); } #[test] fn test_class_hash_conversions_limit() { let mut class_hash: ClassHash = Felt::MAX.into_(); assert_eq!(class_hash, Felt::from_(class_hash).into_()); assert_eq!(class_hash, Felt::from_(class_hash).into_()); assert_eq!(class_hash, Nonce::from_(class_hash).into_()); assert_eq!(class_hash, EntryPointSelector::from_(class_hash).into_()); assert_eq!(class_hash, StarkHash::from_(class_hash).into_()); // PATRICIA_KEY_UPPER_BOUND for contract_address from starknet_api-0.4.1/src/core.rs:156 let max_value = "0x07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"; class_hash = ClassHash::try_from_hex_str(max_value).unwrap(); assert_eq!(class_hash, ContractAddress::from_(class_hash).into_()); // Unknown source for this value, founded by try and error(cairo-lang-runner-2.2.0/src/short_string.rs). let max_value = "0x0777777777777777777777777777777777777f7f7f7f7f7f7f7f7f7f7f7f7f7f"; class_hash = ClassHash::try_from_hex_str(max_value).unwrap(); assert_eq!( class_hash, ClassHash::try_from_dec_str(&class_hash.into_dec_string()).unwrap() ); } #[test] fn test_class_hash_conversions_out_of_range() { assert!(ClassHash::try_from_hex_str(PRIME_STR).unwrap() == Felt::from(0_u8).into_()); } } ================================================ FILE: crates/conversions/tests/e2e/contract_address.rs ================================================ #[cfg(test)] mod tests_contract_address { use cairo_vm::utils::PRIME_STR; use conversions::string::{IntoDecStr, TryFromDecStr, TryFromHexStr}; use conversions::{FromConv, IntoConv}; use starknet_api::core::{ClassHash, EntryPointSelector, Nonce}; use starknet_api::core::{ContractAddress, PatriciaKey}; use starknet_api::hash::StarkHash; use starknet_types_core::felt::Felt; #[test] fn test_contract_address_conversions_happy_case() { let felt = Felt::from_bytes_be(&[1u8; 32]); let contract_address = ContractAddress(PatriciaKey::try_from(felt).unwrap()); assert_eq!(contract_address, ClassHash::from_(contract_address).into_(),); assert_eq!(contract_address, Felt::from_(contract_address).into_()); assert_eq!(contract_address, Felt::from_(contract_address).into_()); assert_eq!(contract_address, Nonce::from_(contract_address).into_()); assert_eq!( contract_address, EntryPointSelector::from_(contract_address).into_() ); assert_eq!(contract_address, StarkHash::from_(contract_address).into_()); assert_eq!( contract_address, ContractAddress::try_from_dec_str(&contract_address.into_dec_string()).unwrap() ); } #[test] fn test_contract_address_conversions_zero() { let felt = Felt::ZERO; let contract_address = ContractAddress(PatriciaKey::try_from(felt).unwrap()); assert_eq!(contract_address, ClassHash::from_(contract_address).into_(),); assert_eq!(contract_address, Felt::from_(contract_address).into_()); assert_eq!(contract_address, Felt::from_(contract_address).into_()); assert_eq!(contract_address, Nonce::from_(contract_address).into_()); assert_eq!( contract_address, EntryPointSelector::from_(contract_address).into_() ); assert_eq!(contract_address, StarkHash::from_(contract_address).into_()); assert_eq!( contract_address, ContractAddress::try_from_dec_str(&contract_address.into_dec_string()).unwrap() ); } #[test] fn test_contract_address_conversions_limit() { // PATRICIA_KEY_UPPER_BOUND for contract_address from starknet_api-0.4.1/src/core.rs:156 let mut max_value = "0x07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"; let mut contract_address = ContractAddress::try_from_hex_str(max_value).unwrap(); assert_eq!(contract_address, ClassHash::from_(contract_address).into_(),); assert_eq!(contract_address, Felt::from_(contract_address).into_()); assert_eq!(contract_address, Felt::from_(contract_address).into_()); assert_eq!(contract_address, Nonce::from_(contract_address).into_()); assert_eq!( contract_address, EntryPointSelector::from_(contract_address).into_() ); assert_eq!(contract_address, StarkHash::from_(contract_address).into_()); // Unknown source for this value, founded by try and error(cairo-lang-runner-2.2.0/src/short_string.rs). max_value = "0x0777777777777777777777777777777777777f7f7f7f7f7f7f7f7f7f7f7f7f7f"; contract_address = ContractAddress::try_from_hex_str(max_value).unwrap(); assert_eq!( contract_address, ContractAddress::try_from_dec_str(&contract_address.into_dec_string()).unwrap() ); } #[test] fn test_contract_address_conversions_out_of_range() { assert!(ContractAddress::try_from_hex_str(PRIME_STR).unwrap() == Felt::from(0_u8).into_()); } } ================================================ FILE: crates/conversions/tests/e2e/entrypoint_selector.rs ================================================ #[cfg(test)] mod tests_entrypoint_selector { use cairo_vm::utils::PRIME_STR; use conversions::string::{IntoDecStr, TryFromDecStr, TryFromHexStr}; use conversions::{FromConv, IntoConv}; use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce}; use starknet_api::hash::StarkHash; use starknet_types_core::felt::Felt; #[test] fn test_entrypoint_selector_conversions_happy_case() { let felt = Felt::from_bytes_be(&[1u8; 32]); let entrypoint_selector = EntryPointSelector(felt); assert_eq!( entrypoint_selector, ClassHash::from_(entrypoint_selector).into_() ); assert_eq!( entrypoint_selector, ContractAddress::from_(entrypoint_selector).into_() ); assert_eq!( entrypoint_selector, Felt::from_(entrypoint_selector).into_() ); assert_eq!( entrypoint_selector, Felt::from_(entrypoint_selector).into_() ); assert_eq!( entrypoint_selector, StarkHash::from_(entrypoint_selector).into_() ); assert_eq!( entrypoint_selector, Nonce::from_(entrypoint_selector).into_() ); assert_eq!( entrypoint_selector, EntryPointSelector::try_from_dec_str(&entrypoint_selector.into_dec_string()).unwrap() ); } #[test] fn test_entrypoint_selector_conversions_zero() { let felt = Felt::ZERO; let entrypoint_selector = EntryPointSelector(felt); assert_eq!( entrypoint_selector, ClassHash::from_(entrypoint_selector).into_() ); assert_eq!( entrypoint_selector, ContractAddress::from_(entrypoint_selector).into_() ); assert_eq!( entrypoint_selector, Felt::from_(entrypoint_selector).into_() ); assert_eq!( entrypoint_selector, Felt::from_(entrypoint_selector).into_() ); assert_eq!( entrypoint_selector, StarkHash::from_(entrypoint_selector).into_() ); assert_eq!( entrypoint_selector, Nonce::from_(entrypoint_selector).into_() ); assert_eq!( entrypoint_selector, EntryPointSelector::try_from_dec_str(&entrypoint_selector.into_dec_string()).unwrap() ); } #[test] fn test_entrypoint_selector_conversions_limit() { let mut entrypoint_selector: EntryPointSelector = Felt::MAX.into_(); assert_eq!( entrypoint_selector, Felt::from_(entrypoint_selector).into_() ); assert_eq!( entrypoint_selector, Felt::from_(entrypoint_selector).into_() ); assert_eq!( entrypoint_selector, ClassHash::from_(entrypoint_selector).into_() ); assert_eq!( entrypoint_selector, StarkHash::from_(entrypoint_selector).into_() ); assert_eq!( entrypoint_selector, Nonce::from_(entrypoint_selector).into_() ); // PATRICIA_KEY_UPPER_BOUND for contract_address from starknet_api-0.4.1/src/core.rs:156 let max_value = "0x07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"; entrypoint_selector = EntryPointSelector::try_from_hex_str(max_value).unwrap(); assert_eq!( entrypoint_selector, ContractAddress::from_(entrypoint_selector).into_() ); // Unknown source for this value, founded by try and error(cairo-lang-runner-2.2.0/src/short_string.rs). let max_value = "0x0777777777777777777777777777777777777f7f7f7f7f7f7f7f7f7f7f7f7f7f"; entrypoint_selector = EntryPointSelector::try_from_hex_str(max_value).unwrap(); assert_eq!( entrypoint_selector, EntryPointSelector::try_from_dec_str(&entrypoint_selector.into_dec_string()).unwrap() ); } #[test] fn test_entrypoint_selector_conversions_out_of_range() { assert!( EntryPointSelector::try_from_hex_str(PRIME_STR).unwrap() == Felt::from(0_u8).into_() ); } } ================================================ FILE: crates/conversions/tests/e2e/felt.rs ================================================ #[cfg(test)] mod tests_felt { use cairo_vm::utils::PRIME_STR; use conversions::byte_array::ByteArray; use conversions::felt::FromShortString; use conversions::serde::serialize::SerializeToFeltVec; use conversions::string::{IntoDecStr, TryFromDecStr, TryFromHexStr}; use conversions::{FromConv, IntoConv}; use itertools::chain; use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce}; use starknet_api::hash::StarkHash; use starknet_types_core::felt::Felt; #[test] fn test_felt_conversions_happy_case() { let felt = Felt::from(1u8); assert_eq!(felt, ClassHash::from_(felt).into_()); assert_eq!(felt, ContractAddress::from_(felt).into_()); assert_eq!(felt, Felt::from_(felt).into_()); assert_eq!(felt, Nonce::from_(felt).into_()); assert_eq!(felt, EntryPointSelector::from_(felt).into_()); assert_eq!(felt, StarkHash::from_(felt).into_()); assert_eq!( felt, Felt::try_from_dec_str(&felt.into_dec_string()).unwrap() ); } #[test] fn test_felt_conversions_zero() { let felt = Felt::from(0u8); assert_eq!(felt, ClassHash::from_(felt).into_()); assert_eq!(felt, ContractAddress::from_(felt).into_()); assert_eq!(felt, Felt::from_(felt).into_()); assert_eq!(felt, Nonce::from_(felt).into_()); assert_eq!(felt, EntryPointSelector::from_(felt).into_()); assert_eq!(felt, StarkHash::from_(felt).into_()); assert_eq!( felt, Felt::try_from_dec_str(&felt.into_dec_string()).unwrap() ); } #[test] fn test_felt_conversions_limit() { let mut felt = Felt::MAX; assert_eq!(felt, Nonce::from_(felt).into_()); assert_eq!(felt, EntryPointSelector::from_(felt).into_()); assert_eq!(felt, Felt::from_(felt).into_()); assert_eq!(felt, ClassHash::from_(felt).into_()); assert_eq!(felt, StarkHash::from_(felt).into_()); // PATRICIA_KEY_UPPER_BOUND for contract_address from starknet_api-0.4.1/src/core.rs:156 let max_value = "0x07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"; felt = Felt::try_from_hex_str(max_value).unwrap(); assert_eq!(felt, ContractAddress::from_(felt).into_()); // Unknown source for this value, founded by try and error(cairo-lang-runner-2.2.0/src/short_string.rs). let max_value = "0x0777777777777777777777777777777777777f7f7f7f7f7f7f7f7f7f7f7f7f7f"; felt = Felt::try_from_hex_str(max_value).unwrap(); assert_eq!( felt, Felt::try_from_dec_str(&felt.into_dec_string()).unwrap() ); } #[test] fn test_felt_try_from_string_out_of_range() { assert!(Felt::try_from_hex_str(PRIME_STR).unwrap() == Felt::from(0_u8)); } #[test] fn test_decimal_string() { let felt = Felt::try_from_dec_str("123456").unwrap(); assert_eq!(felt, Felt::from(123_456)); } #[test] fn test_from_short_string() { let felt = Felt::from_short_string("abc").unwrap(); assert_eq!(felt, Felt::from_hex("0x616263").unwrap()); } #[test] fn test_from_short_string_too_long() { // 32 characters long let shortstring = String::from("1234567890123456789012345678901a"); assert!(Felt::from_short_string(&shortstring).is_err()); } #[test] fn test_result_to_felt_vec() { let val: ByteArray = "a".into(); let serialised_val = vec![Felt::from(0), Felt::from(97), Felt::from(1)]; let res: Result = Ok(val.clone()); let expected: Vec = chain!(vec![Felt::from(0)], serialised_val.clone()).collect(); assert_eq!(res.serialize_to_vec(), expected); let res: Result = Err(val); let expected: Vec = chain!(vec![Felt::from(1)], serialised_val).collect(); assert_eq!(res.serialize_to_vec(), expected); } } ================================================ FILE: crates/conversions/tests/e2e/field_elements.rs ================================================ #[cfg(test)] mod tests_field_elements { use cairo_vm::utils::PRIME_STR; use conversions::string::{IntoDecStr, TryFromDecStr, TryFromHexStr}; use conversions::{FromConv, IntoConv}; use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce}; use starknet_api::hash::StarkHash; use starknet_types_core::felt::Felt; #[test] fn test_field_elements_conversions_happy_case() { let field_element = Felt::from(1u8); assert_eq!(field_element, ClassHash::from_(field_element).into_()); assert_eq!(field_element, ContractAddress::from_(field_element).into_()); assert_eq!(field_element, Felt::from_(field_element).into_()); assert_eq!(field_element, Nonce::from_(field_element).into_()); assert_eq!( field_element, EntryPointSelector::from_(field_element).into_() ); assert_eq!(field_element, StarkHash::from_(field_element).into_()); assert_eq!( field_element, Felt::try_from_dec_str(&field_element.into_dec_string()).unwrap() ); } #[test] fn test_field_elements_conversions_zero() { let field_element = Felt::from(0u8); assert_eq!(field_element, ClassHash::from_(field_element).into_()); assert_eq!(field_element, ContractAddress::from_(field_element).into_()); assert_eq!(field_element, Felt::from_(field_element).into_()); assert_eq!(field_element, Nonce::from_(field_element).into_()); assert_eq!( field_element, EntryPointSelector::from_(field_element).into_() ); assert_eq!(field_element, StarkHash::from_(field_element).into_()); assert_eq!( field_element, Felt::try_from_dec_str(&field_element.into_dec_string()).unwrap() ); } #[test] fn test_field_element_conversions_out_of_range() { assert!(Felt::try_from_hex_str(PRIME_STR).unwrap() == Felt::from(0_u8).into_()); } } ================================================ FILE: crates/conversions/tests/e2e/mod.rs ================================================ mod class_hash; mod contract_address; mod entrypoint_selector; mod felt; mod field_elements; mod non_zero_felt; mod non_zero_u128; mod non_zero_u64; mod nonce; mod padded_felt; mod string; ================================================ FILE: crates/conversions/tests/e2e/non_zero_felt.rs ================================================ #[cfg(test)] mod tests_non_zero_felt { use std::num::{NonZeroU64, NonZeroU128}; use conversions::FromConv; use starknet_types_core::felt::{Felt, NonZeroFelt}; #[test] fn test_happy_case() { let non_zero_felt = NonZeroFelt::try_from(Felt::from(1_u8)).unwrap(); assert_eq!( non_zero_felt, NonZeroFelt::from_(NonZeroU64::new(1).unwrap()) ); assert_eq!( non_zero_felt, NonZeroFelt::from_(NonZeroU128::new(1).unwrap()) ); } } ================================================ FILE: crates/conversions/tests/e2e/non_zero_u128.rs ================================================ #[cfg(test)] mod tests_non_zero_u128 { use conversions::TryFromConv; use starknet_types_core::felt::{Felt, NonZeroFelt}; use std::num::NonZeroU128; #[test] fn test_happy_case() { let non_zero_u128 = NonZeroU128::new(1).unwrap(); assert_eq!( non_zero_u128, NonZeroU128::try_from_(NonZeroFelt::try_from(Felt::from(1_u8)).unwrap()).unwrap() ); } #[test] fn test_limit() { let felt = Felt::from_dec_str(&u128::MAX.to_string()).unwrap(); let non_zero_felt = NonZeroFelt::try_from(felt).unwrap(); let result = NonZeroU128::try_from_(non_zero_felt); assert!(result.is_ok()); assert_eq!(result.unwrap().get(), u128::MAX); } #[test] fn test_felt_too_large() { let large_felt = Felt::TWO.pow(128_u8); let non_zero_felt = NonZeroFelt::try_from(large_felt).unwrap(); let result = NonZeroU128::try_from_(non_zero_felt); assert!(result.is_err()); assert_eq!(result.unwrap_err(), "felt was too large to fit in u128"); } } ================================================ FILE: crates/conversions/tests/e2e/non_zero_u64.rs ================================================ #[cfg(test)] mod tests_non_zero_u64 { use conversions::TryFromConv; use starknet_types_core::felt::{Felt, NonZeroFelt}; use std::num::NonZeroU64; #[test] fn test_happy_case() { let non_zero_u64 = NonZeroU64::new(1).unwrap(); assert_eq!( non_zero_u64, NonZeroU64::try_from_(NonZeroFelt::try_from(Felt::from(1_u8)).unwrap()).unwrap() ); } #[test] fn test_limit() { let felt = Felt::from_dec_str(&u64::MAX.to_string()).unwrap(); let non_zero_felt = NonZeroFelt::try_from(felt).unwrap(); let result = NonZeroU64::try_from_(non_zero_felt); assert!(result.is_ok()); assert_eq!(result.unwrap().get(), u64::MAX); } #[test] fn test_felt_too_large() { let large_felt = Felt::TWO.pow(64_u8); let non_zero_felt = NonZeroFelt::try_from(large_felt).unwrap(); let result = NonZeroU64::try_from_(non_zero_felt); assert!(result.is_err()); assert_eq!(result.unwrap_err(), "felt was too large to fit in u64"); } } ================================================ FILE: crates/conversions/tests/e2e/nonce.rs ================================================ #[cfg(test)] mod tests_nonce { use cairo_vm::utils::PRIME_STR; use conversions::string::{IntoDecStr, TryFromDecStr, TryFromHexStr}; use conversions::{FromConv, IntoConv}; use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce}; use starknet_api::hash::StarkHash; use starknet_types_core::felt::Felt; #[test] fn test_nonce_conversions_happy_case() { let felt = Felt::from_bytes_be(&[1u8; 32]); let nonce = Nonce(felt); assert_eq!(nonce, ClassHash::from_(nonce).into_()); assert_eq!(nonce, ContractAddress::from_(nonce).into_()); assert_eq!(nonce, Felt::from_(nonce).into_()); assert_eq!(nonce, Felt::from_(nonce).into_()); assert_eq!(nonce, StarkHash::from_(nonce).into_()); assert_eq!(nonce, EntryPointSelector::from_(nonce).into_()); assert_eq!( nonce, Nonce::try_from_dec_str(&nonce.into_dec_string()).unwrap() ); } #[test] fn test_nonce_conversions_zero() { let felt = Felt::ZERO; let nonce = Nonce(felt); assert_eq!(nonce, ClassHash::from_(nonce).into_()); assert_eq!(nonce, ContractAddress::from_(nonce).into_()); assert_eq!(nonce, Felt::from_(nonce).into_()); assert_eq!(nonce, Felt::from_(nonce).into_()); assert_eq!(nonce, StarkHash::from_(nonce).into_()); assert_eq!(nonce, EntryPointSelector::from_(nonce).into_()); assert_eq!( nonce, Nonce::try_from_dec_str(&nonce.into_dec_string()).unwrap() ); } #[test] fn test_nonce_conversions_limit() { let mut nonce: Nonce = Felt::MAX.into_(); assert_eq!(nonce, Felt::from_(nonce).into_()); assert_eq!(nonce, Felt::from_(nonce).into_()); assert_eq!(nonce, ClassHash::from_(nonce).into_()); assert_eq!(nonce, StarkHash::from_(nonce).into_()); assert_eq!(nonce, EntryPointSelector::from_(nonce).into_()); // PATRICIA_KEY_UPPER_BOUND for contract_address from starknet_api-0.4.1/src/core.rs:156 let max_value = "0x07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"; nonce = Nonce::try_from_hex_str(max_value).unwrap(); assert_eq!(nonce, ContractAddress::from_(nonce).into_()); // Unknown source for this value, founded by try and error(cairo-lang-runner-2.2.0/src/short_string.rs). let max_value = "0x0777777777777777777777777777777777777f7f7f7f7f7f7f7f7f7f7f7f7f7f"; nonce = Nonce::try_from_hex_str(max_value).unwrap(); assert_eq!( nonce, Nonce::try_from_dec_str(&nonce.into_dec_string()).unwrap() ); } #[test] fn test_nonce_conversions_out_of_range() { assert!(Nonce::try_from_hex_str(PRIME_STR).unwrap() == Felt::from(0_u8).into_()); } } ================================================ FILE: crates/conversions/tests/e2e/padded_felt.rs ================================================ #[cfg(test)] mod tests { use conversions::padded_felt::PaddedFelt; use conversions::{FromConv, IntoConv}; use starknet_api::core::{ClassHash, ContractAddress}; use starknet_types_core::felt::Felt; #[test] fn test_padded_felt_lower_hex() { let felt = Felt::from_hex("0x123").unwrap(); let padded_felt = PaddedFelt(felt); assert_eq!( format!("{padded_felt:x}"), "0x0000000000000000000000000000000000000000000000000000000000000123".to_string() ); } #[test] fn test_padded_felt_max() { let felt = Felt::MAX; let padded_felt = PaddedFelt(felt); assert_eq!( format!("{padded_felt:x}"), "0x0800000000000011000000000000000000000000000000000000000000000000".to_string() ); } #[test] fn test_padded_felt_conversions_happy_case() { let felt = Felt::from_bytes_be(&[1u8; 32]); let padded_felt = PaddedFelt(felt); assert_eq!(padded_felt, ContractAddress::from_(padded_felt).into_()); assert_eq!(padded_felt, ClassHash::from_(padded_felt).into_()); } #[test] fn test_padded_felt_serialize() { let felt = Felt::ONE; let padded_felt = PaddedFelt(felt); let serialized = serde_json::to_string(&padded_felt).unwrap(); assert_eq!( serialized, "\"0x0000000000000000000000000000000000000000000000000000000000000001\"".to_string() ); } } ================================================ FILE: crates/conversions/tests/e2e/string.rs ================================================ use conversions::string::{IntoDecStr, TryFromDecStr}; use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce}; use starknet_api::hash::StarkHash; use starknet_types_core::felt::Felt; #[test] fn test_short_strings_conversions_happy_case() { let short_string = "1"; assert_eq!( short_string, (ClassHash::try_from_dec_str(short_string).unwrap()).into_dec_string() ); assert_eq!( short_string, (ContractAddress::try_from_dec_str(short_string).unwrap()).into_dec_string() ); assert_eq!( short_string, (Felt::try_from_dec_str(short_string).unwrap()).into_dec_string() ); assert_eq!( short_string, (Felt::try_from_dec_str(short_string).unwrap()).into_dec_string() ); assert_eq!( short_string, (Nonce::try_from_dec_str(short_string).unwrap()).into_dec_string() ); assert_eq!( short_string, (EntryPointSelector::try_from_dec_str(short_string).unwrap()).into_dec_string() ); assert_eq!( short_string, (StarkHash::try_from_dec_str(short_string).unwrap()).into_dec_string() ); } #[test] fn test_short_strings_conversions_zero() { let short_string = "0"; assert_eq!( short_string, (ClassHash::try_from_dec_str(short_string).unwrap()).into_dec_string() ); assert_eq!( short_string, (ContractAddress::try_from_dec_str(short_string).unwrap()).into_dec_string() ); assert_eq!( short_string, (Felt::try_from_dec_str(short_string).unwrap()).into_dec_string() ); assert_eq!( short_string, (Felt::try_from_dec_str(short_string).unwrap()).into_dec_string() ); assert_eq!( short_string, (Nonce::try_from_dec_str(short_string).unwrap()).into_dec_string() ); assert_eq!( short_string, (EntryPointSelector::try_from_dec_str(short_string).unwrap()).into_dec_string() ); assert_eq!( short_string, (StarkHash::try_from_dec_str(short_string).unwrap()).into_dec_string() ); } #[test] fn test_short_string_conversions_limit() { // 31 characters. let short_string = "1234567890123456789012345678901"; assert_eq!( short_string, (ClassHash::try_from_dec_str(short_string).unwrap()).into_dec_string() ); assert_eq!( short_string, (Felt::try_from_dec_str(short_string).unwrap()).into_dec_string() ); assert_eq!( short_string, (Felt::try_from_dec_str(short_string).unwrap()).into_dec_string() ); assert_eq!( short_string, (Nonce::try_from_dec_str(short_string).unwrap()).into_dec_string() ); assert_eq!( short_string, (EntryPointSelector::try_from_dec_str(short_string).unwrap()).into_dec_string() ); assert_eq!( short_string, (StarkHash::try_from_dec_str(short_string).unwrap()).into_dec_string() ); assert_eq!( short_string, (ContractAddress::try_from_dec_str(short_string).unwrap()).into_dec_string() ); } ================================================ FILE: crates/conversions/tests/main.rs ================================================ mod e2e; ================================================ FILE: crates/data-transformer/ARCHITECTURE.md ================================================ # Reverse Transformer Architecture ## Overview The Reverse Transformer module provides functionality to interpret `Starknet` smart contract calldata and output data into a human-readable, Cairo-like source code format. It parses a low-level flat array of Felt values using known ABI definitions and produces structured string representations of the corresponding values. This process is called reverse transformation and is primarily handled by parsing expressions representing function argument types, traversing those types recursively, and reading the correctly typed values from the provided calldata buffer. Reverse transformation relies on processing Cairo-style type paths. These paths can be: - Non-generic paths (simple types such as u32, felt252, starknet::ContractAddress) - Generic paths (parametrized types such as Array, Span, core::Option) The transformer processes these paths by resolving them via ABI metadata into meaningful type definitions, allowing the data buffer to be decoded according to its declared type signature. ## High-Level Flow These are the two entry points for this functionality: - reverse_transform_input - reverse_transform_output Both functions extract the parameter types (input or output) of a function in the ABI, then reversely transform the list of Felt values to their Cairo types. ## Supported Types (Type enum) | Variant | Description | |-----------|---------------------------------------------------------------------------| | Primitive | Matches raw Cairo-builtins like u32, i128, felt252, ContractAddress, etc. | | Struct | Record-like types with named fields and inner types | | Enum | Sum types, each variant might carry data | | Sequence | Repetitive/counted types like Array, Span | | Tuple | Positional group of types | All are internally represented and then formatted using their Display implementation. ## Diagram The following flow best describes the process of parsing and transformation: ```mermaid flowchart TD A["parse"] --> B["transform expression"] B -->|Tuple| C["transform tuple"] B -->|Path| D["transform path "] B -->|Other| E["return Unsupported Type Error"] D -->|Simple Path| F["transform simple path"] D -->|Generic Path| G["transform generic path"] F --> H{"is primitive?"} H -->|Yes| I["transform primitive type"] H -->|No| J["find item in abi"] J --> K{"item type?"} K -->|Enum| L["transform enum"] K -->|Struct| M["transform struct"] G --> N["read length from buffer"] N --> O["loop: parse"] O --> P["build & Return Type::Sequence"] C --> Q["map: transform expr for each element"] Q --> R["build & Return Type::Tuple"] M --> S["map: parse expr for each field"] S --> T["build & Return Type::Struct"] L --> U["read position from buffer"] U --> V{"is unit?"} V -->|Yes| W["build Enum"] V -->|No| X["parse"] X --> Y["build Enum with argument"] ``` ================================================ FILE: crates/data-transformer/Cargo.toml ================================================ [package] name = "data-transformer" version = "1.0.0" edition.workspace = true [dependencies] anyhow.workspace = true serde_json.workspace = true serde.workspace = true starknet-rust.workspace = true starknet-types-core.workspace = true cairo-lang-utils.workspace = true cairo-lang-parser.workspace = true cairo-lang-syntax.workspace = true cairo-lang-diagnostics.workspace = true cairo-lang-filesystem.workspace = true itertools.workspace = true num-bigint.workspace = true test-case.workspace = true thiserror.workspace = true conversions = { path = "../conversions" } cairo-serde-macros = { path = "../conversions/cairo-serde-macros" } [dev-dependencies] indoc.workspace = true primitive-types.workspace = true tokio.workspace = true url.workspace = true shared = { path = "../shared" } ================================================ FILE: crates/data-transformer/src/cairo_types/bytes31.rs ================================================ use cairo_serde_macros::{CairoDeserialize, CairoSerialize}; use starknet_rust::core::types::FromStrError; use starknet_types_core::felt::Felt; use std::str::FromStr; #[derive(CairoDeserialize, CairoSerialize, Debug, Clone, Copy, PartialEq, Eq)] pub struct CairoBytes31 { inner: Felt, } impl CairoBytes31 { pub const MAX: CairoBytes31 = CairoBytes31 { inner: Felt::from_hex_unchecked( "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", ), }; } #[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] pub enum ParseBytes31Error { #[error("Failed to parse as Cairo type")] CairoFromStrError, // `FromStrError` thrown on unsuccessful Felt parsing is useless. We cannot write anything beyond that #[error("Number is too large to fit in 31 bytes")] Overflow, } impl From for ParseBytes31Error { fn from(_value: FromStrError) -> Self { ParseBytes31Error::CairoFromStrError } } impl FromStr for CairoBytes31 { type Err = ParseBytes31Error; fn from_str(input: &str) -> Result { let inner = input.parse::()?; if inner > CairoBytes31::MAX.inner { return Err(ParseBytes31Error::Overflow); } Ok(CairoBytes31 { inner }) } } impl From for Felt { fn from(value: CairoBytes31) -> Self { value.inner } } ================================================ FILE: crates/data-transformer/src/cairo_types/helpers.rs ================================================ use num_bigint::BigUint; use thiserror; #[derive(Clone, Debug)] pub enum RadixInput { Decimal(Box<[u8]>), Hexadecimal(Box<[u8]>), } #[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] pub enum ParseRadixError { #[error("Input contains invalid digit")] InvalidString, #[error(transparent)] ParseIntError(#[from] std::num::ParseIntError), } impl<'input> TryFrom<&'input [u8]> for RadixInput { type Error = ParseRadixError; fn try_from(bytes: &'input [u8]) -> Result { let mut is_hex = false; if bytes.len() > 2 { is_hex = bytes[1] == b'x'; } let result = bytes .iter() .skip_while(|&&byte| byte == b'0' || byte == b'x') .filter(|&&byte| byte != b'_') .map(|&byte| { if byte.is_ascii_digit() { Ok(byte - b'0') } else if (b'a'..b'g').contains(&byte) { is_hex = true; Ok(byte - b'a' + 10) } else if (b'A'..b'G').contains(&byte) { is_hex = true; Ok(byte - b'A' + 10) } else { Err(ParseRadixError::InvalidString) } }) .collect::, ParseRadixError>>()?; Ok(if is_hex { Self::Hexadecimal(result) } else { Self::Decimal(result) }) } } impl TryFrom for BigUint { type Error = ParseRadixError; fn try_from(value: RadixInput) -> Result { match value { RadixInput::Decimal(digits) => { BigUint::from_radix_be(&digits, 10).ok_or(ParseRadixError::InvalidString) } RadixInput::Hexadecimal(digits) => { BigUint::from_radix_be(&digits, 16).ok_or(ParseRadixError::InvalidString) } } } } ================================================ FILE: crates/data-transformer/src/cairo_types/mod.rs ================================================ mod bytes31; mod helpers; mod u256; mod u384; mod u512; mod u96; pub use bytes31::{CairoBytes31, ParseBytes31Error}; pub use u96::{CairoU96, ParseCairoU96Error}; pub use u256::{CairoU256, ParseCairoU256Error}; pub use u384::{CairoU384, ParseCairoU384Error}; pub use u512::{CairoU512, ParseCairoU512Error}; ================================================ FILE: crates/data-transformer/src/cairo_types/u256.rs ================================================ use super::helpers::{ParseRadixError, RadixInput}; use cairo_serde_macros::{CairoDeserialize, CairoSerialize}; use conversions; use num_bigint::BigUint; use std::fmt; use std::fmt::Display; use std::str::FromStr; use thiserror; #[derive(CairoDeserialize, CairoSerialize, Clone, Copy, Debug, PartialEq, Eq)] pub struct CairoU256 { low: u128, high: u128, } impl CairoU256 { #[must_use] pub fn from_bytes(bytes: &[u8; 32]) -> Self { Self { low: u128::from_be_bytes(bytes[16..32].try_into().unwrap()), high: u128::from_be_bytes(bytes[0..16].try_into().unwrap()), } } #[must_use] pub fn to_be_bytes(&self) -> [u8; 32] { let mut result = [0; 32]; result[16..].copy_from_slice(&self.low.to_be_bytes()); result[..16].copy_from_slice(&self.high.to_be_bytes()); result } } impl Display for CairoU256 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let number = BigUint::from_bytes_be(&self.to_be_bytes()); write!(f, "{number}") } } #[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] pub enum ParseCairoU256Error { #[error(transparent)] InvalidString(#[from] ParseRadixError), #[error("Number is too large to fit in 32 bytes")] Overflow, } impl FromStr for CairoU256 { type Err = ParseCairoU256Error; fn from_str(input: &str) -> Result { let number: BigUint = RadixInput::try_from(input.as_bytes())?.try_into()?; let bytes = number.to_bytes_be(); if bytes.len() > 32 { return Err(ParseCairoU256Error::Overflow); } let mut result = [0u8; 32]; let start = 32 - bytes.len(); result[start..].copy_from_slice(&bytes); Ok(CairoU256::from_bytes(&result)) } } // Testing method: compare limbs against `Serde::serialize` output in Cairo #[cfg(test)] mod tests { use super::CairoU256; use test_case::test_case; const BIG_NUMBER_HEX: &str = "0xde0945c2474d9b33333123e53e37a93f5de4ba0adbf4c0a38afd2afd7d357f2c"; const BIG_NUMBER_DEC: &str = "100429835467304823721949931582394957675800948774630560463857421711344858922796"; const BIG_NUMBER_BYTES: [u8; 32] = [ 222, 9, 69, 194, 71, 77, 155, 51, 51, 49, 35, 229, 62, 55, 169, 63, 93, 228, 186, 10, 219, 244, 192, 163, 138, 253, 42, 253, 125, 53, 127, 44, ]; const BIG_NUMBER_LIMBS: [u128; 2] = [ 124_805_820_680_284_125_994_760_982_863_763_832_620, 295_136_760_614_571_572_862_546_075_274_463_127_871, ]; #[test_case(&[0; 32], [0, 0] ; "zeros")] #[test_case(&BIG_NUMBER_BYTES, BIG_NUMBER_LIMBS; "big")] fn test_happy_case_from_bytes(bytes: &[u8; 32], expected_limbs: [u128; 2]) { let result = CairoU256::from_bytes(bytes); assert_eq!([result.low, result.high], expected_limbs); } #[test_case("0x0", [0, 0] ; "zero_hex")] #[test_case("0", [0, 0] ; "zero_dec")] #[test_case("0x237abc", [2_325_180, 0] ; "small_hex")] #[test_case("237abc", [2_325_180, 0] ; "small_hex_no_leading_0x")] #[test_case("2325180", [2_325_180, 0] ; "small_dec")] #[test_case(BIG_NUMBER_HEX, BIG_NUMBER_LIMBS; "big_hex")] #[test_case(BIG_NUMBER_DEC, BIG_NUMBER_LIMBS; "big_dec")] fn test_happy_case_from_str(encoded: &str, expected_limbs: [u128; 2]) -> anyhow::Result<()> { let result: CairoU256 = encoded.parse()?; assert_eq!([result.low, result.high], expected_limbs); Ok(()) } #[test_case([0, 0], "0"; "zero")] #[test_case([2_325_180, 0], "2325180"; "small")] #[test_case(BIG_NUMBER_LIMBS, BIG_NUMBER_DEC; "big")] fn test_display(limbs: [u128; 2], expected: &str) { let result = CairoU256 { low: limbs[0], high: limbs[1], }; assert_eq!(result.to_string(), expected); } } ================================================ FILE: crates/data-transformer/src/cairo_types/u384.rs ================================================ use super::helpers::{ParseRadixError, RadixInput}; use cairo_serde_macros::{CairoDeserialize, CairoSerialize}; use num_bigint::BigUint; use std::fmt; use std::fmt::Display; use std::str::FromStr; #[expect(clippy::struct_field_names)] #[derive(CairoDeserialize, CairoSerialize, Clone, Copy, Debug, PartialEq, Eq)] pub struct CairoU384 { limb_0: u128, limb_1: u128, limb_2: u128, limb_3: u128, } impl CairoU384 { #[must_use] pub fn from_bytes(bytes: &[u8; 48]) -> Self { fn to_u128(slice: &[u8]) -> u128 { let mut padded = [0u8; 16]; padded[4..].copy_from_slice(slice); u128::from_be_bytes(padded) } Self { limb_0: to_u128(&bytes[36..48]), limb_1: to_u128(&bytes[24..36]), limb_2: to_u128(&bytes[12..24]), limb_3: to_u128(&bytes[0..12]), } } #[must_use] pub fn to_be_bytes(&self) -> [u8; 48] { let mut result = [0; 48]; result[36..48].copy_from_slice(&self.limb_0.to_be_bytes()); result[24..36].copy_from_slice(&self.limb_1.to_be_bytes()); result[12..24].copy_from_slice(&self.limb_2.to_be_bytes()); result[0..12].copy_from_slice(&self.limb_3.to_be_bytes()); result } } impl Display for CairoU384 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let number = BigUint::from_bytes_be(&self.to_be_bytes()); write!(f, "{number}") } } #[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] pub enum ParseCairoU384Error { #[error(transparent)] InvalidString(#[from] ParseRadixError), #[error("Number is too large to fit in 48 bytes")] Overflow, } impl FromStr for CairoU384 { type Err = ParseCairoU384Error; fn from_str(input: &str) -> Result { let number: BigUint = RadixInput::try_from(input.as_bytes())?.try_into()?; let bytes = number.to_bytes_be(); if bytes.len() > 48 { return Err(ParseCairoU384Error::Overflow); } let mut result = [0u8; 48]; let start = 48 - bytes.len(); result[start..].copy_from_slice(&bytes); Ok(CairoU384::from_bytes(&result)) } } ================================================ FILE: crates/data-transformer/src/cairo_types/u512.rs ================================================ use super::helpers::{ParseRadixError, RadixInput}; use cairo_serde_macros::{CairoDeserialize, CairoSerialize}; use num_bigint::BigUint; use std::fmt; use std::fmt::Display; use std::str::FromStr; #[expect(clippy::struct_field_names)] #[derive(CairoDeserialize, CairoSerialize, Clone, Copy, Debug, PartialEq, Eq)] pub struct CairoU512 { limb_0: u128, limb_1: u128, limb_2: u128, limb_3: u128, } impl CairoU512 { #[must_use] pub fn from_bytes(bytes: &[u8; 64]) -> Self { Self { limb_0: u128::from_be_bytes(bytes[48..64].try_into().unwrap()), limb_1: u128::from_be_bytes(bytes[32..48].try_into().unwrap()), limb_2: u128::from_be_bytes(bytes[16..32].try_into().unwrap()), limb_3: u128::from_be_bytes(bytes[00..16].try_into().unwrap()), } } #[must_use] pub fn to_be_bytes(&self) -> [u8; 64] { let mut result = [0; 64]; result[48..64].copy_from_slice(&self.limb_0.to_be_bytes()); result[32..48].copy_from_slice(&self.limb_1.to_be_bytes()); result[16..32].copy_from_slice(&self.limb_2.to_be_bytes()); result[00..16].copy_from_slice(&self.limb_3.to_be_bytes()); result } } impl Display for CairoU512 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let number = BigUint::from_bytes_be(&self.to_be_bytes()); write!(f, "{number}") } } #[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] pub enum ParseCairoU512Error { #[error(transparent)] InvalidString(#[from] ParseRadixError), #[error("Number is too large to fit in 64 bytes")] Overflow, } impl FromStr for CairoU512 { type Err = ParseCairoU512Error; fn from_str(input: &str) -> Result { let number: BigUint = RadixInput::try_from(input.as_bytes())?.try_into()?; let bytes = number.to_bytes_be(); if bytes.len() > 64 { return Err(ParseCairoU512Error::Overflow); } let mut result = [0u8; 64]; let start = 64 - bytes.len(); result[start..].copy_from_slice(&bytes); Ok(CairoU512::from_bytes(&result)) } } // Testing method: compare limbs against `Serde::serialize` output in Cairo #[cfg(test)] mod tests { use super::CairoU512; use test_case::test_case; const BIG_NUMBER_HEX: &str = "0xec6710e3f6607d8528d37b2b7110c1a65d6482a9bd5cf8d6fe0620ce8972c857960c53c1c06a94c104957f378fa4a3a080b84117d9d093466849643204da84e7"; const BIG_NUMBER_DEC: &str = "12381408885777547607539348003833063591238452153837122447405738741626823601474822019420743833187140657614799860086984246159941269173037600465935986717263079"; const BIG_NUMBER_BYTES: [u8; 64] = [ 236, 103, 16, 227, 246, 96, 125, 133, 40, 211, 123, 43, 113, 16, 193, 166, 93, 100, 130, 169, 189, 92, 248, 214, 254, 6, 32, 206, 137, 114, 200, 87, 150, 12, 83, 193, 192, 106, 148, 193, 4, 149, 127, 55, 143, 164, 163, 160, 128, 184, 65, 23, 217, 208, 147, 70, 104, 73, 100, 50, 4, 218, 132, 231, ]; const BIG_NUMBER_LIMBS: [u128; 4] = [ 171_097_886_328_722_014_390_365_673_661_673_407_719, 199_448_205_720_622_237_702_144_553_019_244_848_032, 124_140_083_455_263_661_715_169_373_816_437_786_711, 314_232_956_161_265_744_462_671_566_462_772_101_542, ]; #[test_case(&[0; 64], [0, 0, 0, 0] ; "zeros")] #[test_case(&BIG_NUMBER_BYTES, BIG_NUMBER_LIMBS; "big")] fn test_happy_case_from_bytes(bytes: &[u8; 64], expected_limbs: [u128; 4]) { let result = CairoU512::from_bytes(bytes); assert_eq!( [result.limb_0, result.limb_1, result.limb_2, result.limb_3], expected_limbs ); } #[test_case("0x0", [0, 0, 0, 0] ; "zero_hex")] #[test_case("0", [0, 0, 0, 0] ; "zero_dec")] #[test_case("0x237abc", [2_325_180, 0, 0, 0] ; "small_hex")] #[test_case("237abc", [2_325_180, 0, 0, 0] ; "small_hex_no_leading_0x")] #[test_case("2325180", [2_325_180, 0, 0, 0] ; "small_dec")] #[test_case(BIG_NUMBER_HEX, BIG_NUMBER_LIMBS; "big_hex")] #[test_case(BIG_NUMBER_DEC, BIG_NUMBER_LIMBS; "big_dec")] fn test_happy_case_from_str(encoded: &str, expected_limbs: [u128; 4]) -> anyhow::Result<()> { let result: CairoU512 = encoded.parse()?; assert_eq!( [result.limb_0, result.limb_1, result.limb_2, result.limb_3], expected_limbs ); Ok(()) } #[test_case([0, 0, 0, 0], "0"; "zero")] #[test_case([2_325_180, 0, 0, 0], "2325180"; "small")] #[test_case(BIG_NUMBER_LIMBS, BIG_NUMBER_DEC; "big")] fn test_display(limbs: [u128; 4], expected: &str) { let number = CairoU512 { limb_0: limbs[0], limb_1: limbs[1], limb_2: limbs[2], limb_3: limbs[3], }; assert_eq!(number.to_string(), expected); } } ================================================ FILE: crates/data-transformer/src/cairo_types/u96.rs ================================================ use cairo_serde_macros::{CairoDeserialize, CairoSerialize}; use starknet_types_core::felt::Felt; use std::fmt::Display; use std::{ fmt, num::{IntErrorKind, ParseIntError}, str::FromStr, }; #[derive(CairoSerialize, CairoDeserialize, Clone, Copy, Debug, PartialEq, Eq)] pub struct CairoU96 { inner: u128, } impl Display for CairoU96 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.inner) } } const MAX_VALUE: u128 = (2 << 96) - 1; impl From for Felt { fn from(value: CairoU96) -> Self { Felt::from(value.inner) } } #[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] pub enum ParseCairoU96Error { #[error(transparent)] InvalidString(#[from] ParseIntError), #[error("Number is too large to fit in 24 bytes")] Overflow, } impl FromStr for CairoU96 { type Err = ParseCairoU96Error; fn from_str(input: &str) -> Result { let is_hex = input.starts_with("0x") || input.contains(|c: char| c.is_alphabetic()); let number = if is_hex { u128::from_str_radix(input, 16) } else { u128::from_str(input) } .map_err(|err| { if err.kind() == &IntErrorKind::PosOverflow { ParseCairoU96Error::Overflow } else { err.into() } })?; if number > MAX_VALUE { return Err(ParseCairoU96Error::Overflow); } Ok(Self { inner: number }) } } ================================================ FILE: crates/data-transformer/src/lib.rs ================================================ pub mod cairo_types; mod reverse_transformer; mod shared; mod transformer; pub use reverse_transformer::{ ReverseTransformError, reverse_transform_input, reverse_transform_output, }; pub use transformer::transform; ================================================ FILE: crates/data-transformer/src/reverse_transformer/mod.rs ================================================ mod transform; mod types; use crate::reverse_transformer::transform::{ReverseTransformer, TransformationError}; use crate::shared::extraction::extract_function_from_selector; use cairo_lang_parser::utils::SimpleParserDatabase; use starknet_rust::core::types::contract::AbiEntry; use starknet_types_core::felt::Felt; #[derive(Debug, thiserror::Error)] pub enum ReverseTransformError { #[error(r#"Function with selector "{0:#x}" not found in ABI of the contract"#)] FunctionNotFound(Felt), #[error(transparent)] TransformationError(#[from] TransformationError), } /// Transforms a calldata into a Cairo-like string representation of the arguments pub fn reverse_transform_input( input: &[Felt], abi: &[AbiEntry], function_selector: &Felt, ) -> Result { let input_types: Vec<_> = extract_function_from_selector(abi, *function_selector) .ok_or(ReverseTransformError::FunctionNotFound(*function_selector))? .inputs .into_iter() .map(|input| input.r#type) .collect(); reverse_transform(input, abi, &input_types) } /// Transforms a call output into a Cairo-like string representation of the return values pub fn reverse_transform_output( output: &[Felt], abi: &[AbiEntry], function_selector: &Felt, ) -> Result { let output_types: Vec<_> = extract_function_from_selector(abi, *function_selector) .ok_or(ReverseTransformError::FunctionNotFound(*function_selector))? .outputs .into_iter() .map(|input| input.r#type) .collect(); reverse_transform(output, abi, &output_types) } fn reverse_transform( felts: &[Felt], abi: &[AbiEntry], types: &[String], ) -> Result { let db = SimpleParserDatabase::default(); let mut reverse_transformer = ReverseTransformer::new(felts, abi); Ok(types .iter() .map(|parameter_type| { Ok(reverse_transformer .parse_and_transform(parameter_type, &db)? .to_string()) }) .collect::, TransformationError>>()? .join(", ")) } ================================================ FILE: crates/data-transformer/src/reverse_transformer/transform.rs ================================================ use crate::reverse_transformer::types::{ Enum, Primitive, Sequence, SequenceType, Struct, StructField, Tuple, Type, }; use crate::shared::parsing::{ParseError, parse_expression}; use crate::shared::path::{PathSplitError, SplitResult, split}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::ast::{Expr, ExprListParenthesized, ExprPath}; use conversions::serde::deserialize::{BufferReadError, BufferReader}; use starknet_rust::core::types::contract::{AbiEntry, AbiEnum, AbiStruct}; use starknet_types_core::felt::Felt; /// An error that can occur during the transformation process. #[derive(Debug, thiserror::Error)] pub enum TransformationError { #[error("type unsupported by reverse transformer")] UnsupportedType, #[error("reading from buffer failed with error: {0}")] BufferReaderError(#[from] BufferReadError), #[error("{enum_name} enum does not have variant at position {variant_position}")] NoSuchEnumVariant { enum_name: String, variant_position: usize, }, #[error("abi is invalid")] InvalidAbi, #[error(transparent)] ParseError(#[from] ParseError), #[error(transparent)] PathSplitError(#[from] PathSplitError), } /// An error that can occur when trying to transform a primitive type. #[derive(Debug, thiserror::Error)] enum PrimitiveError { #[error("Type {0} is not primitive")] NotFound(String), #[error(transparent)] BufferReaderError(#[from] BufferReadError), } pub struct ReverseTransformer<'a> { abi: &'a [AbiEntry], buffer_reader: BufferReader<'a>, } impl<'a> ReverseTransformer<'a> { /// Creates a new instance of [`ReverseTransformer`]. pub fn new(inputs: &'a [Felt], abi: &'a [AbiEntry]) -> Self { Self { abi, buffer_reader: BufferReader::new(inputs), } } /// Parses the given `&str` to an [`Expr`] and then transforms it to a [`Type`]. pub fn parse_and_transform( &mut self, expr: &str, db: &SimpleParserDatabase, ) -> Result { let parsed_expr = parse_expression(expr, db)?; self.transform_expr(&parsed_expr, db) } /// Transforms the given [`Expr`] to a [`Type`]. fn transform_expr( &mut self, expr: &Expr, db: &SimpleParserDatabase, ) -> Result { match expr { Expr::Tuple(expr) => self.transform_tuple(expr, db), Expr::Path(expr) => self.transform_path(expr, db), _ => Err(TransformationError::UnsupportedType), } } /// Transforms a tuple expression to a [`Type::Tuple`]. fn transform_tuple( &mut self, expr: &ExprListParenthesized, db: &SimpleParserDatabase, ) -> Result { let elements = expr.expressions(db).elements(db).collect::>(); let parsed_exprs = elements .iter() .map(|expr| self.transform_expr(expr, db)) .collect::, TransformationError>>()?; Ok(Type::Tuple(Tuple(parsed_exprs))) } /// Transforms a [`ExprPath`] to a [`Type`]. fn transform_path( &mut self, expr: &ExprPath, db: &SimpleParserDatabase, ) -> Result { match split(expr, db)? { SplitResult::Simple { splits } => self.transform_simple_path(&splits, db), SplitResult::WithGenericArgs { splits, generic_args, } => self.transform_generic_path(&splits, &generic_args, db), } } /// Transforms a generic path to a [`Type::Sequence`]. /// /// It first checks if the path is a known sequence type (Array or Span). /// If it is, it reads the length of the sequence from the buffer. /// Then it recursively transforms the elements of the sequence. fn transform_generic_path( &mut self, splits: &[String], generic_args: &str, db: &SimpleParserDatabase, ) -> Result { let sequence_type = match splits.join("::").as_str() { "core::array::Array" => SequenceType::Array, "core::array::Span" => SequenceType::Span, _ => return Err(TransformationError::UnsupportedType), }; let length = self.buffer_reader.read::()?; let sequence = (0..length) .map(|_| self.parse_and_transform(generic_args, db)) .collect::, TransformationError>>()?; Ok(Type::Sequence(Sequence { sequence_type, sequence, })) } /// Transforms a simple path to a [`Type`]. /// /// It first tries to transform it to a primitive type. /// If that fails, it means it is a complex type. /// Then it tries to find the type in the ABI and transform it to the matching representation. fn transform_simple_path( &mut self, parts: &[String], db: &SimpleParserDatabase, ) -> Result { let name = parts.last().expect("path should not be empty").to_owned(); match self.transform_primitive_type(&name) { Err(PrimitiveError::NotFound(_)) => (), Ok(primitive) => return Ok(Type::Primitive(primitive)), Err(PrimitiveError::BufferReaderError(error)) => { return Err(TransformationError::BufferReaderError(error)); } } match find_item(self.abi, parts).ok_or(TransformationError::InvalidAbi)? { AbiStructOrEnum::Enum(enum_abi_definition) => { self.transform_enum(enum_abi_definition, name, db) } AbiStructOrEnum::Struct(struct_type_definition) => { self.transform_struct(struct_type_definition, db) } } } /// Transforms to a [`Type::Struct`]. /// /// Recursively transforms the fields of the struct and then creates a [`Type::Struct`] instance. fn transform_struct( &mut self, abi_struct: &AbiStruct, db: &SimpleParserDatabase, ) -> Result { let fields = abi_struct .members .iter() .map(|member| { let value = self.parse_and_transform(&member.r#type, db)?; let name = member.name.clone(); Ok(StructField { name, value }) }) .collect::, TransformationError>>()?; let name = abi_struct .name .split("::") .last() .expect("path should not be empty") .to_owned(); Ok(Type::Struct(Struct { name, fields })) } /// Transforms to a [`Type::Enum`]. /// /// It first reads the position of the enum variant from the buffer. /// Then it retrieves the variant name and type from the ABI. /// If the variant type is unit, it sets the argument to `None`, else it recursively transforms the variant type. /// Finally, it creates a [`Type::Enum`] instance. fn transform_enum( &mut self, abi_enum: &AbiEnum, name: String, db: &SimpleParserDatabase, ) -> Result { let position: usize = self.buffer_reader.read()?; let variant = abi_enum.variants.get(position).ok_or_else(|| { TransformationError::NoSuchEnumVariant { enum_name: name.clone(), variant_position: position, } })?; let enum_variant_type = variant.r#type.as_str(); let variant = variant.name.clone(); let argument = if enum_variant_type == "()" { None } else { Some(Box::new(self.parse_and_transform(enum_variant_type, db)?)) }; Ok(Type::Enum(Enum { name, variant, argument, })) } /// Transforms a primitive type string to a [`Primitive`]. /// /// It matches the type string against known primitive types and reads the corresponding value from the buffer. fn transform_primitive_type(&mut self, type_str: &str) -> Result { match type_str { "bool" => Ok(Primitive::Bool(self.buffer_reader.read()?)), "u8" => Ok(Primitive::U8(self.buffer_reader.read()?)), "u16" => Ok(Primitive::U16(self.buffer_reader.read()?)), "u32" => Ok(Primitive::U32(self.buffer_reader.read()?)), "u64" => Ok(Primitive::U64(self.buffer_reader.read()?)), "u96" => Ok(Primitive::U96(self.buffer_reader.read()?)), "u128" => Ok(Primitive::U128(self.buffer_reader.read()?)), "u256" => Ok(Primitive::U256(self.buffer_reader.read()?)), "u384" => Ok(Primitive::U384(self.buffer_reader.read()?)), "u512" => Ok(Primitive::U512(self.buffer_reader.read()?)), "i8" => Ok(Primitive::I8(self.buffer_reader.read()?)), "i16" => Ok(Primitive::I16(self.buffer_reader.read()?)), "i32" => Ok(Primitive::I32(self.buffer_reader.read()?)), "i64" => Ok(Primitive::I64(self.buffer_reader.read()?)), "i128" => Ok(Primitive::I128(self.buffer_reader.read()?)), "ByteArray" => Ok(Primitive::ByteArray(self.buffer_reader.read()?)), "bytes31" => Ok(Primitive::CairoBytes31(self.buffer_reader.read()?)), "ContractAddress" => Ok(Primitive::ContractAddress(self.buffer_reader.read()?)), "ClassHash" => Ok(Primitive::ClassHash(self.buffer_reader.read()?)), "StorageAddress" => Ok(Primitive::StorageAddress(self.buffer_reader.read()?)), "EthAddress" => Ok(Primitive::EthAddress(self.buffer_reader.read()?)), "felt" | "felt252" => Ok(Primitive::Felt(self.buffer_reader.read()?)), _ => Err(PrimitiveError::NotFound(type_str.to_string())), } } } enum AbiStructOrEnum<'a> { Struct(&'a AbiStruct), Enum(&'a AbiEnum), } fn find_item<'a>(items_from_abi: &'a [AbiEntry], path: &[String]) -> Option> { let path = path.join("::"); items_from_abi.iter().find_map(|item| match item { AbiEntry::Struct(abi_struct) if abi_struct.name == path => { Some(AbiStructOrEnum::Struct(abi_struct)) } AbiEntry::Enum(abi_enum) if abi_enum.name == path => Some(AbiStructOrEnum::Enum(abi_enum)), _ => None, }) } ================================================ FILE: crates/data-transformer/src/reverse_transformer/types.rs ================================================ use crate::cairo_types::{CairoBytes31, CairoU96, CairoU256, CairoU384, CairoU512}; use conversions::byte_array::ByteArray; use starknet_types_core::felt::Felt; use std::fmt; use std::fmt::Display; /// Types that are supported by the reverse transformer #[derive(Debug)] pub enum Type { Struct(Struct), Sequence(Sequence), Enum(Enum), Primitive(Primitive), Tuple(Tuple), } impl Display for Type { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Type::Struct(value) => write!(f, "{value}"), Type::Sequence(value) => write!(f, "{value}"), Type::Enum(value) => write!(f, "{value}"), Type::Primitive(value) => write!(f, "{value}"), Type::Tuple(value) => write!(f, "{value}"), } } } /// Primitive types supported by the reverse transformer #[derive(Debug)] pub enum Primitive { Bool(bool), U8(u8), U16(u16), U32(u32), U64(u64), U96(CairoU96), U128(u128), U256(CairoU256), U384(CairoU384), U512(CairoU512), I8(i8), I16(i16), I32(i32), I64(i64), I128(i128), Felt(Felt), CairoBytes31(CairoBytes31), ByteArray(ByteArray), ContractAddress(Felt), ClassHash(Felt), StorageAddress(Felt), EthAddress(Felt), } impl Display for Primitive { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Primitive::Bool(value) => write!(f, "{value}"), Primitive::U8(value) => write!(f, "{value}_u8"), Primitive::U16(value) => write!(f, "{value}_u16"), Primitive::U32(value) => write!(f, "{value}_u32"), Primitive::U64(value) => write!(f, "{value}_u64"), Primitive::U96(value) => write!(f, "{value}_96"), Primitive::U128(value) => write!(f, "{value}_u128"), Primitive::U256(value) => write!(f, "{value}_u256"), Primitive::U384(value) => write!(f, "{value}_u384"), Primitive::U512(value) => write!(f, "{value}_u512"), Primitive::I8(value) => write!(f, "{value}_i8"), Primitive::I16(value) => write!(f, "{value}_i16"), Primitive::I32(value) => write!(f, "{value}_i32"), Primitive::I64(value) => write!(f, "{value}_i64"), Primitive::I128(value) => write!(f, "{value}_i128"), Primitive::Felt(value) => write!(f, "{value:#x}"), Primitive::ByteArray(value) => write!(f, "\"{value}\""), Primitive::ContractAddress(value) => write!(f, "ContractAddress({value:#x})"), Primitive::ClassHash(value) => write!(f, "ClassHash({value:#x})"), Primitive::StorageAddress(value) => write!(f, "StorageAddress({value:#x})"), Primitive::EthAddress(value) => write!(f, "EthAddress({value:#x})"), Primitive::CairoBytes31(value) => { let felt = Felt::from(*value); write!(f, "CairoBytes31({felt:#x})") } } } } #[derive(Debug)] pub struct Tuple(pub Vec); impl Display for Tuple { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut dbg = f.debug_tuple(""); for item in &self.0 { dbg.field(&format_args!("{item}")); } dbg.finish() } } #[derive(Debug)] pub struct StructField { pub name: String, pub value: Type, } #[derive(Debug)] pub struct Struct { pub name: String, pub fields: Vec, } impl Display for Struct { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut dbg = f.debug_struct(&self.name); for field in &self.fields { dbg.field(&field.name, &format_args!("{}", field.value)); } dbg.finish() } } #[derive(Debug)] pub struct Enum { pub name: String, pub variant: String, pub argument: Option>, } impl Display for Enum { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let variant_name = format!("{}::{}", self.name, self.variant); if let Some(arg) = &self.argument { let mut dbg = f.debug_tuple(&variant_name); dbg.field(&format_args!("{arg}")); dbg.finish() } else { write!(f, "{variant_name}") } } } #[derive(Debug)] pub enum SequenceType { Array, Span, } #[derive(Debug)] pub struct Sequence { pub sequence_type: SequenceType, pub sequence: Vec, } impl Display for Sequence { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "array!")?; let mut list = f.debug_list(); for item in &self.sequence { list.entry(&format_args!("{item}")); } list.finish()?; if let SequenceType::Span = &self.sequence_type { write!(f, ".span()")?; } Ok(()) } } ================================================ FILE: crates/data-transformer/src/shared/extraction.rs ================================================ use starknet_rust::core::types::contract::{AbiEntry, AbiFunction, StateMutability}; use starknet_rust::core::utils::get_selector_from_name; use starknet_types_core::felt::Felt; pub fn extract_function_from_selector( abi: &[AbiEntry], searched_selector: Felt, ) -> Option { const CONSTRUCTOR_AS_SELECTOR: Felt = Felt::from_hex_unchecked( "0x28ffe4ff0f226a9107253e17a904099aa4f63a02a5621de0576e5aa71bc5194", ); search_for_function(abi, searched_selector) // If the user doesn't explicitly define a constructor in the contract, // it won't be present in the ABI. In such cases, an implicit constructor // with no arguments is assumed. .or_else(|| (searched_selector == CONSTRUCTOR_AS_SELECTOR).then(default_constructor)) } fn default_constructor() -> AbiFunction { AbiFunction { name: "constructor".to_string(), inputs: vec![], outputs: vec![], state_mutability: StateMutability::View, } } fn search_for_function(abi: &[AbiEntry], searched_selector: Felt) -> Option { abi.iter().find_map(|entry| match entry { AbiEntry::Function(func) => { let selector = get_selector_from_name(&func.name).ok()?; (selector == searched_selector).then(|| func.clone()) } // We treat constructor like a regular function // because it's searched for using Felt entrypoint selector, identically as functions. // Also, we don't need any constructor-specific properties, just argument types. AbiEntry::Constructor(constructor) => { let selector = get_selector_from_name(&constructor.name).ok()?; (selector == searched_selector).then(|| AbiFunction { name: constructor.name.clone(), inputs: constructor.inputs.clone(), outputs: vec![], state_mutability: StateMutability::View, }) } AbiEntry::Interface(interface) => search_for_function(&interface.items, searched_selector), _ => None, }) } ================================================ FILE: crates/data-transformer/src/shared/mod.rs ================================================ pub mod extraction; pub mod parsing; pub mod path; ================================================ FILE: crates/data-transformer/src/shared/parsing.rs ================================================ use cairo_lang_diagnostics::DiagnosticsBuilder; use cairo_lang_filesystem::ids::{FileKind, FileLongId, SmolStrId, VirtualFile}; use cairo_lang_parser::parser::Parser; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::ast::Expr; use cairo_lang_utils::Intern; #[derive(Debug, thiserror::Error)] pub enum ParseError { #[error("Invalid Cairo expression found in input calldata \"{expr}\":\n{diagnostics}")] InvalidExpression { expr: String, diagnostics: String }, } pub fn parse_expression<'a>( source: &'a str, db: &'a SimpleParserDatabase, ) -> Result, ParseError> { let file = FileLongId::Virtual(VirtualFile { parent: None, name: SmolStrId::from(db, "parser_input"), content: SmolStrId::from(db, source), code_mappings: [].into(), kind: FileKind::Expr, original_item_removed: false, }) .intern(db); let mut diagnostics = DiagnosticsBuilder::default(); let expression = Parser::parse_file_expr(db, &mut diagnostics, file, source); let diagnostics = diagnostics.build(); if diagnostics.check_error_free().is_err() { return Err(ParseError::InvalidExpression { expr: source.to_string(), diagnostics: diagnostics.format(db), }); } Ok(expression) } ================================================ FILE: crates/data-transformer/src/shared/path.rs ================================================ use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::ast::{ Expr, ExprPath, GenericArg, PathSegment, PathSegmentWithGenericArgs, }; use cairo_lang_syntax::node::{Token, TypedSyntaxNode}; #[derive(Debug, thiserror::Error)] pub enum PathSplitError { #[error("Invalid generic arguments")] InvalidGenericArgs, #[error("Expected exactly one generic argument")] MoreThanOneGenericArg, #[error("Path segment missing")] PathSegmentMissing, } pub enum SplitResult { Simple { splits: Vec, }, WithGenericArgs { splits: Vec, generic_args: String, }, } /// Splits a path into its segments, and extracts generic arguments if present. /// /// In the case of Cairo-like language constructs such as arrays or spans, /// we assume that if there are generic arguments (e.g., `Span`), they /// appear at the end of the path. Therefore, by the time we encounter a /// segment with generic arguments, all preceding segments have already /// been collected. /// /// For example, in a path like `core::array::Array`, this function will: /// - Collect "core", "array", and "Array" into `splits` /// - Extract the generic argument `felt252` from `Array` pub fn split(path: &ExprPath, db: &SimpleParserDatabase) -> Result { let mut splits = Vec::new(); let elements = path.segments(db).elements(db); let elements_len = elements.len(); for (i, p) in elements.enumerate() { match p { PathSegment::Simple(segment) => { splits.push(segment.ident(db).token(db).text(db).to_string(db)); } PathSegment::WithGenericArgs(segment) => { splits.push(segment.ident(db).token(db).text(db).to_string(db)); let generic_args = extract_generic_args(&segment, db)?; let is_last = i == elements_len - 1; return if is_last { Ok(SplitResult::WithGenericArgs { splits, generic_args, }) } else { Err(PathSplitError::InvalidGenericArgs) }; } PathSegment::Missing(_segment) => Err(PathSplitError::PathSegmentMissing)?, } } Ok(SplitResult::Simple { splits }) } fn extract_generic_args( segment: &PathSegmentWithGenericArgs, db: &SimpleParserDatabase, ) -> Result { let generic_args = segment .generic_args(db) .generic_args(db) .elements(db) .map(|arg| match arg { GenericArg::Named(_) => Err(PathSplitError::InvalidGenericArgs), GenericArg::Unnamed(arg) => match arg.value(db) { Expr::Underscore(_) => Err(PathSplitError::InvalidGenericArgs), expr => Ok(expr.as_syntax_node().get_text(db)), }, }) .collect::, PathSplitError>>()?; let [generic_arg] = generic_args.as_slice() else { return Err(PathSplitError::MoreThanOneGenericArg); }; Ok(generic_arg.to_string()) } ================================================ FILE: crates/data-transformer/src/transformer/mod.rs ================================================ mod sierra_abi; use crate::shared::extraction::extract_function_from_selector; use crate::shared::parsing::parse_expression; use crate::transformer::sierra_abi::build_representation; use anyhow::{Context, Result, bail, ensure}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::ast::Expr; use conversions::serde::serialize::SerializeToFeltVec; use itertools::Itertools; use starknet_rust::core::types::contract::{AbiEntry, AbiFunction}; use starknet_types_core::felt::Felt; /// Interpret `calldata` as a comma-separated series of expressions in Cairo syntax and serialize it pub fn transform(calldata: &str, abi: &[AbiEntry], function_selector: &Felt) -> Result> { let function = extract_function_from_selector(abi, *function_selector).with_context(|| { format!( r#"Function with selector "{function_selector:#x}" not found in ABI of the contract"# ) })?; let db = SimpleParserDatabase::default(); let input = convert_to_tuple(calldata); let calldata = split_expressions(&input, &db)?; process(calldata, &function, abi, &db).context("Error while processing Cairo-like calldata") } fn split_expressions<'a>(input: &'a str, db: &'a SimpleParserDatabase) -> Result>> { if input.is_empty() { return Ok(Vec::new()); } let expr = parse_expression(input, db)?; match expr { Expr::Tuple(tuple) => Ok(tuple.expressions(db).elements(db).collect()), Expr::Parenthesized(expr) => Ok(vec![expr.expr(db)]), _ => bail!("Wrong calldata format - expected tuple of Cairo expressions"), } } fn process( calldata: Vec, function: &AbiFunction, abi: &[AbiEntry], db: &SimpleParserDatabase, ) -> Result> { let n_inputs = function.inputs.len(); let n_arguments = calldata.len(); ensure!( n_inputs == n_arguments, "Invalid number of arguments: passed {n_arguments}, expected {n_inputs}", ); function .inputs .iter() .zip(calldata) .map(|(parameter, expr)| { let representation = build_representation(expr, ¶meter.r#type, abi, db)?; Ok(representation.serialize_to_vec()) }) .flatten_ok() .collect::>() } fn convert_to_tuple(calldata: &str) -> String { // We need to convert our comma-separated string of expressions into something that is a valid // Cairo expression, so we can parse it. // // We convert to tuple by wrapping in `()` with a trailing `,` to handle case of a single argument if calldata.is_empty() { return String::new(); } format!("({calldata},)") } ================================================ FILE: crates/data-transformer/src/transformer/sierra_abi/binary.rs ================================================ use crate::transformer::sierra_abi::SupportedCalldataKind; use crate::transformer::sierra_abi::data_representation::AllowedCalldataArgument; use anyhow::{Result, bail, ensure}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::ast::{ BinaryOperator, Expr, ExprBinary, ExprFunctionCall, PathSegment, }; use cairo_lang_syntax::node::{Terminal, TypedSyntaxNode}; use starknet_rust::core::types::contract::AbiEntry; impl SupportedCalldataKind for ExprBinary<'_> { fn transform( &self, expected_type: &str, abi: &[AbiEntry], db: &SimpleParserDatabase, ) -> Result { let op = self.op(db); let lhs = self.lhs(db); let rhs = self.rhs(db); if !matches!(op, BinaryOperator::Dot(_)) { let op = op .as_syntax_node() .get_text_without_trivia(db) .to_string(db); bail!(r#"Invalid operator, expected ".", got "{op}""#) } let Expr::InlineMacro(lhs) = lhs else { let lhs = lhs .as_syntax_node() .get_text_without_trivia(db) .to_string(db); bail!(r#"Only "array![]" is supported as left-hand side of "." operator, got "{lhs}""#); }; let Expr::FunctionCall(rhs) = rhs else { let rhs = rhs .as_syntax_node() .get_text_without_trivia(db) .to_string(db); bail!(r#"Only calling ".span()" on "array![]" is supported, got "{rhs}""#); }; assert_is_span(&rhs, db)?; let expected_type = expected_type.replace("Span", "Array"); lhs.transform(&expected_type, abi, db) } } fn assert_is_span(expr: &ExprFunctionCall, db: &SimpleParserDatabase) -> Result<()> { match expr .path(db) .segments(db) .elements(db) .last() .expect("Function call must have a name") { PathSegment::Simple(simple) => { let function_name = simple.ident(db).text(db).to_string(db); ensure!( function_name == "span", r#"Invalid function name, expected "span", got "{function_name}""# ); Ok(()) } PathSegment::WithGenericArgs(_) => { bail!("Invalid path specified: generic args in function call not supported") } PathSegment::Missing(_segment) => { bail!("Path segment missing") } } } ================================================ FILE: crates/data-transformer/src/transformer/sierra_abi/complex_types.rs ================================================ use super::data_representation::{ AllowedCalldataArgument, CalldataEnum, CalldataStruct, CalldataStructField, CalldataTuple, }; use super::parsing::parse_argument_list; use super::{SupportedCalldataKind, build_representation}; use crate::shared; use crate::shared::parsing::parse_expression; use crate::shared::path::SplitResult; use anyhow::{Context, Result, bail, ensure}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::ast::{ Expr, ExprFunctionCall, ExprListParenthesized, ExprPath, ExprStructCtorCall, OptionStructArgExpr, StructArg, }; use cairo_lang_syntax::node::{Terminal, TypedSyntaxNode}; use itertools::Itertools; use starknet_rust::core::types::contract::{AbiEntry, AbiEnum, AbiNamedMember, AbiStruct}; use std::collections::HashSet; pub trait EnumOrStruct { const VARIANT: &'static str; const VARIANT_CAPITALIZED: &'static str; fn name(&self) -> String; } impl EnumOrStruct for AbiStruct { const VARIANT: &'static str = "struct"; const VARIANT_CAPITALIZED: &'static str = "Struct"; fn name(&self) -> String { self.name.clone() } } impl EnumOrStruct for AbiEnum { const VARIANT: &'static str = "enum"; const VARIANT_CAPITALIZED: &'static str = "Enum"; fn name(&self) -> String { self.name.clone() } } fn validate_path_argument( param_type: &str, path_argument: &[String], path_argument_joined: &String, ) -> Result<()> { if *path_argument.last().unwrap() != param_type.split("::").last().unwrap() && path_argument_joined != param_type { bail!(r#"Invalid argument type, expected "{param_type}", got "{path_argument_joined}""#) } Ok(()) } fn split(path: &ExprPath, db: &SimpleParserDatabase) -> Result> { match shared::path::split(path, db)? { SplitResult::Simple { splits } => Ok(splits), SplitResult::WithGenericArgs { .. } => { bail!("Cannot use generic args when specifying struct/enum path") } } } fn find_all_structs(abi: &[AbiEntry]) -> Vec<&AbiStruct> { abi.iter() .filter_map(|entry| match entry { AbiEntry::Struct(r#struct) => Some(r#struct), _ => None, }) .collect() } fn find_enum_variant_position<'a>( variant: &String, path: &[String], abi: &'a [AbiEntry], ) -> Result<(usize, &'a AbiNamedMember)> { let enums_from_abi = abi .iter() .filter_map(|abi_entry| { if let AbiEntry::Enum(abi_enum) = abi_entry { Some(abi_enum) } else { None } }) .collect::>(); let enum_abi_definition = find_item_with_path(enums_from_abi, path)?; let position_and_enum_variant = enum_abi_definition .variants .iter() .find_position(|item| item.name == *variant) .with_context(|| { format!( r#"Couldn't find variant "{}" in enum "{}""#, variant, path.join("::") ) })?; Ok(position_and_enum_variant) } /// Structs and enums in ABI can be searched in the same way. 'item' here refers either to an enum or a struct fn find_item_with_path<'item, T: EnumOrStruct>( items_from_abi: Vec<&'item T>, path: &[String], ) -> Result<&'item T> { // Argument is a module path to an item (module_name::StructName {}) if path.len() > 1 { let full_path_item = items_from_abi .into_iter() .find(|x| x.name() == path.join("::")); ensure!( full_path_item.is_some(), r#"{} "{}" not found in ABI"#, T::VARIANT_CAPITALIZED, path.join("::") ); return Ok(full_path_item.unwrap()); } // Argument is just the name of the item (Struct {}) let mut matching_items_from_abi: Vec<&T> = items_from_abi .into_iter() .filter(|x| x.name().split("::").last() == path.last().map(String::as_str)) .collect(); ensure!( !matching_items_from_abi.is_empty(), r#"{} "{}" not found in ABI"#, T::VARIANT_CAPITALIZED, path.join("::") ); ensure!( matching_items_from_abi.len() == 1, r#"Found more than one {} "{}" in ABI, please specify a full path to the item"#, T::VARIANT, path.join("::") ); Ok(matching_items_from_abi.pop().unwrap()) } fn get_struct_arguments_with_values<'a>( arguments: &'a [StructArg<'a>], db: &'a SimpleParserDatabase, ) -> Result)>> { arguments .iter() .map(|elem| { match elem { // Holds info about parameter and argument in struct creation, e.g.: // in case of "Struct { a: 1, b: 2 }", two separate StructArgSingle hold info // about "a: 1" and "b: 2" respectively. StructArg::StructArgSingle(whole_arg) => { match whole_arg.arg_expr(db) { // It's probably a case of constructor invocation `Struct {a, b}` catching variables `a` and `b` from context OptionStructArgExpr::Empty(_) => { bail!( "Shorthand arguments are not allowed - used \"{ident}\", expected \"{ident}: value\"", ident = whole_arg.identifier(db).text(db).to_string(db) ) } // Holds info about the argument, e.g.: in case of "a: 1" holds info // about ": 1" OptionStructArgExpr::StructArgExpr(arg_value_with_colon) => Ok(( whole_arg.identifier(db).text(db).to_string(db), arg_value_with_colon.expr(db), )), } } StructArg::StructArgTail(_) => { bail!("Struct initialization with \"..\" operator is not allowed") } } }) .collect() } // Structs impl SupportedCalldataKind for ExprStructCtorCall<'_> { fn transform( &self, expected_type: &str, abi: &[AbiEntry], db: &SimpleParserDatabase, ) -> Result { let struct_path: Vec = split(&self.path(db), db)?; let struct_path_joined = struct_path.clone().join("::"); validate_path_argument(expected_type, &struct_path, &struct_path_joined)?; let structs_from_abi = find_all_structs(abi); let struct_abi_definition = find_item_with_path(structs_from_abi, &struct_path)?; let struct_args = self .arguments(db) .arguments(db) .elements(db) .collect::>(); let struct_args_with_values = get_struct_arguments_with_values(&struct_args, db) .context("Found invalid expression in struct argument")?; if struct_args_with_values.len() != struct_abi_definition.members.len() { bail!( r#"Invalid number of struct arguments in struct "{}", expected {} arguments, found {}"#, struct_path_joined, struct_abi_definition.members.len(), struct_args.len() ) } // validate if all arguments' names have corresponding names in abi if struct_args_with_values .iter() .map(|(arg_name, _)| arg_name.clone()) .collect::>() != struct_abi_definition .members .iter() .map(|x| x.name.clone()) .collect::>() { // TODO add message which arguments are invalid (Issue #2549) bail!( r"Arguments in constructor invocation for struct {expected_type} do not match struct arguments in ABI", ) } let fields = struct_args_with_values .into_iter() .map(|(arg_name, expr)| { let abi_entry = struct_abi_definition .members .iter() .find(|&abi_member| abi_member.name == arg_name) .expect("Arg name should be in ABI - it is checked before with HashSets"); Ok(CalldataStructField::new(build_representation( expr, &abi_entry.r#type, abi, db, )?)) }) .collect::>>()?; Ok(AllowedCalldataArgument::Struct(CalldataStruct::new(fields))) } } // Unit enum variants impl SupportedCalldataKind for ExprPath<'_> { fn transform( &self, expected_type: &str, abi: &[AbiEntry], db: &SimpleParserDatabase, ) -> Result { // Enums with no value - Enum::Variant let enum_path_with_variant = split(self, db)?; let (enum_variant_name, enum_path) = enum_path_with_variant.split_last().unwrap(); let enum_path_joined = enum_path.join("::"); validate_path_argument(expected_type, enum_path, &enum_path_joined)?; let (enum_position, enum_variant) = find_enum_variant_position(enum_variant_name, enum_path, abi)?; if enum_variant.r#type != "()" { bail!(r#"Couldn't find variant "{enum_variant_name}" in enum "{enum_path_joined}""#) } Ok(AllowedCalldataArgument::Enum(CalldataEnum::new( enum_position, None, ))) } } // Tuple-like enum variants impl SupportedCalldataKind for ExprFunctionCall<'_> { fn transform( &self, expected_type: &str, abi: &[AbiEntry], db: &SimpleParserDatabase, ) -> Result { // Enums with value - Enum::Variant(10) let enum_path_with_variant = split(&self.path(db), db)?; let (enum_variant_name, enum_path) = enum_path_with_variant.split_last().unwrap(); let enum_path_joined = enum_path.join("::"); validate_path_argument(expected_type, enum_path, &enum_path_joined)?; let (enum_position, enum_variant) = find_enum_variant_position(enum_variant_name, enum_path, abi)?; let arguments = self.arguments(db).arguments(db); // Enum variant constructor invocation has one argument - an ArgList. // We parse it to a vector of expressions and pop + unwrap safely. let expr = parse_argument_list(&arguments, db)?.pop().unwrap(); let parsed_expr = build_representation(expr, &enum_variant.r#type, abi, db)?; Ok(AllowedCalldataArgument::Enum(CalldataEnum::new( enum_position, Some(Box::new(parsed_expr)), ))) } } // Tuples impl SupportedCalldataKind for ExprListParenthesized<'_> { fn transform( &self, expected_type: &str, abi: &[AbiEntry], db: &SimpleParserDatabase, ) -> Result { let Expr::Tuple(tuple) = parse_expression(expected_type, db)? else { bail!(r#"Invalid argument type, expected "{expected_type}", got tuple"#); }; let tuple_types = tuple .expressions(db) .elements(db) .map(|element| match element { Expr::Path(path) => Ok(path.as_syntax_node().get_text(db)), other => bail!( "Unexpected expression found in ABI: {}. Contract ABI may be invalid", other.as_syntax_node().get_text(db) ), }) .collect::>>()?; let parsed_exprs = self .expressions(db) .elements(db) .zip(tuple_types) .map(|(expr, single_param)| build_representation(expr, single_param, abi, db)) .collect::>>()?; Ok(AllowedCalldataArgument::Tuple(CalldataTuple::new( parsed_exprs, ))) } } ================================================ FILE: crates/data-transformer/src/transformer/sierra_abi/data_representation.rs ================================================ use crate::cairo_types::{CairoBytes31, CairoU96, CairoU256, CairoU384, CairoU512}; use anyhow::{Context, bail}; use conversions::felt::FromShortString; use conversions::{ byte_array::ByteArray, serde::serialize::{BufferWriter, CairoSerialize}, }; use starknet_types_core::felt::Felt; use std::{any::type_name, str::FromStr}; fn neat_parsing_error_message(value: &str, parsing_type: &str, reason: Option<&str>) -> String { if let Some(message) = reason { format!(r#"Failed to parse value "{value}" into type "{parsing_type}": {message}"#) } else { format!(r#"Failed to parse value "{value}" into type "{parsing_type}""#) } } fn parse_with_type(value: &str) -> anyhow::Result where ::Err: std::error::Error + Send + Sync + 'static, { value .parse::() .context(neat_parsing_error_message(value, type_name::(), None)) } /// A fundamental struct for representing expression types supported by the transformer #[derive(Debug)] pub enum AllowedCalldataArgument { Struct(CalldataStruct), ArrayMacro(CalldataArrayMacro), Enum(CalldataEnum), Primitive(CalldataPrimitive), Tuple(CalldataTuple), } impl CairoSerialize for AllowedCalldataArgument { fn serialize(&self, output: &mut BufferWriter) { match self { AllowedCalldataArgument::Struct(value) => value.serialize(output), AllowedCalldataArgument::ArrayMacro(value) => value.serialize(output), AllowedCalldataArgument::Enum(value) => value.serialize(output), AllowedCalldataArgument::Primitive(value) => value.serialize(output), AllowedCalldataArgument::Tuple(value) => value.serialize(output), } } } #[derive(Debug)] pub enum CalldataPrimitive { Bool(bool), U8(u8), U16(u16), U32(u32), U64(u64), U96(CairoU96), U128(u128), U256(CairoU256), U384(CairoU384), U512(CairoU512), I8(i8), I16(i16), I32(i32), I64(i64), I128(i128), Felt(Felt), ByteArray(ByteArray), } impl CalldataPrimitive { pub(crate) fn try_from_str_with_type( value: &str, type_with_path: &str, ) -> anyhow::Result { let type_str = type_with_path .split("::") .last() .context("Couldn't parse parameter type from ABI")?; // TODO add all corelib types (Issue #2550) match type_str { "bool" => Ok(Self::Bool(parse_with_type(value)?)), "u8" => Ok(Self::U8(parse_with_type(value)?)), "u16" => Ok(Self::U16(parse_with_type(value)?)), "u32" => Ok(Self::U32(parse_with_type(value)?)), "u64" => Ok(Self::U64(parse_with_type(value)?)), "u96" => Ok(Self::U96(parse_with_type(value)?)), "u128" => Ok(Self::U128(parse_with_type(value)?)), "u256" => Ok(Self::U256(parse_with_type(value)?)), "u384" => Ok(Self::U384(parse_with_type(value)?)), "u512" => Ok(Self::U512(parse_with_type(value)?)), "i8" => Ok(Self::I8(parse_with_type(value)?)), "i16" => Ok(Self::I16(parse_with_type(value)?)), "i32" => Ok(Self::I32(parse_with_type(value)?)), "i64" => Ok(Self::I64(parse_with_type(value)?)), "i128" => Ok(Self::I128(parse_with_type(value)?)), "ByteArray" => Ok(Self::ByteArray(ByteArray::from(value))), // bytes31 is a helper type defined in Cairo corelib; // (e.g. alexandria_data_structures::bit_array::BitArray uses that) // https://github.com/starkware-libs/cairo/blob/bf48e658b9946c2d5446eeb0c4f84868e0b193b5/corelib/src/bytes_31.cairo#L14 // It's actually felt under the hood. Although conversion from felt252 to bytes31 returns Result, it never fails. "bytes31" => Ok(Self::Felt(parse_with_type::(value)?.into())), "shortstring" => { let felt = Felt::from_short_string(value)?; Ok(Self::Felt(felt)) } "felt252" | "felt" | "ContractAddress" | "ClassHash" | "StorageAddress" | "EthAddress" => { let felt = Felt::from_dec_str(value) .with_context(|| neat_parsing_error_message(value, type_with_path, None))?; Ok(Self::Felt(felt)) } _ => { bail!(neat_parsing_error_message( value, type_with_path, Some(&format!("unsupported type {type_with_path}")) )) } } } } impl CairoSerialize for CalldataPrimitive { // https://docs.starknet.io/architecture-and-concepts/smart-contracts/serialization-of-cairo-types/ fn serialize(&self, output: &mut BufferWriter) { match self { CalldataPrimitive::Bool(value) => value.serialize(output), CalldataPrimitive::U8(value) => value.serialize(output), CalldataPrimitive::U16(value) => value.serialize(output), CalldataPrimitive::U32(value) => value.serialize(output), CalldataPrimitive::U64(value) => value.serialize(output), CalldataPrimitive::U96(value) => value.serialize(output), CalldataPrimitive::U128(value) => value.serialize(output), CalldataPrimitive::U256(value) => value.serialize(output), CalldataPrimitive::U384(value) => value.serialize(output), CalldataPrimitive::U512(value) => value.serialize(output), CalldataPrimitive::I8(value) => value.serialize(output), CalldataPrimitive::I16(value) => value.serialize(output), CalldataPrimitive::I32(value) => value.serialize(output), CalldataPrimitive::I64(value) => value.serialize(output), CalldataPrimitive::I128(value) => value.serialize(output), CalldataPrimitive::Felt(value) => value.serialize(output), CalldataPrimitive::ByteArray(value) => value.serialize(output), } } } #[derive(Debug)] pub struct CalldataTuple(Vec); impl CalldataTuple { pub fn new(arguments: Vec) -> Self { Self(arguments) } } impl CairoSerialize for CalldataTuple { fn serialize(&self, output: &mut BufferWriter) { self.0.iter().for_each(|field| field.serialize(output)); } } #[derive(Debug, CairoSerialize)] pub struct CalldataStructField(AllowedCalldataArgument); impl CalldataStructField { pub fn new(value: AllowedCalldataArgument) -> Self { Self(value) } } #[derive(Debug)] pub struct CalldataStruct(Vec); impl CalldataStruct { pub fn new(arguments: Vec) -> Self { Self(arguments) } } impl CairoSerialize for CalldataStruct { // https://docs.starknet.io/architecture-and-concepts/smart-contracts/serialization-of-cairo-types/#serialization_of_structs fn serialize(&self, output: &mut BufferWriter) { self.0.iter().for_each(|field| field.serialize(output)); } } #[derive(Debug)] pub struct CalldataEnum { position: usize, argument: Option>, } impl CalldataEnum { pub fn new(position: usize, argument: Option>) -> Self { Self { position, argument } } } impl CairoSerialize for CalldataEnum { // https://docs.starknet.io/architecture-and-concepts/smart-contracts/serialization-of-cairo-types/#serialization_of_enums fn serialize(&self, output: &mut BufferWriter) { self.position.serialize(output); if let Some(arg) = &self.argument { arg.serialize(output); } } } #[derive(Debug, CairoSerialize)] pub struct CalldataArrayMacro(Vec); impl CalldataArrayMacro { pub fn new(arguments: Vec) -> Self { Self(arguments) } } ================================================ FILE: crates/data-transformer/src/transformer/sierra_abi/literals.rs ================================================ use super::SupportedCalldataKind; use super::data_representation::{AllowedCalldataArgument, CalldataPrimitive}; use anyhow::{Context, Result, bail}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::Terminal; use cairo_lang_syntax::node::ast::{ Expr, ExprUnary, TerminalFalse, TerminalLiteralNumber, TerminalShortString, TerminalString, TerminalTrue, UnaryOperator, }; use starknet_rust::core::types::contract::AbiEntry; use std::ops::Neg; impl SupportedCalldataKind for TerminalLiteralNumber<'_> { fn transform( &self, expected_type: &str, _abi: &[AbiEntry], db: &SimpleParserDatabase, ) -> Result { let (value, suffix) = self .numeric_value_and_suffix(db) .with_context(|| format!("Couldn't parse value: {}", self.text(db).to_string(db)))?; let proper_param_type = match suffix { None => expected_type, Some(ref suffix) => &suffix.to_string(db), }; Ok(AllowedCalldataArgument::Primitive( CalldataPrimitive::try_from_str_with_type(&value.to_string(), proper_param_type)?, )) } } impl SupportedCalldataKind for ExprUnary<'_> { fn transform( &self, expected_type: &str, _abi: &[AbiEntry], db: &SimpleParserDatabase, ) -> Result { let (value, suffix) = match self.expr(db) { Expr::Literal(literal_number) => literal_number .numeric_value_and_suffix(db) .with_context(|| { format!( "Couldn't parse value: {}", literal_number.text(db).to_string(db) ) }), _ => bail!("Invalid expression with unary operator, only numbers allowed"), }?; let proper_param_type = match suffix { None => expected_type, Some(ref suffix) => &suffix.to_string(db), }; match self.op(db) { UnaryOperator::Not(_) => { bail!("Invalid unary operator in expression !{value} , only - allowed, got !") } UnaryOperator::Desnap(_) => { bail!("Invalid unary operator in expression *{value} , only - allowed, got *") } UnaryOperator::BitNot(_) => { bail!("Invalid unary operator in expression ~{value} , only - allowed, got ~") } UnaryOperator::At(_) => { bail!("Invalid unary operator in expression @{value} , only - allowed, got @") } UnaryOperator::Reference(_) => { bail!("Invalid unary operator in expression &{value} , only - allowed, got &") } UnaryOperator::Minus(_) => {} } Ok(AllowedCalldataArgument::Primitive( CalldataPrimitive::try_from_str_with_type(&value.neg().to_string(), proper_param_type)?, )) } } impl SupportedCalldataKind for TerminalShortString<'_> { fn transform( &self, expected_type: &str, _abi: &[AbiEntry], db: &SimpleParserDatabase, ) -> Result { let value = self .string_value(db) .context("Invalid shortstring passed as an argument")?; // TODO(#2623) add better handling let expected_type = match expected_type.split("::").last() { Some("felt" | "felt252") => "shortstring", _ => expected_type, }; Ok(AllowedCalldataArgument::Primitive( CalldataPrimitive::try_from_str_with_type(&value, expected_type)?, )) } } impl SupportedCalldataKind for TerminalString<'_> { fn transform( &self, expected_type: &str, _abi: &[AbiEntry], db: &SimpleParserDatabase, ) -> Result { let value = self .string_value(db) .context("Invalid string passed as an argument")?; Ok(AllowedCalldataArgument::Primitive( CalldataPrimitive::try_from_str_with_type(&value, expected_type)?, )) } } impl SupportedCalldataKind for TerminalTrue<'_> { fn transform( &self, expected_type: &str, _abi: &[AbiEntry], db: &SimpleParserDatabase, ) -> Result { let value = self.text(db).to_string(db); Ok(AllowedCalldataArgument::Primitive( CalldataPrimitive::try_from_str_with_type(&value, expected_type)?, )) } } impl SupportedCalldataKind for TerminalFalse<'_> { fn transform( &self, expected_type: &str, _abi: &[AbiEntry], db: &SimpleParserDatabase, ) -> Result { let value = self.text(db).to_string(db); Ok(AllowedCalldataArgument::Primitive( CalldataPrimitive::try_from_str_with_type(&value, expected_type)?, )) } } ================================================ FILE: crates/data-transformer/src/transformer/sierra_abi/macros.rs ================================================ use super::data_representation::{AllowedCalldataArgument, CalldataArrayMacro}; use super::parsing::parse_inline_macro; use super::{SupportedCalldataKind, build_representation}; use crate::shared::parsing::parse_expression; use crate::transformer::{convert_to_tuple, split_expressions}; use anyhow::{Context, Result, bail}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::TypedSyntaxNode; use cairo_lang_syntax::node::ast::{Expr, ExprInlineMacro, PathSegment, PathSegment::Simple}; use itertools::Itertools; use starknet_rust::core::types::contract::AbiEntry; impl SupportedCalldataKind for ExprInlineMacro<'_> { fn transform( &self, expected_type: &str, abi: &[AbiEntry], db: &SimpleParserDatabase, ) -> Result { // array![] calls let parsed_string = convert_to_tuple(&parse_inline_macro(self, db)?); let parsed_exprs = split_expressions(&parsed_string, db)?; // We do not expect any other expression in proper ABI let Expr::Path(path) = parse_expression(expected_type, db)? else { bail!("Unexpected expression encountered in ABI: {expected_type}. ABI may be invalid"); }; let type_parameters_from_abi = path .segments(db) .elements(db) .find_map(|element| match element { // We expect exactly one PathSegment::WithGenericArgs. More means that ABI is broken, less means that type other than Array is expected Simple(_) => None, PathSegment::WithGenericArgs(segment) => Some( segment .generic_args(db) .generic_args(db) .elements(db) .map(|arg| match arg { // There shouldn't be expressions like `identifier` in the ABI arg @ cairo_lang_syntax::node::ast::GenericArg::Named(_) => bail!( "Unexpected named generic found in ABI: {}. Contract ABI may be invalid", arg.as_syntax_node().get_text(db) ), cairo_lang_syntax::node::ast::GenericArg::Unnamed(arg) => { match arg.value(db) { Expr::Underscore(expr) => bail!( "Unexpected type with underscore generic placeholder found in ABI: {}. Contract ABI may be invalid", expr.as_syntax_node().get_text(db) ), expr => Ok(expr.as_syntax_node().get_text(db)), } } }) .collect::>>(), ), PathSegment::Missing(_segment) => Some(Err(anyhow::anyhow!("Path segment missing"))) }) .transpose()? .with_context(|| format!(r#"Invalid argument type, expected "{expected_type}", got array"#))?; // Check by string; A proper array type in ABI looks exactly like this if !expected_type.contains("core::array::Array") { bail!(r#"Expected "{expected_type}", got array"#); } // Array should have exactly one type parameter. ABI is invalid otherwise let [element_type] = &type_parameters_from_abi[..] else { let parameters_punctuated = type_parameters_from_abi.into_iter().join(", "); bail!( "Expected exactly one generic parameter of Array type, got {parameters_punctuated}. Contract ABI may be invalid", ); }; let arguments = parsed_exprs .into_iter() .map(|arg| build_representation(arg, element_type, abi, db)) .collect::>>()?; Ok(AllowedCalldataArgument::ArrayMacro( CalldataArrayMacro::new(arguments), )) } } ================================================ FILE: crates/data-transformer/src/transformer/sierra_abi/mod.rs ================================================ use anyhow::{Result, bail}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::ast::Expr; use data_representation::AllowedCalldataArgument; use starknet_rust::core::types::contract::AbiEntry; mod binary; mod complex_types; pub(crate) mod data_representation; mod literals; mod macros; pub(crate) mod parsing; /// A main trait that allows particular calldata types to be recognized and transformed trait SupportedCalldataKind { fn transform( &self, expected_type: &str, abi: &[AbiEntry], db: &SimpleParserDatabase, ) -> Result; } /// A main function that transforms expressions supported by the transformer /// to their corresponding serializable struct representations pub(crate) fn build_representation( expression: Expr<'_>, expected_type: &str, abi: &[AbiEntry], db: &SimpleParserDatabase, ) -> Result { match expression { Expr::StructCtorCall(item) => item.transform(expected_type, abi, db), Expr::Literal(item) => item.transform(expected_type, abi, db), Expr::Unary(item) => item.transform(expected_type, abi, db), Expr::ShortString(item) => item.transform(expected_type, abi, db), Expr::String(item) => item.transform(expected_type, abi, db), Expr::True(item) => item.transform(expected_type, abi, db), Expr::False(item) => item.transform(expected_type, abi, db), Expr::Path(item) => item.transform(expected_type, abi, db), Expr::FunctionCall(item) => item.transform(expected_type, abi, db), Expr::InlineMacro(item) => item.transform(expected_type, abi, db), Expr::Tuple(item) => item.transform(expected_type, abi, db), Expr::Binary(item) => item.transform(expected_type, abi, db), Expr::Parenthesized(_) | Expr::Block(_) | Expr::Match(_) | Expr::If(_) | Expr::Loop(_) | Expr::While(_) | Expr::For(_) | Expr::Closure(_) | Expr::ErrorPropagate(_) | Expr::FieldInitShorthand(_) | Expr::Indexed(_) | Expr::FixedSizeArray(_) | Expr::Missing(_) | Expr::Underscore(_) => { bail!(r#"Invalid argument type: unsupported expression for type "{expected_type}""#) } } } ================================================ FILE: crates/data-transformer/src/transformer/sierra_abi/parsing.rs ================================================ use anyhow::{Result, bail}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::ast::WrappedTokenTree; use cairo_lang_syntax::node::ast::{ ArgClause, ArgList, Expr, ExprInlineMacro, Modifier, PathSegment, PathSegment::Simple, }; use cairo_lang_syntax::node::{Terminal, TypedSyntaxNode}; use itertools::Itertools; fn modifier_syntax_token(item: &Modifier) -> &'static str { match item { Modifier::Ref(_) => "ref", Modifier::Mut(_) => "mut", } } pub fn parse_argument_list<'a>( arguments: &'a ArgList, db: &'a SimpleParserDatabase, ) -> Result>> { let args_lists = arguments; let arguments = arguments.elements(db); if let Some(modifiers) = arguments .map(|arg| arg.modifiers(db).elements(db)) .find(|mod_list| mod_list.len() != 0) { let modifiers = modifiers .map(|modifier| modifier_syntax_token(&modifier)) .collect_vec(); match &modifiers[..] { [] => unreachable!(), [single] => bail!(r#""{single}" modifier is not allowed"#), [multiple @ .., last] => { bail!( "{} and {} modifiers are not allowed", multiple.iter().join(", "), last ) } } } args_lists .elements(db) .map(|arg| match arg.arg_clause(db) { ArgClause::Unnamed(expr) => Ok(expr.value(db)), ArgClause::Named(_) => { bail!("Named arguments are not allowed") } ArgClause::FieldInitShorthand(_) => { bail!("Field init shorthands are not allowed") } }) .collect::>>() } pub fn parse_inline_macro<'a>( invocation: &'a ExprInlineMacro<'a>, db: &'a SimpleParserDatabase, ) -> Result { match invocation .path(db) .segments(db) .elements(db) .last() .expect("Macro must have a name") { Simple(simple) => { let macro_name = simple.ident(db).text(db).to_string(db); if macro_name != "array" { bail!(r#"Invalid macro name, expected "array![]", got "{macro_name}""#) } } PathSegment::WithGenericArgs(_) => { bail!("Invalid path specified: generic args in array![] macro not supported") } PathSegment::Missing(_segment) => { bail!("Path segment missing") } } match invocation.arguments(db).subtree(db) { WrappedTokenTree::Bracketed(token_tree) => Ok(token_tree .tokens(db) .elements(db) .map(|token| token.as_syntax_node().get_text(db)) .collect::()), WrappedTokenTree::Parenthesized(_) | WrappedTokenTree::Braced(_) => { bail!("`array` macro supports only square brackets: array![]") } WrappedTokenTree::Missing(_) => unreachable!( "If any type of parentheses is missing, then diagnostics have been reported and whole flow should have already been terminated." ), } } ================================================ FILE: crates/data-transformer/tests/data/data_transformer/Scarb.toml ================================================ [package] name = "data_transformer_contract" version = "0.1.0" edition = "2024_07" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] starknet = "2.9.4" alexandria_data_structures = "0.4.0" [dev-dependencies] snforge_std = "0.39.0" assert_macros = "2.9.4" [[target.starknet-contract]] sierra = true [scripts] test = "snforge test" [tool.scarb] allow-prebuilt-plugins = ["snforge_std"] ================================================ FILE: crates/data-transformer/tests/data/data_transformer/src/lib.cairo ================================================ #[derive(Serde, Drop)] pub struct SimpleStruct { a: felt252, } #[derive(Serde, Drop)] pub struct NestedStructWithField { a: SimpleStruct, b: felt252, } #[derive(Serde, Drop)] pub enum Enum { One: (), Two: u128, Three: NestedStructWithField, } #[derive(Serde, Drop)] pub struct ComplexStruct { a: NestedStructWithField, b: felt252, c: u8, d: i32, e: Enum, f: ByteArray, g: Array, h: u256, i: (i128, u128), } #[derive(Serde, Drop)] pub struct BitArray { bit: felt252, } #[starknet::interface] pub trait IDataTransformer { fn simple_fn(ref self: TContractState, a: felt252) -> felt252; fn u256_fn(ref self: TContractState, a: u256) -> u256; fn signed_fn(ref self: TContractState, a: i32) -> i32; fn unsigned_fn(ref self: TContractState, a: u32) -> u32; fn tuple_fn(ref self: TContractState, a: (felt252, u8, Enum)) -> (felt252, u8, Enum); fn complex_fn( ref self: TContractState, arr: Array>, one: u8, two: i16, three: ByteArray, four: (felt252, u32), five: bool, six: u256, ); fn simple_struct_fn(ref self: TContractState, a: SimpleStruct) -> SimpleStruct; fn nested_struct_fn( ref self: TContractState, a: NestedStructWithField, ) -> NestedStructWithField; fn enum_fn(ref self: TContractState, a: Enum) -> Enum; fn complex_struct_fn(ref self: TContractState, a: ComplexStruct) -> ComplexStruct; fn external_struct_fn( ref self: TContractState, a: BitArray, b: alexandria_data_structures::bit_array::BitArray, ) -> (BitArray, alexandria_data_structures::bit_array::BitArray); fn span_fn(ref self: TContractState, a: Span) -> Span; fn multiple_signed_fn(ref self: TContractState, a: i32, b: i8); fn no_args_fn(ref self: TContractState); } #[starknet::contract] mod DataTransformer { use core::starknet::ContractAddress; use super::*; #[storage] struct Storage { balance: felt252, } #[constructor] fn constructor(ref self: ContractState, init_owner: ContractAddress) {} #[abi(embed_v0)] impl DataTransformerImpl of super::IDataTransformer { fn simple_fn(ref self: ContractState, a: felt252) -> felt252 { a } fn u256_fn(ref self: ContractState, a: u256) -> u256 { a } fn signed_fn(ref self: ContractState, a: i32) -> i32 { a } fn unsigned_fn(ref self: ContractState, a: u32) -> u32 { a } fn tuple_fn(ref self: ContractState, a: (felt252, u8, Enum)) -> (felt252, u8, Enum) { a } fn complex_fn( ref self: ContractState, arr: Array>, one: u8, two: i16, three: ByteArray, four: (felt252, u32), five: bool, six: u256, ) {} fn simple_struct_fn(ref self: ContractState, a: SimpleStruct) -> SimpleStruct { a } fn nested_struct_fn( ref self: ContractState, a: NestedStructWithField, ) -> NestedStructWithField { a } fn enum_fn(ref self: ContractState, a: Enum) -> Enum { a } fn complex_struct_fn(ref self: ContractState, a: ComplexStruct) -> ComplexStruct { a } fn external_struct_fn( ref self: ContractState, a: BitArray, b: alexandria_data_structures::bit_array::BitArray, ) -> (BitArray, alexandria_data_structures::bit_array::BitArray) { (a, b) } fn span_fn(ref self: ContractState, a: Span) -> Span { a } fn multiple_signed_fn(ref self: ContractState, a: i32, b: i8) {} fn no_args_fn(ref self: ContractState) {} } } #[starknet::contract] mod DataTransformerNoConstructor { #[storage] struct Storage {} } ================================================ FILE: crates/data-transformer/tests/integration/identity.rs ================================================ use crate::integration::get_abi; use data_transformer::{reverse_transform_input, transform}; use primitive_types::U256; use starknet_rust::core::utils::get_selector_from_name; use test_case::test_case; #[test_case("1010101_u32", "unsigned_fn"; "u32")] #[test_case(&format!("{}_u32", u32::MAX), "unsigned_fn"; "u32_max")] #[test_case("0x64", "simple_fn"; "felt252")] #[test_case(&format!("{}_u256", U256::MAX), "u256_fn"; "u256_max")] #[test_case("8503_u256", "u256_fn"; "u256")] #[test_case("-273_i32", "signed_fn"; "i32")] #[test_case("-100_i32, -50_i8", "multiple_signed_fn"; "multiple_signed")] #[test_case("(0x859, 1_u8, Enum::One)", "tuple_fn"; "tuple")] #[test_case("(0x7b, 234_u8, Enum::Three(NestedStructWithField { a: SimpleStruct { a: 0x159 }, b: 0x1c8 }))", "tuple_fn"; "tuple_nested")] #[test_case("array![array![0x2137, 0x420], array![0x420, 0x2137]], 8_u8, -270_i16, \"hello\", (0x2, 100_u32), true, 3_u256", "complex_fn"; "complex")] #[test_case("SimpleStruct { a: 0x12 }", "simple_struct_fn"; "simple_struct")] #[test_case("NestedStructWithField { a: SimpleStruct { a: 0x24 }, b: 0x60 }", "nested_struct_fn"; "nested_struct")] #[test_case("array![0x1, 0x2, 0x3].span()", "span_fn"; "span")] #[test_case("array![].span()", "span_fn"; "span_empty")] #[test_case("Enum::One", "enum_fn"; "enum_no_data")] #[test_case("Enum::Two(128_u128)", "enum_fn"; "enum_tuple")] #[test_case("Enum::Three(NestedStructWithField { a: SimpleStruct { a: 0x7b }, b: 0xea })", "enum_fn"; "enum_nested")] #[test_case(r#"ComplexStruct { a: NestedStructWithField { a: SimpleStruct { a: 0x1 }, b: 0x2 }, b: 0x3, c: 4_u8, d: 5_i32, e: Enum::Two(6_u128), f: "seven", g: array![0x8, 0x9], h: 10_u256, i: (11_i128, 12_u128) }"#, "complex_struct_fn"; "complex_struct")] #[test_case("", "no_args_fn"; "no_arguments_function")] #[tokio::test] async fn test_check_for_identity(calldata: &str, selector: &str) { let abi = get_abi().await; let selector = get_selector_from_name(selector).unwrap(); let felts = transform(calldata, &abi, &selector).unwrap(); let result = reverse_transform_input(&felts, &abi, &selector).unwrap(); assert_eq!(result, calldata); } ================================================ FILE: crates/data-transformer/tests/integration/mod.rs ================================================ use starknet_rust::core::types::contract::AbiEntry; use starknet_rust::core::types::{BlockId, BlockTag, ContractClass}; use starknet_rust::providers::jsonrpc::HttpTransport; use starknet_rust::providers::{JsonRpcClient, Provider}; use starknet_types_core::felt::Felt; use tokio::sync::OnceCell; use url::Url; mod identity; mod reverse_transformer; mod transformer; /// Class hash of the declared `DataTransformer` contract from `/tests/data/data_transformer` const TEST_CLASS_HASH: Felt = Felt::from_hex_unchecked("0x071b56ab58087fd00a0b4ddcdfecb727ae11d1674a4a0f5af7c30f9bb2f7150e"); /// Class hash of the declared `DataTransformerNoConstructor` contract from `/tests/data/data_transformer` const NO_CONSTRUCTOR_CLASS_HASH: Felt = Felt::from_hex_unchecked("0x051d0347d3bfcd87eea5175994f55158a24b003370d8c83d2c430f663eceb08d"); static CLASS: OnceCell = OnceCell::const_new(); async fn init_class(class_hash: Felt) -> ContractClass { let client = JsonRpcClient::new(HttpTransport::new( Url::parse("http://188.34.188.184:7070/rpc/v0_10").unwrap(), )); client .get_class(BlockId::Tag(BlockTag::Latest), class_hash) .await .unwrap() } async fn get_abi() -> Vec { let class = CLASS.get_or_init(|| init_class(TEST_CLASS_HASH)).await; let ContractClass::Sierra(sierra_class) = class else { panic!("Expected Sierra class, but got legacy Sierra class") }; serde_json::from_str(sierra_class.abi.as_str()).unwrap() } ================================================ FILE: crates/data-transformer/tests/integration/reverse_transformer.rs ================================================ use crate::integration::{NO_CONSTRUCTOR_CLASS_HASH, get_abi, init_class}; use data_transformer::{reverse_transform_input, reverse_transform_output}; use itertools::Itertools; use primitive_types::U256; use starknet_rust::core::types::ContractClass; use starknet_rust::core::types::contract::AbiEntry; use starknet_rust::core::utils::get_selector_from_name; use starknet_types_core::felt::Felt; async fn assert_reverse_transformation( input: &[Felt], selector: &str, expected_input: &str, expected_output: Option<&str>, ) { let abi = get_abi().await; let selector = get_selector_from_name(selector).unwrap(); let result = reverse_transform_input(input, &abi, &selector).unwrap(); assert_eq!(result, expected_input); let result = reverse_transform_output(input, &abi, &selector).unwrap(); if let Some(expected_output) = expected_output { assert_eq!(result, expected_output); } else { // tests are written in a way that in most case the output is the same as the input // so passing None means we expect the output to be the same as the input assert_eq!(result, expected_input); } } #[tokio::test] async fn test_unsigned() { assert_reverse_transformation( &[Felt::from(1_010_101_u32)], "unsigned_fn", "1010101_u32", None, ) .await; } #[tokio::test] async fn test_felt() { assert_reverse_transformation( &[Felt::from_hex_unchecked("0x64")], "simple_fn", "0x64", None, ) .await; } #[tokio::test] async fn test_u256_max() { assert_reverse_transformation( &[ Felt::from_hex_unchecked("0xffffffffffffffffffffffffffffffff"), Felt::from_hex_unchecked("0xffffffffffffffffffffffffffffffff"), ], "u256_fn", &format!("{}_u256", U256::MAX), None, ) .await; } #[tokio::test] async fn test_u256() { assert_reverse_transformation( &[ Felt::from_hex_unchecked("0x2137"), Felt::from_hex_unchecked("0x0"), ], "u256_fn", "8503_u256", None, ) .await; } #[tokio::test] async fn test_signed() { assert_reverse_transformation(&[Felt::from(-273i16)], "signed_fn", "-273_i32", None).await; } #[tokio::test] async fn test_u32_max() { assert_reverse_transformation( &[Felt::from(u32::MAX)], "unsigned_fn", &format!("{}_u32", u32::MAX), None, ) .await; } #[tokio::test] async fn test_tuple_enum() { assert_reverse_transformation( &[ Felt::from_hex_unchecked("0x859"), Felt::from_hex_unchecked("0x1"), Felt::from_hex_unchecked("0x0"), ], "tuple_fn", "(0x859, 1_u8, Enum::One)", None, ) .await; } #[tokio::test] async fn test_tuple_enum_nested_struct() { assert_reverse_transformation( &[ Felt::from(123), Felt::from(234), Felt::from(2), Felt::from(345), Felt::from(456), ], "tuple_fn", "(0x7b, 234_u8, Enum::Three(NestedStructWithField { a: SimpleStruct { a: 0x159 }, b: 0x1c8 }))", None ) .await; } #[tokio::test] async fn test_happy_case_complex_function_cairo_expressions_input() { let max_u256 = U256::max_value().to_string(); let expected = format!( "array![array![0x2137, 0x420], array![0x420, 0x2137]], 8_u8, -270_i16, \"some_string\", (0x73686f727420737472696e67, 100_u32), true, {max_u256}_u256" ); let input = [ "0x2", "0x2", "0x2137", "0x420", "0x2", "0x420", "0x2137", "0x8", "0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffef3", "0x0", "0x736f6d655f737472696e67", "0xb", "0x73686f727420737472696e67", "0x64", "0x1", "0xffffffffffffffffffffffffffffffff", "0xffffffffffffffffffffffffffffffff", ] .into_iter() .map(Felt::from_hex_unchecked) .collect_vec(); assert_reverse_transformation(&input, "complex_fn", &expected, Some("")).await; } #[tokio::test] async fn test_simple_struct() { assert_reverse_transformation( &[Felt::from_hex_unchecked("0x12")], "simple_struct_fn", "SimpleStruct { a: 0x12 }", None, ) .await; } #[tokio::test] async fn test_nested_struct() { assert_reverse_transformation( &[ Felt::from_hex_unchecked("0x24"), Felt::from_hex_unchecked("0x60"), ], "nested_struct_fn", "NestedStructWithField { a: SimpleStruct { a: 0x24 }, b: 0x60 }", None, ) .await; } #[tokio::test] async fn test_span() { assert_reverse_transformation( &[ Felt::from_hex_unchecked("0x3"), Felt::from_hex_unchecked("0x1"), Felt::from_hex_unchecked("0x2"), Felt::from_hex_unchecked("0x3"), ], "span_fn", "array![0x1, 0x2, 0x3].span()", None, ) .await; } #[tokio::test] async fn test_span_empty() { assert_reverse_transformation(&[Felt::ZERO], "span_fn", "array![].span()", None).await; } #[tokio::test] async fn test_enum() { assert_reverse_transformation(&[Felt::ZERO], "enum_fn", "Enum::One", None).await; } #[tokio::test] async fn test_enum_tuple() { assert_reverse_transformation( &[ Felt::from_hex_unchecked("0x1"), Felt::from_hex_unchecked("0x80"), ], "enum_fn", "Enum::Two(128_u128)", None, ) .await; } #[tokio::test] async fn test_enum_nested_struct() { assert_reverse_transformation( &[ Felt::from_hex_unchecked("0x2"), Felt::from_hex_unchecked("0x7b"), Felt::from_hex_unchecked("0xea"), ], "enum_fn", "Enum::Three(NestedStructWithField { a: SimpleStruct { a: 0x7b }, b: 0xea })", None, ) .await; } #[tokio::test] async fn test_complex_struct() { let expected = r#"ComplexStruct { a: NestedStructWithField { a: SimpleStruct { a: 0x1 }, b: 0x2 }, b: 0x3, c: 4_u8, d: 5_i32, e: Enum::Two(6_u128), f: "seven", g: array![0x8, 0x9], h: 10_u256, i: (11_i128, 12_u128) }"#; let input = [ // a: NestedStruct Felt::from_hex_unchecked("0x1"), Felt::from_hex_unchecked("0x2"), // b: felt252 Felt::from_hex_unchecked("0x3"), // c: u8 Felt::from_hex_unchecked("0x4"), // d: i32 Felt::from_hex_unchecked("0x5"), // e: Enum Felt::from_hex_unchecked("0x1"), Felt::from_hex_unchecked("0x6"), // f: ByteArray Felt::from_hex_unchecked("0x0"), Felt::from_hex_unchecked("0x736576656e"), Felt::from_hex_unchecked("0x5"), // g: Array Felt::from_hex_unchecked("0x2"), Felt::from_hex_unchecked("0x8"), Felt::from_hex_unchecked("0x9"), // h: u256 Felt::from_hex_unchecked("0xa"), Felt::from_hex_unchecked("0x0"), // i: (i128, u128) Felt::from_hex_unchecked("0xb"), Felt::from_hex_unchecked("0xc"), ]; assert_reverse_transformation(&input, "complex_struct_fn", expected, None).await; } #[tokio::test] async fn test_external_type() { let input = [ Felt::from_hex_unchecked("0x17"), Felt::from_hex_unchecked("0x1"), Felt::from_hex_unchecked("0x0"), Felt::from_hex_unchecked("0x1"), Felt::from_hex_unchecked("0x2"), Felt::from_hex_unchecked("0x3"), ]; let expected = "BitArray { bit: 0x17 }, BitArray { data: array![CairoBytes31(0x0)], current: 0x1, read_pos: 2_u32, write_pos: 3_u32 }"; assert_reverse_transformation( &input, "external_struct_fn", expected, Some(&format!("({expected})")), ) .await; } #[tokio::test] async fn test_constructor() { assert_reverse_transformation( &[Felt::from_hex_unchecked("0x123")], "constructor", "ContractAddress(0x123)", Some(""), ) .await; } #[tokio::test] async fn test_multiple_signed() { assert_reverse_transformation( &[Felt::from(124), Felt::from(97)], "multiple_signed_fn", "124_i32, 97_i8", Some(""), ) .await; } #[tokio::test] async fn test_multiple_signed_min() { assert_reverse_transformation( &[Felt::from(i32::MIN), Felt::from(i8::MIN)], "multiple_signed_fn", "-2147483648_i32, -128_i8", Some(""), ) .await; } #[tokio::test] async fn test_multiple_signed_max() { assert_reverse_transformation( &[Felt::from(i32::MAX), Felt::from(i8::MAX)], "multiple_signed_fn", "2147483647_i32, 127_i8", Some(""), ) .await; } #[tokio::test] async fn test_no_argument_function() { assert_reverse_transformation(&[], "no_args_fn", "", None).await; } #[tokio::test] async fn test_implicit_contract_constructor() { let class = init_class(NO_CONSTRUCTOR_CLASS_HASH).await; let ContractClass::Sierra(sierra_class) = class else { panic!("Expected Sierra class, but got legacy Sierra class") }; let abi: Vec = serde_json::from_str(sierra_class.abi.as_str()).unwrap(); let result = reverse_transform_input(&[], &abi, &get_selector_from_name("constructor").unwrap()) .unwrap(); let expected_output = ""; assert_eq!(result, expected_output); } ================================================ FILE: crates/data-transformer/tests/integration/transformer.rs ================================================ use crate::integration::{NO_CONSTRUCTOR_CLASS_HASH, get_abi, init_class}; use core::fmt; use data_transformer::transform; use indoc::indoc; use itertools::Itertools; use primitive_types::U256; use starknet_rust::core::types::contract::AbiEntry; use starknet_rust::core::types::{BlockId, BlockTag, ContractClass}; use starknet_rust::core::utils::get_selector_from_name; use starknet_rust::providers::jsonrpc::HttpTransport; use starknet_rust::providers::{JsonRpcClient, Provider}; use starknet_types_core::felt::Felt; use std::ops::Not; use test_case::test_case; trait Contains { fn assert_contains(&self, value: T); } impl Contains<&str> for anyhow::Error { fn assert_contains(&self, value: &str) { self.chain() .any(|err| err.to_string().contains(value)) .not() .then(|| panic!("{value:?}\nnot found in\n{self:#?}")); } } async fn run_transformer(input: &str, selector: &str) -> anyhow::Result> { let abi = get_abi().await; transform( input, &abi, &get_selector_from_name(selector).expect("should be valid selector"), ) } #[tokio::test] async fn test_function_not_found() { let selector = "nonexistent_fn"; let result = run_transformer("('some_felt',)", selector).await; result.unwrap_err().assert_contains( format!( r#"Function with selector "{:#x}" not found in ABI of the contract"#, get_selector_from_name(selector).unwrap() ) .as_str(), ); } #[tokio::test] async fn test_happy_case_numeric_type_suffix() { let result = run_transformer("1010101_u32", "unsigned_fn").await.unwrap(); let expected_output = [Felt::from(1_010_101_u32)]; assert_eq!(result, expected_output); } #[tokio::test] async fn test_invalid_numeric_type_suffix() { let result = run_transformer("1_u10", "simple_fn").await; result .unwrap_err() .assert_contains(r#"Failed to parse value "1" into type "u10": unsupported type u10"#); } #[tokio::test] async fn test_invalid_cairo_expression() { let result = run_transformer("(some_invalid_expression:,)", "simple_fn").await; result .unwrap_err() .assert_contains("Invalid Cairo expression found in input calldata"); } #[tokio::test] async fn test_invalid_argument_number() { let result = run_transformer("0x123, 'some_obsolete_argument', 10", "simple_fn").await; result .unwrap_err() .assert_contains("Invalid number of arguments: passed 3, expected 1"); } #[tokio::test] async fn test_happy_case_simple_cairo_expressions_input() { let result = run_transformer("100", "simple_fn").await.unwrap(); let expected_output = [Felt::from_hex_unchecked("0x64")]; assert_eq!(result, expected_output); } #[tokio::test] async fn test_happy_case_u256_function_cairo_expressions_input_decimal() { let result = run_transformer(&format!("{}_u256", U256::MAX), "u256_fn") .await .unwrap(); let expected_output = [ Felt::from_hex_unchecked("0xffffffffffffffffffffffffffffffff"), Felt::from_hex_unchecked("0xffffffffffffffffffffffffffffffff"), ]; assert_eq!(result, expected_output); } #[tokio::test] async fn test_happy_case_u256_function_cairo_expressions_input_hex() { let result = run_transformer("0x2137_u256", "u256_fn").await.unwrap(); let expected_output = [ Felt::from_hex_unchecked("0x2137"), Felt::from_hex_unchecked("0x0"), ]; assert_eq!(result, expected_output); } #[tokio::test] async fn test_happy_case_signed_function_cairo_expressions_input() { let result = run_transformer("-273", "signed_fn").await.unwrap(); let expected_output = [Felt::from(-273i16)]; assert_eq!(result, expected_output); } // Problem: Although transformer fails to process the given input as `i32`, it then succeeds to interpret it as `felt252` // Overflow checks will not work for functions having the same serialized and Cairo-like calldata length. // User must provide a type suffix or get the invoke-time error // Issue #2559 #[ignore = "Impossible to pass with the current solution"] #[tokio::test] async fn test_signed_fn_overflow() { let result = run_transformer(&format!("({},)", i32::MAX as u64 + 1), "signed_fn").await; result .unwrap_err() .assert_contains(r#"Failed to parse value "2147483648" into type "i32""#); } #[tokio::test] async fn test_signed_fn_overflow_with_type_suffix() { let result = run_transformer(&format!("{}_i32", i32::MAX as u64 + 1), "signed_fn").await; result .unwrap_err() .assert_contains(r#"Failed to parse value "2147483648" into type "i32""#); } #[tokio::test] async fn test_happy_case_unsigned_function_cairo_expressions_input() { let result = run_transformer(&format!("{}", u32::MAX), "unsigned_fn") .await .unwrap(); let expected_output = [Felt::from(u32::MAX)]; assert_eq!(result, expected_output); } #[tokio::test] async fn test_happy_case_tuple_function_cairo_expression_input() { let result = run_transformer("(2137_felt252, 1_u8, Enum::One)", "tuple_fn") .await .unwrap(); let expected_output = [ Felt::from_hex_unchecked("0x859"), Felt::from_hex_unchecked("0x1"), Felt::from_hex_unchecked("0x0"), ]; assert_eq!(result, expected_output); } #[tokio::test] async fn test_happy_case_tuple_function_with_nested_struct_cairo_expression_input() { let result = run_transformer( "(123, 234, Enum::Three(NestedStructWithField {a: SimpleStruct {a: 345}, b: 456 }))", "tuple_fn", ) .await .unwrap(); let expected_output = [123, 234, 2, 345, 456] .into_iter() .map(Felt::from) .collect_vec(); assert_eq!(result, expected_output); } #[tokio::test] async fn test_happy_case_complex_function_cairo_expressions_input() { let max_u256 = U256::max_value().to_string(); let input = format!( "array![array![0x2137, 0x420], array![0x420, 0x2137]], 8_u8, -270, \"some_string\", ('short string', 100), true, {max_u256}", ); let result = run_transformer(&input, "complex_fn").await.unwrap(); // Manually serialized in Cairo let expected_output = [ "0x2", "0x2", "0x2137", "0x420", "0x2", "0x420", "0x2137", "0x8", "0x800000000000010fffffffffffffffffffffffffffffffffffffffffffffef3", "0x0", "0x736f6d655f737472696e67", "0xb", "0x73686f727420737472696e67", "0x64", "0x1", "0xffffffffffffffffffffffffffffffff", "0xffffffffffffffffffffffffffffffff", ] .into_iter() .map(Felt::from_hex_unchecked) .collect_vec(); assert_eq!(result, expected_output); } #[tokio::test] async fn test_happy_case_simple_struct_function_cairo_expression_input() { let result = run_transformer("SimpleStruct {a: 0x12}", "simple_struct_fn") .await .unwrap(); let expected_output = [Felt::from_hex_unchecked("0x12")]; assert_eq!(result, expected_output); } #[tokio::test] async fn test_simple_struct_function_invalid_struct_argument() { let result = run_transformer(r#"SimpleStruct {a: "string"}"#, "simple_struct_fn").await; result .unwrap_err() .assert_contains(r#"Failed to parse value "string" into type "core::felt252""#); } #[tokio::test] async fn test_simple_struct_function_invalid_struct_name() { let result = run_transformer("InvalidStructName {a: 0x10}", "simple_struct_fn").await; result .unwrap_err() .assert_contains(r#"Invalid argument type, expected "data_transformer_contract::SimpleStruct", got "InvalidStructName""#); } #[test_case(r#""string_argument""#, r#"Failed to parse value "string_argument" into type "data_transformer_contract::SimpleStruct""# ; "string")] #[test_case("'shortstring'", r#"Failed to parse value "shortstring" into type "data_transformer_contract::SimpleStruct""# ; "shortstring")] #[test_case("true", r#"Failed to parse value "true" into type "data_transformer_contract::SimpleStruct""# ; "bool")] #[test_case("array![0x1, 2, 0x3, 04]", r#"Invalid argument type, expected "data_transformer_contract::SimpleStruct", got array"# ; "array")] #[test_case("(1, array![2], 0x3)", r#"Invalid argument type, expected "data_transformer_contract::SimpleStruct", got tuple"# ; "tuple")] #[test_case("My::Enum", r#"Invalid argument type, expected "data_transformer_contract::SimpleStruct", got "My""# ; "enum_variant")] #[test_case("core::path::My::Enum(10)", r#"Invalid argument type, expected "data_transformer_contract::SimpleStruct", got "core::path::My""# ; "enum_variant_with_path")] #[tokio::test] async fn test_simple_struct_function_cairo_expression_input_invalid_argument_type( input: &str, error_message: &str, ) { let result = run_transformer(input, "simple_struct_fn").await; result.unwrap_err().assert_contains(error_message); } #[tokio::test] async fn test_happy_case_nested_struct_function_cairo_expression_input() { let result = run_transformer( "NestedStructWithField { a: SimpleStruct { a: 0x24 }, b: 96 }", "nested_struct_fn", ) .await .unwrap(); let expected_output = [ Felt::from_hex_unchecked("0x24"), Felt::from_hex_unchecked("0x60"), ]; assert_eq!(result, expected_output); } #[tokio::test] async fn test_happy_case_span_function_cairo_expression_input() { let result = run_transformer("array![1, 2, 3].span()", "span_fn") .await .unwrap(); let expected_output = [ Felt::from_hex_unchecked("0x3"), Felt::from_hex_unchecked("0x1"), Felt::from_hex_unchecked("0x2"), Felt::from_hex_unchecked("0x3"), ]; assert_eq!(result, expected_output); } #[tokio::test] async fn test_happy_case_empty_span_function_cairo_expression_input() { let result = run_transformer("array![].span()", "span_fn").await.unwrap(); let expected_output = [Felt::from_hex_unchecked("0x0")]; assert_eq!(result, expected_output); } #[tokio::test] async fn test_span_function_array_input() { let result = run_transformer("array![1, 2, 3]", "span_fn").await; result .unwrap_err() .assert_contains(r#"Expected "core::array::Span::", got array"#); } #[tokio::test] async fn test_span_function_unsupported_method() { let result = run_transformer("array![1, 2, 3].into()", "span_fn").await; result .unwrap_err() .assert_contains(r#"Invalid function name, expected "span", got "into""#); } #[tokio::test] async fn test_span_function_unsupported_operator() { let result = run_transformer("array![1, 2, 3]*span()", "span_fn").await; result .unwrap_err() .assert_contains(r#"Invalid operator, expected ".", got "*""#); } #[tokio::test] async fn test_span_function_unsupported_right_hand_side() { let result = run_transformer("array![1, 2, 3].span", "span_fn").await; result .unwrap_err() .assert_contains(r#"Only calling ".span()" on "array![]" is supported, got "span""#); } #[tokio::test] async fn test_span_function_unsupported_left_hand_side() { let result = run_transformer("(1, 2, 3).span", "span_fn").await; result.unwrap_err().assert_contains( r#"Only "array![]" is supported as left-hand side of "." operator, got "(1, 2, 3)""#, ); } #[tokio::test] async fn test_happy_case_enum_function_empty_variant_cairo_expression_input() { let result = run_transformer("Enum::One", "enum_fn").await.unwrap(); let expected_output = [Felt::ZERO]; assert_eq!(result, expected_output); } #[tokio::test] async fn test_happy_case_enum_function_one_argument_variant_cairo_expression_input() -> anyhow::Result<()> { let abi = get_abi().await; let result = transform("Enum::Two(128)", &abi, &get_selector_from_name("enum_fn")?)?; let expected_output = [ Felt::from_hex_unchecked("0x1"), Felt::from_hex_unchecked("0x80"), ]; assert_eq!(result, expected_output); Ok(()) } #[tokio::test] async fn test_happy_case_enum_function_nested_struct_variant_cairo_expression_input() { let result = run_transformer( "Enum::Three(NestedStructWithField { a: SimpleStruct { a: 123 }, b: 234 })", "enum_fn", ) .await .unwrap(); let expected_output = [ Felt::from_hex_unchecked("0x2"), Felt::from_hex_unchecked("0x7b"), Felt::from_hex_unchecked("0xea"), ]; assert_eq!(result, expected_output); } #[tokio::test] async fn test_enum_function_invalid_variant_cairo_expression_input() { let result = run_transformer("Enum::InvalidVariant", "enum_fn").await; result .unwrap_err() .assert_contains(r#"Couldn't find variant "InvalidVariant" in enum "Enum""#); } #[tokio::test] async fn test_happy_case_complex_struct_function_cairo_expression_input() { let data = indoc!( r#" ComplexStruct { a: NestedStructWithField { a: SimpleStruct { a: 1 }, b: 2 }, b: 3, c: 4, d: 5, e: Enum::Two(6), f: "seven", g: array![8, 9], h: 10, i: (11, 12) } "# ); let result = run_transformer(data, "complex_struct_fn").await.unwrap(); let expected_output = [ // a: NestedStruct Felt::from_hex_unchecked("0x1"), Felt::from_hex_unchecked("0x2"), // b: felt252 Felt::from_hex_unchecked("0x3"), // c: u8 Felt::from_hex_unchecked("0x4"), // d: i32 Felt::from_hex_unchecked("0x5"), // e: Enum Felt::from_hex_unchecked("0x1"), Felt::from_hex_unchecked("0x6"), // f: ByteArray Felt::from_hex_unchecked("0x0"), Felt::from_hex_unchecked("0x736576656e"), Felt::from_hex_unchecked("0x5"), // g: Array Felt::from_hex_unchecked("0x2"), Felt::from_hex_unchecked("0x8"), Felt::from_hex_unchecked("0x9"), // h: u256 Felt::from_hex_unchecked("0xa"), Felt::from_hex_unchecked("0x0"), // i: (i128, u128) Felt::from_hex_unchecked("0xb"), Felt::from_hex_unchecked("0xc"), ]; assert_eq!(result, expected_output); } #[tokio::test] async fn test_external_struct_function_ambiguous_struct_name_cairo_expression_input() { let input = " BitArray { bit: 23 }, \ BitArray { data: array![0], current: 1, read_pos: 2, write_pos: 3 } "; let result = run_transformer(input, "external_struct_fn").await; result.unwrap_err().assert_contains( r#"Found more than one struct "BitArray" in ABI, please specify a full path to the item"#, ); } #[tokio::test] async fn test_happy_case_external_struct_function_cairo_expression_input() { let input = indoc!( " data_transformer_contract::BitArray { bit: 23 }, \ alexandria_data_structures::bit_array::BitArray { data: array![0], current: 1, read_pos: 2, write_pos: 3 } " ); let result = run_transformer(input, "external_struct_fn").await.unwrap(); let expected_output = [ Felt::from_hex_unchecked("0x17"), Felt::from_hex_unchecked("0x1"), Felt::from_hex_unchecked("0x0"), Felt::from_hex_unchecked("0x1"), Felt::from_hex_unchecked("0x2"), Felt::from_hex_unchecked("0x3"), ]; assert_eq!(result, expected_output); } #[tokio::test] async fn test_external_struct_function_invalid_path_to_external_struct() { let input = indoc!( " something::BitArray { bit: 23 }, \ BitArray { data: array![0], current: 1, read_pos: 2, write_pos: 3 } " ); let result = run_transformer(input, "external_struct_fn").await; result .unwrap_err() .assert_contains(r#"Struct "something::BitArray" not found in ABI"#); } #[tokio::test] async fn test_happy_case_contract_constructor() { let result = run_transformer("0x123", "constructor").await.unwrap(); let expected_output = [Felt::from_hex_unchecked("0x123")]; assert_eq!(result, expected_output); } #[tokio::test] async fn test_happy_case_no_argument_function() { let result = run_transformer("", "no_args_fn").await.unwrap(); let expected_output = []; assert_eq!(result, expected_output); } #[tokio::test] async fn test_happy_case_implicit_contract_constructor() { let class = init_class(NO_CONSTRUCTOR_CLASS_HASH).await; let ContractClass::Sierra(sierra_class) = class else { panic!("Expected Sierra class, but got legacy Sierra class") }; let abi: Vec = serde_json::from_str(sierra_class.abi.as_str()).unwrap(); let result = transform("", &abi, &get_selector_from_name("constructor").unwrap()).unwrap(); let expected_output = []; assert_eq!(result, expected_output); } #[tokio::test] async fn test_external_enum_function_ambiguous_enum_name_cairo_expression_input() { // https://sepolia.voyager.online/class/0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d#code let test_class_hash: Felt = Felt::from_hex_unchecked( "0x019ea00ebe2d83fb210fbd6f52c302b83c69e3c8c934f9404c87861e9d3aebbc", ); let client = JsonRpcClient::new(HttpTransport::new( shared::test_utils::node_url::node_rpc_url(), )); let contract_class = client .get_class(BlockId::Tag(BlockTag::Latest), test_class_hash) .await .unwrap(); let input = " TransactionState::Init() , \ TransactionState::NotFound() "; let ContractClass::Sierra(sierra_class) = contract_class else { panic!("Expected Sierra class, but got legacy Sierra class") }; let abi: Vec = serde_json::from_str(sierra_class.abi.as_str()).unwrap(); let result = transform( input, &abi, &get_selector_from_name("external_enum_fn").unwrap(), ); result.unwrap_err().assert_contains( r#"Found more than one enum "TransactionState" in ABI, please specify a full path to the item"#, ); } ================================================ FILE: crates/data-transformer/tests/lib.rs ================================================ mod integration; mod unit; ================================================ FILE: crates/data-transformer/tests/unit/bytes31.rs ================================================ use data_transformer::cairo_types::{CairoBytes31, ParseBytes31Error}; use starknet_types_core::felt::Felt; use std::str::FromStr; #[cfg(test)] mod tests_bytes31 { use super::*; use test_case::test_case; #[test] fn test_happy_case() { let bytes31 = CairoBytes31::from_str("0x123456789abcdef").unwrap(); assert_eq!( Felt::from(bytes31), Felt::from_hex_unchecked("0x123456789abcdef") ); } #[test] fn test_max_value() { let max_bytes31 = CairoBytes31::MAX; let from_str = CairoBytes31::from_str( "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", ) .unwrap(); assert_eq!(max_bytes31, from_str); } #[test] fn test_overflow() { let result = CairoBytes31::from_str( "0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", ); assert!(matches!(result, Err(ParseBytes31Error::Overflow))); } #[test_case("wrong_string_value"; "wrong string value")] #[test_case(""; "empty string")] fn test_invalid_string(input: &str) { let result = CairoBytes31::from_str(input); assert!(matches!(result, Err(ParseBytes31Error::CairoFromStrError))); } #[test] fn test_felt_conversion() { let bytes31 = CairoBytes31::from_str("0x123").unwrap(); let felt: Felt = bytes31.into(); assert_eq!(felt, Felt::from_hex_unchecked("0x123")); } #[test] fn test_zero_value() { let bytes31 = CairoBytes31::from_str("0x0").unwrap(); assert_eq!(Felt::from(bytes31), Felt::ZERO); } #[test] fn test_leading_zeros() { let bytes31 = CairoBytes31::from_str("0x000123").unwrap(); assert_eq!(Felt::from(bytes31), Felt::from_hex_unchecked("0x123")); } } ================================================ FILE: crates/data-transformer/tests/unit/mod.rs ================================================ mod bytes31; mod u384; mod u96; ================================================ FILE: crates/data-transformer/tests/unit/u384.rs ================================================ use data_transformer::cairo_types::CairoU384; use data_transformer::cairo_types::ParseCairoU384Error; use std::str::FromStr; #[cfg(test)] mod tests { use super::*; #[test] fn test_from_bytes() { let mut input = [0u8; 48]; input[36..48].copy_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4]); // limb_0 input[24..36].copy_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3]); // limb_1 input[12..24].copy_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]); // limb_2 input[0..12].copy_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); // limb_3 let first = CairoU384::from_bytes(&input); let second = CairoU384::from_bytes(&input); assert_eq!(first, second); } #[test] fn test_valid_decimal() { let input = "123456789"; let result = CairoU384::from_str(input).unwrap(); let mut expected = [0u8; 48]; expected[44..48].copy_from_slice(&[0x07, 0x5B, 0xCD, 0x15]); let expected = CairoU384::from_bytes(&expected); assert_eq!(result, expected); } #[test] fn test_valid_hex() { let input = "0x1234567890abcdef"; let result = CairoU384::from_str(input).unwrap(); let mut expected = [0u8; 48]; expected[40..48].copy_from_slice(&[0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF]); let expected = CairoU384::from_bytes(&expected); assert_eq!(result, expected); } #[test] fn test_overflow() { let large_hex = "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; let result = CairoU384::from_str(large_hex); assert!(matches!(result, Err(ParseCairoU384Error::Overflow))); } #[test] fn test_zero_value() { let zero = "0"; let result = CairoU384::from_str(zero).unwrap(); let expected = CairoU384::from_bytes(&[0u8; 48]); assert_eq!(result, expected); } #[test] fn test_max_value() { let max_value = "0xffffffffffffffffffffffffffffffff"; let result = CairoU384::from_str(max_value).unwrap(); let mut bytes = [0u8; 48]; let start = 48 - max_value[2..].len() / 2; bytes[start..].fill(0xFF); let expected = CairoU384::from_bytes(&bytes); assert_eq!(result, expected); } } ================================================ FILE: crates/data-transformer/tests/unit/u96.rs ================================================ #[cfg(test)] mod tests_cairo_u96 { use data_transformer::cairo_types::{CairoU96, ParseCairoU96Error}; use starknet_types_core::felt::Felt; use std::str::FromStr; use test_case::test_case; const U96_MAX: u128 = (2u128 << 96) - 1; #[test_case("0", 0_u128 ; "zero")] #[test_case("123", 123_u128 ; "small decimal")] #[test_case("1000000", 1_000_000_u128 ; "million")] #[test_case("ff", 0xff_u128 ; "small hex")] #[test_case("1234abcd", 0x1234_abcd_u128 ; "large hex")] fn test_valid_numbers(input: &str, expected: u128) { let parsed = CairoU96::from_str(input).unwrap(); assert_eq!( Felt::from(parsed), Felt::from(expected), "Failed parsing {input} - expected {expected}" ); } #[test] fn test_max_value() { let max_value_str = U96_MAX.to_string(); let result = CairoU96::from_str(&max_value_str).unwrap(); assert_eq!(Felt::from(result), Felt::from(U96_MAX)); let max_value_hex = format!("{U96_MAX:x}"); let result_hex = CairoU96::from_str(&max_value_hex).unwrap(); assert_eq!(Felt::from(result_hex), Felt::from(U96_MAX)); } #[test_case("" ; "empty string")] #[test_case("not_a_number" ; "invalid string")] fn test_invalid_input(input: &str) { assert!(matches!( CairoU96::from_str(input), Err(ParseCairoU96Error::InvalidString(_)) )); } #[test_case("0", 0_u128 ; "zero conversion")] #[test_case("123", 123_u128 ; "small number conversion")] #[test_case("1000000", 1_000_000_u128 ; "million conversion")] #[test_case("ff", 255_u128 ; "hex conversion")] #[test_case(&U96_MAX.to_string(), U96_MAX ; "max value conversion")] fn test_felt_conversion(input: &str, expected: u128) { let cairo_u96 = CairoU96::from_str(input).unwrap(); assert_eq!(Felt::from(cairo_u96), Felt::from(expected)); } } ================================================ FILE: crates/debugging/Cargo.toml ================================================ [package] name = "debugging" version = "1.0.0" edition.workspace = true [dependencies] blockifier.workspace = true cairo-lang-sierra.workspace = true cairo-lang-sierra-to-casm.workspace = true cairo-lang-starknet-classes.workspace = true cheatnet = { path = "../cheatnet" } console.workspace = true data-transformer = { path = "../data-transformer" } paste.workspace = true ptree.workspace = true rayon.workspace = true serde_json.workspace = true starknet-types-core.workspace = true starknet-rust.workspace = true starknet_api.workspace = true thiserror.workspace = true ================================================ FILE: crates/debugging/src/contracts_data_store.rs ================================================ use crate::trace::types::{ContractName, Selector}; use cairo_lang_sierra::program::ProgramArtifact; use cairo_lang_starknet_classes::contract_class::ContractClass; use cheatnet::forking::data::ForkData; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; use rayon::iter::IntoParallelRefIterator; use rayon::iter::ParallelIterator; use starknet_api::core::{ClassHash, EntryPointSelector}; use starknet_rust::core::types::contract::{AbiEntry, SierraClass}; use std::collections::HashMap; /// Data structure containing information about contracts, /// including their ABI, names, selectors and programs that will be used to create a [`Trace`](crate::Trace). pub struct ContractsDataStore { abi: HashMap>, contract_names: HashMap, selectors: HashMap, programs: HashMap, } impl ContractsDataStore { /// Creates a new instance of [`ContractsDataStore`] from a similar structure from `cheatnet`: [`ContractsData`] and [`ForkData`]. #[must_use] pub fn new(contracts_data: &ContractsData, fork_data: &ForkData) -> Self { let contract_names = contracts_data .class_hashes .iter() .map(|(name, class_hash)| (*class_hash, ContractName(name.clone()))) .collect(); let selectors = contracts_data .selectors .iter() .chain(&fork_data.selectors) .map(|(selector, function_name)| (*selector, Selector(function_name.clone()))) .collect(); let abi = contracts_data .contracts .par_iter() .map(|(_, contract_data)| { let sierra = serde_json::from_str::(&contract_data.artifacts.sierra) .expect("this should be valid `SierraClass`"); (contract_data.class_hash, sierra.abi) }) .chain(fork_data.abi.clone()) .collect(); let programs = contracts_data .contracts .par_iter() .map(|(_, contract_data)| { let contract_class = serde_json::from_str::(&contract_data.artifacts.sierra) .expect("this should be valid `ContractClass`"); let program = contract_class .extract_sierra_program(false) .expect("extraction should succeed") .program; let debug_info = contract_class.sierra_program_debug_info; let program_artifact = ProgramArtifact { program, debug_info, }; (contract_data.class_hash, program_artifact) }) .collect(); Self { abi, contract_names, selectors, programs, } } /// Gets the [`ContractName`] for a given contract [`ClassHash`]. #[must_use] pub fn get_contract_name(&self, class_hash: &ClassHash) -> Option<&ContractName> { self.contract_names.get(class_hash) } /// Gets the `abi` for a given contract [`ClassHash`] from [`ContractsDataStore`]. pub fn get_abi(&self, class_hash: &ClassHash) -> Option<&[AbiEntry]> { self.abi.get(class_hash).map(Vec::as_slice) } /// Gets the [`Selector`] in human-readable form for a given [`EntryPointSelector`] from [`ContractsDataStore`]. #[must_use] pub fn get_selector(&self, entry_point_selector: &EntryPointSelector) -> Option<&Selector> { self.selectors.get(entry_point_selector) } /// Gets the [`ProgramArtifact`] for a given contract [`ClassHash`]. #[must_use] pub fn get_program_artifact(&self, class_hash: &ClassHash) -> Option<&ProgramArtifact> { self.programs.get(class_hash) } } ================================================ FILE: crates/debugging/src/lib.rs ================================================ //! Crate with debugging utilities in forge. //! //! Currently, the main purpose of this crate is displaying pretty traces. //! The entry point for that is the [`Trace`] struct that implements the [`Display`](std::fmt::Display) //! which allows for pretty printing of traces. mod contracts_data_store; mod trace; mod tree; pub use contracts_data_store::ContractsDataStore; pub use trace::components::{Component, Components}; pub use trace::{context::Context, types::ContractName, types::Trace}; ================================================ FILE: crates/debugging/src/trace/collect.rs ================================================ use crate::contracts_data_store::ContractsDataStore; use crate::trace::types::{ CallerAddress, ContractAddress, ContractName, ContractTrace, Gas, Selector, TestName, TraceInfo, TransformedCallResult, TransformedCalldata, }; use crate::{Context, Trace}; use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::rpc::{ CallFailure, CallSuccess, }; use cheatnet::trace_data::{CallTrace, CallTraceNode}; use data_transformer::{reverse_transform_input, reverse_transform_output}; use starknet_api::core::ClassHash; use starknet_api::execution_utils::format_panic_data; use starknet_rust::core::types::contract::AbiEntry; pub struct Collector<'a> { call_trace: &'a CallTrace, context: &'a Context, } impl<'a> Collector<'a> { /// Creates a new [`Collector`] from a given `cheatnet` [`CallTrace`], [`ContractsDataStore`] and [`Verbosity`]. #[must_use] pub fn new(call_trace: &'a CallTrace, context: &'a Context) -> Collector<'a> { Collector { call_trace, context, } } pub fn collect_trace(&self, test_name: String) -> Trace { Trace { test_name: TestName(test_name), nested_calls: self.collect_nested_calls(), } } fn collect_contract_trace(&self) -> ContractTrace { let components = self.context.components(); let entry_point = &self.call_trace.entry_point; let nested_calls = self.collect_nested_calls(); let contract_name = self.collect_contract_name(); let abi = self.collect_abi(); let trace_info = TraceInfo { contract_name: components.contract_name(contract_name), entry_point_type: components.entry_point_type(entry_point.entry_point_type), calldata: components.calldata_lazy(|| self.collect_transformed_calldata(abi)), contract_address: components .contract_address(ContractAddress(entry_point.storage_address)), caller_address: components.caller_address(CallerAddress(entry_point.caller_address)), call_type: components.call_type(entry_point.call_type), nested_calls, call_result: components.call_result_lazy(|| self.collect_transformed_call_result(abi)), gas: components.gas_lazy(|| self.collect_gas()), }; ContractTrace { selector: self.collect_selector().clone(), trace_info, } } fn collect_nested_calls(&self) -> Vec { self.call_trace .nested_calls .iter() .filter_map(CallTraceNode::extract_entry_point_call) .filter_map(|call_trace| { let call_trace = call_trace.borrow(); // Filter mock calls that have empty class hashes. call_trace.entry_point.class_hash.is_some().then(|| { Collector { call_trace: &call_trace, context: self.context, } .collect_contract_trace() }) }) .collect() } fn collect_contract_name(&self) -> ContractName { self.contracts_data_store() .get_contract_name(self.class_hash()) .cloned() .unwrap_or_else(|| ContractName("forked contract".to_string())) } fn collect_selector(&self) -> &Selector { self.contracts_data_store() .get_selector(&self.call_trace.entry_point.entry_point_selector) .expect("`Selector` should be present") } fn collect_abi(&self) -> &[AbiEntry] { self.contracts_data_store() .get_abi(self.class_hash()) .expect("`ABI` should be present") } fn collect_transformed_calldata(&self, abi: &[AbiEntry]) -> TransformedCalldata { TransformedCalldata( reverse_transform_input( &self.call_trace.entry_point.calldata.0, abi, &self.call_trace.entry_point.entry_point_selector.0, ) .expect("calldata should be successfully transformed"), ) } fn collect_transformed_call_result(&self, abi: &[AbiEntry]) -> TransformedCallResult { TransformedCallResult(match &self.call_trace.result { Ok(CallSuccess { ret_data }) => { let ret_data = reverse_transform_output( ret_data, abi, &self.call_trace.entry_point.entry_point_selector.0, ) .expect("call result should be successfully transformed"); format_result_message("success", &ret_data) } Err(failure) => match failure { CallFailure::Recoverable { panic_data } => { format_result_message("panic", &format_panic_data(panic_data)) } CallFailure::Unrecoverable { msg } => { format_result_message("error", &msg.to_string()) } }, }) } fn collect_gas(&self) -> Gas { Gas(self .call_trace .gas_report_data .as_ref() .expect("Gas report data must be updated after test execution") .get_gas() .l2_gas) } fn class_hash(&self) -> &ClassHash { self.call_trace .entry_point .class_hash .as_ref() .expect("Entries with empty class hash are filtered in `collect_nested_calls`") } fn contracts_data_store(&self) -> &ContractsDataStore { self.context.contracts_data_store() } } fn format_result_message(tag: &str, message: &str) -> String { if message.is_empty() { tag.to_string() } else { format!("{tag}: {message}") } } ================================================ FILE: crates/debugging/src/trace/components.rs ================================================ use crate::trace::types::{ CallerAddress, ContractAddress, ContractName, Gas, TransformedCallResult, TransformedCalldata, }; use blockifier::execution::entry_point::CallType; use paste::paste; use starknet_api::contract_class::EntryPointType; use std::collections::HashSet; /// Represents a set of [`Component`] that will be included in a trace. pub struct Components { set: HashSet, } impl Components { /// Creates a new [`Components`] instance with the specified set of [`Component`]. #[must_use] pub fn new(components: HashSet) -> Self { Self { set: components } } /// Checks if a specific [`Component`] is included in the set. #[must_use] pub fn contains(&self, component: &Component) -> bool { self.set.contains(component) } } /// Components that will be included in the trace. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Component { /// The name of the contract being called. ContractName, /// The type of the entry point being called (e.g., `External`, `L1Handler`, etc.). EntryPointType, /// The calldata of the call, transformed for display. Calldata, /// The address of the contract being called. ContractAddress, /// The address of the caller contract. CallerAddress, /// The type of the call (e.g., `Call`, `Delegate`, etc.). CallType, /// The result of the call, transformed for display. CallResult, /// The L2 gas used by the call. Gas, } macro_rules! impl_component_container { // Short form - uses the same name for both the Component variant and the value type ($variant:ident) => { impl_component_container!($variant, $variant); }; // Full form - Component name and different value type ($variant:ident, $ty:ty) => { paste! { #[doc = concat!( "Container for a `", stringify!($ty), "` that is computed only if `Component::", stringify!($variant), "` is included in the [`Components`]." )] #[derive(Debug, Clone)] pub struct [<$variant Container>] { value: Option<$ty>, } impl [<$variant Container>] { #[doc = concat!( "Creates a new [`", stringify!($variant), "Container`] from a specific value.\n\n", "This will store the value only if `Component::", stringify!($variant), "` is present in the [`Components`]." )] #[must_use] pub fn new(components: &Components, value: $ty) -> Self { Self { value: components.contains(&Component::$variant).then_some(value), } } #[doc = concat!( "Creates a new [`", stringify!($variant), "Container`] using a lazy supplier function.\n\n", "The function will be called only if `Component::", stringify!($variant), "` is present in [`Components`]." )] #[must_use] pub fn new_lazy(components: &Components, supply_fn: impl FnOnce() -> $ty) -> Self { Self { value: components.contains(&Component::$variant).then(supply_fn), } } #[doc = "Returns a reference to the contained value if it was computed.\n\nReturns `None` otherwise."] #[must_use] pub fn as_option(&self) -> Option<&$ty> { self.value.as_ref() } } impl Components { #[doc = concat!( "Returns a [`", stringify!($variant), "Container`] from a direct value.\n\n", "The value will only be stored if `Component::", stringify!($variant), "` is in the set." )] #[must_use] pub fn [<$variant:snake>](&self, value: $ty) -> [<$variant Container>] { [<$variant Container>]::new(self, value) } #[doc = concat!( "Returns a [`", stringify!($variant), "Container`] using a lazy supplier function.\n\n", "The function will only be called if `Component::", stringify!($variant), "` is in the set." )] pub fn [<$variant:snake _lazy>](&self, supply_fn: impl FnOnce() -> $ty) -> [<$variant Container>] { [<$variant Container>]::new_lazy(self, supply_fn) } } } }; } impl_component_container!(ContractName); impl_component_container!(EntryPointType); impl_component_container!(Calldata, TransformedCalldata); impl_component_container!(ContractAddress); impl_component_container!(CallerAddress); impl_component_container!(CallType); impl_component_container!(CallResult, TransformedCallResult); impl_component_container!(Gas); ================================================ FILE: crates/debugging/src/trace/context.rs ================================================ use crate::Components; use crate::contracts_data_store::ContractsDataStore; use cheatnet::forking::data::ForkData; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; /// Context is a structure that holds the necessary data for creating a [`Trace`](crate::Trace). pub struct Context { contracts_data_store: ContractsDataStore, components: Components, } impl Context { /// Creates a new instance of [`Context`] from a given `cheatnet` [`ContractsData`], [`ForkData`] and [`Components`]. #[must_use] pub fn new( contracts_data: &ContractsData, fork_data: &ForkData, components: Components, ) -> Self { let contracts_data_store = ContractsDataStore::new(contracts_data, fork_data); Self { contracts_data_store, components, } } /// Returns a reference to the [`ContractsDataStore`]. #[must_use] pub fn contracts_data_store(&self) -> &ContractsDataStore { &self.contracts_data_store } /// Returns a reference to the [`Components`]. #[must_use] pub fn components(&self) -> &Components { &self.components } } ================================================ FILE: crates/debugging/src/trace/mod.rs ================================================ pub mod collect; pub mod components; pub mod context; pub mod types; ================================================ FILE: crates/debugging/src/trace/types.rs ================================================ use crate::Context; use crate::trace::collect::Collector; use crate::trace::components::{ CallResultContainer, CallTypeContainer, CalldataContainer, CallerAddressContainer, ContractAddressContainer, ContractNameContainer, EntryPointTypeContainer, GasContainer, }; use crate::tree::TreeSerialize; use cheatnet::trace_data::CallTrace; use starknet_api::core::ContractAddress as ApiContractAddress; use starknet_api::execution_resources::GasAmount as ApiGasAmount; use std::fmt; use std::fmt::Display; #[derive(Debug, Clone)] pub struct Trace { pub test_name: TestName, pub nested_calls: Vec, } #[derive(Debug, Clone)] pub struct ContractTrace { pub selector: Selector, pub trace_info: TraceInfo, } #[derive(Debug, Clone)] pub struct TraceInfo { pub contract_name: ContractNameContainer, pub entry_point_type: EntryPointTypeContainer, pub calldata: CalldataContainer, pub contract_address: ContractAddressContainer, pub caller_address: CallerAddressContainer, pub call_type: CallTypeContainer, pub nested_calls: Vec, pub call_result: CallResultContainer, pub gas: GasContainer, } #[derive(Debug, Clone)] pub struct TransformedCallResult(pub String); #[derive(Debug, Clone)] pub struct TransformedCalldata(pub String); #[derive(Debug, Clone)] pub struct Selector(pub String); #[derive(Debug, Clone)] pub struct TestName(pub String); #[derive(Debug, Clone)] pub struct ContractName(pub String); #[derive(Debug, Clone)] pub struct ContractAddress(pub ApiContractAddress); #[derive(Debug, Clone)] pub struct CallerAddress(pub ApiContractAddress); #[derive(Debug, Clone)] pub struct Gas(pub ApiGasAmount); impl Trace { /// Creates a new [`Trace`] from a given [`Context`] and a test name. #[must_use] pub fn new(call_trace: &CallTrace, context: &Context, test_name: String) -> Self { Collector::new(call_trace, context).collect_trace(test_name) } } impl Display for Trace { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.serialize()) } } ================================================ FILE: crates/debugging/src/tree/building/builder.rs ================================================ use ptree::TreeBuilder; use ptree::item::StringItem; /// A wrapper around [`TreeBuilder`] that adds two guard nodes to the tree upon creation and removes them upon building. pub struct TreeBuilderWithGuard { builder: TreeBuilder, } impl TreeBuilderWithGuard { /// Creates a new [`TreeBuilderWithGuard`]. pub fn new() -> Self { let mut builder = TreeBuilder::new("guard".to_string()); builder.begin_child("guard".to_string()); Self { builder } } /// Add a child to the current item and make the new child current. pub fn begin_child(&mut self, text: String) { self.builder.begin_child(text); } /// Add an empty child (leaf item) to the current item. pub fn add_empty_child(&mut self, text: String) { self.builder.add_empty_child(text); } /// Finish adding children, and make the current item's parent current. pub fn end_child(&mut self) { self.builder.end_child(); } /// Finish building the tree and return the top level item, not accounting for the guard nodes. pub fn build(mut self) -> StringItem { let string_item = self.builder.build(); extract_guard(extract_guard(string_item)) } } /// Extracts the guard node from a [`StringItem`]. fn extract_guard(mut string_item: StringItem) -> StringItem { assert_eq!(string_item.text, "guard"); assert_eq!(string_item.children.len(), 1); string_item.children.pop().unwrap_or_else(|| { unreachable!("guard is guaranteed to have one child by the assertion above") }) } #[cfg(test)] mod tests { use super::*; #[test] fn test_happy_path() { let mut builder = TreeBuilderWithGuard::new(); builder.add_empty_child("leaf".to_string()); let tree = builder.build(); assert_eq!(tree.text, "leaf"); assert!(tree.children.is_empty()); } #[test] fn test_two_guards() { let mut builder = TreeBuilderWithGuard::new(); let tree = builder.builder.build(); assert_eq!(tree.text, "guard"); assert_eq!(tree.children.len(), 1); assert_eq!(tree.children[0].text, "guard"); assert!(tree.children[0].children.is_empty()); } } ================================================ FILE: crates/debugging/src/tree/building/mod.rs ================================================ pub mod builder; pub mod node; ================================================ FILE: crates/debugging/src/tree/building/node.rs ================================================ use crate::tree::building::builder::TreeBuilderWithGuard; use crate::tree::ui::as_tree_node::AsTreeNode; use crate::tree::ui::display::NodeDisplay; /// Abstraction over a [`ptree::TreeBuilder`] that automatically ends building a current node when dropped. /// # Note /// The last node should be at level one when dropped, to not cause overflow. /// This is enforced at type level by using [`TreeBuilderWithGuard`] instead of [`ptree::TreeBuilder`]. pub struct Node<'a> { builder: &'a mut TreeBuilderWithGuard, } impl Drop for Node<'_> { fn drop(&mut self) { self.builder.end_child(); } } impl<'a> Node<'a> { /// Creates a new [`Node`] with a given [`TreeBuilderWithGuard`]. pub fn new(builder: &'a mut TreeBuilderWithGuard) -> Self { Node { builder } } /// Calls [`AsTreeNode::as_tree_node`] on the given item. /// Utility function to allow chaining. pub fn as_tree_node(&mut self, tree_item: &impl AsTreeNode) { tree_item.as_tree_node(self); } /// Creates a child node which parent is the current node and returns handle to created node. #[must_use = "if you want to create a leaf node use leaf() instead"] pub fn child_node(&mut self, tree_item: &impl NodeDisplay) -> Node<'_> { self.builder.begin_child(tree_item.display()); Node::new(self.builder) } /// Creates a leaf node which parent is the current node. pub fn leaf(&mut self, tree_item: &impl NodeDisplay) { self.builder.add_empty_child(tree_item.display()); } /// Creates a leaf node which parent is the current node if the item is not `None`. pub fn leaf_optional(&mut self, tree_item: Option<&impl NodeDisplay>) { if let Some(tree_item) = tree_item { self.leaf(tree_item); } } } ================================================ FILE: crates/debugging/src/tree/mod.rs ================================================ mod building; mod ui; use crate::tree::building::builder::TreeBuilderWithGuard; use crate::tree::building::node::Node; use crate::tree::ui::as_tree_node::AsTreeNode; use ptree::item::StringItem; /// Serialize a type that implements [`AsTreeNode`] to a string. pub trait TreeSerialize { fn serialize(&self) -> String; } impl TreeSerialize for T { fn serialize(&self) -> String { let mut builder = TreeBuilderWithGuard::new(); Node::new(&mut builder).as_tree_node(self); let string_item = builder.build(); write_to_string(&string_item) } } /// Writes a [`StringItem`] to a string. fn write_to_string(string_item: &StringItem) -> String { let mut buf = Vec::new(); ptree::write_tree(string_item, &mut buf).expect("write_tree failed"); String::from_utf8(buf).expect("valid UTF-8") } ================================================ FILE: crates/debugging/src/tree/ui/as_tree_node.rs ================================================ use crate::trace::types::{ContractTrace, Trace, TraceInfo}; use crate::tree::building::node::Node; /// Trait for adding a type to a tree. /// Implementations of this trait should only focus on placements of nodes in a tree not display aspects of them. /// Display should be handled by the [`NodeDisplay`](super::display::NodeDisplay) trait. pub trait AsTreeNode { fn as_tree_node(&self, parent: &mut Node); } impl AsTreeNode for Trace { fn as_tree_node(&self, parent: &mut Node) { let mut node = parent.child_node(&self.test_name); for nested_call in &self.nested_calls { node.as_tree_node(nested_call); } } } impl AsTreeNode for ContractTrace { fn as_tree_node(&self, parent: &mut Node) { parent .child_node(&self.selector) .as_tree_node(&self.trace_info); } } impl AsTreeNode for TraceInfo { fn as_tree_node(&self, parent: &mut Node) { parent.leaf_optional(self.contract_name.as_option()); parent.leaf_optional(self.entry_point_type.as_option()); parent.leaf_optional(self.calldata.as_option()); parent.leaf_optional(self.contract_address.as_option()); parent.leaf_optional(self.caller_address.as_option()); parent.leaf_optional(self.call_type.as_option()); parent.leaf_optional(self.call_result.as_option()); parent.leaf_optional(self.gas.as_option()); for nested_call in &self.nested_calls { parent.as_tree_node(nested_call); } } } ================================================ FILE: crates/debugging/src/tree/ui/display.rs ================================================ use crate::trace::types::{ CallerAddress, ContractAddress, ContractName, Gas, Selector, TestName, TransformedCallResult, TransformedCalldata, }; use blockifier::execution::entry_point::CallType; use starknet_api::contract_class::EntryPointType; use starknet_types_core::felt::Felt; use std::fmt::Debug; /// Trait controlling the display of a node in a tree. /// All nodes should have a tag that explains what the node represents /// and a pretty string representation of data held by the node. pub trait NodeDisplay { const TAG: &'static str; fn string_pretty(&self) -> String; fn display(&self) -> String { let tag = console::style(Self::TAG).magenta(); let content = self.string_pretty(); format!("[{tag}] {content}") } } impl NodeDisplay for TestName { const TAG: &'static str = "test name"; fn string_pretty(&self) -> String { self.0.clone() } } impl NodeDisplay for ContractName { const TAG: &'static str = "contract name"; fn string_pretty(&self) -> String { self.0.clone() } } impl NodeDisplay for Selector { const TAG: &'static str = "selector"; fn string_pretty(&self) -> String { self.0.clone() } } impl NodeDisplay for EntryPointType { const TAG: &'static str = "entry point type"; fn string_pretty(&self) -> String { string_debug(self) } } impl NodeDisplay for TransformedCalldata { const TAG: &'static str = "calldata"; fn string_pretty(&self) -> String { self.0.clone() } } impl NodeDisplay for ContractAddress { const TAG: &'static str = "contract address"; fn string_pretty(&self) -> String { string_hex(self.0) } } impl NodeDisplay for CallerAddress { const TAG: &'static str = "caller address"; fn string_pretty(&self) -> String { string_hex(self.0) } } impl NodeDisplay for CallType { const TAG: &'static str = "call type"; fn string_pretty(&self) -> String { string_debug(self) } } impl NodeDisplay for TransformedCallResult { const TAG: &'static str = "call result"; fn string_pretty(&self) -> String { self.0.clone() } } impl NodeDisplay for Gas { const TAG: &'static str = "L2 gas"; fn string_pretty(&self) -> String { self.0.to_string() } } /// Helper function to get hex representation /// of a type that can be converted to a [`Felt`]. fn string_hex(data: impl Into) -> String { data.into().to_hex_string() } /// Helper function to get debug representation of a type as a string. /// Mainly used for enums that hold no data or vectors of felts. fn string_debug(data: impl Debug) -> String { format!("{data:?}") } ================================================ FILE: crates/debugging/src/tree/ui/mod.rs ================================================ pub mod as_tree_node; pub mod display; ================================================ FILE: crates/docs/Cargo.toml ================================================ [package] name = "docs" version.workspace = true edition.workspace = true repository.workspace = true license.workspace = true [dependencies] regex.workspace = true shell-words.workspace = true walkdir.workspace = true serde.workspace = true serde_json.workspace = true toml_edit.workspace = true camino.workspace = true tempfile.workspace = true anyhow.workspace = true semver.workspace = true scarb-api = { path = "../scarb-api" } [features] testing = [] ================================================ FILE: crates/docs/src/lib.rs ================================================ #[cfg(feature = "testing")] pub mod snippet; #[cfg(feature = "testing")] pub mod validation; #[cfg(feature = "testing")] pub mod utils; ================================================ FILE: crates/docs/src/snippet.rs ================================================ use std::sync::LazyLock; use regex::Regex; use scarb_api::version::scarb_version; use semver::VersionReq; use serde::{Deserialize, Serialize}; static RE_SNCAST: LazyLock = LazyLock::new(|| { Regex::new( r"(?ms)^(?:\n)?```shell\n\$ (?Psncast .+?)\n```(?:\s*
\nOutput:<\/summary>\n\n```shell\n(?P[\s\S]+?)\n```[\s]*<\/details>)?").expect("Failed to create regex for sncast snippet") }); static RE_SNFORGE: LazyLock = LazyLock::new(|| { Regex::new( r"(?ms)^(?:\n)?```shell\n\$ (?Psnforge .+?)\n```(?:\s*
\nOutput:<\/summary>\n\n```shell\n(?P[\s\S]+?)\n```[\s]*<\/details>)?").expect("Failed to create regex for snforge snippet") }); #[derive(Clone, Debug)] pub struct SnippetType(String); impl SnippetType { #[must_use] pub fn forge() -> Self { SnippetType("snforge".to_string()) } #[must_use] pub fn sncast() -> Self { SnippetType("sncast".to_string()) } #[must_use] pub fn as_str(&self) -> &str { &self.0 } #[must_use] pub fn get_re(&self) -> &'static Regex { // The regex pattern is used to match the snippet, its config and the output. Example: // // ```shell // $ snforge or sncast command with args... // ``` //
// Output: // ```shell // Output of the command... // ``` //
match self.as_str() { "snforge" => &RE_SNFORGE, "sncast" => &RE_SNCAST, _ => panic!("Regex for {} not found", self.as_str()), } } } #[allow(clippy::struct_excessive_bools)] #[derive(Debug, Serialize)] #[serde(default)] pub struct SnippetConfig { pub ignored: bool, pub requires_ledger: bool, pub package_name: Option, pub ignored_output: bool, pub replace_network: bool, pub scarb_version: Option, } #[allow(clippy::struct_excessive_bools)] #[derive(Deserialize)] #[serde(default)] struct SnippetConfigProxy { ignored: bool, requires_ledger: bool, package_name: Option, ignored_output: bool, replace_network: bool, scarb_version: Option, } impl Default for SnippetConfigProxy { fn default() -> Self { Self { ignored: false, requires_ledger: false, package_name: None, ignored_output: false, replace_network: true, scarb_version: None, } } } impl Default for SnippetConfig { fn default() -> Self { Self { ignored: false, requires_ledger: false, package_name: None, ignored_output: false, replace_network: true, scarb_version: None, } } } impl SnippetConfig { fn check_scarb_compatibility(&mut self) { if let Some(ref scarb_version_req) = self.scarb_version { let current_scarb_version = scarb_version().expect("Failed to get scarb version").scarb; if !scarb_version_req.matches(¤t_scarb_version) { self.ignored = true; } } } } impl<'de> Deserialize<'de> for SnippetConfig { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'de>, { let proxy = SnippetConfigProxy::deserialize(deserializer)?; let mut config = SnippetConfig { ignored: proxy.ignored, requires_ledger: proxy.requires_ledger, package_name: proxy.package_name, ignored_output: proxy.ignored_output, replace_network: proxy.replace_network, scarb_version: proxy.scarb_version, }; config.check_scarb_compatibility(); Ok(config) } } #[derive(Debug)] pub struct Snippet { pub command: String, pub output: Option, pub file_path: String, pub line_start: usize, pub snippet_type: SnippetType, pub config: SnippetConfig, } impl Snippet { #[must_use] pub fn to_command_args(&self) -> Vec { let cleaned_command = self .command .lines() .collect::>() .join(" ") .replace(" \\", ""); shell_words::split(&cleaned_command) .expect("Failed to parse snippet string") .into_iter() .map(|arg| arg.trim().to_string()) .collect() } #[must_use] pub fn capture_package_from_output(&self) -> Option { let re = Regex::new(r"Collected \d+ test\(s\) from ([a-zA-Z_][a-zA-Z0-9_]*) package").unwrap(); re.captures_iter(self.output.as_ref()?) .filter_map(|caps| caps.get(1)) .last() .map(|m| m.as_str().to_string()) } } ================================================ FILE: crates/docs/src/utils.rs ================================================ use crate::snippet::Snippet; use anyhow::{Result, anyhow}; use camino::Utf8PathBuf; use std::{env, fs, path::PathBuf, str::FromStr}; use tempfile::TempDir; use toml_edit::{DocumentMut, value}; #[must_use] pub fn get_nth_ancestor(levels_up: usize) -> PathBuf { let mut dir = env::current_dir().expect("Failed to get the current directory"); for _ in 0..levels_up { dir = dir .parent() .expect("Failed to navigate to parent directory") .to_owned(); } dir } pub fn assert_valid_snippet(condition: bool, snippet: &Snippet, err_message: &str) { assert!( condition, "Found invalid {} snippet in the docs at {}:{}:1\n{}", snippet.snippet_type.as_str(), snippet.file_path, snippet.line_start, err_message ); } pub fn print_snippets_validation_summary(snippets: &[Snippet], tool_name: &str) { let validated_snippets_count = snippets .iter() .filter(|snippet| !snippet.config.ignored) .count(); let ignored_snippets_count = snippets.len() - validated_snippets_count; println!( "Finished validation of {tool_name} docs snippets\nValidated: {validated_snippets_count}, Ignored: {ignored_snippets_count}" ); } pub fn print_ignored_snippet_message(snippet: &Snippet) { println!( "Ignoring {} docs snippet, file: {}:{}:1", snippet.snippet_type.as_str(), snippet.file_path, snippet.line_start, ); } fn get_canonical_path(relative_path: &str) -> Result { Ok(Utf8PathBuf::from_str(relative_path) .map_err(|e| anyhow!("Failed to create Utf8PathBuf: {e}"))? .canonicalize_utf8() .map_err(|e| anyhow!("Failed to canonicalize path: {e}"))? .to_string()) } pub fn update_scarb_toml_dependencies(temp: &TempDir) -> Result<(), Box> { let snforge_std_path = get_canonical_path("../../snforge_std")?; let sncast_std_path = get_canonical_path("../../sncast_std")?; let scarb_toml_path = temp.path().join("Scarb.toml"); let mut scarb_toml = fs::read_to_string(&scarb_toml_path) .unwrap() .parse::() .unwrap(); scarb_toml["dependencies"]["sncast_std"]["path"] = value(&sncast_std_path); scarb_toml["dev-dependencies"]["snforge_std"]["path"] = value(&snforge_std_path); fs::write(&scarb_toml_path, scarb_toml.to_string())?; Ok(()) } ================================================ FILE: crates/docs/src/validation.rs ================================================ use crate::snippet::{Snippet, SnippetConfig, SnippetType}; use regex::Regex; use std::sync::LazyLock; use std::{fs, io, path::Path}; const EXTENSION: Option<&str> = Some("md"); pub fn extract_snippets_from_file( file_path: &Path, snippet_type: &SnippetType, ) -> io::Result> { let content = fs::read_to_string(file_path)?; let file_path_str = file_path .to_str() .expect("Failed to get file path") .to_string(); let snippets = snippet_type .get_re() .captures_iter(&content) .filter_map(|caps| { let match_start = caps.get(0)?.start(); let config_str = caps .name("config") .map_or_else(String::new, |m| m.as_str().to_string()); let command_match = caps.name("command")?; let output = caps.name("output").map(|m| { static GAS_RE: LazyLock = LazyLock::new(|| Regex::new(r"gas: (?:~\d+|\{.+\})").unwrap()); static EXECUTION_RESOURCES_RE: LazyLock = LazyLock::new(|| { Regex::new(r"(steps|memory holes|builtins|syscalls|sierra gas): (\d+|\(.+\))") .unwrap() }); let output = GAS_RE.replace_all(m.as_str(), "gas: [..]").to_string(); EXECUTION_RESOURCES_RE .replace_all(output.as_str(), "${1}: [..]") .to_string() }); let config = if config_str.is_empty() { SnippetConfig::default() } else { serde_json::from_str(&config_str).expect("Failed to parse snippet config") }; Some(Snippet { command: command_match.as_str().to_string(), output, file_path: file_path_str.clone(), line_start: content[..match_start].lines().count() + 1, snippet_type: snippet_type.clone(), config, }) }) .collect(); Ok(snippets) } pub fn extract_snippets_from_directory( dir_path: &Path, snippet_type: &SnippetType, ) -> io::Result> { let mut all_snippets = Vec::new(); let files = walkdir::WalkDir::new(dir_path) .into_iter() .map(|entry| entry.expect("Failed to read directory")) .filter(|entry| entry.path().is_file()); for file in files { let path = file.path(); if EXTENSION .is_none_or(|ext| path.extension().and_then(|path_ext| path_ext.to_str()) == Some(ext)) { let snippets = extract_snippets_from_file(path, snippet_type)?; all_snippets.extend(snippets); } } Ok(all_snippets) } ================================================ FILE: crates/forge/Cargo.toml ================================================ [package] name = "forge" version.workspace = true edition.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] # This should match `MINIMAL_SCARB_VERSION` scarb_2_12_0 = [] no_scarb_installed = [] non_exact_gas_assertions = [] skip_test_for_only_latest_scarb = [] run_test_for_scarb_since_2_13_1 = [] run_test_for_scarb_since_2_15_1 = [] test_for_multiple_scarb_versions = [] cairo-native = ["cheatnet/cairo-native", "scarb-api/cairo-native"] [dependencies] anyhow.workspace = true blockifier.workspace = true camino.workspace = true include_dir.workspace = true starknet_api.workspace = true shared.workspace = true cheatnet = { path = "../cheatnet" } conversions = { path = "../conversions" } configuration = { path = "../configuration" } scarb-api = { path = "../scarb-api" } forge_runner = { path = "../forge-runner" } universal-sierra-compiler-api = { path = "../universal-sierra-compiler-api" } cairo-lang-sierra.workspace = true cairo-lang-starknet-classes.workspace = true cairo-annotations.workspace = true starknet-types-core.workspace = true regex.workspace = true serde_json.workspace = true serde.workspace = true starknet-rust.workspace = true reqwest.workspace = true num-bigint.workspace = true clap.workspace = true clap_complete.workspace = true console.workspace = true rand.workspace = true scarb-metadata.workspace = true scarb-ui.workspace = true semver.workspace = true cairo-vm.workspace = true # openssl is being used, please do not remove it! openssl.workspace = true toml_edit.workspace = true tokio.workspace = true futures.workspace = true url.workspace = true indoc.workspace = true derive_more.workspace = true foundry-ui = { path = "../foundry-ui" } chrono.workspace = true tracing-subscriber.workspace = true tracing.workspace = true tracing-chrome.workspace = true rayon.workspace = true tempfile.workspace = true fs_extra.workspace = true comfy-table.workspace = true plotters.workspace = true create-output-dir.workspace = true mimalloc.workspace = true [[bin]] name = "snforge" path = "src/main.rs" [dev-dependencies] assert_fs.workspace = true snapbox.workspace = true axum.workspace = true cairo-lang-starknet-classes.workspace = true walkdir.workspace = true test-case.workspace = true itertools.workspace = true insta.workspace = true docs = { workspace = true, features = ["testing"] } packages_validation = { path = "../testing/packages_validation" } project-root.workspace = true ================================================ FILE: crates/forge/src/block_number_map.rs ================================================ use anyhow::{Result, anyhow}; use conversions::{IntoConv, string::IntoHexStr}; use starknet_api::block::BlockNumber; use starknet_rust::{ core::types::{BlockId, MaybePreConfirmedBlockWithTxHashes}, providers::{JsonRpcClient, Provider, jsonrpc::HttpTransport}, }; use starknet_types_core::felt::Felt; use std::collections::HashMap; use std::sync::Mutex; use tokio::runtime::Handle; use url::Url; /// Caches block numbers fetched from RPC nodes, shared across concurrent config passes. #[derive(Default)] pub struct BlockNumberMap { url_to_latest_block_number: Mutex>, url_and_hash_to_block_number: Mutex>, } impl BlockNumberMap { pub async fn get_latest_block_number(&self, url: Url) -> Result { // Release lock before awaiting. { let map = self.url_to_latest_block_number.lock().unwrap(); if let Some(&block_number) = map.get(&url) { return Ok(block_number); } } let fetched = fetch_latest_block_number(url.clone()).await?; // or_insert avoids overwriting if a concurrent task raced us. let mut map = self.url_to_latest_block_number.lock().unwrap(); Ok(*map.entry(url).or_insert(fetched)) } pub async fn get_block_number_for_hash(&self, url: Url, hash: Felt) -> Result { { let map = self.url_and_hash_to_block_number.lock().unwrap(); if let Some(&block_number) = map.get(&(url.clone(), hash)) { return Ok(block_number); } } let fetched = fetch_block_number_for_hash(url.clone(), hash).await?; let mut map = self.url_and_hash_to_block_number.lock().unwrap(); Ok(*map.entry((url, hash)).or_insert(fetched)) } #[must_use] pub fn get_url_to_latest_block_number(&self) -> HashMap { self.url_to_latest_block_number.lock().unwrap().clone() } } async fn fetch_latest_block_number(url: Url) -> Result { let client = JsonRpcClient::new(HttpTransport::new(url)); Ok(Handle::current() .spawn(async move { client.block_number().await }) .await? .map(BlockNumber)?) } async fn fetch_block_number_for_hash(url: Url, block_hash: Felt) -> Result { let client = JsonRpcClient::new(HttpTransport::new(url)); let hash = BlockId::Hash(block_hash.into_()); match Handle::current() .spawn(async move { client.get_block_with_tx_hashes(hash).await }) .await? { Ok(MaybePreConfirmedBlockWithTxHashes::Block(block)) => Ok(BlockNumber(block.block_number)), _ => Err(anyhow!( "Could not get the block number for block with hash 0x{}", block_hash.into_hex_string() )), } } ================================================ FILE: crates/forge/src/clean.rs ================================================ use crate::{CleanArgs, CleanComponent}; use anyhow::{Context, Result, ensure}; use camino::Utf8PathBuf; use foundry_ui::UI; use scarb_api::metadata::{MetadataOpts, metadata_with_opts}; use std::fs; const COVERAGE_DIR: &str = "coverage"; const PROFILE_DIR: &str = "profile"; const CACHE_DIR: &str = ".snfoundry_cache"; const TRACE_DIR: &str = "snfoundry_trace"; pub fn clean(args: CleanArgs, ui: &UI) -> Result<()> { let components = if args.clean_components.contains(&CleanComponent::All) { ensure!( args.clean_components.len() == 1, "The 'all' component cannot be combined with other components" ); vec![ CleanComponent::Trace, CleanComponent::Profile, CleanComponent::Cache, CleanComponent::Coverage, ] } else { args.clean_components }; let scarb_metadata = metadata_with_opts(MetadataOpts { no_deps: true, ..MetadataOpts::default() })?; let workspace_root = scarb_metadata.workspace.root; let packages_root: Vec = scarb_metadata .packages .into_iter() .map(|package_metadata| package_metadata.root) .collect(); for component in &components { match component { CleanComponent::Coverage => clean_dirs(&packages_root, COVERAGE_DIR, ui)?, CleanComponent::Profile => clean_dirs(&packages_root, PROFILE_DIR, ui)?, CleanComponent::Cache => clean_dir(&workspace_root, CACHE_DIR, ui)?, CleanComponent::Trace => clean_dir(&workspace_root, TRACE_DIR, ui)?, CleanComponent::All => unreachable!("All component should have been handled earlier"), } } Ok(()) } fn clean_dirs(root_dirs: &[Utf8PathBuf], dir_name: &str, ui: &UI) -> Result<()> { for root_dir in root_dirs { clean_dir(root_dir, dir_name, ui)?; } Ok(()) } fn clean_dir(dir: &Utf8PathBuf, dir_name: &str, ui: &UI) -> Result<()> { let dir = dir.join(dir_name); if dir.exists() { fs::remove_dir_all(&dir).with_context(|| format!("Failed to remove directory: {dir}"))?; ui.println(&format!("Removed directory: {dir}")); } Ok(()) } ================================================ FILE: crates/forge/src/combine_configs.rs ================================================ use crate::TestArgs; use crate::scarb::config::ForgeConfigFromScarb; use camino::Utf8PathBuf; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; use forge_runner::forge_config::{ ExecutionDataToSave, ForgeConfig, OutputConfig, TestRunnerConfig, }; use rand::{RngCore, thread_rng}; use std::env; use std::num::NonZeroU32; use std::sync::Arc; pub fn combine_configs( args: &TestArgs, contracts_data: ContractsData, cache_dir: Utf8PathBuf, forge_config_from_scarb: &ForgeConfigFromScarb, ) -> ForgeConfig { let execution_data_to_save = ExecutionDataToSave::from_flags( args.save_trace_data || forge_config_from_scarb.save_trace_data, args.build_profile || forge_config_from_scarb.build_profile, args.coverage || forge_config_from_scarb.coverage, &args.additional_args, ); ForgeConfig { test_runner_config: Arc::new(TestRunnerConfig { exit_first: args.exit_first || forge_config_from_scarb.exit_first, deterministic_output: args.deterministic_output, fuzzer_runs: args .fuzzer_runs .or(forge_config_from_scarb.fuzzer_runs) .unwrap_or(NonZeroU32::new(256).unwrap()), fuzzer_seed: args .fuzzer_seed .or(forge_config_from_scarb.fuzzer_seed) .unwrap_or_else(|| thread_rng().next_u64()), max_n_steps: args.max_n_steps.or(forge_config_from_scarb.max_n_steps), is_vm_trace_needed: execution_data_to_save.is_vm_trace_needed(), cache_dir, contracts_data, tracked_resource: args.tracked_resource, environment_variables: env::vars().collect(), launch_debugger: args.launch_debugger, }), output_config: Arc::new(OutputConfig { trace_args: args.trace_args.clone(), detailed_resources: args.detailed_resources || forge_config_from_scarb.detailed_resources, execution_data_to_save, gas_report: args.gas_report || forge_config_from_scarb.gas_report, }), } } #[cfg(test)] mod tests { use super::combine_configs; use crate::TestArgs; use crate::scarb::config::ForgeConfigFromScarb; use camino::Utf8PathBuf; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; use clap::Parser; use forge_runner::debugging::TraceArgs; use forge_runner::forge_config::{ ExecutionDataToSave, ForgeConfig, ForgeTrackedResource, OutputConfig, TestRunnerConfig, }; use std::num::NonZeroU32; use std::sync::Arc; #[test] fn fuzzer_default_seed() { let args = TestArgs::parse_from(["snforge"]); let config = combine_configs( &args, ContractsData::default(), Utf8PathBuf::default(), &ForgeConfigFromScarb::default(), ); let config2 = combine_configs( &args, ContractsData::default(), Utf8PathBuf::default(), &ForgeConfigFromScarb::default(), ); assert_ne!(config.test_runner_config.fuzzer_seed, 0); assert_ne!(config2.test_runner_config.fuzzer_seed, 0); assert_ne!( config.test_runner_config.fuzzer_seed, config2.test_runner_config.fuzzer_seed ); } #[test] fn runner_config_default_arguments() { let args = TestArgs::parse_from(["snforge"]); let config = combine_configs( &args, ContractsData::default(), Utf8PathBuf::default(), &ForgeConfigFromScarb::default(), ); assert_eq!( config, ForgeConfig { test_runner_config: Arc::new(TestRunnerConfig { exit_first: false, deterministic_output: false, fuzzer_runs: NonZeroU32::new(256).unwrap(), fuzzer_seed: config.test_runner_config.fuzzer_seed, max_n_steps: None, tracked_resource: ForgeTrackedResource::SierraGas, is_vm_trace_needed: false, cache_dir: Utf8PathBuf::default(), contracts_data: ContractsData::default(), environment_variables: config.test_runner_config.environment_variables.clone(), launch_debugger: false, }), output_config: Arc::new(OutputConfig { detailed_resources: false, execution_data_to_save: ExecutionDataToSave::default(), trace_args: TraceArgs::default(), gas_report: false, }), } ); } #[test] fn runner_config_just_scarb_arguments() { let config_from_scarb = ForgeConfigFromScarb { exit_first: true, fork: vec![], fuzzer_runs: Some(NonZeroU32::new(1234).unwrap()), fuzzer_seed: Some(500), detailed_resources: true, save_trace_data: true, build_profile: true, coverage: true, gas_report: true, max_n_steps: Some(1_000_000), tracked_resource: ForgeTrackedResource::CairoSteps, }; let args = TestArgs::parse_from(["snforge"]); let config = combine_configs( &args, ContractsData::default(), Utf8PathBuf::default(), &config_from_scarb, ); assert_eq!( config, ForgeConfig { test_runner_config: Arc::new(TestRunnerConfig { exit_first: true, deterministic_output: false, fuzzer_runs: NonZeroU32::new(1234).unwrap(), fuzzer_seed: 500, max_n_steps: Some(1_000_000), // tracked_resource comes from args only; ForgeConfigFromScarb.tracked_resource // is not used by combine_configs, so this stays at the args default. tracked_resource: ForgeTrackedResource::SierraGas, is_vm_trace_needed: true, cache_dir: Utf8PathBuf::default(), contracts_data: ContractsData::default(), environment_variables: config.test_runner_config.environment_variables.clone(), launch_debugger: false, }), output_config: Arc::new(OutputConfig { detailed_resources: true, execution_data_to_save: ExecutionDataToSave { trace: true, profile: true, coverage: true, additional_args: vec![], }, trace_args: TraceArgs::default(), gas_report: true, }), } ); } #[test] fn runner_config_argument_precedence() { let config_from_scarb = ForgeConfigFromScarb { exit_first: false, fork: vec![], fuzzer_runs: Some(NonZeroU32::new(1234).unwrap()), fuzzer_seed: Some(1000), detailed_resources: false, save_trace_data: false, build_profile: false, coverage: false, gas_report: false, max_n_steps: Some(1234), tracked_resource: ForgeTrackedResource::SierraGas, }; // Note: --build-profile and --coverage conflict in clap, so only one can be used at a time. // We use --save-trace-data + --build-profile here to verify precedence for trace/profile flags. let args = TestArgs::parse_from([ "snforge", "--exit-first", "--fuzzer-runs", "100", "--fuzzer-seed", "32", "--detailed-resources", "--save-trace-data", "--build-profile", "--gas-report", "--max-n-steps", "1000000", "--tracked-resource", "cairo-steps", ]); let config = combine_configs( &args, ContractsData::default(), Utf8PathBuf::default(), &config_from_scarb, ); assert_eq!( config, ForgeConfig { test_runner_config: Arc::new(TestRunnerConfig { exit_first: true, deterministic_output: false, fuzzer_runs: NonZeroU32::new(100).unwrap(), fuzzer_seed: 32, max_n_steps: Some(1_000_000), tracked_resource: ForgeTrackedResource::CairoSteps, is_vm_trace_needed: true, cache_dir: Utf8PathBuf::default(), contracts_data: ContractsData::default(), environment_variables: config.test_runner_config.environment_variables.clone(), launch_debugger: false, }), output_config: Arc::new(OutputConfig { detailed_resources: true, execution_data_to_save: ExecutionDataToSave { trace: true, profile: true, coverage: false, additional_args: vec![], }, trace_args: TraceArgs::default(), gas_report: true, }), } ); } } ================================================ FILE: crates/forge/src/compatibility_check.rs ================================================ use anyhow::{Context, Result, anyhow}; use foundry_ui::UI; use regex::Regex; use semver::Version; use shared::command::CommandExt; use std::cell::RefCell; use std::process::Command; type VersionParser<'a> = dyn Fn(&str) -> Result + 'a; pub struct Requirement<'a> { pub name: String, pub command: RefCell, pub version_parser: Box>, pub helper_text: String, pub minimal_version: Version, pub minimal_recommended_version: Option, pub maximal_recommended_version: Option, } impl Requirement<'_> { // TODO(#3404) fn validate_and_get_output(&self) -> (bool, String) { let version = self.get_version(); let mut is_valid; let output = if let Ok(version) = version { is_valid = version >= self.minimal_version; let is_min_recommended = self.minimal_recommended_version.as_ref().is_none_or( |minimal_recommended_version| { Self::version_satisfies_min(&version, minimal_recommended_version) }, ); let is_max_recommended = self.maximal_recommended_version.as_ref().is_none_or( |maximal_recommended_version| { Self::version_satisfies_max(&version, maximal_recommended_version) }, ); let min_version_to_display = self .minimal_recommended_version .as_ref() .unwrap_or(&self.minimal_version); if !is_valid { is_valid = false; format!( "❌ {} Version {} doesn't satisfy minimal {}\n{}", self.name, version, min_version_to_display, self.helper_text ) } else if !is_min_recommended { format!( "⚠️ {} Version {} doesn't satisfy minimal recommended {}\n{}", self.name, version, self.minimal_recommended_version.as_ref().unwrap(), self.helper_text ) } else if !is_max_recommended { format!( "⚠️ {} Version {} doesn't satisfy maximal recommended {}\n{}", self.name, version, self.maximal_recommended_version.as_ref().unwrap(), self.helper_text ) } else { format!("✅ {} {}", self.name, version) } } else { is_valid = false; format!( "❌ {} is not installed or not added to the PATH\n{}\n", self.name, self.helper_text ) }; (is_valid, output) } fn get_version(&self) -> Result { let version_command_output = self.command.borrow_mut().output_checked()?; let raw_version = String::from_utf8_lossy(&version_command_output.stdout) .trim() .to_string(); (self.version_parser)(&raw_version) } fn version_satisfies_min(version: &Version, required: &Version) -> bool { version >= required } fn version_satisfies_max(version: &Version, required: &Version) -> bool { (version.major, version.minor) <= (required.major, required.minor) } } pub struct RequirementsChecker<'a> { output_on_success: bool, requirements: Vec>, } impl<'a> RequirementsChecker<'a> { pub(crate) fn new(output_on_success: bool) -> Self { Self { output_on_success, requirements: Vec::new(), } } pub fn add_requirement(&mut self, requirement: Requirement<'a>) { self.requirements.push(requirement); } pub fn check(&self, ui: &UI) -> Result<()> { let (validation_output, all_requirements_valid) = self.check_and_prepare_output(); if self.output_on_success || !all_requirements_valid { ui.println(&validation_output); } if all_requirements_valid { Ok(()) } else { Err(anyhow!("Requirements not satisfied")) } } fn check_and_prepare_output(&self) -> (String, bool) { let mut validation_output = "Checking requirements\n\n".to_string(); let mut all_valid = true; for requirement in &self.requirements { let (is_valid, output) = requirement.validate_and_get_output(); all_valid &= is_valid; validation_output += output.as_str(); validation_output += "\n"; } (validation_output, all_valid) } } pub fn create_version_parser<'a>(name: &'a str, pattern: &'a str) -> Box> { let regex = Regex::new(pattern).unwrap(); Box::new(move |raw_version: &str| { let matches = regex.captures(raw_version).with_context(|| { format!("Failed to match {name} version from output: {raw_version}") })?; let version_str = matches .name("version") .with_context(|| format!("Failed to parse {name} version"))? .as_str(); Version::parse(version_str).with_context(|| "Failed to parse version") }) } #[cfg(test)] mod tests { use super::*; use crate::MINIMAL_SCARB_VERSION; use assert_fs::{ TempDir, fixture::{FileWriteStr, PathChild}, }; use scarb_api::ScarbCommand; #[test] fn happy_case() { let mut requirements_checker = RequirementsChecker::new(true); requirements_checker.add_requirement(Requirement { name: "Rust".to_string(), command: RefCell::new({ let mut cmd = Command::new("rustc"); cmd.arg("--version"); cmd }), version_parser: create_version_parser( "Rust", r"rustc (?[0-9]+.[0-9]+.[0-9]+)", ), helper_text: "Follow instructions from https://www.rust-lang.org/tools/install" .to_string(), minimal_version: Version::new(1, 80, 1), minimal_recommended_version: None, maximal_recommended_version: None, }); requirements_checker.add_requirement(Requirement { name: "Scarb".to_string(), command: RefCell::new(ScarbCommand::new().arg("--version").command()), minimal_version: MINIMAL_SCARB_VERSION, minimal_recommended_version: Some(Version::new(2, 9, 4)), maximal_recommended_version: None, helper_text: "Follow instructions from https://docs.swmansion.com/scarb/download.html" .to_string(), version_parser: create_version_parser( "Scarb", r"scarb (?[0-9]+.[0-9]+.[0-9]+)", ), }); requirements_checker.add_requirement(Requirement { name: "Universal Sierra Compiler".to_string(), command: RefCell::new(universal_sierra_compiler_api::version_command().unwrap()), minimal_version: Version::new(2, 0, 0), minimal_recommended_version: None, maximal_recommended_version: None, helper_text: "Reinstall `snforge` using the same installation method or follow instructions from https://foundry-rs.github.io/starknet-foundry/getting-started/installation.html#universal-sierra-compiler-update".to_string(), version_parser: create_version_parser( "Universal Sierra Compiler", r"universal-sierra-compiler (?[0-9]+.[0-9]+.[0-9]+)", ), }); let (validation_output, is_valid) = requirements_checker.check_and_prepare_output(); assert!(is_valid); assert!(validation_output.contains("✅ Rust")); assert!(validation_output.contains("✅ Scarb")); assert!(validation_output.contains("✅ Universal Sierra Compiler")); } #[test] fn failing_requirements() { let mut requirements_checker = RequirementsChecker::new(true); requirements_checker.add_requirement(Requirement { name: "Rust".to_string(), command: RefCell::new({ let mut cmd = Command::new("rustc"); cmd.arg("--version"); cmd }), version_parser: create_version_parser( "Rust", r"rustc (?[0-9]+.[0-9]+.[0-9]+)", ), helper_text: "Follow instructions from https://www.rust-lang.org/tools/install" .to_string(), minimal_version: Version::new(999, 0, 0), minimal_recommended_version: None, maximal_recommended_version: None, }); let (validation_output, is_valid) = requirements_checker.check_and_prepare_output(); assert!(!is_valid); assert!(validation_output.contains("❌ Rust Version")); assert!(validation_output.contains("doesn't satisfy minimal 999.0.0")); } #[test] fn warning_requirements() { let mut requirements_checker = RequirementsChecker::new(true); requirements_checker.add_requirement(Requirement { name: "Scarb".to_string(), command: RefCell::new(ScarbCommand::new().arg("--version").command()), minimal_version: MINIMAL_SCARB_VERSION, minimal_recommended_version: Some(Version::new(999, 0, 0)), maximal_recommended_version: None, helper_text: "Follow instructions from https://docs.swmansion.com/scarb/download.html" .to_string(), version_parser: create_version_parser( "Scarb", r"scarb (?[0-9]+.[0-9]+.[0-9]+)", ), }); let (validation_output, is_valid) = requirements_checker.check_and_prepare_output(); let ui = UI::default(); ui.println(&validation_output); assert!(is_valid); assert!(validation_output.contains("⚠️ Scarb Version")); assert!(validation_output.contains("doesn't satisfy minimal recommended 999.0.0")); } #[test] fn failing_requirements_on_both_minimal_versions_defined() { let mut requirements_checker = RequirementsChecker::new(true); requirements_checker.add_requirement(Requirement { name: "Scarb".to_string(), command: RefCell::new(ScarbCommand::new().arg("--version").command()), minimal_version: Version::new(111, 0, 0), minimal_recommended_version: Some(Version::new(999, 0, 0)), maximal_recommended_version: None, helper_text: "Follow instructions from https://docs.swmansion.com/scarb/download.html" .to_string(), version_parser: create_version_parser( "Scarb", r"scarb (?[0-9]+.[0-9]+.[0-9]+)", ), }); let (validation_output, is_valid) = requirements_checker.check_and_prepare_output(); assert!(!is_valid); assert!(validation_output.contains("❌ Scarb Version")); assert!(validation_output.contains("doesn't satisfy minimal 999.0.0")); } #[test] #[cfg_attr(not(feature = "no_scarb_installed"), ignore)] fn failing_tool_not_installed() { let temp_dir = TempDir::new().unwrap(); temp_dir .child(".tool-versions") .write_str("scarb 2.9.9\n") .unwrap(); let mut requirements_checker = RequirementsChecker::new(true); requirements_checker.add_requirement(Requirement { name: "Scarb".to_string(), command: RefCell::new( ScarbCommand::new() .arg("--version") .current_dir(temp_dir.path()) .command(), ), minimal_version: Version::new(2, 8, 5), minimal_recommended_version: Some(Version::new(2, 9, 4)), maximal_recommended_version: None, helper_text: "Follow instructions from https://docs.swmansion.com/scarb/download.html" .to_string(), version_parser: create_version_parser( "Scarb", r"scarb (?[0-9]+.[0-9]+.[0-9]+)", ), }); requirements_checker.add_requirement(Requirement { name: "Universal Sierra Compiler".to_string(), command: RefCell::new(universal_sierra_compiler_api::version_command().unwrap()), minimal_version: Version::new(2, 4, 0), minimal_recommended_version: None, maximal_recommended_version: None, helper_text: "Reinstall `snforge` using the same installation method or follow instructions from https://foundry-rs.github.io/starknet-foundry/getting-started/installation.html#universal-sierra-compiler-update".to_string(), version_parser: create_version_parser( "Universal Sierra Compiler", r"universal-sierra-compiler (?[0-9]+.[0-9]+.[0-9]+)", ), }); let (validation_output, is_valid) = requirements_checker.check_and_prepare_output(); assert!(!is_valid); assert!(validation_output.contains("❌ Scarb is not installed or not added to the PATH")); assert!( validation_output.contains( "Follow instructions from https://docs.swmansion.com/scarb/download.html" ) ); assert!(validation_output.contains("✅ Universal Sierra Compiler")); } #[test] fn warning_maximal_version() { let mut requirements_checker = RequirementsChecker::new(true); requirements_checker.add_requirement(Requirement { name: "Scarb".to_string(), command: RefCell::new(ScarbCommand::new().arg("--version").command()), minimal_version: Version::new(2, 8, 0), minimal_recommended_version: Some(Version::new(2, 9, 4)), maximal_recommended_version: Some(Version::new(2, 11, 0)), helper_text: "Follow instructions from https://docs.swmansion.com/scarb/download.html" .to_string(), version_parser: create_version_parser( "Scarb", r"scarb (?[0-9]+.[0-9]+.[0-9]+)", ), }); let (validation_output, is_valid) = requirements_checker.check_and_prepare_output(); let ui = UI::default(); ui.println(&validation_output); assert!(is_valid); assert!(validation_output.contains("⚠️ Scarb Version")); assert!(validation_output.contains("doesn't satisfy maximal recommended 2.11.0")); } #[test] fn test_version_satisfies_min() { assert!(Requirement::version_satisfies_min( &Version::new(2, 15, 5), &Version::new(2, 15, 0) )); assert!(!Requirement::version_satisfies_min( &Version::new(2, 15, 0), &Version::new(2, 15, 2) )); } #[test] fn test_version_satisfies_max() { assert!(Requirement::version_satisfies_max( &Version::new(2, 15, 5), &Version::new(2, 15, 0) )); assert!(!Requirement::version_satisfies_max( &Version::new(2, 16, 0), &Version::new(2, 15, 0) )); } } ================================================ FILE: crates/forge/src/lib.rs ================================================ use crate::compatibility_check::{Requirement, RequirementsChecker, create_version_parser}; use anyhow::Result; use camino::Utf8PathBuf; use clap::builder::BoolishValueParser; use clap::{CommandFactory, Parser, Subcommand, ValueEnum}; use derive_more::Display; use forge_runner::CACHE_DIR; use forge_runner::debugging::TraceArgs; use forge_runner::forge_config::ForgeTrackedResource; use forge_runner::partition::Partition; use foundry_ui::UI; use foundry_ui::components::warning::WarningMessage; use run_tests::workspace::run_for_workspace; use scarb_api::ScarbCommand; use scarb_api::metadata::metadata; use scarb_ui::args::{FeaturesSpec, PackagesFilter, ProfileSpec}; use semver::Version; use shared::auto_completions::{Completions, generate_completions}; use std::cell::RefCell; use std::ffi::OsString; use std::sync::Arc; use std::{ fs, num::{NonZeroU32, NonZeroUsize}, thread::available_parallelism, }; use tokio::runtime::Builder; pub mod block_number_map; mod clean; mod combine_configs; mod compatibility_check; mod new; mod optimize_inlining; mod profile_validation; pub mod run_tests; pub mod scarb; pub mod shared_cache; pub mod test_filter; mod warn; pub const CAIRO_EDITION: &str = "2024_07"; const MINIMAL_SCARB_VERSION: Version = Version::new(2, 12, 0); const MINIMAL_RECOMMENDED_SCARB_VERSION: Version = Version::new(2, 15, 2); const MAXIMAL_RECOMMENDED_SCARB_VERSION: Version = Version::new(2, 17, 0); const MINIMAL_USC_VERSION: Version = Version::new(2, 0, 0); const MINIMAL_SNFORGE_STD_VERSION: Version = Version::new(0, 50, 0); #[derive(Parser, Debug)] #[command( version, help_template = "\ {name} {version} {author-with-newline}{about-with-newline} Use -h for short descriptions and --help for more details. {before-help}{usage-heading} {usage} {all-args}{after-help} ", after_help = "Read the docs: https://foundry-rs.github.io/starknet-foundry/", after_long_help = "\ Read the docs: - Starknet Foundry Book: https://foundry-rs.github.io/starknet-foundry/ - Cairo Book: https://book.cairo-lang.org/ - Starknet Book: https://book.starknet.io/ - Starknet Documentation: https://docs.starknet.io/ - Scarb Documentation: https://docs.swmansion.com/scarb/docs.html Join the community: - Follow core developers on X: https://twitter.com/swmansionxyz - Get support via Telegram: https://t.me/starknet_foundry_support - Or discord: https://discord.gg/starknet-community - Or join our general chat (Telegram): https://t.me/starknet_foundry Report bugs: https://github.com/foundry-rs/starknet-foundry/issues/new/choose\ " )] #[command(about = "snforge - a testing tool for Starknet contracts", long_about = None)] #[command(name = "snforge")] pub struct Cli { #[command(subcommand)] subcommand: ForgeSubcommand, } #[derive(Subcommand, Debug)] enum ForgeSubcommand { /// Run tests for a project in the current directory Test { #[command(flatten)] args: Box, }, /// Create a new Forge project at New { #[command(flatten)] args: NewArgs, }, /// Clean `snforge` generated directories Clean { #[command(flatten)] args: CleanArgs, }, /// Clean Forge cache directory CleanCache {}, /// Check if all `snforge` requirements are installed CheckRequirements, /// Generate completions script Completions(Completions), /// Find optimal inlining-strategy value to minimize gas cost OptimizeInlining { #[command(flatten)] args: Box, }, } #[derive(Parser, Debug)] pub struct CleanArgs { #[arg(num_args = 1.., required = true)] pub clean_components: Vec, } #[derive(ValueEnum, Debug, Clone, PartialEq, Eq)] pub enum CleanComponent { /// Clean the `coverage` directory Coverage, /// Clean the `profile` directory Profile, /// Clean the `.snfoundry_cache` directory Cache, /// Clean the `snfoundry_trace` directory Trace, /// Clean all generated directories All, } #[derive(ValueEnum, Debug, Clone)] enum ColorOption { Auto, Always, Never, } #[derive(Parser, Debug)] #[expect(clippy::struct_excessive_bools)] pub struct TestArgs { /// Name used to filter tests test_filter: Option, #[command(flatten)] trace_args: TraceArgs, /// Run contracts on `cairo-native` instead of the default `cairo-vm`. This will set `tracked-resource` to `sierra-gas`. /// /// Note: Only contracts execution through native is supported, test code itself will still run on `cairo-vm`. #[arg(long)] #[cfg(feature = "cairo-native")] run_native: bool, /// Use exact matches for `test_filter` #[arg(short, long, conflicts_with = "partition", requires = "test_filter")] exact: bool, /// Skips any tests whose name contains the given SKIP string. #[arg(long)] skip: Vec, /// Stop executing tests after the first failed test #[arg(short = 'x', long)] exit_first: bool, /// Sort test result outputs by test name, for reproducible outputs (e.g. for snapshot tests). #[arg(long, env = "SNFORGE_DETERMINISTIC_OUTPUT", default_value_t = false, hide = true, value_parser = BoolishValueParser::new())] deterministic_output: bool, /// Number of fuzzer runs #[arg(short = 'r', long)] fuzzer_runs: Option, /// Seed for the fuzzer #[arg(short = 's', long, env = "SNFORGE_FUZZER_SEED")] fuzzer_seed: Option, /// Run only tests marked with `#[ignore]` attribute #[arg(long = "ignored")] only_ignored: bool, /// Run all tests regardless of `#[ignore]` attribute #[arg(long, conflicts_with = "only_ignored")] include_ignored: bool, /// Display more detailed info about used resources #[arg(long)] detailed_resources: bool, /// Control when colored output is used #[arg(value_enum, long, default_value_t = ColorOption::Auto, value_name="WHEN")] color: ColorOption, /// Run tests that failed during the last run #[arg(long)] rerun_failed: bool, /// Save execution traces of all test which have passed and are not fuzz tests #[arg(long)] #[cfg_attr(feature = "cairo-native", arg(conflicts_with = "run_native"))] save_trace_data: bool, /// Build profiles of all tests which have passed and are not fuzz tests using the cairo-profiler #[arg(long, conflicts_with_all = ["coverage"])] #[cfg_attr(feature = "cairo-native", arg(conflicts_with_all = ["run_native", "coverage"]))] build_profile: bool, /// Generate a coverage report for the executed tests which have passed and are not fuzz tests using the cairo-coverage #[arg(long, conflicts_with_all = ["build_profile"])] #[cfg_attr(feature = "cairo-native", arg(conflicts_with_all = ["run_native", "build_profile"]))] coverage: bool, /// Number of maximum steps during a single test. For fuzz tests this value is applied to each subtest separately. #[arg(long)] max_n_steps: Option, /// Build contracts separately in the scarb starknet contract target #[arg(long)] no_optimization: bool, /// Specify tracked resource type #[arg(long, value_enum, default_value_t)] tracked_resource: ForgeTrackedResource, /// Display a table of L2 gas breakdown for each contract and selector #[arg(long)] gas_report: bool, /// Divides tests into `TOTAL` partitions and runs partition `INDEX` (1-based), e.g. 1/4 #[arg(long, value_name = "INDEX/TOTAL")] partition: Option, /// Maximum number of threads used for test execution #[arg(long)] max_threads: Option, /// Additional arguments for cairo-coverage or cairo-profiler #[arg(last = true)] additional_args: Vec, #[command(flatten)] scarb_args: ScarbArgs, /// Launch the given test in debug mode using `cairo-debugger` crate. /// /// It makes snforge act as a debug adapter, enabling communication with an editor/IDE /// such as `VSCode` over DAP protocol. #[arg(long, requires = "exact")] #[cfg_attr(feature = "cairo-native", arg(conflicts_with = "run_native"))] launch_debugger: bool, } impl TestArgs { /// Adjust dependent arguments based on related flags. /// /// This function mutates the `TestArgs` instance to enforce logical coherence /// between fields. pub fn normalize(&mut self) { // Force using `SierraGas` as tracked resource when running with `cairo-native`, // as otherwise it would run on vm. #[cfg(feature = "cairo-native")] if self.run_native { self.tracked_resource = ForgeTrackedResource::SierraGas; } } } #[derive(Parser, Debug)] pub struct ScarbArgs { #[command(flatten)] packages_filter: PackagesFilter, #[command(flatten)] features: FeaturesSpec, #[command(flatten)] profile: ProfileSpec, } #[derive(ValueEnum, Display, Debug, Clone)] pub enum Template { /// Simple Cairo program with unit tests #[display("cairo-program")] CairoProgram, /// Basic contract with example tests #[display("balance-contract")] BalanceContract, /// ERC20 contract for mock token #[display("erc20-contract")] Erc20Contract, } #[derive(Parser, Debug)] pub struct NewArgs { /// Path to a location where the new project will be created path: Utf8PathBuf, /// Name of a new project, defaults to the directory name #[arg(short, long)] name: Option, /// Do not initialize a new Git repository #[arg(long)] no_vcs: bool, /// Try to create the project even if the specified directory at is not empty, which can result in overwriting existing files #[arg(long)] overwrite: bool, /// Template to use for the new project #[arg(short, long, default_value_t = Template::BalanceContract)] template: Template, } pub enum ExitStatus { Success, Failure, } #[tracing::instrument(skip_all, level = "debug")] pub fn main_execution(ui: Arc) -> Result { let cli = Cli::parse(); match cli.subcommand { ForgeSubcommand::New { args } => { new::new(args)?; Ok(ExitStatus::Success) } ForgeSubcommand::Clean { args } => { clean::clean(args, &ui)?; Ok(ExitStatus::Success) } ForgeSubcommand::CleanCache {} => { ui.println(&WarningMessage::new("`snforge clean-cache` is deprecated and will be removed in the future. Use `snforge clean cache` instead")); let scarb_metadata = metadata()?; let cache_dir = scarb_metadata.workspace.root.join(CACHE_DIR); if cache_dir.exists() { fs::remove_dir_all(&cache_dir)?; } Ok(ExitStatus::Success) } ForgeSubcommand::Test { mut args } => { args.normalize(); check_requirements(false, &ui)?; let cores = resolve_thread_count(args.max_threads, &ui); let rt = Builder::new_multi_thread() .max_blocking_threads(cores) .enable_all() .build()?; rt.block_on(run_for_workspace(*args, ui)) } ForgeSubcommand::CheckRequirements => { check_requirements(true, &ui)?; Ok(ExitStatus::Success) } ForgeSubcommand::Completions(completions) => { generate_completions(completions.shell, &mut Cli::command())?; Ok(ExitStatus::Success) } ForgeSubcommand::OptimizeInlining { args } => { let cores = resolve_thread_count(args.test_args.max_threads, &ui); check_requirements(false, &ui)?; optimize_inlining::optimize_inlining(&args, cores, &ui) } } } #[tracing::instrument(skip_all, level = "debug")] fn check_requirements(output_on_success: bool, ui: &UI) -> Result<()> { let mut requirements_checker = RequirementsChecker::new(output_on_success); requirements_checker.add_requirement(Requirement { name: "Scarb".to_string(), command: RefCell::new(ScarbCommand::new().arg("--version").command()), minimal_version: MINIMAL_SCARB_VERSION, minimal_recommended_version: Some(MINIMAL_RECOMMENDED_SCARB_VERSION), maximal_recommended_version: Some(MAXIMAL_RECOMMENDED_SCARB_VERSION), helper_text: "Follow instructions from https://docs.swmansion.com/scarb/download.html" .to_string(), version_parser: create_version_parser("Scarb", r"scarb (?[0-9]+.[0-9]+.[0-9]+)"), }); requirements_checker.add_requirement(Requirement { name: "Universal Sierra Compiler".to_string(), command: RefCell::new(universal_sierra_compiler_api::version_command()?), minimal_version: MINIMAL_USC_VERSION, minimal_recommended_version: None, maximal_recommended_version: None, helper_text: "Reinstall `snforge` using the same installation method or follow instructions from https://foundry-rs.github.io/starknet-foundry/getting-started/installation.html#universal-sierra-compiler-update".to_string(), version_parser: create_version_parser( "Universal Sierra Compiler", r"universal-sierra-compiler (?[0-9]+.[0-9]+.[0-9]+)", ), }); requirements_checker.check(ui)?; Ok(()) } fn resolve_thread_count(max_threads: Option, ui: &UI) -> usize { match (available_parallelism(), max_threads) { (Ok(available_cores), Some(max_threads)) => { let available = available_cores.get(); let max = max_threads.get(); if max > available { ui.println(&WarningMessage::new(format!( "`--max-threads` value ({max}) is greater than the number of available cores ({available})" ))); } max } (Ok(available_cores), None) => available_cores.get(), (Err(_), Some(max_threads)) => { ui.println(&WarningMessage::new( "Failed to get the number of available cores, using `--max-threads` value", )); max_threads.get() } (Err(_), None) => { ui.println(&WarningMessage::new( "Failed to get the number of available cores, defaulting to 1", )); 1 } } } ================================================ FILE: crates/forge/src/main.rs ================================================ use forge::{ExitStatus, main_execution}; use foundry_ui::{UI, components::error::ErrorMessage}; use mimalloc::MiMalloc; use std::io::IsTerminal; use std::process::ExitCode; use std::sync::Arc; use std::{env, io}; #[global_allocator] static GLOBAL: MiMalloc = MiMalloc; fn main() -> ExitCode { let _guard = init_logging(); let ui = Arc::new(UI::default()); match main_execution(ui.clone()) { Ok(ExitStatus::Success) => ExitCode::SUCCESS, Ok(ExitStatus::Failure) => ExitCode::from(1), Err(error) => { ui.println(&ErrorMessage::from(error)); ExitCode::from(2) } } } fn init_logging() -> Option { use chrono::Local; use std::fs; use std::path::PathBuf; use tracing_chrome::ChromeLayerBuilder; use tracing_subscriber::filter::{EnvFilter, LevelFilter, Targets}; use tracing_subscriber::fmt::Layer; use tracing_subscriber::fmt::time::Uptime; use tracing_subscriber::prelude::*; let mut guard = None; let fmt_layer = Layer::new() .with_writer(io::stderr) .with_ansi(io::stderr().is_terminal()) .with_timer(Uptime::default()) .with_filter( EnvFilter::builder() .with_default_directive(LevelFilter::WARN.into()) .with_env_var("SNFORGE_LOG") .from_env_lossy(), ); let tracing_profile = env::var("SNFORGE_TRACING_PROFILE").ok().is_some_and(|var| { let s = var.as_str(); s == "true" || s == "1" }); let profile_layer = if tracing_profile { let mut path = PathBuf::from(format!( "./snforge-profile-{}.json", Local::now().to_rfc3339() )); // Create the file now, so that we early panic, and `fs::canonicalize` will work. let profile_file = fs::File::create(&path).expect("failed to create profile file"); // Try to canonicalize the path so that it is easier to find the file from logs. if let Ok(canonical) = fs::canonicalize(&path) { path = canonical; } eprintln!( "`snforge` run will output tracing profile to: {}", path.display() ); eprintln!( "Open that file with https://ui.perfetto.dev (or chrome://tracing) to analyze it" ); let (profile_layer, profile_layer_guard) = ChromeLayerBuilder::new() .writer(profile_file) .include_args(true) .build(); // Filter out less important logs because they're too verbose, // and with them the profile file quickly grows to several GBs of data. let profile_layer = profile_layer.with_filter( Targets::new() .with_default(LevelFilter::TRACE) .with_target("salsa", LevelFilter::WARN), ); guard = Some(profile_layer_guard); Some(profile_layer) } else { None }; tracing::subscriber::set_global_default( tracing_subscriber::registry() .with(fmt_layer) .with(profile_layer), ) .expect("could not set up global logger"); guard } ================================================ FILE: crates/forge/src/new.rs ================================================ use crate::scarb::config::SCARB_MANIFEST_TEMPLATE_CONTENT; use crate::{CAIRO_EDITION, NewArgs, Template}; use anyhow::{Context, Ok, Result, anyhow, bail, ensure}; use camino::Utf8PathBuf; use include_dir::{Dir, DirEntry, include_dir}; use indoc::formatdoc; use scarb_api::version::scarb_version; use scarb_api::{ScarbCommand, ensure_scarb_available}; use semver::Version; use shared::consts::FREE_RPC_PROVIDER_URL; use std::env; use std::fs::{self, OpenOptions}; use std::io::Write; use std::path::{Path, PathBuf}; use toml_edit::{Array, ArrayOfTables, DocumentMut, Item, Table, Value, value}; const OZ_INTERFACES_VERSION: Version = Version::new(2, 1, 0); const OZ_TOKEN_VERSION: Version = Version::new(3, 0, 0); const OZ_UTILS_VERSION: Version = Version::new(2, 1, 0); static TEMPLATES_DIR: Dir = include_dir!("snforge_templates"); const SCARB_WITHOUT_CAIRO_TEST_TEMPLATE: Version = Version::new(2, 13, 0); struct Dependency { name: String, version: String, dev: bool, } impl Dependency { fn add(&self, scarb_manifest_path: &PathBuf) -> Result<()> { let mut cmd = ScarbCommand::new_with_stdio(); cmd.manifest_path(scarb_manifest_path).offline().arg("add"); if self.dev { cmd.arg("--dev"); } cmd.arg(format!("{}@{}", self.name, self.version)) .run() .context(format!("Failed to add {} dependency", self.name))?; Ok(()) } } struct TemplateManifestConfig { dependencies: Vec, contract_target: bool, fork_config: bool, } impl TemplateManifestConfig { fn add_dependencies(&self, scarb_manifest_path: &PathBuf) -> Result<()> { if env::var("DEV_DISABLE_SNFORGE_STD_DEPENDENCY").is_err() { let snforge_version = env!("CARGO_PKG_VERSION"); Dependency { name: "snforge_std".to_string(), version: snforge_version.to_string(), dev: true, } .add(scarb_manifest_path)?; } for dep in &self.dependencies { dep.add(scarb_manifest_path)?; } Ok(()) } fn update_config(&self, scarb_manifest_path: &Path) -> Result<()> { let scarb_toml_content = fs::read_to_string(scarb_manifest_path)?; let mut document = scarb_toml_content .parse::() .context("invalid document")?; if self.contract_target { add_target_to_toml(&mut document); } set_cairo_edition(&mut document, CAIRO_EDITION); add_test_script(&mut document); add_assert_macros(&mut document)?; add_allow_prebuilt_macros(&mut document)?; if self.fork_config { add_fork_config(&mut document)?; } fs::write(scarb_manifest_path, document.to_string())?; Ok(()) } } impl TryFrom<&Template> for TemplateManifestConfig { type Error = anyhow::Error; fn try_from(template: &Template) -> Result { let cairo_version = scarb_version()?.cairo; match template { Template::CairoProgram => Ok(TemplateManifestConfig { dependencies: vec![], contract_target: false, fork_config: false, }), Template::BalanceContract => Ok(TemplateManifestConfig { dependencies: vec![Dependency { name: "starknet".to_string(), version: cairo_version.to_string(), dev: false, }], contract_target: true, fork_config: false, }), Template::Erc20Contract => Ok(TemplateManifestConfig { dependencies: vec![ Dependency { name: "starknet".to_string(), version: cairo_version.to_string(), dev: false, }, Dependency { name: "openzeppelin_interfaces".to_string(), version: OZ_INTERFACES_VERSION.to_string(), dev: false, }, Dependency { name: "openzeppelin_token".to_string(), version: OZ_TOKEN_VERSION.to_string(), dev: false, }, Dependency { name: "openzeppelin_utils".to_string(), version: OZ_UTILS_VERSION.to_string(), dev: false, }, ], contract_target: true, fork_config: true, }), } } } fn create_snfoundry_manifest(path: &PathBuf) -> Result<()> { fs::write( path, formatdoc! {r#" # Visit https://foundry-rs.github.io/starknet-foundry/appendix/snfoundry-toml.html # and https://foundry-rs.github.io/starknet-foundry/projects/configuration.html for more information # [sncast.default] # Define a profile name # url = "{default_rpc_url}" # Url of the RPC provider # accounts-file = "../account-file" # Path to the file with the account data # account = "mainuser" # Account from `accounts_file` or default account file that will be used for the transactions # keystore = "~/keystore" # Path to the keystore file # wait-params = {{ timeout = 300, retry-interval = 10 }} # Wait for submitted transaction parameters # block-explorer = "Voyager" # Block explorer service used to display links to transaction details # show-explorer-links = true # Print links pointing to pages with transaction details in the chosen block explorer "#, default_rpc_url = FREE_RPC_PROVIDER_URL, }, )?; Ok(()) } fn add_template_to_scarb_manifest(path: &PathBuf) -> Result<()> { if !path.exists() { bail!("Scarb.toml not found"); } let mut file = OpenOptions::new() .append(true) .open(path) .context("Failed to open Scarb.toml")?; file.write_all(SCARB_MANIFEST_TEMPLATE_CONTENT.as_bytes()) .context("Failed to write to Scarb.toml")?; Ok(()) } fn overwrite_or_copy_template_files( dir: &Dir, template_path: &Path, project_path: &Path, project_name: &str, ) -> Result<()> { for entry in dir.entries() { let path_without_template_name = entry.path().strip_prefix(template_path)?; let destination = project_path.join(path_without_template_name); match entry { DirEntry::Dir(dir) => { fs::create_dir_all(&destination)?; overwrite_or_copy_template_files(dir, template_path, project_path, project_name)?; } DirEntry::File(file) => { let contents = file.contents(); let contents = replace_project_name(contents, project_name)?; fs::write(destination, contents)?; } } } Ok(()) } fn replace_project_name(contents: &[u8], project_name: &str) -> Result> { let contents = std::str::from_utf8(contents).context("UTF-8 error")?; let contents = contents.replace("{{ PROJECT_NAME }}", project_name); Ok(contents.into_bytes()) } fn add_test_script(document: &mut DocumentMut) { let mut test = Table::new(); test.insert("test", value("snforge test")); document.insert("scripts", Item::Table(test)); } fn add_target_to_toml(document: &mut DocumentMut) { let mut array_of_tables = ArrayOfTables::new(); let mut sierra = Table::new(); let mut contract = Table::new(); contract.set_implicit(true); sierra.insert("sierra", Item::Value(true.into())); array_of_tables.push(sierra); contract.insert("starknet-contract", Item::ArrayOfTables(array_of_tables)); document.insert("target", Item::Table(contract)); } fn set_cairo_edition(document: &mut DocumentMut, cairo_edition: &str) { document["package"]["edition"] = value(cairo_edition); } fn add_assert_macros(document: &mut DocumentMut) -> Result<()> { let version = scarb_version()?.cairo; document .entry("dev-dependencies") .or_insert(Item::Table(Table::new())) .as_table_mut() .context("Failed to get dev-dependencies from Scarb.toml")? .insert("assert_macros", value(version.to_string())); Ok(()) } fn add_allow_prebuilt_macros(document: &mut DocumentMut) -> Result<()> { let tool_section = document.entry("tool").or_insert(Item::Table(Table::new())); let tool_table = tool_section .as_table_mut() .context("Failed to get tool table from Scarb.toml")?; tool_table.set_implicit(true); let mut scarb_table = Table::new(); let mut allow_prebuilt_macros = Array::new(); allow_prebuilt_macros.push("snforge_std"); scarb_table.insert( "allow-prebuilt-plugins", Item::Value(Value::Array(allow_prebuilt_macros)), ); tool_table.insert("scarb", Item::Table(scarb_table)); Ok(()) } fn add_fork_config(document: &mut DocumentMut) -> Result<()> { let tool_section = document.entry("tool").or_insert(Item::Table(Table::new())); let tool_table = tool_section .as_table_mut() .context("Failed to get tool table from Scarb.toml")?; let mut fork_table = Table::new(); fork_table.insert("name", Item::Value(Value::from("SEPOLIA_LATEST"))); fork_table.insert("url", Item::Value(Value::from(FREE_RPC_PROVIDER_URL))); let mut block_id_table = Table::new(); block_id_table.insert("tag", Item::Value(Value::from("latest"))); fork_table.insert( "block_id", Item::Value(Value::from(block_id_table.into_inline_table())), ); let mut array_of_tables = ArrayOfTables::new(); array_of_tables.push(fork_table); let mut fork = Table::new(); fork.set_implicit(true); fork.insert("fork", Item::ArrayOfTables(array_of_tables)); tool_table.insert("snforge", Item::Table(fork)); Ok(()) } fn extend_gitignore(path: &Path) -> Result<()> { if path.join(".gitignore").exists() { let mut file = OpenOptions::new() .append(true) .open(path.join(".gitignore"))?; writeln!(file, ".snfoundry_cache/")?; writeln!(file, "snfoundry_trace/")?; writeln!(file, "coverage/")?; writeln!(file, "profile/")?; } Ok(()) } pub fn new( NewArgs { path, name, no_vcs, overwrite, template, }: NewArgs, ) -> Result<()> { ensure_scarb_available()?; if !overwrite { ensure!( !path.exists() || path.read_dir().is_ok_and(|mut i| i.next().is_none()), format!( "The provided path `{path}` points to a non-empty directory. If you wish to create a project in this directory, use the `--overwrite` flag" ) ); } let name = infer_name(name, &path)?; let scarb_version = scarb_version()?.scarb; if matches!(template, Template::Erc20Contract) { let min_scarb_version = Version::new(2, 15, 1); ensure!( scarb_version >= min_scarb_version, format!( "The `erc20-contract` template requires Scarb version {min_scarb_version} or higher. Current Scarb version: {scarb_version}. Please update Scarb to use this template." ) ); } fs::create_dir_all(&path)?; let project_path = path.canonicalize()?; let scarb_manifest_path = project_path.join("Scarb.toml"); let snfoundry_manifest_path = project_path.join("snfoundry.toml"); // if there is no Scarb.toml run `scarb init` if !scarb_manifest_path.is_file() { let mut cmd = ScarbCommand::new_with_stdio(); cmd.current_dir(&project_path) .args(["init", "--name", &name]); if no_vcs { cmd.arg("--no-vcs"); } // TODO(#3910) let test_runner = if scarb_version < SCARB_WITHOUT_CAIRO_TEST_TEMPLATE { "cairo-test" } else { "none" }; cmd.env("SCARB_INIT_TEST_RUNNER", test_runner) .env("SCARB_INIT_EMPTY", "true") .run() .context("Failed to initialize a new project")?; // TODO(#3910) if scarb_version < SCARB_WITHOUT_CAIRO_TEST_TEMPLATE { ScarbCommand::new_with_stdio() .current_dir(&project_path) .manifest_path(scarb_manifest_path.clone()) .offline() .arg("remove") .arg("--dev") .arg("cairo_test") .run() .context("Failed to remove cairo_test dependency")?; } } add_template_to_scarb_manifest(&scarb_manifest_path)?; if !snfoundry_manifest_path.is_file() { create_snfoundry_manifest(&snfoundry_manifest_path)?; } let template_config = TemplateManifestConfig::try_from(&template)?; template_config.add_dependencies(&scarb_manifest_path)?; template_config.update_config(&scarb_manifest_path)?; let template_dir = get_template_dir(&template)?; overwrite_or_copy_template_files(&template_dir, template_dir.path(), &project_path, &name)?; extend_gitignore(&project_path)?; // Fetch to create lock file. if env::var("DEV_USE_OFFLINE_MODE").is_err() { ScarbCommand::new_with_stdio() .manifest_path(scarb_manifest_path) .arg("fetch") .run() .context("Failed to fetch created project")?; } Ok(()) } fn infer_name(name: Option, path: &Utf8PathBuf) -> Result { let name = if let Some(name) = name { name } else { let Some(file_name) = path.file_name() else { bail!("Cannot infer package name from path: {path}. Please: use the flag `--name`"); }; file_name.to_string() }; Ok(name) } fn get_template_dir(template: &Template) -> Result> { let dir_name = match template { Template::CairoProgram => "cairo_program", Template::BalanceContract => "balance_contract", Template::Erc20Contract => "erc20_contract", }; TEMPLATES_DIR .get_dir(dir_name) .ok_or_else(|| anyhow!("Directory {dir_name} not found")) .cloned() } ================================================ FILE: crates/forge/src/optimize_inlining/args.rs ================================================ use crate::TestArgs; use anyhow::{Result, ensure}; use clap::Parser; use std::num::NonZeroU32; #[derive(Parser, Debug)] pub struct OptimizeInliningArgs { /// Minimum inlining-strategy value to test // Arbitrary default value. #[arg(long, default_value = "0")] pub min_threshold: u32, /// Maximum inlining-strategy value to test // Arbitrary default value. #[arg(long, default_value = "250")] pub max_threshold: u32, /// Step size for threshold search // Arbitrary default value. #[arg(long, default_value = "25")] pub step: NonZeroU32, /// Maximum allowed contract file size in bytes // Limits are documented here: https://docs.starknet.io/learn/cheatsheets/chain-info#current-limits #[arg(long, default_value = "4089446")] pub max_contract_size: u64, /// Maximum allowed length of compiled contract program. // Limits are documented here: https://docs.starknet.io/learn/cheatsheets/chain-info#current-limits #[arg(long, default_value = "81920")] pub max_contract_program_len: u64, /// Update Scarb.toml with the threshold that minimizes runtime gas cost #[arg(long, conflicts_with = "size")] pub gas: bool, /// Update Scarb.toml with the threshold that minimizes contract size cost #[arg(long, conflicts_with = "gas")] pub size: bool, /// Comma-delimited list of contract names or Cairo paths (e.g. `MyContract,pkg::MyOther`) /// to include in contract size checks. #[arg(long, value_delimiter = ',', required = true)] pub contracts: Vec, /// Test arguments (same as for `snforge test`) #[command(flatten)] pub test_args: TestArgs, } impl OptimizeInliningArgs { pub fn validate(&self) -> Result<()> { ensure!( self.test_args.exact, "optimize-inlining requires using the `--exact` flag" ); ensure!( self.min_threshold <= self.max_threshold, "min-threshold ({}) must be <= max-threshold ({})", self.min_threshold, self.max_threshold ); Ok(()) } } #[cfg(test)] mod tests { use super::OptimizeInliningArgs; use clap::Parser; #[test] fn validation_fails_without_exact() { let args = OptimizeInliningArgs::parse_from(["snforge", "--contracts", "MyContract", "test_name"]); let error = args.validate().unwrap_err().to_string(); assert!(error.contains("optimize-inlining requires using the `--exact` flag")); } #[test] fn validation_fails_without_test_name() { let args = OptimizeInliningArgs::try_parse_from([ "snforge", "--exact", "--contracts", "MyContract", ]); let error = args.unwrap_err().to_string(); assert!(error.contains( "error: the following required arguments were not provided:\n " )); } #[test] fn validation_passes_with_single_exact_test_name() { let args = OptimizeInliningArgs::parse_from([ "snforge", "--exact", "--contracts", "MyContract", "test_name", ]); args.validate().unwrap(); } } ================================================ FILE: crates/forge/src/optimize_inlining/contract_size.rs ================================================ use anyhow::{Context, Result, bail}; use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; use cairo_lang_starknet_classes::contract_class::ContractClass; use camino::Utf8PathBuf; use scarb_api::artifacts::deserialized::artifacts_for_package; use std::collections::HashSet; use std::fs; const MODULE_PATH_SEPARATOR: &str = "::"; #[derive(Debug)] pub struct ContractSizeInfo { pub contract_id: String, pub artifact_type: ContractArtifactType, pub size: u64, pub felts_count: u64, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] // Currently, we price both artifact types with the same weight. // Thus, this enum could be completely removed from the source code. // Kept for debugging and general code descriptivity. pub enum ContractArtifactType { Sierra, Casm, } pub fn check_and_validate_contract_sizes( starknet_artifacts_paths: &[Utf8PathBuf], max_size: u64, max_felts_count: u64, contracts_filter: &[String], ) -> Result<(bool, Vec)> { let mut sizes = Vec::new(); let mut all_valid = true; let mut matched_filters: HashSet<&str> = HashSet::new(); let mut available_contracts: Vec = Vec::new(); for starknet_artifacts_path in starknet_artifacts_paths { let artifacts = artifacts_for_package(starknet_artifacts_path.as_path())?; let artifacts_dir = starknet_artifacts_path .parent() .expect("Starknet artifacts path must have a parent"); for contract in &artifacts.contracts { available_contracts.push(contract.contract_name.clone()); let matching_filter = contracts_filter.iter().find(|f| { if contract.contract_name == **f { return true; } if !f.contains(MODULE_PATH_SEPARATOR) { return false; } contract.module_path.ends_with(f.as_str()) }); let Some(filter) = matching_filter else { continue; }; matched_filters.insert(filter.as_str()); let sierra_path = artifacts_dir.join(&contract.artifacts.sierra); let size = get_contract_size(&sierra_path)?; if size > max_size { all_valid = false; } let class: ContractClass = serde_json::from_str(&fs::read_to_string(&sierra_path)?)?; let sierra_felts: u64 = class.sierra_program.len() as u64; if sierra_felts > max_felts_count { all_valid = false; } sizes.push(ContractSizeInfo { contract_id: contract.id.clone(), artifact_type: ContractArtifactType::Sierra, size, felts_count: sierra_felts, }); if let Some(casm_path) = &contract.artifacts.casm { let casm_path = artifacts_dir.join(casm_path); let size = get_contract_size(&casm_path)?; if size > max_size { all_valid = false; } let class: CasmContractClass = serde_json::from_str(&fs::read_to_string(&casm_path)?)?; let casm_felts: u64 = class.bytecode.len() as u64; if casm_felts > max_felts_count { all_valid = false; } sizes.push(ContractSizeInfo { contract_id: contract.id.clone(), artifact_type: ContractArtifactType::Casm, size, felts_count: casm_felts, }); } } } let unmatched: Vec<&str> = contracts_filter .iter() .filter(|f| !matched_filters.contains(f.as_str())) .map(String::as_str) .collect(); if !unmatched.is_empty() { bail!( "The following contracts were not found in starknet artifacts: {}. Available contracts: {}", unmatched.join(", "), available_contracts.join(", ") ); } Ok((all_valid, sizes)) } fn get_contract_size(contract_path: &Utf8PathBuf) -> Result { let size = fs::metadata(contract_path) .with_context(|| format!("Failed to read {contract_path}"))? .len(); Ok(size) } ================================================ FILE: crates/forge/src/optimize_inlining/mod.rs ================================================ mod args; mod contract_size; mod optimizer; mod paths; mod runner; pub use args::OptimizeInliningArgs; use crate::ExitStatus; use anyhow::{Context, Result, bail}; use camino::Utf8PathBuf; use foundry_ui::UI; use optimizer::Optimizer; use paths::copy_project_to_temp_dir; use scarb_api::manifest::ManifestEditor; use scarb_api::metadata::{MetadataOpts, metadata_with_opts}; use std::sync::Arc; pub fn optimize_inlining( args: &OptimizeInliningArgs, cores: usize, ui: &Arc, ) -> Result { args.validate()?; let profile = args.test_args.scarb_args.profile.specified(); ui.println(&format!( "Starting inlining strategy optimization...\n\ Search range: {} to {}, step: {}, max contract size: {} bytes, max felts: {}", args.min_threshold, args.max_threshold, args.step, args.max_contract_size, args.max_contract_program_len )); let original_metadata = metadata_with_opts(MetadataOpts { profile: profile.clone(), ..MetadataOpts::default() })?; let workspace_root = &original_metadata.workspace.root; let target_dir = &original_metadata .target_dir .unwrap_or(workspace_root.join("target")); let temp_dir = copy_project_to_temp_dir(workspace_root)?; let temp_path = Utf8PathBuf::try_from(temp_dir.path().to_path_buf()) .context("Temporary directory path is not valid UTF-8")?; let scarb_metadata = metadata_with_opts(MetadataOpts { profile: profile.clone(), current_dir: Some(temp_path.clone().into()), ..MetadataOpts::default() })?; let manifest_editor = ManifestEditor::new(&original_metadata.runtime_manifest); let mut optimizer = Optimizer::new(args, &scarb_metadata); let optimization_result = optimizer.optimize(args, cores, ui); ui.print_blank_line(); ui.println(&"Optimization Results:".to_string()); optimizer.print_results_table(ui); let workspace_name = workspace_root .file_name() .map(|n| format!("{n}_")) .unwrap_or_default(); let graph_path = target_dir.join(format!( "{workspace_name}optimization_results_l_{}_h_{}_s_{}.png", args.min_threshold, args.max_threshold, args.step )); create_output_dir::create_output_dir(target_dir.as_std_path())?; if let Err(e) = optimizer.save_results_graph(&graph_path, ui) { ui.eprintln(&format!("Warning: Failed to save graph: {e}")); } match optimization_result { Ok(_) => { let profile_name = profile.unwrap_or_else(|| "release".to_string()); let optimal = if args.gas { Some(optimizer.find_best_result_by_gas()?) } else if args.size { Some(optimizer.find_best_result_by_contract_size()?) } else { None }; if let Some(optimal) = optimal { manifest_editor.set_inlining_strategy(optimal.threshold, &profile_name)?; ui.println(&format!( "Updated Scarb.toml with inlining-strategy = {}", optimal.threshold )); } else { ui.println( &"Scarb.toml not modified. Use --gas or --size to apply a threshold." .to_string(), ); } Ok(ExitStatus::Success) } Err(e) => { bail!(format!("Optimization failed: {e}")); } } } ================================================ FILE: crates/forge/src/optimize_inlining/optimizer.rs ================================================ use crate::optimize_inlining::args::OptimizeInliningArgs; use crate::optimize_inlining::runner::{ OptimizationIterationResult, TotalGas, compile_default, run_optimization_iteration, }; use anyhow::{Result, anyhow}; use camino::Utf8Path; use comfy_table::modifiers::UTF8_ROUND_CORNERS; use comfy_table::presets::UTF8_FULL; use comfy_table::{Cell, ContentArrangement, Table}; use foundry_ui::UI; use plotters::prelude::*; use plotters::style::{FontStyle, register_font}; use scarb_api::metadata::Metadata; use std::num::NonZeroU32; use std::sync::Arc; const ROBOTO_REGULAR: &[u8] = include_bytes!("../../assets/fonts/Roboto-Regular.ttf"); const ROBOTO_FAMILY: &str = "roboto"; pub struct Optimizer { pub min_threshold: u32, pub max_threshold: u32, pub step: NonZeroU32, pub results: Vec, scarb_metadata: Metadata, } pub struct OptimalResult { pub threshold: u32, pub total_gas: TotalGas, pub contract_code_l2_gas: u64, } impl Optimizer { pub fn new(args: &OptimizeInliningArgs, scarb_metadata: &Metadata) -> Self { Self { min_threshold: args.min_threshold, max_threshold: args.max_threshold, step: args.step, results: Vec::new(), scarb_metadata: scarb_metadata.clone(), } } pub fn optimize( &mut self, args: &OptimizeInliningArgs, cores: usize, ui: &Arc, ) -> Result { compile_default(&self.scarb_metadata, ui)?; self.optimize_bruteforce(args, cores, ui) } pub fn find_best_result_by_gas(&self) -> Result { Ok(self .valid_results()? .into_iter() .min_by(|a, b| { a.total_gas .l2() .total_cmp(&b.total_gas.l2()) .then(a.contract_code_l2_gas.cmp(&b.contract_code_l2_gas)) .then(a.threshold.cmp(&b.threshold)) }) .map(|r| OptimalResult { threshold: r.threshold, total_gas: r.total_gas.clone(), contract_code_l2_gas: r.contract_code_l2_gas, }) .expect("valid_results must return at least one result")) } pub fn find_best_result_by_contract_size(&self) -> Result { Ok(self .valid_results()? .into_iter() .min_by(|a, b| { a.contract_code_l2_gas .cmp(&b.contract_code_l2_gas) .then(a.total_gas.l2().total_cmp(&b.total_gas.l2())) .then(a.threshold.cmp(&b.threshold)) }) .map(|r| OptimalResult { threshold: r.threshold, total_gas: r.total_gas.clone(), contract_code_l2_gas: r.contract_code_l2_gas, }) .expect("valid_results must return at least one result")) } pub fn print_results_table(&self, ui: &UI) { let mut sorted_results: Vec<_> = self.results.iter().collect(); sorted_results.sort_by_key(|r| r.threshold); let mut table = Table::new(); table.load_preset(UTF8_FULL); table.apply_modifier(UTF8_ROUND_CORNERS); table.set_content_arrangement(ContentArrangement::Dynamic); table.set_header(vec![ Cell::new("Threshold"), Cell::new("Total Gas"), Cell::new("Contract Size"), Cell::new("Contract Bytecode L2 Gas"), Cell::new("Status"), ]); for r in sorted_results { let status = if r.tests_passed && r.error.is_none() { "✓" } else { "✗" }; let gas_str = if r.tests_passed && r.error.is_none() { format!("{:.0}", r.total_gas.l2()) } else { "-".to_string() }; table.add_row(vec![ Cell::new(r.threshold), Cell::new(gas_str), Cell::new(r.max_contract_size), Cell::new(r.contract_code_l2_gas), Cell::new(status), ]); } ui.println(&table.to_string()); if let Ok(best) = self.find_best_result_by_gas() { ui.print_blank_line(); ui.println(&format!( "Lowest runtime gas cost: threshold={}, gas={:.0}, contract bytecode L2 gas={}", best.threshold, best.total_gas.l2(), best.contract_code_l2_gas )); } if let Ok(best) = self.find_best_result_by_contract_size() { ui.println(&format!( "Lowest contract size cost: threshold={}, gas={:.0}, contract bytecode L2 gas={}", best.threshold, best.total_gas.l2(), best.contract_code_l2_gas )); } } #[allow(clippy::too_many_lines)] pub fn save_results_graph(&self, output_path: &Utf8Path, ui: &UI) -> Result<()> { for style in [ FontStyle::Normal, FontStyle::Bold, FontStyle::Oblique, FontStyle::Italic, ] { register_font(ROBOTO_FAMILY, style, ROBOTO_REGULAR) .map_err(|_| anyhow!("Failed to register bundled Roboto-Regular.ttf font"))?; register_font("sans-serif", style, ROBOTO_REGULAR) .map_err(|_| anyhow!("Failed to register bundled Roboto-Regular.ttf font"))?; } let mut sorted_results = self.valid_results()?; if sorted_results.len() < 2 { ui.println(&"Not enough data points to draw graph (need at least 2)".to_string()); return Ok(()); } sorted_results.sort_by_key(|r| r.threshold); let (max_gas, max_contract_code_l2_gas) = self.get_max_values(); let gas_points: Vec<(f64, f64)> = sorted_results .iter() .map(|r| (f64::from(r.threshold), r.total_gas.l2() / max_gas)) .collect(); let bytecode_l2_gas_points: Vec<(f64, f64)> = sorted_results .iter() .map(|r| { (f64::from(r.threshold), { #[allow(clippy::cast_precision_loss)] let gas = r.contract_code_l2_gas as f64; gas / max_contract_code_l2_gas }) }) .collect(); let x_min = f64::from(self.min_threshold); let x_max = f64::from(self.max_threshold); let y_min = gas_points .iter() .chain(bytecode_l2_gas_points.iter()) .map(|(_, y)| *y) .fold(f64::MAX, f64::min) * 0.95; let y_max = gas_points .iter() .chain(bytecode_l2_gas_points.iter()) .map(|(_, y)| *y) .fold(f64::MIN, f64::max) * 1.05; let root = BitMapBackend::new(output_path.as_std_path(), (1920, 1080)).into_drawing_area(); root.fill(&WHITE)?; let mut chart = ChartBuilder::on(&root) .caption("Inlining Optimization Results", (ROBOTO_FAMILY, 48)) .margin(40) .x_label_area_size(80) .y_label_area_size(100) .build_cartesian_2d(x_min..x_max, y_min..y_max)?; chart .configure_mesh() .x_desc("Threshold") .y_desc("Normalized Value") .label_style((ROBOTO_FAMILY, 24)) .x_label_formatter(&|x| format!("{x:.0}")) .y_label_formatter(&|y| format!("{y:.2}")) .draw()?; chart .draw_series(LineSeries::new(gas_points.clone(), RED.stroke_width(3)))? .label("Gas") .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 40, y)], RED.stroke_width(3))); chart.draw_series( gas_points .iter() .map(|(x, y)| Circle::new((*x, *y), 8, RED.filled())), )?; chart .draw_series(LineSeries::new( bytecode_l2_gas_points.clone(), GREEN.stroke_width(3), ))? .label("Contract Bytecode L2 Gas") .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 40, y)], GREEN.stroke_width(3))); chart.draw_series( bytecode_l2_gas_points .iter() .map(|(x, y)| Circle::new((*x, *y), 8, GREEN.filled())), )?; chart .configure_series_labels() .label_font((ROBOTO_FAMILY, 24)) .legend_area_size(50) .background_style(WHITE.mix(0.8)) .border_style(BLACK) .position(SeriesLabelPosition::UpperRight) .draw()?; root.present()?; ui.print_blank_line(); ui.println(&format!("Graph saved to: {output_path}")); Ok(()) } fn optimize_bruteforce( &mut self, args: &OptimizeInliningArgs, cores: usize, ui: &Arc, ) -> Result { let total_iterations = ((self.max_threshold - self.min_threshold) / self.step.get()) + 1; let mut current = self.min_threshold; let mut iteration = 1; while current <= self.max_threshold { if !self.results.iter().any(|r| r.threshold == current) { ui.print_blank_line(); ui.println(&format!( "[{iteration}/{total_iterations}] Testing threshold {current}...", )); let result = run_optimization_iteration(current, args, &self.scarb_metadata, cores, ui)?; if let Some(ref error) = result.error { ui.println(&format!(" ✗ {error}")); } else { ui.println(&format!( " ✓ Tests passed, gas: {:.0}, max contract size: {} bytes, contract bytecode L2 gas: {}", result.total_gas.l2(), result.max_contract_size, result.contract_code_l2_gas )); } self.results.push(result); } current += self.step.get(); iteration += 1; } self.find_best_result_by_gas() } fn valid_results(&self) -> Result> { let results: Vec<_> = self .results .iter() .filter(|r| r.tests_passed && r.error.is_none()) .collect(); if results.is_empty() { return Err(anyhow!("No valid optimization results found")); } Ok(results) } fn get_max_values(&self) -> (f64, f64) { let valid_results = self.valid_results().unwrap_or_default(); let max_gas = valid_results .iter() .map(|r| r.total_gas.l2()) .fold(1.0_f64, f64::max); let max_contract_code_l2_gas = valid_results .iter() .map(|r| r.contract_code_l2_gas) .max() .unwrap_or(1); #[allow(clippy::cast_precision_loss)] (max_gas, max_contract_code_l2_gas as f64) } } ================================================ FILE: crates/forge/src/optimize_inlining/paths.rs ================================================ use anyhow::{Context, Result}; use camino::{Utf8Component, Utf8Path, Utf8PathBuf}; use scarb_api::manifest::overwrite_starknet_contract_target_flags; use std::fs; use toml_edit::{DocumentMut, Item, Value}; // Instead of modifying user project in place (i.e. setting inlining strategy in manifest file), we copy it to a temp dir. // Note this operation is not a trivial recursive copy, as we take care to properly handle relative paths as well. pub(super) fn copy_project_to_temp_dir( workspace_root: &camino::Utf8Path, ) -> Result { let temp_dir = tempfile::TempDir::new().context("Failed to create temporary directory")?; let options = fs_extra::dir::CopyOptions::new().content_only(true); fs_extra::dir::copy(workspace_root, temp_dir.path(), &options) .context("Failed to copy project to temporary directory")?; let copied_workspace_root = Utf8PathBuf::from_path_buf(temp_dir.path().to_path_buf()) .map_err(|_| anyhow::anyhow!("Temporary directory path is not valid UTF-8"))?; rewrite_manifest_paths_to_absolute(workspace_root, &copied_workspace_root)?; Ok(temp_dir) } pub(super) fn rewrite_manifest_paths_to_absolute( original_workspace_root: &Utf8Path, copied_workspace_root: &Utf8Path, ) -> Result<()> { rewrite_manifest_paths_in_dir( original_workspace_root, copied_workspace_root, copied_workspace_root, ) } fn rewrite_manifest_paths_in_dir( original_workspace_root: &Utf8Path, copied_workspace_root: &Utf8Path, dir: &Utf8Path, ) -> Result<()> { for entry in fs::read_dir(dir)? { let entry = entry?; let path = Utf8PathBuf::from_path_buf(entry.path()) .map_err(|_| anyhow::anyhow!("Workspace path is not valid UTF-8"))?; if path.is_dir() { rewrite_manifest_paths_in_dir(original_workspace_root, copied_workspace_root, &path)?; continue; } if path.file_name() != Some("Scarb.toml") { continue; } let relative_manifest_path = path .strip_prefix(copied_workspace_root) .context("Copied manifest path is outside copied workspace root")?; let original_manifest_path = original_workspace_root.join(relative_manifest_path); let original_manifest_dir = original_manifest_path .parent() .context("Manifest path has no parent directory")?; rewrite_single_manifest_paths_to_absolute( &path, original_manifest_dir, original_workspace_root, )?; } Ok(()) } fn rewrite_single_manifest_paths_to_absolute( manifest_path: &Utf8Path, original_manifest_dir: &Utf8Path, original_workspace_root: &Utf8Path, ) -> Result<()> { let content = fs::read_to_string(manifest_path)?; let mut doc = content .parse::() .context("Failed to parse copied Scarb.toml")?; let paths_rewritten = rewrite_dependency_paths_to_absolute( &mut doc, original_manifest_dir, original_workspace_root, ); let target_flags_overwritten = overwrite_starknet_contract_target_flags(&mut doc); if paths_rewritten || target_flags_overwritten { fs::write(manifest_path, doc.to_string())?; } Ok(()) } // Rewrite path dependencies in package and workspace sections. fn rewrite_dependency_paths_to_absolute( doc: &mut DocumentMut, original_manifest_dir: &Utf8Path, original_workspace_root: &Utf8Path, ) -> bool { let mut changed = false; if let Some(workspace_dependencies) = doc .as_table_mut() .get_mut("workspace") .and_then(Item::as_table_mut) .and_then(|workspace| workspace.get_mut("dependencies")) { changed |= rewrite_dependency_table_paths_to_absolute( workspace_dependencies, original_manifest_dir, original_workspace_root, ); } if let Some(package_dependencies) = doc.as_table_mut().get_mut("dependencies") { changed |= rewrite_dependency_table_paths_to_absolute( package_dependencies, original_manifest_dir, original_workspace_root, ); } if let Some(package_dependencies) = doc.as_table_mut().get_mut("dev-dependencies") { changed |= rewrite_dependency_table_paths_to_absolute( package_dependencies, original_manifest_dir, original_workspace_root, ); } changed } fn rewrite_dependency_table_paths_to_absolute( dependencies_item: &mut Item, original_manifest_dir: &Utf8Path, original_workspace_root: &Utf8Path, ) -> bool { let Some(dependencies_table) = dependencies_item.as_table_mut() else { return false; }; let mut changed = false; for (_, dependency_item) in dependencies_table.iter_mut() { match dependency_item { Item::Value(Value::InlineTable(inline_table)) => { if let Some(path_value) = inline_table.get_mut("path") { changed |= rewrite_value_if_relative_path( path_value, original_manifest_dir, original_workspace_root, ); } } Item::Table(dependency_table) => { if let Some(path_item) = dependency_table.get_mut("path") && let Some(path_value) = path_item.as_value_mut() { changed |= rewrite_value_if_relative_path( path_value, original_manifest_dir, original_workspace_root, ); } } _ => {} } } changed } fn rewrite_value_if_relative_path( value_item: &mut Value, original_manifest_dir: &Utf8Path, original_workspace_root: &Utf8Path, ) -> bool { match value_item { Value::String(path) => { let path_str = path.value(); if let Some(absolute_path) = absolutize_path(path_str, original_manifest_dir, original_workspace_root) { *value_item = Value::from(absolute_path); return true; } false } _ => false, } } fn absolutize_path( path: &str, original_manifest_dir: &Utf8Path, original_workspace_root: &Utf8Path, ) -> Option { let utf8_path = Utf8Path::new(path); if utf8_path.is_absolute() { None } else { let resolved_path = normalize_utf8_path_lexically(&original_manifest_dir.join(utf8_path)); if resolved_path.starts_with(original_workspace_root) { None } else { Some(resolved_path.to_string()) } } } pub(super) fn normalize_utf8_path_lexically(path: &Utf8PathBuf) -> Utf8PathBuf { let mut normalized_parts: Vec = Vec::new(); let mut prefix: Option = None; let is_absolute = path.is_absolute(); for component in path.components() { match component { Utf8Component::Prefix(prefix_component) => { prefix = Some(prefix_component.as_str().to_string()); } Utf8Component::RootDir | Utf8Component::CurDir => {} Utf8Component::ParentDir => { if is_absolute || normalized_parts.last().is_some_and(|part| part != "..") { normalized_parts.pop(); } else { normalized_parts.push("..".to_string()); } } Utf8Component::Normal(part) => normalized_parts.push(part.to_string()), } } let mut normalized_path = String::new(); if let Some(prefix) = prefix { normalized_path.push_str(&prefix); } if is_absolute { normalized_path.push('/'); } normalized_path.push_str(&normalized_parts.join("/")); if normalized_path.is_empty() { ".".into() } else { normalized_path.into() } } #[cfg(test)] mod tests { use super::{normalize_utf8_path_lexically, rewrite_manifest_paths_to_absolute}; use anyhow::{Result, anyhow}; use camino::Utf8PathBuf; use indoc::indoc; use std::fs; use toml_edit::DocumentMut; #[test] fn rewrites_relative_manifest_paths_to_absolute_with_original_manifest_as_base() -> Result<()> { let temp_dir = tempfile::tempdir()?; let root = Utf8PathBuf::from_path_buf(temp_dir.path().to_path_buf()) .map_err(|_| anyhow!("Temporary path is not valid UTF-8"))?; let original_workspace = root.join("original-workspace"); let copied_workspace = root.join("copied-workspace"); let original_package = original_workspace.join("crates/package_a"); let copied_package = copied_workspace.join("crates/package_a"); fs::create_dir_all(&original_package)?; fs::create_dir_all(&copied_package)?; let manifest_content = indoc! {r#" [package] name = "package_a" version = "0.1.0" [workspace] [workspace.dependencies] ws_local_dep = { path = "../ws_dep_b" } ws_external_dep = { path = "../../../external/ws_dep_c" } [dependencies] local_dep = { path = "../dep_b" } external_dep = { path = "../../../external/dep_c" } [[target.test]] source-path = "./tests/tests.cairo" [[target.starknet-contract]] casm = false sierra = false "#}; fs::write(original_package.join("Scarb.toml"), manifest_content)?; fs::write(copied_package.join("Scarb.toml"), manifest_content)?; rewrite_manifest_paths_to_absolute(&original_workspace, &copied_workspace)?; let rewritten_manifest = fs::read_to_string(copied_package.join("Scarb.toml"))?; let rewritten_doc = rewritten_manifest.parse::()?; let expected_external_dep_path = normalize_utf8_path_lexically(&original_package.join("../../../external/dep_c")) .to_string(); let expected_ws_external_dep_path = normalize_utf8_path_lexically(&original_package.join("../../../external/ws_dep_c")) .to_string(); assert_eq!( rewritten_doc["dependencies"]["local_dep"]["path"].as_str(), Some("../dep_b") ); assert_eq!( rewritten_doc["dependencies"]["external_dep"]["path"].as_str(), Some(expected_external_dep_path.as_str()) ); assert_eq!( rewritten_doc["workspace"]["dependencies"]["ws_local_dep"]["path"].as_str(), Some("../ws_dep_b") ); assert_eq!( rewritten_doc["workspace"]["dependencies"]["ws_external_dep"]["path"].as_str(), Some(expected_ws_external_dep_path.as_str()) ); assert_eq!( rewritten_doc["target"]["test"][0]["source-path"].as_str(), Some("./tests/tests.cairo") ); assert_eq!( rewritten_doc["target"]["starknet-contract"][0]["casm"].as_bool(), Some(true) ); assert_eq!( rewritten_doc["target"]["starknet-contract"][0]["sierra"].as_bool(), Some(true) ); Ok(()) } } ================================================ FILE: crates/forge/src/optimize_inlining/runner.rs ================================================ use crate::optimize_inlining::args::OptimizeInliningArgs; use crate::optimize_inlining::contract_size::{ ContractArtifactType, ContractSizeInfo, check_and_validate_contract_sizes, }; use crate::run_tests::workspace::execute_workspace; use anyhow::{Context, Result}; use blockifier::blockifier_versioned_constants::VersionedConstants; use blockifier::fee::eth_gas_constants::WORD_WIDTH; use camino::{Utf8Path, Utf8PathBuf}; use forge_runner::test_case_summary::{AnyTestCaseSummary, TestCaseSummary}; use foundry_ui::UI; use indoc::formatdoc; use scarb_api::ScarbCommand; use scarb_api::artifacts::deserialized::artifacts_for_package; use scarb_api::manifest::ManifestEditor; use scarb_api::metadata::{Metadata, MetadataOpts, metadata_with_opts}; use scarb_api::{target_dir_for_workspace, test_targets_by_name}; use starknet_api::transaction::fields::GasVectorComputationMode; use starknet_api::versioned_constants_logic::VersionedConstantsTrait; use std::collections::{HashMap, HashSet}; use std::sync::Arc; use std::{env, fs}; use tokio::runtime::Builder; #[derive(Debug, Clone, Default)] pub struct TotalGas { pub l1: f64, pub l1_data: f64, pub l2: f64, } impl TotalGas { pub fn l2(&self) -> f64 { self.l2 } } #[derive(Debug, Clone)] pub struct OptimizationIterationResult { pub threshold: u32, pub total_gas: TotalGas, pub max_contract_size: u64, pub contract_code_l2_gas: u64, pub tests_passed: bool, pub error: Option, } pub fn compile_default(scarb_metadata: &Metadata, ui: &Arc) -> Result<()> { ui.println(&"Compiling project with default threshold...".to_string()); let profile = &scarb_metadata.current_profile; ScarbCommand::new_with_stdio() .manifest_path(&scarb_metadata.runtime_manifest) .arg("--profile") .arg(profile) .arg("build") .arg("-w") .arg("--test") .run() .map_err(|e| anyhow::anyhow!("Build failed: {e}"))?; let artifacts_dir = target_dir_for_workspace(scarb_metadata).join(profile); let saved_dir = target_dir_for_workspace(scarb_metadata).join("inlining_optimizer_artifacts"); fs::create_dir_all(&saved_dir)?; for entry in fs::read_dir(&artifacts_dir).context("Failed to read artifacts directory")? { let entry = entry?; if entry.file_type()?.is_file() { let src_path = Utf8PathBuf::try_from(entry.path()) .context("Non-UTF-8 path in artifacts directory")?; let dst_path = saved_dir.join(src_path.file_name().context("Missing file name")?); fs::copy(&src_path, &dst_path)?; } } Ok(()) } pub fn run_optimization_iteration( threshold: u32, args: &OptimizeInliningArgs, scarb_metadata: &Metadata, cores: usize, ui: &Arc, ) -> Result { let profile = &scarb_metadata.current_profile; let manifest_editor = ManifestEditor::new(&scarb_metadata.workspace.manifest_path); manifest_editor.set_inlining_strategy(threshold, profile)?; let build_result = ScarbCommand::new_with_stdio() .manifest_path(&scarb_metadata.runtime_manifest) .arg("--profile") .arg(profile) .arg("build") .arg("-w") .arg("--test") .run(); if let Err(e) = build_result { return Ok(OptimizationIterationResult { threshold, total_gas: TotalGas::default(), max_contract_size: 0, contract_code_l2_gas: 0, tests_passed: false, error: Some(format!("Build failed: {e}")), }); } let artifacts_dir = target_dir_for_workspace(scarb_metadata).join(profile); let starknet_artifacts_paths = find_test_target_starknet_artifacts(&artifacts_dir, scarb_metadata)?; if starknet_artifacts_paths.is_empty() { return Err(anyhow::anyhow!( "No starknet_artifacts.json found. Only projects with contracts can be optimized." )); } let saved_dir = target_dir_for_workspace(scarb_metadata).join("inlining_optimizer_artifacts"); let keep_filenames = matching_contract_artifact_filenames(&starknet_artifacts_paths, &args.contracts)?; restore_non_contract_artifacts(&artifacts_dir, &saved_dir, &keep_filenames) .context("Failed to restore non-contract artifacts from default build")?; let (sizes_valid, sizes) = check_and_validate_contract_sizes( &starknet_artifacts_paths, args.max_contract_size, args.max_contract_program_len, &args.contracts, )?; let max_contract_size = sizes.iter().map(|s| s.size).max().unwrap_or(0); let contract_code_l2_gas = contract_code_l2_gas(&sizes)?; if !sizes_valid { return Ok(OptimizationIterationResult { threshold, total_gas: TotalGas::default(), max_contract_size, contract_code_l2_gas, tests_passed: false, error: Some(formatdoc!( r" Contract size {max_contract_size} exceeds limit {} or felts {} exceeds limit {}. Try optimizing with lower threshold limit. ", args.max_contract_size, sizes.iter().map(|s| s.felts_count).max().unwrap_or(0), args.max_contract_program_len )), }); } let test_result = run_tests_with_execute_workspace( scarb_metadata.runtime_manifest.clone().parent().unwrap(), args, cores, ui, )?; let tests_passed = test_result.success; let total_gas = if tests_passed { test_result.total_gas } else { TotalGas::default() }; Ok(OptimizationIterationResult { threshold, total_gas, max_contract_size, contract_code_l2_gas, tests_passed, error: if tests_passed { None } else { Some( test_result .error .unwrap_or_else(|| "Some tests failed".to_string()), ) }, }) } /// Estimates the L2 data gas cost of deploying contarct code for all project contracts. /// /// This estimation is only concerned with the part of L2 data cost, that depends on the compile contract code size. /// /// We sum Sierra and CASM felt counts per contract, convert felts to bytes /// (`felt_count * WORD_WIDTH`), then multiply by the `gas_per_code_byte` /// rate from the latest Starknet versioned constants. /// /// See for details. fn contract_code_l2_gas(sizes: &[ContractSizeInfo]) -> Result { let mut felts_by_contract: HashMap<&str, u64> = HashMap::new(); for size in sizes { if matches!( size.artifact_type, ContractArtifactType::Sierra | ContractArtifactType::Casm ) { *felts_by_contract .entry(size.contract_id.as_str()) .or_default() += size.felts_count; } } let versioned_constants = VersionedConstants::latest_constants(); let gas_per_code_byte = versioned_constants .get_archival_data_gas_costs(&GasVectorComputationMode::All) .gas_per_code_byte; let word_width = u64::try_from(WORD_WIDTH).expect("WORD_WIDTH should fit into u64"); felts_by_contract .values() .try_fold(0_u64, |total, felt_count| { let code_size_bytes = felt_count .checked_mul(word_width) .context("code size in bytes overflowed while calculating L2 gas")?; let code_l2_gas = (gas_per_code_byte * code_size_bytes).to_integer(); total .checked_add(code_l2_gas) .context("contract code L2 gas overflowed") }) } struct TestRunResult { success: bool, total_gas: TotalGas, error: Option, } fn run_tests_with_execute_workspace( root: &Utf8Path, args: &OptimizeInliningArgs, cores: usize, ui: &Arc, ) -> Result { let rt = Builder::new_multi_thread() .max_blocking_threads(cores) .enable_all() .build()?; let original_cwd = env::current_dir()?; env::set_current_dir(root)?; let scarb_metadata = metadata_with_opts(MetadataOpts { profile: args.test_args.scarb_args.profile.specified(), ..MetadataOpts::default() })?; let result = rt.block_on(execute_workspace( &args.test_args, ui.clone(), &scarb_metadata, )); env::set_current_dir(&original_cwd)?; match result { Ok(summary) => { let mut all_passed = true; let mut total_gas = TotalGas::default(); let mut first_error: Option = None; let mut tests_run = 0u64; for test_target in &summary.all_tests { for test_case in &test_target.test_case_summaries { if test_case.is_failed() { tests_run += 1; all_passed = false; if first_error.is_none() { first_error = Some(format!( "Test '{}' failed", test_case.name().unwrap_or("unknown") )); } } if test_case.is_passed() { tests_run += 1; let gas = extract_gas_from_summary(test_case); total_gas.l1 += gas.l1; total_gas.l1_data += gas.l1_data; total_gas.l2 += gas.l2; } } } if tests_run == 0 { return Err(anyhow::anyhow!( "No tests were executed. The --exact filter did not match any test cases." )); } Ok(TestRunResult { success: all_passed, total_gas, error: first_error, }) } Err(e) => Ok(TestRunResult { success: false, total_gas: TotalGas::default(), error: Some(format!("Test execution failed: {e}")), }), } } #[allow(clippy::cast_precision_loss)] fn extract_gas_from_summary(summary: &AnyTestCaseSummary) -> TotalGas { match summary { AnyTestCaseSummary::Single(TestCaseSummary::Passed { gas_info, .. }) => TotalGas { l1: gas_info.gas_used.l1_gas.0 as f64, l1_data: gas_info.gas_used.l1_data_gas.0 as f64, l2: gas_info.gas_used.l2_gas.0 as f64, }, AnyTestCaseSummary::Fuzzing(TestCaseSummary::Passed { gas_info, .. }) => TotalGas { l1: gas_info.l1_gas.mean, l1_data: gas_info.l1_data_gas.mean, l2: gas_info.l2_gas.mean, }, _ => TotalGas::default(), } } fn matching_contract_artifact_filenames( starknet_artifacts_paths: &[Utf8PathBuf], contracts_filter: &[String], ) -> Result> { let mut filenames = HashSet::new(); for starknet_artifacts_path in starknet_artifacts_paths { let artifacts = artifacts_for_package(starknet_artifacts_path.as_path())?; for contract in &artifacts.contracts { let matches = contracts_filter.iter().any(|f| { contract.contract_name == *f || (f.contains("::") && contract.module_path.ends_with(f.as_str())) }); if !matches { continue; } if let Some(name) = contract.artifacts.sierra.file_name() { filenames.insert(name.to_owned()); } if let Some(casm) = &contract.artifacts.casm && let Some(name) = casm.file_name() { filenames.insert(name.to_owned()); } } } Ok(filenames) } fn restore_non_contract_artifacts( artifacts_dir: &Utf8Path, saved_dir: &Utf8Path, keep_filenames: &HashSet, ) -> Result<()> { for entry in fs::read_dir(saved_dir).context("Failed to read saved artifacts directory")? { let entry = entry?; if !entry.file_type()?.is_file() { continue; } let filename = entry.file_name(); let filename_str = filename.to_string_lossy(); if keep_filenames.contains(filename_str.as_ref()) { continue; } let src = Utf8PathBuf::try_from(entry.path()).context("Non-UTF-8 path in saved artifacts")?; let dst = artifacts_dir.join(filename_str.as_ref()); fs::copy(&src, &dst)?; } Ok(()) } fn find_test_target_starknet_artifacts( artifacts_dir: &camino::Utf8Path, scarb_metadata: &Metadata, ) -> Result> { let mut paths = Vec::new(); for package in &scarb_metadata.packages { let test_targets = test_targets_by_name(package); for target_name in test_targets.keys() { let artifact_path = artifacts_dir.join(format!("{target_name}.test.starknet_artifacts.json")); if artifact_path.exists() && has_non_empty_contracts_field(&artifact_path)? { paths.push(artifact_path); } } } Ok(paths) } fn has_non_empty_contracts_field(artifact_path: &Utf8Path) -> Result { let artifacts = artifacts_for_package(artifact_path) .with_context(|| format!("Failed to load starknet artifacts from {artifact_path}"))?; Ok(!artifacts.contracts.is_empty()) } ================================================ FILE: crates/forge/src/profile_validation/backtrace.rs ================================================ use crate::TestArgs; use crate::profile_validation::{check_cairo_profile_entries, get_manifest}; use anyhow::ensure; use indoc::formatdoc; use scarb_metadata::Metadata; /// Checks if backtrace can be generated based on scarb version, profile settings extracted from /// the provided [`Metadata`] and if native execution is disabled in the provided [`TestArgs`]. #[allow(unused_variables)] pub fn check_backtrace_compatibility( test_args: &TestArgs, scarb_metadata: &Metadata, ) -> anyhow::Result<()> { #[cfg(feature = "cairo-native")] check_if_native_disabled(test_args)?; check_profile(scarb_metadata)?; Ok(()) } /// Checks if native execution is disabled in the provided [`TestArgs`]. #[cfg(feature = "cairo-native")] fn check_if_native_disabled(test_args: &TestArgs) -> anyhow::Result<()> { ensure!( !test_args.run_native, "Backtrace generation is not supported with `cairo-native` execution", ); Ok(()) } /// Checks if the runtime profile settings in the provided from [`Metadata`] contain the required entries for backtrace generation. fn check_profile(scarb_metadata: &Metadata) -> anyhow::Result<()> { const BACKTRACE_REQUIRED_ENTRIES: &[(&str, &str)] = &[ ("unstable-add-statements-functions-debug-info", "true"), ("unstable-add-statements-code-locations-debug-info", "true"), ("panic-backtrace", "true"), ]; let manifest = get_manifest(scarb_metadata)?; let has_needed_entries = check_cairo_profile_entries(&manifest, scarb_metadata, BACKTRACE_REQUIRED_ENTRIES); ensure!( has_needed_entries, formatdoc! { "Scarb.toml must have the following Cairo compiler configuration to run backtrace: [profile.{profile}.cairo] unstable-add-statements-functions-debug-info = true unstable-add-statements-code-locations-debug-info = true panic-backtrace = true ... other entries ... ", profile = scarb_metadata.current_profile }, ); Ok(()) } ================================================ FILE: crates/forge/src/profile_validation/coverage.rs ================================================ use crate::profile_validation::{check_cairo_profile_entries, get_manifest}; use anyhow::ensure; use indoc::formatdoc; use scarb_metadata::Metadata; /// Checks if coverage can be based on scarb version and profile settings extracted from the provided [`Metadata`]. pub fn check_coverage_compatibility(scarb_metadata: &Metadata) -> anyhow::Result<()> { check_profile(scarb_metadata) } /// Checks if the runtime profile settings in the provided from [`Metadata`] contain the required entries for coverage generation. fn check_profile(scarb_metadata: &Metadata) -> anyhow::Result<()> { const CAIRO_COVERAGE_REQUIRED_ENTRIES: &[(&str, &str)] = &[ ("unstable-add-statements-functions-debug-info", "true"), ("unstable-add-statements-code-locations-debug-info", "true"), ("inlining-strategy", "avoid"), ]; let manifest = get_manifest(scarb_metadata)?; let has_needed_entries = check_cairo_profile_entries(&manifest, scarb_metadata, CAIRO_COVERAGE_REQUIRED_ENTRIES); ensure!( has_needed_entries, formatdoc! { "Scarb.toml must have the following Cairo compiler configuration to run coverage: [profile.{profile}.cairo] unstable-add-statements-functions-debug-info = true unstable-add-statements-code-locations-debug-info = true inlining-strategy = \"avoid\" ... other entries ... ", profile = scarb_metadata.current_profile }, ); Ok(()) } ================================================ FILE: crates/forge/src/profile_validation/mod.rs ================================================ mod backtrace; mod coverage; use crate::TestArgs; use crate::profile_validation::backtrace::check_backtrace_compatibility; use crate::profile_validation::coverage::check_coverage_compatibility; use forge_runner::backtrace::is_backtrace_enabled; use scarb_metadata::Metadata; use std::fs; use toml_edit::{DocumentMut, Table}; /// Checks if current profile provided in [`Metadata`] can be used to run coverage and backtrace if applicable. pub fn check_profile_compatibility( test_args: &TestArgs, scarb_metadata: &Metadata, ) -> anyhow::Result<()> { if test_args.coverage { check_coverage_compatibility(scarb_metadata)?; } if is_backtrace_enabled() { check_backtrace_compatibility(test_args, scarb_metadata)?; } Ok(()) } /// Gets the runtime manifest from the [`Metadata`] and parses it into a [`DocumentMut`]. fn get_manifest(scarb_metadata: &Metadata) -> anyhow::Result { Ok(fs::read_to_string(&scarb_metadata.runtime_manifest)?.parse::()?) } /// Check if the Cairo profile entries in the manifest contain the required entries. fn check_cairo_profile_entries( manifest: &DocumentMut, scarb_metadata: &Metadata, required_entries: &[(&str, &str)], ) -> bool { manifest .get("profile") .and_then(|profile| profile.get(&scarb_metadata.current_profile)) .and_then(|profile| profile.get("cairo")) .and_then(|cairo| cairo.as_table()) .is_some_and(|profile_cairo| { required_entries .iter() .all(|(key, value)| contains_entry_with_value(profile_cairo, key, value)) }) } /// Check if the table contains an entry with the given key and value. /// Accepts only bool and string values. fn contains_entry_with_value(table: &Table, key: &str, value: &str) -> bool { table.get(key).is_some_and(|entry| { if let Some(entry) = entry.as_bool() { entry.to_string() == value } else if let Some(entry) = entry.as_str() { entry == value } else { false } }) } ================================================ FILE: crates/forge/src/run_tests/maat.rs ================================================ #[must_use] pub fn env_ignore_fork_tests() -> bool { std::env::var("SNFORGE_IGNORE_FORK_TESTS").is_ok_and(|v| v == "1") } ================================================ FILE: crates/forge/src/run_tests/messages/collected_tests_count.rs ================================================ use console::style; use foundry_ui::Message; use serde::Serialize; use serde_json::{Value, json}; #[derive(Serialize)] pub struct CollectedTestsCountMessage { pub tests_num: usize, pub package_name: String, } impl Message for CollectedTestsCountMessage { fn text(&self) -> String { let full = format!( "\n\nCollected {} test(s) from {} package", self.tests_num, self.package_name ); style(full).bold().to_string() } fn json(&self) -> Value { json!(self) } } ================================================ FILE: crates/forge/src/run_tests/messages/latest_blocks_numbers.rs ================================================ use foundry_ui::Message; use serde::Serialize; use serde_json::{Value, json}; use std::{collections::HashMap, fmt::Write}; #[derive(Serialize)] pub struct LatestBlocksNumbersMessage { url_to_latest_block_number_map: HashMap, } impl LatestBlocksNumbersMessage { #[must_use] pub fn new( url_to_latest_block_number_map: HashMap, ) -> Self { Self { url_to_latest_block_number_map, } } } impl Message for LatestBlocksNumbersMessage { fn text(&self) -> String { let mut output = String::new(); output = format!("{output}\n"); for (url, latest_block_number) in &self.url_to_latest_block_number_map { let _ = writeln!( &mut output, "Latest block number = {latest_block_number} for url = {url}" ); } output } fn json(&self) -> Value { json!(self) } } ================================================ FILE: crates/forge/src/run_tests/messages/mod.rs ================================================ pub mod collected_tests_count; pub mod latest_blocks_numbers; pub mod overall_summary; pub mod partition; pub mod tests_failure_summary; pub mod tests_run; pub mod tests_summary; ================================================ FILE: crates/forge/src/run_tests/messages/overall_summary.rs ================================================ use console::style; use forge_runner::test_target_summary::TestTargetSummary; use forge_runner::tests_summary::TestsSummary; use foundry_ui::{Message, components::labeled::LabeledMessage}; use serde::Serialize; use serde_json::{Value, json}; #[derive(Serialize)] pub struct OverallSummaryMessage { summary: TestsSummary, } impl OverallSummaryMessage { pub const LABEL: &str = "Tests summary"; #[must_use] pub fn new(summaries: &[TestTargetSummary], filtered: Option) -> Self { Self { summary: TestsSummary::new(summaries, filtered), } } } impl Message for OverallSummaryMessage { fn text(&self) -> String { let styled_label = style(&Self::LABEL).bold().to_string(); LabeledMessage::new(&styled_label, &self.summary.format_summary_message()).text() } fn json(&self) -> Value { json!(self) } } ================================================ FILE: crates/forge/src/run_tests/messages/partition.rs ================================================ use console::style; use forge_runner::partition::Partition; use foundry_ui::{Message, components::labeled::LabeledMessage}; use serde::Serialize; use serde_json::{Value, json}; #[derive(Serialize)] pub struct PartitionFinishedMessage { partition: Partition, included_tests_count: usize, total_tests_count: usize, } impl PartitionFinishedMessage { #[must_use] pub fn new( partition: Partition, included_tests_count: usize, total_tests_count: usize, ) -> Self { Self { partition, included_tests_count, total_tests_count, } } fn summary(&self) -> String { format!( "{}/{}, included {} out of total {} tests", self.partition.index(), self.partition.total(), self.included_tests_count, self.total_tests_count, ) } } impl Message for PartitionFinishedMessage { fn text(&self) -> String { let label = style("Finished partition run").bold().to_string(); LabeledMessage::new(&label, &self.summary()).text() } fn json(&self) -> Value { json!(self) } } #[derive(Serialize)] pub struct PartitionStartedMessage { partition: Partition, } impl PartitionStartedMessage { #[must_use] pub fn new(partition: Partition) -> Self { Self { partition } } fn summary(&self) -> String { format!("{}/{}", self.partition.index(), self.partition.total()) } } impl Message for PartitionStartedMessage { fn text(&self) -> String { let label = style("Running partition run").bold().to_string(); LabeledMessage::new(&label, &self.summary()).text() } fn json(&self) -> Value { json!(self) } } ================================================ FILE: crates/forge/src/run_tests/messages/tests_failure_summary.rs ================================================ use console::style; use forge_runner::test_case_summary::AnyTestCaseSummary; use foundry_ui::Message; use serde::Serialize; use serde_json::{Value, json}; use std::fmt::Write; #[derive(Serialize)] pub struct TestsFailureSummaryMessage { pub failed_test_names: Vec, } impl TestsFailureSummaryMessage { #[must_use] pub fn new(all_failed_tests: &[&AnyTestCaseSummary]) -> Self { let failed_test_names = all_failed_tests .iter() .map(|any_test_case_summary| any_test_case_summary.name().unwrap().to_string()) .collect(); Self { failed_test_names } } } impl Message for TestsFailureSummaryMessage { fn text(&self) -> String { if self.failed_test_names.is_empty() { return String::new(); } let mut failures = "\nFailures:".to_string(); for name in &self.failed_test_names { let _ = write!(&mut failures, "\n {name}"); } style(failures).bold().to_string() } fn json(&self) -> Value { json!(self) } } ================================================ FILE: crates/forge/src/run_tests/messages/tests_run.rs ================================================ use console::style; use forge_runner::package_tests::TestTargetLocation; use foundry_ui::Message; use serde::Serialize; use serde_json::{Value, json}; #[derive(Serialize)] pub struct TestsRunMessage { test_target_location: TestTargetLocation, tests_num: usize, } impl TestsRunMessage { #[must_use] pub fn new(test_target_location: TestTargetLocation, tests_num: usize) -> Self { Self { test_target_location, tests_num, } } } impl Message for TestsRunMessage { fn text(&self) -> String { let dir_name = match self.test_target_location { TestTargetLocation::Lib => "src", TestTargetLocation::Tests => "tests", }; let plain_text = format!("Running {} test(s) from {}/", self.tests_num, dir_name); style(plain_text).bold().to_string() } fn json(&self) -> Value { json!(self) } } ================================================ FILE: crates/forge/src/run_tests/messages/tests_summary.rs ================================================ use console::style; use forge_runner::{test_target_summary::TestTargetSummary, tests_summary::TestsSummary}; use foundry_ui::{Message, components::labeled::LabeledMessage}; use serde::Serialize; use serde_json::{Value, json}; #[derive(Serialize)] pub struct TestsSummaryMessage { summary: TestsSummary, } impl TestsSummaryMessage { pub const LABEL: &str = "Tests"; #[must_use] pub fn new(summaries: &[TestTargetSummary], filtered: Option) -> Self { Self { summary: TestsSummary::new(summaries, filtered), } } } impl Message for TestsSummaryMessage { fn text(&self) -> String { let styled_label = style(&Self::LABEL).bold().to_string(); LabeledMessage::new(&styled_label, &self.summary.format_summary_message()).text() } fn json(&self) -> Value { json!(self) } } ================================================ FILE: crates/forge/src/run_tests/package.rs ================================================ use super::{ resolve_config::resolve_config, test_target::{ExitFirstChannel, TestTargetRunResult, run_for_test_target}, }; use crate::scarb::{ config::{ForgeConfigFromScarb, ForkTarget}, load_package_config, }; use crate::{ TestArgs, block_number_map::BlockNumberMap, combine_configs::combine_configs, run_tests::messages::{ collected_tests_count::CollectedTestsCountMessage, tests_run::TestsRunMessage, tests_summary::TestsSummaryMessage, }, shared_cache::FailedTestsCache, test_filter::{NameFilter, TestsFilter}, warn::warn_if_incompatible_rpc_version, }; use anyhow::Result; use camino::{Utf8Path, Utf8PathBuf}; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; use console::Style; use forge_runner::{ forge_config::{ForgeConfig, ForgeTrackedResource}, package_tests::{ raw::TestTargetRaw, with_config::TestTargetWithConfig, with_config_resolved::{TestCaseWithResolvedConfig, sanitize_test_case_name}, }, partition::PartitionConfig, running::target::prepare_test_target, scarb::load_test_artifacts, test_case_summary::AnyTestCaseSummary, test_target_summary::TestTargetSummary, }; use foundry_ui::{UI, components::labeled::LabeledMessage}; use scarb_api::{CompilationOpts, get_contracts_artifacts_and_source_sierra_paths}; use scarb_metadata::{Metadata, PackageMetadata}; use std::sync::Arc; use tokio::task::JoinHandle; pub struct PackageTestResult { summaries: Vec, filtered: Option, } impl PackageTestResult { #[must_use] pub fn new(summaries: Vec, filtered: Option) -> Self { Self { summaries, filtered, } } #[must_use] pub fn filtered(&self) -> Option { self.filtered } #[must_use] pub fn summaries(self) -> Vec { self.summaries } } pub struct RunForPackageArgs { pub target_handles: Vec>>, pub tests_filter: TestsFilter, pub forge_config: Arc, pub fork_targets: Vec, pub package_name: String, pub package_root: Utf8PathBuf, } impl RunForPackageArgs { #[tracing::instrument(skip_all, level = "debug")] pub fn build( package: PackageMetadata, scarb_metadata: &Metadata, args: &TestArgs, cache_dir: &Utf8PathBuf, artifacts_dir: &Utf8Path, partitioning_config: PartitionConfig, ui: &UI, ) -> Result { let mut raw_test_targets = load_test_artifacts(artifacts_dir, &package)?; let contracts = get_contracts_artifacts_and_source_sierra_paths( artifacts_dir, &package, ui, CompilationOpts { use_test_target_contracts: !args.no_optimization, #[cfg(feature = "cairo-native")] run_native: args.run_native, }, )?; let contracts_data = ContractsData::try_from(contracts)?; let forge_config_from_scarb = load_package_config::(scarb_metadata, &package.id)?; let forge_config = Arc::new(combine_configs( args, contracts_data, cache_dir.clone(), &forge_config_from_scarb, )); let tests_filter = TestsFilter::from_flags( args.test_filter.clone(), args.exact, args.skip.clone(), args.only_ignored, args.include_ignored, args.rerun_failed, FailedTestsCache::new(cache_dir), partitioning_config, ); if args.deterministic_output { raw_test_targets.sort_by_key(|t| t.tests_location); } let tracked_resource = forge_config.test_runner_config.tracked_resource; let target_handles = raw_test_targets .into_iter() .map(|t| spawn_prepare_test_target(t, tracked_resource)) .collect(); Ok(RunForPackageArgs { target_handles, forge_config, tests_filter, fork_targets: forge_config_from_scarb.fork, package_name: package.name.clone(), package_root: package.root, }) } } fn spawn_prepare_test_target( target: TestTargetRaw, tracked_resource: ForgeTrackedResource, ) -> JoinHandle> { tokio::task::spawn_blocking(move || prepare_test_target(target, &tracked_resource)) } fn sum_test_cases_from_test_target( test_cases: &[TestCaseWithResolvedConfig], partitioning_config: &PartitionConfig, ) -> usize { match partitioning_config { PartitionConfig::Disabled => test_cases.len(), PartitionConfig::Enabled { partition, partition_map, } => test_cases .iter() .filter(|test_case| { let test_assigned_index = partition_map .get_assigned_index(&sanitize_test_case_name(&test_case.name)) .expect("Partition map must contain all test cases"); test_assigned_index == partition.index() }) .count(), } } #[tracing::instrument(skip_all, level = "debug")] pub async fn run_for_package( RunForPackageArgs { target_handles, forge_config, tests_filter, fork_targets, package_name, package_root: _, }: RunForPackageArgs, block_number_map: &BlockNumberMap, ui: Arc, exit_first_channel: &mut ExitFirstChannel, ) -> Result { // Resolve all targets first so the collected count includes #[ignore] filtering. let mut resolved_targets = vec![]; let mut all_tests = 0; let mut not_filtered_total = 0; for handle in target_handles { let target_with_config = handle.await??; let mut resolved = resolve_config( target_with_config, &fork_targets, block_number_map, &tests_filter, ) .await?; let all = sum_test_cases_from_test_target( &resolved.test_cases, &tests_filter.partitioning_config, ); tests_filter.filter_tests(&mut resolved.test_cases)?; let not_filtered = sum_test_cases_from_test_target( &resolved.test_cases, &tests_filter.partitioning_config, ); all_tests += all; not_filtered_total += not_filtered; resolved_targets.push(resolved); } warn_if_incompatible_rpc_version(&resolved_targets, ui.clone()).await?; ui.println(&CollectedTestsCountMessage { tests_num: not_filtered_total, package_name: package_name.clone(), }); let mut summaries = vec![]; for resolved in resolved_targets { ui.println(&TestsRunMessage::new( resolved.tests_location, sum_test_cases_from_test_target( &resolved.test_cases, &tests_filter.partitioning_config, ), )); let summary = run_for_test_target( resolved, forge_config.clone(), &tests_filter, ui.clone(), exit_first_channel, ) .await?; let (TestTargetRunResult::Ok(s) | TestTargetRunResult::Interrupted(s)) = summary; summaries.push(s); } // TODO(#2574): Bring back "filtered out" number in tests summary when running with `--exact` flag let filtered_count = if let NameFilter::ExactMatch(_) = tests_filter.name_filter { None } else { Some(all_tests - not_filtered_total) }; ui.println(&TestsSummaryMessage::new(&summaries, filtered_count)); let any_fuzz_test_was_run = summaries.iter().any(|test_target_summary| { test_target_summary .test_case_summaries .iter() .filter(|summary| matches!(summary, AnyTestCaseSummary::Fuzzing(_))) .any(|summary| summary.is_passed() || summary.is_failed()) }); if any_fuzz_test_was_run { ui.println(&LabeledMessage::new( &Style::new().bold().apply_to("Fuzzer seed").to_string(), &forge_config.test_runner_config.fuzzer_seed.to_string(), )); } Ok(PackageTestResult::new(summaries, filtered_count)) } ================================================ FILE: crates/forge/src/run_tests/resolve_config.rs ================================================ use super::maat::env_ignore_fork_tests; use crate::{ block_number_map::BlockNumberMap, scarb::config::ForkTarget, test_filter::TestsFilter, }; use anyhow::{Result, anyhow}; use cheatnet::runtime_extensions::forge_config_extension::config::{ BlockId, InlineForkConfig, OverriddenForkConfig, RawForkConfig, }; use conversions::byte_array::ByteArray; use forge_runner::{ filtering::{FilterResult, TestCaseFilter}, package_tests::{ with_config::TestTargetWithConfig, with_config_resolved::{ ResolvedForkConfig, TestCaseResolvedConfig, TestCaseWithResolvedConfig, TestTargetWithResolvedConfig, }, }, }; use starknet_api::block::BlockNumber; #[tracing::instrument(skip_all, level = "debug")] pub async fn resolve_config( test_target: TestTargetWithConfig, fork_targets: &[ForkTarget], block_number_map: &BlockNumberMap, tests_filter: &TestsFilter, ) -> Result { let mut test_cases = Vec::with_capacity(test_target.test_cases.len()); let env_ignore_fork_tests = env_ignore_fork_tests(); for case in test_target.test_cases { let filter_result = tests_filter.filter(&case); let should_be_run = matches!(filter_result, FilterResult::Included); test_cases.push(TestCaseWithResolvedConfig::new( &case.name, case.test_details.clone(), TestCaseResolvedConfig { available_gas: case.config.available_gas, ignored: case.config.ignored || (env_ignore_fork_tests && case.config.fork_config.is_some()), fork_config: if should_be_run { resolve_fork_config(case.config.fork_config, block_number_map, fork_targets) .await? } else { None }, expected_result: case.config.expected_result, fuzzer_config: case.config.fuzzer_config, disable_predeployed_contracts: case.config.disable_predeployed_contracts, }, )); } Ok(TestTargetWithResolvedConfig { tests_location: test_target.tests_location, sierra_program: test_target.sierra_program, sierra_program_path: test_target.sierra_program_path, casm_program: test_target.casm_program, test_cases, }) } async fn resolve_fork_config( fork_config: Option, block_number_map: &BlockNumberMap, fork_targets: &[ForkTarget], ) -> Result> { let Some(fc) = fork_config else { return Ok(None); }; let raw_fork_params = replace_id_with_params(fc, fork_targets)?; let url = raw_fork_params.url; let block_number = match raw_fork_params.block { BlockId::BlockNumber(block_number) => BlockNumber(block_number), BlockId::BlockHash(hash) => { block_number_map .get_block_number_for_hash(url.clone(), hash) .await? } BlockId::BlockTag => { block_number_map .get_latest_block_number(url.clone()) .await? } }; Ok(Some(ResolvedForkConfig { url, block_number })) } fn get_fork_target_from_runner_config<'a>( fork_targets: &'a [ForkTarget], name: &ByteArray, ) -> Result<&'a ForkTarget> { fork_targets .iter() .find(|fork| fork.name == name.to_string()) .ok_or_else(|| { let name = name.to_string(); anyhow!("Fork configuration named = {name} not found in the Scarb.toml") }) } fn replace_id_with_params( raw_fork_config: RawForkConfig, fork_targets: &[ForkTarget], ) -> Result { match raw_fork_config { RawForkConfig::Inline(raw_fork_params) => Ok(raw_fork_params), RawForkConfig::Named(name) => { let fork_target_from_runner_config = get_fork_target_from_runner_config(fork_targets, &name)?; let block_id = fork_target_from_runner_config.block_id.clone(); Ok(InlineForkConfig { url: fork_target_from_runner_config.url.clone(), block: block_id, }) } RawForkConfig::Overridden(OverriddenForkConfig { name, block }) => { let fork_target_from_runner_config = get_fork_target_from_runner_config(fork_targets, &name)?; let url = fork_target_from_runner_config.url.clone(); Ok(InlineForkConfig { url, block }) } } } #[cfg(test)] mod tests { use super::*; use crate::shared_cache::FailedTestsCache; use cairo_lang_sierra::program::ProgramArtifact; use cairo_lang_sierra::{ids::GenericTypeId, program::Program}; use forge_runner::package_tests::TestTargetLocation; use forge_runner::package_tests::with_config::{TestCaseConfig, TestCaseWithConfig}; use forge_runner::partition::PartitionConfig; use forge_runner::{expected_result::ExpectedTestResult, package_tests::TestDetails}; use std::sync::Arc; use universal_sierra_compiler_api::compile_raw_sierra; use url::Url; fn program_for_testing() -> ProgramArtifact { ProgramArtifact { program: Program { type_declarations: vec![], libfunc_declarations: vec![], statements: vec![], funcs: vec![], }, debug_info: None, } } fn create_test_case_with_config( name: &str, ignored: bool, fork_config: Option, ) -> TestCaseWithConfig { TestCaseWithConfig { name: name.to_string(), config: TestCaseConfig { available_gas: None, ignored, expected_result: ExpectedTestResult::Success, fork_config, fuzzer_config: None, disable_predeployed_contracts: false, }, test_details: TestDetails { sierra_entry_point_statement_idx: 100, parameter_types: vec![ GenericTypeId("RangeCheck".into()), GenericTypeId("GasBuiltin".into()), ], }, } } fn create_test_target_with_cases(test_cases: Vec) -> TestTargetWithConfig { TestTargetWithConfig { sierra_program: program_for_testing(), sierra_program_path: Arc::default(), casm_program: Arc::new( compile_raw_sierra(&serde_json::to_value(&program_for_testing().program).unwrap()) .unwrap(), ), test_cases, tests_location: TestTargetLocation::Lib, } } fn create_fork_target(name: &str, url: &str, block_id: BlockId) -> ForkTarget { ForkTarget { name: name.to_string(), url: Url::parse(url).expect("Should be valid url"), block_id, } } #[tokio::test] async fn to_runnable_non_existent_id() { let mocked_tests = create_test_target_with_cases(vec![create_test_case_with_config( "crate1::do_thing", false, Some(RawForkConfig::Named("non_existent".into())), )]); let tests_filter = TestsFilter::from_flags( None, false, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); assert!( resolve_config( mocked_tests, &[create_fork_target( "definitely_non_existing", "https://not_taken.com", BlockId::BlockNumber(120) )], &BlockNumberMap::default(), &tests_filter, ) .await .is_err() ); } #[tokio::test] async fn test_ignored_filter_skips_fork_config_resolution() { let ignored_test = create_test_case_with_config( "ignored_test", true, Some(RawForkConfig::Named("non_existent_fork".into())), ); let test_target = create_test_target_with_cases(vec![ignored_test]); // Create a filter that excludes ignored tests let tests_filter = TestsFilter::from_flags( None, false, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let resolved = resolve_config(test_target, &[], &BlockNumberMap::default(), &tests_filter) .await .unwrap(); assert_eq!(resolved.test_cases.len(), 1); assert!(resolved.test_cases[0].config.ignored); assert!(resolved.test_cases[0].config.fork_config.is_none()); } #[tokio::test] async fn test_non_ignored_filter_resolves_fork_config() { let test_case = create_test_case_with_config( "valid_test", false, Some(RawForkConfig::Named("valid_fork".into())), ); let test_target = create_test_target_with_cases(vec![test_case]); let fork_targets = vec![create_fork_target( "valid_fork", "https://example.com", BlockId::BlockNumber(100), )]; let tests_filter = TestsFilter::from_flags( None, false, Vec::new(), false, true, false, FailedTestsCache::default(), PartitionConfig::default(), ); let resolved = resolve_config( test_target, &fork_targets, &BlockNumberMap::default(), &tests_filter, ) .await .unwrap(); assert_eq!(resolved.test_cases.len(), 1); assert!(!resolved.test_cases[0].config.ignored); assert!(resolved.test_cases[0].config.fork_config.is_some()); let fork_config = resolved.test_cases[0].config.fork_config.as_ref().unwrap(); assert_eq!(fork_config.url.as_str(), "https://example.com/"); assert_eq!(fork_config.block_number.0, 100); } #[tokio::test] async fn test_name_filtered_test_still_resolves_fork_config() { let test_case = create_test_case_with_config( "filtered_out_test", false, Some(RawForkConfig::Named("valid_fork".into())), ); let test_target = create_test_target_with_cases(vec![test_case]); let fork_targets = vec![create_fork_target( "valid_fork", "https://example.com", BlockId::BlockNumber(100), )]; let tests_filter = TestsFilter::from_flags( Some("different_pattern".to_string()), false, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let resolved = resolve_config( test_target, &fork_targets, &BlockNumberMap::default(), &tests_filter, ) .await .unwrap(); assert_eq!(resolved.test_cases.len(), 1); assert!(!resolved.test_cases[0].config.ignored); assert!(resolved.test_cases[0].config.fork_config.is_some()); let fork_config = resolved.test_cases[0].config.fork_config.as_ref().unwrap(); assert_eq!(fork_config.url.as_str(), "https://example.com/"); assert_eq!(fork_config.block_number.0, 100); } #[tokio::test] async fn test_mixed_scenarios_with_ignored_filter() { let test_cases = vec![ create_test_case_with_config( "ignored_with_valid_fork", true, Some(RawForkConfig::Named("valid_fork".into())), ), create_test_case_with_config( "matching_test", false, Some(RawForkConfig::Named("valid_fork".into())), ), create_test_case_with_config( "non_matching_test", false, Some(RawForkConfig::Named("valid_fork".into())), ), create_test_case_with_config("no_fork_test", false, None), ]; let test_target = create_test_target_with_cases(test_cases); let fork_targets = vec![create_fork_target( "valid_fork", "https://example.com", BlockId::BlockNumber(200), )]; let tests_filter = TestsFilter::from_flags( Some("matching".to_string()), false, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let resolved = resolve_config( test_target, &fork_targets, &BlockNumberMap::default(), &tests_filter, ) .await .unwrap(); assert_eq!(resolved.test_cases.len(), 4); // Check ignored test - should have no fork config resolved let ignored_test = &resolved.test_cases[0]; assert_eq!(ignored_test.name, "ignored_with_valid_fork"); assert!(ignored_test.config.ignored); assert!(ignored_test.config.fork_config.is_none()); // Check matching test - should have fork config resolved let matching_test = &resolved.test_cases[1]; assert_eq!(matching_test.name, "matching_test"); assert!(!matching_test.config.ignored); assert!(matching_test.config.fork_config.is_some()); // Check non-matching test - should still have fork config resolved (name filtering happens later) let non_matching_test = &resolved.test_cases[2]; assert_eq!(non_matching_test.name, "non_matching_test"); assert!(!non_matching_test.config.ignored); assert!(non_matching_test.config.fork_config.is_some()); // Check no-fork test - should work fine let no_fork_test = &resolved.test_cases[3]; assert_eq!(no_fork_test.name, "no_fork_test"); assert!(!no_fork_test.config.ignored); assert!(no_fork_test.config.fork_config.is_none()); } #[tokio::test] async fn test_only_ignored_filter_skips_non_ignored_fork_resolution() { let test_cases = vec![ create_test_case_with_config( "ignored_test", true, Some(RawForkConfig::Named("valid_fork".into())), ), create_test_case_with_config( "non_ignored_test", false, Some(RawForkConfig::Named("valid_fork".into())), ), ]; let test_target = create_test_target_with_cases(test_cases); let fork_targets = vec![create_fork_target( "valid_fork", "https://example.com", BlockId::BlockNumber(400), )]; let tests_filter = TestsFilter::from_flags( None, false, Vec::new(), true, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let resolved = resolve_config( test_target, &fork_targets, &BlockNumberMap::default(), &tests_filter, ) .await .unwrap(); assert_eq!(resolved.test_cases.len(), 2); // Ignored test should have fork config resolved since it should be run let ignored_test = &resolved.test_cases[0]; assert_eq!(ignored_test.name, "ignored_test"); assert!(ignored_test.config.ignored); assert!(ignored_test.config.fork_config.is_some()); // Non-ignored test should not have fork config resolved since it won't be run let non_ignored_test = &resolved.test_cases[1]; assert_eq!(non_ignored_test.name, "non_ignored_test"); assert!(!non_ignored_test.config.ignored); assert!(non_ignored_test.config.fork_config.is_none()); } #[tokio::test] async fn test_include_ignored_filter_resolves_all_fork_configs() { let test_cases = vec![ create_test_case_with_config( "ignored_test", true, Some(RawForkConfig::Named("valid_fork".into())), ), create_test_case_with_config( "non_ignored_test", false, Some(RawForkConfig::Named("valid_fork".into())), ), ]; let test_target = create_test_target_with_cases(test_cases); let fork_targets = vec![create_fork_target( "valid_fork", "https://example.com", BlockId::BlockNumber(500), )]; let tests_filter = TestsFilter::from_flags( None, false, Vec::new(), false, true, false, FailedTestsCache::default(), PartitionConfig::default(), ); let resolved = resolve_config( test_target, &fork_targets, &BlockNumberMap::default(), &tests_filter, ) .await .unwrap(); assert_eq!(resolved.test_cases.len(), 2); for test_case in &resolved.test_cases { assert!(test_case.config.fork_config.is_some()); let fork_config = test_case.config.fork_config.as_ref().unwrap(); assert_eq!(fork_config.url.as_str(), "https://example.com/"); assert_eq!(fork_config.block_number.0, 500); } let ignored_test = &resolved.test_cases[0]; assert_eq!(ignored_test.name, "ignored_test"); assert!(ignored_test.config.ignored); let non_ignored_test = &resolved.test_cases[1]; assert_eq!(non_ignored_test.name, "non_ignored_test"); assert!(!non_ignored_test.config.ignored); } #[tokio::test] async fn test_fork_config_resolution_with_inline_config() { let test_case = create_test_case_with_config( "test_with_inline_fork", false, Some(RawForkConfig::Inline(InlineForkConfig { url: "https://inline-fork.com".parse().unwrap(), block: BlockId::BlockNumber(123), })), ); let test_target = create_test_target_with_cases(vec![test_case]); let tests_filter = TestsFilter::from_flags( None, false, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let resolved = resolve_config(test_target, &[], &BlockNumberMap::default(), &tests_filter) .await .unwrap(); assert_eq!(resolved.test_cases.len(), 1); let test_case = &resolved.test_cases[0]; assert!(!test_case.config.ignored); assert!(test_case.config.fork_config.is_some()); let fork_config = test_case.config.fork_config.as_ref().unwrap(); assert_eq!(fork_config.url.as_str(), "https://inline-fork.com/"); assert_eq!(fork_config.block_number.0, 123); } #[tokio::test] async fn test_overridden_fork_config_resolution() { let test_case = create_test_case_with_config( "test_with_overridden_fork", false, Some(RawForkConfig::Overridden(OverriddenForkConfig { name: "base_fork".into(), block: BlockId::BlockNumber(999), })), ); let test_target = create_test_target_with_cases(vec![test_case]); let fork_targets = vec![create_fork_target( "base_fork", "https://base-fork.com", BlockId::BlockNumber(100), )]; let tests_filter = TestsFilter::from_flags( None, false, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let resolved = resolve_config( test_target, &fork_targets, &BlockNumberMap::default(), &tests_filter, ) .await .unwrap(); assert_eq!(resolved.test_cases.len(), 1); let test_case = &resolved.test_cases[0]; assert!(test_case.config.fork_config.is_some()); let fork_config = test_case.config.fork_config.as_ref().unwrap(); assert_eq!(fork_config.url.as_str(), "https://base-fork.com/"); assert_eq!(fork_config.block_number.0, 999); } #[tokio::test] async fn test_skip_filter_does_not_affect_fork_resolution() { let test_case = create_test_case_with_config( "test_to_be_skipped", false, Some(RawForkConfig::Named("valid_fork".into())), ); let test_target = create_test_target_with_cases(vec![test_case]); let fork_targets = vec![create_fork_target( "valid_fork", "https://example.com", BlockId::BlockNumber(600), )]; let tests_filter = TestsFilter::from_flags( None, false, vec!["skipped".to_string()], false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let resolved = resolve_config( test_target, &fork_targets, &BlockNumberMap::default(), &tests_filter, ) .await .unwrap(); assert_eq!(resolved.test_cases.len(), 1); let test_case = &resolved.test_cases[0]; assert!(!test_case.config.ignored); assert!(test_case.config.fork_config.is_some()); let fork_config = test_case.config.fork_config.as_ref().unwrap(); assert_eq!(fork_config.url.as_str(), "https://example.com/"); assert_eq!(fork_config.block_number.0, 600); } } ================================================ FILE: crates/forge/src/run_tests/test_target.rs ================================================ use anyhow::Result; use forge_runner::filtering::{ExcludeReason, FilterResult, TestCaseFilter}; use forge_runner::messages::TestResultMessage; use forge_runner::{ forge_config::ForgeConfig, maybe_generate_coverage, maybe_save_trace_and_profile, package_tests::with_config_resolved::TestTargetWithResolvedConfig, run_for_test_case, test_case_summary::{AnyTestCaseSummary, TestCaseSummary}, test_target_summary::TestTargetSummary, }; use foundry_ui::UI; use futures::{StreamExt, stream::FuturesUnordered}; use std::sync::Arc; use tokio::sync::mpsc::{Receiver, Sender, channel}; /// Shared cancellation channel for `--exit-first`. /// /// The channel is created at the workspace level and shared across all packages, /// so failure in one package immediately interrupts test cases in all subsequent packages. pub struct ExitFirstChannel { sender: Sender<()>, receiver: Receiver<()>, } impl Default for ExitFirstChannel { fn default() -> Self { Self::new() } } impl ExitFirstChannel { #[must_use] pub fn new() -> Self { let (sender, receiver) = channel(1); Self { sender, receiver } } #[must_use] pub fn sender(&self) -> Sender<()> { self.sender.clone() } pub fn close(&mut self) { self.receiver.close(); } } #[non_exhaustive] pub enum TestTargetRunResult { Ok(TestTargetSummary), Interrupted(TestTargetSummary), } #[tracing::instrument(skip_all, level = "debug")] pub async fn run_for_test_target( tests: TestTargetWithResolvedConfig, forge_config: Arc, tests_filter: &impl TestCaseFilter, ui: Arc, exit_first_channel: &mut ExitFirstChannel, ) -> Result { let casm_program = tests.casm_program.clone(); let mut tasks = FuturesUnordered::new(); for case in tests.test_cases { let case_name = case.name.clone(); let filter_result = tests_filter.filter(&case); match filter_result { FilterResult::Excluded(reason) => match reason { ExcludeReason::ExcludedFromPartition => { tasks.push(tokio::task::spawn(async { Ok(AnyTestCaseSummary::Single( TestCaseSummary::ExcludedFromPartition {}, )) })); } ExcludeReason::Ignored => { tasks.push(tokio::task::spawn(async { Ok(AnyTestCaseSummary::Single(TestCaseSummary::Ignored { name: case_name, })) })); } }, FilterResult::Included => { tasks.push(run_for_test_case( Arc::new(case), casm_program.clone(), forge_config.clone(), tests.sierra_program_path.clone(), exit_first_channel.sender(), )); } } } let mut results = vec![]; let mut saved_trace_data_paths = vec![]; let mut interrupted = false; let deterministic_output = forge_config.test_runner_config.deterministic_output; let print_test_result = |result: &AnyTestCaseSummary| { let test_result_message = TestResultMessage::new( result, forge_config.output_config.detailed_resources, forge_config.test_runner_config.tracked_resource, ); ui.println(&test_result_message); }; while let Some(task) = tasks.next().await { let result = task??; // Skip printing; Print all results at once in a sorted order once they are available if !deterministic_output && should_print_test_result_message(&result) { print_test_result(&result); } let trace_path = maybe_save_trace_and_profile( &result, &forge_config.output_config.execution_data_to_save, )?; if let Some(path) = trace_path { saved_trace_data_paths.push(path); } if result.is_failed() && forge_config.test_runner_config.exit_first { interrupted = true; exit_first_channel.close(); } results.push(result); } if deterministic_output { let mut sorted_results: Vec<_> = results .iter() .filter(|r| should_print_test_result_message(r)) .collect(); sorted_results.sort_by_key(|r| r.name().unwrap_or("")); for result in sorted_results { print_test_result(result); } } maybe_generate_coverage( &forge_config.output_config.execution_data_to_save, &saved_trace_data_paths, &ui, )?; let summary = TestTargetSummary { test_case_summaries: results, }; if interrupted { Ok(TestTargetRunResult::Interrupted(summary)) } else { Ok(TestTargetRunResult::Ok(summary)) } } fn should_print_test_result_message(result: &AnyTestCaseSummary) -> bool { !result.is_interrupted() && !result.is_excluded_from_partition() } ================================================ FILE: crates/forge/src/run_tests/workspace.rs ================================================ use super::package::RunForPackageArgs; use crate::profile_validation::check_profile_compatibility; use crate::run_tests::messages::latest_blocks_numbers::LatestBlocksNumbersMessage; use crate::run_tests::messages::overall_summary::OverallSummaryMessage; use crate::run_tests::messages::partition::{PartitionFinishedMessage, PartitionStartedMessage}; use crate::run_tests::messages::tests_failure_summary::TestsFailureSummaryMessage; use crate::warn::{ error_if_snforge_std_not_compatible, warn_if_snforge_std_does_not_match_package_version, }; use crate::{ ColorOption, ExitStatus, TestArgs, block_number_map::BlockNumberMap, run_tests::package::run_for_package, run_tests::test_target::ExitFirstChannel, scarb::build_artifacts_with_scarb, shared_cache::FailedTestsCache, }; use anyhow::Result; use forge_runner::partition::PartitionConfig; use forge_runner::test_case_summary::AnyTestCaseSummary; use forge_runner::{CACHE_DIR, test_target_summary::TestTargetSummary}; use foundry_ui::UI; use scarb_api::metadata::{MetadataOpts, metadata_with_opts}; use scarb_api::{ metadata::{Metadata, PackageMetadata}, packages_from_filter, target_dir_for_workspace, }; use scarb_ui::args::PackagesFilter; use shared::consts::SNFORGE_TEST_FILTER; use std::env; use std::sync::Arc; #[derive(Debug)] pub struct WorkspaceExecutionSummary { pub all_tests: Vec, } #[tracing::instrument(skip_all, level = "debug")] #[allow(clippy::too_many_lines)] pub async fn run_for_workspace(args: TestArgs, ui: Arc) -> Result { let scarb_metadata = metadata_with_opts(MetadataOpts { profile: args.scarb_args.profile.specified(), ..MetadataOpts::default() })?; let packages: Vec = packages_from_filter(&scarb_metadata, &args.scarb_args.packages_filter)?; let filter = PackagesFilter::generate_for::(packages.iter()); build_artifacts_with_scarb( filter.clone(), args.scarb_args.features.clone(), args.scarb_args.profile.clone(), args.no_optimization, )?; let WorkspaceExecutionSummary { all_tests } = execute_workspace(&args, ui, &scarb_metadata).await?; let has_failures = extract_failed_tests(&all_tests).next().is_some(); Ok(if has_failures { ExitStatus::Failure } else { ExitStatus::Success }) } pub async fn execute_workspace( args: &TestArgs, ui: Arc, scarb_metadata: &Metadata, ) -> Result { let deterministic_output = args.deterministic_output; match args.color { // SAFETY: This runs in a single-threaded environment. ColorOption::Always => unsafe { env::set_var("CLICOLOR_FORCE", "1") }, // SAFETY: This runs in a single-threaded environment. ColorOption::Never => unsafe { env::set_var("CLICOLOR", "0") }, ColorOption::Auto => (), } check_profile_compatibility(args, scarb_metadata)?; error_if_snforge_std_not_compatible(scarb_metadata)?; warn_if_snforge_std_does_not_match_package_version(scarb_metadata, &ui)?; let packages: Vec = packages_from_filter(scarb_metadata, &args.scarb_args.packages_filter)?; let artifacts_dir_path = target_dir_for_workspace(scarb_metadata).join(&scarb_metadata.current_profile); if args.exact { let test_filter = args.test_filter.clone(); if let Some(last_filter) = test_filter.and_then(|filter| filter.split("::").last().map(String::from)) { set_forge_test_filter(last_filter); } } let block_number_map = BlockNumberMap::default(); let mut all_tests = vec![]; let mut total_filtered_count = Some(0); let mut exit_first_channel = ExitFirstChannel::new(); let workspace_root = &scarb_metadata.workspace.root; let cache_dir = workspace_root.join(CACHE_DIR); let packages_len = packages.len(); let partitioning_config = get_partitioning_config(args, &ui, &packages, &artifacts_dir_path)?; // Spawn config passes for all packages before running any tests so that // compilation overlaps with test execution across packages. let mut all_package_args = Vec::with_capacity(packages.len()); for pkg in packages { let cwd = env::current_dir()?; env::set_current_dir(&pkg.root)?; let pkg_args = RunForPackageArgs::build( pkg, scarb_metadata, args, &cache_dir, &artifacts_dir_path, partitioning_config.clone(), &ui, )?; env::set_current_dir(&cwd)?; all_package_args.push(pkg_args); } for pkg_args in all_package_args { let cwd = env::current_dir()?; env::set_current_dir(&pkg_args.package_root)?; let result = run_for_package( pkg_args, &block_number_map, ui.clone(), &mut exit_first_channel, ) .await?; let filtered = result.filtered(); all_tests.extend(result.summaries()); total_filtered_count = calculate_total_filtered_count(total_filtered_count, filtered); env::set_current_dir(&cwd)?; } let overall_summary = OverallSummaryMessage::new(&all_tests, total_filtered_count); let mut all_failed_tests: Vec<&AnyTestCaseSummary> = extract_failed_tests(&all_tests).collect(); if deterministic_output { all_failed_tests.sort_by(|a, b| a.name().unwrap_or("").cmp(b.name().unwrap_or(""))); } FailedTestsCache::new(&cache_dir).save_failed_tests(&all_failed_tests)?; let url_to_block_number = block_number_map.get_url_to_latest_block_number(); if !url_to_block_number.is_empty() { ui.println(&LatestBlocksNumbersMessage::new(url_to_block_number)); } ui.println(&TestsFailureSummaryMessage::new(&all_failed_tests)); // Print the overall summary only when testing multiple packages if packages_len > 1 { // Add newline to separate summary from previous output ui.print_blank_line(); ui.println(&overall_summary); } match partitioning_config { PartitionConfig::Disabled => (), PartitionConfig::Enabled { partition, partition_map, } => { ui.print_blank_line(); let included = partition_map.included_tests_count(partition.index()); let total = partition_map.total_tests_count(); ui.println(&PartitionFinishedMessage::new(partition, included, total)); } } if args.exact { unset_forge_test_filter(); } Ok(WorkspaceExecutionSummary { all_tests }) } fn calculate_total_filtered_count( total_filtered_count: Option, filtered: Option, ) -> Option { // Calculate filtered test counts across packages. When using `--exact` flag, // `result.filtered_count` is None, so `total_filtered_count` becomes None too. match (total_filtered_count, filtered) { (Some(total), Some(f)) => Some(total + f), _ => None, } } fn get_partitioning_config( args: &TestArgs, ui: &UI, packages: &[PackageMetadata], artifacts_dir_path: &camino::Utf8Path, ) -> Result { args.partition .map(|partition| { ui.print_blank_line(); ui.println(&PartitionStartedMessage::new(partition)); PartitionConfig::new(partition, packages, artifacts_dir_path) }) .transpose()? .map_or_else(|| Ok(PartitionConfig::Disabled), Ok) } #[tracing::instrument(skip_all, level = "debug")] fn extract_failed_tests( tests_summaries: &[TestTargetSummary], ) -> impl Iterator { tests_summaries .iter() .flat_map(|summary| &summary.test_case_summaries) .filter(|s| s.is_failed()) } fn set_forge_test_filter(test_filter: String) { // SAFETY: This runs in a single-threaded environment. unsafe { env::set_var(SNFORGE_TEST_FILTER, test_filter); }; } fn unset_forge_test_filter() { // SAFETY: This runs in a single-threaded environment. unsafe { env::remove_var(SNFORGE_TEST_FILTER); }; } ================================================ FILE: crates/forge/src/run_tests.rs ================================================ pub mod maat; pub mod messages; pub mod package; pub mod resolve_config; pub mod test_target; pub mod workspace; ================================================ FILE: crates/forge/src/scarb/config.rs ================================================ use anyhow::Result; use cheatnet::runtime_extensions::forge_config_extension::config::BlockId; use forge_runner::forge_config::ForgeTrackedResource; use serde::{Deserialize, Deserializer}; use std::{collections::HashSet, num::NonZeroU32}; use url::Url; pub const SCARB_MANIFEST_TEMPLATE_CONTENT: &str = r#" # Visit https://foundry-rs.github.io/starknet-foundry/appendix/scarb-toml.html for more information # [tool.snforge] # Define `snforge` tool section # exit_first = true # Stop tests execution immediately upon the first failure # fuzzer_runs = 1234 # Number of runs of the random fuzzer # fuzzer_seed = 1111 # Seed for the random fuzzer # [[tool.snforge.fork]] # Used for fork testing # name = "SOME_NAME" # Fork name # url = "http://your.rpc.url" # Url of the RPC provider # block_id.tag = "latest" # Block to fork from (block tag) # [[tool.snforge.fork]] # name = "SOME_SECOND_NAME" # url = "http://your.second.rpc.url" # block_id.number = "123" # Block to fork from (block number) # [[tool.snforge.fork]] # name = "SOME_THIRD_NAME" # url = "http://your.third.rpc.url" # block_id.hash = "0x123" # Block to fork from (block hash) # [profile.dev.cairo] # Configure Cairo compiler # unstable-add-statements-code-locations-debug-info = true # Should be used if you want to use coverage # unstable-add-statements-functions-debug-info = true # Should be used if you want to use coverage/profiler # inlining-strategy = "avoid" # Should be used if you want to use coverage # [features] # Used for conditional compilation # enable_for_tests = [] # Feature name and list of other features that should be enabled with it "#; #[expect(clippy::struct_excessive_bools)] #[derive(Debug, PartialEq, Default, Deserialize)] pub struct ForgeConfigFromScarb { /// Should runner exit after first failed test #[serde(default)] pub exit_first: bool, /// How many runs should fuzzer execute pub fuzzer_runs: Option, /// Seed to be used by fuzzer pub fuzzer_seed: Option, /// Display more detailed info about used resources #[serde(default)] pub detailed_resources: bool, /// Save execution traces of all test which have passed and are not fuzz tests #[serde(default)] pub save_trace_data: bool, /// Build profiles of all tests which have passed and are not fuzz tests #[serde(default)] pub build_profile: bool, /// Generate a coverage report for the executed tests which have passed and are not fuzz tests #[serde(default)] pub coverage: bool, /// Display a table of L2 gas breakdown for each contract and selector #[serde(default)] pub gas_report: bool, /// Fork configuration profiles #[serde(default, deserialize_with = "validate_forks")] pub fork: Vec, /// Limit of steps pub max_n_steps: Option, /// Set tracked resource #[serde(default)] pub tracked_resource: ForgeTrackedResource, } #[derive(Debug, PartialEq, Clone, Deserialize)] pub struct ForkTarget { pub name: String, pub url: Url, pub block_id: BlockId, } fn validate_forks<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { // deserialize to Vec let fork_targets = Vec::::deserialize(deserializer)?; let names: Vec<_> = fork_targets.iter().map(|fork| &fork.name).collect(); let removed_duplicated_names: HashSet<_> = names.iter().collect(); if names.len() != removed_duplicated_names.len() { return Err(serde::de::Error::custom("Some fork names are duplicated")); } Ok(fork_targets) } #[cfg(test)] mod tests { use super::*; use serde_json::json; use starknet_types_core::felt::Felt; use test_case::test_case; use url::Url; #[test] fn test_fork_target_new_valid_number() { let name = "TestFork"; let url = "http://example.com"; let block_id_type = "number"; let block_id_value = "123"; let json_str = json!({ "name": name, "url": url, "block_id": { block_id_type: block_id_value } }) .to_string(); let fork_target = serde_json::from_str::(&json_str).unwrap(); assert_eq!(fork_target.name, name); assert_eq!(fork_target.url, Url::parse(url).unwrap()); if let BlockId::BlockNumber(number) = fork_target.block_id { assert_eq!(number, 123); } else { panic!("Expected BlockId::BlockNumber"); } } #[test] fn test_fork_target_new_valid_hash() { let name = "TestFork"; let url = "http://example.com"; let json_str = json!({ "name": name, "url": url, "block_id": { "hash": "0x1" } }) .to_string(); let fork_target = serde_json::from_str::(&json_str).unwrap(); assert_eq!(fork_target.name, name); assert_eq!(fork_target.url, Url::parse(url).unwrap()); if let BlockId::BlockHash(hash) = fork_target.block_id { assert_eq!(hash, Felt::from_dec_str("1").unwrap()); } else { panic!("Expected BlockId::BlockHash"); } } #[test] fn test_fork_target_new_valid_tag() { let name = "TestFork"; let url = "http://example.com"; let json_str = json!({ "name": name, "url": url, "block_id": { "tag": "latest" } }) .to_string(); let fork_target = serde_json::from_str::(&json_str).unwrap(); assert_eq!(fork_target.name, name); assert_eq!(fork_target.url, Url::parse(url).unwrap()); if let BlockId::BlockTag = fork_target.block_id { // Expected variant } else { panic!("Expected BlockId::BlockTag"); } } #[test_case( &json!({ "name": "TestFork", "url": "invalid_url", "block_id": { "number": "123" } }), "relative URL without a base"; "Invalid url" )] #[test_case( &json!({ "name": "TestFork", "url": "http://example.com", "block_id": { "number": "invalid_number" } }), "invalid digit found in string"; "Invalid number" )] #[test_case( &json!({ "name": "TestFork", "url": "http://example.com", "block_id": { "hash": "invalid_hash" } }), "failed to create Felt from string"; "Invalid hash" )] fn test_fork_target_invalid_cases(input: &serde_json::Value, expected_error: &str) { let json_str = input.to_string(); let result = serde_json::from_str::(&json_str); assert!(result.is_err()); assert!(result.unwrap_err().to_string().contains(expected_error)); } } ================================================ FILE: crates/forge/src/scarb.rs ================================================ use crate::scarb::config::ForgeConfigFromScarb; use anyhow::{Context, Result, anyhow}; use configuration::Config; use configuration::core::Profile; use scarb_api::ScarbCommand; use scarb_metadata::{Metadata, PackageId}; use scarb_ui::args::{FeaturesSpec, PackagesFilter, ProfileSpec}; pub mod config; impl Config for ForgeConfigFromScarb { fn tool_name() -> &'static str { "snforge" } fn from_raw(config: serde_json::Value) -> Result where Self: Sized, { serde_json::from_value(config.clone()).context("Failed to parse snforge config") } } /// Loads config for a specific package from the `Scarb.toml` file /// # Arguments /// * `metadata` - Scarb metadata object /// * `package` - Id of the Scarb package pub fn load_package_config( metadata: &Metadata, package: &PackageId, ) -> Result { let maybe_raw_metadata = metadata .get_package(package) .ok_or_else(|| anyhow!("Failed to find metadata for package = {package}"))? .tool_metadata(T::tool_name()) .cloned(); match maybe_raw_metadata { Some(raw_metadata) => configuration::core::load_config(raw_metadata, Profile::None), None => Ok(T::default()), } } #[tracing::instrument(skip_all, level = "debug")] pub fn build_artifacts_with_scarb( filter: PackagesFilter, features: FeaturesSpec, profile: ProfileSpec, no_optimization: bool, ) -> Result<()> { if no_optimization { build_contracts_with_scarb(filter.clone(), features.clone(), profile.clone())?; } build_test_artifacts_with_scarb(filter, features, profile)?; Ok(()) } fn build_contracts_with_scarb( filter: PackagesFilter, features: FeaturesSpec, profile: ProfileSpec, ) -> Result<()> { ScarbCommand::new_with_stdio() .arg("build") .packages_filter(filter) .features(features) .profile(profile) .run() .context("Failed to build contracts with Scarb")?; Ok(()) } fn build_test_artifacts_with_scarb( filter: PackagesFilter, features: FeaturesSpec, profile: ProfileSpec, ) -> Result<()> { ScarbCommand::new_with_stdio() .arg("build") .arg("--test") .packages_filter(filter) .features(features) .profile(profile) .run() .context("Failed to build test artifacts with Scarb")?; Ok(()) } ================================================ FILE: crates/forge/src/shared_cache.rs ================================================ use anyhow::Result; use camino::Utf8PathBuf; use forge_runner::test_case_summary::AnyTestCaseSummary; use std::fs::File; use std::io::{BufRead, BufReader, BufWriter, ErrorKind, Write}; #[derive(Debug, PartialEq, Default, Clone)] pub struct FailedTestsCache { cache_file: Utf8PathBuf, } const FILE_WITH_PREV_TESTS_FAILED: &str = ".prev_tests_failed"; impl FailedTestsCache { #[must_use] pub fn new(cache_dir: &Utf8PathBuf) -> Self { Self { cache_file: cache_dir.join(FILE_WITH_PREV_TESTS_FAILED), } } pub fn load(&self) -> Result> { let file = match File::open(&self.cache_file) { Ok(file) => file, Err(err) if err.kind() == ErrorKind::NotFound => return Ok(vec![]), Err(err) => Err(err)?, }; let buf: BufReader = BufReader::new(file); let tests = buf.lines().collect::, _>>()?; Ok(tests) } pub fn save_failed_tests(&self, all_failed_tests: &[&AnyTestCaseSummary]) -> Result<()> { std::fs::create_dir_all(self.cache_file.parent().unwrap())?; let file = File::create(&self.cache_file)?; let mut file = BufWriter::new(file); for line in all_failed_tests { let name = line.name().unwrap(); writeln!(file, "{name}")?; } Ok(()) } } ================================================ FILE: crates/forge/src/test_filter.rs ================================================ use crate::shared_cache::FailedTestsCache; use anyhow::Result; use forge_runner::filtering::{ExcludeReason, FilterResult, TestCaseFilter, TestCaseIsIgnored}; use forge_runner::package_tests::TestCase; use forge_runner::package_tests::with_config_resolved::{ TestCaseWithResolvedConfig, sanitize_test_case_name, }; use forge_runner::partition::PartitionConfig; #[derive(Debug, PartialEq)] // Specifies what tests should be included pub struct TestsFilter { // based on name pub(crate) name_filter: NameFilter, // based on `#[ignore]` attribute ignored_filter: IgnoredFilter, // based on `--rerun_failed` flag last_failed_filter: bool, // based on `--skip` flag skip_filter: Vec, failed_tests_cache: FailedTestsCache, pub(crate) partitioning_config: PartitionConfig, } #[derive(Debug, PartialEq)] pub(crate) enum NameFilter { All, Match(String), ExactMatch(String), } #[derive(Debug, PartialEq)] pub enum IgnoredFilter { IncludeAll, IgnoredOnly, ExcludeIgnored, } impl TestsFilter { #[must_use] #[expect(clippy::fn_params_excessive_bools)] #[expect(clippy::too_many_arguments)] pub fn from_flags( test_name_filter: Option, exact_match: bool, skip: Vec, only_ignored: bool, include_ignored: bool, rerun_failed: bool, failed_tests_cache: FailedTestsCache, partitioning_config: PartitionConfig, ) -> Self { assert!( !(only_ignored && include_ignored), "Arguments only_ignored and include_ignored cannot be both true" ); let ignored_filter = if include_ignored { IgnoredFilter::IncludeAll } else if only_ignored { IgnoredFilter::IgnoredOnly } else { IgnoredFilter::ExcludeIgnored }; let name_filter = if exact_match { NameFilter::ExactMatch( test_name_filter .expect("Argument test_name_filter cannot be None with exact_match"), ) } else if let Some(name) = test_name_filter { NameFilter::Match(name) } else { NameFilter::All }; Self { name_filter, ignored_filter, last_failed_filter: rerun_failed, skip_filter: skip, failed_tests_cache, partitioning_config, } } fn is_in_partition(&self, sanitized_name: &str) -> bool { match &self.partitioning_config { PartitionConfig::Disabled => true, PartitionConfig::Enabled { partition, partition_map, } => { let idx = partition_map .get_assigned_index(sanitized_name) .expect("Partition map must contain all test cases"); idx == partition.index() } } } pub(crate) fn filter_tests( &self, test_cases: &mut Vec, ) -> Result<()> { match &self.name_filter { NameFilter::All => {} NameFilter::Match(filter) => { test_cases.retain(|tc| tc.name.contains(filter)); } NameFilter::ExactMatch(name) => { test_cases.retain(|tc| tc.name == *name); } } if self.last_failed_filter { match self.failed_tests_cache.load()?.as_slice() { [] => {} result => { test_cases.retain(|tc| result.iter().any(|name| name == &tc.name)); } } } match self.ignored_filter { // if ExcludeIgnored (default) we filter ignored tests later and display them as ignored IgnoredFilter::IncludeAll | IgnoredFilter::ExcludeIgnored => {} IgnoredFilter::IgnoredOnly => { test_cases.retain(|tc| tc.config.ignored); } } if !self.skip_filter.is_empty() { test_cases.retain(|tc| !self.skip_filter.iter().any(|s| tc.name.contains(s))); } Ok(()) } } impl TestCaseFilter for TestsFilter { fn filter(&self, test_case: &TestCase) -> FilterResult where T: TestCaseIsIgnored, { // Order of filter checks matters, because we do not want to display a test as ignored if // it was excluded due to partitioning. let sanitized_test_case_name = sanitize_test_case_name(&test_case.name); if !self.is_in_partition(&sanitized_test_case_name) { return FilterResult::Excluded(ExcludeReason::ExcludedFromPartition); } let case_ignored = test_case.config.is_ignored(); match self.ignored_filter { IgnoredFilter::IncludeAll => {} IgnoredFilter::IgnoredOnly => { if !case_ignored { return FilterResult::Excluded(ExcludeReason::Ignored); } } IgnoredFilter::ExcludeIgnored => { if case_ignored { return FilterResult::Excluded(ExcludeReason::Ignored); } } } FilterResult::Included } } #[cfg(test)] mod tests { use crate::shared_cache::FailedTestsCache; use crate::test_filter::TestsFilter; use cairo_lang_sierra::program::Program; use cairo_lang_sierra::program::ProgramArtifact; use forge_runner::expected_result::ExpectedTestResult; use forge_runner::package_tests::with_config_resolved::{ TestCaseResolvedConfig, TestCaseWithResolvedConfig, TestTargetWithResolvedConfig, }; use forge_runner::package_tests::{TestDetails, TestTargetLocation}; use forge_runner::partition::PartitionConfig; use std::sync::Arc; use universal_sierra_compiler_api::compile_raw_sierra; fn program_for_testing() -> ProgramArtifact { ProgramArtifact { program: Program { type_declarations: vec![], libfunc_declarations: vec![], statements: vec![], funcs: vec![], }, debug_info: None, } } #[test] #[should_panic(expected = "Arguments only_ignored and include_ignored cannot be both true")] fn from_flags_only_ignored_and_include_ignored_both_true() { let _ = TestsFilter::from_flags( None, false, Vec::new(), true, true, false, FailedTestsCache::default(), PartitionConfig::default(), ); } #[test] #[should_panic(expected = "Argument test_name_filter cannot be None with exact_match")] fn from_flags_exact_match_true_without_test_filter_name() { let _ = TestsFilter::from_flags( None, true, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); } #[test] #[expect(clippy::too_many_lines)] fn filtering_tests() { let mocked_tests = TestTargetWithResolvedConfig { sierra_program: program_for_testing(), sierra_program_path: Arc::default(), casm_program: Arc::new( compile_raw_sierra(&serde_json::to_value(&program_for_testing().program).unwrap()) .unwrap(), ), test_cases: vec![ TestCaseWithResolvedConfig { name: "crate1::do_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: false, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "crate2::run_other_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: true, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "outer::crate2::execute_next_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: true, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: false, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, ], tests_location: TestTargetLocation::Lib, }; let tests_filter = TestsFilter::from_flags( Some("do".to_string()), false, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let mut filtered = mocked_tests.clone(); tests_filter.filter_tests(&mut filtered.test_cases).unwrap(); assert_eq!( filtered.test_cases, vec![TestCaseWithResolvedConfig { name: "crate1::do_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: false, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, },] ); let tests_filter = TestsFilter::from_flags( Some("te2::run".to_string()), false, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let mut filtered = mocked_tests.clone(); tests_filter.filter_tests(&mut filtered.test_cases).unwrap(); assert_eq!( filtered.test_cases, vec![TestCaseWithResolvedConfig { name: "crate2::run_other_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: true, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, },] ); let tests_filter = TestsFilter::from_flags( Some("thing".to_string()), false, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let mut filtered = mocked_tests.clone(); tests_filter.filter_tests(&mut filtered.test_cases).unwrap(); assert_eq!( filtered.test_cases, vec![ TestCaseWithResolvedConfig { name: "crate1::do_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: false, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "crate2::run_other_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: true, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "outer::crate2::execute_next_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: true, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: false, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, ] ); let tests_filter = TestsFilter::from_flags( Some("nonexistent".to_string()), false, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let mut filtered = mocked_tests.clone(); tests_filter.filter_tests(&mut filtered.test_cases).unwrap(); assert_eq!(filtered.test_cases, vec![]); let tests_filter = TestsFilter::from_flags( Some(String::new()), false, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let mut filtered = mocked_tests.clone(); tests_filter.filter_tests(&mut filtered.test_cases).unwrap(); assert_eq!( filtered.test_cases, vec![ TestCaseWithResolvedConfig { name: "crate1::do_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: false, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "crate2::run_other_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: true, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "outer::crate2::execute_next_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: true, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: false, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, ] ); } #[test] fn filtering_with_no_tests() { let mocked_tests = TestTargetWithResolvedConfig { sierra_program: program_for_testing(), sierra_program_path: Arc::default(), casm_program: Arc::new( compile_raw_sierra(&serde_json::to_value(&program_for_testing().program).unwrap()) .unwrap(), ), test_cases: vec![], tests_location: TestTargetLocation::Lib, }; let tests_filter = TestsFilter::from_flags( Some(String::new()), false, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let mut filtered = mocked_tests.clone(); tests_filter.filter_tests(&mut filtered.test_cases).unwrap(); assert_eq!(filtered.test_cases, vec![]); let tests_filter = TestsFilter::from_flags( Some("thing".to_string()), false, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let mut filtered = mocked_tests.clone(); tests_filter.filter_tests(&mut filtered.test_cases).unwrap(); assert_eq!(filtered.test_cases, vec![]); } #[test] #[expect(clippy::too_many_lines)] fn filtering_with_exact_match() { let mocked_tests = TestTargetWithResolvedConfig { sierra_program: program_for_testing(), sierra_program_path: Arc::default(), casm_program: Arc::new( compile_raw_sierra(&serde_json::to_value(&program_for_testing().program).unwrap()) .unwrap(), ), test_cases: vec![ TestCaseWithResolvedConfig { name: "crate1::do_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: false, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "crate2::run_other_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: true, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "outer::crate3::run_other_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: true, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "do_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: false, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, ], tests_location: TestTargetLocation::Tests, }; let tests_filter = TestsFilter::from_flags( Some(String::new()), true, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let mut filtered = mocked_tests.clone(); tests_filter.filter_tests(&mut filtered.test_cases).unwrap(); assert_eq!(filtered.test_cases, vec![]); let tests_filter = TestsFilter::from_flags( Some("thing".to_string()), true, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let mut filtered = mocked_tests.clone(); tests_filter.filter_tests(&mut filtered.test_cases).unwrap(); assert_eq!(filtered.test_cases, vec![]); let tests_filter = TestsFilter::from_flags( Some("do_thing".to_string()), true, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let mut filtered = mocked_tests.clone(); tests_filter.filter_tests(&mut filtered.test_cases).unwrap(); assert_eq!( filtered.test_cases, vec![TestCaseWithResolvedConfig { name: "do_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: false, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, },] ); let tests_filter = TestsFilter::from_flags( Some("crate1::do_thing".to_string()), true, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let mut filtered = mocked_tests.clone(); tests_filter.filter_tests(&mut filtered.test_cases).unwrap(); assert_eq!( filtered.test_cases, vec![TestCaseWithResolvedConfig { name: "crate1::do_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: false, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, },] ); let tests_filter = TestsFilter::from_flags( Some("crate3::run_other_thing".to_string()), true, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let mut filtered = mocked_tests.clone(); tests_filter.filter_tests(&mut filtered.test_cases).unwrap(); assert_eq!(filtered.test_cases, vec![]); let tests_filter = TestsFilter::from_flags( Some("outer::crate3::run_other_thing".to_string()), true, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let mut filtered = mocked_tests.clone(); tests_filter.filter_tests(&mut filtered.test_cases).unwrap(); assert_eq!( filtered.test_cases, vec![TestCaseWithResolvedConfig { name: "outer::crate3::run_other_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: true, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, },] ); } #[test] fn filtering_with_only_ignored() { let mocked_tests = TestTargetWithResolvedConfig { sierra_program: program_for_testing(), sierra_program_path: Arc::default(), casm_program: Arc::new( compile_raw_sierra(&serde_json::to_value(&program_for_testing().program).unwrap()) .unwrap(), ), test_cases: vec![ TestCaseWithResolvedConfig { name: "crate1::do_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: false, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "crate2::run_other_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: true, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "outer::crate3::run_other_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: true, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "do_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: false, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, ], tests_location: TestTargetLocation::Tests, }; let tests_filter = TestsFilter::from_flags( None, false, Vec::new(), true, false, false, FailedTestsCache::default(), PartitionConfig::default(), ); let mut filtered = mocked_tests; tests_filter.filter_tests(&mut filtered.test_cases).unwrap(); assert_eq!( filtered.test_cases, vec![ TestCaseWithResolvedConfig { name: "crate2::run_other_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: true, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "outer::crate3::run_other_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: true, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, ] ); } #[test] #[expect(clippy::too_many_lines)] fn filtering_with_include_ignored() { let mocked_tests = TestTargetWithResolvedConfig { sierra_program: program_for_testing(), sierra_program_path: Arc::default(), casm_program: Arc::new( compile_raw_sierra(&serde_json::to_value(&program_for_testing().program).unwrap()) .unwrap(), ), test_cases: vec![ TestCaseWithResolvedConfig { name: "crate1::do_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: false, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "crate2::run_other_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: true, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "outer::crate3::run_other_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: true, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "do_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: false, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, ], tests_location: TestTargetLocation::Tests, }; let tests_filter = TestsFilter::from_flags( None, false, Vec::new(), false, true, false, FailedTestsCache::default(), PartitionConfig::default(), ); let mut filtered = mocked_tests; tests_filter.filter_tests(&mut filtered.test_cases).unwrap(); assert_eq!( filtered.test_cases, vec![ TestCaseWithResolvedConfig { name: "crate1::do_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: false, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "crate2::run_other_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: true, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "outer::crate3::run_other_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: true, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, TestCaseWithResolvedConfig { name: "do_thing".to_string(), test_details: TestDetails::default(), config: TestCaseResolvedConfig { available_gas: None, ignored: false, expected_result: ExpectedTestResult::Success, fork_config: None, fuzzer_config: None, disable_predeployed_contracts: false, }, }, ] ); } } ================================================ FILE: crates/forge/src/warn.rs ================================================ use crate::MINIMAL_SNFORGE_STD_VERSION; use anyhow::{Result, anyhow}; use forge_runner::package_tests::with_config_resolved::TestTargetWithResolvedConfig; use foundry_ui::UI; use foundry_ui::components::warning::WarningMessage; use scarb_api::package_matches_version_requirement; use scarb_metadata::Metadata; use semver::{Comparator, Op, Version, VersionReq}; use shared::rpc::create_rpc_client; use shared::verify_and_warn_if_incompatible_rpc_version; use std::collections::HashSet; use std::env; use std::sync::Arc; use url::Url; pub(crate) async fn warn_if_incompatible_rpc_version( test_targets: &[TestTargetWithResolvedConfig], ui: Arc, ) -> Result<()> { let mut urls = HashSet::::new(); // collect urls for test_target in test_targets { for fork_config in test_target .test_cases .iter() .filter_map(|tc| tc.config.fork_config.as_ref()) { urls.insert(fork_config.url.clone()); } } let mut handles = Vec::with_capacity(urls.len()); for url in urls { let ui = ui.clone(); handles.push(tokio::spawn(async move { let client = create_rpc_client(&url)?; verify_and_warn_if_incompatible_rpc_version(&client, &url, &ui).await })); } for handle in handles { handle.await??; } Ok(()) } fn snforge_std_recommended_version() -> VersionReq { let version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap(); let comparator = Comparator { op: Op::Caret, major: version.major, minor: Some(version.minor), patch: Some(version.patch), pre: version.pre, }; VersionReq { comparators: vec![comparator], } } pub fn error_if_snforge_std_not_compatible(scarb_metadata: &Metadata) -> Result<()> { let snforge_std_version_requirement_comparator = Comparator { op: Op::GreaterEq, major: MINIMAL_SNFORGE_STD_VERSION.major, minor: Some(MINIMAL_SNFORGE_STD_VERSION.minor), patch: Some(MINIMAL_SNFORGE_STD_VERSION.patch), pre: MINIMAL_SNFORGE_STD_VERSION.pre, }; let snforge_std_version_requirement = VersionReq { comparators: vec![snforge_std_version_requirement_comparator], }; if !package_matches_version_requirement( scarb_metadata, "snforge_std", &snforge_std_version_requirement, )? { return Err(anyhow!( "Package snforge_std version does not meet the minimum required version {snforge_std_version_requirement}. Please upgrade snforge_std in Scarb.toml" )); } Ok(()) } pub fn warn_if_snforge_std_does_not_match_package_version( scarb_metadata: &Metadata, ui: &UI, ) -> Result<()> { let snforge_std_version_requirement = snforge_std_recommended_version(); if !package_matches_version_requirement( scarb_metadata, "snforge_std", &snforge_std_version_requirement, )? { ui.println(&WarningMessage::new(&format!( "Package snforge_std version does not meet the recommended version requirement {snforge_std_version_requirement}, it might result in unexpected behaviour" ))); } Ok(()) } ================================================ FILE: crates/forge/tests/data/backtrace_panic/Scarb.toml ================================================ [package] name = "backtrace_panic" version = "0.1.0" edition = "2024_07" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] starknet = "2.8.5" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] sierra = true [scripts] test = "snforge test" [profile.dev.cairo] unstable-add-statements-functions-debug-info = true unstable-add-statements-code-locations-debug-info = true panic-backtrace = true ================================================ FILE: crates/forge/tests/data/backtrace_panic/src/lib.cairo ================================================ #[starknet::interface] pub trait IOuterContract { fn outer(self: @TState, contract_address: starknet::ContractAddress); } #[starknet::contract] pub mod OuterContract { use super::{IInnerContractDispatcher, IInnerContractDispatcherTrait}; #[storage] pub struct Storage {} #[abi(embed_v0)] impl OuterContract of super::IOuterContract { fn outer(self: @ContractState, contract_address: starknet::ContractAddress) { let dispatcher = IInnerContractDispatcher { contract_address }; dispatcher.inner(); } } } #[starknet::interface] pub trait IInnerContract { fn inner(self: @TState); } #[starknet::contract] pub mod InnerContract { #[storage] pub struct Storage {} #[abi(embed_v0)] impl InnerContract of super::IInnerContract { fn inner(self: @ContractState) { inner_call() } } fn inner_call() { assert(1 != 1, 'Assert failed'); } } #[cfg(test)] mod Test { use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; use super::{IOuterContractDispatcher, IOuterContractDispatcherTrait}; #[test] fn test_contract_panics() { let contract_inner = declare("InnerContract").unwrap().contract_class(); let (contract_address_inner, _) = contract_inner.deploy(@array![]).unwrap(); let contract_outer = declare("OuterContract").unwrap().contract_class(); let (contract_address_outer, _) = contract_outer.deploy(@array![]).unwrap(); let dispatcher = IOuterContractDispatcher { contract_address: contract_address_outer }; dispatcher.outer(contract_address_inner); } #[ignore] #[should_panic] #[test] fn test_contract_panics_with_should_panic() { let contract_inner = declare("InnerContract").unwrap().contract_class(); let (contract_address_inner, _) = contract_inner.deploy(@array![]).unwrap(); let contract_outer = declare("OuterContract").unwrap().contract_class(); let (contract_address_outer, _) = contract_outer.deploy(@array![]).unwrap(); let dispatcher = IOuterContractDispatcher { contract_address: contract_address_outer }; dispatcher.outer(contract_address_inner); } #[test] #[fork(url: "{{ NODE_RPC_URL }}", block_number: 806134)] fn test_fork_contract_panics() { // NOTE: This is not exactly the same as InnerContract here, but will return the same error // Class hash needs to be different otherwise it would be found locally in the state let contract_address_inner = 0x066eda239a01152a912129fe6b5bf309c9b21e3f583df4e5b7ee8ede1fad820a .try_into() .unwrap(); let contract_outer = declare("OuterContract").unwrap().contract_class(); let (contract_address_outer, _) = contract_outer.deploy(@array![]).unwrap(); let dispatcher = IOuterContractDispatcher { contract_address: contract_address_outer }; dispatcher.outer(contract_address_inner); } } ================================================ FILE: crates/forge/tests/data/backtrace_vm_error/Scarb.toml ================================================ [package] name = "backtrace_vm_error" version = "0.1.0" edition = "2024_07" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] starknet = "2.8.5" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] sierra = true [scripts] test = "snforge test" [profile.dev.cairo] unstable-add-statements-functions-debug-info = true unstable-add-statements-code-locations-debug-info = true panic-backtrace = true ================================================ FILE: crates/forge/tests/data/backtrace_vm_error/src/lib.cairo ================================================ #[starknet::interface] pub trait IOuterContract { fn outer(self: @TState, contract_address: starknet::ContractAddress); } #[starknet::contract] pub mod OuterContract { use super::{IInnerContractDispatcher, IInnerContractDispatcherTrait}; #[storage] pub struct Storage {} #[abi(embed_v0)] impl OuterContract of super::IOuterContract { fn outer(self: @ContractState, contract_address: starknet::ContractAddress) { let dispatcher = IInnerContractDispatcher { contract_address }; dispatcher.inner(); } } } #[starknet::interface] pub trait IInnerContract { fn inner(self: @TState); } #[starknet::contract] pub mod InnerContract { use starknet::syscalls::call_contract_syscall; use starknet::{ContractAddress, SyscallResultTrait}; #[storage] pub struct Storage {} #[abi(embed_v0)] impl InnerContract of super::IInnerContract { fn inner(self: @ContractState) { inner_call() } } fn inner_call() { let address: ContractAddress = 0x123.try_into().unwrap(); let selector = selector!("dummy"); let calldata = array![].span(); // This fails immediately due to nonexistent address call_contract_syscall(address, selector, calldata).unwrap_syscall(); } } #[cfg(test)] mod Test { use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; use super::{IOuterContractDispatcher, IOuterContractDispatcherTrait}; #[test] fn test_unwrapped_call_contract_syscall() { let contract_inner = declare("InnerContract").unwrap().contract_class(); let (contract_address_inner, _) = contract_inner.deploy(@array![]).unwrap(); let contract_outer = declare("OuterContract").unwrap().contract_class(); let (contract_address_outer, _) = contract_outer.deploy(@array![]).unwrap(); let dispatcher = IOuterContractDispatcher { contract_address: contract_address_outer }; dispatcher.outer(contract_address_inner); } #[test] #[fork(url: "{{ NODE_RPC_URL }}", block_number: 806134)] fn test_fork_unwrapped_call_contract_syscall() { // NOTE: This is not exactly the same as InnerContract here, but will return the same error // Class hash needs to be different otherwise it would be found locally in the state let contract_address_inner = 0x01506c04bdb56f2cc9ea1f67fcb086c99df7cec2598ce90e56f1d36fffda1cf4 .try_into() .unwrap(); let contract_outer = declare("OuterContract").unwrap().contract_class(); let (contract_address_outer, _) = contract_outer.deploy(@array![]).unwrap(); let dispatcher = IOuterContractDispatcher { contract_address: contract_address_outer }; dispatcher.outer(contract_address_inner); } } ================================================ FILE: crates/forge/tests/data/collection_with_lib/Scarb.toml ================================================ [package] name = "collection_with_lib" version = "0.1.0" edition = "2024_07" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] # foo = { path = "vendor/foo" } [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } ================================================ FILE: crates/forge/tests/data/collection_with_lib/fob.cairo ================================================ // Won't be found by the collector use collection_with_lib::fob::fob_impl::fob_fn; #[test] fn test_fob() { assert(fob_fn(0, 1, 10) == 55, fob_fn(0, 1, 10)); } ================================================ FILE: crates/forge/tests/data/collection_with_lib/src/fab/fab_impl.cairo ================================================ use collection_with_lib::fib::fib_fn; use super::fn_from_above; pub fn fab_fn(a: felt252, b: felt252, n: felt252) -> felt252 { match n { 0 => a, _ => fab_fn(b, a + b, n - 1), } } #[cfg(test)] mod tests { use super::{fab_fn, fib_fn, fn_from_above}; #[test] fn test_fab() { assert(fab_fn(0, 1, 10) == 55, fab_fn(0, 1, 10)); } #[test] fn test_how_does_this_work() { assert(fib_fn(0, 1, 10) == 55, fib_fn(0, 1, 10)); } #[test] fn test_super() { let one: felt252 = 1; assert(fn_from_above() == one, 1); } } ================================================ FILE: crates/forge/tests/data/collection_with_lib/src/fab/fibfabfob.cairo ================================================ // Won't be found by the collector use collection_with_lib::fab::fab_impl::fab_fn; use collection_with_lib::fib::fib_fn; use collection_with_lib::fob::fob_impl::fob_fn; #[cfg(test)] mod tests { use super::{fab_fn, fib_fn, fob_fb}; #[test] fn test_fib() { assert(fib_fn(0, 1, 10) == 55, fib_fn(0, 1, 10)); } #[test] fn test_fob() { assert(fob_fn(0, 1, 10) == 55, fob_fn(0, 1, 10)); } #[test] fn test_fab() { assert(fab_fn(0, 1, 10) == 55, fab_fn(0, 1, 10)); } } ================================================ FILE: crates/forge/tests/data/collection_with_lib/src/fab.cairo ================================================ pub mod fab_impl; pub fn fn_from_above() -> felt252 { 1 } #[cfg(test)] mod tests { #[test] fn test_simple() { assert(1 == 1, 1); } } ================================================ FILE: crates/forge/tests/data/collection_with_lib/src/fib.cairo ================================================ use collection_with_lib::fob::fob_impl::fob_fn; use super::fab::fab_impl::fab_fn; pub fn fib_fn(a: felt252, b: felt252, n: felt252) -> felt252 { match n { 0 => a, _ => fib_fn(b, a + b, n - 1), } } #[cfg(test)] mod tests { use super::{fab_fn, fib_fn, fob_fn}; #[test] fn test_fib() { assert(fib_fn(0, 1, 10) == 55, fib_fn(0, 1, 10)); } #[test] fn test_fob_in_fib() { assert(fob_fn(0, 1, 10) == 55, fob_fn(0, 1, 10)); } #[test] fn test_fab_in_fib() { assert(fab_fn(0, 1, 10) == 55, fab_fn(0, 1, 10)); } } ================================================ FILE: crates/forge/tests/data/collection_with_lib/src/fob/fibfabfob.cairo ================================================ // Won't be found by the collector use collection_with_lib::fab::fab_impl::fab_fn; use collection_with_lib::fib::fib_fn; use collection_with_lib::fob::fob_impl::fob_fn; #[cfg(test)] mod tests { use super::{fab_fn, fib_fn, fob_fn}; #[test] fn test_fib() { assert(fib_fn(0, 1, 10) == 55, fib_fn(0, 1, 10)); } #[test] fn test_fob() { assert(fob_fn(0, 1, 10) == 55, fob_fn(0, 1, 10)); } #[test] fn test_fab() { assert(fab_fn(0, 1, 10) == 55, fab_fn(0, 1, 10)); } } ================================================ FILE: crates/forge/tests/data/collection_with_lib/src/fob/fob_impl.cairo ================================================ pub fn fob_fn(a: felt252, b: felt252, n: felt252) -> felt252 { match n { 0 => a, _ => fob_fn(b, a + b, n - 1), } } #[cfg(test)] mod tests { use super::fob_fn; #[test] fn test_fob() { assert(fob_fn(0, 1, 10) == 55, fob_fn(0, 1, 10)); } } ================================================ FILE: crates/forge/tests/data/collection_with_lib/src/fob.cairo ================================================ pub mod fob_impl; #[cfg(test)] mod tests { #[test] fn test_simple() { assert(1 == 1, 1); } } ================================================ FILE: crates/forge/tests/data/collection_with_lib/src/lib.cairo ================================================ pub mod fab; pub mod fib; pub mod fob; use fib::fib_fn; use fob::fob_impl::fob_fn; #[cfg(test)] mod tests { use super::{fib_fn, fob_fn}; #[test] fn test_simple() { assert(1 == 1, 1); } #[test] fn test_fob_in_lib() { assert(fob_fn(0, 1, 10) == 55, fob_fn(0, 1, 10)); } #[test] fn test_fib_in_lib() { assert(fib_fn(0, 1, 10) == 55, fib_fn(0, 1, 10)); } } ================================================ FILE: crates/forge/tests/data/collection_with_lib/tests/fab/fab_mod.cairo ================================================ use super::fab_fn; #[test] fn test_fab() { assert(fab_fn(0, 1, 10) == 55, fab_fn(0, 1, 10)); } ================================================ FILE: crates/forge/tests/data/collection_with_lib/tests/fab.cairo ================================================ use collection_with_lib::fab::fab_impl::fab_fn; mod fab_mod; #[test] fn test_fab() { assert(fab_fn(0, 1, 10) == 55, fab_fn(0, 1, 10)); } ================================================ FILE: crates/forge/tests/data/collection_with_lib/tests/fibfabfob.cairo ================================================ use collection_with_lib::fab::fab_impl::fab_fn; use collection_with_lib::fib::fib_fn; use collection_with_lib::fob::fob_impl::fob_fn; #[test] fn test_fib() { assert(fib_fn(0, 1, 10) == 55, fib_fn(0, 1, 10)); } #[test] fn test_fob() { assert(fob_fn(0, 1, 10) == 55, fob_fn(0, 1, 10)); } #[test] fn test_fab() { assert(fab_fn(0, 1, 10) == 55, fab_fn(0, 1, 10)); } ================================================ FILE: crates/forge/tests/data/collection_with_lib/tests/lib.cairo ================================================ pub mod fab; pub mod fibfabfob; ================================================ FILE: crates/forge/tests/data/collection_with_lib/tests/not_found/not_found.cairo ================================================ // Won't be found by the collector use collection_with_lib::fib::fib_fn; #[test] fn test_fib() { assert(fib_fn(0, 1, 10) == 55, fib_fn(0, 1, 10)); } ================================================ FILE: crates/forge/tests/data/collection_with_lib/tests/not_found.cairo ================================================ // Won't be found by the collector use super::fibfabfob::fab_fn; mod fab_mod; #[test] fn test_fab() { assert(fab_fn(0, 1, 10) == 55, fab_fn(0, 1, 10)); } ================================================ FILE: crates/forge/tests/data/collection_without_lib/Scarb.toml ================================================ [package] name = "collection_without_lib" version = "0.1.0" edition = "2024_07" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] # foo = { path = "vendor/foo" } [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } ================================================ FILE: crates/forge/tests/data/collection_without_lib/fob.cairo ================================================ // Won't be found by the collector use collection_without_lib::fob::fob_impl::fob_fn; #[test] fn test_fob() { assert(fob_fn(0, 1, 10) == 55, fob_fn(0, 1, 10)); } ================================================ FILE: crates/forge/tests/data/collection_without_lib/src/fab/fab_impl.cairo ================================================ use collection_without_lib::fib::fib_fn; use super::fn_from_above; pub fn fab_fn(a: felt252, b: felt252, n: felt252) -> felt252 { match n { 0 => a, _ => fab_fn(b, a + b, n - 1), } } #[cfg(test)] mod tests { use super::{fab_fn, fib_fn, fn_from_above}; #[test] fn test_fab() { assert(fab_fn(0, 1, 10) == 55, fab_fn(0, 1, 10)); } #[test] fn test_how_does_this_work() { assert(fib_fn(0, 1, 10) == 55, fib_fn(0, 1, 10)); } #[test] fn test_super() { let one: felt252 = 1; assert(fn_from_above() == one, 1); } } ================================================ FILE: crates/forge/tests/data/collection_without_lib/src/fab/fibfabfob.cairo ================================================ // Won't be found by the collector use collection_without_lib::fab::fab_impl::fab_fn; use collection_without_lib::fib::fib_fn; use collection_without_lib::fob::fob_impl::fob_fn; #[cfg(test)] mod tests { use super::{fab_fn, fib_fn, fob_fb}; #[test] fn test_fib() { assert(fib_fn(0, 1, 10) == 55, fib_fn(0, 1, 10)); } #[test] fn test_fob() { assert(fob_fn(0, 1, 10) == 55, fob_fn(0, 1, 10)); } #[test] fn test_fab() { assert(fab_fn(0, 1, 10) == 55, fab_fn(0, 1, 10)); } } ================================================ FILE: crates/forge/tests/data/collection_without_lib/src/fab.cairo ================================================ pub mod fab_impl; fn fn_from_above() -> felt252 { 1 } #[cfg(test)] mod tests { #[test] fn test_simple() { assert(1 == 1, 1); } } ================================================ FILE: crates/forge/tests/data/collection_without_lib/src/fib.cairo ================================================ use collection_without_lib::fob::fob_impl::fob_fn; use super::fab::fab_impl::fab_fn; pub fn fib_fn(a: felt252, b: felt252, n: felt252) -> felt252 { match n { 0 => a, _ => fib_fn(b, a + b, n - 1), } } #[cfg(test)] mod tests { use super::{fab_fn, fib_fn, fob_fn}; #[test] fn test_fib() { assert(fib_fn(0, 1, 10) == 55, fib_fn(0, 1, 10)); } #[test] fn test_fob_in_fib() { assert(fob_fn(0, 1, 10) == 55, fob_fn(0, 1, 10)); } #[test] fn test_fab_in_fib() { assert(fab_fn(0, 1, 10) == 55, fab_fn(0, 1, 10)); } } ================================================ FILE: crates/forge/tests/data/collection_without_lib/src/fob/fibfabfob.cairo ================================================ // Won't be found by the collector use collection_without_lib::fab::fab_impl::fab_fn; use collection_without_lib::fib::fib_fn; use collection_without_lib::fob::fob_impl::fob_fn; #[cfg(test)] mod tests { use super::{fab_fn, fib_fn, fob_fn}; #[test] fn test_fib() { assert(fib_fn(0, 1, 10) == 55, fib_fn(0, 1, 10)); } #[test] fn test_fob() { assert(fob_fn(0, 1, 10) == 55, fob_fn(0, 1, 10)); } #[test] fn test_fab() { assert(fab_fn(0, 1, 10) == 55, fab_fn(0, 1, 10)); } } ================================================ FILE: crates/forge/tests/data/collection_without_lib/src/fob/fob_impl.cairo ================================================ pub fn fob_fn(a: felt252, b: felt252, n: felt252) -> felt252 { match n { 0 => a, _ => fob_fn(b, a + b, n - 1), } } #[cfg(test)] mod tests { use super::fob_fn; #[test] fn test_fob() { assert(fob_fn(0, 1, 10) == 55, fob_fn(0, 1, 10)); } } ================================================ FILE: crates/forge/tests/data/collection_without_lib/src/fob.cairo ================================================ pub mod fob_impl; #[cfg(test)] mod tests { #[test] fn test_simple() { assert(1 == 1, 1); } } ================================================ FILE: crates/forge/tests/data/collection_without_lib/src/lib.cairo ================================================ pub mod fab; pub mod fib; pub mod fob; use fib::fib_fn; use fob::fob_impl::fob_fn; #[cfg(test)] mod tests { use super::{fib_fn, fob_fn}; #[test] fn test_simple() { assert(1 == 1, 1); } #[test] fn test_fob_in_lib() { assert(fob_fn(0, 1, 10) == 55, fob_fn(0, 1, 10)); } #[test] fn test_fib_in_lib() { assert(fib_fn(0, 1, 10) == 55, fib_fn(0, 1, 10)); } } ================================================ FILE: crates/forge/tests/data/collection_without_lib/tests/fab/fab_mod.cairo ================================================ use super::fab_fn; #[test] fn test_fab() { assert(fab_fn(0, 1, 10) == 55, fab_fn(0, 1, 10)); } ================================================ FILE: crates/forge/tests/data/collection_without_lib/tests/fab.cairo ================================================ use collection_without_lib::fab::fab_impl::fab_fn; mod fab_mod; #[test] fn test_fab() { assert(fab_fn(0, 1, 10) == 55, fab_fn(0, 1, 10)); } ================================================ FILE: crates/forge/tests/data/collection_without_lib/tests/fibfabfob.cairo ================================================ use collection_without_lib::fab::fab_impl::fab_fn; use collection_without_lib::fib::fib_fn; use collection_without_lib::fob::fob_impl::fob_fn; #[test] fn test_fib() { assert(fib_fn(0, 1, 10) == 55, fib_fn(0, 1, 10)); } #[test] fn test_fob() { assert(fob_fn(0, 1, 10) == 55, fob_fn(0, 1, 10)); } #[test] fn test_fab() { assert(fab_fn(0, 1, 10) == 55, fab_fn(0, 1, 10)); } ================================================ FILE: crates/forge/tests/data/collection_without_lib/tests/not_found/not_found.cairo ================================================ // Won't be found by the collector use super::fab_fn; #[test] fn test_fab() { assert(fab_fn(0, 1, 10) == 55, fab_fn(0, 1, 10)); } ================================================ FILE: crates/forge/tests/data/component_macros/Scarb.toml ================================================ [package] name = "component_macros" version = "0.1.0" edition = "2024_07" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] starknet = "2.4.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] sierra = true ================================================ FILE: crates/forge/tests/data/component_macros/src/example.cairo ================================================ const MINTER_ROLE: felt252 = selector!("MINTER_ROLE"); #[starknet::interface] pub trait IMyContract { fn mint(ref self: TContractState); } #[starknet::contract] mod MyContract { use component_macros::oz_ac_component::{AccessControlComponent, SRC5Component}; use starknet::ContractAddress; use super::MINTER_ROLE; component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent); component!(path: SRC5Component, storage: src5, event: SRC5Event); // AccessControl #[abi(embed_v0)] impl AccessControlImpl = AccessControlComponent::AccessControlImpl; impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; // SRC5 #[abi(embed_v0)] impl SRC5Impl = SRC5Component::SRC5Impl; #[storage] struct Storage { #[substorage(v0)] accesscontrol: AccessControlComponent::Storage, #[substorage(v0)] src5: SRC5Component::Storage, } #[event] #[derive(Drop, starknet::Event)] enum Event { #[flat] AccessControlEvent: AccessControlComponent::Event, #[flat] SRC5Event: SRC5Component::Event, } #[constructor] fn constructor(ref self: ContractState, minter: ContractAddress) { // AccessControl-related initialization self.accesscontrol.initializer(); self.accesscontrol._grant_role(MINTER_ROLE, minter); } #[abi(embed_v0)] impl MyContractImpl of super::IMyContract { /// This function can only be called by a minter. fn mint(ref self: ContractState) { self.accesscontrol.assert_only_role(MINTER_ROLE); } } } ================================================ FILE: crates/forge/tests/data/component_macros/src/lib.cairo ================================================ pub mod example; pub mod oz_ac_component; ================================================ FILE: crates/forge/tests/data/component_macros/src/oz_ac_component.cairo ================================================ /// # SRC5 Component /// /// The SRC5 component allows contracts to expose the interfaces they implement. #[starknet::component] pub mod SRC5Component { use starknet::storage::{Map, StorageMapReadAccess, StoragePathEntry, StoragePointerWriteAccess}; const ISRC5_ID: felt252 = 0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055; #[starknet::interface] pub trait ISRC5 { fn supports_interface(self: @TState, interface_id: felt252) -> bool; } #[storage] pub struct Storage { SRC5_supported_interfaces: Map, } pub mod Errors { pub const INVALID_ID: felt252 = 'SRC5: invalid id'; } #[embeddable_as(SRC5Impl)] pub impl SRC5< TContractState, +HasComponent, > of ISRC5> { /// Returns whether the contract implements the given interface. fn supports_interface( self: @ComponentState, interface_id: felt252, ) -> bool { if interface_id == ISRC5_ID { return true; } self.SRC5_supported_interfaces.read(interface_id) } } #[generate_trait] pub impl InternalImpl< TContractState, +HasComponent, > of InternalTrait { /// Registers the given interface as supported by the contract. fn register_interface(ref self: ComponentState, interface_id: felt252) { self.SRC5_supported_interfaces.entry(interface_id).write(true); } /// Deregisters the given interface as supported by the contract. fn deregister_interface(ref self: ComponentState, interface_id: felt252) { assert(interface_id != ISRC5_ID, Errors::INVALID_ID); self.SRC5_supported_interfaces.entry(interface_id).write(true); } } } #[starknet::component] pub mod AccessControlComponent { use starknet::storage::{Map, StorageMapReadAccess, StoragePathEntry, StoragePointerWriteAccess}; use starknet::{ContractAddress, get_caller_address}; use super::SRC5Component; use super::SRC5Component::InternalTrait as SRC5InternalTrait; #[starknet::interface] pub trait IAccessControl { fn has_role(self: @TState, role: felt252, account: ContractAddress) -> bool; fn get_role_admin(self: @TState, role: felt252) -> felt252; fn grant_role(ref self: TState, role: felt252, account: ContractAddress); fn revoke_role(ref self: TState, role: felt252, account: ContractAddress); fn renounce_role(ref self: TState, role: felt252, account: ContractAddress); } #[storage] pub struct Storage { AccessControl_role_admin: Map, AccessControl_role_member: Map<(felt252, ContractAddress), bool>, } #[event] #[derive(Drop, starknet::Event)] pub enum Event { RoleGranted: RoleGranted, RoleRevoked: RoleRevoked, RoleAdminChanged: RoleAdminChanged, } /// Emitted when `account` is granted `role`. /// /// `sender` is the account that originated the contract call, an admin role /// bearer (except if `_grant_role` is called during initialization from the constructor). #[derive(Drop, starknet::Event)] struct RoleGranted { role: felt252, account: ContractAddress, sender: ContractAddress, } /// Emitted when `account` is revoked `role`. /// /// `sender` is the account that originated the contract call: /// - If using `revoke_role`, it is the admin role bearer. /// - If using `renounce_role`, it is the role bearer (i.e. `account`). #[derive(Drop, starknet::Event)] struct RoleRevoked { role: felt252, account: ContractAddress, sender: ContractAddress, } /// Emitted when `new_admin_role` is set as `role`'s admin role, replacing `previous_admin_role` /// /// `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite /// {RoleAdminChanged} not being emitted signaling this. #[derive(Drop, starknet::Event)] struct RoleAdminChanged { role: felt252, previous_admin_role: felt252, new_admin_role: felt252, } pub mod Errors { pub const INVALID_CALLER: felt252 = 'Can only renounce role for self'; pub const MISSING_ROLE: felt252 = 'Caller is missing role'; } #[embeddable_as(AccessControlImpl)] pub impl AccessControl< TContractState, +HasComponent, +SRC5Component::HasComponent, +Drop, > of IAccessControl> { /// Returns whether `account` has been granted `role`. fn has_role( self: @ComponentState, role: felt252, account: ContractAddress, ) -> bool { self.AccessControl_role_member.read((role, account)) } /// Returns the admin role that controls `role`. fn get_role_admin(self: @ComponentState, role: felt252) -> felt252 { self.AccessControl_role_admin.read(role) } /// Grants `role` to `account`. /// /// If `account` had not been already granted `role`, emits a `RoleGranted` event. /// /// Requirements: /// /// - the caller must have `role`'s admin role. fn grant_role( ref self: ComponentState, role: felt252, account: ContractAddress, ) { let admin = self.get_role_admin(role); self.assert_only_role(admin); self._grant_role(role, account); } /// Revokes `role` from `account`. /// /// If `account` had been granted `role`, emits a `RoleRevoked` event. /// /// Requirements: /// /// - the caller must have `role`'s admin role. fn revoke_role( ref self: ComponentState, role: felt252, account: ContractAddress, ) { let admin = self.get_role_admin(role); self.assert_only_role(admin); self._revoke_role(role, account); } /// Revokes `role` from the calling account. /// /// Roles are often managed via `grant_role` and `revoke_role`: this function's /// purpose is to provide a mechanism for accounts to lose their privileges /// if they are compromised (such as when a trusted device is misplaced). /// /// If the calling account had been revoked `role`, emits a `RoleRevoked` /// event. /// /// Requirements: /// /// - the caller must be `account`. fn renounce_role( ref self: ComponentState, role: felt252, account: ContractAddress, ) { let caller: ContractAddress = get_caller_address(); assert(caller == account, Errors::INVALID_CALLER); self._revoke_role(role, account); } } const IACCESSCONTROL_ID: felt252 = 0x23700be02858dbe2ac4dc9c9f66d0b6b0ed81ec7f970ca6844500a56ff61751; #[generate_trait] pub impl InternalImpl< TContractState, +HasComponent, impl SRC5: SRC5Component::HasComponent, +Drop, > of InternalTrait { /// Initializes the contract by registering the IAccessControl interface Id. fn initializer(ref self: ComponentState) { let mut src5_component = get_dep_component_mut!(ref self, SRC5); src5_component.register_interface(IACCESSCONTROL_ID); } /// Validates that the caller has the given role. Otherwise it panics. fn assert_only_role(self: @ComponentState, role: felt252) { let caller: ContractAddress = get_caller_address(); let authorized: bool = self.has_role(role, caller); assert(authorized, Errors::MISSING_ROLE); } /// Attempts to grant `role` to `account`. /// /// Internal function without access restriction. /// /// May emit a `RoleGranted` event. fn _grant_role( ref self: ComponentState, role: felt252, account: ContractAddress, ) { if !self.has_role(role, account) { let caller: ContractAddress = get_caller_address(); self.AccessControl_role_member.entry((role, account)).write(true); self.emit(RoleGranted { role, account, sender: caller }); } } /// Attempts to revoke `role` from `account`. /// /// Internal function without access restriction. /// /// May emit a `RoleRevoked` event. fn _revoke_role( ref self: ComponentState, role: felt252, account: ContractAddress, ) { if self.has_role(role, account) { let caller: ContractAddress = get_caller_address(); self.AccessControl_role_member.entry((role, account)).write(false); self.emit(RoleRevoked { role, account, sender: caller }); } } /// Sets `admin_role` as `role`'s admin role. /// /// Emits a `RoleAdminChanged` event. fn _set_role_admin( ref self: ComponentState, role: felt252, admin_role: felt252, ) { let previous_admin_role: felt252 = self.get_role_admin(role); self.AccessControl_role_admin.entry(role).write(admin_role); self.emit(RoleAdminChanged { role, previous_admin_role, new_admin_role: admin_role }); } } } ================================================ FILE: crates/forge/tests/data/component_macros/tests/test_contract.cairo ================================================ use component_macros::example::{IMyContractDispatcher, IMyContractDispatcherTrait}; use core::option::OptionTrait; use core::result::ResultTrait; use core::traits::TryInto; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare, start_cheat_caller_address}; use starknet::ContractAddress; #[test] fn test_mint() { let contract = declare("MyContract").unwrap().contract_class(); let (address, _) = contract.deploy(@array!['minter']).unwrap(); let minter: ContractAddress = 'minter'.try_into().unwrap(); let dispatcher = IMyContractDispatcher { contract_address: address }; start_cheat_caller_address(address, minter); dispatcher.mint(); } ================================================ FILE: crates/forge/tests/data/contract_state/Scarb.toml ================================================ [package] name = "contract_state" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.11.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } assert_macros = "2.11.0" [[target.starknet-contract]] ================================================ FILE: crates/forge/tests/data/contract_state/src/balance.cairo ================================================ #[starknet::interface] pub trait IHelloStarknetExtended { fn increase_balance(ref self: TContractState, amount: u256); fn get_balance(self: @TContractState) -> u256; fn get_caller_info(self: @TContractState, address: starknet::ContractAddress) -> u256; fn get_balance_at(self: @TContractState, index: u64) -> u256; } #[starknet::contract] pub mod HelloStarknetExtended { use starknet::storage::{ Map, MutableVecTrait, StoragePathEntry, StoragePointerReadAccess, StoragePointerWriteAccess, Vec, VecTrait, }; use starknet::{ContractAddress, get_caller_address}; #[derive(starknet::Store)] struct Owner { pub address: ContractAddress, pub name: felt252, } #[storage] struct Storage { pub owner: Owner, pub balance: u256, pub balance_records: Vec, pub callers: Map, } #[constructor] fn constructor(ref self: ContractState, owner_name: felt252) { self ._set_owner( starknet::get_execution_info().tx_info.account_contract_address, owner_name, ); self.balance_records.push(0); } #[abi(embed_v0)] impl HelloStarknetExtendedImpl of super::IHelloStarknetExtended { fn increase_balance(ref self: ContractState, amount: u256) { let caller = get_caller_address(); let value_before = self.callers.entry(caller).read(); assert(amount != 0, 'Amount cannot be 0'); self.balance.write(self.balance.read() + amount); self.callers.entry(caller).write(value_before + amount); self.balance_records.push(self.balance.read()); } fn get_balance(self: @ContractState) -> u256 { self.balance.read() } fn get_caller_info(self: @ContractState, address: ContractAddress) -> u256 { self.callers.entry(address).read() } fn get_balance_at(self: @ContractState, index: u64) -> u256 { assert(index < self.balance_records.len(), 'Index out of range'); self.balance_records.at(index).read() } } #[generate_trait] pub impl InternalFunctions of InternalFunctionsTrait { fn _set_owner(ref self: ContractState, address: ContractAddress, name: felt252) { self.owner.address.write(address); self.owner.name.write(name); } } } ================================================ FILE: crates/forge/tests/data/contract_state/src/lib.cairo ================================================ pub mod balance; pub mod storage_node; ================================================ FILE: crates/forge/tests/data/contract_state/src/storage_node.cairo ================================================ #[starknet::interface] pub trait IStorageNodeContract { fn get_description_at(self: @TContractState, index: u64) -> felt252; fn get_data_at( self: @TContractState, index: u64, address: starknet::ContractAddress, key: u16, ) -> ByteArray; } #[starknet::contract] pub mod StorageNodeContract { use starknet::ContractAddress; use starknet::storage::{Map, StoragePathEntry, StoragePointerReadAccess}; #[starknet::storage_node] pub struct RandomData { pub description: felt252, pub data: Map<(ContractAddress, u16), ByteArray>, } #[storage] pub struct Storage { pub random_data: Map, } #[abi(embed_v0)] impl IStorageNodeContractImpl of super::IStorageNodeContract { fn get_description_at(self: @ContractState, index: u64) -> felt252 { self.random_data.entry(index).description.read() } fn get_data_at( self: @ContractState, index: u64, address: ContractAddress, key: u16, ) -> ByteArray { self.random_data.entry(index).data.entry((address, key)).read() } } } ================================================ FILE: crates/forge/tests/data/contract_state/tests/test_fork.cairo ================================================ #[starknet::interface] trait IMap { fn put(ref self: TMapState, key: felt252, value: felt252); fn get(self: @TMapState, key: felt252) -> felt252; } #[starknet::contract] mod Map { use starknet::storage::{Map, StorageMapReadAccess, StoragePathEntry, StoragePointerWriteAccess}; #[storage] pub struct Storage { pub storage: Map, } #[abi(embed_v0)] impl MapImpl of super::IMap { fn put(ref self: ContractState, key: felt252, value: felt252) { self.storage.entry(key).write(value); } fn get(self: @ContractState, key: felt252) -> felt252 { self.storage.read(key) } } } use snforge_std::interact_with_state; use starknet::ContractAddress; use starknet::storage::StorageMapWriteAccess; #[test] #[fork(url: "{{ NODE_RPC_URL }}", block_number: 900_000)] fn test_fork_contract() { let contract_address: ContractAddress = 0x00cd8f9ab31324bb93251837e4efb4223ee195454f6304fcfcb277e277653008 .try_into() .unwrap(); let dispatcher = IMapDispatcher { contract_address }; assert(dispatcher.get(1) == 2, 'Wrong value'); interact_with_state( contract_address, || { let mut state = Map::contract_state_for_testing(); state.storage.write(1, 13579) }, ); assert(dispatcher.get(1) == 13579, 'Wrong value'); } ================================================ FILE: crates/forge/tests/data/contract_state/tests/test_state.cairo ================================================ use contract_state::balance::HelloStarknetExtended::InternalFunctionsTrait; use contract_state::balance::{ HelloStarknetExtended, IHelloStarknetExtendedDispatcher, IHelloStarknetExtendedDispatcherTrait, }; use snforge_std::interact_with_state; use starknet::ContractAddress; use starknet::storage::{ MutableVecTrait, StorageMapWriteAccess, StoragePointerReadAccess, StoragePointerWriteAccess, }; use crate::utils::deploy_contract; #[test] fn test_interact_with_state() { let contract_address = deploy_contract("HelloStarknetExtended", array!['Name']); let dispatcher = IHelloStarknetExtendedDispatcher { contract_address }; assert(dispatcher.get_balance() == 0, 'Wrong balance'); interact_with_state( contract_address, || { let mut state = HelloStarknetExtended::contract_state_for_testing(); state.balance.write(987); }, ); assert(dispatcher.get_balance() == 987, 'Wrong balance'); dispatcher.increase_balance(13); assert(dispatcher.get_balance() == 1000, 'Wrong balance'); } #[test] fn test_interact_with_state_return() { let contract_address = deploy_contract("HelloStarknetExtended", array!['Name']); let dispatcher = IHelloStarknetExtendedDispatcher { contract_address }; assert(dispatcher.get_balance() == 0, 'Wrong balance'); let res = interact_with_state( contract_address, || -> u256 { let mut state = HelloStarknetExtended::contract_state_for_testing(); state.balance.write(111); state.balance.read() }, ); assert(res == 111, 'Wrong balance'); } #[test] fn test_interact_with_initialized_state() { let contract_address = deploy_contract("HelloStarknetExtended", array!['Name']); let dispatcher = IHelloStarknetExtendedDispatcher { contract_address }; dispatcher.increase_balance(199); interact_with_state( contract_address, || { let mut state = HelloStarknetExtended::contract_state_for_testing(); assert(state.balance.read() == 199, 'Wrong balance'); state.balance.write(1); }, ); assert(dispatcher.get_balance() == 1, 'Wrong balance'); } #[test] fn test_interact_with_state_vec() { let contract_address = deploy_contract("HelloStarknetExtended", array!['Name']); let dispatcher = IHelloStarknetExtendedDispatcher { contract_address }; dispatcher.increase_balance(1); dispatcher.increase_balance(1); dispatcher.increase_balance(1); interact_with_state( contract_address, || { let mut state = HelloStarknetExtended::contract_state_for_testing(); assert(state.balance_records.len() == 4, 'Wrong length'); state.balance_records.push(10); }, ); assert(dispatcher.get_balance_at(0) == 0, 'Wrong balance'); assert(dispatcher.get_balance_at(2) == 2, 'Wrong balance'); assert(dispatcher.get_balance_at(4) == 10, 'Wrong balance'); } #[test] fn test_interact_with_state_map() { let contract_address = deploy_contract("HelloStarknetExtended", array!['Name']); let dispatcher = IHelloStarknetExtendedDispatcher { contract_address }; dispatcher.increase_balance(1); interact_with_state( contract_address, || { let mut state = HelloStarknetExtended::contract_state_for_testing(); state.callers.write(0x123.try_into().unwrap(), 1000); state.callers.write(0x321.try_into().unwrap(), 2000); }, ); assert( dispatcher.get_caller_info(0x123.try_into().unwrap()) == 1000, 'Wrong data for address 0x123', ); assert( dispatcher.get_caller_info(0x321.try_into().unwrap()) == 2000, 'Wrong data for address 0x321', ); assert( dispatcher.get_caller_info(0x12345.try_into().unwrap()) == 0, 'Wrong data for address 0x12345', ); } #[test] fn test_interact_with_state_internal_function() { let contract_address = deploy_contract("HelloStarknetExtended", array!['Name']); let get_owner = || -> (ContractAddress, felt252) { interact_with_state( contract_address, || -> (ContractAddress, felt252) { let mut state = HelloStarknetExtended::contract_state_for_testing(); (state.owner.address.read(), state.owner.name.read()) }, ) }; let (owner_address, owner_name) = get_owner(); assert(owner_address == 0.try_into().unwrap(), 'Incorrect owner address'); assert(owner_name == 'Name', 'Incorrect owner name'); interact_with_state( contract_address, || { let mut state = HelloStarknetExtended::contract_state_for_testing(); state._set_owner(0x777.try_into().unwrap(), 'New name'); }, ); let (owner_address, owner_name) = get_owner(); assert(owner_address == 0x777.try_into().unwrap(), 'Incorrect owner address'); assert(owner_name == 'New name', 'Incorrect owner name'); } ================================================ FILE: crates/forge/tests/data/contract_state/tests/test_storage_node.cairo ================================================ use contract_state::storage_node::{ IStorageNodeContractDispatcher, IStorageNodeContractDispatcherTrait, StorageNodeContract, }; use snforge_std::interact_with_state; use starknet::storage::{StoragePathEntry, StoragePointerWriteAccess}; use crate::utils::deploy_contract; #[test] fn test_storage_node() { let contract_address = deploy_contract("StorageNodeContract", array![]); let dispatcher = IStorageNodeContractDispatcher { contract_address }; let contract_to_set = 0x123.try_into().unwrap(); assert(dispatcher.get_description_at(1) == '', 'Incorrect description'); assert(dispatcher.get_data_at(1, contract_to_set, 10) == "", 'Incorrect data'); interact_with_state( contract_address, || { let mut state = StorageNodeContract::contract_state_for_testing(); state.random_data.entry(1).description.write('Lorem Ipsum'); state.random_data.entry(1).data.entry((contract_to_set, 10)).write("Verba sine sensu"); }, ); assert(dispatcher.get_description_at(1) == 'Lorem Ipsum', 'Incorrect description'); assert(dispatcher.get_data_at(1, contract_to_set, 10) == "Verba sine sensu", 'Incorrect data'); } ================================================ FILE: crates/forge/tests/data/contract_state/tests/utils.cairo ================================================ use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; pub fn deploy_contract(name: ByteArray, calldata: Array) -> starknet::ContractAddress { let contract = declare(name).unwrap().contract_class(); let (contract_address, _) = contract.deploy(@calldata).unwrap(); contract_address } ================================================ FILE: crates/forge/tests/data/contracts/block_hash_checker.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait BlockHashCheckerInterface { fn write_block(ref self: TContractState); fn read_block_hash(self: @TContractState) -> felt252; } #[starknet::contract] mod BlockHashChecker { use core::starknet::SyscallResultTrait; use array::ArrayTrait; use starknet::{get_block_info, get_block_hash_syscall}; use box::BoxTrait; use starknet::ContractAddress; #[storage] struct Storage { block_hash: felt252, } #[abi(embed_v0)] impl BlockHashCheckerImpl of super::BlockHashCheckerInterface { fn write_block(ref self: ContractState) { let block_info = get_block_info().unbox(); let block_hash = get_block_hash_syscall(block_info.block_number - 10).unwrap_syscall(); self.block_hash.write(block_hash); } fn read_block_hash(self: @ContractState) -> felt252 { self.block_hash.read() } } } ================================================ FILE: crates/forge/tests/data/contracts/block_info_checker.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait IBlockInfoChecker { fn read_block_number(self: @TContractState) -> u64; fn read_block_timestamp(self: @TContractState) -> u64; fn read_sequencer_address(self: @TContractState) -> ContractAddress; } #[starknet::contract] mod BlockInfoChecker { use core::starknet::SyscallResultTrait; use core::array::ArrayTrait; use starknet::get_block_info; use core::box::BoxTrait; use starknet::ContractAddress; #[storage] struct Storage {} #[abi(embed_v0)] impl IBlockInfoChecker of super::IBlockInfoChecker { fn read_block_number(self: @ContractState) -> u64 { get_block_info().unbox().block_number } fn read_block_timestamp(self: @ContractState) -> u64 { get_block_info().unbox().block_timestamp } fn read_sequencer_address(self: @ContractState) -> ContractAddress { get_block_info().unbox().sequencer_address } } } ================================================ FILE: crates/forge/tests/data/contracts/catching_error.cairo ================================================ #[starknet::interface] pub trait ITop { fn call_panic_contract( self: @TContractState, panic_contract_address: starknet::ContractAddress, ); } #[feature("safe_dispatcher")] #[starknet::contract] mod Top { use super::{INestedSafeDispatcher, INestedSafeDispatcherTrait}; #[storage] struct Storage {} #[abi(embed_v0)] impl TopImpl of super::ITop { fn call_panic_contract( self: @ContractState, panic_contract_address: starknet::ContractAddress, ) { let dispatcher = INestedSafeDispatcher { contract_address: panic_contract_address }; match dispatcher.do_panic() { Result::Ok(_) => core::panic_with_felt252('Expected panic'), Result::Err(err_data) => { assert(*err_data.at(0) == 'Panic in Nested contract', 'Incorrect error'); }, } } } } #[starknet::interface] pub trait INested { fn do_panic(self: @TContractState); } #[starknet::contract] mod Nested { #[storage] struct Storage {} #[abi(embed_v0)] impl NestedImpl of super::INested { fn do_panic(self: @ContractState) { core::panic_with_felt252('Panic in Nested contract'); } } } ================================================ FILE: crates/forge/tests/data/contracts/cheat_block_hash_checker.cairo ================================================ #[starknet::interface] trait ICheatBlockHashChecker { fn get_block_hash(ref self: TContractState, block_number: u64) -> felt252; } #[starknet::contract] mod CheatBlockHashChecker { use core::starknet::SyscallResultTrait; use starknet::syscalls::get_block_hash_syscall; #[storage] struct Storage {} #[abi(embed_v0)] impl CheatBlockHashChecker of super::ICheatBlockHashChecker { fn get_block_hash(ref self: ContractState, block_number: u64) -> felt252 { let block_hash = get_block_hash_syscall(block_number).unwrap_syscall(); block_hash } } } ================================================ FILE: crates/forge/tests/data/contracts/cheat_block_number_checker.cairo ================================================ #[starknet::interface] trait ICheatBlockNumberChecker { fn get_block_number(ref self: TContractState) -> u64; fn get_block_number_and_emit_event(ref self: TContractState) -> u64; } #[starknet::contract] mod CheatBlockNumberChecker { use box::BoxTrait; #[storage] struct Storage { balance: felt252, } #[event] #[derive(Drop, starknet::Event)] enum Event { BlockNumberEmitted: BlockNumberEmitted } #[derive(Drop, starknet::Event)] struct BlockNumberEmitted { block_number: u64 } #[abi(embed_v0)] impl ICheatBlockNumberChecker of super::ICheatBlockNumberChecker { fn get_block_number(ref self: ContractState) -> u64 { starknet::get_block_info().unbox().block_number } fn get_block_number_and_emit_event(ref self: ContractState) -> u64 { let block_number = starknet::get_block_info().unbox().block_number; self.emit(Event::BlockNumberEmitted(BlockNumberEmitted { block_number })); block_number } } } ================================================ FILE: crates/forge/tests/data/contracts/cheat_block_timestamp_checker.cairo ================================================ #[starknet::interface] trait ICheatBlockTimestampChecker { fn get_block_timestamp(ref self: TContractState) -> u64; fn get_block_timestamp_and_emit_event(ref self: TContractState) -> u64; fn get_block_timestamp_and_number(ref self: TContractState) -> (u64, u64); } #[starknet::contract] mod CheatBlockTimestampChecker { use box::BoxTrait; #[storage] struct Storage {} #[event] #[derive(Drop, starknet::Event)] enum Event { BlockTimestampEmitted: BlockTimestampEmitted } #[derive(Drop, starknet::Event)] struct BlockTimestampEmitted { block_timestamp: u64 } #[abi(embed_v0)] impl CheatBlockTimestampChecker of super::ICheatBlockTimestampChecker { fn get_block_timestamp(ref self: ContractState) -> u64 { starknet::get_block_info().unbox().block_timestamp } fn get_block_timestamp_and_emit_event(ref self: ContractState) -> u64 { let block_timestamp = starknet::get_block_info().unbox().block_timestamp; self.emit(Event::BlockTimestampEmitted(BlockTimestampEmitted { block_timestamp })); block_timestamp } fn get_block_timestamp_and_number(ref self: ContractState) -> (u64, u64) { let block_info = starknet::get_block_info().unbox(); (block_info.block_timestamp, block_info.block_number) } } } ================================================ FILE: crates/forge/tests/data/contracts/cheat_caller_address_checker.cairo ================================================ use starknet::ClassHash; #[starknet::interface] trait ICheatCallerAddressChecker { fn get_caller_address(ref self: TContractState) -> felt252; fn get_caller_address_and_emit_event(ref self: TContractState) -> felt252; } #[starknet::interface] trait ICheatCallerAddressLibraryCallChecker { fn get_caller_address_via_library_call( self: @TContractState, class_hash: ClassHash, ) -> felt252; } #[starknet::contract] mod CheatCallerAddressChecker { use box::BoxTrait; use starknet::ContractAddressIntoFelt252; use starknet::ContractAddress; use option::Option; use traits::Into; #[storage] struct Storage { balance: felt252, } #[event] #[derive(Drop, starknet::Event)] enum Event { CallerAddressEmitted: CallerAddressEmitted } #[derive(Drop, starknet::Event)] struct CallerAddressEmitted { caller_address: felt252 } #[abi(embed_v0)] impl ICheatCallerAddressChecker of super::ICheatCallerAddressChecker { fn get_caller_address(ref self: ContractState) -> felt252 { starknet::get_caller_address().into() } fn get_caller_address_and_emit_event(ref self: ContractState) -> felt252 { let caller_address = starknet::get_caller_address().into(); self.emit(Event::CallerAddressEmitted(CallerAddressEmitted { caller_address })); caller_address } } } #[starknet::contract] mod CheatCallerAddressLibraryCallChecker { use starknet::ClassHash; use super::ICheatCallerAddressCheckerDispatcherTrait; #[storage] struct Storage {} #[abi(embed_v0)] impl CheatCallerAddressLibraryCallCheckerImpl of super::ICheatCallerAddressLibraryCallChecker { fn get_caller_address_via_library_call( self: @ContractState, class_hash: ClassHash, ) -> felt252 { super::ICheatCallerAddressCheckerLibraryDispatcher { class_hash }.get_caller_address() } } } ================================================ FILE: crates/forge/tests/data/contracts/cheat_sequencer_address_checker.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait ICheatSequencerAddressChecker { fn get_sequencer_address(ref self: TContractState) -> ContractAddress; fn get_seq_addr_and_emit_event(ref self: TContractState) -> ContractAddress; } #[starknet::contract] mod CheatSequencerAddressChecker { use starknet::ContractAddress; #[storage] struct Storage {} #[event] #[derive(Drop, starknet::Event)] enum Event { SequencerAddressEmitted: SequencerAddressEmitted } #[derive(Drop, starknet::Event)] struct SequencerAddressEmitted { sequencer_address: ContractAddress } #[abi(embed_v0)] impl ICheatSequencerAddressChecker of super::ICheatSequencerAddressChecker { fn get_sequencer_address(ref self: ContractState) -> ContractAddress { starknet::get_block_info().unbox().sequencer_address } fn get_seq_addr_and_emit_event(ref self: ContractState) -> ContractAddress { let sequencer_address = starknet::get_block_info().unbox().sequencer_address; self.emit(Event::SequencerAddressEmitted(SequencerAddressEmitted { sequencer_address })); sequencer_address } } } ================================================ FILE: crates/forge/tests/data/contracts/cheat_tx_info_checker.cairo ================================================ use starknet::info::TxInfo; use serde::Serde; use option::OptionTrait; use array::ArrayTrait; use starknet::{ClassHash, ContractAddress}; use starknet::info::v3::ResourceBounds; #[starknet::interface] trait ICheatTxInfoChecker { fn get_tx_hash(self: @TContractState) -> felt252; fn get_nonce(self: @TContractState) -> felt252; fn get_account_contract_address(self: @TContractState) -> ContractAddress; fn get_signature(self: @TContractState) -> Span; fn get_version(self: @TContractState) -> felt252; fn get_max_fee(self: @TContractState) -> u128; fn get_chain_id(self: @TContractState) -> felt252; fn get_resource_bounds(self: @TContractState) -> Span; fn get_tip(self: @TContractState) -> u128; fn get_paymaster_data(self: @TContractState) -> Span; fn get_nonce_data_availability_mode(self: @TContractState) -> u32; fn get_fee_data_availability_mode(self: @TContractState) -> u32; fn get_account_deployment_data(self: @TContractState) -> Span; fn get_proof_facts(self: @TContractState) -> Span; fn get_tx_info(self: @TContractState) -> starknet::info::v2::TxInfo; fn get_tx_info_v3(self: @TContractState) -> starknet::info::v3::TxInfo; } #[starknet::interface] trait ICheatBlockNumberChecker { fn get_block_number(ref self: TContractState) -> u64; } #[starknet::interface] trait ICheatBlockTimestampChecker { fn get_block_timestamp(ref self: TContractState) -> u64; } #[starknet::interface] trait ICheatSequencerAddressChecker { fn get_sequencer_address(ref self: TContractState) -> ContractAddress; } #[starknet::interface] trait ICheatExecutionInfoLibraryCallChecker { fn get_block_number_via_library_call(ref self: TContractState, class_hash: ClassHash) -> u64; fn get_block_timestamp_via_library_call( ref self: TContractState, class_hash: ClassHash, ) -> u64; fn get_sequencer_address_via_library_call( ref self: TContractState, class_hash: ClassHash, ) -> ContractAddress; fn get_tx_hash_via_library_call(self: @TContractState, class_hash: ClassHash) -> felt252; } #[starknet::contract] mod CheatTxInfoChecker { use serde::Serde; use starknet::info::TxInfo; use box::BoxTrait; use starknet::ContractAddress; use starknet::info::v2::ResourceBounds; use starknet::{SyscallResultTrait, SyscallResult, syscalls::get_execution_info_v2_syscall, syscalls::get_execution_info_v3_syscall}; #[storage] struct Storage { balance: felt252, } #[abi(embed_v0)] impl ICheatTxInfoChecker of super::ICheatTxInfoChecker { fn get_tx_hash(self: @ContractState) -> felt252 { starknet::get_tx_info().unbox().transaction_hash } fn get_nonce(self: @ContractState) -> felt252 { starknet::get_tx_info().unbox().nonce } fn get_account_contract_address(self: @ContractState) -> ContractAddress { starknet::get_tx_info().unbox().account_contract_address } fn get_signature(self: @ContractState) -> Span { starknet::get_tx_info().unbox().signature } fn get_version(self: @ContractState) -> felt252 { starknet::get_tx_info().unbox().version } fn get_max_fee(self: @ContractState) -> u128 { starknet::get_tx_info().unbox().max_fee } fn get_chain_id(self: @ContractState) -> felt252 { starknet::get_tx_info().unbox().chain_id } fn get_resource_bounds(self: @ContractState) -> Span { get_tx_info_v2().unbox().resource_bounds } fn get_tip(self: @ContractState) -> u128 { get_tx_info_v2().unbox().tip } fn get_paymaster_data(self: @ContractState) -> Span { get_tx_info_v2().unbox().paymaster_data } fn get_nonce_data_availability_mode(self: @ContractState) -> u32 { get_tx_info_v2().unbox().nonce_data_availability_mode } fn get_fee_data_availability_mode(self: @ContractState) -> u32 { get_tx_info_v2().unbox().fee_data_availability_mode } fn get_account_deployment_data(self: @ContractState) -> Span { get_tx_info_v2().unbox().account_deployment_data } fn get_proof_facts(self: @ContractState) -> Span { get_tx_info_v3().unbox().proof_facts } fn get_tx_info(self: @ContractState) -> starknet::info::v2::TxInfo { get_tx_info_v2().unbox() } fn get_tx_info_v3(self: @ContractState) -> starknet::info::v3::TxInfo { get_tx_info_v3().unbox() } } fn get_execution_info_v2() -> Box { get_execution_info_v2_syscall().unwrap_syscall() } fn get_tx_info_v2() -> Box { get_execution_info_v2().unbox().tx_info } fn get_execution_info_v3() -> Box { get_execution_info_v3_syscall().unwrap_syscall() } fn get_tx_info_v3() -> Box { get_execution_info_v3().unbox().tx_info } } #[starknet::contract] mod CheatExecutionInfoLibraryCallChecker { use starknet::{ClassHash, ContractAddress}; use super::{ ICheatBlockNumberCheckerDispatcherTrait, ICheatBlockTimestampCheckerDispatcherTrait, ICheatSequencerAddressCheckerDispatcherTrait, ICheatTxInfoCheckerDispatcherTrait, }; #[storage] struct Storage {} #[abi(embed_v0)] impl CheatExecutionInfoLibraryCallCheckerImpl of super::ICheatExecutionInfoLibraryCallChecker { fn get_block_number_via_library_call( ref self: ContractState, class_hash: ClassHash, ) -> u64 { super::ICheatBlockNumberCheckerLibraryDispatcher { class_hash }.get_block_number() } fn get_block_timestamp_via_library_call( ref self: ContractState, class_hash: ClassHash, ) -> u64 { super::ICheatBlockTimestampCheckerLibraryDispatcher { class_hash }.get_block_timestamp() } fn get_sequencer_address_via_library_call( ref self: ContractState, class_hash: ClassHash, ) -> ContractAddress { super::ICheatSequencerAddressCheckerLibraryDispatcher { class_hash } .get_sequencer_address() } fn get_tx_hash_via_library_call(self: @ContractState, class_hash: ClassHash) -> felt252 { super::ICheatTxInfoCheckerLibraryDispatcher { class_hash }.get_tx_hash() } } } ================================================ FILE: crates/forge/tests/data/contracts/deploy_checker.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait IDeployChecker { fn get_balance(self: @TContractState) -> felt252; fn get_caller(self: @TContractState) -> ContractAddress; } #[starknet::contract] mod DeployChecker { use starknet::ContractAddress; #[storage] struct Storage { balance: felt252, caller: ContractAddress, } #[constructor] fn constructor(ref self: ContractState, balance: felt252) -> (ContractAddress, felt252) { assert(balance != 0, 'Initial balance cannot be 0'); self.balance.write(balance); self.caller.write(starknet::get_caller_address()); (self.caller.read(), balance) } #[abi(embed_v0)] impl DeployCheckerImpl of super::IDeployChecker { fn get_balance(self: @ContractState) -> felt252 { self.balance.read() } fn get_caller(self: @ContractState) -> ContractAddress { self.caller.read() } } } ================================================ FILE: crates/forge/tests/data/contracts/dict_using_contract.cairo ================================================ #[starknet::contract] mod DictUsingContract { use core::num::traits::{One}; fn unique_count(mut ary: Array) -> u32 { let mut dict: Felt252Dict = Default::default(); let mut counter = 0; // TODO loop { match ary.pop_front() { Option::Some(value) => { if dict.get(value).is_one() { continue; } dict.insert(value, One::one()); counter += 1; }, Option::None => { break; } } }; counter } #[storage] struct Storage { unique_count: u32 } #[constructor] fn constructor(ref self: ContractState, values: Array) { self.unique_count.write(unique_count(values.clone())); // 2 invocations for 2 dict allocations self.unique_count.write(unique_count(values)); } #[external(v0)] fn get_unique(self: @ContractState) -> u32 { self.unique_count.read() } #[external(v0)] fn write_unique(ref self: ContractState, values: Array) { self.unique_count.write(unique_count(values.clone())); // 2 invocations for 2 dict allocations self.unique_count.write(unique_count(values)); } } ================================================ FILE: crates/forge/tests/data/contracts/erc20.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait IERC20 { fn get_name(self: @TContractState) -> felt252; fn get_symbol(self: @TContractState) -> felt252; fn get_decimals(self: @TContractState) -> u8; fn get_total_supply(self: @TContractState) -> u256; fn balance_of(self: @TContractState, account: ContractAddress) -> u256; fn allowance(self: @TContractState, owner: ContractAddress, spender: ContractAddress) -> u256; fn transfer(ref self: TContractState, recipient: ContractAddress, amount: u256); fn transfer_from( ref self: TContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256 ); fn approve(ref self: TContractState, spender: ContractAddress, amount: u256); fn increase_allowance(ref self: TContractState, spender: ContractAddress, added_value: u256); fn decrease_allowance( ref self: TContractState, spender: ContractAddress, subtracted_value: u256 ); } #[starknet::contract] mod ERC20 { use starknet::{ contract_address_const, get_caller_address, ContractAddress, storage::{ StoragePointerWriteAccess, StorageMapReadAccess, StoragePathEntry, Map }, }; use zeroable::Zeroable; #[storage] struct Storage { name: felt252, symbol: felt252, decimals: u8, total_supply: u256, balances: Map, allowances: Map<(ContractAddress, ContractAddress), u256>, } #[event] #[derive(Drop, starknet::Event)] enum Event { Transfer: Transfer, Approval: Approval, } #[derive(Drop, starknet::Event)] struct Transfer { from: ContractAddress, to: ContractAddress, value: u256, } #[derive(Drop, starknet::Event)] struct Approval { owner: ContractAddress, spender: ContractAddress, value: u256, } #[constructor] fn constructor( ref self: ContractState, name_: felt252, symbol_: felt252, decimals_: u8, initial_supply: u256, recipient: ContractAddress ) { self.name.write(name_); self.symbol.write(symbol_); self.decimals.write(decimals_); assert(!recipient.is_zero(), 'ERC20: mint to the 0 address'); self.total_supply.write(initial_supply); self.balances.entry(recipient).write(initial_supply); self .emit( Event::Transfer( Transfer { from: contract_address_const::<0>(), to: recipient, value: initial_supply } ) ); } #[abi(embed_v0)] impl IERC20Impl of super::IERC20 { fn get_name(self: @ContractState) -> felt252 { self.name.read() } fn get_symbol(self: @ContractState) -> felt252 { self.symbol.read() } fn get_decimals(self: @ContractState) -> u8 { self.decimals.read() } fn get_total_supply(self: @ContractState) -> u256 { self.total_supply.read() } fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { self.balances.entry(account).read() } fn allowance( self: @ContractState, owner: ContractAddress, spender: ContractAddress ) -> u256 { self.allowances.entry((owner, spender)).read() } fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) { let sender = get_caller_address(); self.transfer_helper(sender, recipient, amount); } fn transfer_from( ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256 ) { let caller = get_caller_address(); self.spend_allowance(sender, caller, amount); self.transfer_helper(sender, recipient, amount); } fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) { let caller = get_caller_address(); self.approve_helper(caller, spender, amount); } fn increase_allowance( ref self: ContractState, spender: ContractAddress, added_value: u256 ) { let caller = get_caller_address(); self .approve_helper( caller, spender, self.allowances.entry((caller, spender)).read() + added_value ); } fn decrease_allowance( ref self: ContractState, spender: ContractAddress, subtracted_value: u256 ) { let caller = get_caller_address(); self .approve_helper( caller, spender, self.allowances.entry((caller, spender)).read() - subtracted_value ); } } #[generate_trait] impl StorageImpl of StorageTrait { fn transfer_helper( ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256 ) { assert(!sender.is_zero(), 'ERC20: transfer from 0'); assert(!recipient.is_zero(), 'ERC20: transfer to 0'); self.balances.entry(sender).write(self.balances.entry(sender).read() - amount); self.balances.entry(recipient).write(self.balances.entry(recipient).read() + amount); self.emit(Event::Transfer(Transfer { from: sender, to: recipient, value: amount })); } fn spend_allowance( ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256 ) { let current_allowance = self.allowances.entry((owner, spender)).read(); let ONES_MASK = 0xffffffffffffffffffffffffffffffff_u128; let is_unlimited_allowance = current_allowance.low == ONES_MASK && current_allowance.high == ONES_MASK; if !is_unlimited_allowance { self.approve_helper(owner, spender, current_allowance - amount); } } fn approve_helper( ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256 ) { assert(!spender.is_zero(), 'ERC20: approve from 0'); self.allowances.entry((owner, spender)).write(amount); self.emit(Event::Approval(Approval { owner, spender, value: amount })); } } } ================================================ FILE: crates/forge/tests/data/contracts/gas_checker.cairo ================================================ #[starknet::interface] trait IGasChecker { fn keccak(self: @TContractState, repetitions: u32); fn range_check(self: @TContractState); fn bitwise(self: @TContractState, repetitions: u32); fn pedersen(self: @TContractState); fn poseidon(self: @TContractState); fn ec_op(self: @TContractState, repetitions: u32); fn change_balance(ref self: TContractState, new_balance: u64); fn send_l1_message(self: @TContractState); fn emit_event(self: @TContractState, n_keys_and_vals: u32); } #[starknet::contract] mod GasChecker { use core::{ec, ec::{EcPoint, EcPointTrait}}; use starknet::ContractAddress; #[storage] struct Storage { balance: u64, } #[abi(embed_v0)] impl IGasCheckerImpl of super::IGasChecker { fn keccak(self: @ContractState, repetitions: u32) { let mut i: u32 = 0; while i < repetitions { keccak::keccak_u256s_le_inputs(array![1].span()); i += 1; } } fn range_check(self: @ContractState) { // Felt into ContractAddress conversion uses RangeCheck as implicit argument for _ in 0..1000_u16 { let _x: ContractAddress = 1234.try_into().unwrap(); } } fn bitwise(self: @ContractState, repetitions: u32) { let mut i: u32 = 0; while i < repetitions { 1_u8 & 1_u8; 2_u8 & 2_u8; i += 1; } } fn pedersen(self: @ContractState) { core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); core::pedersen::pedersen(1, 2); } fn poseidon(self: @ContractState) { core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); core::poseidon::hades_permutation(0, 0, 0); } fn ec_op(self: @ContractState, repetitions: u32) { let mut i: u32 = 0; while i < repetitions { EcPointTrait::new_from_x(1).unwrap().mul(2); i += 1; } } fn change_balance(ref self: ContractState, new_balance: u64) { self.balance.write(new_balance); } fn send_l1_message(self: @ContractState) { starknet::send_message_to_l1_syscall(1, array![1, 2, 3].span()).unwrap(); } fn emit_event(self: @ContractState, n_keys_and_vals: u32) { let mut keys = array![]; let mut values = array![]; let mut i: u32 = 0; while i < n_keys_and_vals { keys.append('key'); values.append(1); i += 1; }; starknet::emit_event_syscall(keys.span(), values.span()).unwrap(); } } #[l1_handler] fn handle_l1_message(ref self: ContractState, from_address: felt252) { keccak::keccak_u256s_le_inputs(array![1].span()); keccak::keccak_u256s_le_inputs(array![1].span()); keccak::keccak_u256s_le_inputs(array![1].span()); keccak::keccak_u256s_le_inputs(array![1].span()); } } ================================================ FILE: crates/forge/tests/data/contracts/gas_checker_proxy.cairo ================================================ use starknet::{ContractAddress, SyscallResult}; #[starknet::interface] trait IGasChecker { fn send_l1_message(self: @TContractState); } #[starknet::interface] trait IGasCheckerProxy { fn send_l1_message_from_gas_checker(self: @TContractState, address: ContractAddress); fn call_other_contract( self: @TContractState, contract_address: ContractAddress, entry_point_selector: felt252, calldata: Array::, ) -> SyscallResult>; } #[starknet::contract] mod GasCheckerProxy { use starknet::{ContractAddress, SyscallResult}; use starknet::syscalls::call_contract_syscall; use super::{IGasCheckerDispatcher, IGasCheckerDispatcherTrait}; #[storage] struct Storage {} #[abi(embed_v0)] impl IGasCheckerProxy of super::IGasCheckerProxy { fn send_l1_message_from_gas_checker(self: @ContractState, address: ContractAddress) { let gas_checker = IGasCheckerDispatcher { contract_address: address }; gas_checker.send_l1_message() } fn call_other_contract( self: @ContractState, contract_address: ContractAddress, entry_point_selector: felt252, calldata: Array::, ) -> SyscallResult> { call_contract_syscall(contract_address, entry_point_selector, calldata.span()) } } } ================================================ FILE: crates/forge/tests/data/contracts/gas_constructor_checker.cairo ================================================ #[starknet::contract] mod GasConstructorChecker { #[storage] struct Storage {} #[constructor] fn constructor(ref self: ContractState, compute_keccak: bool) { if compute_keccak { keccak::keccak_u256s_le_inputs(array![1].span()); keccak::keccak_u256s_le_inputs(array![1].span()); } } } ================================================ FILE: crates/forge/tests/data/contracts/hello_starknet.cairo ================================================ #[starknet::interface] trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn call_other_contract( self: @TContractState, other_contract_address: felt252, selector: felt252, calldata: Option>, ) -> Span; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); fn do_a_panic_with_bytearray(self: @TContractState); } #[starknet::contract] mod HelloStarknet { use array::ArrayTrait; use starknet::{SyscallResultTrait, syscalls}; #[storage] struct Storage { balance: felt252, } #[abi(embed_v0)] impl IHelloStarknetImpl of super::IHelloStarknet { // Increases the balance by the given amount fn increase_balance(ref self: ContractState, amount: felt252) { self.balance.write(self.balance.read() + amount); } // Returns the current balance fn get_balance(self: @ContractState) -> felt252 { self.balance.read() } fn call_other_contract( self: @ContractState, other_contract_address: felt252, selector: felt252, calldata: Option>, ) -> Span { syscalls::call_contract_syscall( other_contract_address.try_into().unwrap(), selector, match calldata { Some(data) => data.span(), None => array![].span(), }, ) .unwrap_syscall() } // Panics fn do_a_panic(self: @ContractState) { let mut arr = ArrayTrait::new(); arr.append('PANIC'); arr.append('DAYTAH'); panic(arr); } // Panics with given array data fn do_a_panic_with(self: @ContractState, panic_data: Array) { panic(panic_data); } // Panics with a bytearray fn do_a_panic_with_bytearray(self: @ContractState) { assert!( false, "This is a very long\n and multiline message that is certain to fill the buffer", ); } } } ================================================ FILE: crates/forge/tests/data/contracts/hello_starknet_extended.cairo ================================================ #[starknet::interface] pub trait IHelloStarknetExtended { fn increase_balance(ref self: TContractState, amount: u256); fn get_balance(self: @TContractState) -> u256; fn get_caller_info(self: @TContractState, address: starknet::ContractAddress) -> u256; fn get_balance_at(self: @TContractState, index: u64) -> u256; } #[starknet::contract] pub mod HelloStarknetExtended { use starknet::storage::{ Map, MutableVecTrait, StoragePathEntry, StoragePointerReadAccess, StoragePointerWriteAccess, Vec, VecTrait, }; use starknet::{ContractAddress, get_caller_address}; #[derive(starknet::Store)] struct Owner { pub address: ContractAddress, pub name: felt252, } #[storage] struct Storage { pub owner: Owner, pub balance: u256, pub balance_records: Vec, pub callers: Map, } #[constructor] fn constructor(ref self: ContractState, owner_name: felt252) { self ._set_owner( starknet::get_execution_info().tx_info.account_contract_address, owner_name, ); self.balance_records.push(0); } #[abi(embed_v0)] impl HelloStarknetExtendedImpl of super::IHelloStarknetExtended { fn increase_balance(ref self: ContractState, amount: u256) { let caller = get_caller_address(); let value_before = self.callers.entry(caller).read(); assert(amount != 0, 'Amount cannot be 0'); self.balance.write(self.balance.read() + amount); self.callers.entry(caller).write(value_before + amount); self.balance_records.push(self.balance.read()); } fn get_balance(self: @ContractState) -> u256 { self.balance.read() } fn get_caller_info(self: @ContractState, address: ContractAddress) -> u256 { self.callers.entry(address).read() } fn get_balance_at(self: @ContractState, index: u64) -> u256 { assert(index < self.balance_records.len(), 'Index out of range'); self.balance_records.at(index).read() } } #[generate_trait] pub impl InternalFunctions of InternalFunctionsTrait { fn _set_owner(ref self: ContractState, address: ContractAddress, name: felt252) { self.owner.address.write(address); self.owner.name.write(name); } } } ================================================ FILE: crates/forge/tests/data/contracts/hello_starknet_for_nested_calls.cairo ================================================ #[starknet::interface] pub trait IHelloStarknet { fn example_function(ref self: TContractState); } #[starknet::contract] pub mod HelloStarknet { use starknet::SyscallResultTrait; use core::sha256::compute_sha256_u32_array; #[storage] struct Storage {} #[abi(embed_v0)] impl HelloStarknetImpl of super::IHelloStarknet { fn example_function(ref self: ContractState) { core::keccak::keccak_u256s_le_inputs(array![1].span()); let _hash = compute_sha256_u32_array(array![0x68656c6c], 0x6f, 1); starknet::syscalls::get_block_hash_syscall(1).unwrap_syscall(); starknet::syscalls::emit_event_syscall(array![1].span(), array![2].span()) .unwrap_syscall(); } } } ================================================ FILE: crates/forge/tests/data/contracts/keccak_usage.cairo ================================================ #[starknet::interface] trait IHelloKeccak { fn run_keccak(ref self: TContractState, input: Array) -> u256; } #[starknet::contract] mod HelloKeccak { use array::ArrayTrait; use starknet::syscalls::keccak_syscall; use starknet::SyscallResultTrait; #[storage] struct Storage {} #[abi(embed_v0)] impl IHelloKeccakImpl of super::IHelloKeccak { fn run_keccak(ref self: ContractState, input: Array) -> u256 { keccak_syscall(input.span()).unwrap_syscall() } } } ================================================ FILE: crates/forge/tests/data/contracts/l1_handler_execute_checker.cairo ================================================ #[derive(Copy, Serde, Drop)] struct L1Data { balance: felt252, token_id: u256 } #[starknet::interface] trait IBalanceToken { fn get_balance(self: @TContractState) -> felt252; fn get_token_id(self: @TContractState) -> u256; } #[starknet::contract] mod l1_handler_executor { use super::{IBalanceToken, L1Data}; #[storage] struct Storage { l1_caller: felt252, balance: felt252, token_id: u256 } #[constructor] fn constructor(ref self: ContractState, l1_caller: felt252) { self.l1_caller.write(l1_caller); } #[abi(embed_v0)] impl IBalanceTokenImpl of super::IBalanceToken { // Returns the current balance fn get_balance(self: @ContractState) -> felt252 { self.balance.read() } // Returns the current token id. fn get_token_id(self: @ContractState) -> u256 { self.token_id.read() } } #[l1_handler] fn process_l1_message(ref self: ContractState, from_address: felt252, data: L1Data) { assert(from_address == self.l1_caller.read(), 'Unauthorized l1 caller'); self.balance.write(data.balance); self.token_id.write(data.token_id); } #[l1_handler] fn panicking_l1_handler(ref self: ContractState, from_address: felt252) { panic(array!['custom', 'panic']); } } ================================================ FILE: crates/forge/tests/data/contracts/message_to_l1_checker.cairo ================================================ use starknet::{ContractAddress, EthAddress}; #[starknet::interface] trait IMessageToL1Checker { fn send_message(ref self: TContractState, some_data: Array, to_address: EthAddress); } #[starknet::contract] mod MessageToL1Checker { use starknet::{ContractAddress, EthAddress, send_message_to_l1_syscall}; #[storage] struct Storage {} #[abi(embed_v0)] impl IMessageToL1Checker of super::IMessageToL1Checker { fn send_message(ref self: ContractState, some_data: Array, to_address: EthAddress) { send_message_to_l1_syscall(to_address.into(), some_data.span()).unwrap() } } } ================================================ FILE: crates/forge/tests/data/contracts/meta_tx_v0_checkers.cairo ================================================ #[starknet::interface] trait ICheckerMetaTxV0 { fn __execute__(ref self: TContractState) -> felt252; } #[starknet::contract(account)] mod CheatCallerAddressCheckerMetaTxV0 { #[storage] struct Storage {} #[abi(embed_v0)] impl ICheckerMetaTxV0 of super::ICheckerMetaTxV0 { fn __execute__(ref self: ContractState) -> felt252 { starknet::get_caller_address().into() } } } #[starknet::contract(account)] mod TxInfoCheckerMetaTxV0 { use starknet::get_execution_info; #[storage] struct Storage {} #[abi(embed_v0)] impl ITxInfoCheckerMetaTxV0 of super::ICheckerMetaTxV0 { fn __execute__(ref self: ContractState) -> felt252 { let execution_info = get_execution_info().unbox(); let tx_info = execution_info.tx_info.unbox(); tx_info.version } } } #[starknet::interface] trait ICheatBlockHashCheckerMetaTxV0 { fn __execute__(ref self: TContractState, block_number: u64) -> felt252; } #[starknet::contract(account)] mod CheatBlockHashCheckerMetaTxV0 { use starknet::SyscallResultTrait; use starknet::syscalls::get_block_hash_syscall; #[storage] struct Storage {} #[abi(embed_v0)] impl ICheatBlockHashCheckerMetaTxV0 of super::ICheatBlockHashCheckerMetaTxV0 { fn __execute__(ref self: ContractState, block_number: u64) -> felt252 { let block_hash = get_block_hash_syscall(block_number).unwrap_syscall(); block_hash } } } #[starknet::interface] trait ISimpleCheckerMetaTxV0 { fn __execute__(ref self: TContractState) -> felt252; } #[starknet::contract(account)] mod SimpleCheckerMetaTxV0 { use starknet::SyscallResultTrait; use starknet::syscalls::get_block_hash_syscall; #[storage] struct Storage {} #[abi(embed_v0)] impl ISimpleCheckerMetaTxV0 of super::ISimpleCheckerMetaTxV0 { fn __execute__(ref self: ContractState) -> felt252 { 1234567890.into() } } } ================================================ FILE: crates/forge/tests/data/contracts/meta_tx_v0_test.cairo ================================================ #[starknet::interface] trait IMetaTxV0Test { fn execute_meta_tx_v0( ref self: TContractState, target: starknet::ContractAddress, signature: Span, ) -> felt252; fn execute_meta_tx_v0_get_block_hash( ref self: TContractState, target: starknet::ContractAddress, block_number: u64, signature: Span, ) -> felt252; } #[starknet::contract] mod MetaTxV0Test { use starknet::syscalls::meta_tx_v0_syscall; #[storage] struct Storage {} #[abi(embed_v0)] impl IMetaTxV0Test of super::IMetaTxV0Test { fn execute_meta_tx_v0( ref self: ContractState, target: starknet::ContractAddress, signature: Span, ) -> felt252 { let selector = selector!("__execute__"); let calldata = array![]; let result = meta_tx_v0_syscall(target, selector, calldata.span(), signature).unwrap(); *result.at(0) } fn execute_meta_tx_v0_get_block_hash( ref self: ContractState, target: starknet::ContractAddress, block_number: u64, signature: Span, ) -> felt252 { let selector = selector!("__execute__"); let calldata = array![block_number.into()]; let result = meta_tx_v0_syscall(target, selector, calldata.span(), signature).unwrap(); *result.at(0) } } } ================================================ FILE: crates/forge/tests/data/contracts/mock_checker.cairo ================================================ #[derive(Serde, Drop)] struct StructThing { item_one: felt252, item_two: felt252, } #[starknet::interface] trait IMockChecker { fn get_thing(ref self: TContractState) -> felt252; fn get_thing_wrapper(ref self: TContractState) -> felt252; fn get_constant_thing(ref self: TContractState) -> felt252; fn get_struct_thing(ref self: TContractState) -> StructThing; fn get_arr_thing(ref self: TContractState) -> Array; } #[starknet::contract] mod MockChecker { use super::IMockChecker; use super::StructThing; use array::ArrayTrait; #[storage] struct Storage { stored_thing: felt252 } #[constructor] fn constructor(ref self: ContractState, arg1: felt252) { self.stored_thing.write(arg1) } #[abi(embed_v0)] impl IMockCheckerImpl of super::IMockChecker { fn get_thing(ref self: ContractState) -> felt252 { self.stored_thing.read() } fn get_thing_wrapper(ref self: ContractState) -> felt252 { self.get_thing() } fn get_constant_thing(ref self: ContractState) -> felt252 { 13 } fn get_struct_thing(ref self: ContractState) -> StructThing { StructThing {item_one: 12, item_two: 21} } fn get_arr_thing(ref self: ContractState) -> Array { array![StructThing {item_one: 12, item_two: 21}] } } } ================================================ FILE: crates/forge/tests/data/contracts/response_with_2_felts.cairo ================================================ #[starknet::interface] trait IResponseWith2Felts { fn get(self: @TContractState) -> Response; } #[derive(Drop, Serde)] struct Response { a: felt252, b: felt252, } #[starknet::contract] mod ResponseWith2Felts { use core::array::ArrayTrait; use super::Response; #[storage] struct Storage {} #[abi(embed_v0)] impl IResponseWith2FeltsImpl of super::IResponseWith2Felts { // Increases the balance by the given amount fn get(self: @ContractState) -> Response { Response { a: 8, b: 43 } } } } ================================================ FILE: crates/forge/tests/data/contracts/reverts_caller.cairo ================================================ use starknet::SyscallResultTrait; #[starknet::interface] trait ICaller { /// Execute test scenario in tests: call entrypoint that modifies storage and panics, then assert that state was reverted fn call(ref self: TContractState); } #[starknet::interface] trait IContract { /// Return storage value fn read_storage(self: @TContractState) -> felt252; /// Write `value` to storage and emits event fn write_storage(ref self: TContractState, value: felt252); /// Write `value` to storage and then panic fn write_storage_and_panic(ref self: TContractState, value: felt252); } #[starknet::contract] #[feature("safe_dispatcher")] mod Caller { use starknet::ContractAddress; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; use super::{ICaller, IContractSafeDispatcher, IContractSafeDispatcherTrait}; #[storage] struct Storage { address: ContractAddress, } #[constructor] fn constructor(ref self: ContractState, address: ContractAddress) { self.address.write(address); } #[abi(embed_v0)] impl ICallerImpl of ICaller { fn call(ref self: ContractState) { let contract_address = self.address.read(); let dispatcher = IContractSafeDispatcher { contract_address }; dispatcher.write_storage(43).unwrap(); // Make sure the storage is updated let storage = dispatcher.read_storage().unwrap(); assert(storage == 43, 'Incorrect storage'); // Try modifying storage and handle panic match dispatcher.write_storage_and_panic(1) { Result::Ok(_) => panic!("Should have panicked"), Result::Err(_) => { // handled }, } // Check storage change was reverted let storage = dispatcher.read_storage().unwrap(); assert(storage == 43, 'Storage not reverted'); } } } ================================================ FILE: crates/forge/tests/data/contracts/reverts_contract.cairo ================================================ #[starknet::interface] trait IContract { /// Return storage value fn read_storage(self: @TContractState) -> felt252; /// Write `value` to storage and emits event fn write_storage(ref self: TContractState, value: felt252); /// Write `value` to storage and then panic fn write_storage_and_panic(ref self: TContractState, value: felt252); } #[starknet::contract] mod Contract { use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { value: felt252, } #[event] #[derive(Drop, starknet::Event)] pub enum Event { ValueUpdated: ValueUpdated, } #[derive(Drop, starknet::Event)] pub struct ValueUpdated { pub old_value: felt252, pub new_value: felt252, } #[abi(embed_v0)] impl IContractImpl of super::IContract { fn write_storage_and_panic(ref self: ContractState, value: felt252) { self.write_storage(value); panic!("Panicked"); } fn read_storage(self: @ContractState) -> felt252 { self.value.read() } fn write_storage(ref self: ContractState, value: felt252) { let old_value = self.value.read(); self.emit(ValueUpdated { old_value, new_value: value}); self.value.write(value); } } } ================================================ FILE: crates/forge/tests/data/contracts/reverts_proxy.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait IContract { /// Return storage value fn read_storage(self: @TContractState) -> felt252; /// Write `value` to storage and emits event fn write_storage(ref self: TContractState, value: felt252); /// Write `value` to storage and then panic fn write_storage_and_panic(ref self: TContractState, value: felt252); } #[starknet::interface] /// Makes calls to nested contract with address `address` trait ILibraryProxy { /// Call on proxied contract unwrapping the syscall result: Return storage value fn library_read_storage(self: @TContractState, address: ContractAddress) -> felt252; /// Call on proxied contract unwrapping the syscall result: Write `value` to storage fn library_write_storage(self: @TContractState, address: ContractAddress, value: felt252); /// Call on proxied contract with safe dispatcher: Write `value` to storage and then handle panic fn library_write_storage_and_panic(self: @TContractState, address: ContractAddress, value: felt252); } #[starknet::interface] /// Makes calls to nested contract with safe dispatcher trait ISafeProxy { /// Call on proxied contract with safe dispatcher: Write `value` to storage and then handle panic fn call_write_storage_and_handle_panic(ref self: TContractState, value: felt252); } #[feature("safe_dispatcher")] #[starknet::contract] mod Proxy { use starknet::{ContractAddress, SyscallResultTrait}; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; use super::{IContract, ISafeProxy, ILibraryProxy, IContractDispatcher, IContractSafeDispatcher, IContractDispatcherTrait, IContractSafeDispatcherTrait}; #[storage] struct Storage { address: ContractAddress, } #[constructor] fn constructor(ref self: ContractState, address: ContractAddress) { self.address.write(address); } #[abi(embed_v0)] impl IProxyImpl of IContract { fn write_storage_and_panic(ref self: ContractState, value: felt252) { let contract_address = self.address.read(); let dispatcher = IContractDispatcher { contract_address }; let storage = dispatcher.read_storage(); assert(storage != 0, 'Storage already modified'); dispatcher.write_storage(43); let storage = dispatcher.read_storage(); assert(storage == 43, 'Incorrect storage value'); dispatcher.write_storage_and_panic(value); // unreachable assert(false, 'Should not execute'); } fn read_storage(self: @ContractState) -> felt252 { let contract_address = self.address.read(); let dispatcher = IContractDispatcher { contract_address }; dispatcher.read_storage() } fn write_storage(ref self: ContractState, value: felt252) { let contract_address = self.address.read(); let dispatcher = IContractDispatcher { contract_address }; dispatcher.write_storage(value) } } #[abi(embed_v0)] impl ILibraryProxyImpl of ILibraryProxy { fn library_write_storage_and_panic(self: @ContractState, address: ContractAddress, value: felt252) { let dispatcher = IContractDispatcher { contract_address: address }; let storage = dispatcher.read_storage(); assert(storage != 0, 'Storage already modified'); dispatcher.write_storage(43); let storage = dispatcher.read_storage(); assert(storage == 43, 'Incorrect storage value'); dispatcher.write_storage_and_panic(value); // unreachable assert(false, 'Should not execute'); } fn library_read_storage(self: @ContractState, address: ContractAddress) -> felt252 { let dispatcher = IContractDispatcher { contract_address: address }; dispatcher.read_storage() } fn library_write_storage(self: @ContractState, address: ContractAddress, value: felt252) { let dispatcher = IContractDispatcher { contract_address: address }; dispatcher.write_storage(value) } } #[abi(embed_v0)] impl ISafeProxyImpl of ISafeProxy { fn call_write_storage_and_handle_panic(ref self: ContractState, value: felt252) { let contract_address = self.address.read(); let dispatcher = IContractSafeDispatcher { contract_address }; let storage = dispatcher.read_storage().unwrap_syscall(); assert(storage != 0, 'Storage already modified'); if let Ok(_) = dispatcher.write_storage_and_panic(value) { panic!("Should have panicked") } } } } ================================================ FILE: crates/forge/tests/data/contracts/serding.cairo ================================================ #[derive(Drop, Serde)] struct NestedStruct { d: felt252, } #[derive(Drop, Serde)] struct CustomStruct { a: felt252, b: felt252, c: NestedStruct, } #[derive(Drop, Serde)] struct AnotherCustomStruct { e: felt252, } #[starknet::interface] trait ISerding { fn add_multiple_parts( self: @TContractState, custom_struct: CustomStruct, another_struct: AnotherCustomStruct, standalone_arg: felt252 ) -> felt252; } #[starknet::contract] mod Serding { use super::{CustomStruct, AnotherCustomStruct}; #[storage] struct Storage {} #[abi(embed_v0)] impl SerdingImpl of super::ISerding { fn add_multiple_parts( self: @ContractState, custom_struct: CustomStruct, another_struct: AnotherCustomStruct, standalone_arg: felt252 ) -> felt252 { custom_struct.a + custom_struct.b + custom_struct.c.d + another_struct.e + standalone_arg } } } ================================================ FILE: crates/forge/tests/data/contracts/spy_events_checker.cairo ================================================ use starknet::ContractAddress; // 0x2c77ca97586968c6651a533bd5f58042c368b14cf5f526d2f42f670012e10ac #[starknet::interface] trait ICairo0Contract { // this function only job is to emit `my_event` with single felt252 value fn emit_one_cairo0_event(ref self: TContractState, contract_address: felt252); } #[starknet::interface] trait ISpyEventsChecker { fn do_not_emit(ref self: TContractState); fn emit_one_event(ref self: TContractState, some_data: felt252); fn emit_two_events( ref self: TContractState, some_data: felt252, some_more_data: ContractAddress ); fn emit_three_events( ref self: TContractState, some_data: felt252, some_more_data: ContractAddress, even_more_data: u256 ); fn emit_event_syscall(ref self: TContractState, some_key: felt252, some_data: felt252); fn test_cairo0_event_collection(ref self: TContractState, cairo0_address: ContractAddress); } #[starknet::contract] mod SpyEventsChecker { use starknet::ContractAddress; use starknet::SyscallResultTrait; use super::ICairo0ContractDispatcherTrait; #[storage] struct Storage {} #[event] #[derive(Drop, starknet::Event)] enum Event { FirstEvent: FirstEvent, SecondEvent: SecondEvent, ThirdEvent: ThirdEvent, } #[derive(Drop, starknet::Event)] struct FirstEvent { some_data: felt252 } #[derive(Drop, starknet::Event)] struct SecondEvent { some_data: felt252, #[key] some_more_data: ContractAddress } #[derive(Drop, starknet::Event)] struct ThirdEvent { some_data: felt252, some_more_data: ContractAddress, even_more_data: u256 } #[abi(embed_v0)] impl ISpyEventsChecker of super::ISpyEventsChecker { fn do_not_emit(ref self: ContractState) {} fn emit_one_event(ref self: ContractState, some_data: felt252) { self.emit(Event::FirstEvent(FirstEvent { some_data })); } fn emit_two_events( ref self: ContractState, some_data: felt252, some_more_data: ContractAddress ) { self.emit(Event::FirstEvent(FirstEvent { some_data })); self.emit(Event::SecondEvent(SecondEvent { some_data, some_more_data })); } fn emit_three_events( ref self: ContractState, some_data: felt252, some_more_data: ContractAddress, even_more_data: u256 ) { self.emit(Event::FirstEvent(FirstEvent { some_data })); self.emit(Event::SecondEvent(SecondEvent { some_data, some_more_data })); self.emit(Event::ThirdEvent(ThirdEvent { some_data, some_more_data, even_more_data })); } fn emit_event_syscall(ref self: ContractState, some_key: felt252, some_data: felt252) { starknet::emit_event_syscall(array![some_key].span(), array![some_data].span()) .unwrap_syscall(); } fn test_cairo0_event_collection(ref self: ContractState, cairo0_address: ContractAddress) { let cairo0_contract = super::ICairo0ContractDispatcher { contract_address: cairo0_address }; cairo0_contract.emit_one_cairo0_event(123456789); } } } ================================================ FILE: crates/forge/tests/data/contracts/storage_tester.cairo ================================================ #[starknet::contract] mod StorageTester { use starknet::storage::Map; #[derive(Serde, Drop, starknet::Store)] struct NestedStructure { c: felt252 } #[derive(Serde, Drop, starknet::Store)] struct StoredStructure { a: felt252, b: NestedStructure, } #[derive(Serde, Drop, starknet::Store, Hash)] struct NestedKey { c: felt252 } #[derive(Serde, Drop, starknet::Store, Hash)] struct StructuredKey { a: felt252, b: NestedKey, } #[storage] struct Storage { structure: StoredStructure, felt_to_structure: Map, structure_to_felt: Map, felt_to_felt: Map, } #[external(v0)] fn insert_structure(ref self: ContractState, value: StoredStructure) { self.structure.write(value); } #[external(v0)] fn read_structure(self: @ContractState) -> StoredStructure { self.structure.read() } #[external(v0)] fn insert_felt_to_structure(ref self: ContractState, key: felt252, value: StoredStructure) { self.felt_to_structure.write(key, value); } #[external(v0)] fn read_felt_to_structure(self: @ContractState, key: felt252) -> StoredStructure { self.felt_to_structure.read(key) } #[external(v0)] fn insert_structure_to_felt(ref self: ContractState, key: StructuredKey, value: felt252) { self.structure_to_felt.write(key, value); } #[external(v0)] fn read_structure_to_felt(self: @ContractState, key: StructuredKey) -> felt252 { self.structure_to_felt.read(key) } #[external(v0)] fn insert_felt_to_felt(ref self: ContractState, key: felt252, value: felt252) { self.felt_to_felt.write(key, value); } #[external(v0)] fn read_felt_to_felt(self: @ContractState, key: felt252) -> felt252 { self.felt_to_felt.read(key) } } ================================================ FILE: crates/forge/tests/data/contracts/too_many_events.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] trait ITooManyEvents { fn emit_too_many_events(self: @TContractState, count: felt252); fn emit_too_many_keys(self: @TContractState, count: felt252); fn emit_too_many_data(self: @TContractState, count: felt252); } #[starknet::contract] mod TooManyEvents { #[storage] struct Storage {} #[abi(embed_v0)] impl TooManyEventsImpl of super::ITooManyEvents { fn emit_too_many_events(self: @ContractState, mut count: felt252) { while count != 0 { starknet::emit_event_syscall(array![0].span(), array![0].span()).unwrap(); count -= 1; }; } fn emit_too_many_keys(self: @ContractState, mut count: felt252) { let mut arr = array![]; while count != 0 { arr.append(0); count -= 1; }; starknet::emit_event_syscall(arr.span(), array![0].span()).unwrap(); } fn emit_too_many_data(self: @ContractState, mut count: felt252) { let mut arr = array![]; while count != 0 { arr.append(0); count -= 1; }; starknet::emit_event_syscall(array![0].span(), arr.span()).unwrap(); } } } ================================================ FILE: crates/forge/tests/data/contracts/trace_dummy.cairo ================================================ #[starknet::interface] trait ITraceDummy { fn from_proxy_dummy(ref self: T); } #[starknet::contract] mod TraceDummy { #[storage] struct Storage { balance: u8 } #[abi(embed_v0)] impl ITraceDummyImpl of super::ITraceDummy { fn from_proxy_dummy(ref self: ContractState) { self.balance.write(7); } } } ================================================ FILE: crates/forge/tests/data/contracts/trace_info_checker.cairo ================================================ use starknet::{ContractAddress, ClassHash}; #[starknet::interface] trait ITraceInfoProxy { fn with_libcall(self: @T, class_hash: ClassHash) -> felt252; fn regular_call(self: @T, contract_address: ContractAddress) -> felt252; fn with_panic(self: @T, contract_address: ContractAddress); fn call_two(self: @T, checker_address: ContractAddress, dummy_address: ContractAddress); } #[starknet::interface] trait ITraceInfoChecker { fn from_proxy(self: @T, data: felt252) -> felt252; fn panic(self: @T); } #[starknet::contract] mod TraceInfoChecker { use super::{ITraceInfoChecker, ITraceInfoProxyDispatcher, ITraceInfoProxyDispatcherTrait}; use starknet::{ContractAddress, get_contract_address}; #[storage] struct Storage {} #[abi(embed_v0)] impl ITraceInfoChceckerImpl of ITraceInfoChecker { fn from_proxy(self: @ContractState, data: felt252) -> felt252 { 100 + data } fn panic(self: @ContractState) { panic_with_felt252('panic'); } } #[l1_handler] fn handle_l1_message( ref self: ContractState, from_address: felt252, proxy_address: ContractAddress ) -> felt252 { ITraceInfoProxyDispatcher { contract_address: proxy_address } .regular_call(get_contract_address()) } } ================================================ FILE: crates/forge/tests/data/contracts/trace_info_proxy.cairo ================================================ use starknet::{ContractAddress, ClassHash}; #[starknet::interface] trait ITraceInfoProxy { fn with_libcall(self: @T, class_hash: ClassHash) -> felt252; fn regular_call(self: @T, contract_address: ContractAddress) -> felt252; fn with_panic(self: @T, contract_address: ContractAddress); fn call_two(self: @T, checker_address: ContractAddress, dummy_address: ContractAddress); } #[starknet::interface] trait ITraceInfoChecker { fn from_proxy(self: @T, data: felt252) -> felt252; fn panic(self: @T); } #[starknet::interface] trait ITraceDummy { fn from_proxy_dummy(ref self: T); } #[starknet::contract] mod TraceInfoProxy { use super::{ ITraceInfoCheckerDispatcherTrait, ITraceInfoCheckerDispatcher, ITraceInfoCheckerLibraryDispatcher, ITraceInfoProxy, ITraceDummyDispatcher, ITraceDummyDispatcherTrait }; use starknet::{ContractAddress, ClassHash}; #[storage] struct Storage {} #[constructor] fn constructor(ref self: ContractState, contract_address: ContractAddress) { ITraceInfoCheckerDispatcher { contract_address }.from_proxy(1); } #[abi(embed_v0)] impl ITraceInfoProxyImpl of ITraceInfoProxy { fn regular_call(self: @ContractState, contract_address: ContractAddress) -> felt252 { ITraceInfoCheckerDispatcher { contract_address }.from_proxy(2) } fn with_libcall(self: @ContractState, class_hash: ClassHash) -> felt252 { ITraceInfoCheckerLibraryDispatcher { class_hash }.from_proxy(3) } fn with_panic(self: @ContractState, contract_address: ContractAddress) { ITraceInfoCheckerDispatcher { contract_address }.panic(); // unreachable code to check if we stop executing after panic ITraceInfoCheckerDispatcher { contract_address }.from_proxy(5); } fn call_two( self: @ContractState, checker_address: ContractAddress, dummy_address: ContractAddress ) { ITraceInfoCheckerDispatcher { contract_address: checker_address }.from_proxy(42); ITraceDummyDispatcher { contract_address: dummy_address }.from_proxy_dummy(); } } } ================================================ FILE: crates/forge/tests/data/contracts/two_implementations.cairo ================================================ #[starknet::interface] trait IReplaceBytecode { fn get(self: @TContractState) -> felt252; fn libcall(self: @TContractState, class_hash: starknet::ClassHash) -> felt252; } #[starknet::interface] trait ILib { fn get(self: @TContractState) -> felt252; } #[starknet::contract] mod Lib { #[storage] struct Storage {} #[abi(embed_v0)] impl ILib of super::ILib { fn get(self: @ContractState) -> felt252 { 123456789 } } } #[starknet::contract] mod ReplaceBytecodeA { use super::{ILibLibraryDispatcher, ILibDispatcherTrait}; #[storage] struct Storage {} #[abi(embed_v0)] impl IReplaceBytecodeA of super::IReplaceBytecode { fn get(self: @ContractState) -> felt252 { 2137 } fn libcall(self: @ContractState, class_hash: starknet::ClassHash) -> felt252 { let dispatcher = ILibLibraryDispatcher { class_hash }; dispatcher.get() } } } #[starknet::contract] mod ReplaceBytecodeB { use super::{ILibLibraryDispatcher, ILibDispatcherTrait}; #[storage] struct Storage {} #[abi(embed_v0)] impl IReplaceBytecodeB of super::IReplaceBytecode { fn get(self: @ContractState) -> felt252 { 420 } fn libcall(self: @ContractState, class_hash: starknet::ClassHash) -> felt252 { let dispatcher = ILibLibraryDispatcher { class_hash }; dispatcher.get() } } } ================================================ FILE: crates/forge/tests/data/coverage_project/Scarb.toml ================================================ [package] name = "coverage_project" version = "0.1.0" edition = "2024_07" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] starknet = "2.4.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] sierra = true [profile.dev.cairo] unstable-add-statements-functions-debug-info = true # Comment unstable-add-statements-code-locations-debug-info = true inlining-strategy= "avoid" # Comment ================================================ FILE: crates/forge/tests/data/coverage_project/src/lib.cairo ================================================ pub fn increase_by_two(arg: u8) -> u8 { assert(2 == 2, ''); increase_by_one(arg + 1) } pub fn increase_by_one(arg: u8) -> u8 { assert(1 == 1, ''); arg + 1 } ================================================ FILE: crates/forge/tests/data/coverage_project/tests/lib.cairo ================================================ use coverage_project::{increase_by_one, increase_by_two}; #[test] fn my_test() { assert(increase_by_two(1) == 3, ''); // inlines assert(increase_by_one(1) == 2, ''); // inlines } ================================================ FILE: crates/forge/tests/data/debugging/Scarb.toml ================================================ [package] name = "debugging" version = "0.1.0" edition = "2023_01" [dependencies] starknet = ">=2.8.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [features] fuzzer = [] ================================================ FILE: crates/forge/tests/data/debugging/src/lib.cairo ================================================ use starknet::ContractAddress; #[derive(Drop, Serde, Clone)] struct RecursiveCall { contract_address: ContractAddress, payload: Array, } #[starknet::interface] trait RecursiveCaller { fn execute_calls(self: @T, calls: Array) -> Array; } #[starknet::interface] trait Failing { fn fail(self: @TContractState, data: Array); } #[starknet::contract] mod SimpleContract { use core::array::ArrayTrait; use core::traits::Into; use starknet::{ContractAddress, get_contract_address}; use super::{ Failing, RecursiveCall, RecursiveCaller, RecursiveCallerDispatcher, RecursiveCallerDispatcherTrait, }; #[storage] struct Storage {} #[abi(embed_v0)] impl RecursiveCallerImpl of RecursiveCaller { fn execute_calls( self: @ContractState, calls: Array, ) -> Array { let mut i = 0; #[cairofmt::skip] while i < calls.len() { let serviced_call = calls.at(i); RecursiveCallerDispatcher { contract_address: serviced_call.contract_address.clone(), } .execute_calls(serviced_call.payload.clone()); i = i + 1; }; calls } } #[abi(embed_v0)] impl FailingImpl of Failing { fn fail(self: @ContractState, data: Array) { panic(data); } } } ================================================ FILE: crates/forge/tests/data/debugging/tests/test_trace.cairo ================================================ use debugging::{ FailingDispatcher, FailingDispatcherTrait, RecursiveCall, RecursiveCallerDispatcher, RecursiveCallerDispatcherTrait, }; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::trace::get_call_trace; use snforge_std::{ContractClassTrait, declare}; #[test] #[should_panic] fn test_debugging_trace_success() { run_test(); } #[test] fn test_debugging_trace_failure() { run_test(); } fn run_test() { let sc = declare("SimpleContract").unwrap().contract_class(); let (contract_address_A, _) = sc.deploy(@array![]).unwrap(); let (contract_address_B, _) = sc.deploy(@array![]).unwrap(); let (contract_address_C, _) = sc.deploy(@array![]).unwrap(); let calls = array![ RecursiveCall { contract_address: contract_address_B, payload: array![ RecursiveCall { contract_address: contract_address_C, payload: array![] }, RecursiveCall { contract_address: contract_address_C, payload: array![] }, ], }, RecursiveCall { contract_address: contract_address_C, payload: array![] }, ]; RecursiveCallerDispatcher { contract_address: contract_address_A }.execute_calls(calls); let failing_dispatcher = FailingDispatcher { contract_address: contract_address_A }; failing_dispatcher.fail(array![1, 2, 3, 4, 5]); } #[cfg(feature: 'fuzzer')] #[test] #[fuzzer] fn test_debugging_fuzzer(_x: felt252) {} ================================================ FILE: crates/forge/tests/data/debugging_fork/Scarb.toml ================================================ [package] name = "debugging_fork" version = "0.1.0" edition = "2023_01" [dependencies] starknet = ">=2.8.0" [[target.starknet-contract]] sierra = true [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } ================================================ FILE: crates/forge/tests/data/debugging_fork/src/lib.cairo ================================================ use starknet::ContractAddress; #[derive(Drop, Serde, Clone)] struct RecursiveCall { contract_address: ContractAddress, payload: Array, } // `RecursiveCaller` is implemented in the `debugging` package #[starknet::interface] trait RecursiveCaller { fn execute_calls(self: @T, calls: Array) -> Array; } // `Failing` is implemented in the `debugging` package #[starknet::interface] trait Failing { fn fail(self: @TContractState, data: Array); } ================================================ FILE: crates/forge/tests/data/debugging_fork/tests/test_trace.cairo ================================================ use debugging_fork::{ FailingDispatcher, FailingDispatcherTrait, RecursiveCall, RecursiveCallerDispatcher, RecursiveCallerDispatcherTrait, }; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::trace::get_call_trace; use snforge_std::{ContractClassTrait, declare}; #[test] #[should_panic] #[fork(url: "{{ NODE_RPC_URL }}", block_number: 828912)] fn test_debugging_trace_success() { run_test(); } #[test] #[fork(url: "{{ NODE_RPC_URL }}", block_number: 828912)] fn test_debugging_trace_failure() { run_test(); } fn run_test() { let contract_address_A = 0x05005956a18de174e33378fa9a278dde8a22d0bf823d4bd6f9c9051fe99d04a0 .try_into() .unwrap(); let contract_address_B = 0x01c6cec47a2ed95320f76e2b50626879737aa57990748db2d9527e867f98bc55 .try_into() .unwrap(); let contract_address_C = 0x042e631f2785a56bc5bcfd247b16e72d3d957bb8efe136829379a20ac675dbb7 .try_into() .unwrap(); let calls = array![ RecursiveCall { contract_address: contract_address_B, payload: array![ RecursiveCall { contract_address: contract_address_C, payload: array![] }, RecursiveCall { contract_address: contract_address_C, payload: array![] }, ], }, RecursiveCall { contract_address: contract_address_C, payload: array![] }, ]; RecursiveCallerDispatcher { contract_address: contract_address_A }.execute_calls(calls); let failing_dispatcher = FailingDispatcher { contract_address: contract_address_A }; failing_dispatcher.fail(array![1, 2, 3, 4, 5]); } ================================================ FILE: crates/forge/tests/data/deterministic_output/Scarb.toml ================================================ [package] name = "deterministic_output" version = "0.1.0" edition = "2024_07" [dependencies] [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] sierra = true ================================================ FILE: crates/forge/tests/data/deterministic_output/src/lib.cairo ================================================ fn fib(a: felt252, b: felt252, n: felt252) -> felt252 { match n { 0 => a, _ => fib(b, a + b, n - 1), } } #[cfg(test)] mod test { use super::fib; #[test] fn second_test_pass_x() { fib(0, 1, 750000); assert(2 == 2, 'simple check'); } #[test] fn first_test_pass_y() { fib(0, 1, 3); assert(2 == 2, 'simple check'); } #[test] fn second_test_fail_y() { fib(0, 1, 750000); assert(1 == 2, 'simple check'); } #[test] fn first_test_fail_x() { fib(0, 1, 3); assert(1 == 2, 'simple check'); } } ================================================ FILE: crates/forge/tests/data/diagnostics/attributes/.cairofmtignore ================================================ tests/ ================================================ FILE: crates/forge/tests/data/diagnostics/attributes/Scarb.toml ================================================ [package] name = "attributes" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.9.3" [dev-dependencies] snforge_std = { path = "../../../../../../snforge_std" } [[target.starknet-contract]] ================================================ FILE: crates/forge/tests/data/diagnostics/attributes/src/lib.cairo ================================================ ================================================ FILE: crates/forge/tests/data/diagnostics/attributes/tests/contract.cairo ================================================ use core::array::ArrayTrait; use core::result::ResultTrait; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; @attrs@ fn call_and_invoke1(_a: felt252, b: u256) { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (_contract_address1, _) = contract.deploy(constructor_calldata).unwrap() } ================================================ FILE: crates/forge/tests/data/diagnostics/generic/Scarb.toml ================================================ [package] name = "generic" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.9.3" [dev-dependencies] snforge_std = { path = "../../../../../../snforge_std" } [[target.starknet-contract]] ================================================ FILE: crates/forge/tests/data/diagnostics/generic/src/lib.cairo ================================================ ================================================ FILE: crates/forge/tests/data/diagnostics/generic/tests/contract.cairo ================================================ fn smallest_element, impl TCopy: Copy, impl TDrop: Drop>( list: @Array, ) -> T { let mut smallest = *list[0]; let mut index = 1; while index < list.len() { if *list[index] < smallest { smallest = *list[index]; } index = index + 1; } smallest } struct MyStruct { pub value: felt252, } #[test] #[fuzzer] #[fork(url: "http://127.0.0.1:3030", block_tag: latest)] #[ignore] fn call_and_invoke(_a: felt252, b: u256) { let list: Array = array![]; // We need to specify that we are passing a snapshot of `list` as an argument let s = smallest_element(@list); assert!(s == 3); } ================================================ FILE: crates/forge/tests/data/diagnostics/inline_macros/.cairofmtignore ================================================ tests/contract.cairo ================================================ FILE: crates/forge/tests/data/diagnostics/inline_macros/Scarb.toml ================================================ [package] name = "inline_macros" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.9.3" [dev-dependencies] snforge_std = { path = "../../../../../../snforge_std" } [[target.starknet-contract]] ================================================ FILE: crates/forge/tests/data/diagnostics/inline_macros/src/lib.cairo ================================================ #[starknet::interface] pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); } ================================================ FILE: crates/forge/tests/data/diagnostics/inline_macros/tests/contract.cairo ================================================ use core::array::ArrayTrait; use core::result::ResultTrait; use inline_macros::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; #[test] #[fuzzer] #[fork(url: "http://127.0.0.1:3030", block_tag: latest)] #[ignore] fn call_and_invoke(_a: felt252, b: u256) { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; let balance = dispatcher.get_balance(); assert(balance == 0, 'balance == 0'); dispatcher.increase_balance(100); // Error below print!('balance {}'; balance); let balance = dispatcher.get_balance(); assert(balance == 100, 'balance == 100'); } ================================================ FILE: crates/forge/tests/data/diagnostics/multiple/.cairofmtignore ================================================ tests/ ================================================ FILE: crates/forge/tests/data/diagnostics/multiple/Scarb.toml ================================================ [package] name = "multiple" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.9.3" [dev-dependencies] snforge_std = { path = "../../../../../../snforge_std" } [[target.starknet-contract]] ================================================ FILE: crates/forge/tests/data/diagnostics/multiple/src/lib.cairo ================================================ #[starknet::interface] pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); } ================================================ FILE: crates/forge/tests/data/diagnostics/multiple/tests/contract.cairo ================================================ use core::array::ArrayTrait; use core::result::ResultTrait; use multiple::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; #[test] #[fuzzer] #[fork(url: "http://127.0.0.1:3030", block_tag: latest)] #[ignore] fn call_and_invoke(_a: felt252, b: u256) { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; let balance = dispatcher.get_balance(); // Error below assert(balance === 0, 'balance == 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'balance == 100'); } #[test] #[fuzzer] #[fork(url: "http://127.0.0.1:3030", block_tag: latest)] #[ignore] fn call_and_invoke2(_a: felt252, b: u256) { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; let balance = dispatcher.get_balance(); assert(balance == 0, 'balance == 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'balance == 100'); } #[test] #[fuzzer] #[fork(url: "http://127.0.0.1:3030", block_tag: latest)] #[ignore] fn call_and_invoke3(_a: felt252, b: u256) { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; // Error below let balance = dispatcher/get_balance(); assert(balance == 0, 'balance == 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'balance == 100'); } ================================================ FILE: crates/forge/tests/data/diagnostics/parameters/.cairofmtignore ================================================ tests/ ================================================ FILE: crates/forge/tests/data/diagnostics/parameters/Scarb.toml ================================================ [package] name = "parameters" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.9.3" [dev-dependencies] snforge_std = { path = "../../../../../../snforge_std" } [[target.starknet-contract]] ================================================ FILE: crates/forge/tests/data/diagnostics/parameters/src/lib.cairo ================================================ #[starknet::interface] pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); } ================================================ FILE: crates/forge/tests/data/diagnostics/parameters/tests/contract.cairo ================================================ use core::array::ArrayTrait; use core::result::ResultTrait; use parameters::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; #[test] #[fork("TESTNET")] fn call_and_invoke(_a: felt252; b: u256) { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; let balance = dispatcher.get_balance(); assert(balance == 0, 'balance == 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'balance == 100'); } ================================================ FILE: crates/forge/tests/data/diagnostics/semantic/Scarb.toml ================================================ [package] name = "semantic" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.9.3" [dev-dependencies] snforge_std = { path = "../../../../../../snforge_std" } [[target.starknet-contract]] ================================================ FILE: crates/forge/tests/data/diagnostics/semantic/src/lib.cairo ================================================ #[starknet::interface] pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); } ================================================ FILE: crates/forge/tests/data/diagnostics/semantic/tests/contract.cairo ================================================ use core::array::ArrayTrait; use core::result::ResultTrait; use semantic::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; #[test] #[fuzzer] #[fork(url: "http://127.0.0.1:3030", block_tag: latest)] #[ignore] fn call_and_invoke(_a: felt252, b: u256) { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; let balance = dispatcher.get_balance(); assert(balance == 0, 'balance == 0'); dispatcher.increase_balance(100); let y = x; let balance = dispatcher.get_balance(); assert(balance == 100, 'balance == 100'); } ================================================ FILE: crates/forge/tests/data/diagnostics/syntax/.cairofmtignore ================================================ tests/ ================================================ FILE: crates/forge/tests/data/diagnostics/syntax/Scarb.toml ================================================ [package] name = "syntax" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.9.3" [dev-dependencies] snforge_std = { path = "../../../../../../snforge_std" } [[target.starknet-contract]] ================================================ FILE: crates/forge/tests/data/diagnostics/syntax/src/lib.cairo ================================================ #[starknet::interface] pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); } ================================================ FILE: crates/forge/tests/data/diagnostics/syntax/tests/contract.cairo ================================================ use core::array::ArrayTrait; use core::result::ResultTrait; use syntax::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; #[test] #[fuzzer] #[fork(url: "http://127.0.0.1:3030", block_tag: latest)] #[ignore] fn call_and_invoke(_a: felt252, b: u256) { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata),unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; let balance = dispatcher.get_balance(); assert(balance == 0, 'balance == 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'balance == 100'); } ================================================ FILE: crates/forge/tests/data/diagnostics/test_case_attr/.cairofmtignore ================================================ tests/ ================================================ FILE: crates/forge/tests/data/diagnostics/test_case_attr/Scarb.toml ================================================ [package] name = "test_case_attr" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.9.3" [dev-dependencies] snforge_std = { path = "../../../../../../snforge_std" } [[target.starknet-contract]] ================================================ FILE: crates/forge/tests/data/diagnostics/test_case_attr/src/lib.cairo ================================================ ================================================ FILE: crates/forge/tests/data/diagnostics/test_case_attr/tests/basic.cairo ================================================ #[test] #[test_case(3, 4, 7)] fn function_without_params() { let _x = 10; } #[test] #[test_case(3, 4, 7)] fn function_with_invalid_params_count(x: felt252, y: felt252) { let _x = 10; } #[test] #[test_case(name: array![1, 2, 3], 3, 4, 7)] fn invalid_name_arg(x: felt252, y: felt252, expected: felt252) { let _x = 10; } ================================================ FILE: crates/forge/tests/data/dispatchers/Scarb.toml ================================================ [package] name = "dispatchers" version = "0.1.0" edition = "2024_07" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] starknet = "2.9.4" assert_macros = "2.9.4" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] sierra = true [scripts] test = "snforge test" [profile.dev.cairo] unstable-add-statements-functions-debug-info = true unstable-add-statements-code-locations-debug-info = true panic-backtrace = true ================================================ FILE: crates/forge/tests/data/dispatchers/src/error_handler.cairo ================================================ #[starknet::interface] pub trait IErrorHandler { fn catch_panic_and_handle(self: @TContractState); fn catch_panic_and_fail(self: @TContractState); fn call_unrecoverable(self: @TContractState); } #[feature("safe_dispatcher")] #[starknet::contract] pub mod ErrorHandler { use core::panic_with_felt252; use starknet::ContractAddress; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; use crate::failable::{IFailableContractSafeDispatcher, IFailableContractSafeDispatcherTrait}; #[storage] pub struct Storage { failable_address: ContractAddress, } #[constructor] fn constructor(ref self: ContractState, failable_address: ContractAddress) { self.failable_address.write(failable_address); } #[abi(embed_v0)] impl ErrorHandler of super::IErrorHandler { fn catch_panic_and_handle(self: @ContractState) { let dispatcher = get_safe_dispatcher(self); match dispatcher.recoverable_panic() { Result::Ok(_) => panic_with_felt252('Expected panic'), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'Errare humanum est', 'Incorrect error'); assert(*panic_data.at(1) == 'ENTRYPOINT_FAILED', 'Missing generic error'); assert(panic_data.len() == 2, 'Incorrect error length'); }, } } fn catch_panic_and_fail(self: @ContractState) { let dispatcher = get_safe_dispatcher(self); match dispatcher.recoverable_panic() { Result::Ok(_) => panic_with_felt252('Expected panic'), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'Errare humanum est', 'Incorrect error'); assert(*panic_data.at(1) == 'ENTRYPOINT_FAILED', 'Missing generic error'); assert(panic_data.len() == 2, 'Incorrect error length'); }, } panic_with_felt252('Different panic'); } fn call_unrecoverable(self: @ContractState) { let dispatcher = get_safe_dispatcher(self); // Unreachable if let Result::Ok(_) = dispatcher.unrecoverable_error() { {} } } } fn get_safe_dispatcher(self: @ContractState) -> IFailableContractSafeDispatcher { IFailableContractSafeDispatcher { contract_address: self.failable_address.read() } } } ================================================ FILE: crates/forge/tests/data/dispatchers/src/failable.cairo ================================================ #[starknet::interface] pub trait IFailableContract { fn recoverable_panic(self: @TContractState); fn unrecoverable_error(self: @TContractState); } #[starknet::contract] pub mod FailableContract { use starknet::SyscallResultTrait; #[storage] pub struct Storage {} #[abi(embed_v0)] impl FailableContract of super::IFailableContract { fn recoverable_panic(self: @ContractState) { core::panic_with_felt252('Errare humanum est'); } fn unrecoverable_error(self: @ContractState) { // Call syscall with nonexistent address should fail immediately starknet::syscalls::call_contract_syscall( 0x123.try_into().unwrap(), 0x1, array![].span(), ) .unwrap_syscall(); } } } ================================================ FILE: crates/forge/tests/data/dispatchers/src/lib.cairo ================================================ pub mod error_handler; pub mod failable; ================================================ FILE: crates/forge/tests/data/dispatchers/tests/test.cairo ================================================ use dispatchers::error_handler::{ IErrorHandlerDispatcher, IErrorHandlerDispatcherTrait, IErrorHandlerSafeDispatcher, IErrorHandlerSafeDispatcherTrait, }; use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; use starknet::ContractAddress; fn deploy_contracts() -> ContractAddress { let failable = declare("FailableContract").unwrap().contract_class(); let (failable_address, _) = failable.deploy(@array![]).unwrap(); let error_handler = declare("ErrorHandler").unwrap().contract_class(); let (contract_address, _) = error_handler.deploy(@array![failable_address.into()]).unwrap(); contract_address } #[test] fn test_error_handled_in_contract() { let contract_address = deploy_contracts(); let dispatcher = IErrorHandlerDispatcher { contract_address }; dispatcher.catch_panic_and_handle(); } #[should_panic(expected: 'Different panic')] #[test] fn test_handle_and_panic() { let contract_address = deploy_contracts(); let dispatcher = IErrorHandlerDispatcher { contract_address }; dispatcher.catch_panic_and_fail(); } #[feature("safe_dispatcher")] #[test] fn test_handle_recoverable_in_test() { let contract_address = deploy_contracts(); let dispatcher = IErrorHandlerSafeDispatcher { contract_address }; match dispatcher.catch_panic_and_fail() { Result::Ok(_) => core::panic_with_felt252('Expected panic'), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'Different panic', 'Incorrect error'); assert(*panic_data.at(1) == 'ENTRYPOINT_FAILED', 'Missing generic error'); assert(panic_data.len() == 2, 'Incorrect error length'); }, } } #[feature("safe_dispatcher")] #[test] fn test_unrecoverable_not_possible_to_handle() { let contract_address = deploy_contracts(); let dispatcher = IErrorHandlerSafeDispatcher { contract_address }; match dispatcher.call_unrecoverable() { // Unreachable Result::Ok(_) => {}, Result::Err(_) => {}, } } ================================================ FILE: crates/forge/tests/data/duplicated_test_names/Scarb.toml ================================================ [package] name = "duplicated_test_names" version = "0.1.0" edition = "2024_07" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [scripts] test = "snforge test" ================================================ FILE: crates/forge/tests/data/duplicated_test_names/src/lib.cairo ================================================ ================================================ FILE: crates/forge/tests/data/duplicated_test_names/tests/tests_a.cairo ================================================ #[test] fn test_simple() { assert(1 == 1, 'simple check'); } ================================================ FILE: crates/forge/tests/data/duplicated_test_names/tests/tests_b.cairo ================================================ #[test] fn test_simple() { assert(1 == 1, 'simple check'); } ================================================ FILE: crates/forge/tests/data/empty/Scarb.toml ================================================ [package] name = "empty" version = "0.1.0" edition = "2024_07" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] starknet = "2.4.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] sierra = true [profile.custom-profile] inherits = "release" [profile.custom-profile.cairo] sierra-replace-ids = true ================================================ FILE: crates/forge/tests/data/empty/src/lib.cairo ================================================ ================================================ FILE: crates/forge/tests/data/env/Scarb.toml ================================================ [package] name = "env" version = "0.1.0" edition = "2024_07" [dependencies] [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } ================================================ FILE: crates/forge/tests/data/env/src/lib.cairo ================================================ #[cfg(test)] mod tests { use snforge_std::env::var; #[test] fn reading_env_vars() { let felt252_value = var("FELT_ENV_VAR"); let short_string_value = var("STRING_ENV_VAR"); let mut byte_array_value = var("BYTE_ARRAY_ENV_VAR").span(); assert(felt252_value == array![987654321], 'invalid felt value'); assert(short_string_value == array!['abcde'], 'invalid short string value'); let byte_array = Serde::::deserialize(ref byte_array_value).unwrap(); assert( byte_array == "that is a very long environment variable that would normally not fit", 'Invalid ByteArray value', ) } } ================================================ FILE: crates/forge/tests/data/erc20_package/Scarb.toml ================================================ [package] name = "erc20_package" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.4.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] sierra = true [tool.snforge] exit_first = false ================================================ FILE: crates/forge/tests/data/erc20_package/src/erc20.cairo ================================================ use starknet::ContractAddress; #[starknet::interface] pub trait IERC20 { fn get_name(self: @TContractState) -> felt252; fn get_symbol(self: @TContractState) -> felt252; fn get_decimals(self: @TContractState) -> u8; fn get_total_supply(self: @TContractState) -> u256; fn balance_of(self: @TContractState, account: ContractAddress) -> u256; fn allowance(self: @TContractState, owner: ContractAddress, spender: ContractAddress) -> u256; fn transfer(ref self: TContractState, recipient: ContractAddress, amount: u256); fn transfer_from( ref self: TContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256, ); fn approve(ref self: TContractState, spender: ContractAddress, amount: u256); fn increase_allowance(ref self: TContractState, spender: ContractAddress, added_value: u256); fn decrease_allowance( ref self: TContractState, spender: ContractAddress, subtracted_value: u256, ); } #[starknet::contract] pub mod ERC20 { use core::num::traits::zero::Zero; use starknet::storage::{ Map, StoragePathEntry, StoragePointerReadAccess, StoragePointerWriteAccess, }; use starknet::{ContractAddress, contract_address_const, get_caller_address}; #[storage] pub struct Storage { name: felt252, symbol: felt252, decimals: u8, total_supply: u256, balances: Map, allowances: Map<(ContractAddress, ContractAddress), u256>, } #[event] #[derive(Drop, starknet::Event)] enum Event { Transfer: Transfer, Approval: Approval, } #[derive(Drop, starknet::Event)] struct Transfer { from: ContractAddress, to: ContractAddress, value: u256, } #[derive(Drop, starknet::Event)] struct Approval { owner: ContractAddress, spender: ContractAddress, value: u256, } #[constructor] fn constructor( ref self: ContractState, name_: felt252, symbol_: felt252, decimals_: u8, initial_supply: u256, recipient: ContractAddress, ) { self.name.write(name_); self.symbol.write(symbol_); self.decimals.write(decimals_); assert(!recipient.is_zero(), 'ERC20: mint to the 0 address'); self.total_supply.write(initial_supply); self.balances.entry(recipient).write(initial_supply); self .emit( Event::Transfer( Transfer { from: contract_address_const::<0>(), to: recipient, value: initial_supply, }, ), ); } #[abi(embed_v0)] pub impl IERC20Impl of super::IERC20 { fn get_name(self: @ContractState) -> felt252 { self.name.read() } fn get_symbol(self: @ContractState) -> felt252 { self.symbol.read() } fn get_decimals(self: @ContractState) -> u8 { self.decimals.read() } fn get_total_supply(self: @ContractState) -> u256 { self.total_supply.read() } fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { self.balances.entry(account).read() } fn allowance( self: @ContractState, owner: ContractAddress, spender: ContractAddress, ) -> u256 { self.allowances.entry((owner, spender)).read() } fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) { let sender = get_caller_address(); self.transfer_helper(sender, recipient, amount); } fn transfer_from( ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256, ) { let caller = get_caller_address(); self.spend_allowance(sender, caller, amount); self.transfer_helper(sender, recipient, amount); } fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) { let caller = get_caller_address(); self.approve_helper(caller, spender, amount); } fn increase_allowance( ref self: ContractState, spender: ContractAddress, added_value: u256, ) { let caller = get_caller_address(); self .approve_helper( caller, spender, self.allowances.entry((caller, spender)).read() + added_value, ); } fn decrease_allowance( ref self: ContractState, spender: ContractAddress, subtracted_value: u256, ) { let caller = get_caller_address(); self .approve_helper( caller, spender, self.allowances.entry((caller, spender)).read() - subtracted_value, ); } } #[generate_trait] impl StorageImpl of StorageTrait { fn transfer_helper( ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256, ) { assert(!sender.is_zero(), 'ERC20: transfer from 0'); assert(!recipient.is_zero(), 'ERC20: transfer to 0'); self.balances.entry(sender).write(self.balances.entry(sender).read() - amount); self.balances.entry(recipient).write(self.balances.entry(recipient).read() + amount); self.emit(Event::Transfer(Transfer { from: sender, to: recipient, value: amount })); } fn spend_allowance( ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256, ) { let current_allowance = self.allowances.entry((owner, spender)).read(); let ONES_MASK = 0xffffffffffffffffffffffffffffffff_u128; let is_unlimited_allowance = current_allowance.low == ONES_MASK && current_allowance.high == ONES_MASK; if !is_unlimited_allowance { self.approve_helper(owner, spender, current_allowance - amount); } } fn approve_helper( ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256, ) { assert(!spender.is_zero(), 'ERC20: approve from 0'); self.allowances.entry((owner, spender)).write(amount); self.emit(Event::Approval(Approval { owner, spender, value: amount })); } } } ================================================ FILE: crates/forge/tests/data/erc20_package/src/lib.cairo ================================================ pub mod erc20; ================================================ FILE: crates/forge/tests/data/erc20_package/tests/test_complex.cairo ================================================ use erc20_package::erc20::{IERC20Dispatcher, IERC20DispatcherTrait}; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ ContractClassTrait, declare, start_cheat_caller_address, stop_cheat_caller_address, test_address, }; use starknet::ContractAddress; const NAME: felt252 = 'TOKEN'; const SYMBOL: felt252 = 'TKN'; const DECIMALS: u8 = 2; const INITIAL_SUPPLY: u256 = 10; fn deploy_erc20( name: felt252, symbol: felt252, decimals: u8, initial_supply: u256, recipient: ContractAddress, ) -> ContractAddress { let contract = declare("ERC20").unwrap().contract_class(); let mut constructor_calldata: Array = array![name, symbol, decimals.into()]; let mut initial_supply_serialized = array![]; initial_supply.serialize(ref initial_supply_serialized); constructor_calldata.append_span(initial_supply_serialized.span()); constructor_calldata.append(recipient.into()); let (address, _) = contract.deploy(@constructor_calldata).unwrap(); address } // Syscalls from constructor are not counted // StorageRead: 22, StorageWrite: 12, EmitEvent: 4, GetExecutionInfo: 3 #[test] fn complex() { let erc20_address = deploy_erc20(NAME, SYMBOL, DECIMALS, INITIAL_SUPPLY, test_address()); let dispatcher = IERC20Dispatcher { contract_address: erc20_address }; let spender: ContractAddress = 123.try_into().unwrap(); // GetExecutionInfo: 1, StorageRead: 4, StorageWrite: 4, EmitEvent: 1 dispatcher.transfer(spender, 2.into()); // StorageRead: 2 let spender_balance = dispatcher.balance_of(spender); assert(spender_balance == 2, 'invalid spender balance'); start_cheat_caller_address(erc20_address, spender); // GetExecutionInfo: 1, StorageRead: 2, StorageWrite: 2, EmitEvent: 1 dispatcher.increase_allowance(test_address(), 2); // StorageRead: 2 let allowance = dispatcher.allowance(spender, test_address()); assert(allowance == 2, 'invalid allowance'); stop_cheat_caller_address(erc20_address); // GetExecutionInfo: 1, StorageRead: 6, StorageWrite: 6, EmitEvent: 2 dispatcher.transfer_from(spender, test_address(), 2); // StorageRead: 2 let allowance = dispatcher.allowance(spender, test_address()); assert(allowance == 0, 'invalid allowance'); // StorageRead: 2 let spender_balance = dispatcher.balance_of(spender); assert(spender_balance == 0, 'invalid spender balance'); // StorageRead: 2 let balance = dispatcher.balance_of(test_address()); assert(balance == INITIAL_SUPPLY, 'invalid balance'); } ================================================ FILE: crates/forge/tests/data/exit_first/Scarb.toml ================================================ [package] name = "exit_first" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.4.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] sierra = true ================================================ FILE: crates/forge/tests/data/exit_first/src/lib.cairo ================================================ pub fn fib(a: felt252, b: felt252, n: felt252) -> felt252 { match n { 0 => a, _ => fib(b, a + b, n - 1), } } ================================================ FILE: crates/forge/tests/data/exit_first/tests/ext_function_test.cairo ================================================ use exit_first::fib; #[test] fn hard_test() { fib(0, 1, 99999999999999999999999); assert(2 == 2, 'simple check'); } #[test] fn simple_test() { fib(0, 1, 3); assert(1 == 2, 'simple check'); } ================================================ FILE: crates/forge/tests/data/features/Scarb.toml ================================================ [package] name = "features" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.4.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] [features] snforge_test_only = [] ================================================ FILE: crates/forge/tests/data/features/src/lib.cairo ================================================ #[starknet::interface] pub trait IContract { fn response(ref self: TContractState) -> felt252; } #[cfg(feature: 'snforge_test_only')] #[starknet::contract] pub mod MockContract { #[storage] struct Storage {} #[abi(embed_v0)] impl IContractImpl of super::IContract { fn response(ref self: ContractState) -> felt252 { super::some_func() } } } fn some_func() -> felt252 { 1234 } ================================================ FILE: crates/forge/tests/data/features/tests/test.cairo ================================================ use features::{IContractDispatcher, IContractDispatcherTrait}; use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; #[cfg(feature: 'snforge_test_only')] fn mock_in_tests() -> felt252 { 999 } #[test] fn test_mock_function() { assert(mock_in_tests() == 999, ''); } #[test] fn test_mock_contract() { let (contract_address, _) = declare("MockContract") .unwrap() .contract_class() .deploy(@array![]) .unwrap(); let response_result = IContractDispatcher { contract_address }.response(); assert(response_result == 1234, ''); } ================================================ FILE: crates/forge/tests/data/file_reading/.cairofmtignore ================================================ tests/test.cairo ================================================ FILE: crates/forge/tests/data/file_reading/Scarb.toml ================================================ [package] name = "file_reading" version = "0.1.0" edition = "2024_07" [[target.starknet-contract]] sierra = true [dependencies] starknet = "2.4.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } ================================================ FILE: crates/forge/tests/data/file_reading/data/json/invalid.json ================================================ 231232 ================================================ FILE: crates/forge/tests/data/file_reading/data/json/nested_valid.json ================================================ { "b": { "d": 1, "c": "test", "e": { "a": 2 } }, "a": 23 } ================================================ FILE: crates/forge/tests/data/file_reading/data/json/valid.json ================================================ { "b": "hello", "a": 1, "c": 3, "d": 1656, "e": " ", "f": "hello\nworld", "g": "world", "h": 0, "i": 3618502788666131213697322783095070105623107215331596699973092056135872020480 } ================================================ FILE: crates/forge/tests/data/file_reading/data/json/with_array.json ================================================ { "aa": 2, "array": [1, 23, 4, 5], "string_array": ["test", "test2"] } ================================================ FILE: crates/forge/tests/data/file_reading/data/negative_number.txt ================================================ -1241241 ================================================ FILE: crates/forge/tests/data/file_reading/data/non_ascii.txt ================================================ a £ § ================================================ FILE: crates/forge/tests/data/file_reading/data/valid.txt ================================================ 1 'hello' 3 0x678 ' ' 'hello\nworld' 'world' 0 3618502788666131213697322783095070105623107215331596699973092056135872020480 ================================================ FILE: crates/forge/tests/data/file_reading/src/lib.cairo ================================================ ================================================ FILE: crates/forge/tests/data/file_reading/tests/test.cairo ================================================ use core::array::ArrayTrait; use core::option::OptionTrait; use core::serde::Serde; use snforge_std::fs::{FileParser, FileTrait, read_json, read_txt}; fn compare_with_expected_content(content: Array) { let expected = array![ 1, 'hello', 3, 0x678, ' ', 'hello world', 'world', 0, 3618502788666131213697322783095070105623107215331596699973092056135872020480, ]; assert(content.len() == expected.len(), 'lengths not equal'); let mut i = 0; while i != content.len() { assert(*content[i] == *expected[i], 'unexpected content'); i += 1; }; } fn compare_with_expected_content_json(content: Array) { let hello: ByteArray = "hello"; let hello_world: ByteArray = "hello world"; let world: ByteArray = "world"; let spaces: ByteArray = " "; let mut expected = array![1]; hello.serialize(ref expected); expected.append(3); expected.append(0x678); spaces.serialize(ref expected); hello_world.serialize(ref expected); world.serialize(ref expected); expected.append(0); expected.append(3618502788666131213697322783095070105623107215331596699973092056135872020480); assert(content.len() == expected.len(), 'lengths not equal'); let mut i = 0; while i != content.len() { assert(*content[i] == *expected[i], 'unexpected content'); i += 1; }; } #[derive(Serde, Drop, PartialEq)] struct A { a: u32, nested_b: B, nested_d: D, f: felt252, } #[derive(Serde, Drop, PartialEq)] struct B { nested_c: C, hex: felt252, spaces: felt252, multiline: felt252, } #[derive(Serde, Drop, PartialEq)] struct C { c: u256, } #[derive(Serde, Drop, PartialEq)] struct D { d: u64, e: u8, } #[derive(Serde, Drop, PartialEq)] struct E { a: felt252, b: F, } #[derive(Serde, Drop, PartialEq)] struct F { c: ByteArray, d: u8, e: G, } #[derive(Serde, Drop, PartialEq)] struct G { c: felt252, } #[derive(Serde, Destruct, Drop)] struct Test { a: u8, array: Array, string_array: Array, } #[test] fn json_serialization() { let file = FileTrait::new("data/json/valid.json"); let content = read_json(@file); compare_with_expected_content_json(content); } #[test] fn invalid_json() { let file = FileTrait::new("data/json/invalid.json"); read_json(@file); assert(1 == 1, ''); } #[test] fn json_with_array() { let file = FileTrait::new("data/json/with_array.json"); let content = FileParser::::parse_json(@file).unwrap(); let string_array = array!["test", "test2"]; assert(*content.array[0] == 1, '1'); assert(*content.array[1] == 23, '23'); assert(content.string_array == string_array, 'string_array'); } #[test] fn json_deserialization() { let file = FileTrait::new("data/json/nested_valid.json"); let content = FileParser::::parse_json(@file).unwrap(); let mut output_array = ArrayTrait::new(); content.serialize(ref output_array); assert(content.a == 23, ''); assert(content.b.c == "test", ''); assert(content.b.e.c == 2, ''); } #[test] fn json_non_existent() { let file = FileTrait::new("data/non_existent.json"); read_json(@file); assert(1 == 1, ''); } #[test] fn valid_content_and_same_content_no_matter_newlines() { let file = FileTrait::new("data/valid.txt"); let content = FileParser::::parse_txt(@file).unwrap(); let expected = A { a: 1, nested_b: B { nested_c: C { c: u256 { low: 'hello', high: 3 } }, hex: 0x678, spaces: ' ', multiline: 'hello\nworld', }, nested_d: D { d: 'world', e: 0 }, f: 3618502788666131213697322783095070105623107215331596699973092056135872020480, }; assert(content.f == expected.f, '') } #[test] fn serialization() { let file = FileTrait::new("data/valid.txt"); let content = read_txt(@file); compare_with_expected_content(content); } #[test] fn valid_content_different_folder() { let file = FileTrait::new("valid_file.txt"); let content = read_txt(@file); let expected = array!['123', '12dsfwe', 124]; assert(content.len() == expected.len(), 'lengths not equal'); let mut i = 0; while i != content.len() { assert(*content[i] == *expected[i], 'unexpected content'); i += 1; }; assert(1 == 1, ''); } #[test] fn non_existent() { let file = FileTrait::new("data/non_existent.txt"); read_txt(@file); assert(1 == 1, ''); } #[test] fn negative_number() { let file = FileTrait::new("data/negative_number.txt"); read_txt(@file); assert(1 == 1, 'negative numbers not allowed'); } #[test] fn non_ascii() { let file = FileTrait::new("data/non_ascii.txt"); read_txt(@file); assert(1 == 1, ''); } ================================================ FILE: crates/forge/tests/data/file_reading/valid_file.txt ================================================ 123 '12dsfwe' 00124 ================================================ FILE: crates/forge/tests/data/forking/.gitignore ================================================ !.snfoundry_cache/ !.snfoundry_cache/* ================================================ FILE: crates/forge/tests/data/forking/.snfoundry_cache/README.md ================================================ This is a fabricated cache file with value for storage changed from real `2` to fake `333`. It is used to verify if the cache is actually used. ================================================ FILE: crates/forge/tests/data/forking/.snfoundry_cache/http___188_34_188_184_7070_rpc_v0_10_54060_v0_59_0.json ================================================ {"cache_version":"0_59_0","storage_at":{"0x202de98471a4fae6bcbabb96cab00437d381abc58b02509043778074d6781e9":{"0x206f38f7e4f15e87567361213c28f235cccdaa1d7fd34c9db1dfe9489c6a091":"0x14d"}},"nonce_at":{},"class_hash_at":{"0x202de98471a4fae6bcbabb96cab00437d381abc58b02509043778074d6781e9":"0x6a7eb29ee38b0a0b198e39ed6ad458d2e460264b463351a0acfc05822d61550"},"compiled_contract_class":{"0x6a7eb29ee38b0a0b198e39ed6ad458d2e460264b463351a0acfc05822d61550":{"sierra_program":["0x1","0x3","0x0","0x2","0x1","0x0","0xff","0x1","0x23","0x52616e6765436865636b","0x0","0x4761734275696c74696e","0x66656c74323532","0x4172726179","0x1","0x2","0x536e617073686f74","0x3","0x537472756374","0x1baeba72e79e9db2587cf44fedb2f3700b2075a5e8e39a562584862c4b71f62","0x4","0x2ee1e2b1b89f8c495f200e4956278a4d47395fe262f27b52e5865c9524c08c3","0x456e756d","0x11c6d8087e00642489f92d2821ad6ebd6532ad1a3b6d12833da6d6810391511","0x6","0x753332","0x53797374656d","0x16a4c8d7c05909052238a862d8cc3e7975bf05a07b3a69c6b28951083a6d672","0xa","0x5","0x9931c641b913035ae674b400b61a51476d506bbe8bba2ff8a6272790aba9e6","0xc","0xb","0x4275696c74696e436f737473","0x117f8dd6812873d3aeeacdfe88181a6eb024b50a122679c11870b3b47a1ec88","0x5af52ee38c32146750e2728e3556e24468de85c9684e8215a6a54f774a0eb9","0xf","0x10","0x3a44698eeaa62b837a805b0dfc46b2c1e4f013d3acf9b3c68ff14f08abc709","0x11","0x10203be321c62a7bd4c060d69539c1fbe065baa9e253c74d2cc48be163e259","0x13","0xcc5e86243f861d2d64b08c35db21013e773ac5cf10097946fe0011304886d5","0x15","0x17b6ecc31946835b0d9d92c2dd7a9c14f29af0371571ae74a1b228828b2242","0x17","0x34f9bd7c6cb2dd4263175964ad75f1ff1461ddc332fbfb274e0fb2a5d7ab968","0x18","0x426f78","0x29d7d57c04a880978e7b3689f6218e507f3be17588744b58dc17762447ad0e7","0x1a","0x123a1e81adcc5bd99f099d588eab8cc3de808fcdce58bd37e7e866729f3bcec","0x1c","0x53746f726167654261736541646472657373","0x53746f7261676541646472657373","0x90d0203c41ad646d024845257a6eceb2f8b59b29ce7420dd518053d2edeedc","0x101dc0399934cc08fa0d6f6f2daead4e4a38cabeea1c743e1fc28d2d6e58e99","0x4e6f6e5a65726f","0x85","0x7265766f6b655f61705f747261636b696e67","0x77697468647261775f676173","0x6272616e63685f616c69676e","0x73746f72655f74656d70","0x66756e6374696f6e5f63616c6c","0x656e756d5f6d61746368","0x7","0x7374727563745f6465636f6e737472756374","0x61727261795f6c656e","0x736e617073686f745f74616b65","0x8","0x64726f70","0x7533325f636f6e7374","0x72656e616d65","0x7533325f6571","0x9","0x61727261795f6e6577","0x66656c743235325f636f6e7374","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x61727261795f617070656e64","0x7374727563745f636f6e737472756374","0x656e756d5f696e6974","0xd","0x6765745f6275696c74696e5f636f737473","0xe","0x77697468647261775f6761735f616c6c","0x12","0x4f7574206f6620676173","0x496e70757420746f6f2073686f727420666f7220617267756d656e7473","0x14","0x16","0x19","0x61727261795f736e617073686f745f706f705f66726f6e74","0x1b","0x6a756d70","0x756e626f78","0x66656c743235325f616464","0x1d","0x50414e4943","0x444159544148","0x64697361626c655f61705f747261636b696e67","0x73746f726167655f626173655f616464726573735f636f6e7374","0x206f38f7e4f15e87567361213c28f235cccdaa1d7fd34c9db1dfe9489c6a091","0x73746f726167655f616464726573735f66726f6d5f62617365","0x1f","0x73746f726167655f726561645f73797363616c6c","0x20","0x73746f726167655f77726974655f73797363616c6c","0x21","0x647570","0x66656c743235325f69735f7a65726f","0x22","0x66656c743235325f737562","0x2fe","0xffffffffffffffff","0x63","0x54","0x24","0x1e","0x25","0x46","0x26","0x27","0x28","0x29","0x2d","0x2e","0x2f","0x30","0x2a","0x2b","0x2c","0x31","0x3f","0x32","0x33","0x34","0x35","0x36","0x37","0x38","0x39","0x3a","0x3b","0x3c","0x3d","0x3e","0x40","0x41","0x42","0x43","0x44","0x45","0x47","0x48","0x49","0x4a","0x4b","0x4c","0x4d","0x4e","0x4f","0x50","0x51","0x52","0x53","0x55","0x56","0x57","0x58","0x59","0x5a","0x5b","0x5c","0x5d","0x5e","0x5f","0xc6","0x90","0xb9","0xb2","0x121","0xf3","0x114","0x10d","0x19d","0x196","0x187","0x157","0x179","0x172","0x60","0x61","0x62","0x64","0x65","0x66","0x67","0x68","0x69","0x1b2","0x1b7","0x1c1","0x1ed","0x1e7","0x203","0x224","0x229","0x245","0x23f","0x6a","0x262","0x6b","0x6c","0x267","0x6d","0x6e","0x6f","0x272","0x70","0x287","0x71","0x72","0x28c","0x73","0x74","0x75","0x297","0x76","0x77","0x78","0x79","0x7a","0x2d7","0x7b","0x7c","0x2af","0x7d","0x7e","0x2cd","0x7f","0x80","0x2c7","0x81","0x2ec","0x82","0x2f8","0x83","0x84","0xd4","0x12f","0x1ab","0x1c8","0x1cc","0x1f5","0x209","0x20f","0x21c","0x24f","0x255","0x278","0x29e","0x2e6","0x2f2","0x1ae7","0x7060f02090e0d02060a0c060b02070a090606080706060502040203020100","0x617061602090e15060d02070a090614060d02090a1302060a021202111006","0x70a18061f061e02090e10061d060d02090a1c061b02070a1a02060a021918","0x62402090e180623062202090e10060d02070a180621062002090e07060d02","0x10062a062902090e07060628180627062602090e250615060d02090a100609","0x2090e090607062f02090e022e022d18062c062b02090e10061c060d02090a","0x60638020606360c0906371506063602350234023332070606310906100630","0x2413d0606363d0606400207063f3d06063e3d06063c0706063b1506063a39","0x606460706063645070644070606431006063e15090637420606360706063e","0x24c4b060636024a4906063606060636060749060748180606471406064707","0x6063e0906063c1f06063e4d060638100906371d0606361d0606471c060647","0x1d06063c4f0706441506063e4e070644020749060748170606471506064709","0x906373d090637090606360706063c2106063a50060638390906371d06063e","0x65318090637250606382706063a52060638140906372306063e5106063842","0x60638060754060748100606470255540606360c0606360207540607480706","0x63a1006063606073906074839060636020739060748070606400706065654","0x606472c06063a58060638490906370257170906371c0606361c06063c1d06","0x20750060748210606471c06063e06074d0607484d06063602074d0607481f","0x37025b510606360607510607485a0706445907064406075006074850060636","0x65c06072506074806075206074852060636020752060748270606474b0906","0x37610606400607610607486106063602076106074802605f060636025e5d07","0x63a1d090637630606400607630607486306063602076306074802621c0906","0x60748026507060664060758060748580606360207580607482c0606472306","0x207510607482306064763060638610606380267060706446606063e020725","0x90609020269060207023910076a150c076907060207060202690602020268","0x66b18066907420610020c0669060c061502423d07690614060c0214066906","0x1c0769064b0642024b06690649063d02490669063d06390202690602070217","0x269064d061402214d0769061f0642021f0669060218020269061c0614021d","0x69072350074b0250066906500649022306690621061702500669061d061702","0x7690627061f022706690607061d0202690618061c02026906020702026c02","0x2a0669062a0623022a0669060250025206690602210202690625064d022551","0x69065806520258066906542c0727022c066906022502540669062a52075102","0x66d0654026306690651061d026106690615062a025f0669060c0615026d06","0x2000669060006580200066906022c020269060207026663615f0c06660669","0x7206610272066906025f020269060207027170076f6e6c07690700150c096d","0x6230276066906730663027506690607061d02740669066e062a0273066906","0x77a0600026c0669066c0615027a7978096906777675740c66027706690618","0x67e066e027e0669060221020269067b066c020269060207027d067c7b0669","0x82067302820669068106720281066906800671020269067f067002807f0769","0x654028606690679061d028506690678062a02840669066c06150283066906","0x69066c061502880669067d065202026906020702878685840c068706690683","0x8a7c890c068b066906880654028a06690679061d027c06690678062a028906","0x623028d0669060278028c06690602210202690618061c020269060207028b","0x26f0669068e8f0727028f0669060225028e0669068d8c0751028d0669068d","0x9306690607061d029206690671062a029106690670061502900669066f0652","0x3d06790202690617064d02026906020702949392910c069406690690065402","0x69695075102960669069606230296066906027a0295066906022102026906","0xc0615029a0669069906520299066906979807270298066906022502970669","0x9b0c069d0669069a0654026b06690607061d029c06690615062a029b066906","0x29f0669060278029e066906022102026906090679020269060207029d6b9c","0x66906a0a1072702a1066906022502a00669069f9e0751029f0669069f0623","0x690607061d02a506690639062a02a406690610061502a3066906a2065202a2","0xc0769070602070602026906020202a7a6a5a40c06a7066906a3065402a606","0x42064202420669063d063d023d06690609063902026906020702391007a815","0x614024b490769061706420217066906021802026906140614021814076906","0x615021c0669061c0649021d0669064b0617021c0669061806170202690649","0x1f022106690607061d0202690602070202a90269071d1c074b020c0669060c","0x6230223066906025002500669060221020269064d064d024d1f0769062106","0x2270669065125072702250669060225025106690623500751022306690623","0x2c0669061f061d025406690615062a022a0669060c06150252066906270652","0x6d0658026d066906022c02026906020702582c542a0c065806690652065402","0x66906025f02026906020702666307aa615f0769076d150c096d026d066906","0x690661062a020269066e067502706e0769066c0674026c0669060006610200","0x27372710969067a79780976027a066906700663027906690607061d027806","0x6690674067b020269060207027506ab74066907730677025f0669065f0615","0x669067d0623020269067b061c027d7b07690676067d027706690602210276","0x67e066e020269067f064d027f7e0769068180077f028106690677067e0280","0x85067302850669068406720284066906830671020269068206700283820769","0x654028906690672061d028806690671062a02870669065f06150286066906","0x69065f0615028a066906750652020269060207027c8988870c067c06690686","0x8d8c8b0c068e0669068a0654028d06690672061d028c06690671062a028b06","0x51026f0669066f0623026f0669060278028f0669060221020269060207028e","0x930669069206520292066906909107270291066906022502900669066f8f07","0x66906930654029606690607061d029506690666062a029406690663061502","0x60278029806690602210202690609067902026906020702979695940c0697","0x9b0727029b0669060225029a06690699980751029906690699062302990669","0x1d029e06690639062a029d066906100615026b0669069c0652029c0669069a","0x602070602026906020202a09f9e9d0c06a00669066b0654029f0669060706","0x420669063d063d023d06690609063902026906020702391007ac150c076907","0x49076906170642021706690602180202690614061402181407690642064202","0x669061c0649021d0669064b0617021c06690618061702026906490614024b","0x690607061d0202690602070202ad0269071d1c074b020c0669060c0615021c","0x66906025002500669060221020269064d064d024d1f07690621061f022106","0x6512507270225066906022502510669062350075102230669062306230223","0x1f061d025406690615062a022a0669060c0615025206690627065202270669","0x6d066906022c02026906020702582c542a0c0658066906520654022c066906","0x5f02026906020702666307ae615f0769076d150c096d026d0669066d065802","0x63020269066e067502706e0769066c0674026c066906000661020006690602","0xaf73066907710681025f0669065f0615027106690672068002720669067006","0x747a07690679066e0279066906022102026906730682020269060207027806","0x7706690676067302760669067506720275066906740671020269067a067002","0x66906770654027e06690607061d027d06690661062a027b0669065f061502","0x2a02810669065f06150280066906780652020269060207027f7e7d7b0c067f","0x20702848382810c0684066906800654028306690607061d02820669066106","0x6868507510286066906860623028606690602780285066906022102026906","0x630615027c0669068906520289066906878807270288066906022502870669","0x8a0c068d0669067c0654028c06690607061d028b06690666062a028a066906","0x28f0669060278028e066906022102026906090679020269060207028d8c8b","0x669066f90072702900669060225026f0669068f8e0751028f0669068f0623","0x690607061d029406690639062a029306690610061502920669069106520291","0xc0769070602070602026906020202969594930c0696066906920654029506","0x90609021706690615062a02180669060c061502026906020702391007b015","0x60207021c06b14b0669071406840214423d09690649171809830249066906","0x639020269060207022106b24d0669071f0686021f1d0769064b0685020269","0x1802026906510614022551076906230642022306690650063d02500669061d","0x17025406690625061702026906520614022a52076906270642022706690602","0x202690602070202b30269072c54074b0254066906540649022c0669062a06","0x20269066d064d026d580769065f061f025f06690607061d020269064d0670","0x2660669066361075102630669066306230263066906025002610669060221","0x700669063d0615026e0669066c0652026c0669066600072702000669060225","0x2737271700c06730669066e0654027206690658061d027106690642062a02","0x7a7907690778423d096d02780669067806580278066906022c020269060207","0x690677067402770669067606610276066906025f02026906020702757407b4","0x807f078702800669064d067e027f0669067d0663020269067b0675027d7b07","0x82020269060207028206b5810669077e06810279066906790615027e066906","0x6710202690684067002858407690683066e02830669060221020269068106","0x2a028906690679061502880669068706730287066906860672028606690685","0x207028b8a7c890c068b066906880654028a06690607061d027c0669067a06","0x7061d028e0669067a062a028d066906790615028c06690682065202026906","0x269064d0670020269060207026f8f8e8d0c066f0669068c0654028f066906","0x92066906919007510291066906910623029106690602780290066906022102","0x6690674061502950669069406520294066906929307270293066906022502","0x999897960c0699066906950654029806690607061d029706690675062a0296","0x6027a029a0669060221020269061d06790202690621064d02026906020702","0x6b0727026b0669060225029c0669069b9a0751029b0669069b0623029b0669","0x1d02a006690642062a029f0669063d0615029e0669069d0652029d0669069c","0x61c065202026906020702a2a1a09f0c06a20669069e065402a10669060706","0xa3065402a606690607061d02a506690642062a02a40669063d061502a30669","0x2b606690602210202690609067902026906020702a7a6a5a40c06a7066906","0x26a066906022502b8066906b7b6075102b7066906b7062302b70669060278","0x6690639062a02bb06690610061502ba066906b9065202b9066906b86a0727","0x606690602063902bebdbcbb0c06be066906ba065402bd06690607061d02bc","0x607067c0215066906090689020269060207020c06bf090707690706068802","0x23d066906028c0202690602070202c006028b023906690615068a02100669","0x14066906100671023906690642068a02100669060c067c02420669063d068d","0x690618068f020269060207021706c11806690739068e021406690614060902","0x61c0691021d066906140609021c0669064b0690024b06690649066f024906","0x4d0692024d066906028c0202690617064d020269060207021f1d07061f0669","0x6066906028c02235007062306690621069102500669061406090221066906","0x695020c066906070694020907070609066906060693020706690602061d02","0x217066906100696021806690606061d021406690602062a0210150769060c","0x67b020269060207024b06c24906690742067702423d390969061718140997","0x2230669063d061d025006690639062a021d066906091c0798021c06690649","0x69a02214d1f096906255123500c9902250669061d06230251066906150696","0x202690654064d02542a07690627069b020269060207025206c32706690721","0x5f0669066d066b026d066906582c079c0258066906028c022c0669062a0661","0x7026663610906660669065f069d02630669064d061d02610669061f062a02","0x69d026e0669064d061d026c0669061f062a020006690652069e0202690602","0x202690609061c0202690615069f02026906020702706e6c09067006690600","0x7806690671069d02730669063d061d027206690639062a02710669064b069e","0x96023d06690606061d023906690602062a0209066906070694027873720906","0x7021806c4140669071006770210150c096906423d39099702420669060906","0x62a024b0669064906a102490669061706a0021706690614067b0202690602","0x69060207021f1d1c09061f0669064b06a2021d06690615061d021c0669060c","0x69064d06a2025006690615061d02210669060c062a024d0669061806a30202","0xc066906028c020906690607060751020706690602066f0223502109062306","0x60221020269060206750210150706100669060c0693021506690609067e02","0x602a50209066906070607510207066906070623020706690602a402060669","0x1007270210066906022502150669060c090751020c0669060c0623020c0669","0x202690602b602420606420669063d06a7023d0669063906a6023906690615","0x6906150689020269060207021006c5150c0769070906880209066906070639","0x202690602070202c606028b024206690639068a023d0669060c067c023906","0x24206690618068a023d06690610067c021806690614068d0214066906028c","0x69060207024b06c74906690742068e021706690617060902170669063d0671","0x66906020615021f0669060221021d0669061c066f021c06690649068f0202","0x69061d062302270669061f067e0225066906170609025106690606062a0223","0x60207025406c82a0669075006840250214d096906522725512315b7025206","0x615025f0669066d066a026d066906582c07b802582c0769062a0685020269","0x69060207026663610906660669065f06b9026306690621062a02610669064d","0x69060006b9026e06690621062a026c0669064d061502000669065406ba0202","0x7106bb0271066906028c020269064b064d02026906020702706e6c09067006","0x2a0279066906020615027806690673066a0273066906721707b80272066906","0x690602250202690602067502747a790906740669067806b9027a0669060606","0x2150606150669060c06a7020c0669060906a6020906690606070727020706","0x64902150669060218020c0669060906bd020906690602bc0202690607069f","0x18144209ca3d39100969070c1506020cc9020c0669060c06be021506690615","0x24b06690639061d024906690610062a02170669063d06cb02026906020702","0x42062a021d0669061806ce0202690602070202cd06028b021c0669061706cc","0x6d0024d0669061c06cf021c0669061d06cc024b06690614061d0249066906","0x22306690621067b020269060207025006d1210669071f0677021f0669064d","0x520669064b061d022706690649062a02250669065106a102510669062306a0","0x49062a02540669065006a3020269060207022a522709062a0669062506a202","0x6906070695026d582c09066d0669065406a202580669064b061d022c066906","0x66906021802390669061006bd021006690602bc0202690615069f02150c07","0xd3144207690709393d060215d202390669063906be023d0669063d0649023d","0x690642062a021c0669064b06d4024b066906028c0202690602070249171809","0x202690602070202d606028b024d0669061c06d5021f06690614061d021d06","0x4d0669062106d5021f06690617061d021d06690618062a02210669064906d7","0x60207022506da5106690750068102500669062306d902230669064d06d802","0x1d062a022a0669065206dd0252066906270c07dc02270669065106db020269","0x26906020702582c540906580669062a06de022c0669061f061d0254066906","0x610669061f061d025f0669061d062a026d0669062506df020269060c069f02","0x61506580215066906022c0202690602b60263615f0906630669066d06de02","0x769060c06e102026906020702423d07e03910076907150602096d02150669","0x60c061c020269060207021806e30269071406e2021006690610061502140c","0x100615024b06690649066a0249066906170707b802170669060906e4020269","0x269060207021f1d1c09061f0669064b06b9021d06690639062a021c066906","0x6690721061002214d07690650060c0250066906070609020269061806e502","0x270c07e8022706690602e7022506690623090751020269060207025106e623","0x67e025f0669064d0609026d06690639062a02580669061006150252066906","0x2c0684022c542a09690663615f6d5815b70263066906520623026106690625","0x700669066e6c07b8026e6c076906660685020269060207020006e966066907","0x669067106b9027306690654062a02720669062a0615027106690670066a02","0x62a027a0669062a061502790669060006ba02026906020702787372090678","0x269060c061c0202690602070275747a0906750669067906b9027406690654","0x6690677066a0277066906764d07b802760669065106bb0202690609067002","0x27f7e7d09067f0669067b06b9027e06690639062a027d066906100615027b","0x690602210202690607067902026906090670020269060c061c020269060207","0x69060225028206690681800751028106690681062302810669060278028006","0x42062a02860669063d061502850669068406ba028406690682830727028306","0x207020706eb060669070206ea028887860906880669068506b90287066906","0x2150606150669060c06a2020c0669060906a102090669060606a002026906","0xa2023d0669063906a302390669060710072702100669060225020269060207","0x60606ee020269060207020706ed060669070206ec02420606420669063d06","0x22502026906020702150606150669060c06a7020c0669060906ef02090669","0x606420669063d06a7023d0669063906a60239066906071007270210066906","0x9070602494206020c154206020c0209070602494206020c154206020c1f42","0xf109070602494206020c154206020cf009070602494206020c154206020cd4","0x420609f4090706024d420609071d42060cf3021042074206f2023915071506","0x6020915060209f70251061d06f60602100907090707f5070602504206091d","0x9071c42060cfa070602504206091c420609f906025106091d07f807060252","0x6fd0250066106fc0c0907060252060209070915060215fb09070602584206","0xfe02510663"],"contract_class_version":"0.1.0","entry_points_by_type":{"CONSTRUCTOR":[],"EXTERNAL":[{"selector":"0x19c909057a1fa4e06e930f9418d432c08c64bd4bcd4be37d96beecaf3098412","function_idx":3},{"selector":"0x362398bec32bc0ebb411203221a35a0301193a96f317ebe5e40be9f60d15320","function_idx":0},{"selector":"0x39e11d48192e4333233c7eb19d10ad67c362bb28580c604d67884c85da39695","function_idx":1},{"selector":"0x3c90fa28d76cca3d1f524541612bd9b88cc064457761e09c93d034edf542da4","function_idx":2}],"L1_HANDLER":[]},"abi":"[{\"type\": \"impl\", \"name\": \"IHelloStarknetImpl\", \"interface_name\": \"hello_starknet: :hello_starknet: :IHelloStarknet\"}, {\"type\": \"interface\", \"name\": \"hello_starknet: :hello_starknet: :IHelloStarknet\", \"items\": [{\"type\": \"function\", \"name\": \"increase_balance\", \"inputs\": [{\"name\": \"amount\", \"type\": \"core: :felt252\"}], \"outputs\": [], \"state_mutability\": \"external\"}, {\"type\": \"function\", \"name\": \"get_balance\", \"inputs\": [], \"outputs\": [{\"type\": \"core: :felt252\"}], \"state_mutability\": \"view\"}, {\"type\": \"function\", \"name\": \"do_a_panic\", \"inputs\": [], \"outputs\": [], \"state_mutability\": \"view\"}, {\"type\": \"function\", \"name\": \"do_a_panic_with\", \"inputs\": [{\"name\": \"panic_data\", \"type\": \"core: :array: :Array: :\"}], \"outputs\": [], \"state_mutability\": \"view\"}]}, {\"type\": \"event\", \"name\": \"hello_starknet: :hello_starknet: :HelloStarknet: :Event\", \"kind\": \"enum\", \"variants\": []}]"}},"compiled_class_hash":{}} ================================================ FILE: crates/forge/tests/data/forking/Scarb.toml ================================================ [package] name = "forking" version = "0.1.0" edition = "2024_07" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] starknet = "2.4.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] sierra = true ================================================ FILE: crates/forge/tests/data/forking/src/lib.cairo ================================================ #[cfg(test)] mod tests { const CONTRACT_ADDRESS: felt252 = 0x202de98471a4fae6bcbabb96cab00437d381abc58b02509043778074d6781e9; #[starknet::interface] trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; } #[test] #[fork(url: "{{ NODE_RPC_URL }}", block_number: 54060)] fn test_fork_simple() { let dispatcher = IHelloStarknetDispatcher { contract_address: CONTRACT_ADDRESS.try_into().unwrap(), }; let balance = dispatcher.get_balance(); assert(balance == 0, 'Balance should be 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'Balance should be 100'); } #[test] #[fork(url: "{{ NODE_RPC_URL }}", block_number: 0xd32c)] fn test_fork_simple_number_hex() { let dispatcher = IHelloStarknetDispatcher { contract_address: CONTRACT_ADDRESS.try_into().unwrap(), }; let balance = dispatcher.get_balance(); assert(balance == 0, 'Balance should be 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'Balance should be 100'); } #[test] #[fork( url: "{{ NODE_RPC_URL }}", block_hash: 0x06ae121e46f5375f93b00475fb130348ae38148e121f84b0865e17542e9485de, )] fn test_fork_simple_hash_hex() { let dispatcher = IHelloStarknetDispatcher { contract_address: CONTRACT_ADDRESS.try_into().unwrap(), }; let balance = dispatcher.get_balance(); assert(balance == 0, 'Balance should be 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'Balance should be 100'); } #[test] #[fork( url: "{{ NODE_RPC_URL }}", block_hash: 3021433528476416000728121069095289682281028310523383289416465162415092565470, )] fn test_fork_simple_hash_number() { let dispatcher = IHelloStarknetDispatcher { contract_address: CONTRACT_ADDRESS.try_into().unwrap(), }; let balance = dispatcher.get_balance(); assert(balance == 0, 'Balance should be 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'Balance should be 100'); } #[test] #[fork(url: "{{ NODE_RPC_URL }}", block_tag: latest)] fn print_block_number_when_latest() { assert(1 == 1, ''); } #[test] #[fork(url: "{{ NODE_RPC_URL }}", block_number: 785646)] fn test_track_resources() { let dispatcher = IHelloStarknetDispatcher { contract_address: CONTRACT_ADDRESS.try_into().unwrap(), }; dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'Balance should be 100'); // Default init contract compiled with Sierra version 1.7.0 let hello_starknet_updated: starknet::ContractAddress = 0x01aeb8ac66ebc01c7db8bb8123d3bbf1867be93604f57c540ad70056c04c4cc4 .try_into() .unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address: hello_starknet_updated }; dispatcher.increase_balance(200); let balance = dispatcher.get_balance(); assert(balance == 200, 'Balance should be 200'); } } ================================================ FILE: crates/forge/tests/data/fuzzing/.cairofmtignore ================================================ lib.cairo ================================================ FILE: crates/forge/tests/data/fuzzing/Scarb.toml ================================================ [package] name = "fuzzing" version = "0.1.0" edition = "2024_07" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] starknet = "2.4.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [features] unimplemented = [] ================================================ FILE: crates/forge/tests/data/fuzzing/src/lib.cairo ================================================ pub fn adder(a: felt252, b: felt252) -> felt252 { a + b } pub fn always_five(a: felt252, b: felt252) -> felt252 { 5 } pub fn fib(a: felt252, b: felt252, n: felt252) -> felt252 { match n { 0 => a, _ => fib(b, a + b, n - 1), } } #[cfg(test)] mod tests { use super::{adder, always_five}; #[test] #[fuzzer] fn adding() { let result = adder(2, 3); assert(result == 5, '2 + 3 == 5'); } #[test] #[fuzzer] fn fuzzed_argument(b: felt252) { let result = adder(2, b); assert(result == 2 + b, '2 + b == 2 + b'); } #[test] #[fuzzer] fn fuzzed_both_arguments(a: felt252, b: felt252) { let result = adder(a, b); assert(result == a + b, 'result == a + b'); } #[test] #[fuzzer] fn passing() { let result = always_five(2, 3); assert(result == 5, 'result == 5'); } #[test] #[fuzzer] fn failing_fuzz(a: felt252, b: felt252) { let result = always_five(a, b); assert(result == a + b, 'result == a + b'); } #[test] #[fuzzer(runs: 10, seed: 100)] fn custom_fuzzer_config(b: felt252) { let result = adder(2, b); assert(result == 2 + b, '2 + b == 2 + b'); } #[test] #[fuzzer] fn uint8_arg(a: u8) { if a <= 5_u8 { assert(2 == 2, '2 == 2'); } else { let x = a - 5_u8; assert(x == a - 5_u8, 'x != a - 5'); } } #[test] #[fuzzer] fn uint16_arg(a: u16) { if a <= 5_u16 { assert(2 == 2, '2 == 2'); } else { let x = a - 5_u16; assert(x == a - 5_u16, 'x != a - 5'); } } #[test] #[fuzzer] fn uint32_arg(a: u32) { if a <= 5_u32 { assert(2 == 2, '2 == 2'); } else { let x = a - 5_u32; assert(x == a - 5_u32, 'x != a - 5'); } } #[test] #[fuzzer] fn uint64_arg(a: u64) { if a <= 5_u64 { assert(2 == 2, '2 == 2'); } else { let x = a - 5_u64; assert(x == a - 5_u64, 'x != a - 5'); } } #[test] #[fuzzer] fn uint128_arg(a: u128) { if a <= 5_u128 { assert(2 == 2, '2 == 2'); } else { let x = a - 5_u128; assert(x == a - 5_u128, 'x != a - 5'); } } #[test] #[fuzzer] fn uint256_arg(a: u256) { if a <= 5_u256 { assert(2 == 2, '2 == 2'); } else { let x = a - 5_u256; assert(x == a - 5_u256, 'x != a - 5'); } } #[test] #[fuzzer(runs: 256, seed: 100)] fn fuzzed_while_loop(a: u8) { let mut i: u8 = 0; while i != a { i += 1; }; assert(1 == 1, ''); } } ================================================ FILE: crates/forge/tests/data/fuzzing/tests/exit_first_fuzz.cairo ================================================ use fuzzing::{adder, fib}; #[test] #[fuzzer] fn exit_first_fails_test(b: felt252) { adder(0, 1); assert(1 == 2, '2 + b == 2 + b'); } #[test] #[fuzzer] fn exit_first_hard_test(b: felt252) { fib(0, 1, 30344); assert(2 == 2, 'simple check'); } ================================================ FILE: crates/forge/tests/data/fuzzing/tests/exit_first_single_fail.cairo ================================================ use fuzzing::{adder, fib}; #[test] fn exit_first_fails_test() { adder(0, 1); assert(1 == 2, '2 + b == 2 + b'); } #[test] #[fuzzer] fn exit_first_hard_test(b: felt252) { fib(0, 1, 30344); assert(2 == 2, 'simple check'); } ================================================ FILE: crates/forge/tests/data/fuzzing/tests/generate_arg.cairo ================================================ use snforge_std::fuzzable::generate_arg; #[test] fn use_generate_arg_outside_fuzzer() { let random: usize = generate_arg(100, 999); assert(99 < random && random < 1000, 'value outside correct range'); } #[test] fn generate_arg_incorrect_range() { generate_arg(101, 100); } ================================================ FILE: crates/forge/tests/data/fuzzing/tests/generic_struct.cairo ================================================ use snforge_std::fuzzable::Fuzzable; #[derive(Debug, Drop)] struct Price { amount: T, } impl FuzzablePriceU64 of Fuzzable> { fn blank() -> Price { Price { amount: 0 } } fn generate() -> Price { Price { amount: Fuzzable::generate() } } } #[fuzzer] #[test] fn test_generic(price: Price) { assert(price.amount > 0, 'Wrong price'); } ================================================ FILE: crates/forge/tests/data/fuzzing/tests/incorrect_args.cairo ================================================ use fuzzing::adder; #[derive(Debug, Drop)] struct MyStruct { a: felt252, } #[test] #[fuzzer] fn correct_args(b: felt252) { let result = adder(2, b); assert(result == 2 + b, '2 + b == 2 + b'); } #[cfg(feature: 'unimplemented')] #[test] #[fuzzer] fn incorrect_args(b: felt252, a: MyStruct) { let result = adder(2, b); assert(result == 2 + b, '2 + b == 2 + b'); } ================================================ FILE: crates/forge/tests/data/fuzzing/tests/multiple_attributes.cairo ================================================ use fuzzing::{adder, fib}; #[available_gas(l2_gas: 40000000)] #[fuzzer(runs: 50, seed: 123)] #[test] fn with_available_gas(a: usize) { fib(0, 1, 1000); assert(a >= 0, 'unsigned must be >= 0'); } #[fuzzer] #[test] #[should_panic(expected: 'panic message')] fn with_should_panic(a: u64) { let b: i128 = a.into(); assert(b < 0, 'panic message'); } #[available_gas(l2_gas: 5)] #[should_panic(expected: 'panic message')] #[test] #[fuzzer(runs: 300)] fn with_both(a: felt252, b: u128) { let sum = adder(a, b.into()); assert(sum + 1 == 0, 'panic message'); } #[test] #[fuzzer(seed: 5)] #[ignore] fn ignored(a: felt252) { assert(1 == 1, ''); } ================================================ FILE: crates/forge/tests/data/hello_workspaces/Scarb.toml ================================================ [workspace] members = [ "crates/*", ] [workspace.scripts] test = "snforge" [workspace.tool.snforge] [workspace.dependencies] starknet = "2.4.0" snforge_std = { path = "../../../../../snforge_std" } [workspace.package] version = "0.1.0" [package] name = "hello_workspaces" version.workspace = true edition = "2024_07" [scripts] test.workspace = true [tool] snforge.workspace = true [dependencies] starknet.workspace = true fibonacci = { path = "crates/fibonacci" } addition = { path = "crates/addition" } [dev-dependencies] snforge_std.workspace = true [[target.starknet-contract]] sierra = true ================================================ FILE: crates/forge/tests/data/hello_workspaces/crates/addition/Scarb.toml ================================================ [package] name = "addition" version.workspace = true edition = "2024_07" [dependencies] starknet.workspace = true [dev-dependencies] snforge_std.workspace = true [[target.starknet-contract]] sierra = true [lib] ================================================ FILE: crates/forge/tests/data/hello_workspaces/crates/addition/src/lib.cairo ================================================ pub fn add(a: felt252, b: felt252) -> felt252 { a + b } #[starknet::interface] trait IAdditionContract { fn answer(ref self: TContractState) -> felt252; } #[starknet::contract] mod AdditionContract { use addition::add; #[storage] struct Storage {} #[abi(embed_v0)] impl AdditionContractImpl of super::IAdditionContract { fn answer(ref self: ContractState) -> felt252 { add(10, 20) } } } #[cfg(test)] mod tests { use super::add; #[test] fn it_works() { assert(add(2, 3) == 5, 'it works!'); } } ================================================ FILE: crates/forge/tests/data/hello_workspaces/crates/addition/tests/nested/test_nested.cairo ================================================ use super::foo; #[test] fn test_two() { assert(foo() == 2, 'foo() == 2'); } #[test] fn test_two_and_two() { assert(2 == 2, '2 == 2'); assert(2 == 2, '2 == 2'); } ================================================ FILE: crates/forge/tests/data/hello_workspaces/crates/addition/tests/nested.cairo ================================================ use snforge_std::declare; mod test_nested; fn foo() -> u8 { 2 } #[test] fn simple_case() { assert(1 == 1, 'simple check'); } #[test] fn contract_test() { declare("AdditionContract").unwrap(); } ================================================ FILE: crates/forge/tests/data/hello_workspaces/crates/fibonacci/Scarb.toml ================================================ [package] name = "fibonacci" version.workspace = true edition = "2024_07" [scripts] test.workspace = true [tool] snforge.workspace = true [dependencies] addition = { path = "../addition" } starknet.workspace = true [dev-dependencies] snforge_std.workspace = true [[target.starknet-contract]] sierra = true build-external-contracts = ["addition::AdditionContract"] [lib] ================================================ FILE: crates/forge/tests/data/hello_workspaces/crates/fibonacci/src/lib.cairo ================================================ use addition::add; pub fn fib(a: felt252, b: felt252, n: felt252) -> felt252 { match n { 0 => a, _ => fib(b, add(a, b), n - 1), } } #[starknet::contract] mod FibonacciContract { use addition::add; use fibonacci::fib; #[storage] struct Storage {} #[abi(embed_v0)] fn answer(ref self: ContractState) -> felt252 { add(fib(0, 1, 16), fib(0, 1, 8)) } } #[cfg(test)] mod tests { use snforge_std::declare; use super::fib; #[test] fn it_works() { assert(fib(0, 1, 16) == 987, 'it works!'); } #[test] fn contract_test() { declare("FibonacciContract").unwrap(); declare("AdditionContract").unwrap(); } } ================================================ FILE: crates/forge/tests/data/hello_workspaces/crates/fibonacci/tests/abc/efg.cairo ================================================ #[test] fn efg_test() { assert(super::foo() == 1, ''); } #[test] fn failing_test() { assert(1 == 2, ''); } ================================================ FILE: crates/forge/tests/data/hello_workspaces/crates/fibonacci/tests/abc.cairo ================================================ mod efg; #[test] fn abc_test() { assert(foo() == 1, ''); } pub fn foo() -> u8 { 1 } ================================================ FILE: crates/forge/tests/data/hello_workspaces/crates/fibonacci/tests/lib.cairo ================================================ mod abc; #[test] fn lib_test() { assert(abc::foo() == 1, ''); } ================================================ FILE: crates/forge/tests/data/hello_workspaces/crates/fibonacci/tests/not_collected.cairo ================================================ // should not be collected #[test] fn not_collected() { assert(1 == 1, ''); } ================================================ FILE: crates/forge/tests/data/hello_workspaces/src/lib.cairo ================================================ #[starknet::interface] trait IFibContract { fn answer(ref self: TContractState) -> felt252; } #[starknet::contract] mod FibContract { use addition::add; use fibonacci::fib; #[storage] struct Storage {} #[abi(embed_v0)] impl FibContractImpl of super::IFibContract { fn answer(ref self: ContractState) -> felt252 { add(fib(0, 1, 16), fib(0, 1, 8)) } } } #[cfg(test)] mod tests { #[test] fn test_simple() { assert(1 == 1, 1); } } ================================================ FILE: crates/forge/tests/data/hello_workspaces/tests/test_failing.cairo ================================================ #[test] fn test_failing() { assert(1 == 2, 'failing check'); } #[test] fn test_another_failing() { assert(2 == 3, 'failing check'); } ================================================ FILE: crates/forge/tests/data/nonexistent_selector/Scarb.toml ================================================ [package] name = "nonexistent_selector" version = "0.1.0" edition = "2024_07" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] starknet = "2.4.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] sierra = true [scripts] test = "snforge test" ================================================ FILE: crates/forge/tests/data/nonexistent_selector/src/lib.cairo ================================================ #[starknet::interface] pub trait IMyContract { fn my_function(self: @TState); } #[starknet::contract] pub mod MyContract { use starknet::SyscallResultTrait; use starknet::syscalls::call_contract_syscall; #[storage] pub struct Storage {} #[abi(embed_v0)] impl MyContract of super::IMyContract { fn my_function(self: @ContractState) { let this = starknet::get_contract_address(); let selector = selector!("nonexistent"); let calldata = array![].span(); call_contract_syscall(this, selector, calldata).unwrap_syscall(); } } } ================================================ FILE: crates/forge/tests/data/nonexistent_selector/tests/test_contract.cairo ================================================ use nonexistent_selector::{IMyContractSafeDispatcher, IMyContractSafeDispatcherTrait}; use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; #[test] #[feature("safe_dispatcher")] fn test_unwrapped_call_contract_syscall() { let contract = declare("MyContract").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); let safe_dispatcher = IMyContractSafeDispatcher { contract_address }; let res = safe_dispatcher.my_function(); match res { Result::Ok(_) => panic!("Expected an error"), Result::Err(err_data) => { assert(*err_data.at(0) == 'ENTRYPOINT_NOT_FOUND', *err_data.at(0)); }, }; } ================================================ FILE: crates/forge/tests/data/panic_decoding/Scarb.toml ================================================ [package] name = "panic_decoding" version = "0.1.0" edition = "2024_07" [dependencies] assert_macros = "2.8.5" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } ================================================ FILE: crates/forge/tests/data/panic_decoding/src/lib.cairo ================================================ ================================================ FILE: crates/forge/tests/data/panic_decoding/tests/test_panic_decoding.cairo ================================================ use core::array::ArrayTrait; #[test] fn test_simple() { assert(1 == 1, 'simple check'); } #[test] fn test_panic_decoding() { let max_felt = 3618502788666131213697322783095070105623107215331596699973092056135872020480; let mut arr = ArrayTrait::new(); arr.append(123); arr.append('aaa'); arr.append(max_felt); arr.append(152); arr.append(124); arr.append(149); panic(arr); } #[test] fn test_panic_decoding2() { assert(1 == 2, 128); } #[test] fn test_simple2() { assert(2 == 2, 'simple check'); } #[test] fn test_assert_eq() { let x = 5; let y = 6; assert_eq!(x, y); } #[test] fn test_assert_eq_message() { let x = 5; let y = 6; assert_eq!(x, y, "An identifiable and meaningful error message"); } #[test] fn test_assert() { let x = false; assert!(x); } #[test] fn test_assert_message() { let x = false; assert!(x, "Another identifiable and meaningful error message"); } ================================================ FILE: crates/forge/tests/data/partitioning/.tool-versions ================================================ scarb 2.12.0 ================================================ FILE: crates/forge/tests/data/partitioning/Scarb.toml ================================================ [workspace] members = [ "crates/package_a", "crates/package_b", ] [workspace.dependencies] starknet = "2.12.0" snforge_std = { path = "../../../../../snforge_std" } [workspace.package] version = "0.1.0" [package] name = "partitioning" version.workspace = true edition = "2024_07" [dependencies] starknet.workspace = true package_a = { path = "crates/package_a" } package_b = { path = "crates/package_b" } [dev-dependencies] snforge_std.workspace = true [profile.dev.cairo] unstable-add-statements-functions-debug-info = true unstable-add-statements-code-locations-debug-info = true inlining-strategy = "avoid" ================================================ FILE: crates/forge/tests/data/partitioning/crates/package_a/Scarb.toml ================================================ [package] name = "package_a" version.workspace = true edition = "2024_07" [dependencies] starknet.workspace = true [dev-dependencies] snforge_std.workspace = true ================================================ FILE: crates/forge/tests/data/partitioning/crates/package_a/src/lib.cairo ================================================ #[starknet::contract] pub mod HelloStarknet { #[storage] struct Storage {} } #[cfg(test)] mod tests { #[test] fn test_a() { assert!(1 + 1 == 2); } #[test] #[ignore] // Ignored on purpose fn test_b() { assert!(1 + 1 == 2); } } ================================================ FILE: crates/forge/tests/data/partitioning/crates/package_a/tests/tests.cairo ================================================ #[test] fn test_c() { assert!(1 + 1 == 2); } #[test] fn test_d() { assert!(1 + 1 == 2); } ================================================ FILE: crates/forge/tests/data/partitioning/crates/package_b/Scarb.toml ================================================ [package] name = "package_b" version.workspace = true edition = "2024_07" [dependencies] starknet.workspace = true [dev-dependencies] snforge_std.workspace = true ================================================ FILE: crates/forge/tests/data/partitioning/crates/package_b/src/lib.cairo ================================================ #[starknet::contract] pub mod HelloStarknet { #[storage] struct Storage {} } #[cfg(test)] mod tests { #[test] fn test_e() { assert!(1 + 1 == 2); } #[test] fn test_f() { assert!(1 + 1 == 2); } } ================================================ FILE: crates/forge/tests/data/partitioning/crates/package_b/tests/tests.cairo ================================================ #[test] fn test_g() { assert!(1 + 1 == 2); } #[test] fn test_h() { assert!(1 + 1 == 3); // Failing on purpose } ================================================ FILE: crates/forge/tests/data/partitioning/src/lib.cairo ================================================ #[starknet::contract] pub mod HelloStarknet { #[storage] struct Storage {} } #[cfg(test)] mod tests { #[test] fn test_i() { assert!(1 + 1 == 2); } #[test] fn test_j() { assert!(1 + 1 == 2); } } ================================================ FILE: crates/forge/tests/data/partitioning/tests/tests.cairo ================================================ #[test] fn test_k() { assert!(1 + 1 == 2); } #[test] fn test_l() { assert!(1 + 1 == 2); } #[test] fn test_m() { assert!(1 + 1 == 2); } ================================================ FILE: crates/forge/tests/data/runtime_errors_package/Scarb.toml ================================================ [package] name = "runtime_errors_package" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.4.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] [tool.snforge] exit_first = false ================================================ FILE: crates/forge/tests/data/runtime_errors_package/src/hello_starknet.cairo ================================================ #[starknet::interface] pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); } #[starknet::contract] pub mod HelloStarknet { use core::array::ArrayTrait; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { balance: felt252, } #[abi(embed_v0)] impl IHelloStarknetImpl of super::IHelloStarknet { // Increases the balance by the given amount fn increase_balance(ref self: ContractState, amount: felt252) { self.balance.write(self.balance.read() + amount); } // Returns the current balance fn get_balance(self: @ContractState) -> felt252 { self.balance.read() } // Panics fn do_a_panic(self: @ContractState) { let mut arr = ArrayTrait::new(); arr.append('PANIC'); arr.append('DAYTAH'); panic(arr); } // Panics with given array data fn do_a_panic_with(self: @ContractState, panic_data: Array) { panic(panic_data); } } } ================================================ FILE: crates/forge/tests/data/runtime_errors_package/src/lib.cairo ================================================ pub mod hello_starknet; pub fn fib(a: felt252, b: felt252, n: felt252) -> felt252 { match n { 0 => a, _ => fib(b, a + b, n - 1), } } #[cfg(test)] mod tests { use super::fib; #[test] fn test_fib() { assert(fib(0, 1, 10) == 55, fib(0, 1, 10)); } #[test] #[ignore] fn ignored_test() { assert(1 == 1, 'passing'); } } ================================================ FILE: crates/forge/tests/data/runtime_errors_package/tests/with_error.cairo ================================================ use snforge_std::fs::{FileTrait, read_txt}; #[test] #[should_panic(expected: "No such file or directory")] fn catch_no_such_file() { let file = FileTrait::new("no_way_this_file_exists"); let content = read_txt(@file); assert!(false); } ================================================ FILE: crates/forge/tests/data/should_panic_test/Scarb.toml ================================================ [package] name = "should_panic_test" version = "0.1.0" edition = "2024_07" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } ================================================ FILE: crates/forge/tests/data/should_panic_test/src/lib.cairo ================================================ ================================================ FILE: crates/forge/tests/data/should_panic_test/tests/should_panic_test.cairo ================================================ use core::array::ArrayTrait; use core::panic_with_felt252; #[test] #[should_panic] fn should_panic_no_data() { panic_with_felt252(0); } #[test] #[should_panic(expected: ('panic message',))] fn should_panic_check_data() { panic_with_felt252('panic message'); } #[test] #[should_panic(expected: ('panic message', 'second message'))] fn should_panic_multiple_messages() { let mut arr = ArrayTrait::new(); arr.append('panic message'); arr.append('second message'); panic(arr); } #[test] #[should_panic(expected: (0,))] fn should_panic_with_non_matching_data() { panic_with_felt252('failing check'); } #[test] fn didnt_expect_panic() { panic_with_felt252('unexpected panic'); } #[test] #[should_panic] fn expected_panic_but_didnt() { assert(1 == 1, 'err'); } #[test] #[should_panic(expected: 'panic message')] fn expected_panic_but_didnt_with_expected() { assert(1 == 1, 'err'); } #[test] #[should_panic(expected: ('panic message', 'second message'))] fn expected_panic_but_didnt_with_expected_multiple() { assert(1 == 1, 'err'); } #[test] #[should_panic(expected: 'panic message')] fn should_panic_felt_matching() { assert(1 != 1, 'panic message'); } #[test] #[should_panic(expected: "will panicc")] fn should_panic_not_matching_suffix() { panic!("This will panic"); } #[test] #[should_panic(expected: "will panic")] fn should_panic_match_suffix() { panic!("This will panic"); } #[test] #[should_panic(expected: ('This will panic',))] fn should_panic_byte_array_with_felt() { panic!("This will panic"); } #[test] #[should_panic(expected: "This will panic")] fn should_panic_felt_with_byte_array() { panic_with_felt252('This will panic'); } #[test] #[should_panic(expected: "This will panic")] fn should_panic_expected_contains_error() { panic!("will"); } ================================================ FILE: crates/forge/tests/data/simple_package/Scarb.toml ================================================ [package] name = "simple_package" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.4.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] [tool.snforge] exit_first = false ================================================ FILE: crates/forge/tests/data/simple_package/src/hello_starknet.cairo ================================================ #[starknet::interface] pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); } #[starknet::contract] pub mod HelloStarknet { use core::array::ArrayTrait; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { balance: felt252, } #[abi(embed_v0)] impl IHelloStarknetImpl of super::IHelloStarknet { // Increases the balance by the given amount fn increase_balance(ref self: ContractState, amount: felt252) { self.balance.write(self.balance.read() + amount); } // Returns the current balance fn get_balance(self: @ContractState) -> felt252 { self.balance.read() } // Panics fn do_a_panic(self: @ContractState) { let mut arr = ArrayTrait::new(); arr.append('PANIC'); arr.append('DAYTAH'); panic(arr); } // Panics with given array data fn do_a_panic_with(self: @ContractState, panic_data: Array) { panic(panic_data); } } } ================================================ FILE: crates/forge/tests/data/simple_package/src/lib.cairo ================================================ pub mod hello_starknet; pub fn fib(a: felt252, b: felt252, n: felt252) -> felt252 { match n { 0 => a, _ => fib(b, a + b, n - 1), } } #[cfg(test)] mod tests { use super::fib; #[test] fn test_fib() { assert(fib(0, 1, 10) == 55, fib(0, 1, 10)); } #[test] #[ignore] fn ignored_test() { assert(1 == 1, 'passing'); } } ================================================ FILE: crates/forge/tests/data/simple_package/tests/contract.cairo ================================================ use core::array::ArrayTrait; use core::result::ResultTrait; use simple_package::hello_starknet::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; #[test] fn call_and_invoke() { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; let balance = dispatcher.get_balance(); assert(balance == 0, 'balance == 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'balance == 100'); } ================================================ FILE: crates/forge/tests/data/simple_package/tests/ext_function_test.cairo ================================================ use simple_package::fib; #[test] fn test_my_test() { assert(fib(0, 1, 10) == 55, fib(0, 1, 10)); assert(2 == 2, 'simple check'); } #[test] #[ignore] fn ignored_test() { assert(1 == 2, 'not passing'); } #[test] fn test_simple() { assert(1 == 1, 'simple check'); } ================================================ FILE: crates/forge/tests/data/simple_package/tests/test_simple.cairo ================================================ #[test] fn test_simple() { assert(1 == 1, 'simple check'); } #[test] fn test_simple2() { assert(3 == 3, 'simple check'); } #[test] fn test_two() { assert(2 == 2, '2 == 2'); } #[test] fn test_two_and_two() { assert(2 == 2, '2 == 2'); assert(2 == 2, '2 == 2'); } #[test] fn test_failing() { assert(1 == 2, 'failing check'); } #[test] fn test_another_failing() { assert(2 == 3, 'failing check'); } ================================================ FILE: crates/forge/tests/data/simple_package/tests/without_prefix.cairo ================================================ #[test] fn five() { assert(5 == 5, '5 == 5'); } ================================================ FILE: crates/forge/tests/data/simple_package_with_cheats/Scarb.toml ================================================ [package] name = "simple_package_with_cheats" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.12.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] [tool.snforge] exit_first = false ================================================ FILE: crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo ================================================ use starknet::{ClassHash, ContractAddress}; #[starknet::interface] pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn get_block_number(self: @TContractState) -> u64; fn get_block_hash(self: @TContractState) -> felt252; } #[starknet::interface] pub trait ICheatedConstructor { fn get_stored_block_number(self: @TContractState) -> u64; } #[starknet::contract] pub mod CheatedConstructor { use starknet::get_block_number; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { block_number: u64, } #[constructor] fn constructor(ref self: ContractState) { let block_number = get_block_number(); self.block_number.write(block_number); } #[abi(embed_v0)] impl ICheatedConstructorImpl of super::ICheatedConstructor { fn get_stored_block_number(self: @ContractState) -> u64 { self.block_number.read() } } } #[starknet::interface] pub trait IHelloStarknetProxy { fn get_block_number(self: @TContractState) -> u64; fn get_block_number_library_call(self: @TContractState) -> u64; fn deploy_cheated_constructor_contract( ref self: TContractState, class_hash: ClassHash, salt: felt252, ) -> ContractAddress; } #[starknet::contract] pub mod HelloStarknetProxy { use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; use starknet::syscalls::{deploy_syscall, get_class_hash_at_syscall}; use starknet::{ClassHash, ContractAddress}; use crate::{ IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait, IHelloStarknetLibraryDispatcher, }; #[storage] struct Storage { address: ContractAddress, } #[constructor] fn constructor(ref self: ContractState, address: ContractAddress) { self.address.write(address); } #[abi(embed_v0)] impl IHelloStarknetProxyImpl of super::IHelloStarknetProxy { fn get_block_number(self: @ContractState) -> u64 { let address = self.address.read(); let proxied = IHelloStarknetDispatcher { contract_address: address }; proxied.get_block_number() } fn get_block_number_library_call(self: @ContractState) -> u64 { let address = self.address.read(); let class_hash = get_class_hash_at_syscall(address).unwrap(); let library_dispatcher = IHelloStarknetLibraryDispatcher { class_hash }; library_dispatcher.get_block_number() } fn deploy_cheated_constructor_contract( ref self: ContractState, class_hash: ClassHash, salt: felt252, ) -> ContractAddress { let (address, _) = deploy_syscall(class_hash, salt, array![].span(), false).unwrap(); address } } } #[starknet::contract] pub mod HelloStarknet { use core::array::ArrayTrait; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; use starknet::syscalls::get_block_hash_syscall; use starknet::{SyscallResultTrait, get_block_number}; #[storage] struct Storage { balance: felt252, } #[abi(embed_v0)] impl IHelloStarknetImpl of super::IHelloStarknet { // Increases the balance by the given amount fn increase_balance(ref self: ContractState, amount: felt252) { self.balance.write(self.balance.read() + amount); } // Returns the current balance fn get_balance(self: @ContractState) -> felt252 { self.balance.read() } fn get_block_number(self: @ContractState) -> u64 { get_block_number() } fn get_block_hash(self: @ContractState) -> felt252 { get_block_hash_syscall(100).unwrap_syscall() } } } ================================================ FILE: crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo ================================================ use core::array::ArrayTrait; use core::result::ResultTrait; use simple_package_with_cheats::{ ICheatedConstructorDispatcher, ICheatedConstructorDispatcherTrait, IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait, IHelloStarknetProxyDispatcher, IHelloStarknetProxyDispatcherTrait, }; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ ContractClassTrait, declare, start_cheat_block_hash_global, start_cheat_block_number_global, }; use starknet::contract_address; #[test] fn call_and_invoke() { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; let block_number = dispatcher.get_block_number(); println!("block number {}", block_number); // TODO investigate why the default is 2000 assert(block_number == 2000, 'block_info == 2000'); start_cheat_block_number_global(123); let block_number = dispatcher.get_block_number(); assert(block_number == 123, 'block_info == 123'); } #[test] fn call_and_invoke_proxy() { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let proxy_contract = declare("HelloStarknetProxy").unwrap().contract_class(); let mut constructor_calldata = ArrayTrait::new(); contract_address.serialize(ref constructor_calldata); let (proxy_contract_address, _) = proxy_contract.deploy(@constructor_calldata).unwrap(); let dispatcher = IHelloStarknetProxyDispatcher { contract_address: proxy_contract_address }; let block_number = dispatcher.get_block_number(); assert(block_number == 2000, 'block_number == 2000'); start_cheat_block_number_global(123); let block_number = dispatcher.get_block_number(); assert(block_number == 123, 'block_number == 123'); } #[test] fn call_and_invoke_library_call() { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let proxy_contract = declare("HelloStarknetProxy").unwrap().contract_class(); let mut constructor_calldata = ArrayTrait::new(); contract_address.serialize(ref constructor_calldata); let (proxy_contract_address, _) = proxy_contract.deploy(@constructor_calldata).unwrap(); let dispatcher = IHelloStarknetProxyDispatcher { contract_address: proxy_contract_address }; let block_number = dispatcher.get_block_number_library_call(); assert(block_number == 2000, 'block_number == 2000'); start_cheat_block_number_global(123); let block_number = dispatcher.get_block_number_library_call(); assert(block_number == 123, 'block_number == 123'); } #[test] fn deploy_syscall() { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let proxy_contract = declare("HelloStarknetProxy").unwrap().contract_class(); let mut constructor_calldata = ArrayTrait::new(); contract_address.serialize(ref constructor_calldata); let (proxy_contract_address, _) = proxy_contract.deploy(@constructor_calldata).unwrap(); let dispatcher = IHelloStarknetProxyDispatcher { contract_address: proxy_contract_address }; let class_hash = declare("CheatedConstructor").unwrap().contract_class().class_hash; let contract_address = dispatcher.deploy_cheated_constructor_contract(*class_hash, 111); let cheated_constructor_dispatcher = ICheatedConstructorDispatcher { contract_address }; let block_number = cheated_constructor_dispatcher.get_stored_block_number(); assert(block_number == 2000, 'block_number == 2000'); start_cheat_block_number_global(123); let contract_address = dispatcher.deploy_cheated_constructor_contract(*class_hash, 222); let cheated_constructor_dispatcher = ICheatedConstructorDispatcher { contract_address }; let block_number = cheated_constructor_dispatcher.get_stored_block_number(); assert(block_number == 123, 'block_number == 123'); } #[test] fn block_hash() { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; let block_hash = dispatcher.get_block_hash(); assert(block_hash == 0, 'bloch_hash == 0'); start_cheat_block_hash_global(100, 111); let block_hash = dispatcher.get_block_hash(); assert(block_hash == 111, 'bloch_hash == 111'); } ================================================ FILE: crates/forge/tests/data/steps/Scarb.toml ================================================ [package] name = "steps" version = "0.1.0" edition = "2024_07" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] starknet = "2.4.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] sierra = true ================================================ FILE: crates/forge/tests/data/steps/src/lib.cairo ================================================ #[cfg(test)] mod tests { #[test] fn steps_less_than_10000000() { let mut i = 0; while i != 500_000 { i = i + 1; assert(1 + 1 == 2, 'who knows?'); } } #[test] fn steps_more_than_10000000() { let mut i = 0; while i != 1_000_000 { i = i + 1; assert(1 + 1 == 2, 'who knows?'); } } } ================================================ FILE: crates/forge/tests/data/targets/custom_target/Scarb.toml ================================================ [package] name = "custom_target" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.8.5" [dev-dependencies] snforge_std = { path = "../../../../../../snforge_std" } [[target.starknet-contract]] [[test]] name = "custom_target_integrationtest" kind = "test" source-path = "./tests/tests.cairo" test-type = "integration" [[test]] name = "custom_target_unittest" kind = "test" test-type = "unit" [tool.snforge] exit_first = false ================================================ FILE: crates/forge/tests/data/targets/custom_target/src/lib.cairo ================================================ #[starknet::interface] pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); } #[starknet::contract] pub mod HelloStarknet { use core::array::ArrayTrait; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { balance: felt252, } #[abi(embed_v0)] impl IHelloStarknetImpl of super::IHelloStarknet { // Increases the balance by the given amount fn increase_balance(ref self: ContractState, amount: felt252) { self.balance.write(self.balance.read() + amount); } // Returns the current balance fn get_balance(self: @ContractState) -> felt252 { self.balance.read() } // Panics fn do_a_panic(self: @ContractState) { let mut arr = ArrayTrait::new(); arr.append('PANIC'); arr.append('DAYTAH'); panic(arr); } // Panics with given array data fn do_a_panic_with(self: @ContractState, panic_data: Array) { panic(panic_data); } } } #[cfg(test)] mod tests { use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::declare; #[test] fn declare_contract_from_lib() { let _ = declare("HelloStarknet").unwrap().contract_class(); assert(2 == 2, 'Should declare'); } } ================================================ FILE: crates/forge/tests/data/targets/custom_target/tests/tests.cairo ================================================ use core::array::ArrayTrait; use core::result::ResultTrait; use custom_target::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; #[test] fn declare_and_call_contract_from_lib() { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; let balance = dispatcher.get_balance(); assert(balance == 0, 'balance == 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'balance != 100'); } ================================================ FILE: crates/forge/tests/data/targets/custom_target_custom_names/Scarb.toml ================================================ [package] name = "custom_target_custom_names" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.8.5" [dev-dependencies] snforge_std = { path = "../../../../../../snforge_std" } [[test]] name = "custom_first" kind = "my_kind" source-path = "./tests/tests.cairo" test-type = "integration" build-external-contracts = ["custom_target_custom_names::*"] [[test]] name = "custom_second" kind = "my_other_kind" test-type = "unit" [tool.snforge] exit_first = false ================================================ FILE: crates/forge/tests/data/targets/custom_target_custom_names/src/lib.cairo ================================================ #[starknet::interface] pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); } #[starknet::contract] pub mod HelloStarknet { use core::array::ArrayTrait; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { balance: felt252, } #[abi(embed_v0)] impl IHelloStarknetImpl of super::IHelloStarknet { // Increases the balance by the given amount fn increase_balance(ref self: ContractState, amount: felt252) { self.balance.write(self.balance.read() + amount); } // Returns the current balance fn get_balance(self: @ContractState) -> felt252 { self.balance.read() } // Panics fn do_a_panic(self: @ContractState) { let mut arr = ArrayTrait::new(); arr.append('PANIC'); arr.append('DAYTAH'); panic(arr); } // Panics with given array data fn do_a_panic_with(self: @ContractState, panic_data: Array) { panic(panic_data); } } } #[cfg(test)] mod tests { use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::declare; #[test] fn declare_contract_from_lib() { let _ = declare("HelloStarknet").unwrap().contract_class(); assert(2 == 2, 'Should declare'); } } ================================================ FILE: crates/forge/tests/data/targets/custom_target_custom_names/tests/tests.cairo ================================================ use core::array::ArrayTrait; use core::result::ResultTrait; use custom_target_custom_names::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; #[test] fn declare_and_call_contract_from_lib() { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; let balance = dispatcher.get_balance(); assert(balance == 0, 'balance == 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'balance != 100'); } ================================================ FILE: crates/forge/tests/data/targets/custom_target_only_integration/Scarb.toml ================================================ [package] name = "custom_target_only_integration" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.8.5" [dev-dependencies] snforge_std = { path = "../../../../../../snforge_std" } [[target.starknet-contract]] [[test]] name = "custom_first" kind = "test" source-path = "./tests/tests.cairo" test-type = "integration" build-external-contracts = ["custom_target_only_integration::*"] [tool.snforge] exit_first = false ================================================ FILE: crates/forge/tests/data/targets/custom_target_only_integration/src/lib.cairo ================================================ #[starknet::interface] pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); } #[starknet::contract] pub mod HelloStarknet { use core::array::ArrayTrait; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { balance: felt252, } #[abi(embed_v0)] impl IHelloStarknetImpl of super::IHelloStarknet { // Increases the balance by the given amount fn increase_balance(ref self: ContractState, amount: felt252) { self.balance.write(self.balance.read() + amount); } // Returns the current balance fn get_balance(self: @ContractState) -> felt252 { self.balance.read() } // Panics fn do_a_panic(self: @ContractState) { let mut arr = ArrayTrait::new(); arr.append('PANIC'); arr.append('DAYTAH'); panic(arr); } // Panics with given array data fn do_a_panic_with(self: @ContractState, panic_data: Array) { panic(panic_data); } } } #[cfg(test)] mod tests { use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::declare; #[test] fn declare_contract_from_lib() { let _ = declare("HelloStarknet").unwrap().contract_class(); assert(2 == 2, 'Should declare'); } } ================================================ FILE: crates/forge/tests/data/targets/custom_target_only_integration/tests/tests.cairo ================================================ use core::array::ArrayTrait; use core::result::ResultTrait; use custom_target_only_integration::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; #[test] fn declare_and_call_contract_from_lib() { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; let balance = dispatcher.get_balance(); assert(balance == 0, 'balance == 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'balance != 100'); } ================================================ FILE: crates/forge/tests/data/targets/only_integration/Scarb.toml ================================================ [package] name = "only_integration" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.8.5" [dev-dependencies] snforge_std = { path = "../../../../../../snforge_std" } [[target.starknet-contract]] [tool.snforge] exit_first = false ================================================ FILE: crates/forge/tests/data/targets/only_integration/src/lib.cairo ================================================ #[starknet::interface] pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); } #[starknet::contract] pub mod HelloStarknet { use core::array::ArrayTrait; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { balance: felt252, } #[abi(embed_v0)] impl IHelloStarknetImpl of super::IHelloStarknet { // Increases the balance by the given amount fn increase_balance(ref self: ContractState, amount: felt252) { self.balance.write(self.balance.read() + amount); } // Returns the current balance fn get_balance(self: @ContractState) -> felt252 { self.balance.read() } // Panics fn do_a_panic(self: @ContractState) { let mut arr = ArrayTrait::new(); arr.append('PANIC'); arr.append('DAYTAH'); panic(arr); } // Panics with given array data fn do_a_panic_with(self: @ContractState, panic_data: Array) { panic(panic_data); } } } ================================================ FILE: crates/forge/tests/data/targets/only_integration/tests/tests.cairo ================================================ use core::array::ArrayTrait; use core::result::ResultTrait; use only_integration::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; #[test] fn declare_and_call_contract_from_lib() { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; let balance = dispatcher.get_balance(); assert(balance == 0, 'balance == 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'balance != 100'); } ================================================ FILE: crates/forge/tests/data/targets/only_lib_integration/Scarb.toml ================================================ [package] name = "only_lib_integration" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.8.5" [dev-dependencies] snforge_std = { path = "../../../../../../snforge_std" } [[target.starknet-contract]] [tool.snforge] exit_first = false ================================================ FILE: crates/forge/tests/data/targets/only_lib_integration/src/lib.cairo ================================================ #[starknet::interface] pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); } #[starknet::contract] pub mod HelloStarknet { use core::array::ArrayTrait; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { balance: felt252, } #[abi(embed_v0)] impl IHelloStarknetImpl of super::IHelloStarknet { // Increases the balance by the given amount fn increase_balance(ref self: ContractState, amount: felt252) { self.balance.write(self.balance.read() + amount); } // Returns the current balance fn get_balance(self: @ContractState) -> felt252 { self.balance.read() } // Panics fn do_a_panic(self: @ContractState) { let mut arr = ArrayTrait::new(); arr.append('PANIC'); arr.append('DAYTAH'); panic(arr); } // Panics with given array data fn do_a_panic_with(self: @ContractState, panic_data: Array) { panic(panic_data); } } } ================================================ FILE: crates/forge/tests/data/targets/only_lib_integration/tests/lib.cairo ================================================ mod tests; ================================================ FILE: crates/forge/tests/data/targets/only_lib_integration/tests/tests.cairo ================================================ use core::result::ResultTrait; use only_lib_integration::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; #[test] fn declare_and_call_contract_from_lib() { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; let balance = dispatcher.get_balance(); assert(balance == 0, 'balance == 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'balance != 100'); } ================================================ FILE: crates/forge/tests/data/targets/only_unit/Scarb.toml ================================================ [package] name = "only_unit" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.8.5" [dev-dependencies] snforge_std = { path = "../../../../../../snforge_std" } [[target.starknet-contract]] [tool.snforge] exit_first = false ================================================ FILE: crates/forge/tests/data/targets/only_unit/src/lib.cairo ================================================ #[starknet::interface] trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); } #[starknet::contract] mod HelloStarknet { use core::array::ArrayTrait; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { balance: felt252, } #[abi(embed_v0)] impl IHelloStarknetImpl of super::IHelloStarknet { // Increases the balance by the given amount fn increase_balance(ref self: ContractState, amount: felt252) { self.balance.write(self.balance.read() + amount); } // Returns the current balance fn get_balance(self: @ContractState) -> felt252 { self.balance.read() } // Panics fn do_a_panic(self: @ContractState) { let mut arr = ArrayTrait::new(); arr.append('PANIC'); arr.append('DAYTAH'); panic(arr); } // Panics with given array data fn do_a_panic_with(self: @ContractState, panic_data: Array) { panic(panic_data); } } } #[cfg(test)] mod tests { use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::declare; #[test] fn declare_contract_from_lib() { let _ = declare("HelloStarknet").unwrap().contract_class(); assert(2 == 2, 'Should declare'); } } ================================================ FILE: crates/forge/tests/data/targets/unit_and_integration/Scarb.toml ================================================ [package] name = "unit_and_integration" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.8.5" [dev-dependencies] snforge_std = { path = "../../../../../../snforge_std" } [[target.starknet-contract]] [tool.snforge] exit_first = false ================================================ FILE: crates/forge/tests/data/targets/unit_and_integration/src/lib.cairo ================================================ #[starknet::interface] pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); } #[starknet::contract] pub mod HelloStarknet { use core::array::ArrayTrait; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { balance: felt252, } #[abi(embed_v0)] impl IHelloStarknetImpl of super::IHelloStarknet { // Increases the balance by the given amount fn increase_balance(ref self: ContractState, amount: felt252) { self.balance.write(self.balance.read() + amount); } // Returns the current balance fn get_balance(self: @ContractState) -> felt252 { self.balance.read() } // Panics fn do_a_panic(self: @ContractState) { let mut arr = ArrayTrait::new(); arr.append('PANIC'); arr.append('DAYTAH'); panic(arr); } // Panics with given array data fn do_a_panic_with(self: @ContractState, panic_data: Array) { panic(panic_data); } } } #[cfg(test)] mod tests { use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::declare; #[test] fn declare_contract_from_lib() { let _ = declare("HelloStarknet").unwrap().contract_class(); assert(2 == 2, 'Should declare'); } } ================================================ FILE: crates/forge/tests/data/targets/unit_and_integration/tests/tests.cairo ================================================ use core::array::ArrayTrait; use core::result::ResultTrait; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; use unit_and_integration::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; #[test] fn declare_and_call_contract_from_lib() { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; let balance = dispatcher.get_balance(); assert(balance == 0, 'balance == 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'balance != 100'); } ================================================ FILE: crates/forge/tests/data/targets/unit_and_lib_integration/Scarb.toml ================================================ [package] name = "unit_and_lib_integration" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.8.5" [dev-dependencies] snforge_std = { path = "../../../../../../snforge_std" } [[target.starknet-contract]] [tool.snforge] exit_first = false ================================================ FILE: crates/forge/tests/data/targets/unit_and_lib_integration/src/lib.cairo ================================================ #[starknet::interface] pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); } #[starknet::contract] mod HelloStarknet { use core::array::ArrayTrait; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { balance: felt252, } #[abi(embed_v0)] impl IHelloStarknetImpl of super::IHelloStarknet { // Increases the balance by the given amount fn increase_balance(ref self: ContractState, amount: felt252) { self.balance.write(self.balance.read() + amount); } // Returns the current balance fn get_balance(self: @ContractState) -> felt252 { self.balance.read() } // Panics fn do_a_panic(self: @ContractState) { let mut arr = ArrayTrait::new(); arr.append('PANIC'); arr.append('DAYTAH'); panic(arr); } // Panics with given array data fn do_a_panic_with(self: @ContractState, panic_data: Array) { panic(panic_data); } } } #[cfg(test)] mod tests { use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::declare; #[test] fn declare_contract_from_lib() { let _ = declare("HelloStarknet").unwrap().contract_class(); assert(2 == 2, 'Should declare'); } } ================================================ FILE: crates/forge/tests/data/targets/unit_and_lib_integration/tests/lib.cairo ================================================ mod tests; ================================================ FILE: crates/forge/tests/data/targets/unit_and_lib_integration/tests/tests.cairo ================================================ use core::array::ArrayTrait; use core::result::ResultTrait; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; use unit_and_lib_integration::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; #[test] fn declare_and_call_contract_from_lib() { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; let balance = dispatcher.get_balance(); assert(balance == 0, 'balance == 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'balance != 100'); } ================================================ FILE: crates/forge/tests/data/targets/with_features/Scarb.toml ================================================ [package] name = "with_features" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.8.5" [dev-dependencies] snforge_std = { path = "../../../../../../snforge_std" } [[target.starknet-contract]] [tool.snforge] exit_first = false [features] enable_for_tests = [] ================================================ FILE: crates/forge/tests/data/targets/with_features/src/lib.cairo ================================================ #[starknet::interface] pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); } #[cfg(feature: 'enable_for_tests')] pub mod dummy { #[starknet::contract] pub mod HelloStarknet { use core::array::ArrayTrait; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { balance: felt252, } #[abi(embed_v0)] impl IHelloStarknetImpl of with_features::IHelloStarknet { // Increases the balance by the given amount fn increase_balance(ref self: ContractState, amount: felt252) { self.balance.write(self.balance.read() + amount); } // Returns the current balance fn get_balance(self: @ContractState) -> felt252 { self.balance.read() } // Panics fn do_a_panic(self: @ContractState) { let mut arr = ArrayTrait::new(); arr.append('PANIC'); arr.append('DAYTAH'); panic(arr); } // Panics with given array data fn do_a_panic_with(self: @ContractState, panic_data: Array) { panic(panic_data); } } } } #[cfg(test)] mod tests { use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::declare; #[test] fn declare_contract_from_lib() { let _ = declare("HelloStarknet").unwrap().contract_class(); assert(2 == 2, 'Should declare'); } } ================================================ FILE: crates/forge/tests/data/targets/with_features/tests/tests.cairo ================================================ use core::array::ArrayTrait; use core::result::ResultTrait; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; use with_features::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; #[test] fn declare_and_call_contract_from_lib() { let contract = declare("HelloStarknet").unwrap().contract_class(); let constructor_calldata = @ArrayTrait::new(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; let balance = dispatcher.get_balance(); assert(balance == 0, 'balance == 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'balance != 100'); } ================================================ FILE: crates/forge/tests/data/test_case/Scarb.toml ================================================ [package] name = "test_case" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.10.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } ================================================ FILE: crates/forge/tests/data/test_case/src/lib.cairo ================================================ #[starknet::interface] pub trait IValueStorage { fn set_value(ref self: TContractState, value: u128); fn get_value(self: @TContractState) -> u128; } #[starknet::contract] pub mod ValueStorage { use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { stored_value: u128, } #[abi(embed_v0)] impl ValueStorageImpl of super::IValueStorage { fn set_value(ref self: ContractState, value: u128) { self.stored_value.write(value); } fn get_value(self: @ContractState) -> u128 { self.stored_value.read() } } } pub fn add(a: felt252, b: felt252) -> felt252 { a + b } pub fn fib(a: felt252, b: felt252, n: felt252) -> felt252 { match n { 0 => a, _ => fib(b, a + b, n - 1), } } ================================================ FILE: crates/forge/tests/data/test_case/tests/exit_first.cairo ================================================ use test_case::fib; #[test] #[test_case(0, 1, 3)] #[test_case(0, 1, 500000)] fn test_fib_with_threshold(a: felt252, b: felt252, n: felt252) { let threshold: u256 = 10; let res = fib(a, b, n); let res: u256 = res.try_into().unwrap(); assert!(res > threshold, "result should be greater than threshold"); } ================================================ FILE: crates/forge/tests/data/test_case/tests/multiple_attributes.cairo ================================================ use test_case::add; #[test] #[test_case(1, 2, 3)] #[test_case(3, 4, 7)] #[available_gas(l2_gas: 40000000)] fn with_available_gas(a: felt252, b: felt252, expected: felt252) { let result = add(a, b); assert!(result == expected); } #[available_gas(l2_gas: 10000)] #[test_case(1, 2, 3)] #[test_case(3, 4, 7)] #[test] fn with_available_gas_exceed_limit(a: felt252, b: felt252, expected: felt252) { let result = add(a, b); assert!(result == expected); } #[test] #[should_panic(expected: 'panic message')] #[test_case(1, 2, 3)] #[test_case(3, 4, 7)] fn with_should_panic(a: felt252, b: felt252, expected: felt252) { let x: i8 = -1; assert(x > 0, 'panic message'); } #[test] #[test_case(1, 2)] #[test_case(3, 4)] #[fuzzer] fn with_fuzzer(a: felt252, b: felt252) { add(a, b); } #[test_case(1, 2)] #[test_case(3, 4)] #[test] #[fuzzer] fn with_fuzzer_different_order(a: felt252, b: felt252) { add(a, b); } #[test] #[test_case(1, 2, 3)] #[ignore] #[test_case(3, 4, 7)] fn with_ignore(a: felt252, b: felt252, expected: felt252) { let result = add(a, b); assert!(result == expected); } ================================================ FILE: crates/forge/tests/data/test_case/tests/single_attribute.cairo ================================================ use test_case::add; #[test] #[test_case(1, 2, 3)] #[test_case(3, 4, 7)] #[test_case(5, 6, 11)] fn simple_addition(a: felt252, b: felt252, expected: felt252) { let result = add(a, b); assert!(result == expected); } #[test] #[test_case(name: "one_and_two", 1, 2, 3)] #[test_case(name: "three_and_four", 3, 4, 7)] #[test_case(name: "five_and_six", 5, 6, 11)] fn addition_with_name_arg(a: felt252, b: felt252, expected: felt252) { let result = add(a, b); assert!(result == expected); } ================================================ FILE: crates/forge/tests/data/test_case/tests/with_deploy.cairo ================================================ use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; use test_case::{IValueStorageDispatcher, IValueStorageDispatcherTrait}; #[test] #[test_case(100)] #[test_case(42)] #[test_case(0)] fn with_contract_deploy(value: u128) { let contract = declare("ValueStorage").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); let dispatcher = IValueStorageDispatcher { contract_address }; dispatcher.set_value(value); assert!(dispatcher.get_value() == value, "Value mismatch"); } #[test] #[fuzzer] #[test_case(123)] #[test_case(0)] fn with_fuzzer_and_contract_deploy(value: u128) { let contract = declare("ValueStorage").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); let dispatcher = IValueStorageDispatcher { contract_address }; dispatcher.set_value(value); assert!(dispatcher.get_value() == value, "FAIL"); } ================================================ FILE: crates/forge/tests/data/trace/Scarb.toml ================================================ [package] name = "trace_info" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.4.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] sierra = true ================================================ FILE: crates/forge/tests/data/trace/src/lib.cairo ================================================ use starknet::ContractAddress; #[derive(Drop, Serde, Clone)] pub struct RecursiveCall { pub contract_address: ContractAddress, pub payload: Array, } #[starknet::interface] pub trait RecursiveCaller { fn execute_calls(self: @T, calls: Array); } #[starknet::interface] pub trait Failing { fn fail(self: @TContractState, data: Array); } #[starknet::contract] pub mod SimpleContract { use core::array::ArrayTrait; use super::{ Failing, RecursiveCall, RecursiveCaller, RecursiveCallerDispatcher, RecursiveCallerDispatcherTrait, }; #[storage] struct Storage {} #[abi(embed_v0)] impl RecursiveCallerImpl of RecursiveCaller { fn execute_calls(self: @ContractState, calls: Array) { let mut i = 0; while i < calls.len() { let serviced_call = calls.at(i); RecursiveCallerDispatcher { contract_address: serviced_call.contract_address.clone(), } .execute_calls(serviced_call.payload.clone()); i = i + 1; } } } #[abi(embed_v0)] impl FailingImpl of Failing { fn fail(self: @ContractState, data: Array) { panic(data); } } } ================================================ FILE: crates/forge/tests/data/trace/tests/test_trace.cairo ================================================ use core::panic_with_felt252; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::trace::get_call_trace; use snforge_std::{ContractClassTrait, declare}; use trace_info::{ FailingSafeDispatcher, FailingSafeDispatcherTrait, RecursiveCall, RecursiveCallerDispatcher, RecursiveCallerDispatcherTrait, }; #[test] #[feature("safe_dispatcher")] fn test_trace() { let sc = declare("SimpleContract").unwrap().contract_class(); let (contract_address_A, _) = sc.deploy(@array![]).unwrap(); let (contract_address_B, _) = sc.deploy(@array![]).unwrap(); let (contract_address_C, _) = sc.deploy(@array![]).unwrap(); let calls = array![ RecursiveCall { contract_address: contract_address_B, payload: array![ RecursiveCall { contract_address: contract_address_C, payload: array![] }, RecursiveCall { contract_address: contract_address_C, payload: array![] }, ], }, RecursiveCall { contract_address: contract_address_C, payload: array![] }, ]; RecursiveCallerDispatcher { contract_address: contract_address_A }.execute_calls(calls); let failing_dispatcher = FailingSafeDispatcher { contract_address: contract_address_A }; match failing_dispatcher.fail(array![1, 2, 3, 4, 5]) { Result::Ok(_) => panic_with_felt252('shouldve panicked'), Result::Err(panic_data) => { assert(panic_data == array![1, 2, 3, 4, 5, 0x454e545259504f494e545f4641494c4544], ''); }, } println!("{}", get_call_trace()); } ================================================ FILE: crates/forge/tests/data/trace_resources/Scarb.toml ================================================ [package] name = "trace_resources" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.4.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] ================================================ FILE: crates/forge/tests/data/trace_resources/src/empty.cairo ================================================ #[starknet::contract] mod Empty { #[storage] struct Storage {} } ================================================ FILE: crates/forge/tests/data/trace_resources/src/lib.cairo ================================================ pub mod empty; pub mod trace_dummy; pub mod trace_info_checker; pub mod trace_info_proxy; use starknet::syscalls::{emit_event_syscall, get_block_hash_syscall, send_message_to_l1_syscall}; use starknet::{ClassHash, ContractAddress, SyscallResultTrait, get_contract_address}; pub fn use_builtins_and_syscalls(empty_hash: ClassHash, salt: felt252) -> ContractAddress { 1_u8 >= 1_u8; 1_u8 & 1_u8; core::pedersen::pedersen(1, 2); core::poseidon::hades_permutation(0, 0, 0); let ec_point = core::ec::EcPointTrait::new_from_x(1).unwrap(); core::ec::EcPointTrait::mul(ec_point, 2); core::keccak::keccak_u256s_le_inputs(array![1].span()); get_block_hash_syscall(1).unwrap_syscall(); starknet::syscalls::deploy_syscall(empty_hash, salt, array![].span(), false).unwrap_syscall(); emit_event_syscall(array![1].span(), array![2].span()).unwrap_syscall(); send_message_to_l1_syscall(10, array![20, 30].span()).unwrap_syscall(); let x = starknet::syscalls::storage_read_syscall(0, 10.try_into().unwrap()).unwrap_syscall(); starknet::syscalls::storage_write_syscall(0, 10.try_into().unwrap(), x).unwrap_syscall(); get_contract_address() } ================================================ FILE: crates/forge/tests/data/trace_resources/src/trace_dummy.cairo ================================================ #[starknet::interface] pub trait ITraceDummy { fn from_proxy_dummy(ref self: T, empty_hash: starknet::ClassHash, salt: felt252); } #[starknet::contract] mod TraceDummy { use starknet::ClassHash; use super::super::use_builtins_and_syscalls; #[storage] struct Storage { balance: u8, } #[abi(embed_v0)] impl ITraceDummyImpl of super::ITraceDummy { fn from_proxy_dummy(ref self: ContractState, empty_hash: ClassHash, salt: felt252) { use_builtins_and_syscalls(empty_hash, salt); } } } ================================================ FILE: crates/forge/tests/data/trace_resources/src/trace_info_checker.cairo ================================================ use starknet::ClassHash; #[starknet::interface] pub trait ITraceInfoChecker { fn from_proxy(ref self: T, data: felt252, empty_hash: ClassHash, salt: felt252) -> felt252; fn panic(ref self: T, empty_hash: ClassHash, salt: felt252); } #[starknet::contract] mod TraceInfoChecker { use core::panic_with_felt252; use starknet::{ClassHash, ContractAddress}; use trace_resources::trace_info_proxy::{ ITraceInfoProxyDispatcher, ITraceInfoProxyDispatcherTrait, }; use super::ITraceInfoChecker; use super::super::use_builtins_and_syscalls; #[storage] struct Storage { balance: u8, } #[abi(embed_v0)] impl ITraceInfoChceckerImpl of ITraceInfoChecker { fn from_proxy( ref self: ContractState, data: felt252, empty_hash: ClassHash, salt: felt252, ) -> felt252 { use_builtins_and_syscalls(empty_hash, salt); 100 + data } fn panic(ref self: ContractState, empty_hash: ClassHash, salt: felt252) { use_builtins_and_syscalls(empty_hash, salt); panic_with_felt252('panic'); } } #[l1_handler] fn handle_l1_message( ref self: ContractState, from_address: felt252, proxy_address: ContractAddress, empty_hash: ClassHash, salt: felt252, ) -> felt252 { let my_address = use_builtins_and_syscalls(empty_hash, salt); ITraceInfoProxyDispatcher { contract_address: proxy_address } .regular_call(my_address, empty_hash, 10 * salt) } } ================================================ FILE: crates/forge/tests/data/trace_resources/src/trace_info_proxy.cairo ================================================ use starknet::{ClassHash, ContractAddress}; #[starknet::interface] pub trait ITraceInfoProxy { fn with_libcall( ref self: T, class_hash: ClassHash, empty_hash: ClassHash, salt: felt252, ) -> felt252; fn regular_call( ref self: T, contract_address: ContractAddress, empty_hash: ClassHash, salt: felt252, ) -> felt252; fn with_panic( ref self: T, contract_address: ContractAddress, empty_hash: ClassHash, salt: felt252, ); fn call_two( ref self: T, checker_address: ContractAddress, dummy_address: ContractAddress, empty_hash: ClassHash, salt: felt252, ); } #[starknet::contract] mod TraceInfoProxy { use starknet::{ClassHash, ContractAddress}; use trace_resources::trace_dummy::{ITraceDummyDispatcher, ITraceDummyDispatcherTrait}; use trace_resources::trace_info_checker::{ ITraceInfoCheckerDispatcher, ITraceInfoCheckerDispatcherTrait, ITraceInfoCheckerLibraryDispatcher, }; use super::ITraceInfoProxy; use super::super::use_builtins_and_syscalls; #[storage] struct Storage { balance: u8, } #[constructor] fn constructor( ref self: ContractState, contract_address: ContractAddress, empty_hash: ClassHash, salt: felt252, ) { use_builtins_and_syscalls(empty_hash, salt); ITraceInfoCheckerDispatcher { contract_address }.from_proxy(1, empty_hash, 10 * salt); } #[abi(embed_v0)] impl ITraceInfoProxyImpl of ITraceInfoProxy { fn regular_call( ref self: ContractState, contract_address: ContractAddress, empty_hash: ClassHash, salt: felt252, ) -> felt252 { use_builtins_and_syscalls(empty_hash, salt); ITraceInfoCheckerDispatcher { contract_address }.from_proxy(2, empty_hash, 10 * salt) } fn with_libcall( ref self: ContractState, class_hash: ClassHash, empty_hash: ClassHash, salt: felt252, ) -> felt252 { use_builtins_and_syscalls(empty_hash, salt); ITraceInfoCheckerLibraryDispatcher { class_hash }.from_proxy(3, empty_hash, 10 * salt) } fn with_panic( ref self: ContractState, contract_address: ContractAddress, empty_hash: ClassHash, salt: felt252, ) { use_builtins_and_syscalls(empty_hash, salt); ITraceInfoCheckerDispatcher { contract_address }.panic(empty_hash, 10 * salt); // unreachable code to check if we stop executing after panic ITraceInfoCheckerDispatcher { contract_address }.from_proxy(5, empty_hash, 20 * salt); } fn call_two( ref self: ContractState, checker_address: ContractAddress, dummy_address: ContractAddress, empty_hash: ClassHash, salt: felt252, ) { ITraceInfoCheckerDispatcher { contract_address: checker_address } .from_proxy(42, empty_hash, 10 * salt); use_builtins_and_syscalls(empty_hash, salt); ITraceDummyDispatcher { contract_address: dummy_address } .from_proxy_dummy(empty_hash, 20 * salt); } } } ================================================ FILE: crates/forge/tests/data/trace_resources/tests/lib.cairo ================================================ mod test_call; mod test_deploy; mod test_failed_call; mod test_failed_lib_call; mod test_l1_handler; mod test_lib_call; ================================================ FILE: crates/forge/tests/data/trace_resources/tests/test_call.cairo ================================================ use core::clone::Clone; use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; use trace_resources::trace_info_checker::{ ITraceInfoCheckerDispatcher, ITraceInfoCheckerDispatcherTrait, }; use trace_resources::trace_info_proxy::{ITraceInfoProxyDispatcher, ITraceInfoProxyDispatcherTrait}; #[test] fn test_call() { let empty_hash = declare("Empty").unwrap().contract_class().class_hash.clone(); let proxy = declare("TraceInfoProxy").unwrap().contract_class(); let checker = declare("TraceInfoChecker").unwrap().contract_class().clone(); let dummy = declare("TraceDummy").unwrap().contract_class(); trace_resources::use_builtins_and_syscalls(empty_hash, 7); let (checker_address, _) = checker.deploy(@array![]).unwrap(); let (proxy_address, _) = proxy .deploy(@array![checker_address.into(), empty_hash.into(), 5]) .unwrap(); let (dummy_address, _) = dummy.deploy(@array![]).unwrap(); let proxy_dispatcher = ITraceInfoProxyDispatcher { contract_address: proxy_address }; proxy_dispatcher.regular_call(checker_address, empty_hash, 1); proxy_dispatcher.with_libcall(checker.class_hash, empty_hash, 2); proxy_dispatcher.call_two(checker_address, dummy_address, empty_hash, 3); let chcecker_dispatcher = ITraceInfoCheckerDispatcher { contract_address: checker_address }; chcecker_dispatcher.from_proxy(4, empty_hash, 4); } ================================================ FILE: crates/forge/tests/data/trace_resources/tests/test_deploy.cairo ================================================ use core::clone::Clone; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; use starknet::SyscallResultTrait; use starknet::syscalls::deploy_syscall; #[test] fn test_deploy() { let empty_hash = declare("Empty").unwrap().contract_class().class_hash.clone(); let proxy = declare("TraceInfoProxy").unwrap().contract_class().clone(); let checker = declare("TraceInfoChecker").unwrap().contract_class(); trace_resources::use_builtins_and_syscalls(empty_hash, 7); let (checker_address, _) = checker.deploy(@array![]).unwrap(); proxy.deploy(@array![checker_address.into(), empty_hash.into(), 1]).unwrap(); deploy_syscall( proxy.class_hash, 0, array![checker_address.into(), empty_hash.into(), 2].span(), false, ) .unwrap_syscall(); proxy .deploy_at(@array![checker_address.into(), empty_hash.into(), 3], 123.try_into().unwrap()) .unwrap(); deploy_syscall( proxy.class_hash, 12412, array![checker_address.into(), empty_hash.into(), 4].span(), false, ) .unwrap_syscall(); } ================================================ FILE: crates/forge/tests/data/trace_resources/tests/test_failed_call.cairo ================================================ use core::clone::Clone; use core::panic_with_felt252; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; use trace_resources::trace_info_checker::{ ITraceInfoCheckerSafeDispatcher, ITraceInfoCheckerSafeDispatcherTrait, }; use trace_resources::trace_info_proxy::{ ITraceInfoProxySafeDispatcher, ITraceInfoProxySafeDispatcherTrait, }; #[test] #[feature("safe_dispatcher")] fn test_failed_call() { let empty_hash = declare("Empty").unwrap().contract_class().class_hash.clone(); let proxy = declare("TraceInfoProxy").unwrap().contract_class(); let checker = declare("TraceInfoChecker").unwrap().contract_class(); trace_resources::use_builtins_and_syscalls(empty_hash, 7); let (checker_address, _) = checker.deploy(@array![]).unwrap(); let (proxy_address, _) = proxy .deploy(@array![checker_address.into(), empty_hash.into(), 1]) .unwrap(); let proxy_dispatcher = ITraceInfoProxySafeDispatcher { contract_address: proxy_address }; match proxy_dispatcher.with_panic(checker_address, empty_hash, 2) { Result::Ok(_) => panic_with_felt252('shouldve panicked'), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'panic', *panic_data.at(0)); }, } let chcecker_dispatcher = ITraceInfoCheckerSafeDispatcher { contract_address: checker_address }; match chcecker_dispatcher.panic(empty_hash, 3) { Result::Ok(_) => panic_with_felt252('shouldve panicked'), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'panic', *panic_data.at(0)); }, }; } ================================================ FILE: crates/forge/tests/data/trace_resources/tests/test_failed_lib_call.cairo ================================================ use core::clone::Clone; use core::panic_with_felt252; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; use trace_resources::trace_info_checker::{ ITraceInfoCheckerSafeDispatcherTrait, ITraceInfoCheckerSafeLibraryDispatcher, }; use trace_resources::trace_info_proxy::{ ITraceInfoProxySafeDispatcherTrait, ITraceInfoProxySafeLibraryDispatcher, }; #[test] #[feature("safe_dispatcher")] fn test_failed_lib_call() { let empty_hash = declare("Empty").unwrap().contract_class().class_hash.clone(); let proxy_hash = declare("TraceInfoProxy").unwrap().contract_class().class_hash.clone(); let checker = declare("TraceInfoChecker").unwrap().contract_class().clone(); let (checker_address, _) = checker.deploy(@array![]).unwrap(); trace_resources::use_builtins_and_syscalls(empty_hash, 7); let proxy_lib_dispatcher = ITraceInfoProxySafeLibraryDispatcher { class_hash: proxy_hash }; match proxy_lib_dispatcher.with_panic(checker_address, empty_hash, 1) { Result::Ok(_) => panic_with_felt252('shouldve panicked'), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'panic', *panic_data.at(0)); }, } let chcecker_lib_dispatcher = ITraceInfoCheckerSafeLibraryDispatcher { class_hash: checker.class_hash, }; match chcecker_lib_dispatcher.panic(empty_hash, 2) { Result::Ok(_) => panic_with_felt252('shouldve panicked'), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'panic', *panic_data.at(0)); }, }; } ================================================ FILE: crates/forge/tests/data/trace_resources/tests/test_l1_handler.cairo ================================================ use core::clone::Clone; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, L1HandlerTrait, declare}; #[test] fn test_l1_handler() { let empty_hash = declare("Empty").unwrap().contract_class().class_hash.clone(); let proxy = declare("TraceInfoProxy").unwrap().contract_class(); let checker = declare("TraceInfoChecker").unwrap().contract_class(); trace_resources::use_builtins_and_syscalls(empty_hash, 7); let (checker_address, _) = checker.deploy(@array![]).unwrap(); let (proxy_address, _) = proxy .deploy(@array![checker_address.into(), empty_hash.into(), 1]) .unwrap(); let mut l1_handler = L1HandlerTrait::new(checker_address, selector!("handle_l1_message")); l1_handler.execute(123, array![proxy_address.into(), empty_hash.into(), 2].span()).unwrap(); } ================================================ FILE: crates/forge/tests/data/trace_resources/tests/test_lib_call.cairo ================================================ use core::clone::Clone; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare}; use trace_resources::trace_info_checker::{ ITraceInfoCheckerDispatcherTrait, ITraceInfoCheckerLibraryDispatcher, }; use trace_resources::trace_info_proxy::{ ITraceInfoProxyDispatcherTrait, ITraceInfoProxyLibraryDispatcher, }; #[test] fn test_lib_call() { let empty_hash = declare("Empty").unwrap().contract_class().class_hash.clone(); let proxy_hash = declare("TraceInfoProxy").unwrap().contract_class().class_hash.clone(); let checker = declare("TraceInfoChecker").unwrap().contract_class().clone(); let dummy = declare("TraceDummy").unwrap().contract_class(); trace_resources::use_builtins_and_syscalls(empty_hash, 7); let (checker_address, _) = checker.deploy(@array![]).unwrap(); let (dummy_address, _) = dummy.deploy(@array![]).unwrap(); let proxy_lib_dispatcher = ITraceInfoProxyLibraryDispatcher { class_hash: proxy_hash }; proxy_lib_dispatcher.regular_call(checker_address, empty_hash, 1); proxy_lib_dispatcher.with_libcall(checker.class_hash, empty_hash, 2); proxy_lib_dispatcher.call_two(checker_address, dummy_address, empty_hash, 3); let chcecker_lib_dispatcher = ITraceInfoCheckerLibraryDispatcher { class_hash: checker.class_hash, }; chcecker_lib_dispatcher.from_proxy(4, empty_hash, 4); } ================================================ FILE: crates/forge/tests/data/virtual_workspace/Scarb.toml ================================================ [workspace] members = [ "dummy_name/*", ] [workspace.scripts] test = "snforge" [workspace.tool.snforge] exit_first = true [workspace.dependencies] starknet = "2.4.0" snforge_std = { path = "../../../../../snforge_std" } [workspace.package] version = "0.1.0" edition = "2024_07" [scripts] test.workspace = true [tool] snforge.workspace = true [[target.starknet-contract]] ================================================ FILE: crates/forge/tests/data/virtual_workspace/dummy_name/fibonacci_virtual/Scarb.toml ================================================ [package] name = "fibonacci_virtual" version.workspace = true edition = "2024_07" [scripts] test.workspace = true [tool] snforge.workspace = true [dependencies] subtraction = { path = "../subtraction" } starknet.workspace = true [dev-dependencies] snforge_std.workspace = true [[target.starknet-contract]] build-external-contracts = ["subtraction::SubtractionContract"] ================================================ FILE: crates/forge/tests/data/virtual_workspace/dummy_name/fibonacci_virtual/src/lib.cairo ================================================ use subtraction::subtract; fn fib(a: felt252, b: felt252, n: felt252) -> felt252 { match n { 0 => a, _ => fib(b, subtract(a, -b), n - 1), } } #[starknet::interface] trait IFibonacciContract { fn answer(ref self: TContractState) -> felt252; } #[starknet::contract] mod FibonacciContract { use subtraction::subtract; use super::fib; #[storage] struct Storage {} #[abi(embed_v0)] impl FibonacciContractImpl of super::IFibonacciContract { fn answer(ref self: ContractState) -> felt252 { subtract(fib(0, 1, 16), fib(0, 1, 8)) } } } #[cfg(test)] mod tests { use snforge_std::declare; use super::fib; #[test] fn it_works() { assert(fib(0, 1, 16) == 987, 'it works!'); } #[test] fn contract_test() { declare("FibonacciContract").unwrap(); declare("SubtractionContract").unwrap(); } } ================================================ FILE: crates/forge/tests/data/virtual_workspace/dummy_name/fibonacci_virtual/tests/abc/efg.cairo ================================================ #[test] fn efg_test() { assert(super::foo() == 1, ''); } #[test] fn failing_test() { assert(1 == 2, ''); } ================================================ FILE: crates/forge/tests/data/virtual_workspace/dummy_name/fibonacci_virtual/tests/abc.cairo ================================================ mod efg; #[test] fn abc_test() { assert(foo() == 1, ''); } pub fn foo() -> u8 { 1 } ================================================ FILE: crates/forge/tests/data/virtual_workspace/dummy_name/fibonacci_virtual/tests/lib.cairo ================================================ mod abc; #[test] fn lib_test() { assert(abc::foo() == 1, ''); } ================================================ FILE: crates/forge/tests/data/virtual_workspace/dummy_name/fibonacci_virtual/tests/not_collected.cairo ================================================ // should not be collected #[test] fn not_collected() { assert(1 == 1, ''); } ================================================ FILE: crates/forge/tests/data/virtual_workspace/dummy_name/subtraction/Scarb.toml ================================================ [package] name = "subtraction" version.workspace = true edition = "2024_07" [dependencies] starknet.workspace = true [dev-dependencies] snforge_std.workspace = true [[target.starknet-contract]] [lib] ================================================ FILE: crates/forge/tests/data/virtual_workspace/dummy_name/subtraction/src/lib.cairo ================================================ pub fn subtract(a: felt252, b: felt252) -> felt252 { a - b } #[starknet::interface] trait ISubtractionContract { fn answer(ref self: TContractState) -> felt252; } #[starknet::contract] mod SubtractionContract { use super::subtract; #[storage] struct Storage {} #[abi(embed_v0)] impl SubtractionContractImpl of super::ISubtractionContract { fn answer(ref self: ContractState) -> felt252 { subtract(10, 20) } } } #[cfg(test)] mod tests { use super::subtract; #[test] fn it_works() { assert(subtract(3, 2) == 1, 'it works!'); } } ================================================ FILE: crates/forge/tests/data/virtual_workspace/dummy_name/subtraction/tests/nested/test_nested.cairo ================================================ use super::foo; #[test] fn test_two() { assert(foo() == 2, 'foo() == 2'); } #[test] fn test_two_and_two() { assert(2 == 2, '2 == 2'); assert(2 == 2, '2 == 2'); } ================================================ FILE: crates/forge/tests/data/virtual_workspace/dummy_name/subtraction/tests/nested.cairo ================================================ use snforge_std::declare; mod test_nested; fn foo() -> u8 { 2 } #[test] fn simple_case() { assert(1 == 1, 'simple check'); } #[test] fn contract_test() { declare("SubtractionContract").unwrap(); } ================================================ FILE: crates/forge/tests/data/virtual_workspace/not_collected.cairo ================================================ // This file shouldn't be collected #[test] fn test_simple() { assert(1 == 1, 'simple check'); } ================================================ FILE: crates/forge/tests/data/wasm_oracles/Scarb.toml ================================================ [package] name = "oracles" version = "0.1.0" edition = "2024_07" assets = ["wasm_oracle.wasm"] [dependencies] oracle = "1" starknet = "2.4.0" [dev-dependencies] snforge_std = { path = "../../../../../snforge_std" } ================================================ FILE: crates/forge/tests/data/wasm_oracles/build-fixtures.sh ================================================ #!/usr/bin/env sh set -ex # Run this script to generate wasm fixtures from their sources. # Prebuilt fixtures are expected to be committed to the repository. cd "$(dirname "$0")" cargo build --manifest-path=wasm_oracle/Cargo.toml --release --target wasm32-wasip2 cp wasm_oracle/target/wasm32-wasip2/release/wasm_oracle.wasm . ================================================ FILE: crates/forge/tests/data/wasm_oracles/src/lib.cairo ================================================ ================================================ FILE: crates/forge/tests/data/wasm_oracles/tests/test.cairo ================================================ use core::panics::panic_with_byte_array; mod wasm_oracle { pub fn add(left: u64, right: u64) -> oracle::Result { oracle::invoke("wasm:wasm_oracle.wasm", "add", (left, right)) } pub fn err() -> oracle::Result> { oracle::invoke("wasm:wasm_oracle.wasm", "err", ()) } pub fn panic() -> oracle::Result> { oracle::invoke("wasm:wasm_oracle.wasm", "panic", ()) } } #[test] fn add() { assert!(wasm_oracle::add(2, 3) == Ok(5)); } #[test] fn err() { assert!(wasm_oracle::err() == Ok(Err("failed hard"))); } #[should_panic] #[test] fn panic() { wasm_oracle::panic().unwrap().unwrap(); } #[test] fn unexpected_panic() { wasm_oracle::panic().unwrap().unwrap(); } #[test] fn panic_contents() { let err = wasm_oracle::panic().unwrap_err(); // Panic with error so we get better error than "unwrap failed" panic_with_byte_array(@format!("{}", err)) } ================================================ FILE: crates/forge/tests/data/wasm_oracles/wasm_oracle/Cargo.toml ================================================ [workspace] [package] name = "wasm_oracle" version = "0.1.0" edition = "2024" [lib] crate-type = ["cdylib"] [dependencies] wit-bindgen = "0.46.0" ================================================ FILE: crates/forge/tests/data/wasm_oracles/wasm_oracle/src/lib.rs ================================================ wit_bindgen::generate!({ inline: r#" package testing:oracle; world oracle { export add: func(left: u64, right: u64) -> u64; export err: func() -> result; export panic: func() -> result; } "# }); struct MyOracle; impl Guest for MyOracle { fn add(left: u64, right: u64) -> u64 { left + right } fn err() -> Result { Err("failed hard".into()) } fn panic() -> Result { panic!("panicked") } } export!(MyOracle); ================================================ FILE: crates/forge/tests/e2e/backtrace.rs ================================================ use super::common::runner::{setup_package, test_runner}; use crate::assert_cleaned_output; use assert_fs::TempDir; use assert_fs::fixture::{FileWriteStr, PathChild}; use indoc::indoc; use shared::test_utils::output_assert::{AsOutput, assert_stdout_contains}; use std::fs; use toml_edit::{DocumentMut, value}; #[test] fn test_backtrace_missing_env() { let temp = setup_package("backtrace_vm_error"); let output = test_runner(&temp).assert().failure(); assert_stdout_contains( output, indoc! { "Failure data: Got an exception while executing a hint: Requested contract address 0x0000000000000000000000000000000000000000000000000000000000000123 is not deployed. note: run with `SNFORGE_BACKTRACE=1` environment variable to display a backtrace" }, ); } #[cfg_attr(not(feature = "cairo-native"), ignore)] #[test] fn test_backtrace_native_execution() { let temp = setup_package("backtrace_vm_error"); let output = test_runner(&temp) .arg("--run-native") .env("SNFORGE_BACKTRACE", "1") .assert() .code(2); assert_stdout_contains( output, "[ERROR] Backtrace generation is not supported with `cairo-native` execution\n", ); } #[test] fn snap_test_backtrace() { let temp = setup_package("backtrace_vm_error"); let output = test_runner(&temp) .env("SNFORGE_BACKTRACE", "1") .env("SNFORGE_DETERMINISTIC_OUTPUT", "1") .assert() .failure(); assert_cleaned_output!(output); } #[test] fn snap_test_backtrace_without_inlines() { let temp = setup_package("backtrace_vm_error"); without_inlines(&temp); let output = test_runner(&temp) .env("SNFORGE_BACKTRACE", "1") .env("SNFORGE_DETERMINISTIC_OUTPUT", "1") .assert() .failure(); assert_cleaned_output!(output); } #[test] fn test_wrong_scarb_toml_configuration() { let temp = setup_package("backtrace_vm_error"); let manifest_path = temp.child("Scarb.toml"); let mut scarb_toml = fs::read_to_string(&manifest_path) .unwrap() .parse::() .unwrap(); scarb_toml["profile"]["dev"]["cairo"]["unstable-add-statements-code-locations-debug-info"] = value(false); manifest_path.write_str(&scarb_toml.to_string()).unwrap(); let output = test_runner(&temp) .env("SNFORGE_BACKTRACE", "1") .assert() .failure(); assert_stdout_contains( output, indoc! { "[ERROR] Scarb.toml must have the following Cairo compiler configuration to run backtrace: [profile.dev.cairo] unstable-add-statements-functions-debug-info = true unstable-add-statements-code-locations-debug-info = true panic-backtrace = true ... other entries ..." }, ); } #[test] fn snap_test_backtrace_panic() { let temp = setup_package("backtrace_panic"); let output = test_runner(&temp) .env("SNFORGE_BACKTRACE", "1") .env("SNFORGE_DETERMINISTIC_OUTPUT", "1") .assert() .failure(); assert_cleaned_output!(output); } #[test] fn snap_test_backtrace_panic_without_optimizations() { let temp = setup_package("backtrace_panic"); let manifest_path = temp.child("Scarb.toml"); let mut scarb_toml = fs::read_to_string(&manifest_path) .unwrap() .parse::() .unwrap(); scarb_toml["cairo"]["skip-optimizations"] = value(true); manifest_path.write_str(&scarb_toml.to_string()).unwrap(); let output = test_runner(&temp) .env("SNFORGE_BACKTRACE", "1") .env("SNFORGE_DETERMINISTIC_OUTPUT", "1") .assert() .failure(); assert_cleaned_output!(output); } #[test] fn snap_test_backtrace_panic_without_inlines() { let temp = setup_package("backtrace_panic"); without_inlines(&temp); let output = test_runner(&temp) .env("SNFORGE_BACKTRACE", "1") .env("SNFORGE_DETERMINISTIC_OUTPUT", "1") .assert() .failure(); assert_cleaned_output!(output); } #[test] fn snap_test_handled_error_not_display() { let temp = setup_package("dispatchers"); let output = test_runner(&temp) .arg("test_handle_and_panic") .env("SNFORGE_BACKTRACE", "1") .env("SNFORGE_DETERMINISTIC_OUTPUT", "1") .assert() .success(); // Error from the `FailableContract` should not appear in the output assert!( !output .as_stdout() .contains("error occurred in contract 'FailableContract'") ); assert_cleaned_output!(output); } fn without_inlines(temp_dir: &TempDir) { let manifest_path = temp_dir.child("Scarb.toml"); let mut scarb_toml = fs::read_to_string(&manifest_path) .unwrap() .parse::() .unwrap(); scarb_toml["profile"]["dev"]["cairo"]["inlining-strategy"] = value("avoid"); manifest_path.write_str(&scarb_toml.to_string()).unwrap(); } ================================================ FILE: crates/forge/tests/e2e/build_profile.rs ================================================ use super::common::runner::{setup_package, test_runner}; use forge_runner::profiler_api::PROFILE_DIR; #[test] fn simple_package_build_profile() { let temp = setup_package("simple_package"); test_runner(&temp).arg("--build-profile").assert().code(1); assert!( temp.join(PROFILE_DIR) .join("simple_package_tests_test_fib.pb.gz") .is_file() ); assert!( !temp .join(PROFILE_DIR) .join("simple_package_integrationtest_test_simple_test_failing.pb.gz") .is_file() ); assert!( !temp .join(PROFILE_DIR) .join("simple_package_tests_ignored_test.pb.gz") .is_file() ); assert!( temp.join(PROFILE_DIR) .join("simple_package_integrationtest_ext_function_test_test_simple.pb.gz") .is_file() ); // Check if it doesn't crash in case some data already exists test_runner(&temp).arg("--build-profile").assert().code(1); } #[test] fn simple_package_build_profile_and_pass_args() { let temp = setup_package("simple_package"); test_runner(&temp) .arg("--build-profile") .arg("--") .arg("--output-path") .arg("my_file.pb.gz") .assert() .code(1); assert!(temp.join("my_file.pb.gz").is_file()); } ================================================ FILE: crates/forge/tests/e2e/build_trace_data.rs ================================================ use super::common::runner::{setup_package, test_runner}; use crate::e2e::common::get_trace_from_trace_node; use cairo_annotations::trace_data::{ CallTraceNode as ProfilerCallTraceNode, CallTraceV1 as ProfilerCallTrace, VersionedCallTrace as VersionedProfilerCallTrace, }; use cairo_lang_sierra::program::VersionedProgram; use cairo_lang_starknet_classes::contract_class::ContractClass; use forge_runner::build_trace_data::{TEST_CODE_CONTRACT_NAME, TEST_CODE_FUNCTION_NAME, TRACE_DIR}; use std::fs; #[test] fn simple_package_save_trace() { let temp = setup_package("simple_package"); test_runner(&temp).arg("--save-trace-data").assert().code(1); assert!( temp.join(TRACE_DIR) .join("simple_package_tests_test_fib.json") .exists() ); assert!( !temp .join(TRACE_DIR) .join("simple_package_integrationtest_test_simple_test_failing.json") .exists() ); assert!( !temp .join(TRACE_DIR) .join("simple_package_tests_ignored_test.json") .exists() ); assert!( temp.join(TRACE_DIR) .join("simple_package_integrationtest_ext_function_test_test_simple.json") .exists() ); let trace_data = fs::read_to_string( temp.join(TRACE_DIR) .join("simple_package_integrationtest_ext_function_test_test_simple.json"), ) .unwrap(); let VersionedProfilerCallTrace::V1(call_trace) = serde_json::from_str(&trace_data).expect("Failed to parse call_trace"); assert!(call_trace.nested_calls.is_empty()); // Check if it doesn't crash in case some data already exists test_runner(&temp).arg("--save-trace-data").assert().code(1); } #[test] fn trace_has_contract_and_function_names() { let temp = setup_package("trace"); test_runner(&temp) .arg("--save-trace-data") .assert() .success(); let trace_data = fs::read_to_string( temp.join(TRACE_DIR) .join("trace_info_integrationtest_test_trace_test_trace.json"), ) .unwrap(); let call_trace: ProfilerCallTrace = serde_json::from_str(&trace_data).expect("Failed to parse call_trace"); assert_eq!( call_trace.entry_point.contract_name, Some(String::from(TEST_CODE_CONTRACT_NAME)) ); assert_eq!( call_trace.entry_point.function_name, Some(String::from(TEST_CODE_FUNCTION_NAME)) ); assert_contract_and_function_names(get_trace_from_trace_node(&call_trace.nested_calls[3])); } fn assert_contract_and_function_names(trace: &ProfilerCallTrace) { // every call in this package uses the same contract and function assert_eq!( trace.entry_point.contract_name, Some(String::from("SimpleContract")) ); assert_eq!( trace.entry_point.function_name, Some(String::from("execute_calls")) ); for sub_trace_node in &trace.nested_calls { assert_contract_and_function_names(get_trace_from_trace_node(sub_trace_node)); } } #[test] fn trace_has_cairo_execution_info() { let temp = setup_package("trace"); let snapbox = test_runner(&temp); snapbox .arg("--save-trace-data") .current_dir(&temp) .assert() .success(); let trace_data = fs::read_to_string( temp.join(TRACE_DIR) .join("trace_info_integrationtest_test_trace_test_trace.json"), ) .unwrap(); let call_trace: ProfilerCallTrace = serde_json::from_str(&trace_data).expect("Failed to parse call_trace"); assert_cairo_execution_info_exists(&call_trace); } fn assert_cairo_execution_info_exists(trace: &ProfilerCallTrace) { if let Some(cairo_execution_info) = trace.cairo_execution_info.as_ref() { let sierra_string = fs::read_to_string(&cairo_execution_info.source_sierra_path).unwrap(); assert!( serde_json::from_str::(&sierra_string).is_ok() || serde_json::from_str::(&sierra_string).is_ok() ); } else { assert_eq!(trace.entry_point.function_name, Some(String::from("fail"))); } for sub_trace_node in &trace.nested_calls { if let ProfilerCallTraceNode::EntryPointCall(sub_trace) = sub_trace_node { assert_cairo_execution_info_exists(sub_trace); } } } #[test] fn trace_has_deploy_with_no_constructor_phantom_nodes() { let temp = setup_package("trace"); let snapbox = test_runner(&temp); snapbox .arg("--save-trace-data") .current_dir(&temp) .assert() .success(); let trace_data = fs::read_to_string( temp.join(TRACE_DIR) .join("trace_info_integrationtest_test_trace_test_trace.json"), ) .unwrap(); let call_trace: ProfilerCallTrace = serde_json::from_str(&trace_data).expect("Failed to parse call_trace"); // 3 first calls are deploys with empty constructors matches!( call_trace.nested_calls[0], cairo_annotations::trace_data::CallTraceNode::DeployWithoutConstructor ); matches!( call_trace.nested_calls[1], cairo_annotations::trace_data::CallTraceNode::DeployWithoutConstructor ); matches!( call_trace.nested_calls[2], cairo_annotations::trace_data::CallTraceNode::DeployWithoutConstructor ); } #[test] fn trace_is_produced_even_if_contract_panics() { fn assert_all_execution_info_exists(trace: &ProfilerCallTrace) { assert!(trace.cairo_execution_info.is_some()); for trace_node in &trace.nested_calls { if let ProfilerCallTraceNode::EntryPointCall(trace) = trace_node { assert_all_execution_info_exists(trace); } } } let temp = setup_package("backtrace_panic"); test_runner(&temp) .arg("--save-trace-data") .arg("--ignored") .assert() .success(); let trace_data = fs::read_to_string( temp.join(TRACE_DIR) .join("backtrace_panic_Test_test_contract_panics_with_should_panic.json"), ) .unwrap(); let call_trace: ProfilerCallTrace = serde_json::from_str(&trace_data).unwrap(); assert_all_execution_info_exists(&call_trace); } #[test] fn trace_contains_human_readable_form_of_selectors_for_forks() { fn assert_selectors_in_trace_exist(trace: &ProfilerCallTrace) { assert!(trace.entry_point.function_name.is_some()); for trace_node in &trace.nested_calls { if let ProfilerCallTraceNode::EntryPointCall(trace) = trace_node { assert_selectors_in_trace_exist(trace); } } } let temp = setup_package("debugging_fork"); test_runner(&temp).arg("--save-trace-data").assert().code(1); let trace_data = fs::read_to_string( temp.join(TRACE_DIR) .join("debugging_fork_integrationtest_test_trace_test_debugging_trace_success.json"), ) .unwrap(); let call_trace: ProfilerCallTrace = serde_json::from_str(&trace_data).unwrap(); assert_selectors_in_trace_exist(&call_trace); } ================================================ FILE: crates/forge/tests/e2e/clean.rs ================================================ use super::common::runner::{runner, setup_package, test_runner}; use assert_fs::TempDir; use camino::Utf8PathBuf; use scarb_api::metadata::{MetadataOpts, metadata_with_opts}; use shared::test_utils::output_assert::assert_stdout_contains; use std::path::Path; const COVERAGE_DIR: &str = "coverage"; const PROFILE_DIR: &str = "profile"; const CACHE_DIR: &str = ".snfoundry_cache"; const TRACE_DIR: &str = "snfoundry_trace"; #[expect(clippy::struct_excessive_bools)] #[derive(Debug, PartialEq, Eq, Copy, Clone)] struct CleanComponentsState { coverage: bool, profile: bool, cache: bool, trace: bool, } #[test] #[cfg_attr( feature = "cairo-native", ignore = "Native doesn't support coverage yet" )] fn test_clean_coverage() { let temp_dir = setup_package("coverage_project"); let clean_components_state = CleanComponentsState { coverage: true, profile: false, cache: true, trace: true, }; generate_clean_components(clean_components_state, &temp_dir); runner(&temp_dir) .arg("clean") .arg("coverage") .arg("trace") .assert() .success(); let expected_state = CleanComponentsState { coverage: false, profile: false, cache: true, trace: false, }; assert_eq!( check_clean_components_state(temp_dir.path()), expected_state ); } #[test] #[cfg_attr( feature = "cairo-native", ignore = "Native doesn't support profiler yet" )] fn test_clean_profile() { let temp_dir = setup_package("coverage_project"); let clean_components_state = CleanComponentsState { coverage: false, profile: true, cache: true, trace: true, }; generate_clean_components(clean_components_state, &temp_dir); runner(&temp_dir) .arg("clean") .arg("profile") .assert() .success(); let expected_state = CleanComponentsState { coverage: false, profile: false, cache: true, trace: true, }; assert_eq!( check_clean_components_state(temp_dir.path()), expected_state ); } #[test] fn test_clean_cache() { let temp_dir = setup_package("coverage_project"); let clean_components_state = CleanComponentsState { coverage: false, profile: false, cache: true, trace: false, }; generate_clean_components(clean_components_state, &temp_dir); runner(&temp_dir) .arg("clean") .arg("cache") .assert() .success(); let expected_state = CleanComponentsState { coverage: false, profile: false, cache: false, trace: false, }; assert_eq!( check_clean_components_state(temp_dir.path()), expected_state ); } #[test] #[cfg_attr( feature = "cairo-native", ignore = "Native doesn't support trace, coverage and profiler yet" )] fn test_clean_all() { let temp_dir = setup_package("coverage_project"); let clean_components_state = CleanComponentsState { coverage: true, cache: true, trace: true, profile: true, }; generate_clean_components(clean_components_state, &temp_dir); runner(&temp_dir).arg("clean").arg("all").assert().success(); let expected_state = CleanComponentsState { coverage: false, profile: false, cache: false, trace: false, }; assert_eq!( check_clean_components_state(temp_dir.path()), expected_state ); } #[test] fn test_clean_all_and_component() { let temp_dir = setup_package("coverage_project"); let clean_components_state = CleanComponentsState { coverage: false, cache: true, trace: false, profile: false, }; generate_clean_components(clean_components_state, &temp_dir); // This command should fail because 'all' cannot be combined with other components let output = runner(&temp_dir) .arg("clean") .arg("all") .arg("cache") .assert() .failure(); assert_stdout_contains( output, "[ERROR] The 'all' component cannot be combined with other components", ); } fn generate_clean_components(state: CleanComponentsState, temp_dir: &TempDir) { let run_test_runner = |args: &[&str]| { test_runner(temp_dir).args(args).assert().success(); }; match state { CleanComponentsState { coverage: true, trace: true, cache: true, profile: false, } => run_test_runner(&["--coverage"]), CleanComponentsState { profile: true, trace: true, cache: true, coverage: false, } => run_test_runner(&["--build-profile"]), CleanComponentsState { trace: true, cache: true, profile: false, coverage: false, } => run_test_runner(&["--save-trace-data"]), CleanComponentsState { coverage: false, profile: false, trace: false, cache: true, } => run_test_runner(&[]), CleanComponentsState { coverage: true, profile: true, trace: true, cache: true, } => { run_test_runner(&["--coverage"]); run_test_runner(&["--build-profile"]); } state => { panic!("Invalid state: {state:?}"); } } assert_eq!(check_clean_components_state(temp_dir.path()), state); } fn check_clean_components_state(path: &Path) -> CleanComponentsState { let scarb_metadata = metadata_with_opts(MetadataOpts { no_deps: true, current_dir: Some(path.into()), ..MetadataOpts::default() }) .unwrap(); let workspace_root = scarb_metadata.workspace.root; let packages_root: Vec<_> = scarb_metadata .packages .into_iter() .map(|package_metadata| package_metadata.root) .collect(); CleanComponentsState { coverage: dirs_exist(&packages_root, COVERAGE_DIR), profile: dirs_exist(&packages_root, PROFILE_DIR), cache: dir_exists(&workspace_root, CACHE_DIR), trace: dir_exists(&workspace_root, TRACE_DIR), } } fn dirs_exist(root_dirs: &[Utf8PathBuf], dir_name: &str) -> bool { root_dirs .iter() .all(|root_dir| dir_exists(root_dir, dir_name)) } fn dir_exists(dir: &Utf8PathBuf, dir_name: &str) -> bool { dir.join(dir_name).exists() } ================================================ FILE: crates/forge/tests/e2e/code_quality.rs ================================================ use camino::Utf8PathBuf; use packages_validation::check_and_lint; #[test] fn validate_snforge_std() { let package_path = Utf8PathBuf::from("../../snforge_std") .canonicalize() .unwrap() .try_into() .unwrap(); check_and_lint(&package_path); } ================================================ FILE: crates/forge/tests/e2e/collection.rs ================================================ use super::common::runner::{setup_package, test_runner}; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; #[test] fn collection_with_lib() { let temp = setup_package("collection_with_lib"); let output = test_runner(&temp).assert().success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 17 test(s) from collection_with_lib package Running 12 test(s) from src/ [PASS] collection_with_lib::fab::tests::test_simple [..] [PASS] collection_with_lib::fob::tests::test_simple [..] [PASS] collection_with_lib::tests::test_fib_in_lib [..] [PASS] collection_with_lib::tests::test_simple [..] [PASS] collection_with_lib::fib::tests::test_fab_in_fib [..] [PASS] collection_with_lib::fib::tests::test_fib [..] [PASS] collection_with_lib::fab::fab_impl::tests::test_fab [..] [PASS] collection_with_lib::fib::tests::test_fob_in_fib [..] [PASS] collection_with_lib::tests::test_fob_in_lib [..] [PASS] collection_with_lib::fab::fab_impl::tests::test_super [..] [PASS] collection_with_lib::fob::fob_impl::tests::test_fob [..] [PASS] collection_with_lib::fab::fab_impl::tests::test_how_does_this_work [..] Running 5 test(s) from tests/ [PASS] collection_with_lib_tests::fab::fab_mod::test_fab [..] [PASS] collection_with_lib_tests::fibfabfob::test_fob [..] [PASS] collection_with_lib_tests::fab::test_fab [..] [PASS] collection_with_lib_tests::fibfabfob::test_fib [..] [PASS] collection_with_lib_tests::fibfabfob::test_fab [..] Tests: 17 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn collection_without_lib() { let temp = setup_package("collection_without_lib"); let output = test_runner(&temp).assert().success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 17 test(s) from collection_without_lib package Running 12 test(s) from src/ [PASS] collection_without_lib::fab::tests::test_simple [..] [PASS] collection_without_lib::fab::fab_impl::tests::test_super [..] [PASS] collection_without_lib::tests::test_simple [..] [PASS] collection_without_lib::fab::fab_impl::tests::test_fab [..] [PASS] collection_without_lib::fob::tests::test_simple [..] [PASS] collection_without_lib::fib::tests::test_fob_in_fib [..] [PASS] collection_without_lib::tests::test_fib_in_lib [..] [PASS] collection_without_lib::fib::tests::test_fab_in_fib [..] [PASS] collection_without_lib::tests::test_fob_in_lib [..] [PASS] collection_without_lib::fab::fab_impl::tests::test_how_does_this_work [..] [PASS] collection_without_lib::fob::fob_impl::tests::test_fob [..] [PASS] collection_without_lib::fib::tests::test_fib [..] Running 5 test(s) from tests/ [PASS] collection_without_lib_integrationtest::fibfabfob::test_fab [..] [PASS] collection_without_lib_integrationtest::fab::fab_mod::test_fab [..] [PASS] collection_without_lib_integrationtest::fab::test_fab [..] [PASS] collection_without_lib_integrationtest::fibfabfob::test_fob [..] [PASS] collection_without_lib_integrationtest::fibfabfob::test_fib [..] Tests: 17 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } ================================================ FILE: crates/forge/tests/e2e/color.rs ================================================ use super::common::runner::{setup_package, snforge_test_bin_path}; use snapbox::cmd::{Command as SnapboxCommand, OutputAssert}; fn runner_color(value: &str) -> SnapboxCommand { SnapboxCommand::new(snforge_test_bin_path()) .arg("test") .arg("--color") .arg(value) } fn is_colored(output: &OutputAssert) -> bool { String::from_utf8(output.get_output().stdout.clone()) .unwrap() .contains("\x1b[") } #[test] fn color_always() { let temp = setup_package("simple_package"); let snapbox = runner_color("always"); let output = snapbox.current_dir(&temp).assert().code(1); assert!( is_colored(&output), "output expected to be colored but it is not" ); } #[test] #[ignore = "TODO(2356): Fix this test, compiling of snforge_scarb_plugin is causing issues, but only in CI"] fn color_never() { let temp = setup_package("simple_package"); let snapbox = runner_color("never"); let output = snapbox.current_dir(&temp).assert().code(1); assert!( !is_colored(&output), "output not expected to be colored but it is" ); } ================================================ FILE: crates/forge/tests/e2e/common/mod.rs ================================================ #[cfg(not(feature = "cairo-native"))] use cairo_annotations::trace_data::{ CallTraceNode as ProfilerCallTraceNode, CallTraceV1 as ProfilerCallTrace, }; mod output; pub mod runner; #[cfg(not(feature = "cairo-native"))] pub fn get_trace_from_trace_node(trace_node: &ProfilerCallTraceNode) -> &ProfilerCallTrace { if let ProfilerCallTraceNode::EntryPointCall(trace) = trace_node { trace } else { panic!("Deploy without constructor node was not expected") } } ================================================ FILE: crates/forge/tests/e2e/common/output.rs ================================================ /// Asserts a cleaned `stdout` snapshot using `insta`, filtered for non-deterministic lines. /// Additionally, to ensure deterministic snapshots, tests must be run with: /// `SNFORGE_DETERMINISTIC_OUTPUT=1` /// Uses the current Scarb version as a snapshot suffix. #[macro_export] macro_rules! assert_cleaned_output { ($output:expr) => {{ let stdout = String::from_utf8_lossy(&$output.get_output().stdout); let scarb_version = scarb_api::version::scarb_version() .expect("Failed to get scarb version") .scarb; // Extract the module name from module_path to determine snapshot subdirectory let module_path = module_path!(); let snapshot_subdir = module_path .split("::") .last() // Get the last module name (e.g., "backtrace" or "gas_report") .unwrap_or(""); insta::with_settings!({ snapshot_suffix => scarb_version.to_string(), snapshot_path => format!("./snapshots/{}", snapshot_subdir), filters => vec![ (r"\x1B\[[0-?]*[ -/]*[@-~]", ""), // ANSI escape regex - needed for CI (r"(?m)^\s*(Compiling|Finished|Blocking).*", ""), // scarb output (r"(?m)^\s*(Collected|Running|Tests:|Latest block number).*", ""), // snforge output (r"(?m)^\s*(Updating crates\.io index|warning:.*|This may prevent.*|database is locked.*|Caused by:.*| Error code.*).*\n", ""), // cargo warnings and errors (r"(?m)^\s*(Downloading crates|Downloaded).*\n", ""), // cargo download output (r"at /[^\s:]+/src/", "at [..]"), // absolute paths in backtrace (r"Graph saved to: \/.*", "Graph saved to: [..]") // Inlining optimizer graph saved line ]}, { insta::assert_snapshot!(stdout); } ); }}; } ================================================ FILE: crates/forge/tests/e2e/common/runner.rs ================================================ use crate::utils::{ get_assert_macros_version, get_snforge_std_entry, get_std_name, get_std_path, tempdir_with_tool_versions, }; use assert_fs::TempDir; use assert_fs::fixture::{FileWriteStr, PathChild, PathCopy}; use camino::Utf8PathBuf; use indoc::formatdoc; use shared::command::CommandExt; use shared::test_utils::node_url::node_rpc_url; use snapbox::cargo_bin; use snapbox::cmd::Command as SnapboxCommand; use std::path::{Path, PathBuf}; use std::process::Command; use std::str::FromStr; use std::{env, fs}; use toml_edit::{DocumentMut, value}; use walkdir::WalkDir; pub(crate) fn runner>(temp_dir: T) -> SnapboxCommand { SnapboxCommand::new(snforge_test_bin_path()).current_dir(temp_dir) } // If ran on CI, we want to get the nextest's built binary pub fn snforge_test_bin_path() -> PathBuf { if env::var("NEXTEST").unwrap_or("0".to_string()) == "1" { let snforge_nextest_env = env::var("NEXTEST_BIN_EXE_snforge").expect("No snforge binary for nextest found"); return PathBuf::from(snforge_nextest_env); } cargo_bin!("snforge").to_path_buf() } /// Returns a command that runs `snforge test` in the given temporary directory. /// If the `cairo-native` feature is enabled, it adds the `--run-native` flag. pub(crate) fn test_runner>(temp_dir: T) -> SnapboxCommand { if cfg!(feature = "cairo-native") { test_runner_native(temp_dir) } else { test_runner_vm(temp_dir) } } /// Returns a command that runs `snforge test --run-native` in the given temporary directory. /// /// This is useful for testing behavior that occurs only when the `--run-native` flag is passed. /// If the behavior is not specific to native execution, use `test_runner` instead. pub(crate) fn test_runner_native>(temp_dir: T) -> SnapboxCommand { runner(temp_dir).arg("test").arg("--run-native") } /// Returns a command that runs `snforge test` in the given temporary directory. /// /// This is useful for testing behavior that occurs only in the VM execution. /// If the behavior is not specific to VM execution, use `test_runner` instead. pub(crate) fn test_runner_vm>(temp_dir: T) -> SnapboxCommand { runner(temp_dir).arg("test") } pub(crate) static BASE_FILE_PATTERNS: &[&str] = &["**/*.cairo", "**/*.toml", "**/data/*.json", "**/data/*.txt"]; fn is_package_from_docs_listings(package: &str) -> bool { let package_path = Path::new("../../docs/listings").join(package); fs::canonicalize(&package_path).is_ok() } pub enum Package { Name(String), Path(Utf8PathBuf), } pub(crate) fn setup_package_with_file_patterns( package: Package, file_patterns: &[&str], ) -> TempDir { let temp = tempdir_with_tool_versions().unwrap(); let package_path = match package { Package::Name(name) => { let is_from_docs_listings = is_package_from_docs_listings(&name); if is_from_docs_listings { Utf8PathBuf::from(format!("../../docs/listings/{name}")) } else { Utf8PathBuf::from(format!("tests/data/{name}")) } } Package::Path(path) => Utf8PathBuf::from("tests/data").join(path), }; let package_path = package_path .canonicalize_utf8() .unwrap() .to_string() .replace('\\', "/"); temp.copy_from(package_path, file_patterns).unwrap(); let manifest_path = temp.child("Scarb.toml"); let mut scarb_toml = fs::read_to_string(&manifest_path) .unwrap() .parse::() .unwrap(); let is_workspace = scarb_toml.get("workspace").is_some(); let snforge_std_path = Utf8PathBuf::from_str("../../snforge_std") .unwrap() .canonicalize_utf8() .unwrap() .to_string() .replace('\\', "/"); if is_workspace { scarb_toml["workspace"]["dependencies"]["snforge_std"]["path"] = value(snforge_std_path); } else { scarb_toml["dev-dependencies"]["snforge_std"]["path"] = value(snforge_std_path); } scarb_toml["dependencies"]["starknet"] = value("2.4.0"); scarb_toml["dependencies"]["assert_macros"] = value(get_assert_macros_version().unwrap().to_string()); scarb_toml["target.starknet-contract"]["sierra"] = value(true); manifest_path.write_str(&scarb_toml.to_string()).unwrap(); replace_node_rpc_url_placeholders(temp.path()); temp } pub(crate) fn setup_package(package_name: &str) -> TempDir { setup_package_with_file_patterns(Package::Name(package_name.to_string()), BASE_FILE_PATTERNS) } pub(crate) fn setup_package_at_path(package_path: Utf8PathBuf) -> TempDir { setup_package_with_file_patterns(Package::Path(package_path), BASE_FILE_PATTERNS) } fn replace_node_rpc_url_placeholders(dir_path: &Path) { let url = node_rpc_url(); let temp_dir_files = WalkDir::new(dir_path); for entry in temp_dir_files { let entry = entry.unwrap(); let path = entry.path(); if path.is_file() && path.extension().is_some_and(|ex| ex == "cairo") { let content = fs::read_to_string(path).unwrap(); let modified_content = content.replace("{{ NODE_RPC_URL }}", url.as_str()); fs::write(path, modified_content).unwrap(); } } } pub(crate) fn setup_hello_workspace() -> TempDir { let temp = setup_package_with_file_patterns( Package::Path(Utf8PathBuf::from("hello_workspaces")), &["**/*.cairo", "**/*.toml"], ); let snforge_std_name = get_std_name(); let snforge_std_path = get_std_path().unwrap(); let manifest_path = temp.child("Scarb.toml"); manifest_path .write_str(&formatdoc!( r#" [workspace] members = [ "crates/*", ] [workspace.scripts] test = "snforge" [workspace.tool.snforge] [workspace.dependencies] starknet = "2.4.0" {snforge_std_name} = {{ path = "{snforge_std_path}" }} [workspace.package] version = "0.1.0" [package] name = "hello_workspaces" version.workspace = true [scripts] test.workspace = true [tool] snforge.workspace = true [dependencies] starknet.workspace = true fibonacci = {{ path = "crates/fibonacci" }} addition = {{ path = "crates/addition" }} [dev-dependencies] {snforge_std_name}.workspace = true "#, )) .unwrap(); temp } pub(crate) fn setup_virtual_workspace() -> TempDir { let temp = setup_package_with_file_patterns( Package::Path(Utf8PathBuf::from("virtual_workspace")), &["**/*.cairo", "**/*.toml"], ); let manifest_path = temp.child("Scarb.toml"); manifest_path .write_str(&formatdoc!( r#" [workspace] members = [ "dummy_name/*", ] [workspace.scripts] test = "snforge" [workspace.tool.snforge] [workspace.dependencies] starknet = "2.4.0" {} [workspace.package] version = "0.1.0" [scripts] test.workspace = true [tool] snforge.workspace = true "#, get_snforge_std_entry().unwrap() )) .unwrap(); temp } /// In context of GITHUB actions, get the repository name that triggered the workflow run. /// Locally returns current branch. /// /// `REPO_NAME` environment variable is expected to be in format `/.git`. pub(crate) fn get_remote_url() -> String { let name: &str = "REPO_NAME"; if let Ok(v) = env::var(name) { v } else { let output = Command::new("git") .args(["remote", "get-url", "origin"]) .output_checked() .unwrap(); let output = String::from_utf8(output.stdout).unwrap(); if output.trim().starts_with("https://github.com/") { output .trim() .strip_prefix("https://github.com/") .unwrap() .to_string() } else { output .trim() .strip_prefix("git@github.com:") .unwrap() .strip_suffix(".git") .unwrap() .to_string() } } } /// In the context of GITHUB actions, get the source branch that triggered the workflow run. /// Locally returns current branch. pub(crate) fn get_current_branch() -> String { let name: &str = "BRANCH_NAME"; if let Ok(v) = env::var(name) { v } else { let output = Command::new("git") .args(["rev-parse", "--abbrev-ref", "HEAD"]) .output_checked() .unwrap(); String::from_utf8(output.stdout).unwrap().trim().to_string() } } ================================================ FILE: crates/forge/tests/e2e/completions.rs ================================================ use crate::e2e::common::runner::snforge_test_bin_path; use clap::ValueEnum; use clap_complete::Shell; use indoc::formatdoc; use shared::test_utils::output_assert::assert_stdout_contains; use snapbox::cmd::Command; #[test] fn test_happy_case() { for variant in Shell::value_variants() { let shell = variant.to_string(); let snapbox = Command::new(snforge_test_bin_path()) .arg("completions") .arg(shell.as_str()); snapbox.assert().success(); } } #[test] fn test_generate_completions_unsupported_shell() { // SAFETY: Tests run in parallel and share the same environment variables. // However, this modification applies only to this one test. unsafe { std::env::set_var("SHELL", "/bin/unsupported"); } let snapbox = Command::new(snforge_test_bin_path()).arg("completions"); let output = snapbox.assert().failure(); assert_stdout_contains( output, formatdoc!( r" [ERROR] Unsupported shell " ), ); } ================================================ FILE: crates/forge/tests/e2e/components.rs ================================================ use super::common::runner::{setup_package, test_runner}; #[test] fn contract_components() { let temp = setup_package("component_macros"); test_runner(&temp).assert().success(); } ================================================ FILE: crates/forge/tests/e2e/contract_artifacts.rs ================================================ use crate::e2e::common::runner::{setup_package, test_runner}; use assert_fs::fixture::{FileWriteStr, PathChild}; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; use std::fs; use toml_edit::DocumentMut; #[test] fn unit_and_integration() { let temp = setup_package("targets/unit_and_integration"); let output = test_runner(&temp).assert().code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from unit_and_integration package Running 1 test(s) from tests/ [PASS] unit_and_integration_integrationtest::tests::declare_and_call_contract_from_lib (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) Running 1 test(s) from src/ [PASS] unit_and_integration::tests::declare_contract_from_lib (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) Tests: 2 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn unit_and_lib_integration() { let temp = setup_package("targets/unit_and_lib_integration"); let output = test_runner(&temp).assert().code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from unit_and_lib_integration package Running 1 test(s) from tests/ [PASS] unit_and_lib_integration_tests::tests::declare_and_call_contract_from_lib (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) Running 1 test(s) from src/ [PASS] unit_and_lib_integration::tests::declare_contract_from_lib (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) Tests: 2 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn only_integration() { let temp = setup_package("targets/only_integration"); let output = test_runner(&temp).assert().code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from only_integration package Running 1 test(s) from tests/ [PASS] only_integration_integrationtest::tests::declare_and_call_contract_from_lib (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) Running 0 test(s) from src/ Tests: 1 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn only_unit() { let temp = setup_package("targets/only_unit"); let output = test_runner(&temp).assert().code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from only_unit package Running 1 test(s) from src/ [PASS] only_unit::tests::declare_contract_from_lib (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) Tests: 1 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn only_lib_integration() { let temp = setup_package("targets/only_lib_integration"); let output = test_runner(&temp).assert().code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from only_lib_integration package Running 1 test(s) from tests/ [PASS] only_lib_integration_tests::tests::declare_and_call_contract_from_lib (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) Running 0 test(s) from src/ Tests: 1 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn with_features() { let temp = setup_package("targets/with_features"); let output = test_runner(&temp) .arg("--features") .arg("enable_for_tests") .assert() .code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from with_features package Running 1 test(s) from tests/ [PASS] with_features_integrationtest::tests::declare_and_call_contract_from_lib (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) Running 1 test(s) from src/ [PASS] with_features::tests::declare_contract_from_lib (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) Tests: 2 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn with_features_fails_without_flag() { let temp = setup_package("targets/with_features"); let output = test_runner(&temp).assert().code(1); assert_stdout_contains( output, indoc! {r#" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from with_features package Running 1 test(s) from tests/ [FAIL] with_features_integrationtest::tests::declare_and_call_contract_from_lib Failure data: "Failed to get contract artifact for name = HelloStarknet." Running 1 test(s) from src/ [FAIL] with_features::tests::declare_contract_from_lib Failure data: "Failed to get contract artifact for name = HelloStarknet." Tests: 0 passed, 2 failed, 0 ignored, 0 filtered out Failures: with_features_integrationtest::tests::declare_and_call_contract_from_lib with_features::tests::declare_contract_from_lib "#}, ); } #[test] // Case: We define custom test target for both unit and integration test types // We do not define `build-external-contracts = ["targets::*"]` for `integration` target // The test still passes because contracts are collected from `unit` target which includes // the contracts from package by the default fn custom_target() { let temp = setup_package("targets/custom_target"); let output = test_runner(&temp).assert().code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from custom_target package Running 1 test(s) from tests/ [PASS] custom_target_integrationtest::tests::declare_and_call_contract_from_lib (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) Running 1 test(s) from src/ [PASS] custom_target::tests::declare_contract_from_lib (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) Tests: 2 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] // Case: We define custom test target for both unit and integration test types // We do not define `build-external-contracts = ["targets::*"]` for `integration` target // The test still passes because contracts are collected from `unit` target which includes // the contracts from package by the default fn custom_target_custom_names() { let temp = setup_package("targets/custom_target_custom_names"); let output = test_runner(&temp).assert().code(0); // Scarb will use the name of the package for unit tests even if custom // name for the unit test target is defined assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from custom_target_custom_names package Running 1 test(s) from tests/ [PASS] custom_first::tests::declare_and_call_contract_from_lib (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) Running 1 test(s) from src/ [PASS] custom_target_custom_names::tests::declare_contract_from_lib (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) Tests: 2 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] // Case: We define custom test target for both unit and integration test types // We must `build-external-contracts = ["targets::*"]` for `integration` target otherwise // they will not be built and included for declaring. fn custom_target_only_integration() { let temp = setup_package("targets/custom_target_only_integration"); let output = test_runner(&temp).assert().code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from custom_target_only_integration package Running 1 test(s) from tests/ [PASS] custom_first::tests::declare_and_call_contract_from_lib (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) Tests: 1 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] // Case: We define custom test target for integration test type // We delete `build-external-contracts = ["targets::*"]` for `integration` so the test fails fn custom_target_only_integration_without_external() { let temp = setup_package("targets/custom_target_only_integration"); // Remove `build-external-contracts` from `[[test]]` target let manifest_path = temp.child("Scarb.toml"); let mut scarb_toml = fs::read_to_string(&manifest_path) .unwrap() .parse::() .unwrap(); let test_target = scarb_toml["test"].as_array_of_tables_mut().unwrap(); assert_eq!(test_target.len(), 1); let test_target = test_target.get_mut(0).unwrap(); test_target.remove("build-external-contracts").unwrap(); manifest_path.write_str(&scarb_toml.to_string()).unwrap(); let output = test_runner(&temp).assert().code(1); assert_stdout_contains( output, indoc! {r#" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from custom_target_only_integration package Running 1 test(s) from tests/ [FAIL] custom_first::tests::declare_and_call_contract_from_lib Failure data: "Failed to get contract artifact for name = HelloStarknet." Tests: 0 passed, 1 failed, 0 ignored, 0 filtered out "#}, ); } #[test] fn simple_package_no_starknet_contract_target() { let temp = setup_package("simple_package"); let manifest_path = temp.child("Scarb.toml"); let mut scarb_toml = fs::read_to_string(&manifest_path) .unwrap() .parse::() .unwrap(); scarb_toml.as_table_mut().remove("target"); manifest_path.write_str(&scarb_toml.to_string()).unwrap(); let output = test_runner(&temp).assert().code(1); assert!( temp.join("target/dev/simple_package_integrationtest.test.starknet_artifacts.json") .exists() ); assert!( temp.join( "target/dev/simple_package_integrationtest_HelloStarknet.test.contract_class.json" ) .exists() ); assert!( temp.join("target/dev/simple_package_unittest.test.starknet_artifacts.json") .exists() ); assert!( temp.join("target/dev/simple_package_unittest_HelloStarknet.test.contract_class.json") .exists() ); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 13 test(s) from simple_package package Running 2 test(s) from src/ [PASS] simple_package::tests::test_fib [..] [IGNORE] simple_package::tests::ignored_test Running 11 test(s) from tests/ [PASS] simple_package_integrationtest::contract::call_and_invoke [..] [PASS] simple_package_integrationtest::ext_function_test::test_my_test [..] [IGNORE] simple_package_integrationtest::ext_function_test::ignored_test [PASS] simple_package_integrationtest::ext_function_test::test_simple [..] [PASS] simple_package_integrationtest::test_simple::test_simple [..] [PASS] simple_package_integrationtest::test_simple::test_simple2 [..] [PASS] simple_package_integrationtest::test_simple::test_two [..] [PASS] simple_package_integrationtest::test_simple::test_two_and_two [..] [FAIL] simple_package_integrationtest::test_simple::test_failing Failure data: 0x6661696c696e6720636865636b ('failing check') [FAIL] simple_package_integrationtest::test_simple::test_another_failing Failure data: 0x6661696c696e6720636865636b ('failing check') [PASS] simple_package_integrationtest::without_prefix::five [..] Tests: 9 passed, 2 failed, 2 ignored, 0 filtered out Failures: simple_package_integrationtest::test_simple::test_failing simple_package_integrationtest::test_simple::test_another_failing "}, ); } #[test] fn no_optimization_flag() { let temp = setup_package("erc20_package"); let output = test_runner(&temp) .arg("--no-optimization") .assert() .success(); assert!( temp.join("target/dev/erc20_package_ERC20.contract_class.json") .exists() ); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from erc20_package package Running 0 test(s) from src/ Running 1 test(s) from tests/ [PASS] erc20_package_integrationtest::test_complex::complex[..] Tests: 1 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } ================================================ FILE: crates/forge/tests/e2e/coverage.rs ================================================ use super::common::runner::{setup_package, test_runner}; use assert_fs::fixture::{FileWriteStr, PathChild}; use forge_runner::coverage_api::{COVERAGE_DIR, OUTPUT_FILE_NAME}; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; use std::fs; use toml_edit::{DocumentMut, value}; #[test] fn test_coverage_project() { let temp = setup_package("coverage_project"); test_runner(&temp).arg("--coverage").assert().success(); assert!(temp.join(COVERAGE_DIR).join(OUTPUT_FILE_NAME).is_file()); // Check if it doesn't crash in case some data already exists test_runner(&temp).arg("--coverage").assert().success(); } #[test] fn test_coverage_project_and_pass_args() { let temp = setup_package("coverage_project"); test_runner(&temp) .arg("--coverage") .arg("--") .arg("--output-path") .arg("./my_file.lcov") .assert() .success(); assert!(temp.join("my_file.lcov").is_file()); } #[test] fn test_fail_wrong_set_up() { let temp = setup_package("coverage_project"); let manifest_path = temp.child("Scarb.toml"); let mut scarb_toml = fs::read_to_string(&manifest_path) .unwrap() .parse::() .unwrap(); scarb_toml["profile"]["dev"]["cairo"]["unstable-add-statements-code-locations-debug-info"] = value(false); manifest_path.write_str(&scarb_toml.to_string()).unwrap(); let output = test_runner(&temp).arg("--coverage").assert().failure(); assert_stdout_contains( output, indoc! { "[ERROR] Scarb.toml must have the following Cairo compiler configuration to run coverage: [profile.dev.cairo] unstable-add-statements-functions-debug-info = true unstable-add-statements-code-locations-debug-info = true inlining-strategy = \"avoid\" ... other entries ... " }, ); } ================================================ FILE: crates/forge/tests/e2e/debugger.rs ================================================ use super::common::runner::{setup_package, snforge_test_bin_path, test_runner}; use assert_fs::fixture::{FileWriteStr, PathChild}; use indoc::formatdoc; use shared::test_utils::output_assert::assert_stdout_contains; use std::fs; use std::io::{BufRead, BufReader}; use std::process::{Command, Stdio}; #[test] fn test_launch_debugger_waits_for_connection() { let temp = setup_package("debugging"); let manifest_path = temp.child("Scarb.toml"); let existing = fs::read_to_string(&manifest_path).unwrap(); manifest_path .write_str(&formatdoc!( "{existing} [profile.dev.cairo] unstable-add-statements-code-locations-debug-info = true unstable-add-statements-functions-debug-info = true add-functions-debug-info = true skip-optimizations = true", )) .unwrap(); let mut child = Command::new(snforge_test_bin_path()) .args([ "test", "debugging_integrationtest::test_trace::test_debugging_trace_success", "--exact", "--launch-debugger", ]) .current_dir(temp.path()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() .expect("Failed to spawn snforge process"); let found_port_line = BufReader::new(child.stdout.take().unwrap()) .lines() .map_while(Result::ok) .any(|line| line.contains("DEBUGGER PORT")); child.kill().unwrap(); child.wait().unwrap(); // For debugging purposes if the test fails. let stderr = BufReader::new(child.stderr.take().unwrap()) .lines() .map_while(Result::ok) .collect::>() .join("\n"); assert!( found_port_line, "Expected 'DEBUGGER PORT' in snforge output.\n\nstderr:\n{stderr}", ); } #[test] fn test_launch_debugger_fails_for_fuzzer_test() { let temp = setup_package("debugging"); let output = test_runner(&temp) .args([ "debugging_integrationtest::test_trace::test_debugging_fuzzer", "--exact", "--launch-debugger", "--features", "fuzzer", ]) .assert() .code(2); assert_stdout_contains( output, "[ERROR] --launch-debugger is not supported for fuzzer tests", ); } ================================================ FILE: crates/forge/tests/e2e/debugging.rs ================================================ use super::common::runner::{setup_package, test_runner}; use indoc::{formatdoc, indoc}; use shared::test_utils::output_assert::assert_stdout_contains; #[test] fn debugging_trace_custom_components() { let temp = setup_package("debugging"); let output = test_runner(&temp) .arg("--trace-components") .arg("contract-name") .arg("call-result") .arg("call-type") .assert() .code(1); assert_stdout_contains( output, test_output(custom_output_trace_message, "debugging"), ); } #[test] fn debugging_trace_detailed() { let temp = setup_package("debugging"); let output = test_runner(&temp) .arg("--trace-verbosity") .arg("detailed") .assert() .code(1); assert_stdout_contains( output, test_output(detailed_debugging_trace_message, "debugging"), ); } #[test] fn debugging_trace_detailed_fork() { let temp = setup_package("debugging_fork"); let output = test_runner(&temp) .arg("--trace-verbosity") .arg("detailed") .assert() .code(1); assert_stdout_contains( output, test_output(detailed_debugging_trace_message_fork, "debugging_fork"), ); } #[test] fn debugging_trace_standard() { let temp = setup_package("debugging"); let output = test_runner(&temp) .arg("--trace-verbosity") .arg("standard") .assert() .code(1); assert_stdout_contains( output, test_output(standard_debugging_trace_message, "debugging"), ); } #[test] fn debugging_trace_standard_fork() { let temp = setup_package("debugging_fork"); let output = test_runner(&temp) .arg("--trace-verbosity") .arg("standard") .assert() .code(1); assert_stdout_contains( output, test_output(standard_debugging_trace_message_fork, "debugging_fork"), ); } #[test] fn debugging_trace_minimal() { let temp = setup_package("debugging"); let output = test_runner(&temp) .arg("--trace-verbosity") .arg("minimal") .assert() .code(1); assert_stdout_contains( output, test_output(minimal_debugging_trace_message, "debugging"), ); } #[test] fn debugging_trace_minimal_fork() { let temp = setup_package("debugging_fork"); let output = test_runner(&temp) .arg("--trace-verbosity") .arg("minimal") .assert() .code(1); assert_stdout_contains( output, test_output(minimal_debugging_trace_message_fork, "debugging_fork"), ); } #[test] fn debugging_double_flags() { let temp = setup_package("debugging"); test_runner(&temp) .arg("--trace-verbosity") .arg("minimal") .arg("--trace-components") .arg("contract-name") .assert() .code(2) .stderr_eq(indoc! {" error: the argument '--trace-verbosity ' cannot be used with '--trace-components ...' Usage: snforge test --trace-verbosity [TEST_FILTER] [-- ...] For more information, try '--help'. "}); } fn test_output(trace_message_fn: fn(&str, &str) -> String, package_name: &str) -> String { formatdoc! {r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from {package_name} package Running 2 test(s) from tests/ [FAIL] {package_name}_integrationtest::test_trace::test_debugging_trace_failure Failure data: (0x1, 0x2, 0x3, 0x4, 0x5, 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) note: run with `SNFORGE_BACKTRACE=1` environment variable to display a backtrace {debugging_trace_fail} [PASS] {package_name}_integrationtest::test_trace::test_debugging_trace_success (l1_gas: ~[..], l1_data_gas: ~[..], l2_gas: ~[..]) {debugging_trace_pass} Running 0 test(s) from src/ Tests: 1 passed, 1 failed, 0 ignored, 0 filtered out Failures: {package_name}_integrationtest::test_trace::test_debugging_trace_failure ", debugging_trace_fail = trace_message_fn("failure", package_name), debugging_trace_pass = trace_message_fn("success", package_name) } } fn detailed_debugging_trace_message(test_name: &str, package_name: &str) -> String { formatdoc! {r" [test name] {package_name}_integrationtest::test_trace::test_debugging_trace_{test_name} ├─ [selector] execute_calls │ ├─ [contract name] SimpleContract │ ├─ [entry point type] External │ ├─ [calldata] array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] │ ├─ [contract address] [..] │ ├─ [caller address] [..] │ ├─ [call type] Call │ ├─ [call result] success: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] │ ├─ [L2 gas] [..] │ ├─ [selector] execute_calls │ │ ├─ [contract name] SimpleContract │ │ ├─ [entry point type] External │ │ ├─ [calldata] array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] │ │ ├─ [contract address] [..] │ │ ├─ [caller address] [..] │ │ ├─ [call type] Call │ │ ├─ [call result] success: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] │ │ ├─ [L2 gas] [..] │ │ ├─ [selector] execute_calls │ │ │ ├─ [contract name] SimpleContract │ │ │ ├─ [entry point type] External │ │ │ ├─ [calldata] array![] │ │ │ ├─ [contract address] [..] │ │ │ ├─ [caller address] [..] │ │ │ ├─ [call type] Call │ │ │ ├─ [call result] success: array![] │ │ │ └─ [L2 gas] [..] │ │ └─ [selector] execute_calls │ │ ├─ [contract name] SimpleContract │ │ ├─ [entry point type] External │ │ ├─ [calldata] array![] │ │ ├─ [contract address] [..] │ │ ├─ [caller address] [..] │ │ ├─ [call type] Call │ │ ├─ [call result] success: array![] │ │ └─ [L2 gas] [..] │ └─ [selector] execute_calls │ ├─ [contract name] SimpleContract │ ├─ [entry point type] External │ ├─ [calldata] array![] │ ├─ [contract address] [..] │ ├─ [caller address] [..] │ ├─ [call type] Call │ ├─ [call result] success: array![] │ └─ [L2 gas] [..] └─ [selector] fail ├─ [contract name] SimpleContract ├─ [entry point type] External ├─ [calldata] array![0x1, 0x2, 0x3, 0x4, 0x5] ├─ [contract address] [..] ├─ [caller address] [..] ├─ [call type] Call ├─ [call result] panic: (0x1, 0x2, 0x3, 0x4, 0x5) └─ [L2 gas] [..] "} } fn detailed_debugging_trace_message_fork(test_name: &str, package_name: &str) -> String { formatdoc! {r" [test name] {package_name}_integrationtest::test_trace::test_debugging_trace_{test_name} ├─ [selector] execute_calls │ ├─ [contract name] forked contract │ ├─ [entry point type] External │ ├─ [calldata] array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] │ ├─ [contract address] [..] │ ├─ [caller address] [..] │ ├─ [call type] Call │ ├─ [call result] success: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] │ ├─ [L2 gas] [..] │ ├─ [selector] execute_calls │ │ ├─ [contract name] forked contract │ │ ├─ [entry point type] External │ │ ├─ [calldata] array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] │ │ ├─ [contract address] [..] │ │ ├─ [caller address] [..] │ │ ├─ [call type] Call │ │ ├─ [call result] success: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] │ │ ├─ [L2 gas] [..] │ │ ├─ [selector] execute_calls │ │ │ ├─ [contract name] forked contract │ │ │ ├─ [entry point type] External │ │ │ ├─ [calldata] array![] │ │ │ ├─ [contract address] [..] │ │ │ ├─ [caller address] [..] │ │ │ ├─ [call type] Call │ │ │ ├─ [call result] success: array![] │ │ │ └─ [L2 gas] [..] │ │ └─ [selector] execute_calls │ │ ├─ [contract name] forked contract │ │ ├─ [entry point type] External │ │ ├─ [calldata] array![] │ │ ├─ [contract address] [..] │ │ ├─ [caller address] [..] │ │ ├─ [call type] Call │ │ ├─ [call result] success: array![] │ │ └─ [L2 gas] [..] │ └─ [selector] execute_calls │ ├─ [contract name] forked contract │ ├─ [entry point type] External │ ├─ [calldata] array![] │ ├─ [contract address] [..] │ ├─ [caller address] [..] │ ├─ [call type] Call │ ├─ [call result] success: array![] │ └─ [L2 gas] [..] └─ [selector] fail ├─ [contract name] forked contract ├─ [entry point type] External ├─ [calldata] array![0x1, 0x2, 0x3, 0x4, 0x5] ├─ [contract address] [..] ├─ [caller address] [..] ├─ [call type] Call ├─ [call result] panic: (0x1, 0x2, 0x3, 0x4, 0x5) └─ [L2 gas] [..] "} } fn standard_debugging_trace_message(test_name: &str, package_name: &str) -> String { formatdoc! {r" [test name] {package_name}_integrationtest::test_trace::test_debugging_trace_{test_name} ├─ [selector] execute_calls │ ├─ [contract name] SimpleContract │ ├─ [calldata] array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] │ ├─ [call result] success: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] │ ├─ [selector] execute_calls │ │ ├─ [contract name] SimpleContract │ │ ├─ [calldata] array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] │ │ ├─ [call result] success: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] │ │ ├─ [selector] execute_calls │ │ │ ├─ [contract name] SimpleContract │ │ │ ├─ [calldata] array![] │ │ │ └─ [call result] success: array![] │ │ └─ [selector] execute_calls │ │ ├─ [contract name] SimpleContract │ │ ├─ [calldata] array![] │ │ └─ [call result] success: array![] │ └─ [selector] execute_calls │ ├─ [contract name] SimpleContract │ ├─ [calldata] array![] │ └─ [call result] success: array![] └─ [selector] fail ├─ [contract name] SimpleContract ├─ [calldata] array![0x1, 0x2, 0x3, 0x4, 0x5] └─ [call result] panic: (0x1, 0x2, 0x3, 0x4, 0x5) "} } fn standard_debugging_trace_message_fork(test_name: &str, package_name: &str) -> String { formatdoc! {r" [test name] {package_name}_integrationtest::test_trace::test_debugging_trace_{test_name} ├─ [selector] execute_calls │ ├─ [contract name] forked contract │ ├─ [calldata] array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] │ ├─ [call result] success: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] │ ├─ [selector] execute_calls │ │ ├─ [contract name] forked contract │ │ ├─ [calldata] array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] │ │ ├─ [call result] success: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] │ │ ├─ [selector] execute_calls │ │ │ ├─ [contract name] forked contract │ │ │ ├─ [calldata] array![] │ │ │ └─ [call result] success: array![] │ │ └─ [selector] execute_calls │ │ ├─ [contract name] forked contract │ │ ├─ [calldata] array![] │ │ └─ [call result] success: array![] │ └─ [selector] execute_calls │ ├─ [contract name] forked contract │ ├─ [calldata] array![] │ └─ [call result] success: array![] └─ [selector] fail ├─ [contract name] forked contract ├─ [calldata] array![0x1, 0x2, 0x3, 0x4, 0x5] └─ [call result] panic: (0x1, 0x2, 0x3, 0x4, 0x5) "} } fn minimal_debugging_trace_message(test_name: &str, package_name: &str) -> String { formatdoc! {r" [test name] {package_name}_integrationtest::test_trace::test_debugging_trace_{test_name} ├─ [selector] execute_calls │ ├─ [contract name] SimpleContract │ ├─ [selector] execute_calls │ │ ├─ [contract name] SimpleContract │ │ ├─ [selector] execute_calls │ │ │ └─ [contract name] SimpleContract │ │ └─ [selector] execute_calls │ │ └─ [contract name] SimpleContract │ └─ [selector] execute_calls │ └─ [contract name] SimpleContract └─ [selector] fail └─ [contract name] SimpleContract "} } fn minimal_debugging_trace_message_fork(test_name: &str, package_name: &str) -> String { formatdoc! {r" [test name] {package_name}_integrationtest::test_trace::test_debugging_trace_{test_name} ├─ [selector] execute_calls │ ├─ [contract name] forked contract │ ├─ [selector] execute_calls │ │ ├─ [contract name] forked contract │ │ ├─ [selector] execute_calls │ │ │ └─ [contract name] forked contract │ │ └─ [selector] execute_calls │ │ └─ [contract name] forked contract │ └─ [selector] execute_calls │ └─ [contract name] forked contract └─ [selector] fail └─ [contract name] forked contract "} } fn custom_output_trace_message(test_name: &str, package_name: &str) -> String { formatdoc! {r" [test name] {package_name}_integrationtest::test_trace::test_debugging_trace_{test_name} ├─ [selector] execute_calls │ ├─ [contract name] SimpleContract │ ├─ [call type] Call │ ├─ [call result] success: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] │ ├─ [selector] execute_calls │ │ ├─ [contract name] SimpleContract │ │ ├─ [call type] Call │ │ ├─ [call result] success: array![RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}, RecursiveCall {{ contract_address: ContractAddress([..]), payload: array![] }}] │ │ ├─ [selector] execute_calls │ │ │ ├─ [contract name] SimpleContract │ │ │ ├─ [call type] Call │ │ │ └─ [call result] success: array![] │ │ └─ [selector] execute_calls │ │ ├─ [contract name] SimpleContract │ │ ├─ [call type] Call │ │ └─ [call result] success: array![] │ └─ [selector] execute_calls │ ├─ [contract name] SimpleContract │ ├─ [call type] Call │ └─ [call result] success: array![] └─ [selector] fail ├─ [contract name] SimpleContract ├─ [call type] Call └─ [call result] panic: (0x1, 0x2, 0x3, 0x4, 0x5) "} } ================================================ FILE: crates/forge/tests/e2e/docs_snippets_validation.rs ================================================ use clap::Parser; use docs::snippet::SnippetType; use docs::utils::{ assert_valid_snippet, get_nth_ancestor, print_ignored_snippet_message, print_snippets_validation_summary, }; use docs::validation::extract_snippets_from_directory; use forge::Cli; use shared::test_utils::output_assert::assert_stdout_contains; use super::common::runner::{runner, setup_package}; #[test] #[cfg_attr( feature = "cairo-native", ignore = "TODO(#3790): Many snippets show vm resources witch cairo native doesn't support" )] fn test_docs_snippets() { let root_dir = get_nth_ancestor(2); let docs_dir = root_dir.join("docs/src"); let snippet_type = SnippetType::forge(); let snippets = extract_snippets_from_directory(&docs_dir, &snippet_type) .expect("Failed to extract command snippets"); for snippet in &snippets { if snippet.config.ignored { print_ignored_snippet_message(snippet); continue; } let args = snippet.to_command_args(); let mut args: Vec<&str> = args.iter().map(String::as_str).collect(); let parse_result = Cli::try_parse_from(args.clone()); let err_message = if let Err(err) = &parse_result { err.to_string() } else { String::new() }; assert_valid_snippet(parse_result.is_ok(), snippet, &err_message); // Remove "snforge" from the args args.remove(0); if let Some(snippet_output) = &snippet.output { let package_name = snippet .config .package_name .clone() .or_else(|| snippet.capture_package_from_output()) .expect("Cannot find package name in command output or snippet config"); let temp = setup_package(&package_name); let output = runner(&temp).args(args).assert(); assert_stdout_contains(output, snippet_output); } } print_snippets_validation_summary(&snippets, snippet_type.as_str()); } ================================================ FILE: crates/forge/tests/e2e/env.rs ================================================ use super::common::runner::{setup_package, test_runner}; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; #[test] fn env_var_reading() { let temp = setup_package("env"); let output = test_runner(&temp) .env("FELT_ENV_VAR", "987654321") .env("STRING_ENV_VAR", "'abcde'") .env( "BYTE_ARRAY_ENV_VAR", r#""that is a very long environment variable that would normally not fit""#, ) .assert() .code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from env package Running 1 test(s) from src/ [PASS] env::tests::reading_env_vars [..] Tests: 1 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } ================================================ FILE: crates/forge/tests/e2e/features.rs ================================================ use super::common::runner::{setup_package, test_runner}; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; #[test] fn features() { let temp = setup_package("features"); let output = test_runner(&temp) .arg("--features") .arg("snforge_test_only") .assert() .code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from features package Running 0 test(s) from src/ Running 2 test(s) from tests/ [PASS] features_integrationtest::test::test_mock_function [..] [PASS] features_integrationtest::test::test_mock_contract [..] Tests: 2 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn compilation_fails_when_no_features_passed() { let temp = setup_package("features"); let output = test_runner(&temp).assert().failure(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] error[..] Function not found. "}, ); } ================================================ FILE: crates/forge/tests/e2e/fork_warning.rs ================================================ use super::common::runner::{setup_package, test_runner}; use assert_fs::fixture::{FileWriteStr, PathChild}; use axum::{Router, extract::Query, response::Redirect, routing::any}; use indoc::formatdoc; use shared::consts::EXPECTED_RPC_VERSION; use shared::test_utils::node_url::node_url; use shared::test_utils::output_assert::assert_stdout_contains; use std::sync::LazyLock; use std::{thread::sleep, time::Duration}; use tokio::{ net::TcpListener, runtime::{Builder, Runtime}, }; #[derive(serde::Deserialize)] struct Params { url: String, } // to make one url look like different ones fn setup_redirect_server() { static RT: LazyLock = LazyLock::new(|| Builder::new_multi_thread().enable_all().build().unwrap()); RT.spawn(async { let app = Router::new().route( "/", any(|params: Query| async move { Redirect::permanent(¶ms.url) }), ); let listener = TcpListener::bind("127.0.0.1:3030").await.unwrap(); axum::serve(listener, app).await.unwrap(); }); // if test uses server make it wait for a second before it's ready sleep(Duration::from_secs(1)); } #[test] fn should_print_warning() { let temp = setup_package("empty"); let mut node_url = node_url(); node_url.set_path("rpc/v0_6"); temp.child("tests/test.cairo") .write_str( formatdoc!( r#" #[fork(url: "{node_url}", block_tag: latest)] #[test] fn t1() {{ assert!(false); }} "# ) .as_str(), ) .unwrap(); let output = test_runner(&temp).assert(); assert_stdout_contains( output, formatdoc!( r" [..]Compiling[..] [..]Finished[..] [WARNING] RPC node with the url {node_url} uses incompatible version 0.6.0. Expected version: {EXPECTED_RPC_VERSION} Collected 1 test(s) from empty package Running 0 test(s) from src/ Running 1 test(s) from tests/ [FAIL] empty_integrationtest::test::t1 Failure[..] Tests: 0 passed, 1 failed, 0 ignored, 0 filtered out Latest block number = [..] for url = {node_url} Failures: empty_integrationtest::test::t1 " ), ); } #[test] fn should_dedup_urls() { let temp = setup_package("empty"); let mut node_url = node_url(); node_url.set_path("rpc/v0_6"); temp.child("tests/test.cairo") .write_str( formatdoc!( r#" #[fork(url: "{node_url}", block_tag: latest)] #[test] fn t1() {{ assert!(false); }} #[fork(url: "{node_url}", block_tag: latest)] #[test] fn t2() {{ assert!(false); }} "# ) .as_str(), ) .unwrap(); let output = test_runner(&temp).assert(); assert_stdout_contains( output, formatdoc!( r" [..]Compiling[..] [..]Finished[..] [WARNING] RPC node with the url {node_url} uses incompatible version 0.6.0. Expected version: {EXPECTED_RPC_VERSION} Collected 2 test(s) from empty package Running 0 test(s) from src/ Running 2 test(s) from tests/ [FAIL] empty_integrationtest::test::t1 Failure[..] [FAIL] empty_integrationtest::test::t2 Failure[..] Tests: 0 passed, 2 failed, 0 ignored, 0 filtered out Latest block number = [..] for url = {node_url} Failures: empty_integrationtest::test::t1 empty_integrationtest::test::t2 " ), ); } #[test] fn should_print_foreach() { setup_redirect_server(); let temp = setup_package("empty"); let mut node_url = node_url(); node_url.set_path("rpc/v0_6"); temp.child("tests/test.cairo") .write_str( formatdoc!( r#" #[fork(url: "http://127.0.0.1:3030?url={node_url}", block_tag: latest)] #[test] fn t1() {{ assert!(false); }} #[fork(url: "{node_url}", block_tag: latest)] #[test] fn t2() {{ assert!(false); }} "# ) .as_str(), ) .unwrap(); let output = test_runner(&temp).assert(); assert_stdout_contains( output, formatdoc!( r" [..]Compiling[..] [..]Finished[..] [WARNING] RPC node with the url http://127.0.0.1:3030/?url={node_url} uses incompatible version 0.6.0. Expected version: {EXPECTED_RPC_VERSION} [WARNING] RPC node with the url {node_url} uses incompatible version 0.6.0. Expected version: {EXPECTED_RPC_VERSION} Collected 2 test(s) from empty package Running 0 test(s) from src/ Running 2 test(s) from tests/ [FAIL] empty_integrationtest::test::t1 Failure[..] [FAIL] empty_integrationtest::test::t2 Failure[..] Tests: 0 passed, 2 failed, 0 ignored, 0 filtered out Latest block number = [..] for url = http://127.0.0.1:3030/?url={node_url} Latest block number = [..] for url = {node_url} Failures: empty_integrationtest::test::t1 empty_integrationtest::test::t2 " ), ); } ================================================ FILE: crates/forge/tests/e2e/forking.rs ================================================ use super::common::runner::{ BASE_FILE_PATTERNS, Package, runner, setup_package_with_file_patterns, test_runner, }; use forge_runner::CACHE_DIR; use indoc::{formatdoc, indoc}; use shared::test_utils::node_url::node_rpc_url; use shared::test_utils::output_assert::assert_stdout_contains; #[test] fn without_cache() { let temp = setup_package_with_file_patterns(Package::Name("forking".to_string()), BASE_FILE_PATTERNS); let output = test_runner(&temp) .arg("forking::tests::test_fork_simple") .assert() .code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 4 test(s) from forking package Running 4 test(s) from src/ [PASS] forking::tests::test_fork_simple [..] [PASS] forking::tests::test_fork_simple_number_hex [..] [PASS] forking::tests::test_fork_simple_hash_hex [..] [PASS] forking::tests::test_fork_simple_hash_number [..] Tests: 4 passed, 0 failed, 0 ignored, 2 filtered out "}, ); } #[test] /// The cache file at `forking/$CACHE_DIR` was modified to have different value stored /// that this from the real network. We use it to verify that values from cache are actually used. /// /// The test that passed when using data from network, should fail for fabricated data. fn with_cache() { let temp = setup_package_with_file_patterns( Package::Name("forking".to_string()), &[BASE_FILE_PATTERNS, &[&format!("{CACHE_DIR}/*.json")]].concat(), ); let output = test_runner(&temp) .args(["--exact", "forking::tests::test_fork_simple"]) .assert() // if this fails after bumping rpc version change cache file name (name contains url) in tests/data/forking/.snfoundry_cache/ .code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from forking package Running 1 test(s) from src/ [FAIL] forking::tests::test_fork_simple Failure data: 0x42616c616e63652073686f756c642062652030 ('Balance should be 0') Tests: 0 passed, 1 failed, 0 ignored, other filtered out Failures: forking::tests::test_fork_simple "}, ); } #[test] fn with_clean_cache() { let temp = setup_package_with_file_patterns( Package::Name("forking".to_string()), &[BASE_FILE_PATTERNS, &[&format!("{CACHE_DIR}/*.json")]].concat(), ); runner(&temp).arg("clean-cache").assert().code(0); let output = test_runner(&temp) .args(["--exact", "forking::tests::test_fork_simple"]) .assert() .code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from forking package Running 1 test(s) from src/ [PASS] forking::tests::test_fork_simple [..] Tests: 1 passed, 0 failed, 0 ignored, other filtered out "}, ); } #[test] fn printing_latest_block_number() { let temp = setup_package_with_file_patterns( Package::Name("forking".to_string()), &[BASE_FILE_PATTERNS, &[&format!("{CACHE_DIR}/*.json")]].concat(), ); let node_rpc_url = node_rpc_url(); let output = test_runner(&temp) .args(["--exact", "forking::tests::print_block_number_when_latest"]) .assert() .code(0); assert_stdout_contains( output, formatdoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from forking package Running 1 test(s) from src/ [PASS] forking::tests::print_block_number_when_latest [..] Tests: 1 passed, 0 failed, 0 ignored, other filtered out Latest block number = [..] for url = {node_rpc_url} "}, ); } #[test] fn with_skip_fork_tests_env() { let temp = setup_package_with_file_patterns(Package::Name("forking".to_string()), BASE_FILE_PATTERNS); let output = test_runner(&temp) .env("SNFORGE_IGNORE_FORK_TESTS", "1") .arg("forking::tests::test_fork_simple") .assert() .code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 4 test(s) from forking package Running 4 test(s) from src/ [IGNORE] forking::tests::test_fork_simple [IGNORE] forking::tests::test_fork_simple_number_hex [IGNORE] forking::tests::test_fork_simple_hash_hex [IGNORE] forking::tests::test_fork_simple_hash_number Tests: 0 passed, 0 failed, 4 ignored, 2 filtered out "}, ); } ================================================ FILE: crates/forge/tests/e2e/fuzzing.rs ================================================ use super::common::runner::{setup_package, test_runner}; use assert_fs::fixture::{FileTouch, FileWriteStr, PathChild}; use indoc::indoc; use shared::test_utils::output_assert::{assert_stderr_contains, assert_stdout_contains}; #[test] fn fuzzing() { let temp = setup_package("fuzzing"); let output = test_runner(&temp).arg("fuzzing::").assert().code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 13 test(s) from fuzzing package Running 13 test(s) from src/ [PASS] fuzzing::tests::adding [..] [PASS] fuzzing::tests::fuzzed_argument (runs: 256, [..] [PASS] fuzzing::tests::fuzzed_both_arguments (runs: 256, [..] [PASS] fuzzing::tests::passing [..] [FAIL] fuzzing::tests::failing_fuzz (runs: 1, arguments: [[..], [..]]) Failure data: 0x726573756c74203d3d2061202b2062 ('result == a + b') [PASS] fuzzing::tests::custom_fuzzer_config (runs: 10, [..] [PASS] fuzzing::tests::uint8_arg (runs: 256, [..] [PASS] fuzzing::tests::fuzzed_while_loop (runs: 256, [..] [PASS] fuzzing::tests::uint16_arg (runs: 256, [..] [PASS] fuzzing::tests::uint32_arg (runs: 256, [..] [PASS] fuzzing::tests::uint64_arg (runs: 256, [..] [PASS] fuzzing::tests::uint128_arg (runs: 256, [..] [PASS] fuzzing::tests::uint256_arg (runs: 256, [..] Running 0 test(s) from tests/ Tests: 12 passed, 1 failed, 0 ignored, 12 filtered out Fuzzer seed: [..] Failures: fuzzing::tests::failing_fuzz "}, ); } #[test] fn fuzzing_set_runs() { let temp = setup_package("fuzzing"); let output = test_runner(&temp) .args(["fuzzing::", "--fuzzer-runs", "10"]) .assert() .code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 13 test(s) from fuzzing package Running 13 test(s) from src/ [PASS] fuzzing::tests::adding [..] [PASS] fuzzing::tests::fuzzed_argument (runs: 10, [..] [PASS] fuzzing::tests::fuzzed_both_arguments (runs: 10, [..] [PASS] fuzzing::tests::passing [..] [FAIL] fuzzing::tests::failing_fuzz (runs: 1, arguments: [[..], [..]]) Failure data: 0x726573756c74203d3d2061202b2062 ('result == a + b') [PASS] fuzzing::tests::custom_fuzzer_config (runs: 10, [..] [PASS] fuzzing::tests::uint8_arg (runs: 10, [..] [PASS] fuzzing::tests::fuzzed_while_loop (runs: 256, [..] [PASS] fuzzing::tests::uint16_arg (runs: 10, [..] [PASS] fuzzing::tests::uint32_arg (runs: 10, [..] [PASS] fuzzing::tests::uint64_arg (runs: 10, [..] [PASS] fuzzing::tests::uint128_arg (runs: 10, [..] [PASS] fuzzing::tests::uint256_arg (runs: 10, [..] Running 0 test(s) from tests/ Tests: 12 passed, 1 failed, 0 ignored, 12 filtered out Fuzzer seed: [..] Failures: fuzzing::tests::failing_fuzz "}, ); } #[test] fn fuzzing_set_seed() { let temp = setup_package("fuzzing"); let output = test_runner(&temp) .args(["fuzzing::", "--fuzzer-seed", "1234"]) .assert() .code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 13 test(s) from fuzzing package Running 13 test(s) from src/ [PASS] fuzzing::tests::adding [..] [PASS] fuzzing::tests::fuzzed_argument (runs: 256, [..] [PASS] fuzzing::tests::fuzzed_both_arguments (runs: 256, [..] [PASS] fuzzing::tests::passing [..] [FAIL] fuzzing::tests::failing_fuzz (runs: 1, arguments: [[..], [..]]) Failure data: 0x726573756c74203d3d2061202b2062 ('result == a + b') [PASS] fuzzing::tests::custom_fuzzer_config (runs: 10, [..] [PASS] fuzzing::tests::uint8_arg (runs: 256, [..] [PASS] fuzzing::tests::fuzzed_while_loop (runs: 256, [..] [PASS] fuzzing::tests::uint16_arg (runs: 256, [..] [PASS] fuzzing::tests::uint32_arg (runs: 256, [..] [PASS] fuzzing::tests::uint64_arg (runs: 256, [..] [PASS] fuzzing::tests::uint128_arg (runs: 256, [..] [PASS] fuzzing::tests::uint256_arg (runs: 256, [..] Running 0 test(s) from tests/ Tests: 12 passed, 1 failed, 0 ignored, 12 filtered out Fuzzer seed: 1234 Failures: fuzzing::tests::failing_fuzz "}, ); } #[test] fn fuzzing_incorrect_runs() { let temp = setup_package("fuzzing"); let output = test_runner(&temp) .args(["fuzzing::", "--fuzzer-runs", "0"]) .assert() .code(2); assert_stderr_contains( output, indoc! {r" error: invalid value '0' for '--fuzzer-runs ': number would be zero for non-zero type For more information, try '--help'. "}, ); } #[test] fn fuzzing_incorrect_function_args() { let temp = setup_package("fuzzing"); let output = test_runner(&temp) .args(["incorrect_args", "--features", "unimplemented"]) .assert() .code(2); // TODO(#4170): Replace [..] with actual error message when scarb 2.16.0 will be minimal test version. assert_stdout_contains( output, indoc! {r" error[..]: Trait has no implementation in context: snforge_std[..]::fuzzable::Fuzzable::. [ERROR] Failed to build test artifacts with Scarb: `scarb` exited with error "}, ); } #[test] fn fuzzing_exit_first() { let temp = setup_package("fuzzing"); let output = test_runner(&temp) .args(["exit_first_fuzz", "-x"]) .assert() .code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from fuzzing package Running 2 test(s) from tests/ [FAIL] fuzzing_integrationtest::exit_first_fuzz::exit_first_fails_test (runs: 1, arguments: [[..]]) Failure data: 0x32202b2062203d3d2032202b2062 ('2 + b == 2 + b') Tests: 0 passed, 1 failed, 0 ignored, 23 filtered out Interrupted execution of 1 test(s). Fuzzer seed: [..] Failures: fuzzing_integrationtest::exit_first_fuzz::exit_first_fails_test "}, ); } #[test] fn fuzzing_exit_first_single_fail() { let temp = setup_package("fuzzing"); let output = test_runner(&temp) .args(["exit_first_single_fail", "-x"]) .assert() .code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from fuzzing package Running 2 test(s) from tests/ [FAIL] fuzzing_integrationtest::exit_first_single_fail::exit_first_fails_test Failure data: 0x32202b2062203d3d2032202b2062 ('2 + b == 2 + b') Failures: fuzzing_integrationtest::exit_first_single_fail::exit_first_fails_test Tests: 0 passed, 1 failed, 0 ignored, 23 filtered out Interrupted execution of 1 test(s). "}, ); } #[test] fn fuzzing_multiple_attributes() { let temp = setup_package("fuzzing"); let output = test_runner(&temp) .arg("multiple_attributes") .assert() .success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 4 test(s) from fuzzing package Running 4 test(s) from tests/ [IGNORE] fuzzing_integrationtest::multiple_attributes::ignored [PASS] fuzzing_integrationtest::multiple_attributes::with_should_panic (runs: 256, [..]) [PASS] fuzzing_integrationtest::multiple_attributes::with_available_gas (runs: 50, [..]) [PASS] fuzzing_integrationtest::multiple_attributes::with_both (runs: 300, [..]) Tests: 3 passed, 0 failed, 1 ignored, 21 filtered out "}, ); } #[test] fn generate_arg_cheatcode() { let temp = setup_package("fuzzing"); let output = test_runner(&temp).arg("generate_arg").assert().code(1); assert_stdout_contains( output, indoc! {r#" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from fuzzing package Running 2 test(s) from tests/ [FAIL] fuzzing_integrationtest::generate_arg::generate_arg_incorrect_range Failure data: "`generate_arg` cheatcode: `min_value` must be <= `max_value`, provided values after deserialization: 101 and 100" [PASS] fuzzing_integrationtest::generate_arg::use_generate_arg_outside_fuzzer (l1_gas: ~0, l1_data_gas: ~0, l2_gas: ~[..]) Tests: 1 passed, 1 failed, 0 ignored, 23 filtered out "#}, ); } #[test] #[cfg_attr( feature = "skip_test_for_only_latest_scarb", ignore = "Plugin checks skipped" )] fn no_fuzzer_attribute() { let temp = setup_package("fuzzing"); let test_file = temp.child("tests/no_attribute.cairo"); test_file.touch().unwrap(); test_file .write_str(indoc! {r" #[test] fn no_attribute(arg: felt252) { assert(1 == 1, ''); } "}) .unwrap(); let output = test_runner(&temp).assert().code(2); assert_stdout_contains( output, indoc! {r" error[E2200]: Plugin diagnostic: #[test] function with parameters must have #[fuzzer] or #[test_case] attribute --> [..]no_attribute.cairo:1:1 #[test] error: could not compile `fuzzing_integrationtest` due to 1 previous error and 1 warning [ERROR] Failed to build test artifacts with Scarb: `scarb` exited with error "}, ); } #[test] fn fuzz_generic_struct() { let temp = setup_package("fuzzing"); let output = test_runner(&temp).arg("test_generic").assert().success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from fuzzing package Running 1 test(s) from tests/ [PASS] fuzzing_integrationtest::generic_struct::test_generic ([..]) Tests: 1 passed, 0 failed, 0 ignored, 24 filtered out "}, ); } ================================================ FILE: crates/forge/tests/e2e/gas_report.rs ================================================ use crate::assert_cleaned_output; use crate::e2e::common::runner::{ BASE_FILE_PATTERNS, Package, setup_package, setup_package_with_file_patterns, test_runner, }; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; #[test] fn snap_basic() { let temp = setup_package("simple_package"); let output = test_runner(&temp) .arg("--gas-report") .arg("call_and_invoke") .env("SNFORGE_DETERMINISTIC_OUTPUT", "1") .assert() .code(0); assert_cleaned_output!(output); } #[test] fn snap_recursive_calls() { let temp = setup_package("debugging"); let output = test_runner(&temp) .arg("--gas-report") .arg("test_debugging_trace_success") .env("SNFORGE_DETERMINISTIC_OUTPUT", "1") .assert() .code(0); assert_cleaned_output!(output); } #[test] fn snap_multiple_contracts_and_constructor() { let temp = setup_package("simple_package_with_cheats"); let output = test_runner(&temp) .arg("--gas-report") .arg("call_and_invoke_proxy") .env("SNFORGE_DETERMINISTIC_OUTPUT", "1") .assert() .code(0); assert_cleaned_output!(output); } #[test] fn snap_fork() { let temp = setup_package_with_file_patterns(Package::Name("forking".to_string()), BASE_FILE_PATTERNS); let output = test_runner(&temp) .arg("--gas-report") .arg("test_track_resources") .env("SNFORGE_DETERMINISTIC_OUTPUT", "1") .assert() .code(0); assert_cleaned_output!(output); } #[test] fn no_transactions() { let temp = setup_package("simple_package"); let output = test_runner(&temp) .arg("--gas-report") .arg("test_fib") .assert() .code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from simple_package package Running 1 test(s) from src/ [PASS] simple_package::tests::test_fib (l1_gas: ~0, l1_data_gas: ~0, l2_gas: ~[..]) No contract gas usage data to display, no contract calls made. Running 0 test(s) from tests/ Tests: 1 passed, 0 failed, 0 ignored, [..] filtered out "}, ); } ================================================ FILE: crates/forge/tests/e2e/io_operations.rs ================================================ use super::common::runner::{ BASE_FILE_PATTERNS, Package, setup_package_with_file_patterns, test_runner, }; use assert_fs::fixture::PathChild; use indoc::formatdoc; use shared::test_utils::output_assert::assert_stdout_contains; #[test] fn file_reading() { let temp = setup_package_with_file_patterns( Package::Name("file_reading".to_string()), &[BASE_FILE_PATTERNS, &["**/*.txt", "**/*.json"]].concat(), ); let expected_file_error = "No such file or directory [..]"; let expected = formatdoc! {r#" [..]Compiling[..] [..]Finished[..] Collected 11 test(s) from file_reading package Running 0 test(s) from src/ Running 11 test(s) from tests/ [FAIL] file_reading_integrationtest::test::json_non_existent Failure data: "{}" [FAIL] file_reading_integrationtest::test::invalid_json Failure data: "Parse JSON error: invalid type: integer `231232`, expected a map at line 1 column 6 , in file data/json/invalid.json" [FAIL] file_reading_integrationtest::test::non_existent Failure data: "{}" [FAIL] file_reading_integrationtest::test::non_ascii Failure data: "Failed to parse data/non_ascii.txt file" [PASS] file_reading_integrationtest::test::valid_content_and_same_content_no_matter_newlines [..] [PASS] file_reading_integrationtest::test::serialization [..] [PASS] file_reading_integrationtest::test::json_with_array [..] [FAIL] file_reading_integrationtest::test::negative_number "Failed to parse data/negative_number.txt file" Failure data: [FAIL] file_reading_integrationtest::test::valid_content_different_folder Failure data: 0x756e657870656374656420636f6e74656e74 ('unexpected content') [PASS] file_reading_integrationtest::test::json_serialization [..] [PASS] file_reading_integrationtest::test::json_deserialization [..] Tests: 5 passed, 6 failed, 0 ignored, 0 filtered out Failures: file_reading_integrationtest::test::json_non_existent file_reading_integrationtest::test::invalid_json file_reading_integrationtest::test::non_existent file_reading_integrationtest::test::non_ascii file_reading_integrationtest::test::valid_content_different_folder file_reading_integrationtest::test::negative_number "#, expected_file_error, expected_file_error}; // run from different directories to make sure cwd is always set to package directory let output = test_runner(&temp).assert().code(1); assert_stdout_contains(output, &expected); let output = test_runner(&temp) .current_dir(temp.child("src")) .assert() .code(1); assert_stdout_contains(output, &expected); let output = test_runner(&temp) .current_dir(temp.child("data")) .assert() .code(1); assert_stdout_contains(output, &expected); } ================================================ FILE: crates/forge/tests/e2e/mod.rs ================================================ #[cfg(not(feature = "cairo-native"))] mod backtrace; #[cfg(not(feature = "cairo-native"))] mod build_profile; #[cfg(not(feature = "cairo-native"))] mod build_trace_data; mod clean; mod code_quality; mod collection; mod color; pub(crate) mod common; mod completions; mod components; mod contract_artifacts; #[cfg(not(feature = "cairo-native"))] mod coverage; #[cfg(not(feature = "cairo-native"))] mod debugger; #[cfg(not(feature = "cairo-native"))] mod debugging; mod docs_snippets_validation; mod env; mod features; mod fork_warning; mod forking; mod fuzzing; mod gas_report; mod io_operations; mod new; mod optimize_inlining; mod oracles; mod package_warnings; mod partitioning; mod plugin_diagnostics; mod plugin_versions; mod profiles; mod requirements; mod running; mod steps; mod templates; mod test_case; mod trace_print; #[cfg(not(feature = "cairo-native"))] mod trace_resources; mod workspaces; ================================================ FILE: crates/forge/tests/e2e/new.rs ================================================ use super::common::runner::{runner, snforge_test_bin_path, test_runner}; use crate::utils::tempdir_with_tool_versions; use assert_fs::TempDir; use assert_fs::fixture::{FileTouch, PathChild}; use forge::CAIRO_EDITION; use forge::Template; use forge::scarb::config::SCARB_MANIFEST_TEMPLATE_CONTENT; use indoc::{formatdoc, indoc}; use itertools::Itertools; use regex::Regex; use scarb_api::ScarbCommand; use shared::consts::FREE_RPC_PROVIDER_URL; use shared::test_utils::output_assert::assert_stdout_contains; use snapbox::assert_data_eq; use snapbox::cmd::Command as SnapboxCommand; use std::ffi::OsString; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::LazyLock; use std::{env, fs, iter}; use test_case::test_case; use toml_edit::{DocumentMut, Formatted, InlineTable, Item, Value}; static RE_NEWLINES: LazyLock = LazyLock::new(|| Regex::new(r"\n{3,}").unwrap()); #[test_case(&Template::CairoProgram; "cairo-program")] #[test_case(&Template::BalanceContract; "balance-contract")] #[cfg_attr( not(feature = "run_test_for_scarb_since_2_15_1"), test_case(&Template::Erc20Contract => ignore["Skipping test because feature run_test_for_scarb_since_2_15_1 is not enabled"] (); "erc20-contract") )] #[cfg_attr( feature = "run_test_for_scarb_since_2_15_1", test_case(&Template::Erc20Contract; "erc20-contract") )] fn create_new_project_dir_not_exist(template: &Template) { let temp = tempdir_with_tool_versions().unwrap(); let project_path = temp.join("new").join("project"); runner(&temp) .args([ "new", "--name", "test_name", "--template", template.to_string().as_str(), ]) .arg(&project_path) .env("DEV_DISABLE_SNFORGE_STD_DEPENDENCY", "true") .assert() .success(); validate_init(&project_path, false, template); } #[test] fn create_new_project_dir_not_empty() { let temp = tempdir_with_tool_versions().unwrap(); temp.child("empty.txt").touch().unwrap(); let output = runner(&temp) .args(["new", "--name", "test_name"]) .arg(temp.path()) .assert() .code(2); assert_stdout_contains( output, indoc!( r" [ERROR] The provided path [..] points to a non-empty directory. If you wish to create a project in this directory, use the `--overwrite` flag " ), ); } #[test] fn create_new_project_dir_exists_and_empty() { let temp = tempdir_with_tool_versions().unwrap(); let project_path = temp.join("new").join("project"); fs::create_dir_all(&project_path).unwrap(); assert!(project_path.exists()); runner(&temp) .args(["new", "--name", "test_name"]) .arg(&project_path) .env("DEV_DISABLE_SNFORGE_STD_DEPENDENCY", "true") .assert() .success(); validate_init(&project_path, false, &Template::BalanceContract); } #[test] fn init_new_project_from_scarb() { let temp = tempdir_with_tool_versions().unwrap(); SnapboxCommand::from_std( ScarbCommand::new() .current_dir(temp.path()) .args(["new", "test_name"]) .env("SCARB_INIT_TEST_RUNNER", "starknet-foundry") .env( "PATH", append_to_path_var(snforge_test_bin_path().parent().unwrap()), ) .command(), ) .assert() .success(); validate_init(&temp.join("test_name"), true, &Template::BalanceContract); } pub fn append_to_path_var(path: &Path) -> OsString { let script_path = iter::once(path.to_path_buf()); let os_path = env::var_os("PATH").unwrap(); let other_paths = env::split_paths(&os_path); env::join_paths(script_path.chain(other_paths)).unwrap() } fn validate_init(project_path: &PathBuf, validate_snforge_std: bool, template: &Template) { let manifest_path = project_path.join("Scarb.toml"); let scarb_toml = fs::read_to_string(manifest_path.clone()).unwrap(); let expected = get_expected_manifest_content(template, validate_snforge_std); assert_manifest_matches(&expected, &scarb_toml); let mut scarb_toml = DocumentMut::from_str(&scarb_toml).unwrap(); let dependencies = scarb_toml .get_mut("dev-dependencies") .unwrap() .as_table_mut() .unwrap(); let local_snforge_std = Path::new("../../snforge_std") .canonicalize() .unwrap() .to_str() .unwrap() .to_string(); let mut snforge_std = InlineTable::new(); snforge_std.insert("path", Value::String(Formatted::new(local_snforge_std))); dependencies.remove("snforge_std"); dependencies.insert("snforge_std", Item::Value(Value::InlineTable(snforge_std))); fs::write(manifest_path, scarb_toml.to_string()).unwrap(); let output = test_runner(TempDir::new().unwrap()) .current_dir(project_path) .assert() .success(); let expected = get_expected_output(template); assert_stdout_contains(output, expected); } fn get_expected_manifest_content(template: &Template, validate_snforge_std: bool) -> String { let snforge_std_assert = if validate_snforge_std { "\nsnforge_std = \"[..]\"" } else { "" }; let target_contract_entry = "[[target.starknet-contract]]\nsierra = true"; let fork_config = if let Template::Erc20Contract = template { &formatdoc!( r#" [[tool.snforge.fork]] name = "SEPOLIA_LATEST" url = "{FREE_RPC_PROVIDER_URL}" block_id = {{ tag = "latest" }} "# ) } else { "" }; let (dependencies, target_contract_entry) = match template { Template::BalanceContract => ("starknet = \"[..]\"", target_contract_entry), Template::Erc20Contract => ( "openzeppelin_interfaces = \"[..]\"\nopenzeppelin_token = \"[..]\"\nopenzeppelin_utils = \"[..]\"\nstarknet = \"[..]\"", target_contract_entry, ), Template::CairoProgram => ("", ""), }; let allow_prebuild_plugins_assert = r#"allow-prebuilt-plugins = ["snforge_std"]"#; let expected_manifest = formatdoc!( r#" [package] name = "test_name" version = "0.1.0" edition = "{CAIRO_EDITION}" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] {dependencies} [dev-dependencies]{} assert_macros = "[..]" {target_contract_entry} [scripts] test = "snforge test" [tool.scarb] {allow_prebuild_plugins_assert} {fork_config} {} "#, snforge_std_assert, SCARB_MANIFEST_TEMPLATE_CONTENT.trim_end() ); // Replace 3 or more consecutive newlines with exactly 2 newlines RE_NEWLINES .replace_all(&expected_manifest, "\n\n") .to_string() } fn get_expected_output(template: &Template) -> &str { match template { Template::CairoProgram => { indoc!( r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from test_name package Running 1 test(s) from src/ [PASS] test_name::tests::it_works [..] Tests: 1 passed, 0 failed, 0 ignored, 0 filtered out " ) } Template::BalanceContract => { indoc!( r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from test_name package Running 0 test(s) from src/ Running 2 test(s) from tests/ [PASS] test_name_integrationtest::test_contract::test_increase_balance [..] [PASS] test_name_integrationtest::test_contract::test_cannot_increase_balance_with_zero_value [..] Tests: 2 passed, 0 failed, 0 ignored, 0 filtered out " ) } Template::Erc20Contract => { indoc!( r" [..]Compiling[..] [..]Finished[..] Collected 8 test(s) from test_name package Running 8 test(s) from tests/ [PASS] test_name_integrationtest::test_erc20::should_panic_transfer [..] [PASS] test_name_integrationtest::test_erc20::test_get_balance [..] [PASS] test_name_integrationtest::test_erc20::test_transfer [..] [PASS] test_name_integrationtest::test_erc20::test_transfer_event [..] [PASS] test_name_integrationtest::test_token_sender::test_multisend [..] [PASS] test_name_integrationtest::test_token_sender::test_single_send_fuzz [..] [PASS] test_name_integrationtest::test_token_sender::test_single_send [..] [PASS] test_name_integrationtest::test_erc20::test_fork_transfer [..] Running 0 test(s) from src/ Tests: 8 passed, 0 failed, 0 ignored, 0 filtered out " ) } } } #[test] fn create_new_project_and_check_gitignore() { let temp = tempdir_with_tool_versions().unwrap(); let project_path = temp.join("project"); runner(&temp) .env("DEV_DISABLE_SNFORGE_STD_DEPENDENCY", "true") .args(["new", "--name", "test_name"]) .arg(&project_path) .assert() .success(); let gitignore_path = project_path.join(".gitignore"); assert!(gitignore_path.exists(), ".gitignore file should exist"); let gitignore_content = fs::read_to_string(gitignore_path).unwrap(); let expected_gitignore_content = indoc! { r" target .snfoundry_cache/ snfoundry_trace/ coverage/ profile/ " }; assert_eq!(gitignore_content, expected_gitignore_content); } /// Asserts that two manifest contents match, ignoring the order of lines. /// /// # Why Line Order Can Vary /// /// Starting from Scarb version 2.13, `scarb init` no longer inserts the `[dev-dependencies]` section. /// When tools like `forge new` generate a new project, they insert this section later /// in the manifest. As a result, the relative placement of `[dev-dependencies]` and other sections /// might differ depending on the version of Scarb used. fn assert_manifest_matches(expected: &str, actual: &str) { // TODO(#3910) fn sort_lines(s: &str) -> String { s.lines().sorted().join("\n") } assert_data_eq!(sort_lines(actual), sort_lines(expected)); } ================================================ FILE: crates/forge/tests/e2e/optimize_inlining.rs ================================================ use super::common::runner::{runner, setup_package}; use crate::assert_cleaned_output; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; use std::fs; use toml_edit::Document; fn read_optimization_graph(dir: &std::path::Path, min: u32, max: u32, step: u32) -> Vec { let workspace_name = dir.file_name().unwrap().to_string_lossy(); let filename = format!("{workspace_name}_optimization_results_l_{min}_h_{max}_s_{step}.png"); fs::read(dir.join("target").join(&filename)) .unwrap_or_else(|e| panic!("Failed to read {filename}: {e}")) } #[test] fn snap_optimize_inlining_dry_run() { let temp = setup_package("simple_package"); let output = runner(&temp) .env("SCARB_UI_VERBOSITY", "quiet") .env("SNFORGE_DETERMINISTIC_OUTPUT", "1") .arg("optimize-inlining") .arg("--exact") .arg("simple_package_integrationtest::contract::call_and_invoke") .arg("--contracts") .arg("HelloStarknet") .arg("--min-threshold") .arg("0") .arg("--max-threshold") .arg("100") .arg("--step") .arg("50") .assert() .success(); assert_cleaned_output!(output); let graph_bytes = read_optimization_graph(temp.path(), 0, 100, 50); insta::assert_binary_snapshot!("optimize_inlining_dry_run.png", graph_bytes); } #[test] fn snap_optimize_inlining_updates_manifest() { let temp = setup_package("simple_package"); let initial_scarb_toml = fs::read_to_string(temp.path().join("Scarb.toml")) .expect("Failed to read initial Scarb.toml"); let initial_scarb_toml = Document::parse(&initial_scarb_toml).expect("Failed to parse initial Scarb.toml"); let initial_inlining_strategy = initial_scarb_toml .as_table() .get("profile") .and_then(|item| item.as_table()) .and_then(|profile| profile.get("release")) .and_then(|item| item.as_table()) .and_then(|release| release.get("cairo")) .and_then(|item| item.as_table()) .and_then(|cairo| cairo.get("inlining-strategy")) .and_then(toml_edit::Item::as_integer); assert!( initial_inlining_strategy.is_none(), "inlining-strategy should not be set before optimization" ); let output = runner(&temp) .env("SCARB_UI_VERBOSITY", "quiet") .env("SNFORGE_DETERMINISTIC_OUTPUT", "1") .arg("optimize-inlining") .arg("--exact") .arg("simple_package_integrationtest::contract::call_and_invoke") .arg("--contracts") .arg("HelloStarknet") .arg("--gas") .arg("--min-threshold") .arg("0") .arg("--max-threshold") .arg("10") .arg("--step") .arg("10") .assert() .success(); assert_cleaned_output!(output); let graph_bytes = read_optimization_graph(temp.path(), 0, 10, 10); insta::assert_binary_snapshot!("optimize_inlining_updates_manifest.png", graph_bytes); let scarb_toml = fs::read_to_string(temp.path().join("Scarb.toml")) .expect("Failed to read updated Scarb.toml"); let scarb_toml = Document::parse(&scarb_toml).expect("Failed to parse updated Scarb.toml"); let updated_inlining_strategy = scarb_toml .as_table() .get("profile") .and_then(|item| item.as_table()) .and_then(|profile| profile.get("release")) .and_then(|item| item.as_table()) .and_then(|release| release.get("cairo")) .and_then(|item| item.as_table()) .and_then(|cairo| cairo.get("inlining-strategy")) .and_then(toml_edit::Item::as_integer); assert_eq!( updated_inlining_strategy, Some(10), "inlining-strategy should be set to 10 after optimization" ); } #[test] fn optimize_inlining_fails_without_contracts() { let temp = setup_package("fuzzing"); let output = runner(&temp) .arg("optimize-inlining") .arg("--exact") .arg("fuzzing::tests::test_fuzz") .arg("--contracts") .arg("SomeContract") .assert() .failure(); assert_stdout_contains( output, "[ERROR] Optimization failed: No starknet_artifacts.json found. Only projects with contracts can be optimized.", ); } #[test] fn optimize_inlining_fails_with_nonexistent_contract() { let temp = setup_package("simple_package"); let output = runner(&temp) .arg("optimize-inlining") .arg("--exact") .arg("simple_package_integrationtest::contract::call_and_invoke") .arg("--contracts") .arg("NonExistentContract") .arg("--min-threshold") .arg("0") .arg("--max-threshold") .arg("0") .arg("--step") .arg("1") .assert() .failure(); assert_stdout_contains( output, indoc! {r" [ERROR] Optimization failed: The following contracts were not found in starknet artifacts: NonExistentContract. Available contracts: [..] "}, ); } #[test] fn optimize_inlining_fails_with_low_max_program_len() { let temp = setup_package("simple_package"); let output = runner(&temp) .arg("optimize-inlining") .arg("--exact") .arg("simple_package_integrationtest::contract::call_and_invoke") .arg("--contracts") .arg("HelloStarknet") .arg("--max-contract-program-len") .arg("1") .arg("--min-threshold") .arg("0") .arg("--max-threshold") .arg("0") .arg("--step") .arg("1") .assert() .failure(); assert_stdout_contains( output, indoc! {r" [1/1] Testing threshold 0... [..] ✗ Contract size [..] exceeds limit [..] or felts [..] exceeds limit 1. Try optimizing with lower threshold limit. [ERROR] Optimization failed: No valid optimization results found "}, ); } #[test] fn optimize_inlining_fails_when_no_tests_matched() { let temp = setup_package("simple_package"); let output = runner(&temp) .arg("optimize-inlining") .arg("--exact") .arg("simple_package::tests::nonexistent_test") .arg("--contracts") .arg("HelloStarknet") .arg("--min-threshold") .arg("0") .arg("--max-threshold") .arg("0") .arg("--step") .arg("1") .assert() .failure(); assert_stdout_contains( output, "[ERROR] Optimization failed: No tests were executed. The --exact filter did not match any test cases.", ); } #[test] fn optimize_inlining_requires_single_exact_test_case() { let temp = setup_package("simple_package"); let output = runner(&temp) .arg("optimize-inlining") .arg("--contracts") .arg("HelloStarknet") .assert() .failure(); assert_stdout_contains( output, "[ERROR] optimize-inlining requires using the `--exact` flag", ); } ================================================ FILE: crates/forge/tests/e2e/oracles.rs ================================================ use crate::e2e::common::runner::{ BASE_FILE_PATTERNS, Package, setup_package_with_file_patterns, test_runner, }; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; #[cfg_attr( not(feature = "run_test_for_scarb_since_2_13_1"), ignore = "Skipping test because feature skip_test_for_scarb_2_13 enabled" )] #[test] fn wasm() { let temp = setup_package_with_file_patterns( Package::Name("wasm_oracles".to_string()), &[BASE_FILE_PATTERNS, &["*.wasm"]].concat(), ); let output = test_runner(&temp) // Output of oracle is different depending on the env, and Intellij sets it automatically .env_remove("RUST_BACKTRACE") .assert() .code(1); assert_stdout_contains( output, indoc! {r#" [..]Compiling[..] [..]Finished[..] Collected 5 test(s) from oracles package Running 5 test(s) from tests/ [PASS] oracles_integrationtest::test::err ([..]) [PASS] oracles_integrationtest::test::add ([..]) [PASS] oracles_integrationtest::test::panic ([..]) [FAIL] oracles_integrationtest::test::unexpected_panic Failure data: 0x526573756c743a3a756e77726170206661696c65642e ('Result::unwrap failed.') [FAIL] oracles_integrationtest::test::panic_contents Failure data: "error while executing at wasm backtrace: [..] [..] wasm_oracle.wasm!panic Caused by: wasm trap: wasm `unreachable` instruction executed" Running 0 test(s) from src/ Tests: 3 passed, 2 failed, 0 ignored, 0 filtered out "#}, ); } ================================================ FILE: crates/forge/tests/e2e/package_warnings.rs ================================================ use crate::e2e::common::runner::{get_current_branch, get_remote_url, setup_package}; use assert_fs::fixture::{FileWriteStr, PathChild}; use indoc::formatdoc; use scarb_api::ScarbCommand; use shared::test_utils::output_assert::AsOutput; use snapbox::cmd::Command as SnapboxCommand; #[ignore = "TODO(#4091): Restore this test, verify if scheduled tests work"] #[test] fn no_warnings_are_produced() { let temp = setup_package("simple_package"); let remote_url = get_remote_url().to_lowercase(); let branch = get_current_branch(); let manifest_path = temp.child("Scarb.toml"); let snforge_std = format!( r#"snforge_std = {{ git = "https://github.com/{remote_url}", branch = "{branch}" }}"# ); manifest_path .write_str(&formatdoc!( r#" [package] name = "simple_package" version = "0.1.0" edition = "2024_07" [[target.starknet-contract]] [dependencies] starknet = "2.10.1" {snforge_std} [cairo] allow-warnings = false "#, )) .unwrap(); let output = SnapboxCommand::from( ScarbCommand::new() .current_dir(temp.path()) .args(["build", "--test"]) .command(), ) .assert() .code(0); assert!(!output.as_stdout().contains("warn:")); } ================================================ FILE: crates/forge/tests/e2e/partitioning.rs ================================================ use crate::e2e::common::runner::{setup_package, test_runner}; use indoc::indoc; use shared::test_utils::output_assert::{assert_stderr_contains, assert_stdout_contains}; #[test] fn test_does_not_work_with_exact_flag() { let temp = setup_package("simple_package"); let output = test_runner(&temp) .args(["--partition", "3/3", "--workspace", "--exact"]) .assert() .code(2); assert_stderr_contains( output, indoc! {r" error: the argument '--partition ' cannot be used with '--exact' "}, ); } #[test] fn test_whole_workspace_partition_1_2() { let temp = setup_package("partitioning"); let output = test_runner(&temp) .args(["--partition", "1/2", "--workspace"]) .assert() .code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Running partition run: 1/2 Collected 2 test(s) from package_a package Running 1 test(s) from tests/ [PASS] package_a_integrationtest::tests::test_c ([..]) Running 1 test(s) from src/ [PASS] package_a::tests::test_a ([..]) Tests: 2 passed, 0 failed, 0 ignored, 0 filtered out Collected 2 test(s) from package_b package Running 1 test(s) from src/ [PASS] package_b::tests::test_e ([..]) Running 1 test(s) from tests/ [PASS] package_b_integrationtest::tests::test_g ([..]) Tests: 2 passed, 0 failed, 0 ignored, 0 filtered out Collected 3 test(s) from partitioning package Running 2 test(s) from tests/ [PASS] partitioning_integrationtest::tests::test_k ([..]) [PASS] partitioning_integrationtest::tests::test_m ([..]) Running 1 test(s) from src/ [PASS] partitioning::tests::test_i ([..]) Tests: 3 passed, 0 failed, 0 ignored, 0 filtered out Tests summary: 7 passed, 0 failed, 0 ignored, 0 filtered out Finished partition run: 1/2, included 7 out of total 13 tests "}, ); } #[test] fn test_whole_workspace_partition_2_2() { let temp = setup_package("partitioning"); let output = test_runner(&temp) .args(["--partition", "2/2", "--workspace"]) .assert() .code(1); assert_stdout_contains( output, indoc! {r#" [..]Compiling[..] [..]Finished[..] Running partition run: 2/2 Collected 2 test(s) from package_a package Running 1 test(s) from tests/ [PASS] package_a_integrationtest::tests::test_d ([..]) Running 1 test(s) from src/ [IGNORE] package_a::tests::test_b Tests: 1 passed, 0 failed, 1 ignored, 0 filtered out Collected 2 test(s) from package_b package Running 1 test(s) from src/ [PASS] package_b::tests::test_f ([..]) Running 1 test(s) from tests/ [FAIL] package_b_integrationtest::tests::test_h Failure data: "assertion failed: `1 + 1 == 3`." Tests: 1 passed, 1 failed, 0 ignored, 0 filtered out Collected 2 test(s) from partitioning package Running 1 test(s) from tests/ [PASS] partitioning_integrationtest::tests::test_l ([..]) Running 1 test(s) from src/ [PASS] partitioning::tests::test_j ([..]) Tests: 2 passed, 0 failed, 0 ignored, 0 filtered out Failures: package_b_integrationtest::tests::test_h Tests summary: 4 passed, 1 failed, 1 ignored, 0 filtered out Finished partition run: 2/2, included 6 out of total 13 tests "#}, ); } #[test] fn test_whole_workspace_partition_1_3() { let temp = setup_package("partitioning"); let output = test_runner(&temp) .args(["--partition", "1/3", "--workspace"]) .assert() .code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Running partition run: 1/3 Collected 2 test(s) from package_a package Running 1 test(s) from src/ [PASS] package_a::tests::test_a ([..]) Running 1 test(s) from tests/ [PASS] package_a_integrationtest::tests::test_d ([..]) Tests: 2 passed, 0 failed, 0 ignored, 0 filtered out Collected 1 test(s) from package_b package Running 0 test(s) from src/ Running 1 test(s) from tests/ [PASS] package_b_integrationtest::tests::test_g ([..]) Tests: 1 passed, 0 failed, 0 ignored, 0 filtered out Collected 2 test(s) from partitioning package Running 1 test(s) from src/ [PASS] partitioning::tests::test_j ([..]) Running 1 test(s) from tests/ [PASS] partitioning_integrationtest::tests::test_m ([..]) Tests: 2 passed, 0 failed, 0 ignored, 0 filtered out Tests summary: 5 passed, 0 failed, 0 ignored, 0 filtered out Finished partition run: 1/3, included 5 out of total 13 tests "}, ); } #[test] fn test_whole_workspace_partition_2_3() { let temp = setup_package("partitioning"); let output = test_runner(&temp) .args(["--partition", "2/3", "--workspace"]) .assert() .code(1); assert_stdout_contains( output, indoc! {r#" [..]Compiling[..] [..]Finished[..] Running partition run: 2/3 Collected 1 test(s) from package_a package Running 0 test(s) from tests/ Running 1 test(s) from src/ [IGNORE] package_a::tests::test_b Tests: 0 passed, 0 failed, 1 ignored, 0 filtered out Collected 2 test(s) from package_b package Running 1 test(s) from tests/ [FAIL] package_b_integrationtest::tests::test_h Failure data: "assertion failed: `1 + 1 == 3`." Running 1 test(s) from src/ [PASS] package_b::tests::test_e ([..]) Tests: 1 passed, 1 failed, 0 ignored, 0 filtered out Collected 1 test(s) from partitioning package Running 1 test(s) from tests/ [PASS] partitioning_integrationtest::tests::test_k ([..]) Running 0 test(s) from src/ Tests: 1 passed, 0 failed, 0 ignored, 0 filtered out Failures: package_b_integrationtest::tests::test_h Tests summary: 2 passed, 1 failed, 1 ignored, 0 filtered out Finished partition run: 2/3, included 4 out of total 13 tests "#}, ); } #[test] fn test_whole_workspace_partition_3_3() { let temp = setup_package("partitioning"); let output = test_runner(&temp) .args(["--partition", "3/3", "--workspace"]) .assert() .code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Running partition run: 3/3 Collected 1 test(s) from package_a package Running 1 test(s) from tests/ [PASS] package_a_integrationtest::tests::test_c ([..]) Running 0 test(s) from src/ Tests: 1 passed, 0 failed, 0 ignored, 0 filtered out Collected 1 test(s) from package_b package Running 1 test(s) from src/ [PASS] package_b::tests::test_f ([..]) Running 0 test(s) from tests/ Tests: 1 passed, 0 failed, 0 ignored, 0 filtered out Collected 2 test(s) from partitioning package Running 1 test(s) from tests/ [PASS] partitioning_integrationtest::tests::test_l ([..]) Running 1 test(s) from src/ [PASS] partitioning::tests::test_i ([..]) Tests: 2 passed, 0 failed, 0 ignored, 0 filtered out Tests summary: 4 passed, 0 failed, 0 ignored, 0 filtered out Finished partition run: 3/3, included 4 out of total 13 tests "}, ); } #[test] fn test_works_with_name_filter() { let temp = setup_package("partitioning"); let output = test_runner(&temp) .args(["--partition", "1/3", "--workspace", "test_a"]) .assert() .code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Running partition run: 1/3 Collected 1 test(s) from package_a package Running 0 test(s) from tests/ Running 1 test(s) from src/ [PASS] package_a::tests::test_a ([..]) Tests: 1 passed, 0 failed, 0 ignored, 1 filtered out Collected 0 test(s) from package_b package Running 0 test(s) from src/ Running 0 test(s) from tests/ Tests: 0 passed, 0 failed, 0 ignored, 1 filtered out Collected 0 test(s) from partitioning package Running 0 test(s) from tests/ Running 0 test(s) from src/ Tests: 0 passed, 0 failed, 0 ignored, 2 filtered out Tests summary: 1 passed, 0 failed, 0 ignored, 4 filtered out Finished partition run: 1/3, included 5 out of total 13 tests "}, ); } #[cfg(not(feature = "cairo-native"))] #[test] fn test_works_with_coverage() { let temp = setup_package("partitioning"); test_runner(&temp) .args(["--partition", "1/2", "--workspace", "--coverage"]) .assert() .success(); assert!(temp.join("coverage/coverage.lcov").is_file()); } ================================================ FILE: crates/forge/tests/e2e/plugin_diagnostics.rs ================================================ use crate::e2e::common::runner::setup_package_at_path; use camino::Utf8PathBuf; use indoc::{formatdoc, indoc}; use scarb_api::ScarbCommand; use shared::test_utils::output_assert::{assert_stdout_contains, case_assert_stdout_contains}; use snapbox::cmd::Command as SnapboxCommand; use std::fs; #[test] #[cfg_attr( feature = "skip_test_for_only_latest_scarb", ignore = "Plugin checks skipped" )] #[allow(clippy::too_many_lines)] fn syntax() { let temp = setup_package_at_path(Utf8PathBuf::from("diagnostics/syntax")); let output = SnapboxCommand::from_std( ScarbCommand::new() .current_dir(temp.path()) .args(["build", "--test"]) .command(), ) .assert() .failure(); assert_stdout_contains( output, indoc! {r#" error[E1001]: Missing token ';'. --> [..]/tests/contract.cairo:14:70 let (contract_address, _) = contract.deploy(constructor_calldata),unwrap(); ^ error[E1000]: Skipped tokens. Expected: statement. --> [..]/tests/contract.cairo:14:70 let (contract_address, _) = contract.deploy(constructor_calldata),unwrap(); ^ error[E1001]: Missing token ';'. --> [..]/tests/contract.cairo:14:70 let (contract_address, _) = contract.deploy(constructor_calldata),unwrap(); ^ note: this error originates in the attribute macro: `test` error[E1000]: Skipped tokens. Expected: statement. --> [..]/tests/contract.cairo:14:70 let (contract_address, _) = contract.deploy(constructor_calldata),unwrap(); ^ note: this error originates in the attribute macro: `test` error[E1001]: Missing token ';'. --> [..]/tests/contract.cairo:14:70 let (contract_address, _) = contract.deploy(constructor_calldata),unwrap(); ^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` error[E1000]: Skipped tokens. Expected: statement. --> [..]/tests/contract.cairo:14:70 let (contract_address, _) = contract.deploy(constructor_calldata),unwrap(); ^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` error[E1001]: Missing token ';'. --> [..]/tests/contract.cairo:14:70 let (contract_address, _) = contract.deploy(constructor_calldata),unwrap(); ^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` note: this error originates in the attribute macro: `fork` error[E1000]: Skipped tokens. Expected: statement. --> [..]/tests/contract.cairo:14:70 let (contract_address, _) = contract.deploy(constructor_calldata),unwrap(); ^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` note: this error originates in the attribute macro: `fork` error[E2200]: Plugin diagnostic: Failed because of invalid syntax --> [..]/tests/contract.cairo:7:1 #[test] ^^^^^^^ error[E2200]: Plugin diagnostic: Failed because of invalid syntax --> [..]/tests/contract.cairo:8:1 #[fuzzer] ^^^^^^^^^ note: this error originates in the attribute macro: `test` error[E2200]: Plugin diagnostic: Failed because of invalid syntax --> [..]/tests/contract.cairo:9:1 #[fork(url: "http://127.0.0.1:3030", block_tag: latest)] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` error[E2200]: Plugin diagnostic: Failed because of invalid syntax --> [..]/tests/contract.cairo:10:1 #[ignore] ^^^^^^^^^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` note: this error originates in the attribute macro: `fork` error[E2105]: Unexpected type for tuple pattern. "core::result::Result::<(core::starknet::contract_address::ContractAddress, core::array::Span::), core::array::Array::>" is not a tuple. --> [..]/tests/contract.cairo:14:9 let (contract_address, _) = contract.deploy(constructor_calldata),unwrap(); ^^^^^^^^^^^^^^^^^^^^^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` note: this error originates in the attribute macro: `fork` error[E0006]: Function not found. --> [..]/tests/contract.cairo:14:71 let (contract_address, _) = contract.deploy(constructor_calldata),unwrap(); ^^^^^^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` note: this error originates in the attribute macro: `fork` error: could not compile `syntax_integrationtest` due to 14 previous errors and 1 warning "#}, ); } #[test] #[cfg_attr( feature = "skip_test_for_only_latest_scarb", ignore = "Plugin checks skipped" )] fn semantic() { let temp = setup_package_at_path(Utf8PathBuf::from("diagnostics/semantic")); let output = SnapboxCommand::from_std( ScarbCommand::new() .current_dir(temp.path()) .args(["build", "--test"]) .command(), ) .assert() .failure(); assert_stdout_contains( output, indoc! {r" error[E0006]: Identifier not found. --> [..]/tests/contract.cairo:21:13 let y = x; ^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` note: this error originates in the attribute macro: `__fuzzer_config` note: this error originates in the attribute macro: `__fuzzer_wrapper` note: this error originates in the attribute macro: `fork` note: this error originates in the attribute macro: `ignore` note: this error originates in the attribute macro: `__internal_config_statement` warn[E0001]: Unused variable. Consider ignoring by prefixing with `_`. --> [..]/tests/contract.cairo:21:9 let y = x; ^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` note: this error originates in the attribute macro: `__fuzzer_config` note: this error originates in the attribute macro: `__fuzzer_wrapper` note: this error originates in the attribute macro: `fork` note: this error originates in the attribute macro: `ignore` note: this error originates in the attribute macro: `__internal_config_statement` error: could not compile `semantic_integrationtest` due to 1 previous error and 2 warnings "}, ); } #[test] #[cfg_attr( feature = "skip_test_for_only_latest_scarb", ignore = "Plugin checks skipped" )] fn parameters() { let temp = setup_package_at_path(Utf8PathBuf::from("diagnostics/parameters")); let output = SnapboxCommand::from_std( ScarbCommand::new() .current_dir(temp.path()) .args(["build", "--test"]) .command(), ) .assert() .failure(); assert_stdout_contains( output, indoc! {r#" error[E1001]: Missing token ','. --> [..]/tests/contract.cairo:9:31 fn call_and_invoke(_a: felt252; b: u256) { ^ error[E1000]: Skipped tokens. Expected: parameter. --> [..]/tests/contract.cairo:9:31 fn call_and_invoke(_a: felt252; b: u256) { ^ error[E1001]: Missing token ','. --> [..]/tests/contract.cairo:9:31 fn call_and_invoke(_a: felt252; b: u256) { ^ note: this error originates in the attribute macro: `test` error[E1000]: Skipped tokens. Expected: parameter. --> [..]/tests/contract.cairo:9:31 fn call_and_invoke(_a: felt252; b: u256) { ^^ note: this error originates in the attribute macro: `test` error[E2200]: Plugin diagnostic: Failed because of invalid syntax --> [..]/tests/contract.cairo:7:1 #[test] ^^^^^^^ error[E2200]: Plugin diagnostic: Failed because of invalid syntax --> [..]/tests/contract.cairo:8:1 #[fork("TESTNET")] ^^^^^^^^^^^^^^^^^^ note: this error originates in the attribute macro: `test` error: could not compile `parameters_integrationtest` due to 6 previous errors and 1 warning "#}, ); } #[test] #[cfg_attr( feature = "skip_test_for_only_latest_scarb", ignore = "Plugin checks skipped" )] fn multiple() { let temp = setup_package_at_path(Utf8PathBuf::from("diagnostics/multiple")); let output = SnapboxCommand::from_std( ScarbCommand::new() .current_dir(temp.path()) .args(["build", "--test"]) .command(), ) .assert() .failure(); assert_stdout_contains( output, indoc! {r#" error[E1002]: Missing tokens. Expected an expression. --> [..]/tests/contract.cairo:19:22 assert(balance === 0, 'balance == 0'); ^ error[E1002]: Missing tokens. Expected an expression. --> [..]/tests/contract.cairo:19:22 assert(balance === 0, 'balance == 0'); ^ note: this error originates in the attribute macro: `test` error[E1002]: Missing tokens. Expected an expression. --> [..]/tests/contract.cairo:19:22 assert(balance === 0, 'balance == 0'); ^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` error[E1002]: Missing tokens. Expected an expression. --> [..]/tests/contract.cairo:19:22 assert(balance === 0, 'balance == 0'); ^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` note: this error originates in the attribute macro: `fork` error[E2200]: Plugin diagnostic: Failed because of invalid syntax --> [..]/tests/contract.cairo:7:1 #[test] ^^^^^^^ error[E2200]: Plugin diagnostic: Failed because of invalid syntax --> [..]/tests/contract.cairo:8:1 #[fuzzer] ^^^^^^^^^ note: this error originates in the attribute macro: `test` error[E2200]: Plugin diagnostic: Failed because of invalid syntax --> [..]/tests/contract.cairo:9:1 #[fork(url: "http://127.0.0.1:3030", block_tag: latest)] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` error[E2200]: Plugin diagnostic: Failed because of invalid syntax --> [..]/tests/contract.cairo:10:1 #[ignore] ^^^^^^^^^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` note: this error originates in the attribute macro: `fork` error[E2000]: Unsupported feature. --> [..]/tests/contract.cairo:19:22 assert(balance === 0, 'balance == 0'); ^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` note: this error originates in the attribute macro: `fork` error[E2084]: Invalid left-hand side of assignment. --> [..]/tests/contract.cairo:19:12 assert(balance === 0, 'balance == 0'); ^^^^^^^^^^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` note: this error originates in the attribute macro: `fork` error[E0006]: Function not found. --> [..]/tests/contract.cairo:57:30 let balance = dispatcher/get_balance(); ^^^^^^^^^^^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` note: this error originates in the attribute macro: `__fuzzer_config` note: this error originates in the attribute macro: `__fuzzer_wrapper` note: this error originates in the attribute macro: `fork` note: this error originates in the attribute macro: `ignore` note: this error originates in the attribute macro: `__internal_config_statement` error: could not compile `multiple_integrationtest` due to 11 previous errors and 1 warning "#}, ); } #[test] #[cfg_attr( feature = "skip_test_for_only_latest_scarb", ignore = "Plugin checks skipped" )] fn generic() { let temp = setup_package_at_path(Utf8PathBuf::from("diagnostics/generic")); let output = SnapboxCommand::from_std( ScarbCommand::new() .current_dir(temp.path()) .args(["build", "--test"]) .command(), ) .assert() .failure(); assert_stdout_contains( output, indoc! {r" error[E2311]: Trait has no implementation in context: core::traits::PartialOrd::. --> [..]/tests/contract.cairo:29:13 let s = smallest_element(@list); ^^^^^^^^^^^^^^^^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` note: this error originates in the attribute macro: `__fuzzer_config` note: this error originates in the attribute macro: `__fuzzer_wrapper` note: this error originates in the attribute macro: `fork` note: this error originates in the attribute macro: `ignore` note: this error originates in the attribute macro: `__internal_config_statement` error: could not compile `generic_integrationtest` due to 1 previous error and 1 warning "}, ); } #[test] #[cfg_attr( feature = "skip_test_for_only_latest_scarb", ignore = "Plugin checks skipped" )] fn inline_macros() { let temp = setup_package_at_path(Utf8PathBuf::from("diagnostics/inline_macros")); let output = SnapboxCommand::from_std( ScarbCommand::new() .current_dir(temp.path()) .args(["build", "--test"]) .command(), ) .assert() .failure(); assert_stdout_contains( output, indoc! {r" error[E2200]: Plugin diagnostic: Macro cannot be parsed as legacy macro. Expected an argument list wrapped in either parentheses, brackets, or braces. --> [..]/tests/contract.cairo:23:5 print!('balance {}'; balance); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: this error originates in the attribute macro: `test` note: this error originates in the attribute macro: `fuzzer` note: this error originates in the attribute macro: `__fuzzer_config` note: this error originates in the attribute macro: `__fuzzer_wrapper` note: this error originates in the attribute macro: `fork` note: this error originates in the attribute macro: `ignore` note: this error originates in the attribute macro: `__internal_config_statement` error: could not compile `inline_macros_integrationtest` due to 1 previous error and 1 warning "}, ); } #[test] #[cfg_attr( feature = "skip_test_for_only_latest_scarb", ignore = "Plugin checks skipped" )] fn different_attributes() { fn generate_attributes() -> impl Iterator { let attributes = vec![ "#[fuzzer]".to_string(), r#"#[fork(url: "http://127.0.0.1:3030", block_tag: latest)]"#.to_string(), "#[ignore]".to_string(), "#[available_gas(l1_gas: 100, l2_gas: 200, l1_data_gas)]".to_string(), "#[should_panic(expected: 'panic message')]".to_string(), ]; attributes.into_iter() } for attribute in generate_attributes() { let temp = setup_package_at_path(Utf8PathBuf::from("diagnostics/attributes")); let test_file = temp.join("tests/contract.cairo"); let test_file_contents = fs::read_to_string(&test_file).unwrap(); let test_file_contents = test_file_contents.replace("@attrs@", &attribute); fs::write(&test_file, test_file_contents).unwrap(); let output = SnapboxCommand::new("scarb") .current_dir(&temp) .args(["build", "--test"]) .assert() .failure(); let expected_underline = "^".repeat(attribute.len()); case_assert_stdout_contains( attribute.clone(), output, formatdoc! {r" error[E1001]: Missing token ';'. --> [..]/tests/contract.cairo:10:81 let (_contract_address1, _) = contract.deploy(constructor_calldata).unwrap() ^ error[E2200]: Plugin diagnostic: Failed because of invalid syntax --> [..]/tests/contract.cairo:6:1 {attribute} {expected_underline} error: could not compile `attributes_integrationtest` due to 2 previous errors and 1 warning "}, ); } } #[test] #[cfg_attr( feature = "skip_test_for_only_latest_scarb", ignore = "Plugin checks skipped" )] fn test_case() { let temp = setup_package_at_path(Utf8PathBuf::from("diagnostics/test_case_attr")); let output = SnapboxCommand::from_std( ScarbCommand::new() .current_dir(temp.path()) .args(["build", "--test"]) .command(), ) .assert() .failure(); assert_stdout_contains( output, indoc! {r" error[E2200]: Plugin diagnostic: #[test_case] The function must have at least one parameter to use #[test_case] attribute --> [..]/tests/basic.cairo:2:1 #[test_case(3, 4, 7)] ^^^^^^^^^^^^^^^^^^^^^ note: this error originates in the attribute macro: `test` error[E2200]: Plugin diagnostic: #[test_case] Expected 2 arguments, but got 3 #[test_case(3, 4, 7)] ^^^^^^^^^^^^^^^^^^^^^ note: this error originates in the attribute macro: `test` error[E2200]: Plugin diagnostic: #[test_case] Only string literals are allowed for 'name' argument. #[test_case(name: array![1, 2, 3], 3, 4, 7)] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: this error originates in the attribute macro: `test` error: could not compile `test_case_attr_integrationtest` due to 3 previous errors and 1 warning "}, ); } ================================================ FILE: crates/forge/tests/e2e/plugin_versions.rs ================================================ use crate::e2e::common::runner::{runner, test_runner}; use crate::utils::tempdir_with_tool_versions; use camino::Utf8PathBuf; use indoc::{formatdoc, indoc}; use scarb_api::ScarbCommand; use shared::test_utils::output_assert::assert_stdout_contains; use snapbox::cmd::Command; use toml_edit::Document; #[test] #[cfg_attr( not(feature = "test_for_multiple_scarb_versions"), ignore = "Multiple scarb versions must be installed" )] fn new_with_new_scarb() { let temp = tempdir_with_tool_versions().unwrap(); runner(&temp) .env("DEV_USE_OFFLINE_MODE", "true") .args(["new", "abc"]) .assert() .success(); let manifest = temp.path().join("abc").join("Scarb.toml"); let manifest = &std::fs::read_to_string(manifest).unwrap(); let manifest = Document::parse(manifest).unwrap(); let snforge_std = manifest .get("dev-dependencies") .unwrap() .get("snforge_std") .unwrap(); let snforge_std = snforge_std.as_str().unwrap(); assert_eq!(snforge_std, env!("CARGO_PKG_VERSION")); } #[test] #[cfg_attr( not(feature = "test_for_multiple_scarb_versions"), ignore = "Multiple scarb versions must be installed" )] fn new_with_minimal_scarb() { let temp = tempdir_with_tool_versions().unwrap(); Command::new("asdf") .current_dir(&temp) .args(["set", "scarb", "2.12.0"]) .assert() .success(); runner(&temp) .env("DEV_USE_OFFLINE_MODE", "true") .args(["new", "abc"]) .assert() .success(); let manifest = temp.path().join("abc").join("Scarb.toml"); let manifest = &std::fs::read_to_string(manifest).unwrap(); let manifest = Document::parse(manifest).unwrap(); let snforge_std = manifest .get("dev-dependencies") .unwrap() .get("snforge_std") .unwrap(); let snforge_std = snforge_std.as_str().unwrap(); assert_eq!(snforge_std, env!("CARGO_PKG_VERSION")); } #[test] #[cfg_attr( not(feature = "test_for_multiple_scarb_versions"), ignore = "Multiple scarb versions must be installed" )] fn new_scarb_new_macros() { let temp = tempdir_with_tool_versions().unwrap(); runner(&temp) .env("DEV_DISABLE_SNFORGE_STD_DEPENDENCY", "true") .args(["new", "abc"]) .assert() .success(); let snforge_std = Utf8PathBuf::from("../../snforge_std") .canonicalize_utf8() .unwrap(); ScarbCommand::new() .current_dir(temp.path().join("abc")) .args(["add", "snforge_std", "--path", snforge_std.as_str()]) .command() .output() .unwrap(); let output = test_runner(temp.join("abc")).assert().success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from abc package Running 0 test(s) from src/ Running 2 test(s) from tests/ [PASS] abc_integrationtest::test_contract::test_cannot_increase_balance_with_zero_value (l1_gas: ~[..], l1_data_gas: ~[..], l2_gas: ~[..]) [PASS] abc_integrationtest::test_contract::test_increase_balance (l1_gas: ~[..], l1_data_gas: ~192, l2_gas: ~[..]) Tests: 2 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] #[cfg_attr( not(feature = "test_for_multiple_scarb_versions"), ignore = "Multiple scarb versions must be installed" )] fn new_scarb_old_macros() { let temp = tempdir_with_tool_versions().unwrap(); runner(&temp) .env("DEV_DISABLE_SNFORGE_STD_DEPENDENCY", "true") .args(["new", "abc"]) .assert() .success(); ScarbCommand::new() .current_dir(temp.path().join("abc")) .args(["add", "snforge_std@0.44.0"]) .command() .output() .unwrap(); let output = test_runner(temp.join("abc")).assert().failure(); assert_stdout_contains( output, formatdoc! {r" [ERROR] Package snforge_std version does not meet the minimum required version >=0.50.0. Please upgrade snforge_std in Scarb.toml ", }, ); } ================================================ FILE: crates/forge/tests/e2e/profiles.rs ================================================ use super::common::runner::{setup_package, test_runner}; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; #[test] fn release() { let temp = setup_package("empty"); let output = test_runner(&temp).arg("--release").assert().code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished `release` profile target(s) in [..] Collected 0 test(s) from empty package Tests: 0 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn custom() { let temp = setup_package("empty"); let output = test_runner(&temp) .args(["--profile", "custom-profile"]) .assert() .code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished `custom-profile` profile target(s) in [..] Collected 0 test(s) from empty package Tests: 0 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } ================================================ FILE: crates/forge/tests/e2e/requirements.rs ================================================ use crate::e2e::common::runner::{runner, setup_package}; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; #[test] fn happy_path() { let temp = setup_package("simple_package"); let output = runner(&temp).arg("check-requirements").assert(); assert_stdout_contains( output, indoc! {r" Checking requirements ✅ Scarb [..] ✅ Universal Sierra Compiler [..] "}, ); } #[test] #[cfg_attr(not(feature = "scarb_2_12_0"), ignore)] fn test_warning_on_scarb_version_below_recommended() { let temp = setup_package("simple_package"); let output = runner(&temp).arg("check-requirements").assert(); assert_stdout_contains( output, indoc! {r" Checking requirements ⚠️ Scarb Version 2.12.0 doesn't satisfy minimal recommended [..] ✅ Universal Sierra Compiler [..] "}, ); } ================================================ FILE: crates/forge/tests/e2e/running.rs ================================================ use super::common::runner::{ get_current_branch, get_remote_url, setup_package, test_runner, test_runner_native, }; use crate::utils::get_snforge_std_entry; use assert_fs::fixture::{FileWriteStr, PathChild}; use indoc::{formatdoc, indoc}; use shared::test_utils::output_assert::{AsOutput, assert_stdout, assert_stdout_contains}; use std::fs; use toml_edit::{DocumentMut, value}; #[test] fn simple_package() { let temp = setup_package("simple_package"); let output = test_runner(&temp).assert().code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 13 test(s) from simple_package package Running 2 test(s) from src/ [PASS] simple_package::tests::test_fib [..] [IGNORE] simple_package::tests::ignored_test Running 11 test(s) from tests/ [PASS] simple_package_integrationtest::contract::call_and_invoke [..] [PASS] simple_package_integrationtest::ext_function_test::test_my_test [..] [IGNORE] simple_package_integrationtest::ext_function_test::ignored_test [PASS] simple_package_integrationtest::ext_function_test::test_simple [..] [PASS] simple_package_integrationtest::test_simple::test_simple [..] [PASS] simple_package_integrationtest::test_simple::test_simple2 [..] [PASS] simple_package_integrationtest::test_simple::test_two [..] [PASS] simple_package_integrationtest::test_simple::test_two_and_two [..] [FAIL] simple_package_integrationtest::test_simple::test_failing Failure data: 0x6661696c696e6720636865636b ('failing check') [FAIL] simple_package_integrationtest::test_simple::test_another_failing Failure data: 0x6661696c696e6720636865636b ('failing check') [PASS] simple_package_integrationtest::without_prefix::five [..] Tests: 9 passed, 2 failed, 2 ignored, 0 filtered out Failures: simple_package_integrationtest::test_simple::test_failing simple_package_integrationtest::test_simple::test_another_failing "}, ); } #[cfg_attr( not(feature = "cairo-native"), ignore = "Requires cairo-native feature" )] #[test] fn simple_package_native() { let temp = setup_package("simple_package"); let output = test_runner_native(&temp).assert().code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 13 test(s) from simple_package package Running 2 test(s) from src/ [PASS] simple_package::tests::test_fib [..] [IGNORE] simple_package::tests::ignored_test Running 11 test(s) from tests/ [PASS] simple_package_integrationtest::contract::call_and_invoke [..] [PASS] simple_package_integrationtest::ext_function_test::test_my_test [..] [IGNORE] simple_package_integrationtest::ext_function_test::ignored_test [PASS] simple_package_integrationtest::ext_function_test::test_simple [..] [PASS] simple_package_integrationtest::test_simple::test_simple [..] [PASS] simple_package_integrationtest::test_simple::test_simple2 [..] [PASS] simple_package_integrationtest::test_simple::test_two [..] [PASS] simple_package_integrationtest::test_simple::test_two_and_two [..] [FAIL] simple_package_integrationtest::test_simple::test_failing Failure data: 0x6661696c696e6720636865636b ('failing check') [FAIL] simple_package_integrationtest::test_simple::test_another_failing Failure data: 0x6661696c696e6720636865636b ('failing check') [PASS] simple_package_integrationtest::without_prefix::five [..] Tests: 9 passed, 2 failed, 2 ignored, 0 filtered out Failures: simple_package_integrationtest::test_simple::test_failing simple_package_integrationtest::test_simple::test_another_failing "}, ); } #[test] fn simple_package_with_cheats() { let temp = setup_package("simple_package_with_cheats"); let output = test_runner(&temp).assert().code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 5 test(s) from simple_package_with_cheats package Running 0 test(s) from src/ Running 5 test(s) from tests/ [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke [..] [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke_proxy [..] [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke_library_call [..] [PASS] simple_package_with_cheats_integrationtest::contract::deploy_syscall [..] [PASS] simple_package_with_cheats_integrationtest::contract::block_hash [..] Tests: 5 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn simple_package_with_git_dependency() { let temp = setup_package("simple_package"); let remote_url = get_remote_url().to_lowercase(); let branch = get_current_branch(); let manifest_path = temp.child("Scarb.toml"); let snforge_std = format!( r#"snforge_std = {{ git = "https://github.com/{remote_url}", branch = "{branch}" }}"# ); manifest_path .write_str(&formatdoc!( r#" [package] name = "simple_package" version = "0.1.0" [[target.starknet-contract]] [dependencies] starknet = "2.6.4" {snforge_std} "#, )) .unwrap(); let output = test_runner(&temp).assert().code(1); assert_stdout_contains( output, formatdoc!( r" [..]Updating git repository https://github.com/{} [..]Compiling[..] [..]Finished[..] Collected 13 test(s) from simple_package package Running 2 test(s) from src/ [PASS] simple_package::tests::test_fib [..] [IGNORE] simple_package::tests::ignored_test Running 11 test(s) from tests/ [PASS] simple_package_integrationtest::contract::call_and_invoke [..] [PASS] simple_package_integrationtest::ext_function_test::test_my_test [..] [IGNORE] simple_package_integrationtest::ext_function_test::ignored_test [PASS] simple_package_integrationtest::ext_function_test::test_simple [..] [PASS] simple_package_integrationtest::test_simple::test_simple [..] [PASS] simple_package_integrationtest::test_simple::test_simple2 [..] [PASS] simple_package_integrationtest::test_simple::test_two [..] [PASS] simple_package_integrationtest::test_simple::test_two_and_two [..] [FAIL] simple_package_integrationtest::test_simple::test_failing Failure data: 0x6661696c696e6720636865636b ('failing check') [FAIL] simple_package_integrationtest::test_simple::test_another_failing Failure data: 0x6661696c696e6720636865636b ('failing check') [PASS] simple_package_integrationtest::without_prefix::five [..] Tests: 9 passed, 2 failed, 2 ignored, 0 filtered out Failures: simple_package_integrationtest::test_simple::test_failing simple_package_integrationtest::test_simple::test_another_failing ", remote_url.trim_end_matches(".git") ), ); } #[test] fn with_failing_scarb_build() { let temp = setup_package("simple_package"); temp.child("src/lib.cairo") .write_str(indoc!( r" mod hello_starknet; mods erc20; " )) .unwrap(); let output = test_runner(&temp).arg("--no-optimization").assert().code(2); assert_stdout_contains( output, indoc!( r" [ERROR] Failed to build contracts with Scarb: `scarb` exited with error " ), ); } #[test] fn with_filter() { let temp = setup_package("simple_package"); let output = test_runner(&temp).arg("two").assert().success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from simple_package package Running 2 test(s) from tests/ [PASS] simple_package_integrationtest::test_simple::test_two [..] [PASS] simple_package_integrationtest::test_simple::test_two_and_two [..] Tests: 2 passed, 0 failed, 0 ignored, 11 filtered out "}, ); } #[test] fn with_filter_matching_module() { let temp = setup_package("simple_package"); let output = test_runner(&temp) .arg("ext_function_test::") .assert() .success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 3 test(s) from simple_package package Running 3 test(s) from tests/ [PASS] simple_package_integrationtest::ext_function_test::test_my_test [..] [IGNORE] simple_package_integrationtest::ext_function_test::ignored_test [PASS] simple_package_integrationtest::ext_function_test::test_simple [..] Tests: 2 passed, 0 failed, 1 ignored, 10 filtered out "}, ); } #[test] fn with_exact_filter() { let temp = setup_package("simple_package"); let output = test_runner(&temp) .arg("simple_package_integrationtest::test_simple::test_two") .arg("--exact") .assert() .success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from simple_package package Running 0 test(s) from src/ Running 1 test(s) from tests/ [PASS] simple_package_integrationtest::test_simple::test_two [..] Tests: 1 passed, 0 failed, 0 ignored, other filtered out "}, ); } #[test] fn with_skip_filter_matching_module() { let temp = setup_package("simple_package"); let output = test_runner(&temp) .arg("--skip") .arg("simple_package") .assert() .success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 0 test(s) from simple_package package Running 0 test(s) from src/ Running 0 test(s) from tests/ Tests: 0 passed, 0 failed, 0 ignored, 13 filtered out "}, ); } #[test] fn with_skip_filter_matching_full_module_path() { let temp = setup_package("simple_package"); let output = test_runner(&temp) .arg("--skip") .arg("simple_package_integrationtest::test_simple::test_two_and_two") .assert() .failure(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 12 test(s) from simple_package package Running 10 test(s) from tests/ [IGNORE] simple_package_integrationtest::ext_function_test::ignored_test [PASS] simple_package_integrationtest::ext_function_test::test_simple [..] [PASS] simple_package_integrationtest::without_prefix::five [..] [PASS] simple_package_integrationtest::test_simple::test_simple2 [..] [PASS] simple_package_integrationtest::test_simple::test_two [..] [FAIL] simple_package_integrationtest::test_simple::test_another_failing Failure data: 0x6661696c696e6720636865636b ('failing check') [PASS] simple_package_integrationtest::ext_function_test::test_my_test [..] [FAIL] simple_package_integrationtest::test_simple::test_failing Failure data: 0x6661696c696e6720636865636b ('failing check') [PASS] simple_package_integrationtest::test_simple::test_simple [..] [PASS] simple_package_integrationtest::contract::call_and_invoke [..] Running 2 test(s) from src/ [IGNORE] simple_package::tests::ignored_test [PASS] simple_package::tests::test_fib [..] Tests: 8 passed, 2 failed, 2 ignored, 1 filtered out Failures: simple_package_integrationtest::test_simple::test_another_failing simple_package_integrationtest::test_simple::test_failing "}, ); } #[test] fn with_skip_filter_matching_test_name() { let temp = setup_package("simple_package"); let output = test_runner(&temp) .arg("--skip") .arg("failing") .assert() .success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 11 test(s) from simple_package package Running 9 test(s) from tests/ [IGNORE] simple_package_integrationtest::ext_function_test::ignored_test [PASS] simple_package_integrationtest::test_simple::test_two [..] [PASS] simple_package_integrationtest::test_simple::test_two_and_two [..] [PASS] simple_package_integrationtest::test_simple::test_simple2 [..] [PASS] simple_package_integrationtest::ext_function_test::test_simple [..] [PASS] simple_package_integrationtest::without_prefix::five [..] [PASS] simple_package_integrationtest::ext_function_test::test_my_test [..] [PASS] simple_package_integrationtest::test_simple::test_simple [..] [PASS] simple_package_integrationtest::contract::call_and_invoke [..] Running 2 test(s) from src/ [IGNORE] simple_package::tests::ignored_test [PASS] simple_package::tests::test_fib [..] Tests: 9 passed, 0 failed, 2 ignored, 2 filtered out "}, ); } #[test] fn with_skip_filter_matching_multiple_test_name() { let temp = setup_package("simple_package"); let output = test_runner(&temp) .arg("--skip") .arg("failing") .arg("--skip") .arg("two") .assert() .success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 9 test(s) from simple_package package Running 7 test(s) from tests/ [IGNORE] simple_package_integrationtest::ext_function_test::ignored_test [PASS] simple_package_integrationtest::test_simple::test_simple2 [..] [PASS] simple_package_integrationtest::ext_function_test::test_simple [..] [PASS] simple_package_integrationtest::without_prefix::five [..] [PASS] simple_package_integrationtest::ext_function_test::test_my_test [..] [PASS] simple_package_integrationtest::test_simple::test_simple [..] [PASS] simple_package_integrationtest::contract::call_and_invoke [..] Running 2 test(s) from src/ [IGNORE] simple_package::tests::ignored_test [PASS] simple_package::tests::test_fib [..] Tests: 7 passed, 0 failed, 2 ignored, 4 filtered out "}, ); } #[test] fn with_exact_filter_and_duplicated_test_names() { let temp = setup_package("duplicated_test_names"); let output = test_runner(&temp) .arg("duplicated_test_names_integrationtest::tests_a::test_simple") .arg("--exact") .assert() .success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from duplicated_test_names package Running 0 test(s) from src/ Running 1 test(s) from tests/ [PASS] duplicated_test_names_integrationtest::tests_a::test_simple [..] Tests: 1 passed, 0 failed, 0 ignored, other filtered out "}, ); } #[test] fn with_non_matching_filter() { let temp = setup_package("simple_package"); let output = test_runner(&temp).arg("qwerty").assert().success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 0 test(s) from simple_package package Running 0 test(s) from src/ Running 0 test(s) from tests/ Tests: 0 passed, 0 failed, 0 ignored, 13 filtered out "}, ); } #[test] fn with_ignored_flag() { let temp = setup_package("simple_package"); let output = test_runner(&temp).arg("--ignored").assert().code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from simple_package package Running 1 test(s) from src/ [PASS] simple_package::tests::ignored_test [..] Running 1 test(s) from tests/ [FAIL] simple_package_integrationtest::ext_function_test::ignored_test Failure data: 0x6e6f742070617373696e67 ('not passing') Tests: 1 passed, 1 failed, 0 ignored, 11 filtered out Failures: simple_package_integrationtest::ext_function_test::ignored_test "}, ); } #[test] fn with_include_ignored_flag() { let temp = setup_package("simple_package"); let output = test_runner(&temp).arg("--include-ignored").assert().code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 13 test(s) from simple_package package Running 2 test(s) from src/ [PASS] simple_package::tests::test_fib [..] [PASS] simple_package::tests::ignored_test [..] Running 11 test(s) from tests/ [PASS] simple_package_integrationtest::contract::call_and_invoke [..] [PASS] simple_package_integrationtest::ext_function_test::test_my_test [..] [FAIL] simple_package_integrationtest::ext_function_test::ignored_test Failure data: 0x6e6f742070617373696e67 ('not passing') [PASS] simple_package_integrationtest::ext_function_test::test_simple [..] [PASS] simple_package_integrationtest::test_simple::test_simple [..] [PASS] simple_package_integrationtest::test_simple::test_simple2 [..] [PASS] simple_package_integrationtest::test_simple::test_two [..] [PASS] simple_package_integrationtest::test_simple::test_two_and_two [..] [FAIL] simple_package_integrationtest::test_simple::test_failing Failure data: 0x6661696c696e6720636865636b ('failing check') [FAIL] simple_package_integrationtest::test_simple::test_another_failing Failure data: 0x6661696c696e6720636865636b ('failing check') [PASS] simple_package_integrationtest::without_prefix::five [..] Tests: 10 passed, 3 failed, 0 ignored, 0 filtered out Failures: simple_package_integrationtest::ext_function_test::ignored_test simple_package_integrationtest::test_simple::test_failing simple_package_integrationtest::test_simple::test_another_failing "}, ); } #[test] fn with_ignored_flag_and_filter() { let temp = setup_package("simple_package"); let output = test_runner(&temp) .arg("--ignored") .arg("ext_function_test::ignored_test") .assert() .code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from simple_package package Running 0 test(s) from src/ Running 1 test(s) from tests/ [FAIL] simple_package_integrationtest::ext_function_test::ignored_test Failure data: 0x6e6f742070617373696e67 ('not passing') Tests: 0 passed, 1 failed, 0 ignored, 12 filtered out Failures: simple_package_integrationtest::ext_function_test::ignored_test "}, ); } #[test] fn with_include_ignored_flag_and_filter() { let temp = setup_package("simple_package"); let output = test_runner(&temp) .arg("--include-ignored") .arg("ignored_test") .assert() .code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from simple_package package Running 1 test(s) from src/ [PASS] simple_package::tests::ignored_test [..] Running 1 test(s) from tests/ [FAIL] simple_package_integrationtest::ext_function_test::ignored_test Failure data: 0x6e6f742070617373696e67 ('not passing') Tests: 1 passed, 1 failed, 0 ignored, 11 filtered out Failures: simple_package_integrationtest::ext_function_test::ignored_test "}, ); } #[test] fn with_rerun_failed_flag_without_cache() { let temp = setup_package("simple_package"); let output = test_runner(&temp).arg("--rerun-failed").assert().code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 13 test(s) from simple_package package Running 2 test(s) from src/ [PASS] simple_package::tests::test_fib [..] Running 11 test(s) from tests/ [PASS] simple_package_integrationtest::contract::call_and_invoke [..] [PASS] simple_package_integrationtest::ext_function_test::test_my_test [..] [PASS] simple_package_integrationtest::ext_function_test::test_simple [..] [PASS] simple_package_integrationtest::test_simple::test_simple [..] [PASS] simple_package_integrationtest::test_simple::test_simple2 [..] [PASS] simple_package_integrationtest::test_simple::test_two [..] [PASS] simple_package_integrationtest::test_simple::test_two_and_two [..] [FAIL] simple_package_integrationtest::test_simple::test_failing Failure data: 0x6661696c696e6720636865636b ('failing check') [FAIL] simple_package_integrationtest::test_simple::test_another_failing [PASS] simple_package_integrationtest::without_prefix::five [..] Failures: simple_package_integrationtest::test_simple::test_failing simple_package_integrationtest::test_simple::test_another_failing [IGNORE] simple_package::tests::ignored_test [IGNORE] simple_package_integrationtest::ext_function_test::ignored_test Tests: 9 passed, 2 failed, 2 ignored, 0 filtered out Failure data: 0x6661696c696e6720636865636b ('failing check') "}, ); } #[test] fn with_rerun_failed_flag_and_name_filter() { let temp = setup_package("simple_package"); test_runner(&temp).assert().code(1); let output = test_runner(&temp) .arg("--rerun-failed") .arg("test_another_failing") .assert() .code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from simple_package package Running 1 test(s) from tests/ [FAIL] simple_package_integrationtest::test_simple::test_another_failing Failure data: 0x6661696c696e6720636865636b ('failing check') Tests: 0 passed, 1 failed, 0 ignored, 12 filtered out Failures: simple_package_integrationtest::test_simple::test_another_failing "}, ); } #[test] fn with_rerun_failed_flag() { let temp = setup_package("simple_package"); test_runner(&temp).assert().code(1); let output = test_runner(&temp).arg("--rerun-failed").assert().code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from simple_package package Running 0 test(s) from src/ Running 2 test(s) from tests/ [FAIL] simple_package_integrationtest::test_simple::test_another_failing Failure data: 0x6661696c696e6720636865636b ('failing check') [FAIL] simple_package_integrationtest::test_simple::test_failing Failure data: 0x6661696c696e6720636865636b ('failing check') Tests: 0 passed, 2 failed, 0 ignored, 11 filtered out Failures: simple_package_integrationtest::test_simple::test_another_failing simple_package_integrationtest::test_simple::test_failing "}, ); } #[test] fn with_panic_data_decoding() { let temp = setup_package("panic_decoding"); let output = test_runner(&temp).assert().code(1); assert_stdout_contains( output, indoc! {r#" [..]Compiling[..] [..]Finished[..] Collected 8 test(s) from panic_decoding package Running 8 test(s) from tests/ [FAIL] panic_decoding_integrationtest::test_panic_decoding::test_panic_decoding2 Failure data: 0x80 [FAIL] panic_decoding_integrationtest::test_panic_decoding::test_assert Failure data: "assertion failed: `x`." [FAIL] panic_decoding_integrationtest::test_panic_decoding::test_panic_decoding Failure data: (0x7b ('{'), 0x616161 ('aaa'), 0x800000000000011000000000000000000000000000000000000000000000000, 0x98, 0x7c ('|'), 0x95) [PASS] panic_decoding_integrationtest::test_panic_decoding::test_simple2 (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) [PASS] panic_decoding_integrationtest::test_panic_decoding::test_simple (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) [FAIL] panic_decoding_integrationtest::test_panic_decoding::test_assert_eq Failure data: "assertion `x == y` failed. x: 5 y: 6" [FAIL] panic_decoding_integrationtest::test_panic_decoding::test_assert_message Failure data: "Another identifiable and meaningful error message" [FAIL] panic_decoding_integrationtest::test_panic_decoding::test_assert_eq_message Failure data: "assertion `x == y` failed: An identifiable and meaningful error message x: 5 y: 6" Tests: 2 passed, 6 failed, 0 ignored, 0 filtered out Failures: panic_decoding_integrationtest::test_panic_decoding::test_panic_decoding2 panic_decoding_integrationtest::test_panic_decoding::test_assert panic_decoding_integrationtest::test_panic_decoding::test_panic_decoding panic_decoding_integrationtest::test_panic_decoding::test_assert_eq panic_decoding_integrationtest::test_panic_decoding::test_assert_message panic_decoding_integrationtest::test_panic_decoding::test_assert_eq_message "#}, ); } #[test] fn with_exit_first() { let temp = setup_package("exit_first"); let scarb_path = temp.child("Scarb.toml"); scarb_path .write_str(&formatdoc!( r#" [package] name = "exit_first" version = "0.1.0" [dependencies] starknet = "2.4.0" {} [tool.snforge] exit_first = true "#, get_snforge_std_entry().unwrap() )) .unwrap(); let output = test_runner(&temp).assert().code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from exit_first package Running 2 test(s) from tests/ [FAIL] exit_first_integrationtest::ext_function_test::simple_test Failure data: 0x73696d706c6520636865636b ('simple check') Tests: 0 passed, 1 failed, 0 ignored, 0 filtered out Interrupted execution of 1 test(s). Failures: exit_first_integrationtest::ext_function_test::simple_test "}, ); } #[test] fn with_exit_first_flag() { let temp = setup_package("exit_first"); let output = test_runner(&temp).arg("--exit-first").assert().code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from exit_first package Running 2 test(s) from tests/ [FAIL] exit_first_integrationtest::ext_function_test::simple_test Failure data: 0x73696d706c6520636865636b ('simple check') Tests: 0 passed, 1 failed, 0 ignored, 0 filtered out Interrupted execution of 1 test(s). Failures: exit_first_integrationtest::ext_function_test::simple_test "}, ); } #[test] fn should_panic() { let temp = setup_package("should_panic_test"); let output = test_runner(&temp).assert().code(1); assert_stdout_contains( output, indoc! { r" Collected 14 test(s) from should_panic_test package Running 0 test(s) from src/ Running 14 test(s) from tests/ [FAIL] should_panic_test_integrationtest::should_panic_test::didnt_expect_panic Failure data: 0x756e65787065637465642070616e6963 ('unexpected panic') [FAIL] should_panic_test_integrationtest::should_panic_test::should_panic_expected_contains_error Failure data: Incorrect panic data Actual: [0x46a6158a16a947e5916b2a2ca68501a45e93d7110e81aa2d6438b1c57c879a3, 0x0, 0x77696c6c, 0x4] (will) Expected: [0x46a6158a16a947e5916b2a2ca68501a45e93d7110e81aa2d6438b1c57c879a3, 0x0, 0x546869732077696c6c2070616e6963, 0xf] (This will panic) [FAIL] should_panic_test_integrationtest::should_panic_test::should_panic_byte_array_with_felt Failure data: Incorrect panic data Actual: [0x46a6158a16a947e5916b2a2ca68501a45e93d7110e81aa2d6438b1c57c879a3, 0x0, 0x546869732077696c6c2070616e6963, 0xf] (This will panic) Expected: [0x546869732077696c6c2070616e6963] (This will panic) [FAIL] should_panic_test_integrationtest::should_panic_test::expected_panic_but_didnt_with_expected_multiple Failure data: Expected to panic, but no panic occurred Expected panic data: [0x70616e6963206d657373616765, 0x7365636f6e64206d657373616765] (panic message, second message) [FAIL] should_panic_test_integrationtest::should_panic_test::expected_panic_but_didnt Failure data: Expected to panic, but no panic occurred [PASS] should_panic_test_integrationtest::should_panic_test::should_panic_no_data (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) [PASS] should_panic_test_integrationtest::should_panic_test::should_panic_check_data (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) [FAIL] should_panic_test_integrationtest::should_panic_test::should_panic_not_matching_suffix Failure data: Incorrect panic data Actual: [0x46a6158a16a947e5916b2a2ca68501a45e93d7110e81aa2d6438b1c57c879a3, 0x0, 0x546869732077696c6c2070616e6963, 0xf] (This will panic) Expected: [0x46a6158a16a947e5916b2a2ca68501a45e93d7110e81aa2d6438b1c57c879a3, 0x0, 0x77696c6c2070616e696363, 0xb] (will panicc) [PASS] should_panic_test_integrationtest::should_panic_test::should_panic_match_suffix (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) [PASS] should_panic_test_integrationtest::should_panic_test::should_panic_felt_matching (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) [FAIL] should_panic_test_integrationtest::should_panic_test::should_panic_felt_with_byte_array Failure data: Incorrect panic data Actual: [0x546869732077696c6c2070616e6963] (This will panic) Expected: [0x46a6158a16a947e5916b2a2ca68501a45e93d7110e81aa2d6438b1c57c879a3, 0x0, 0x546869732077696c6c2070616e6963, 0xf] (This will panic) [PASS] should_panic_test_integrationtest::should_panic_test::should_panic_multiple_messages (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) [FAIL] should_panic_test_integrationtest::should_panic_test::expected_panic_but_didnt_with_expected Failure data: Expected to panic, but no panic occurred Expected panic data: [0x70616e6963206d657373616765] (panic message) [FAIL] should_panic_test_integrationtest::should_panic_test::should_panic_with_non_matching_data Failure data: Incorrect panic data Actual: [0x6661696c696e6720636865636b] (failing check) Expected: [0x0] () Tests: 5 passed, 9 failed, 0 ignored, 0 filtered out Failures: should_panic_test_integrationtest::should_panic_test::didnt_expect_panic should_panic_test_integrationtest::should_panic_test::should_panic_expected_contains_error should_panic_test_integrationtest::should_panic_test::should_panic_byte_array_with_felt should_panic_test_integrationtest::should_panic_test::expected_panic_but_didnt_with_expected_multiple should_panic_test_integrationtest::should_panic_test::expected_panic_but_didnt should_panic_test_integrationtest::should_panic_test::should_panic_not_matching_suffix should_panic_test_integrationtest::should_panic_test::should_panic_felt_with_byte_array should_panic_test_integrationtest::should_panic_test::expected_panic_but_didnt_with_expected should_panic_test_integrationtest::should_panic_test::should_panic_with_non_matching_data "}, ); } #[ignore = "TODO Restore this test once there are at least 2 versions supporting v2 macros"] #[test] // #[cfg_attr(feature = "skip_test_for_only_latest_scarb", ignore = "Plugin checks skipped")] fn incompatible_snforge_std_version_warning() { let temp = setup_package("steps"); let manifest_path = temp.child("Scarb.toml"); let mut scarb_toml = fs::read_to_string(&manifest_path) .unwrap() .parse::() .unwrap(); scarb_toml["dev-dependencies"]["snforge_std"] = value("0.45.0"); manifest_path.write_str(&scarb_toml.to_string()).unwrap(); let output = test_runner(&temp).assert().failure(); assert_stdout_contains( output, indoc! {r" [WARNING] Package snforge_std version does not meet the recommended version requirement ^0.[..], [..] [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from steps package Running 2 test(s) from src/ [PASS] steps::tests::steps_less_than_10000000 [..] [FAIL] steps::tests::steps_more_than_10000000 Failure data: Could not reach the end of the program. RunResources has no remaining steps. Suggestion: Consider using the flag `--max-n-steps` to increase allowed limit of steps Tests: 1 passed, 1 failed, 0 ignored, 0 filtered out Failures: steps::tests::steps_more_than_10000000 "}, ); } #[test] fn incompatible_snforge_std_version_error() { let temp = setup_package("steps"); let manifest_path = temp.child("Scarb.toml"); let mut scarb_toml = fs::read_to_string(&manifest_path) .unwrap() .parse::() .unwrap(); scarb_toml["dev-dependencies"]["snforge_std"] = value("0.42.0"); scarb_toml["dev-dependencies"]["snforge_scarb_plugin"] = value("0.42.0"); manifest_path.write_str(&scarb_toml.to_string()).unwrap(); let output = test_runner(&temp).assert().failure(); assert_stdout_contains( output, indoc! {r" [ERROR] Package snforge_std version does not meet the minimum required version >=0.50.0. Please upgrade snforge_std in Scarb.toml "}, ); } #[test] #[cfg_attr( feature = "cairo-native", ignore = "Native runner does not support vm resources tracking" )] fn detailed_resources_flag_cairo_steps() { let temp = setup_package("erc20_package"); let output = test_runner(&temp) .arg("--detailed-resources") .arg("--tracked-resource") .arg("cairo-steps") .assert() .success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from erc20_package package Running 0 test(s) from src/ Running 1 test(s) from tests/ [PASS] erc20_package_integrationtest::test_complex::complex[..] steps: [..] memory holes: [..] builtins: ([..]) syscalls: ([..]) events: (count: [..], keys: [..], data size: [..]) messages: ([..]) Tests: 1 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn detailed_resources_flag() { let temp = setup_package("erc20_package"); let output = test_runner(&temp) .arg("--detailed-resources") .assert() .success(); // Extra check to ensure that the output does not contain VM resources assert!(!output.as_stdout().contains("steps:")); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from erc20_package package Running 0 test(s) from src/ Running 1 test(s) from tests/ [PASS] erc20_package_integrationtest::test_complex::complex[..] sierra gas: [..] syscalls: ([..]) events: (count: [..], keys: [..], data size: [..]) messages: ([..]) Tests: 1 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn detailed_resources_mixed_resources() { let temp = setup_package("forking"); let output = test_runner(&temp) .arg("test_track_resources") .arg("--detailed-resources") .arg("--tracked-resource") .arg("sierra-gas") .assert() .success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from forking package Running 1 test(s) from src/ [PASS] forking::tests::test_track_resources [..] sierra gas: [..] syscalls: ([..]) events: ([..]) messages: ([..]) steps: [..] memory holes: [..] builtins: (Builtin(range_check): [..]) Tests: 1 passed, 0 failed, 0 ignored, [..] filtered out "}, ); } #[test] fn catch_runtime_errors() { let temp = setup_package("runtime_errors_package"); let output = test_runner(&temp).assert(); assert_stdout_contains( output, formatdoc!( r" [..]Compiling[..] [..]Finished[..] [PASS] runtime_errors_package_integrationtest::with_error::catch_no_such_file [..] " ), ); } #[test] fn call_nonexistent_selector() { let temp = setup_package("nonexistent_selector"); let output = test_runner(&temp).assert().code(0); assert_stdout_contains( output, indoc! {r" Collected 1 test(s) from nonexistent_selector package Running 0 test(s) from src/ Running 1 test(s) from tests/ [PASS] nonexistent_selector_integrationtest::test_contract::test_unwrapped_call_contract_syscall [..] Tests: 1 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn exact_printing_pass() { let temp = setup_package("deterministic_output"); let output = test_runner(&temp).arg("pass").assert().code(0); assert_stdout( output, indoc! {r" Collected 2 test(s) from deterministic_output package Running 2 test(s) from src/ [PASS] deterministic_output::test::first_test_pass_y [..] [PASS] deterministic_output::test::second_test_pass_x [..] Tests: 2 passed, 0 failed, 0 ignored, 2 filtered out "}, ); } #[test] fn exact_printing_fail() { let temp = setup_package("deterministic_output"); let output = test_runner(&temp).arg("fail").assert().code(1); assert_stdout( output, indoc! {r" Collected 2 test(s) from deterministic_output package Running 2 test(s) from src/ [FAIL] deterministic_output::test::first_test_fail_x Failure data: 0x73696d706c6520636865636b ('simple check') [FAIL] deterministic_output::test::second_test_fail_y Failure data: 0x73696d706c6520636865636b ('simple check') Tests: 0 passed, 2 failed, 0 ignored, 2 filtered out Failures: deterministic_output::test::first_test_fail_x deterministic_output::test::second_test_fail_y "}, ); } #[test] fn exact_printing_mixed() { let temp = setup_package("deterministic_output"); let output = test_runner(&temp).arg("x").assert().code(1); assert_stdout( output, indoc! {r" Collected 2 test(s) from deterministic_output package Running 2 test(s) from src/ [FAIL] deterministic_output::test::first_test_fail_x Failure data: 0x73696d706c6520636865636b ('simple check') [PASS] deterministic_output::test::second_test_pass_x [..] Tests: 1 passed, 1 failed, 0 ignored, 2 filtered out Failures: deterministic_output::test::first_test_fail_x "}, ); } #[test] #[cfg_attr( feature = "cairo-native", ignore = "Native runner does not support panic backtrace yet" )] fn dispatchers() { let temp = setup_package("dispatchers"); let output = test_runner(&temp).assert().code(1); assert_stdout_contains( output, indoc! {r" Collected 4 test(s) from dispatchers package Running 0 test(s) from src/ Running 4 test(s) from tests/ [FAIL] dispatchers_integrationtest::test::test_unrecoverable_not_possible_to_handle Failure data: Got an exception while executing a hint: Requested contract address [..] is not deployed. [PASS] dispatchers_integrationtest::test::test_error_handled_in_contract [..] [PASS] dispatchers_integrationtest::test::test_handle_and_panic [..] [PASS] dispatchers_integrationtest::test::test_handle_recoverable_in_test [..] Tests: 3 passed, 1 failed, 0 ignored, 0 filtered out Failures: dispatchers_integrationtest::test::test_unrecoverable_not_possible_to_handle "}, ); } #[test] fn test_interact_with_state() { let temp = setup_package("contract_state"); let output = test_runner(&temp).assert().code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 8 test(s) from contract_state package Running 0 test(s) from src/ Running 8 test(s) from tests/ [PASS] contract_state_integrationtest::test_storage_node::test_storage_node [..] [PASS] contract_state_integrationtest::test_state::test_interact_with_state_return [..] [PASS] contract_state_integrationtest::test_state::test_interact_with_state_internal_function [..] [PASS] contract_state_integrationtest::test_state::test_interact_with_initialized_state [..] [PASS] contract_state_integrationtest::test_state::test_interact_with_state [..] [PASS] contract_state_integrationtest::test_state::test_interact_with_state_map [..] [PASS] contract_state_integrationtest::test_state::test_interact_with_state_vec [..] [PASS] contract_state_integrationtest::test_fork::test_fork_contract [..] Tests: 8 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn max_threads_exceeds_available_prints_warning() { let temp = setup_package("simple_package"); let output = test_runner(&temp).args(["--max-threads", "256"]).assert(); assert_stdout_contains( output, "[WARNING] `--max-threads` value (256) is greater than the number of available cores ([..])", ); } #[test] fn max_threads_within_available_no_warning() { let temp = setup_package("simple_package"); let output = test_runner(&temp) .args(["--max-threads", "1"]) .assert() .code(1); assert!(!output.as_stdout().contains("[WARNING]")); } ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace@2.15.2.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_vm_error::Test::test_fork_unwrapped_call_contract_syscall Failure data: Got an exception while executing a hint: Hint Error: Error at pc=0:86: Got an exception while executing a hint: Error at pc=0:86: Got an exception while executing a hint: Requested contract address 0x0000000000000000000000000000000000000000000000000000000000000123 is not deployed. error occurred in forked contract with class hash: 0x1a92e0ec431585e5c19b98679e582ebc07d43681ba1cc9c55dcb5ba0ce721a1 error occurred in contract 'OuterContract' stack backtrace: 0: (inlined) backtrace_vm_error::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 1: (inlined) backtrace_vm_error::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 2: backtrace_vm_error::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [FAIL] backtrace_vm_error::Test::test_unwrapped_call_contract_syscall Failure data: Got an exception while executing a hint: Hint Error: Error at pc=0:86: Got an exception while executing a hint: Error at pc=0:57: Got an exception while executing a hint: Requested contract address 0x0000000000000000000000000000000000000000000000000000000000000123 is not deployed. error occurred in contract 'InnerContract' stack backtrace: 0: (inlined) backtrace_vm_error::InnerContract::inner_call at [..]lib.cairo:48:9 1: (inlined) backtrace_vm_error::InnerContract::InnerContract::inner at [..]lib.cairo:38:13 2: backtrace_vm_error::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:35:5 error occurred in contract 'OuterContract' stack backtrace: 0: (inlined) backtrace_vm_error::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 1: (inlined) backtrace_vm_error::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 2: backtrace_vm_error::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_vm_error::Test::test_fork_unwrapped_call_contract_syscall backtrace_vm_error::Test::test_unwrapped_call_contract_syscall ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace@2.16.0.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_vm_error::Test::test_fork_unwrapped_call_contract_syscall Failure data: Got an exception while executing a hint: Hint Error: Error at pc=0:86: Got an exception while executing a hint: Error at pc=0:86: Got an exception while executing a hint: Requested contract address 0x0000000000000000000000000000000000000000000000000000000000000123 is not deployed. error occurred in forked contract with class hash: 0x1a92e0ec431585e5c19b98679e582ebc07d43681ba1cc9c55dcb5ba0ce721a1 error occurred in contract 'OuterContract' stack backtrace: 0: (inlined) backtrace_vm_error::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 1: (inlined) backtrace_vm_error::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 2: backtrace_vm_error::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [FAIL] backtrace_vm_error::Test::test_unwrapped_call_contract_syscall Failure data: Got an exception while executing a hint: Hint Error: Error at pc=0:86: Got an exception while executing a hint: Error at pc=0:57: Got an exception while executing a hint: Requested contract address 0x0000000000000000000000000000000000000000000000000000000000000123 is not deployed. error occurred in contract 'InnerContract' stack backtrace: 0: (inlined) backtrace_vm_error::InnerContract::inner_call at [..]lib.cairo:48:9 1: (inlined) backtrace_vm_error::InnerContract::InnerContract::inner at [..]lib.cairo:38:13 2: backtrace_vm_error::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:35:5 error occurred in contract 'OuterContract' stack backtrace: 0: (inlined) backtrace_vm_error::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 1: (inlined) backtrace_vm_error::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 2: backtrace_vm_error::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_vm_error::Test::test_fork_unwrapped_call_contract_syscall backtrace_vm_error::Test::test_unwrapped_call_contract_syscall ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace@2.16.1.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_vm_error::Test::test_fork_unwrapped_call_contract_syscall Failure data: Got an exception while executing a hint: Hint Error: Error at pc=0:86: Got an exception while executing a hint: Error at pc=0:86: Got an exception while executing a hint: Requested contract address 0x0000000000000000000000000000000000000000000000000000000000000123 is not deployed. error occurred in forked contract with class hash: 0x1a92e0ec431585e5c19b98679e582ebc07d43681ba1cc9c55dcb5ba0ce721a1 error occurred in contract 'OuterContract' stack backtrace: 0: (inlined) backtrace_vm_error::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 1: (inlined) backtrace_vm_error::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 2: backtrace_vm_error::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [FAIL] backtrace_vm_error::Test::test_unwrapped_call_contract_syscall Failure data: Got an exception while executing a hint: Hint Error: Error at pc=0:86: Got an exception while executing a hint: Error at pc=0:57: Got an exception while executing a hint: Requested contract address 0x0000000000000000000000000000000000000000000000000000000000000123 is not deployed. error occurred in contract 'InnerContract' stack backtrace: 0: (inlined) backtrace_vm_error::InnerContract::inner_call at [..]lib.cairo:48:9 1: (inlined) backtrace_vm_error::InnerContract::InnerContract::inner at [..]lib.cairo:38:13 2: backtrace_vm_error::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:35:5 error occurred in contract 'OuterContract' stack backtrace: 0: (inlined) backtrace_vm_error::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 1: (inlined) backtrace_vm_error::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 2: backtrace_vm_error::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_vm_error::Test::test_fork_unwrapped_call_contract_syscall backtrace_vm_error::Test::test_unwrapped_call_contract_syscall ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace@2.17.0.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_vm_error::Test::test_fork_unwrapped_call_contract_syscall Failure data: Got an exception while executing a hint: Hint Error: Error at pc=0:86: Got an exception while executing a hint: Error at pc=0:86: Got an exception while executing a hint: Requested contract address 0x0000000000000000000000000000000000000000000000000000000000000123 is not deployed. error occurred in forked contract with class hash: 0x1a92e0ec431585e5c19b98679e582ebc07d43681ba1cc9c55dcb5ba0ce721a1 error occurred in contract 'OuterContract' stack backtrace: 0: (inlined) backtrace_vm_error::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 1: (inlined) backtrace_vm_error::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 2: backtrace_vm_error::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [FAIL] backtrace_vm_error::Test::test_unwrapped_call_contract_syscall Failure data: Got an exception while executing a hint: Hint Error: Error at pc=0:86: Got an exception while executing a hint: Error at pc=0:57: Got an exception while executing a hint: Requested contract address 0x0000000000000000000000000000000000000000000000000000000000000123 is not deployed. error occurred in contract 'InnerContract' stack backtrace: 0: (inlined) backtrace_vm_error::InnerContract::inner_call at [..]lib.cairo:48:9 1: (inlined) backtrace_vm_error::InnerContract::InnerContract::inner at [..]lib.cairo:38:13 2: backtrace_vm_error::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:35:5 error occurred in contract 'OuterContract' stack backtrace: 0: (inlined) backtrace_vm_error::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 1: (inlined) backtrace_vm_error::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 2: backtrace_vm_error::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_vm_error::Test::test_fork_unwrapped_call_contract_syscall backtrace_vm_error::Test::test_unwrapped_call_contract_syscall ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace_panic@2.15.2.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_panic::Test::test_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in contract 'InnerContract' stack backtrace: 0: core::panic_with_const_felt252 at [..]lib.cairo:364:5 1: core::panic_with_const_felt252 at [..]lib.cairo:364:5 2: backtrace_panic::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:32:5 error occurred in contract 'OuterContract' stack backtrace: 0: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [IGNORE] backtrace_panic::Test::test_contract_panics_with_should_panic [FAIL] backtrace_panic::Test::test_fork_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in forked contract with class hash: 0x554cb276fb5eb0788344f5431b9a166e2f445d8a91c7aef79d8c77e7eede956 error occurred in contract 'OuterContract' stack backtrace: 0: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_panic::Test::test_contract_panics backtrace_panic::Test::test_fork_contract_panics ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace_panic@2.16.0.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_panic::Test::test_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in contract 'InnerContract' stack backtrace: 0: core::panic_with_const_felt252 at [..]lib.cairo:360:5 1: core::panic_with_const_felt252 at [..]lib.cairo:360:5 2: backtrace_panic::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:32:5 error occurred in contract 'OuterContract' stack backtrace: 0: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [IGNORE] backtrace_panic::Test::test_contract_panics_with_should_panic [FAIL] backtrace_panic::Test::test_fork_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in forked contract with class hash: 0x554cb276fb5eb0788344f5431b9a166e2f445d8a91c7aef79d8c77e7eede956 error occurred in contract 'OuterContract' stack backtrace: 0: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_panic::Test::test_contract_panics backtrace_panic::Test::test_fork_contract_panics ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace_panic@2.16.1.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_panic::Test::test_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in contract 'InnerContract' stack backtrace: 0: core::panic_with_const_felt252 at [..]lib.cairo:360:5 1: core::panic_with_const_felt252 at [..]lib.cairo:360:5 2: backtrace_panic::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:32:5 error occurred in contract 'OuterContract' stack backtrace: 0: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [IGNORE] backtrace_panic::Test::test_contract_panics_with_should_panic [FAIL] backtrace_panic::Test::test_fork_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in forked contract with class hash: 0x554cb276fb5eb0788344f5431b9a166e2f445d8a91c7aef79d8c77e7eede956 error occurred in contract 'OuterContract' stack backtrace: 0: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_panic::Test::test_contract_panics backtrace_panic::Test::test_fork_contract_panics ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace_panic@2.17.0.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_panic::Test::test_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in contract 'InnerContract' stack backtrace: 0: core::panic_with_const_felt252 at [..]lib.cairo:360:5 1: core::panic_with_const_felt252 at [..]lib.cairo:360:5 2: backtrace_panic::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:32:5 error occurred in contract 'OuterContract' stack backtrace: 0: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [IGNORE] backtrace_panic::Test::test_contract_panics_with_should_panic [FAIL] backtrace_panic::Test::test_fork_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in forked contract with class hash: 0x554cb276fb5eb0788344f5431b9a166e2f445d8a91c7aef79d8c77e7eede956 error occurred in contract 'OuterContract' stack backtrace: 0: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_panic::Test::test_contract_panics backtrace_panic::Test::test_fork_contract_panics ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace_panic_without_inlines@2.15.2.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_panic::Test::test_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in contract 'InnerContract' stack backtrace: 0: core::array_inline_macro at [..]lib.cairo:350:11 1: core::assert at [..]lib.cairo:377:9 2: backtrace_panic::InnerContract::inner_call at [..]lib.cairo:40:9 3: backtrace_panic::InnerContract::unsafe_new_contract_state at [..]lib.cairo:29:5 4: backtrace_panic::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:32:5 error occurred in contract 'OuterContract' stack backtrace: 0: core::starknet::SyscallResultTraitImpl::unwrap_syscall at [..]starknet.cairo:135:52 1: backtrace_panic::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 2: backtrace_panic::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 3: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [IGNORE] backtrace_panic::Test::test_contract_panics_with_should_panic [FAIL] backtrace_panic::Test::test_fork_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in forked contract with class hash: 0x554cb276fb5eb0788344f5431b9a166e2f445d8a91c7aef79d8c77e7eede956 error occurred in contract 'OuterContract' stack backtrace: 0: core::starknet::SyscallResultTraitImpl::unwrap_syscall at [..]starknet.cairo:135:52 1: backtrace_panic::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 2: backtrace_panic::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 3: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_panic::Test::test_contract_panics backtrace_panic::Test::test_fork_contract_panics ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace_panic_without_inlines@2.16.0.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_panic::Test::test_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in contract 'InnerContract' stack backtrace: 0: core::array_inline_macro at [..]lib.cairo:346:11 1: core::assert at [..]lib.cairo:373:9 2: backtrace_panic::InnerContract::inner_call at [..]lib.cairo:40:9 3: backtrace_panic::InnerContract::unsafe_new_contract_state at [..]lib.cairo:29:5 4: backtrace_panic::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:32:5 error occurred in contract 'OuterContract' stack backtrace: 0: core::starknet::SyscallResultTraitImpl::unwrap_syscall at [..]starknet.cairo:135:52 1: backtrace_panic::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 2: backtrace_panic::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 3: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [IGNORE] backtrace_panic::Test::test_contract_panics_with_should_panic [FAIL] backtrace_panic::Test::test_fork_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in forked contract with class hash: 0x554cb276fb5eb0788344f5431b9a166e2f445d8a91c7aef79d8c77e7eede956 error occurred in contract 'OuterContract' stack backtrace: 0: core::starknet::SyscallResultTraitImpl::unwrap_syscall at [..]starknet.cairo:135:52 1: backtrace_panic::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 2: backtrace_panic::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 3: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_panic::Test::test_contract_panics backtrace_panic::Test::test_fork_contract_panics ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace_panic_without_inlines@2.16.1.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_panic::Test::test_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in contract 'InnerContract' stack backtrace: 0: core::array_inline_macro at [..]lib.cairo:346:11 1: core::assert at [..]lib.cairo:373:9 2: backtrace_panic::InnerContract::inner_call at [..]lib.cairo:40:9 3: backtrace_panic::InnerContract::unsafe_new_contract_state at [..]lib.cairo:29:5 4: backtrace_panic::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:32:5 error occurred in contract 'OuterContract' stack backtrace: 0: core::starknet::SyscallResultTraitImpl::unwrap_syscall at [..]starknet.cairo:135:52 1: backtrace_panic::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 2: backtrace_panic::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 3: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [IGNORE] backtrace_panic::Test::test_contract_panics_with_should_panic [FAIL] backtrace_panic::Test::test_fork_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in forked contract with class hash: 0x554cb276fb5eb0788344f5431b9a166e2f445d8a91c7aef79d8c77e7eede956 error occurred in contract 'OuterContract' stack backtrace: 0: core::starknet::SyscallResultTraitImpl::unwrap_syscall at [..]starknet.cairo:135:52 1: backtrace_panic::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 2: backtrace_panic::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 3: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_panic::Test::test_contract_panics backtrace_panic::Test::test_fork_contract_panics ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace_panic_without_inlines@2.17.0.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_panic::Test::test_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in contract 'InnerContract' stack backtrace: 0: core::array_inline_macro at [..]lib.cairo:346:11 1: core::assert at [..]lib.cairo:373:9 2: backtrace_panic::InnerContract::inner_call at [..]lib.cairo:40:9 3: backtrace_panic::InnerContract::unsafe_new_contract_state at [..]lib.cairo:29:5 4: backtrace_panic::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:32:5 error occurred in contract 'OuterContract' stack backtrace: 0: core::starknet::SyscallResultTraitImpl::unwrap_syscall at [..]starknet.cairo:135:52 1: backtrace_panic::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 2: backtrace_panic::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 3: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [IGNORE] backtrace_panic::Test::test_contract_panics_with_should_panic [FAIL] backtrace_panic::Test::test_fork_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in forked contract with class hash: 0x554cb276fb5eb0788344f5431b9a166e2f445d8a91c7aef79d8c77e7eede956 error occurred in contract 'OuterContract' stack backtrace: 0: core::starknet::SyscallResultTraitImpl::unwrap_syscall at [..]starknet.cairo:135:52 1: backtrace_panic::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 2: backtrace_panic::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 3: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_panic::Test::test_contract_panics backtrace_panic::Test::test_fork_contract_panics ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace_panic_without_optimizations@2.15.2.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_panic::Test::test_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in contract 'InnerContract' stack backtrace: 0: core::array_inline_macro at [..]lib.cairo:350:11 1: core::assert at [..]lib.cairo:377:9 2: backtrace_panic::InnerContract::inner_call at [..]lib.cairo:40:9 3: backtrace_panic::InnerContract::unsafe_new_contract_state at [..]lib.cairo:29:5 4: backtrace_panic::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:32:5 error occurred in contract 'OuterContract' stack backtrace: 0: core::starknet::SyscallResultTraitImpl::unwrap_syscall at [..]starknet.cairo:135:52 1: backtrace_panic::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 2: backtrace_panic::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 3: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [IGNORE] backtrace_panic::Test::test_contract_panics_with_should_panic [FAIL] backtrace_panic::Test::test_fork_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in forked contract with class hash: 0x554cb276fb5eb0788344f5431b9a166e2f445d8a91c7aef79d8c77e7eede956 error occurred in contract 'OuterContract' stack backtrace: 0: core::starknet::SyscallResultTraitImpl::unwrap_syscall at [..]starknet.cairo:135:52 1: backtrace_panic::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 2: backtrace_panic::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 3: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_panic::Test::test_contract_panics backtrace_panic::Test::test_fork_contract_panics ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace_panic_without_optimizations@2.16.0.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_panic::Test::test_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in contract 'InnerContract' stack backtrace: 0: core::array_inline_macro at [..]lib.cairo:346:11 1: core::assert at [..]lib.cairo:373:9 2: backtrace_panic::InnerContract::inner_call at [..]lib.cairo:40:9 3: backtrace_panic::InnerContract::unsafe_new_contract_state at [..]lib.cairo:29:5 4: backtrace_panic::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:32:5 error occurred in contract 'OuterContract' stack backtrace: 0: core::starknet::SyscallResultTraitImpl::unwrap_syscall at [..]starknet.cairo:135:52 1: backtrace_panic::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 2: backtrace_panic::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 3: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [IGNORE] backtrace_panic::Test::test_contract_panics_with_should_panic [FAIL] backtrace_panic::Test::test_fork_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in forked contract with class hash: 0x554cb276fb5eb0788344f5431b9a166e2f445d8a91c7aef79d8c77e7eede956 error occurred in contract 'OuterContract' stack backtrace: 0: core::starknet::SyscallResultTraitImpl::unwrap_syscall at [..]starknet.cairo:135:52 1: backtrace_panic::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 2: backtrace_panic::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 3: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_panic::Test::test_contract_panics backtrace_panic::Test::test_fork_contract_panics ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace_panic_without_optimizations@2.16.1.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_panic::Test::test_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in contract 'InnerContract' stack backtrace: 0: core::array_inline_macro at [..]lib.cairo:346:11 1: core::assert at [..]lib.cairo:373:9 2: backtrace_panic::InnerContract::inner_call at [..]lib.cairo:40:9 3: backtrace_panic::InnerContract::unsafe_new_contract_state at [..]lib.cairo:29:5 4: backtrace_panic::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:32:5 error occurred in contract 'OuterContract' stack backtrace: 0: core::starknet::SyscallResultTraitImpl::unwrap_syscall at [..]starknet.cairo:135:52 1: backtrace_panic::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 2: backtrace_panic::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 3: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [IGNORE] backtrace_panic::Test::test_contract_panics_with_should_panic [FAIL] backtrace_panic::Test::test_fork_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in forked contract with class hash: 0x554cb276fb5eb0788344f5431b9a166e2f445d8a91c7aef79d8c77e7eede956 error occurred in contract 'OuterContract' stack backtrace: 0: core::starknet::SyscallResultTraitImpl::unwrap_syscall at [..]starknet.cairo:135:52 1: backtrace_panic::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 2: backtrace_panic::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 3: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_panic::Test::test_contract_panics backtrace_panic::Test::test_fork_contract_panics ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace_panic_without_optimizations@2.17.0.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_panic::Test::test_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in contract 'InnerContract' stack backtrace: 0: core::array_inline_macro at [..]lib.cairo:346:11 1: core::assert at [..]lib.cairo:373:9 2: backtrace_panic::InnerContract::inner_call at [..]lib.cairo:40:9 3: backtrace_panic::InnerContract::unsafe_new_contract_state at [..]lib.cairo:29:5 4: backtrace_panic::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:32:5 error occurred in contract 'OuterContract' stack backtrace: 0: core::starknet::SyscallResultTraitImpl::unwrap_syscall at [..]starknet.cairo:135:52 1: backtrace_panic::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 2: backtrace_panic::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 3: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [IGNORE] backtrace_panic::Test::test_contract_panics_with_should_panic [FAIL] backtrace_panic::Test::test_fork_contract_panics Failure data: (0x417373657274206661696c6564 ('Assert failed'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) error occurred in forked contract with class hash: 0x554cb276fb5eb0788344f5431b9a166e2f445d8a91c7aef79d8c77e7eede956 error occurred in contract 'OuterContract' stack backtrace: 0: core::starknet::SyscallResultTraitImpl::unwrap_syscall at [..]starknet.cairo:135:52 1: backtrace_panic::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 2: backtrace_panic::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 3: backtrace_panic::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_panic::Test::test_contract_panics backtrace_panic::Test::test_fork_contract_panics ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace_without_inlines@2.15.2.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_vm_error::Test::test_fork_unwrapped_call_contract_syscall Failure data: Got an exception while executing a hint: Hint Error: Error at pc=0:280: Got an exception while executing a hint: Error at pc=0:86: Got an exception while executing a hint: Requested contract address 0x0000000000000000000000000000000000000000000000000000000000000123 is not deployed. Cairo traceback (most recent call last): Unknown location (pc=0:50) Unknown location (pc=0:207) error occurred in forked contract with class hash: 0x1a92e0ec431585e5c19b98679e582ebc07d43681ba1cc9c55dcb5ba0ce721a1 error occurred in contract 'OuterContract' stack backtrace: 0: backtrace_vm_error::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 1: backtrace_vm_error::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 2: backtrace_vm_error::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [FAIL] backtrace_vm_error::Test::test_unwrapped_call_contract_syscall Failure data: Got an exception while executing a hint: Hint Error: Error at pc=0:280: Got an exception while executing a hint: Error at pc=0:184: Got an exception while executing a hint: Requested contract address 0x0000000000000000000000000000000000000000000000000000000000000123 is not deployed. Cairo traceback (most recent call last): Unknown location (pc=0:43) Unknown location (pc=0:133) Cairo traceback (most recent call last): Unknown location (pc=0:50) Unknown location (pc=0:207) error occurred in contract 'InnerContract' stack backtrace: 0: backtrace_vm_error::InnerContract::inner_call at [..]lib.cairo:48:9 1: backtrace_vm_error::InnerContract::InnerContract::inner at [..]lib.cairo:38:13 2: backtrace_vm_error::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:35:5 error occurred in contract 'OuterContract' stack backtrace: 0: backtrace_vm_error::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 1: backtrace_vm_error::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 2: backtrace_vm_error::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_vm_error::Test::test_fork_unwrapped_call_contract_syscall backtrace_vm_error::Test::test_unwrapped_call_contract_syscall ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace_without_inlines@2.16.0.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_vm_error::Test::test_fork_unwrapped_call_contract_syscall Failure data: Got an exception while executing a hint: Hint Error: Error at pc=0:280: Got an exception while executing a hint: Error at pc=0:86: Got an exception while executing a hint: Requested contract address 0x0000000000000000000000000000000000000000000000000000000000000123 is not deployed. Cairo traceback (most recent call last): Unknown location (pc=0:50) Unknown location (pc=0:207) error occurred in forked contract with class hash: 0x1a92e0ec431585e5c19b98679e582ebc07d43681ba1cc9c55dcb5ba0ce721a1 error occurred in contract 'OuterContract' stack backtrace: 0: backtrace_vm_error::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 1: backtrace_vm_error::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 2: backtrace_vm_error::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [FAIL] backtrace_vm_error::Test::test_unwrapped_call_contract_syscall Failure data: Got an exception while executing a hint: Hint Error: Error at pc=0:280: Got an exception while executing a hint: Error at pc=0:184: Got an exception while executing a hint: Requested contract address 0x0000000000000000000000000000000000000000000000000000000000000123 is not deployed. Cairo traceback (most recent call last): Unknown location (pc=0:43) Unknown location (pc=0:133) Cairo traceback (most recent call last): Unknown location (pc=0:50) Unknown location (pc=0:207) error occurred in contract 'InnerContract' stack backtrace: 0: backtrace_vm_error::InnerContract::inner_call at [..]lib.cairo:48:9 1: backtrace_vm_error::InnerContract::InnerContract::inner at [..]lib.cairo:38:13 2: backtrace_vm_error::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:35:5 error occurred in contract 'OuterContract' stack backtrace: 0: backtrace_vm_error::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 1: backtrace_vm_error::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 2: backtrace_vm_error::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_vm_error::Test::test_fork_unwrapped_call_contract_syscall backtrace_vm_error::Test::test_unwrapped_call_contract_syscall ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace_without_inlines@2.16.1.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_vm_error::Test::test_fork_unwrapped_call_contract_syscall Failure data: Got an exception while executing a hint: Hint Error: Error at pc=0:280: Got an exception while executing a hint: Error at pc=0:86: Got an exception while executing a hint: Requested contract address 0x0000000000000000000000000000000000000000000000000000000000000123 is not deployed. Cairo traceback (most recent call last): Unknown location (pc=0:50) Unknown location (pc=0:207) error occurred in forked contract with class hash: 0x1a92e0ec431585e5c19b98679e582ebc07d43681ba1cc9c55dcb5ba0ce721a1 error occurred in contract 'OuterContract' stack backtrace: 0: backtrace_vm_error::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 1: backtrace_vm_error::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 2: backtrace_vm_error::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [FAIL] backtrace_vm_error::Test::test_unwrapped_call_contract_syscall Failure data: Got an exception while executing a hint: Hint Error: Error at pc=0:280: Got an exception while executing a hint: Error at pc=0:184: Got an exception while executing a hint: Requested contract address 0x0000000000000000000000000000000000000000000000000000000000000123 is not deployed. Cairo traceback (most recent call last): Unknown location (pc=0:43) Unknown location (pc=0:133) Cairo traceback (most recent call last): Unknown location (pc=0:50) Unknown location (pc=0:207) error occurred in contract 'InnerContract' stack backtrace: 0: backtrace_vm_error::InnerContract::inner_call at [..]lib.cairo:48:9 1: backtrace_vm_error::InnerContract::InnerContract::inner at [..]lib.cairo:38:13 2: backtrace_vm_error::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:35:5 error occurred in contract 'OuterContract' stack backtrace: 0: backtrace_vm_error::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 1: backtrace_vm_error::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 2: backtrace_vm_error::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_vm_error::Test::test_fork_unwrapped_call_contract_syscall backtrace_vm_error::Test::test_unwrapped_call_contract_syscall ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_backtrace_without_inlines@2.17.0.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [FAIL] backtrace_vm_error::Test::test_fork_unwrapped_call_contract_syscall Failure data: Got an exception while executing a hint: Hint Error: Error at pc=0:280: Got an exception while executing a hint: Error at pc=0:86: Got an exception while executing a hint: Requested contract address 0x0000000000000000000000000000000000000000000000000000000000000123 is not deployed. Cairo traceback (most recent call last): Unknown location (pc=0:50) Unknown location (pc=0:207) error occurred in forked contract with class hash: 0x1a92e0ec431585e5c19b98679e582ebc07d43681ba1cc9c55dcb5ba0ce721a1 error occurred in contract 'OuterContract' stack backtrace: 0: backtrace_vm_error::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 1: backtrace_vm_error::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 2: backtrace_vm_error::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 [FAIL] backtrace_vm_error::Test::test_unwrapped_call_contract_syscall Failure data: Got an exception while executing a hint: Hint Error: Error at pc=0:280: Got an exception while executing a hint: Error at pc=0:184: Got an exception while executing a hint: Requested contract address 0x0000000000000000000000000000000000000000000000000000000000000123 is not deployed. Cairo traceback (most recent call last): Unknown location (pc=0:43) Unknown location (pc=0:133) Cairo traceback (most recent call last): Unknown location (pc=0:50) Unknown location (pc=0:207) error occurred in contract 'InnerContract' stack backtrace: 0: backtrace_vm_error::InnerContract::inner_call at [..]lib.cairo:48:9 1: backtrace_vm_error::InnerContract::InnerContract::inner at [..]lib.cairo:38:13 2: backtrace_vm_error::InnerContract::__wrapper__InnerContract__inner at [..]lib.cairo:35:5 error occurred in contract 'OuterContract' stack backtrace: 0: backtrace_vm_error::IInnerContractDispatcherImpl::inner at [..]lib.cairo:22:1 1: backtrace_vm_error::OuterContract::OuterContract::outer at [..]lib.cairo:17:13 2: backtrace_vm_error::OuterContract::__wrapper__OuterContract__outer at [..]lib.cairo:13:5 Failures: backtrace_vm_error::Test::test_fork_unwrapped_call_contract_syscall backtrace_vm_error::Test::test_unwrapped_call_contract_syscall ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_handled_error_not_display@2.15.2.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [PASS] dispatchers_integrationtest::test::test_handle_and_panic (l1_gas: ~0, l1_data_gas: ~288, l2_gas: ~1028560) error occurred in contract 'ErrorHandler' stack backtrace: 0: core::panic_with_const_felt252 at [..]lib.cairo:364:5 1: core::panic_with_const_felt252 at [..]lib.cairo:364:5 2: dispatchers::error_handler::ErrorHandler::ErrorHandler::catch_panic_and_fail at [..]error_handler.cairo:50:21 3: dispatchers::error_handler::ErrorHandler::__wrapper__ErrorHandler__catch_panic_and_fail at [..]error_handler.cairo:27:5 ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_handled_error_not_display@2.16.0.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [PASS] dispatchers_integrationtest::test::test_handle_and_panic (l1_gas: ~0, l1_data_gas: ~288, l2_gas: ~1028560) error occurred in contract 'ErrorHandler' stack backtrace: 0: core::panic_with_const_felt252 at [..]lib.cairo:360:5 1: core::panic_with_const_felt252 at [..]lib.cairo:360:5 2: dispatchers::error_handler::ErrorHandler::ErrorHandler::catch_panic_and_fail at [..]error_handler.cairo:50:21 3: dispatchers::error_handler::ErrorHandler::__wrapper__ErrorHandler__catch_panic_and_fail at [..]error_handler.cairo:27:5 ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_handled_error_not_display@2.16.1.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [PASS] dispatchers_integrationtest::test::test_handle_and_panic (l1_gas: ~0, l1_data_gas: ~288, l2_gas: ~1028560) error occurred in contract 'ErrorHandler' stack backtrace: 0: core::panic_with_const_felt252 at [..]lib.cairo:360:5 1: core::panic_with_const_felt252 at [..]lib.cairo:360:5 2: dispatchers::error_handler::ErrorHandler::ErrorHandler::catch_panic_and_fail at [..]error_handler.cairo:50:21 3: dispatchers::error_handler::ErrorHandler::__wrapper__ErrorHandler__catch_panic_and_fail at [..]error_handler.cairo:27:5 ================================================ FILE: crates/forge/tests/e2e/snapshots/backtrace/main__e2e__backtrace__snap_test_handled_error_not_display@2.17.0.snap ================================================ --- source: crates/forge/tests/e2e/backtrace.rs expression: stdout --- [PASS] dispatchers_integrationtest::test::test_handle_and_panic (l1_gas: ~0, l1_data_gas: ~288, l2_gas: ~1028560) error occurred in contract 'ErrorHandler' stack backtrace: 0: core::panic_with_const_felt252 at [..]lib.cairo:360:5 1: core::panic_with_const_felt252 at [..]lib.cairo:360:5 2: dispatchers::error_handler::ErrorHandler::ErrorHandler::catch_panic_and_fail at [..]error_handler.cairo:50:21 3: dispatchers::error_handler::ErrorHandler::__wrapper__ErrorHandler__catch_panic_and_fail at [..]error_handler.cairo:27:5 ================================================ FILE: crates/forge/tests/e2e/snapshots/gas_report/main__e2e__gas_report__snap_basic@2.15.2.snap ================================================ --- source: crates/forge/tests/e2e/gas_report.rs expression: stdout --- [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~974690) ╭------------------------+-------+-------+-------+---------+---------╮ | HelloStarknet Contract | | | | | | +====================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |------------------------+-------+-------+-------+---------+---------| | get_balance | 21410 | 21410 | 21410 | 0 | 2 | |------------------------+-------+-------+-------+---------+---------| | increase_balance | 67980 | 67980 | 67980 | 0 | 1 | ╰------------------------+-------+-------+-------+---------+---------╯ ================================================ FILE: crates/forge/tests/e2e/snapshots/gas_report/main__e2e__gas_report__snap_basic@2.16.0.snap ================================================ --- source: crates/forge/tests/e2e/gas_report.rs expression: stdout --- [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~974690) ╭------------------------+-------+-------+-------+---------+---------╮ | HelloStarknet Contract | | | | | | +====================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |------------------------+-------+-------+-------+---------+---------| | get_balance | 21410 | 21410 | 21410 | 0 | 2 | |------------------------+-------+-------+-------+---------+---------| | increase_balance | 67980 | 67980 | 67980 | 0 | 1 | ╰------------------------+-------+-------+-------+---------+---------╯ ================================================ FILE: crates/forge/tests/e2e/snapshots/gas_report/main__e2e__gas_report__snap_basic@2.16.1.snap ================================================ --- source: crates/forge/tests/e2e/gas_report.rs expression: stdout --- [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~974690) ╭------------------------+-------+-------+-------+---------+---------╮ | HelloStarknet Contract | | | | | | +====================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |------------------------+-------+-------+-------+---------+---------| | get_balance | 21410 | 21410 | 21410 | 0 | 2 | |------------------------+-------+-------+-------+---------+---------| | increase_balance | 67980 | 67980 | 67980 | 0 | 1 | ╰------------------------+-------+-------+-------+---------+---------╯ ================================================ FILE: crates/forge/tests/e2e/snapshots/gas_report/main__e2e__gas_report__snap_basic@2.17.0.snap ================================================ --- source: crates/forge/tests/e2e/gas_report.rs expression: stdout --- [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~974690) ╭------------------------+-------+-------+-------+---------+---------╮ | HelloStarknet Contract | | | | | | +====================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |------------------------+-------+-------+-------+---------+---------| | get_balance | 21410 | 21410 | 21410 | 0 | 2 | |------------------------+-------+-------+-------+---------+---------| | increase_balance | 67980 | 67980 | 67980 | 0 | 1 | ╰------------------------+-------+-------+-------+---------+---------╯ ================================================ FILE: crates/forge/tests/e2e/snapshots/gas_report/main__e2e__gas_report__snap_fork@2.15.2.snap ================================================ --- source: crates/forge/tests/e2e/gas_report.rs expression: stdout --- [PASS] forking::tests::test_track_resources (l1_gas: ~0, l1_data_gas: ~320, l2_gas: ~1403270) ╭---------------------------+-------+-------+-------+---------+---------╮ | forked contract | | | | | | | (class hash: 0x06a7…1550) | | | | | | +=======================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |---------------------------+-------+-------+-------+---------+---------| | get_balance | 40000 | 40000 | 40000 | 0 | 1 | |---------------------------+-------+-------+-------+---------+---------| | increase_balance | 80000 | 80000 | 80000 | 0 | 1 | ╰---------------------------+-------+-------+-------+---------+---------╯ ╭---------------------------+-------+-------+-------+---------+---------╮ | forked contract | | | | | | | (class hash: 0x07aa…af4b) | | | | | | +=======================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |---------------------------+-------+-------+-------+---------+---------| | get_balance | 21910 | 21910 | 21910 | 0 | 1 | |---------------------------+-------+-------+-------+---------+---------| | increase_balance | 68880 | 68880 | 68880 | 0 | 1 | ╰---------------------------+-------+-------+-------+---------+---------╯ ================================================ FILE: crates/forge/tests/e2e/snapshots/gas_report/main__e2e__gas_report__snap_fork@2.16.0.snap ================================================ --- source: crates/forge/tests/e2e/gas_report.rs expression: stdout --- [PASS] forking::tests::test_track_resources (l1_gas: ~0, l1_data_gas: ~320, l2_gas: ~1403270) ╭---------------------------+-------+-------+-------+---------+---------╮ | forked contract | | | | | | | (class hash: 0x06a7…1550) | | | | | | +=======================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |---------------------------+-------+-------+-------+---------+---------| | get_balance | 40000 | 40000 | 40000 | 0 | 1 | |---------------------------+-------+-------+-------+---------+---------| | increase_balance | 80000 | 80000 | 80000 | 0 | 1 | ╰---------------------------+-------+-------+-------+---------+---------╯ ╭---------------------------+-------+-------+-------+---------+---------╮ | forked contract | | | | | | | (class hash: 0x07aa…af4b) | | | | | | +=======================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |---------------------------+-------+-------+-------+---------+---------| | get_balance | 21910 | 21910 | 21910 | 0 | 1 | |---------------------------+-------+-------+-------+---------+---------| | increase_balance | 68880 | 68880 | 68880 | 0 | 1 | ╰---------------------------+-------+-------+-------+---------+---------╯ ================================================ FILE: crates/forge/tests/e2e/snapshots/gas_report/main__e2e__gas_report__snap_fork@2.16.1.snap ================================================ --- source: crates/forge/tests/e2e/gas_report.rs expression: stdout --- [PASS] forking::tests::test_track_resources (l1_gas: ~0, l1_data_gas: ~320, l2_gas: ~1403270) ╭---------------------------+-------+-------+-------+---------+---------╮ | forked contract | | | | | | | (class hash: 0x06a7…1550) | | | | | | +=======================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |---------------------------+-------+-------+-------+---------+---------| | get_balance | 40000 | 40000 | 40000 | 0 | 1 | |---------------------------+-------+-------+-------+---------+---------| | increase_balance | 80000 | 80000 | 80000 | 0 | 1 | ╰---------------------------+-------+-------+-------+---------+---------╯ ╭---------------------------+-------+-------+-------+---------+---------╮ | forked contract | | | | | | | (class hash: 0x07aa…af4b) | | | | | | +=======================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |---------------------------+-------+-------+-------+---------+---------| | get_balance | 21910 | 21910 | 21910 | 0 | 1 | |---------------------------+-------+-------+-------+---------+---------| | increase_balance | 68880 | 68880 | 68880 | 0 | 1 | ╰---------------------------+-------+-------+-------+---------+---------╯ ================================================ FILE: crates/forge/tests/e2e/snapshots/gas_report/main__e2e__gas_report__snap_fork@2.17.0.snap ================================================ --- source: crates/forge/tests/e2e/gas_report.rs expression: stdout --- [PASS] forking::tests::test_track_resources (l1_gas: ~0, l1_data_gas: ~320, l2_gas: ~1403270) ╭---------------------------+-------+-------+-------+---------+---------╮ | forked contract | | | | | | | (class hash: 0x06a7…1550) | | | | | | +=======================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |---------------------------+-------+-------+-------+---------+---------| | get_balance | 40000 | 40000 | 40000 | 0 | 1 | |---------------------------+-------+-------+-------+---------+---------| | increase_balance | 80000 | 80000 | 80000 | 0 | 1 | ╰---------------------------+-------+-------+-------+---------+---------╯ ╭---------------------------+-------+-------+-------+---------+---------╮ | forked contract | | | | | | | (class hash: 0x07aa…af4b) | | | | | | +=======================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |---------------------------+-------+-------+-------+---------+---------| | get_balance | 21910 | 21910 | 21910 | 0 | 1 | |---------------------------+-------+-------+-------+---------+---------| | increase_balance | 68880 | 68880 | 68880 | 0 | 1 | ╰---------------------------+-------+-------+-------+---------+---------╯ ================================================ FILE: crates/forge/tests/e2e/snapshots/gas_report/main__e2e__gas_report__snap_multiple_contracts_and_constructor@2.15.2.snap ================================================ --- source: crates/forge/tests/e2e/gas_report.rs expression: stdout --- [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke_proxy (l1_gas: ~0, l1_data_gas: ~288, l2_gas: ~1334740) ╭------------------------+-------+-------+-------+---------+---------╮ | HelloStarknet Contract | | | | | | +====================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |------------------------+-------+-------+-------+---------+---------| | get_block_number | 15780 | 15780 | 15780 | 0 | 2 | ╰------------------------+-------+-------+-------+---------+---------╯ ╭-----------------------------+--------+--------+--------+---------+---------╮ | HelloStarknetProxy Contract | | | | | | +============================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |-----------------------------+--------+--------+--------+---------+---------| | constructor | 49620 | 49620 | 49620 | 0 | 1 | |-----------------------------+--------+--------+--------+---------+---------| | get_block_number | 133350 | 133350 | 133350 | 0 | 2 | ╰-----------------------------+--------+--------+--------+---------+---------╯ ================================================ FILE: crates/forge/tests/e2e/snapshots/gas_report/main__e2e__gas_report__snap_multiple_contracts_and_constructor@2.16.0.snap ================================================ --- source: crates/forge/tests/e2e/gas_report.rs expression: stdout --- [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke_proxy (l1_gas: ~0, l1_data_gas: ~288, l2_gas: ~1334740) ╭------------------------+-------+-------+-------+---------+---------╮ | HelloStarknet Contract | | | | | | +====================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |------------------------+-------+-------+-------+---------+---------| | get_block_number | 15780 | 15780 | 15780 | 0 | 2 | ╰------------------------+-------+-------+-------+---------+---------╯ ╭-----------------------------+--------+--------+--------+---------+---------╮ | HelloStarknetProxy Contract | | | | | | +============================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |-----------------------------+--------+--------+--------+---------+---------| | constructor | 49620 | 49620 | 49620 | 0 | 1 | |-----------------------------+--------+--------+--------+---------+---------| | get_block_number | 133350 | 133350 | 133350 | 0 | 2 | ╰-----------------------------+--------+--------+--------+---------+---------╯ ================================================ FILE: crates/forge/tests/e2e/snapshots/gas_report/main__e2e__gas_report__snap_multiple_contracts_and_constructor@2.16.1.snap ================================================ --- source: crates/forge/tests/e2e/gas_report.rs expression: stdout --- [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke_proxy (l1_gas: ~0, l1_data_gas: ~288, l2_gas: ~1334740) ╭------------------------+-------+-------+-------+---------+---------╮ | HelloStarknet Contract | | | | | | +====================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |------------------------+-------+-------+-------+---------+---------| | get_block_number | 15780 | 15780 | 15780 | 0 | 2 | ╰------------------------+-------+-------+-------+---------+---------╯ ╭-----------------------------+--------+--------+--------+---------+---------╮ | HelloStarknetProxy Contract | | | | | | +============================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |-----------------------------+--------+--------+--------+---------+---------| | constructor | 49620 | 49620 | 49620 | 0 | 1 | |-----------------------------+--------+--------+--------+---------+---------| | get_block_number | 133350 | 133350 | 133350 | 0 | 2 | ╰-----------------------------+--------+--------+--------+---------+---------╯ ================================================ FILE: crates/forge/tests/e2e/snapshots/gas_report/main__e2e__gas_report__snap_multiple_contracts_and_constructor@2.17.0.snap ================================================ --- source: crates/forge/tests/e2e/gas_report.rs expression: stdout --- [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke_proxy (l1_gas: ~0, l1_data_gas: ~288, l2_gas: ~1334740) ╭------------------------+-------+-------+-------+---------+---------╮ | HelloStarknet Contract | | | | | | +====================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |------------------------+-------+-------+-------+---------+---------| | get_block_number | 15780 | 15780 | 15780 | 0 | 2 | ╰------------------------+-------+-------+-------+---------+---------╯ ╭-----------------------------+--------+--------+--------+---------+---------╮ | HelloStarknetProxy Contract | | | | | | +============================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |-----------------------------+--------+--------+--------+---------+---------| | constructor | 49620 | 49620 | 49620 | 0 | 1 | |-----------------------------+--------+--------+--------+---------+---------| | get_block_number | 133350 | 133350 | 133350 | 0 | 2 | ╰-----------------------------+--------+--------+--------+---------+---------╯ ================================================ FILE: crates/forge/tests/e2e/snapshots/gas_report/main__e2e__gas_report__snap_recursive_calls@2.15.2.snap ================================================ --- source: crates/forge/tests/e2e/gas_report.rs expression: stdout --- [PASS] debugging_integrationtest::test_trace::test_debugging_trace_success (l1_gas: ~0, l1_data_gas: ~288, l2_gas: ~1382600) ╭-------------------------+-------+--------+--------+---------+---------╮ | SimpleContract Contract | | | | | | +=======================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |-------------------------+-------+--------+--------+---------+---------| | execute_calls | 11660 | 609380 | 184000 | 235987 | 5 | |-------------------------+-------+--------+--------+---------+---------| | fail | 17950 | 17950 | 17950 | 0 | 1 | ╰-------------------------+-------+--------+--------+---------+---------╯ ================================================ FILE: crates/forge/tests/e2e/snapshots/gas_report/main__e2e__gas_report__snap_recursive_calls@2.16.0.snap ================================================ --- source: crates/forge/tests/e2e/gas_report.rs expression: stdout --- [PASS] debugging_integrationtest::test_trace::test_debugging_trace_success (l1_gas: ~0, l1_data_gas: ~288, l2_gas: ~1382600) ╭-------------------------+-------+--------+--------+---------+---------╮ | SimpleContract Contract | | | | | | +=======================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |-------------------------+-------+--------+--------+---------+---------| | execute_calls | 11660 | 609380 | 184000 | 235987 | 5 | |-------------------------+-------+--------+--------+---------+---------| | fail | 17950 | 17950 | 17950 | 0 | 1 | ╰-------------------------+-------+--------+--------+---------+---------╯ ================================================ FILE: crates/forge/tests/e2e/snapshots/gas_report/main__e2e__gas_report__snap_recursive_calls@2.16.1.snap ================================================ --- source: crates/forge/tests/e2e/gas_report.rs expression: stdout --- [PASS] debugging_integrationtest::test_trace::test_debugging_trace_success (l1_gas: ~0, l1_data_gas: ~288, l2_gas: ~1382600) ╭-------------------------+-------+--------+--------+---------+---------╮ | SimpleContract Contract | | | | | | +=======================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |-------------------------+-------+--------+--------+---------+---------| | execute_calls | 11660 | 609380 | 184000 | 235987 | 5 | |-------------------------+-------+--------+--------+---------+---------| | fail | 17950 | 17950 | 17950 | 0 | 1 | ╰-------------------------+-------+--------+--------+---------+---------╯ ================================================ FILE: crates/forge/tests/e2e/snapshots/gas_report/main__e2e__gas_report__snap_recursive_calls@2.17.0.snap ================================================ --- source: crates/forge/tests/e2e/gas_report.rs expression: stdout --- [PASS] debugging_integrationtest::test_trace::test_debugging_trace_success (l1_gas: ~0, l1_data_gas: ~288, l2_gas: ~1382600) ╭-------------------------+-------+--------+--------+---------+---------╮ | SimpleContract Contract | | | | | | +=======================================================================+ | Function Name | Min | Max | Avg | Std Dev | # Calls | |-------------------------+-------+--------+--------+---------+---------| | execute_calls | 11660 | 609380 | 184000 | 235987 | 5 | |-------------------------+-------+--------+--------+---------+---------| | fail | 17950 | 17950 | 17950 | 0 | 1 | ╰-------------------------+-------+--------+--------+---------+---------╯ ================================================ FILE: crates/forge/tests/e2e/snapshots/main__e2e__optimize_inlining__optimize_inlining_dry_run.snap ================================================ --- source: crates/forge/tests/e2e/optimize_inlining.rs expression: graph_bytes extension: png snapshot_kind: binary --- ================================================ FILE: crates/forge/tests/e2e/snapshots/main__e2e__optimize_inlining__optimize_inlining_updates_manifest.snap ================================================ --- source: crates/forge/tests/e2e/optimize_inlining.rs expression: graph_bytes extension: png snapshot_kind: binary --- ================================================ FILE: crates/forge/tests/e2e/snapshots/optimize_inlining/main__e2e__optimize_inlining__snap_optimize_inlining_dry_run@2.15.2.snap ================================================ --- source: crates/forge/tests/e2e/optimize_inlining.rs expression: stdout --- Starting inlining strategy optimization... Search range: 0 to 100, step: 50, max contract size: 4089446 bytes, max felts: 81920 [1/3] Testing threshold 0... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~1009130) ✓ Tests passed, gas: 1009130, max contract size: 52390 bytes, contract bytecode L2 gas: 49725440 [2/3] Testing threshold 50... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~975690) ✓ Tests passed, gas: 975690, max contract size: 21760 bytes, contract bytecode L2 gas: 27033600 [3/3] Testing threshold 100... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~974690) ✓ Tests passed, gas: 974690, max contract size: 19732 bytes, contract bytecode L2 gas: 25559040 Optimization Results: ╭───────────┬───────────┬───────────────┬──────────────────────────┬────────╮ │ Threshold ┆ Total Gas ┆ Contract Size ┆ Contract Bytecode L2 Gas ┆ Status │ ╞═══════════╪═══════════╪═══════════════╪══════════════════════════╪════════╡ │ 0 ┆ 1009130 ┆ 52390 ┆ 49725440 ┆ ✓ │ ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ │ 50 ┆ 975690 ┆ 21760 ┆ 27033600 ┆ ✓ │ ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ │ 100 ┆ 974690 ┆ 19732 ┆ 25559040 ┆ ✓ │ ╰───────────┴───────────┴───────────────┴──────────────────────────┴────────╯ Lowest runtime gas cost: threshold=100, gas=974690, contract bytecode L2 gas=25559040 Lowest contract size cost: threshold=100, gas=974690, contract bytecode L2 gas=25559040 Graph saved to: [..] Scarb.toml not modified. Use --gas or --size to apply a threshold. ================================================ FILE: crates/forge/tests/e2e/snapshots/optimize_inlining/main__e2e__optimize_inlining__snap_optimize_inlining_dry_run@2.16.0.snap ================================================ --- source: crates/forge/tests/e2e/optimize_inlining.rs expression: stdout --- Starting inlining strategy optimization... Search range: 0 to 100, step: 50, max contract size: 4089446 bytes, max felts: 81920 [1/3] Testing threshold 0... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~1009130) ✓ Tests passed, gas: 1009130, max contract size: 52391 bytes, contract bytecode L2 gas: 49725440 [2/3] Testing threshold 50... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~975690) ✓ Tests passed, gas: 975690, max contract size: 21761 bytes, contract bytecode L2 gas: 27033600 [3/3] Testing threshold 100... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~974690) ✓ Tests passed, gas: 974690, max contract size: 19733 bytes, contract bytecode L2 gas: 25559040 Optimization Results: ╭───────────┬───────────┬───────────────┬──────────────────────────┬────────╮ │ Threshold ┆ Total Gas ┆ Contract Size ┆ Contract Bytecode L2 Gas ┆ Status │ ╞═══════════╪═══════════╪═══════════════╪══════════════════════════╪════════╡ │ 0 ┆ 1009130 ┆ 52391 ┆ 49725440 ┆ ✓ │ ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ │ 50 ┆ 975690 ┆ 21761 ┆ 27033600 ┆ ✓ │ ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ │ 100 ┆ 974690 ┆ 19733 ┆ 25559040 ┆ ✓ │ ╰───────────┴───────────┴───────────────┴──────────────────────────┴────────╯ Lowest runtime gas cost: threshold=100, gas=974690, contract bytecode L2 gas=25559040 Lowest contract size cost: threshold=100, gas=974690, contract bytecode L2 gas=25559040 Graph saved to: [..] Scarb.toml not modified. Use --gas or --size to apply a threshold. ================================================ FILE: crates/forge/tests/e2e/snapshots/optimize_inlining/main__e2e__optimize_inlining__snap_optimize_inlining_dry_run@2.16.1.snap ================================================ --- source: crates/forge/tests/e2e/optimize_inlining.rs expression: stdout --- Starting inlining strategy optimization... Search range: 0 to 100, step: 50, max contract size: 4089446 bytes, max felts: 81920 [1/3] Testing threshold 0... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~1009130) ✓ Tests passed, gas: 1009130, max contract size: 52391 bytes, contract bytecode L2 gas: 49725440 [2/3] Testing threshold 50... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~975690) ✓ Tests passed, gas: 975690, max contract size: 21761 bytes, contract bytecode L2 gas: 27033600 [3/3] Testing threshold 100... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~974690) ✓ Tests passed, gas: 974690, max contract size: 19733 bytes, contract bytecode L2 gas: 25559040 Optimization Results: ╭───────────┬───────────┬───────────────┬──────────────────────────┬────────╮ │ Threshold ┆ Total Gas ┆ Contract Size ┆ Contract Bytecode L2 Gas ┆ Status │ ╞═══════════╪═══════════╪═══════════════╪══════════════════════════╪════════╡ │ 0 ┆ 1009130 ┆ 52391 ┆ 49725440 ┆ ✓ │ ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ │ 50 ┆ 975690 ┆ 21761 ┆ 27033600 ┆ ✓ │ ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ │ 100 ┆ 974690 ┆ 19733 ┆ 25559040 ┆ ✓ │ ╰───────────┴───────────┴───────────────┴──────────────────────────┴────────╯ Lowest runtime gas cost: threshold=100, gas=974690, contract bytecode L2 gas=25559040 Lowest contract size cost: threshold=100, gas=974690, contract bytecode L2 gas=25559040 Graph saved to: [..] Scarb.toml not modified. Use --gas or --size to apply a threshold. ================================================ FILE: crates/forge/tests/e2e/snapshots/optimize_inlining/main__e2e__optimize_inlining__snap_optimize_inlining_dry_run@2.17.0.snap ================================================ --- source: crates/forge/tests/e2e/optimize_inlining.rs expression: stdout --- Starting inlining strategy optimization... Search range: 0 to 100, step: 50, max contract size: 4089446 bytes, max felts: 81920 [1/3] Testing threshold 0... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~1009130) ✓ Tests passed, gas: 1009130, max contract size: 52391 bytes, contract bytecode L2 gas: 49725440 [2/3] Testing threshold 50... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~975690) ✓ Tests passed, gas: 975690, max contract size: 21761 bytes, contract bytecode L2 gas: 27033600 [3/3] Testing threshold 100... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~974690) ✓ Tests passed, gas: 974690, max contract size: 19733 bytes, contract bytecode L2 gas: 25559040 Optimization Results: ╭───────────┬───────────┬───────────────┬──────────────────────────┬────────╮ │ Threshold ┆ Total Gas ┆ Contract Size ┆ Contract Bytecode L2 Gas ┆ Status │ ╞═══════════╪═══════════╪═══════════════╪══════════════════════════╪════════╡ │ 0 ┆ 1009130 ┆ 52391 ┆ 49725440 ┆ ✓ │ ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ │ 50 ┆ 975690 ┆ 21761 ┆ 27033600 ┆ ✓ │ ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ │ 100 ┆ 974690 ┆ 19733 ┆ 25559040 ┆ ✓ │ ╰───────────┴───────────┴───────────────┴──────────────────────────┴────────╯ Lowest runtime gas cost: threshold=100, gas=974690, contract bytecode L2 gas=25559040 Lowest contract size cost: threshold=100, gas=974690, contract bytecode L2 gas=25559040 Graph saved to: [..] Scarb.toml not modified. Use --gas or --size to apply a threshold. ================================================ FILE: crates/forge/tests/e2e/snapshots/optimize_inlining/main__e2e__optimize_inlining__snap_optimize_inlining_updates_manifest@2.15.2.snap ================================================ --- source: crates/forge/tests/e2e/optimize_inlining.rs expression: stdout --- Starting inlining strategy optimization... Search range: 0 to 10, step: 10, max contract size: 4089446 bytes, max felts: 81920 [1/2] Testing threshold 0... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~1009130) ✓ Tests passed, gas: 1009130, max contract size: 52390 bytes, contract bytecode L2 gas: 49725440 [2/2] Testing threshold 10... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~984990) ✓ Tests passed, gas: 984990, max contract size: 29545 bytes, contract bytecode L2 gas: 34897920 Optimization Results: ╭───────────┬───────────┬───────────────┬──────────────────────────┬────────╮ │ Threshold ┆ Total Gas ┆ Contract Size ┆ Contract Bytecode L2 Gas ┆ Status │ ╞═══════════╪═══════════╪═══════════════╪══════════════════════════╪════════╡ │ 0 ┆ 1009130 ┆ 52390 ┆ 49725440 ┆ ✓ │ ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ │ 10 ┆ 984990 ┆ 29545 ┆ 34897920 ┆ ✓ │ ╰───────────┴───────────┴───────────────┴──────────────────────────┴────────╯ Lowest runtime gas cost: threshold=10, gas=984990, contract bytecode L2 gas=34897920 Lowest contract size cost: threshold=10, gas=984990, contract bytecode L2 gas=34897920 Graph saved to: [..] Updated Scarb.toml with inlining-strategy = 10 ================================================ FILE: crates/forge/tests/e2e/snapshots/optimize_inlining/main__e2e__optimize_inlining__snap_optimize_inlining_updates_manifest@2.16.0.snap ================================================ --- source: crates/forge/tests/e2e/optimize_inlining.rs expression: stdout --- Starting inlining strategy optimization... Search range: 0 to 10, step: 10, max contract size: 4089446 bytes, max felts: 81920 [1/2] Testing threshold 0... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~1009130) ✓ Tests passed, gas: 1009130, max contract size: 52391 bytes, contract bytecode L2 gas: 49725440 [2/2] Testing threshold 10... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~984990) ✓ Tests passed, gas: 984990, max contract size: 29546 bytes, contract bytecode L2 gas: 34897920 Optimization Results: ╭───────────┬───────────┬───────────────┬──────────────────────────┬────────╮ │ Threshold ┆ Total Gas ┆ Contract Size ┆ Contract Bytecode L2 Gas ┆ Status │ ╞═══════════╪═══════════╪═══════════════╪══════════════════════════╪════════╡ │ 0 ┆ 1009130 ┆ 52391 ┆ 49725440 ┆ ✓ │ ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ │ 10 ┆ 984990 ┆ 29546 ┆ 34897920 ┆ ✓ │ ╰───────────┴───────────┴───────────────┴──────────────────────────┴────────╯ Lowest runtime gas cost: threshold=10, gas=984990, contract bytecode L2 gas=34897920 Lowest contract size cost: threshold=10, gas=984990, contract bytecode L2 gas=34897920 Graph saved to: [..] Updated Scarb.toml with inlining-strategy = 10 ================================================ FILE: crates/forge/tests/e2e/snapshots/optimize_inlining/main__e2e__optimize_inlining__snap_optimize_inlining_updates_manifest@2.16.1.snap ================================================ --- source: crates/forge/tests/e2e/optimize_inlining.rs expression: stdout --- Starting inlining strategy optimization... Search range: 0 to 10, step: 10, max contract size: 4089446 bytes, max felts: 81920 [1/2] Testing threshold 0... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~1009130) ✓ Tests passed, gas: 1009130, max contract size: 52391 bytes, contract bytecode L2 gas: 49725440 [2/2] Testing threshold 10... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~984990) ✓ Tests passed, gas: 984990, max contract size: 29546 bytes, contract bytecode L2 gas: 34897920 Optimization Results: ╭───────────┬───────────┬───────────────┬──────────────────────────┬────────╮ │ Threshold ┆ Total Gas ┆ Contract Size ┆ Contract Bytecode L2 Gas ┆ Status │ ╞═══════════╪═══════════╪═══════════════╪══════════════════════════╪════════╡ │ 0 ┆ 1009130 ┆ 52391 ┆ 49725440 ┆ ✓ │ ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ │ 10 ┆ 984990 ┆ 29546 ┆ 34897920 ┆ ✓ │ ╰───────────┴───────────┴───────────────┴──────────────────────────┴────────╯ Lowest runtime gas cost: threshold=10, gas=984990, contract bytecode L2 gas=34897920 Lowest contract size cost: threshold=10, gas=984990, contract bytecode L2 gas=34897920 Graph saved to: [..] Updated Scarb.toml with inlining-strategy = 10 ================================================ FILE: crates/forge/tests/e2e/snapshots/optimize_inlining/main__e2e__optimize_inlining__snap_optimize_inlining_updates_manifest@2.17.0.snap ================================================ --- source: crates/forge/tests/e2e/optimize_inlining.rs expression: stdout --- Starting inlining strategy optimization... Search range: 0 to 10, step: 10, max contract size: 4089446 bytes, max felts: 81920 [1/2] Testing threshold 0... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~1009130) ✓ Tests passed, gas: 1009130, max contract size: 52391 bytes, contract bytecode L2 gas: 49725440 [2/2] Testing threshold 10... [PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~192, l2_gas: ~984990) ✓ Tests passed, gas: 984990, max contract size: 29546 bytes, contract bytecode L2 gas: 34897920 Optimization Results: ╭───────────┬───────────┬───────────────┬──────────────────────────┬────────╮ │ Threshold ┆ Total Gas ┆ Contract Size ┆ Contract Bytecode L2 Gas ┆ Status │ ╞═══════════╪═══════════╪═══════════════╪══════════════════════════╪════════╡ │ 0 ┆ 1009130 ┆ 52391 ┆ 49725440 ┆ ✓ │ ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ │ 10 ┆ 984990 ┆ 29546 ┆ 34897920 ┆ ✓ │ ╰───────────┴───────────┴───────────────┴──────────────────────────┴────────╯ Lowest runtime gas cost: threshold=10, gas=984990, contract bytecode L2 gas=34897920 Lowest contract size cost: threshold=10, gas=984990, contract bytecode L2 gas=34897920 Graph saved to: [..] Updated Scarb.toml with inlining-strategy = 10 ================================================ FILE: crates/forge/tests/e2e/steps.rs ================================================ use super::common::runner::{setup_package, test_runner}; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; // TODO(#2806) #[test] fn should_allow_less_than_default() { let temp = setup_package("steps"); let output = test_runner(&temp) .args(["--max-n-steps", "100000"]) .assert() .code(1); assert_stdout_contains( output, indoc!( r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from steps package Running 2 test(s) from src/ [FAIL] steps::tests::steps_less_than_10000000 Failure data: Could not reach the end of the program. RunResources has no remaining steps. Suggestion: Consider using the flag `--max-n-steps` to increase allowed limit of steps [FAIL] steps::tests::steps_more_than_10000000 Failure data: Could not reach the end of the program. RunResources has no remaining steps. Suggestion: Consider using the flag `--max-n-steps` to increase allowed limit of steps Tests: 0 passed, 2 failed, 0 ignored, 0 filtered out Failures: steps::tests::steps_less_than_10000000 steps::tests::steps_more_than_10000000 " ), ); } #[test] // 10_000_000 is blockifier limit we want to omit fn should_allow_more_than_10m() { let temp = setup_package("steps"); let output = test_runner(&temp) .args(["--max-n-steps", "15000100"]) .assert() .code(0); assert_stdout_contains( output, indoc!( r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from steps package Running 2 test(s) from src/ [PASS] steps::tests::steps_more_than_10000000 [..] [PASS] steps::tests::steps_less_than_10000000 [..] Tests: 2 passed, 0 failed, 0 ignored, 0 filtered out " ), ); } #[test] fn should_default_to_10m() { let temp = setup_package("steps"); let output = test_runner(&temp).assert().code(1); assert_stdout_contains( output, indoc!( r" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from steps package Running 2 test(s) from src/ [PASS] steps::tests::steps_less_than_10000000 (l1_gas: ~[..], l1_data_gas: ~[..], l2_gas: ~[..]) [FAIL] steps::tests::steps_more_than_10000000 Failure data: Could not reach the end of the program. RunResources has no remaining steps. Suggestion: Consider using the flag `--max-n-steps` to increase allowed limit of steps Tests: 1 passed, 1 failed, 0 ignored, 0 filtered out Failures: steps::tests::steps_more_than_10000000 " ), ); } ================================================ FILE: crates/forge/tests/e2e/templates.rs ================================================ use super::common::runner::runner; use crate::utils::tempdir_with_tool_versions; use assert_fs::prelude::PathChild; use camino::Utf8PathBuf; use forge::Template; use packages_validation::check_and_lint; use scarb_api::ScarbCommand; use std::fs; use std::process::Stdio; use test_case::test_case; use toml_edit::DocumentMut; #[test_case(&Template::CairoProgram; "cairo-program")] #[test_case(&Template::BalanceContract; "balance-contract")] #[test_case(&Template::Erc20Contract; "erc20-contract")] #[cfg_attr(not(feature = "run_test_for_scarb_since_2_15_1"), ignore)] fn validate_templates(template: &Template) { let temp_dir = tempdir_with_tool_versions().expect("Unable to create a temporary directory"); let package_name = format!("{}_test", template.to_string().replace('-', "_")); let snforge_std = Utf8PathBuf::from("../../snforge_std") .canonicalize_utf8() .unwrap(); runner(&temp_dir) .env("DEV_DISABLE_SNFORGE_STD_DEPENDENCY", "true") .args([ "new", "--template", template.to_string().as_str(), &package_name, ]) .assert() .success(); let package_path = temp_dir.child(package_name); let package_path = Utf8PathBuf::from_path_buf(package_path.to_path_buf()) .expect("Failed to convert to Utf8PathBuf"); let scarb_add = ScarbCommand::new() .current_dir(&package_path) .args([ "add", "snforge_std", "--dev", "--path", snforge_std.as_str(), ]) .command() .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) .spawn() .expect("Failed to run scarb add") .wait() .expect("Failed to wait for scarb add"); assert!(scarb_add.success(), "Failed to add snforge_std to package"); // Overwrite Scarb.toml with `allow-warnings = false` let scarb_toml_path = package_path.join("Scarb.toml"); let mut scarb_toml = fs::read_to_string(&scarb_toml_path) .unwrap() .parse::() .unwrap(); scarb_toml["cairo"]["allow-warnings"] = toml_edit::value(false); fs::write(&scarb_toml_path, scarb_toml.to_string()).expect("Failed to write to Scarb.toml"); check_and_lint(&package_path); } ================================================ FILE: crates/forge/tests/e2e/test_case.rs ================================================ use super::common::runner::{setup_package, test_runner}; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; #[test] #[cfg_attr( feature = "skip_test_for_only_latest_scarb", ignore = "Plugin checks skipped" )] fn simple_addition() { let temp = setup_package("test_case"); let output = test_runner(&temp).arg("simple_addition").assert().code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 3 test(s) from test_case package Running 3 test(s) from tests/ [PASS] test_case_integrationtest::single_attribute::simple_addition_1_2_3 [..] [PASS] test_case_integrationtest::single_attribute::simple_addition_3_4_7 [..] [PASS] test_case_integrationtest::single_attribute::simple_addition_5_6_11 [..] Running 0 test(s) from src/ Tests: 3 passed, 0 failed, 0 ignored, [..] filtered out "}, ); } #[test] #[cfg_attr( feature = "skip_test_for_only_latest_scarb", ignore = "Plugin checks skipped" )] fn with_exit_first_flag() { let temp = setup_package("test_case"); let output = test_runner(&temp) .arg("test_fib_with_threshold") .arg("--exit-first") .assert() .code(1); assert_stdout_contains( output, indoc! {r#" [..]Compiling[..] [..]Finished[..] Collected 2 test(s) from test_case package Running 2 test(s) from tests/ [FAIL] test_case_integrationtest::exit_first::test_fib_with_threshold_0_1_3 Failure data: "result should be greater than threshold" Tests: 0 passed, 1 failed, 0 ignored, [..] filtered out Interrupted execution of 1 test(s). Failures: test_case_integrationtest::exit_first::test_fib_with_threshold_0_1_3 "#}, ); } #[test] #[cfg_attr( feature = "skip_test_for_only_latest_scarb", ignore = "Plugin checks skipped" )] fn with_multiple_attributes() { let temp = setup_package("test_case"); let output = test_runner(&temp) .arg("multiple_attributes") .assert() .code(1); assert_stdout_contains( output, indoc! {r" [IGNORE] test_case_integrationtest::multiple_attributes::with_ignore_3_4_7 [IGNORE] test_case_integrationtest::multiple_attributes::with_ignore_1_2_3 [PASS] test_case_integrationtest::multiple_attributes::with_available_gas_3_4_7 [..] [PASS] test_case_integrationtest::multiple_attributes::with_fuzzer_3_4 [..] [PASS] test_case_integrationtest::multiple_attributes::with_fuzzer_different_order_3_4 [..] [FAIL] test_case_integrationtest::multiple_attributes::with_available_gas_exceed_limit_3_4_7 Failure data: Test cost exceeded the available gas. Consumed [..] [PASS] test_case_integrationtest::multiple_attributes::with_available_gas_1_2_3 [..] [PASS] test_case_integrationtest::multiple_attributes::with_should_panic_3_4_7 [..] [PASS] test_case_integrationtest::multiple_attributes::with_should_panic_1_2_3 [..] [FAIL] test_case_integrationtest::multiple_attributes::with_available_gas_exceed_limit_1_2_3 Failure data: Test cost exceeded the available gas. Consumed [..] [PASS] test_case_integrationtest::multiple_attributes::with_fuzzer_1_2 [..] [PASS] test_case_integrationtest::multiple_attributes::with_fuzzer [..] [PASS] test_case_integrationtest::multiple_attributes::with_fuzzer_different_order_1_2 [..] [PASS] test_case_integrationtest::multiple_attributes::with_fuzzer_different_order [..] Running 0 test(s) from src/ Tests: 10 passed, 2 failed, 2 ignored, [..] filtered out Fuzzer seed: [..] Failures: test_case_integrationtest::multiple_attributes::with_available_gas_exceed_limit_3_4_7 test_case_integrationtest::multiple_attributes::with_available_gas_exceed_limit_1_2_3 "}, ); } #[test] #[cfg_attr( feature = "skip_test_for_only_latest_scarb", ignore = "Plugin checks skipped" )] fn addition_with_name_arg() { let temp = setup_package("test_case"); let output = test_runner(&temp) .arg("addition_with_name_arg") .assert() .code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 3 test(s) from test_case package Running 3 test(s) from tests/ [PASS] test_case_integrationtest::single_attribute::addition_with_name_arg_one_and_two [..] [PASS] test_case_integrationtest::single_attribute::addition_with_name_arg_three_and_four [..] [PASS] test_case_integrationtest::single_attribute::addition_with_name_arg_five_and_six [..] Running 0 test(s) from src/ Tests: 3 passed, 0 failed, 0 ignored, [..] filtered out "}, ); } #[test] #[cfg_attr( feature = "skip_test_for_only_latest_scarb", ignore = "Plugin checks skipped" )] fn with_contract_deploy() { let temp = setup_package("test_case"); let output = test_runner(&temp) .arg("with_contract_deploy") .assert() .code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 3 test(s) from test_case package Running 3 test(s) from tests/ [PASS] test_case_integrationtest::with_deploy::with_contract_deploy_100 [..] [PASS] test_case_integrationtest::with_deploy::with_contract_deploy_42 [..] [PASS] test_case_integrationtest::with_deploy::with_contract_deploy_0 [..] Running 0 test(s) from src/ Tests: 3 passed, 0 failed, 0 ignored, [..] filtered out "}, ); } #[test] #[cfg_attr( feature = "skip_test_for_only_latest_scarb", ignore = "Plugin checks skipped" )] fn with_fuzzer_and_contract_deploy() { let temp = setup_package("test_case"); let output = test_runner(&temp) .arg("with_fuzzer_and_contract_deploy") .assert() .code(0); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 3 test(s) from test_case package Running 3 test(s) from tests/ [PASS] test_case_integrationtest::with_deploy::with_fuzzer_and_contract_deploy_123 [..] [PASS] test_case_integrationtest::with_deploy::with_fuzzer_and_contract_deploy_0 [..] [PASS] test_case_integrationtest::with_deploy::with_fuzzer_and_contract_deploy [..] Running 0 test(s) from src/ Tests: 3 passed, 0 failed, 0 ignored, [..] filtered out Fuzzer seed: [..] "}, ); } ================================================ FILE: crates/forge/tests/e2e/trace_print.rs ================================================ use super::common::runner::{setup_package, test_runner}; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; #[test] fn trace_info_print() { let temp = setup_package("trace"); let output = test_runner(&temp).assert().success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from trace_info package Running 0 test(s) from src/ Running 1 test(s) from tests/ Entry point type: External Selector: [..] Calldata: [] Storage address: [..] Caller address: 0 Call type: Call Nested Calls: [ ( Entry point type: External Selector: [..] Calldata: [..] Storage address: [..] Caller address: [..] Call type: Call Nested Calls: [ ( Entry point type: External Selector: [..] Calldata: [..] Storage address: [..] Caller address: [..] Call type: Call Nested Calls: [ ( Entry point type: External Selector: [..] Calldata: [0] Storage address: [..] Caller address: [..] Call type: Call Nested Calls: [] Call Result: Success: [] ), ( Entry point type: External Selector: [..] Calldata: [0] Storage address: [..] Caller address: [..] Call type: Call Nested Calls: [] Call Result: Success: [] ) ] Call Result: Success: [] ), ( Entry point type: External Selector: [..] Calldata: [0] Storage address: [..] Caller address: [..] Call type: Call Nested Calls: [] Call Result: Success: [] ) ] Call Result: Success: [] ), ( Entry point type: External Selector: 1423007881864269398513176851135908567621420218646181695002463829511917924133 Calldata: [5, 1, 2, 3, 4, 5] Storage address: [..] Caller address: 469394814521890341860918960550914 Call type: Call Nested Calls: [] Call Result: Failure: [1, 2, 3, 4, 5] ) ] Call Result: Success: [] [PASS] trace_info_integrationtest::test_trace::test_trace (l1_gas: [..], l1_data_gas: [..], l2_gas: [..]) Tests: 1 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } ================================================ FILE: crates/forge/tests/e2e/trace_resources.rs ================================================ use super::common::runner::{setup_package, test_runner}; use crate::e2e::common::get_trace_from_trace_node; use assert_fs::TempDir; use cairo_annotations::trace_data::{ CallTraceNode as ProfilerCallTraceNode, CallTraceV1 as ProfilerCallTrace, DeprecatedSyscallSelector::{ CallContract, Deploy, EmitEvent, GetBlockHash, GetExecutionInfo, Keccak, LibraryCall, SendMessageToL1, StorageRead, StorageWrite, }, ExecutionResources as ProfilerExecutionResources, SyscallUsage, VersionedCallTrace as VersionedProfilerCallTrace, }; use forge_runner::build_trace_data::TRACE_DIR; use std::{collections::HashMap, fs}; #[test] fn trace_resources_call() { assert_vm_resources_for_test("test_call", check_call); } #[test] fn trace_resources_deploy() { assert_vm_resources_for_test("test_deploy", check_deploy); } #[test] fn trace_resources_l1_handler() { assert_vm_resources_for_test("test_l1_handler", check_l1_handler); } #[test] fn trace_resources_lib_call() { assert_vm_resources_for_test("test_lib_call", check_libcall); } #[test] #[ignore = "TODO(#1657)"] fn trace_resources_failed_call() { assert_vm_resources_for_test("test_failed_call", |_| ()); } #[test] #[ignore = "TODO(#1657)"] fn trace_resources_failed_lib_call() { assert_vm_resources_for_test("test_failed_lib_call", |_| ()); } fn assert_vm_resources_for_test( test_name: &str, check_not_easily_unifiable_syscalls: fn(&ProfilerCallTrace), ) { let temp = setup_package("trace_resources"); test_runner(&temp) .arg(test_name) .arg("--save-trace-data") .arg("--tracked-resource") .arg("cairo-steps") .assert() .success(); let VersionedProfilerCallTrace::V1(call_trace) = deserialize_call_trace(test_name, &temp); check_vm_resources_and_easily_unifiable_syscalls(&call_trace); // test Deploy, CallContract and LibraryCall syscalls as their counts cannot be unified as easily as the rest check_not_easily_unifiable_syscalls(&call_trace); } fn deserialize_call_trace(test_name: &str, temp_dir: &TempDir) -> VersionedProfilerCallTrace { let trace_data = fs::read_to_string(temp_dir.join(TRACE_DIR).join(format!( "trace_resources_tests_{test_name}_{test_name}.json" ))) .unwrap(); serde_json::from_str(&trace_data).expect("Failed to parse call trace") } fn check_vm_resources_and_easily_unifiable_syscalls( call_trace: &ProfilerCallTrace, ) -> &ProfilerExecutionResources { let mut child_resources = vec![]; for call_node in &call_trace.nested_calls { if let cairo_annotations::trace_data::CallTraceNode::EntryPointCall(call) = call_node { child_resources.push(check_vm_resources_and_easily_unifiable_syscalls(call)); } } let mut sum_child_resources = ProfilerExecutionResources::default(); for resource in child_resources { sum_child_resources += resource; } let current_resources = &call_trace.cumulative_resources; assert!(is_greater_eq_than(current_resources, &sum_child_resources)); let mut resource_diff = current_resources.clone(); resource_diff -= &sum_child_resources; assert_correct_diff_for_builtins_and_easily_unifiable_syscalls(&resource_diff); assert_l2_l1_messages(call_trace); current_resources } fn assert_correct_diff_for_builtins_and_easily_unifiable_syscalls( resource_diff: &ProfilerExecutionResources, ) { for syscall in [ EmitEvent, GetBlockHash, GetExecutionInfo, StorageWrite, StorageRead, SendMessageToL1, Keccak, ] { assert_eq!( resource_diff .clone() .syscall_counter .unwrap() .get(&syscall) .unwrap_or_else(|| panic!("Expected resource diff to contain {syscall:?}")) .call_count, 1, "Incorrect diff for {syscall:?}" ); } for builtin in [ "poseidon_builtin", "ec_op_builtin", "bitwise_builtin", "pedersen_builtin", ] { assert_eq!( *resource_diff .vm_resources .builtin_instance_counter .get(builtin) .unwrap_or_else(|| panic!("Expected resource diff to contain {builtin:?}")), 1, "Incorrect diff for {builtin:?}" ); } } fn assert_l2_l1_messages(call_trace: &ProfilerCallTrace) { assert_eq!( call_trace.used_l1_resources.l2_l1_message_sizes.len(), 1, "Every call should have one message" ); assert_eq!( call_trace.used_l1_resources.l2_l1_message_sizes, vec![2], "Message should have payload of length 2" ); } // When sth fails in the functions below, and you didn't change anything in the cairo code it is a BUG. // If you changed the corresponding cairo code count the expected occurrences of syscalls manually first, then assert them. // TL;DR: DON't mindlessly change numbers to fix the tests if they ever fail. fn check_call(test_call_trace: &ProfilerCallTrace) { assert_not_easily_unifiable_syscalls(test_call_trace, 14, 8, 1); let regular_call = get_trace_from_trace_node(&test_call_trace.nested_calls[4]); assert_not_easily_unifiable_syscalls(regular_call, 2, 1, 0); let from_proxy = get_trace_from_trace_node(®ular_call.nested_calls[1]); assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); let with_libcall = get_trace_from_trace_node(&test_call_trace.nested_calls[5]); assert_not_easily_unifiable_syscalls(with_libcall, 2, 0, 1); let from_proxy = get_trace_from_trace_node(&with_libcall.nested_calls[1]); assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); let call_two = get_trace_from_trace_node(&test_call_trace.nested_calls[6]); assert_not_easily_unifiable_syscalls(call_two, 3, 2, 0); let from_proxy = get_trace_from_trace_node(&call_two.nested_calls[0]); assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); let from_proxy_dummy = get_trace_from_trace_node(&call_two.nested_calls[2]); assert_not_easily_unifiable_syscalls(from_proxy_dummy, 1, 0, 0); let from_proxy = get_trace_from_trace_node(&test_call_trace.nested_calls[7]); assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); } fn check_deploy(test_call_trace: &ProfilerCallTrace) { assert_not_easily_unifiable_syscalls(test_call_trace, 14, 4, 0); for deploy_proxy_node in &test_call_trace.nested_calls { if let ProfilerCallTraceNode::EntryPointCall(deploy_proxy) = deploy_proxy_node { assert_not_easily_unifiable_syscalls(deploy_proxy, 2, 1, 0); let from_proxy = get_trace_from_trace_node(&deploy_proxy.nested_calls[1]); assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); } } } fn check_l1_handler(test_call_trace: &ProfilerCallTrace) { assert_not_easily_unifiable_syscalls(test_call_trace, 8, 3, 0); let handle_l1 = get_trace_from_trace_node(&test_call_trace.nested_calls[3]); assert_not_easily_unifiable_syscalls(handle_l1, 3, 2, 0); let regular_call = get_trace_from_trace_node(&handle_l1.nested_calls[1]); assert_not_easily_unifiable_syscalls(regular_call, 2, 1, 0); let from_proxy = get_trace_from_trace_node(®ular_call.nested_calls[1]); assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); } fn check_libcall(test_call_trace: &ProfilerCallTrace) { assert_not_easily_unifiable_syscalls(test_call_trace, 11, 3, 5); let regular_call = get_trace_from_trace_node(&test_call_trace.nested_calls[3]); assert_not_easily_unifiable_syscalls(regular_call, 2, 1, 0); let from_proxy = get_trace_from_trace_node(®ular_call.nested_calls[1]); assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); let with_libcall = get_trace_from_trace_node(&test_call_trace.nested_calls[4]); assert_not_easily_unifiable_syscalls(with_libcall, 2, 0, 1); let call_two = get_trace_from_trace_node(&test_call_trace.nested_calls[5]); assert_not_easily_unifiable_syscalls(call_two, 3, 2, 0); let from_proxy = get_trace_from_trace_node(&call_two.nested_calls[0]); assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); let from_proxy_dummy = get_trace_from_trace_node(&call_two.nested_calls[2]); assert_not_easily_unifiable_syscalls(from_proxy_dummy, 1, 0, 0); let from_proxy = get_trace_from_trace_node(&test_call_trace.nested_calls[6]); assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); } fn assert_not_easily_unifiable_syscalls( call: &ProfilerCallTrace, deploy_count: usize, call_contract_count: usize, library_call_count: usize, ) { let syscall_counter = &call.cumulative_resources.syscall_counter; let expected_counts: HashMap<_, _> = [ (Deploy, deploy_count), (CallContract, call_contract_count), (LibraryCall, library_call_count), ] .into_iter() .filter(|(_key, val)| *val > 0) .collect(); for key in [Deploy, CallContract, LibraryCall] { assert_eq!( syscall_counter .clone() .unwrap() .get(&key) .unwrap_or(&SyscallUsage::default()) .call_count, expected_counts.get(&key).copied().unwrap_or(0), "Incorrect count for {key:?}" ); } } fn is_greater_eq_than( left: &ProfilerExecutionResources, right: &ProfilerExecutionResources, ) -> bool { // Check VM resources if left.vm_resources.n_steps < right.vm_resources.n_steps || left.vm_resources.n_memory_holes < right.vm_resources.n_memory_holes { return false; } let left_builtin_counter = &left.vm_resources.builtin_instance_counter; let right_builtin_counter = &right.vm_resources.builtin_instance_counter; for (builtin, count) in right_builtin_counter { let left_count = left_builtin_counter.get(builtin).unwrap_or(&0); if left_count < count { return false; } } // Check gas consumed if let (Some(left_gas), Some(right_gas)) = (left.gas_consumed, right.gas_consumed) { if left_gas < right_gas { return false; } } else if right.gas_consumed.is_some() && left.gas_consumed.is_none() { return false; } // Check syscall counter if let (Some(left_syscalls), Some(right_syscalls)) = (&left.syscall_counter, &right.syscall_counter) { for (syscall, usage) in right_syscalls { match left_syscalls.get(syscall) { Some(left_usage) => { if left_usage.call_count < usage.call_count || left_usage.linear_factor < usage.linear_factor { return false; } } None => return false, } } } else if right.syscall_counter.is_some() && left.syscall_counter.is_none() { return false; } true } ================================================ FILE: crates/forge/tests/e2e/workspaces.rs ================================================ use super::common::runner::{setup_hello_workspace, setup_virtual_workspace, test_runner}; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; use std::path::PathBuf; #[test] fn root_workspace_without_arguments() { let temp = setup_hello_workspace(); let output = test_runner(&temp).assert().code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 3 test(s) from hello_workspaces package Running 1 test(s) from src/ [PASS] hello_workspaces::tests::test_simple [..] Running 2 test(s) from tests/ [FAIL] hello_workspaces_integrationtest::test_failing::test_failing Failure data: 0x6661696c696e6720636865636b ('failing check') [FAIL] hello_workspaces_integrationtest::test_failing::test_another_failing Failure data: 0x6661696c696e6720636865636b ('failing check') Tests: 1 passed, 2 failed, 0 ignored, 0 filtered out Failures: hello_workspaces_integrationtest::test_failing::test_failing hello_workspaces_integrationtest::test_failing::test_another_failing "}, ); } #[test] fn root_workspace_specific_package() { let temp = setup_hello_workspace(); let output = test_runner(&temp) .args(["--package", "addition"]) .assert() .success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Compiling[..] [..]Finished[..] Collected 5 test(s) from addition package Running 1 test(s) from src/ [PASS] addition::tests::it_works [..] Running 4 test(s) from tests/ [PASS] addition_integrationtest::nested::simple_case [..] [PASS] addition_integrationtest::nested::contract_test [..] [PASS] addition_integrationtest::nested::test_nested::test_two [..] [PASS] addition_integrationtest::nested::test_nested::test_two_and_two [..] Tests: 5 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn root_workspace_specific_package2() { let temp = setup_hello_workspace(); let output = test_runner(&temp) .args(["--package", "fibonacci"]) .assert() .code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Compiling[..] [..]Finished[..] Collected 6 test(s) from fibonacci package Running 2 test(s) from src/ [PASS] fibonacci::tests::it_works [..] [PASS] fibonacci::tests::contract_test [..] Running 4 test(s) from tests/ [PASS] fibonacci_tests::lib_test [..] [PASS] fibonacci_tests::abc::abc_test [..] [PASS] fibonacci_tests::abc::efg::efg_test [..] [FAIL] fibonacci_tests::abc::efg::failing_test Failure data: 0x0 ('') Tests: 5 passed, 1 failed, 0 ignored, 0 filtered out Failures: fibonacci_tests::abc::efg::failing_test "}, ); } #[test] fn root_workspace_specific_package_and_name() { let temp = setup_hello_workspace(); let output = test_runner(&temp) .args(["simple", "--package", "addition"]) .assert() .success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from addition package Running 0 test(s) from src/ Running 1 test(s) from tests/ [PASS] addition_integrationtest::nested::simple_case [..] Tests: 1 passed, 0 failed, 0 ignored, 4 filtered out "}, ); } #[test] fn root_workspace_specify_root_package() { let temp = setup_hello_workspace(); let output = test_runner(&temp) .args(["--package", "hello_workspaces"]) .assert() .code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 3 test(s) from hello_workspaces package Running 1 test(s) from src/ [PASS] hello_workspaces::tests::test_simple [..] Running 2 test(s) from tests/ [FAIL] hello_workspaces_integrationtest::test_failing::test_failing Failure data: 0x6661696c696e6720636865636b ('failing check') [FAIL] hello_workspaces_integrationtest::test_failing::test_another_failing Failure data: 0x6661696c696e6720636865636b ('failing check') Tests: 1 passed, 2 failed, 0 ignored, 0 filtered out Failures: hello_workspaces_integrationtest::test_failing::test_failing hello_workspaces_integrationtest::test_failing::test_another_failing "}, ); } #[test] fn root_workspace_inside_nested_package() { let temp = setup_hello_workspace(); let output = test_runner(&temp) .current_dir(temp.join("crates/addition")) .assert() .success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Compiling[..] [..]Finished[..] Collected 5 test(s) from addition package Running 1 test(s) from src/ [PASS] addition::tests::it_works [..] Running 4 test(s) from tests/ [PASS] addition_integrationtest::nested::simple_case [..] [PASS] addition_integrationtest::nested::contract_test [..] [PASS] addition_integrationtest::nested::test_nested::test_two [..] [PASS] addition_integrationtest::nested::test_nested::test_two_and_two [..] Tests: 5 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn root_workspace_for_entire_workspace() { let temp = setup_hello_workspace(); let output = test_runner(&temp).arg("--workspace").assert().code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Compiling[..] [..]Compiling[..] [..]Compiling[..] [..]Compiling[..] [..]Finished[..] Collected 5 test(s) from addition package Running 1 test(s) from src/ [PASS] addition::tests::it_works [..] Running 4 test(s) from tests/ [PASS] addition_integrationtest::nested::simple_case [..] [PASS] addition_integrationtest::nested::contract_test [..] [PASS] addition_integrationtest::nested::test_nested::test_two [..] [PASS] addition_integrationtest::nested::test_nested::test_two_and_two [..] Tests: 5 passed, 0 failed, 0 ignored, 0 filtered out Collected 6 test(s) from fibonacci package Running 2 test(s) from src/ [PASS] fibonacci::tests::it_works [..] [PASS] fibonacci::tests::contract_test [..] Running 4 test(s) from tests/ [PASS] fibonacci_tests::lib_test [..] [PASS] fibonacci_tests::abc::abc_test [..] [PASS] fibonacci_tests::abc::efg::efg_test [..] [FAIL] fibonacci_tests::abc::efg::failing_test Failure data: 0x0 ('') Tests: 5 passed, 1 failed, 0 ignored, 0 filtered out Collected 3 test(s) from hello_workspaces package Running 1 test(s) from src/ [PASS] hello_workspaces::tests::test_simple [..] Running 2 test(s) from tests/ [FAIL] hello_workspaces_integrationtest::test_failing::test_failing Failure data: 0x6661696c696e6720636865636b ('failing check') [FAIL] hello_workspaces_integrationtest::test_failing::test_another_failing Failure data: 0x6661696c696e6720636865636b ('failing check') Tests: 1 passed, 2 failed, 0 ignored, 0 filtered out Failures: fibonacci_tests::abc::efg::failing_test hello_workspaces_integrationtest::test_failing::test_failing hello_workspaces_integrationtest::test_failing::test_another_failing Tests summary: 11 passed, 3 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn root_workspace_for_entire_workspace_inside_package() { let temp = setup_hello_workspace(); let output = test_runner(&temp) .current_dir(temp.join("crates/fibonacci")) .arg("--workspace") .assert() .code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Compiling[..] [..]Compiling[..] [..]Compiling[..] [..]Compiling[..] [..]Finished[..] Collected 5 test(s) from addition package Running 1 test(s) from src/ [PASS] addition::tests::it_works [..] Running 4 test(s) from tests/ [PASS] addition_integrationtest::nested::simple_case [..] [PASS] addition_integrationtest::nested::contract_test [..] [PASS] addition_integrationtest::nested::test_nested::test_two [..] [PASS] addition_integrationtest::nested::test_nested::test_two_and_two [..] Tests: 5 passed, 0 failed, 0 ignored, 0 filtered out Collected 6 test(s) from fibonacci package Running 2 test(s) from src/ [PASS] fibonacci::tests::it_works [..] [PASS] fibonacci::tests::contract_test [..] Running 4 test(s) from tests/ [PASS] fibonacci_tests::lib_test [..] [PASS] fibonacci_tests::abc::abc_test [..] [PASS] fibonacci_tests::abc::efg::efg_test [..] [FAIL] fibonacci_tests::abc::efg::failing_test Failure data: 0x0 ('') Tests: 5 passed, 1 failed, 0 ignored, 0 filtered out Collected 3 test(s) from hello_workspaces package Running 1 test(s) from src/ [PASS] hello_workspaces::tests::test_simple [..] Running 2 test(s) from tests/ [FAIL] hello_workspaces_integrationtest::test_failing::test_failing Failure data: 0x6661696c696e6720636865636b ('failing check') [FAIL] hello_workspaces_integrationtest::test_failing::test_another_failing Failure data: 0x6661696c696e6720636865636b ('failing check') Tests: 1 passed, 2 failed, 0 ignored, 0 filtered out Failures: fibonacci_tests::abc::efg::failing_test hello_workspaces_integrationtest::test_failing::test_failing hello_workspaces_integrationtest::test_failing::test_another_failing Tests summary: 11 passed, 3 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn root_workspace_for_entire_workspace_and_specific_package() { let temp = setup_hello_workspace(); let result = test_runner(&temp) .args(["--workspace", "--package", "addition"]) .assert() .code(2); let stderr = String::from_utf8_lossy(&result.get_output().stderr); assert!(stderr.contains("the argument '--workspace' cannot be used with '--package '")); } #[test] fn root_workspace_missing_package() { let temp = setup_hello_workspace(); let result = test_runner(&temp) .args(["--package", "missing_package"]) .assert() .code(2); let stdout = String::from_utf8_lossy(&result.get_output().stdout); assert!(stdout.contains("Failed to find any packages matching the specified filter")); } #[test] fn virtual_workspace_without_arguments() { let temp = setup_virtual_workspace(); let snapbox = test_runner(&temp); let output = snapbox.current_dir(&temp).assert().code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Compiling[..] [..]Compiling[..] [..]Finished[..] Collected 6 test(s) from fibonacci_virtual package Running 2 test(s) from src/ [PASS] fibonacci_virtual::tests::it_works [..] [PASS] fibonacci_virtual::tests::contract_test [..] Running 4 test(s) from tests/ [PASS] fibonacci_virtual_tests::lib_test [..] [PASS] fibonacci_virtual_tests::abc::abc_test [..] [PASS] fibonacci_virtual_tests::abc::efg::efg_test [..] [FAIL] fibonacci_virtual_tests::abc::efg::failing_test Failure data: 0x0 ('') Tests: 5 passed, 1 failed, 0 ignored, 0 filtered out Collected 5 test(s) from subtraction package Running 1 test(s) from src/ [PASS] subtraction::tests::it_works [..] Running 4 test(s) from tests/ [PASS] subtraction_integrationtest::nested::simple_case [..] [PASS] subtraction_integrationtest::nested::contract_test [..] [PASS] subtraction_integrationtest::nested::test_nested::test_two [..] [PASS] subtraction_integrationtest::nested::test_nested::test_two_and_two [..] Tests: 5 passed, 0 failed, 0 ignored, 0 filtered out Failures: fibonacci_virtual_tests::abc::efg::failing_test Tests summary: 10 passed, 1 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn virtual_workspace_specify_package() { let temp = setup_virtual_workspace(); let snapbox = test_runner(&temp).arg("--package").arg("subtraction"); let output = snapbox.current_dir(&temp).assert().success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Compiling[..] [..]Finished[..] Collected 5 test(s) from subtraction package Running 1 test(s) from src/ [PASS] subtraction::tests::it_works [..] Running 4 test(s) from tests/ [PASS] subtraction_integrationtest::nested::simple_case [..] [PASS] subtraction_integrationtest::nested::contract_test [..] [PASS] subtraction_integrationtest::nested::test_nested::test_two [..] [PASS] subtraction_integrationtest::nested::test_nested::test_two_and_two [..] Tests: 5 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn virtual_workspace_specific_package2() { let temp = setup_virtual_workspace(); let snapbox = test_runner(&temp).arg("--package").arg("fibonacci_virtual"); let output = snapbox.current_dir(&temp).assert().code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Finished[..] Collected 6 test(s) from fibonacci_virtual package Running 2 test(s) from src/ [PASS] fibonacci_virtual::tests::it_works [..] [PASS] fibonacci_virtual::tests::contract_test [..] Running 4 test(s) from tests/ [PASS] fibonacci_virtual_tests::lib_test [..] [PASS] fibonacci_virtual_tests::abc::abc_test [..] [PASS] fibonacci_virtual_tests::abc::efg::efg_test [..] [FAIL] fibonacci_virtual_tests::abc::efg::failing_test Failure data: 0x0 ('') Tests: 5 passed, 1 failed, 0 ignored, 0 filtered out Failures: fibonacci_virtual_tests::abc::efg::failing_test "}, ); } #[test] fn virtual_workspace_specific_package_and_name() { let temp = setup_virtual_workspace(); let snapbox = test_runner(&temp) .arg("simple") .arg("--package") .arg("subtraction"); let output = snapbox.current_dir(&temp).assert().success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from subtraction package Running 0 test(s) from src/ Running 1 test(s) from tests/ [PASS] subtraction_integrationtest::nested::simple_case [..] Tests: 1 passed, 0 failed, 0 ignored, 4 filtered out "}, ); } #[test] fn virtual_workspace_inside_nested_package() { let temp = setup_virtual_workspace(); let package_dir = temp.join(PathBuf::from("dummy_name/subtraction")); let snapbox = test_runner(&temp); let output = snapbox.current_dir(package_dir).assert().success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Compiling[..] [..]Finished[..] Collected 5 test(s) from subtraction package Running 1 test(s) from src/ [PASS] subtraction::tests::it_works [..] Running 4 test(s) from tests/ [PASS] subtraction_integrationtest::nested::simple_case [..] [PASS] subtraction_integrationtest::nested::contract_test [..] [PASS] subtraction_integrationtest::nested::test_nested::test_two [..] [PASS] subtraction_integrationtest::nested::test_nested::test_two_and_two [..] Tests: 5 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn virtual_workspace_for_entire_workspace() { let temp = setup_virtual_workspace(); let snapbox = test_runner(&temp); let output = snapbox.current_dir(&temp).assert().code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Compiling[..] [..]Compiling[..] [..]Finished[..] Collected 6 test(s) from fibonacci_virtual package Running 2 test(s) from src/ [PASS] fibonacci_virtual::tests::it_works [..] [PASS] fibonacci_virtual::tests::contract_test [..] Running 4 test(s) from tests/ [PASS] fibonacci_virtual_tests::lib_test [..] [PASS] fibonacci_virtual_tests::abc::abc_test [..] [PASS] fibonacci_virtual_tests::abc::efg::efg_test [..] [FAIL] fibonacci_virtual_tests::abc::efg::failing_test Failure data: 0x0 ('') Tests: 5 passed, 1 failed, 0 ignored, 0 filtered out Collected 5 test(s) from subtraction package Running 1 test(s) from src/ [PASS] subtraction::tests::it_works [..] Running 4 test(s) from tests/ [PASS] subtraction_integrationtest::nested::simple_case [..] [PASS] subtraction_integrationtest::nested::contract_test [..] [PASS] subtraction_integrationtest::nested::test_nested::test_two [..] [PASS] subtraction_integrationtest::nested::test_nested::test_two_and_two [..] Tests: 5 passed, 0 failed, 0 ignored, 0 filtered out Failures: fibonacci_virtual_tests::abc::efg::failing_test Tests summary: 10 passed, 1 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn virtual_workspace_for_entire_workspace_inside_package() { let temp = setup_virtual_workspace(); let package_dir = temp.join(PathBuf::from("dummy_name/fibonacci_virtual")); let snapbox = test_runner(&temp).arg("--workspace"); let output = snapbox.current_dir(package_dir).assert().code(1); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Compiling[..] [..]Compiling[..] [..]Finished[..] Collected 6 test(s) from fibonacci_virtual package Running 2 test(s) from src/ [PASS] fibonacci_virtual::tests::it_works [..] [PASS] fibonacci_virtual::tests::contract_test [..] Running 4 test(s) from tests/ [PASS] fibonacci_virtual_tests::lib_test [..] [PASS] fibonacci_virtual_tests::abc::abc_test [..] [PASS] fibonacci_virtual_tests::abc::efg::efg_test [..] [FAIL] fibonacci_virtual_tests::abc::efg::failing_test Failure data: 0x0 ('') Tests: 5 passed, 1 failed, 0 ignored, 0 filtered out Collected 5 test(s) from subtraction package Running 1 test(s) from src/ [PASS] subtraction::tests::it_works [..] Running 4 test(s) from tests/ [PASS] subtraction_integrationtest::nested::simple_case [..] [PASS] subtraction_integrationtest::nested::contract_test [..] [PASS] subtraction_integrationtest::nested::test_nested::test_two [..] [PASS] subtraction_integrationtest::nested::test_nested::test_two_and_two [..] Tests: 5 passed, 0 failed, 0 ignored, 0 filtered out Failures: fibonacci_virtual_tests::abc::efg::failing_test Tests summary: 10 passed, 1 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn virtual_workspace_for_entire_workspace_and_specific_package() { let temp = setup_virtual_workspace(); let snapbox = test_runner(&temp) .arg("--workspace") .arg("--package") .arg("subtraction"); let result = snapbox.current_dir(&temp).assert().code(2); let stderr = String::from_utf8_lossy(&result.get_output().stderr); assert!(stderr.contains("the argument '--workspace' cannot be used with '--package '")); } #[test] fn virtual_workspace_missing_package() { let temp = setup_virtual_workspace(); let snapbox = test_runner(&temp).arg("--package").arg("missing_package"); let result = snapbox.current_dir(&temp).assert().code(2); let stdout = String::from_utf8_lossy(&result.get_output().stdout); assert!(stdout.contains("Failed to find any packages matching the specified filter")); } #[test] fn root_workspace_for_entire_workspace_with_filter() { let temp = setup_hello_workspace(); let output = test_runner(&temp) .args(["--workspace", "simple"]) .assert() .success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Compiling[..] [..]Compiling[..] [..]Compiling[..] [..]Compiling[..] [..]Finished[..] Collected 1 test(s) from addition package Running 0 test(s) from src/ Running 1 test(s) from tests/ [PASS] addition_integrationtest::nested::simple_case [..] Tests: 1 passed, 0 failed, 0 ignored, 4 filtered out Collected 0 test(s) from fibonacci package Running 0 test(s) from src/ Running 0 test(s) from tests/ Tests: 0 passed, 0 failed, 0 ignored, 6 filtered out Collected 1 test(s) from hello_workspaces package Running 1 test(s) from src/ [PASS] hello_workspaces::tests::test_simple [..] Running 0 test(s) from tests/ Tests: 1 passed, 0 failed, 0 ignored, 2 filtered out Tests summary: 2 passed, 0 failed, 0 ignored, 12 filtered out "}, ); } #[test] fn virtual_workspace_for_entire_workspace_with_filter() { let temp = setup_virtual_workspace(); let output = test_runner(&temp).arg("simple").assert().success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Compiling[..] [..]Compiling[..] [..]Finished[..] Collected 0 test(s) from fibonacci_virtual package Running 0 test(s) from src/ Running 0 test(s) from tests/ Tests: 0 passed, 0 failed, 0 ignored, 6 filtered out Collected 1 test(s) from subtraction package Running 0 test(s) from src/ Running 1 test(s) from tests/ [PASS] subtraction_integrationtest::nested::simple_case [..] Tests: 1 passed, 0 failed, 0 ignored, 4 filtered out Tests summary: 1 passed, 0 failed, 0 ignored, 10 filtered out "}, ); } #[test] fn root_workspace_multiple_package_arguments() { let temp = setup_hello_workspace(); let result = test_runner(&temp) .args(["--package", "addition", "--package", "fibonacci"]) .assert() .code(1); assert_stdout_contains( result, indoc! {r" [..]Compiling[..] [..]Compiling[..] [..]Compiling[..] [..]Finished[..] Collected 5 test(s) from addition package Running 1 test(s) from src/ [PASS] addition::tests::it_works [..] Running 4 test(s) from tests/ [PASS] addition_integrationtest::nested::simple_case [..] [PASS] addition_integrationtest::nested::contract_test [..] [PASS] addition_integrationtest::nested::test_nested::test_two [..] [PASS] addition_integrationtest::nested::test_nested::test_two_and_two [..] Tests: 5 passed, 0 failed, 0 ignored, 0 filtered out Collected 6 test(s) from fibonacci package Running 2 test(s) from src/ [PASS] fibonacci::tests::it_works [..] [PASS] fibonacci::tests::contract_test [..] Running 4 test(s) from tests/ [PASS] fibonacci_tests::lib_test [..] [PASS] fibonacci_tests::abc::abc_test [..] [PASS] fibonacci_tests::abc::efg::efg_test [..] [FAIL] fibonacci_tests::abc::efg::failing_test Failure data: 0x0 ('') Tests: 5 passed, 1 failed, 0 ignored, 0 filtered out Failures: fibonacci_tests::abc::efg::failing_test Tests summary: 10 passed, 1 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn virtual_workspace_multiple_package_arguments() { let temp = setup_virtual_workspace(); let result = test_runner(&temp) .args(["--package", "fibonacci_virtual", "--package", "subtraction"]) .assert() .code(1); assert_stdout_contains( result, indoc! {r" [..]Compiling[..] [..]Compiling[..] [..]Compiling[..] [..]Finished[..] Collected 6 test(s) from fibonacci_virtual package Running 2 test(s) from src/ [PASS] fibonacci_virtual::tests::it_works [..] [PASS] fibonacci_virtual::tests::contract_test [..] Running 4 test(s) from tests/ [PASS] fibonacci_virtual_tests::lib_test [..] [PASS] fibonacci_virtual_tests::abc::abc_test [..] [PASS] fibonacci_virtual_tests::abc::efg::efg_test [..] [FAIL] fibonacci_virtual_tests::abc::efg::failing_test Failure data: 0x0 ('') Tests: 5 passed, 1 failed, 0 ignored, 0 filtered out Collected 5 test(s) from subtraction package Running 1 test(s) from src/ [PASS] subtraction::tests::it_works [..] Running 4 test(s) from tests/ [PASS] subtraction_integrationtest::nested::simple_case [..] [PASS] subtraction_integrationtest::nested::contract_test [..] [PASS] subtraction_integrationtest::nested::test_nested::test_two [..] [PASS] subtraction_integrationtest::nested::test_nested::test_two_and_two [..] Tests: 5 passed, 0 failed, 0 ignored, 0 filtered out Failures: fibonacci_virtual_tests::abc::efg::failing_test Tests summary: 10 passed, 1 failed, 0 ignored, 0 filtered out "}, ); } #[test] fn root_workspace_for_entire_workspace_with_exact() { let temp = setup_hello_workspace(); let output = test_runner(&temp) .args([ "--workspace", "--exact", "hello_workspaces::tests::test_simple", ]) .assert() .success(); assert_stdout_contains( output, indoc! {r" [..]Compiling[..] [..]Compiling[..] [..]Compiling[..] [..]Compiling[..] [..]Compiling[..] [..]Finished[..] Collected 0 test(s) from addition package Running 0 test(s) from src/ Running 0 test(s) from tests/ Tests: 0 passed, 0 failed, 0 ignored, other filtered out Collected 0 test(s) from fibonacci package Running 0 test(s) from src/ Running 0 test(s) from tests/ Tests: 0 passed, 0 failed, 0 ignored, other filtered out Collected 1 test(s) from hello_workspaces package Running 1 test(s) from src/ [PASS] hello_workspaces::tests::test_simple [..] Running 0 test(s) from tests/ Tests: 1 passed, 0 failed, 0 ignored, other filtered out Tests summary: 1 passed, 0 failed, 0 ignored, other filtered out "}, ); } #[test] fn exit_first_stops_execution_after_one_failure_in_workspace() { let temp = setup_hello_workspace(); let output = test_runner(&temp) .args(["--workspace", "--exit-first"]) .assert() .code(1); // Note that the output is not deterministic, so we only assert specific parts of it. // The most important is that there is exactly 1 failed test in the summary. // This test also relies on the execution order of packages, so that // the fibonacci package is executed before the hello_workspaces package. // In general, we don't control it, as the order of execution depends on the order returned by the `scarb metadata` command. assert_stdout_contains( output, indoc! {r" Collected 5 test(s) from addition package Running 1 test(s) from src/ Running 4 test(s) from tests/ Collected 6 test(s) from fibonacci package Running 4 test(s) from tests/ Running 2 test(s) from src/ [FAIL] fibonacci_tests::abc::efg::failing_test Tests: [..] passed, 1 failed, 0 ignored, 0 filtered out Collected 3 test(s) from hello_workspaces package Running 2 test(s) from tests/ Running 1 test(s) from src/ Tests: 0 passed, 0 failed, 0 ignored, 0 filtered out Interrupted execution of [..] test(s). Failures: fibonacci_tests::abc::efg::failing_test Tests summary: [..] passed, 1 failed, 0 ignored, 0 filtered out Interrupted execution of [..] test(s). "}, ); } ================================================ FILE: crates/forge/tests/integration/available_gas.rs ================================================ use crate::utils::runner::{assert_case_output_contains, assert_failed, assert_passed}; use crate::utils::running_tests::run_test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; #[test] fn correct_available_gas() { let test = crate::utils::test_case!(indoc!( r" #[test] #[available_gas(l2_gas: 440000)] fn keccak_cost() { keccak::keccak_u256s_le_inputs(array![1].span()); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn available_gas_exceeded() { let test = crate::utils::test_case!(indoc!( r" #[test] #[available_gas(l2_gas: 5)] fn keccak_cost() { keccak::keccak_u256s_le_inputs(array![1].span()); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "keccak_cost", "Test cost exceeded the available gas. Consumed l1_gas: ~0, l1_data_gas: ~0, l2_gas: ~240000", ); } #[test] fn available_gas_fuzzing() { let test = crate::utils::test_case!(indoc!( r" #[test] #[available_gas(l2_gas: 40000000)] #[fuzzer] fn keccak_cost(x: u256) { keccak::keccak_u256s_le_inputs(array![x].span()); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/builtins.rs ================================================ use crate::utils::runner::assert_passed; use crate::utils::running_tests::run_test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; #[test] fn builtin_test() { let test = crate::utils::test_case!(indoc!( r" use core::{ pedersen::Pedersen, RangeCheck, integer::Bitwise, ec::EcOp, poseidon::Poseidon, SegmentArena, gas::GasBuiltin, starknet::System }; use core::circuit::{RangeCheck96, AddMod, MulMod}; #[test] fn test_builtins() { core::internal::require_implicit::(); core::internal::require_implicit::(); core::internal::require_implicit::(); core::internal::require_implicit::(); core::internal::require_implicit::(); core::internal::require_implicit::(); core::internal::require_implicit::(); core::internal::require_implicit::(); core::internal::require_implicit::(); core::internal::require_implicit::(); core::internal::require_implicit::(); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/cheat_block_hash.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn cheat_block_hash_basic() { let test = test_case!( indoc!( r#" use starknet::get_block_info; use snforge_std::{ CheatSpan, declare, ContractClassTrait, DeclareResultTrait, cheat_block_hash, start_cheat_block_hash, stop_cheat_block_hash, start_cheat_block_hash_global, stop_cheat_block_hash_global}; #[starknet::interface] trait ICheatBlockHashChecker { fn get_block_hash(ref self: TContractState, block_number: u64) -> felt252; } fn deploy_cheat_block_hash_checker() -> ICheatBlockHashCheckerDispatcher { let contract = declare("CheatBlockHashChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); ICheatBlockHashCheckerDispatcher { contract_address } } const BLOCK_NUMBER: u64 = 123; #[test] fn test_cheat_block_hash() { let cheat_block_hash_checker = deploy_cheat_block_hash_checker(); let old_block_hash = cheat_block_hash_checker.get_block_hash(BLOCK_NUMBER); start_cheat_block_hash(cheat_block_hash_checker.contract_address, BLOCK_NUMBER, 1234); let new_block_hash = cheat_block_hash_checker.get_block_hash(BLOCK_NUMBER); assert(new_block_hash == 1234, 'Wrong block hash'); stop_cheat_block_hash(cheat_block_hash_checker.contract_address, BLOCK_NUMBER); let new_block_hash = cheat_block_hash_checker.get_block_hash(BLOCK_NUMBER); assert(new_block_hash == old_block_hash, 'hash did not change back') } #[test] fn cheat_block_hash_multiple() { let contract = declare("CheatBlockHashChecker").unwrap().contract_class(); let (contract_address1, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let (contract_address2, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let cheat_block_hash_checker1 = ICheatBlockHashCheckerDispatcher { contract_address: contract_address1 }; let cheat_block_hash_checker2 = ICheatBlockHashCheckerDispatcher { contract_address: contract_address2 }; let old_block_hash1 = cheat_block_hash_checker1.get_block_hash(BLOCK_NUMBER); let old_block_hash2 = cheat_block_hash_checker2.get_block_hash(BLOCK_NUMBER); start_cheat_block_hash(cheat_block_hash_checker1.contract_address, BLOCK_NUMBER, 1234); start_cheat_block_hash(cheat_block_hash_checker2.contract_address, BLOCK_NUMBER, 1234); let new_block_hash1 = cheat_block_hash_checker1.get_block_hash(BLOCK_NUMBER); let new_block_hash2 = cheat_block_hash_checker2.get_block_hash(BLOCK_NUMBER); assert(new_block_hash1 == 1234, 'Wrong block hash #1'); assert(new_block_hash2 == 1234, 'Wrong block hash #2'); stop_cheat_block_hash(cheat_block_hash_checker1.contract_address, BLOCK_NUMBER); stop_cheat_block_hash(cheat_block_hash_checker2.contract_address, BLOCK_NUMBER); let new_block_hash1 = cheat_block_hash_checker1.get_block_hash(BLOCK_NUMBER); let new_block_hash2 = cheat_block_hash_checker2.get_block_hash(BLOCK_NUMBER); assert(new_block_hash1 == old_block_hash1, 'not stopped #1'); assert(new_block_hash2 == old_block_hash2, 'not stopped #2'); } #[test] fn cheat_block_hash_all_stop_one() { let cheat_block_hash_checker = deploy_cheat_block_hash_checker(); let old_block_hash = cheat_block_hash_checker.get_block_hash(BLOCK_NUMBER); start_cheat_block_hash_global(BLOCK_NUMBER, 1234); let new_block_hash = cheat_block_hash_checker.get_block_hash(BLOCK_NUMBER); assert(new_block_hash == 1234, 'Wrong block hash'); stop_cheat_block_hash(cheat_block_hash_checker.contract_address, BLOCK_NUMBER); let new_block_hash = cheat_block_hash_checker.get_block_hash(BLOCK_NUMBER); assert(new_block_hash == old_block_hash, 'hash did not change back') } #[test] fn cheat_block_hash_all() { let contract = declare("CheatBlockHashChecker").unwrap().contract_class(); let (contract_address1, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let (contract_address2, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let cheat_block_hash_checker1 = ICheatBlockHashCheckerDispatcher { contract_address: contract_address1 }; let cheat_block_hash_checker2 = ICheatBlockHashCheckerDispatcher { contract_address: contract_address2 }; let old_block_hash1 = cheat_block_hash_checker1.get_block_hash(BLOCK_NUMBER); let old_block_hash2 = cheat_block_hash_checker2.get_block_hash(BLOCK_NUMBER); start_cheat_block_hash_global(BLOCK_NUMBER, 1234); let new_block_hash1 = cheat_block_hash_checker1.get_block_hash(BLOCK_NUMBER); let new_block_hash2 = cheat_block_hash_checker2.get_block_hash(BLOCK_NUMBER); assert(new_block_hash1 == 1234, 'Wrong block hash #1'); assert(new_block_hash2 == 1234, 'Wrong block hash #2'); stop_cheat_block_hash_global(BLOCK_NUMBER); let new_block_hash1 = cheat_block_hash_checker1.get_block_hash(BLOCK_NUMBER); let new_block_hash2 = cheat_block_hash_checker2.get_block_hash(BLOCK_NUMBER); assert(new_block_hash1 == old_block_hash1, 'Wrong block hash #1'); assert(new_block_hash2 == old_block_hash2, 'Wrong block hash #2'); } "# ), Contract::from_code_path( "CheatBlockHashChecker".to_string(), Path::new("tests/data/contracts/cheat_block_hash_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn cheat_block_hash_complex() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_block_hash, stop_cheat_block_hash, start_cheat_block_hash_global }; #[starknet::interface] trait ICheatBlockHashChecker { fn get_block_hash(ref self: TContractState, block_number: u64) -> felt252; } fn deploy_cheat_block_hash_checker() -> ICheatBlockHashCheckerDispatcher { let contract = declare("CheatBlockHashChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); ICheatBlockHashCheckerDispatcher { contract_address } } const BLOCK_NUMBER: u64 = 123; #[test] fn cheat_block_hash_complex() { let contract = declare("CheatBlockHashChecker").unwrap().contract_class(); let (contract_address1, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let (contract_address2, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let cheat_block_hash_checker1 = ICheatBlockHashCheckerDispatcher { contract_address: contract_address1 }; let cheat_block_hash_checker2 = ICheatBlockHashCheckerDispatcher { contract_address: contract_address2 }; let old_block_hash2 = cheat_block_hash_checker2.get_block_hash(BLOCK_NUMBER); start_cheat_block_hash_global(BLOCK_NUMBER, 123); let new_block_hash1 = cheat_block_hash_checker1.get_block_hash(BLOCK_NUMBER); let new_block_hash2 = cheat_block_hash_checker2.get_block_hash(BLOCK_NUMBER); assert(new_block_hash1 == 123, 'Wrong block hash #1'); assert(new_block_hash2 == 123, 'Wrong block hash #2'); start_cheat_block_hash(cheat_block_hash_checker1.contract_address, BLOCK_NUMBER, 456); let new_block_hash1 = cheat_block_hash_checker1.get_block_hash(BLOCK_NUMBER); let new_block_hash2 = cheat_block_hash_checker2.get_block_hash(BLOCK_NUMBER); assert(new_block_hash1 == 456, 'Wrong block hash #3'); assert(new_block_hash2 == 123, 'Wrong block hash #4'); start_cheat_block_hash(cheat_block_hash_checker1.contract_address, BLOCK_NUMBER, 789); start_cheat_block_hash(cheat_block_hash_checker2.contract_address, BLOCK_NUMBER, 789); let new_block_hash1 = cheat_block_hash_checker1.get_block_hash(BLOCK_NUMBER); let new_block_hash2 = cheat_block_hash_checker2.get_block_hash(BLOCK_NUMBER); assert(new_block_hash1 == 789, 'Wrong block hash #5'); assert(new_block_hash2 == 789, 'Wrong block hash #6'); stop_cheat_block_hash(cheat_block_hash_checker2.contract_address, BLOCK_NUMBER); let new_block_hash1 = cheat_block_hash_checker1.get_block_hash(BLOCK_NUMBER); let new_block_hash2 = cheat_block_hash_checker2.get_block_hash(BLOCK_NUMBER); assert(new_block_hash1 == 789, 'Wrong block hash #7'); assert(new_block_hash2 == old_block_hash2, 'Wrong block hash #8'); } "# ), Contract::from_code_path( "CheatBlockHashChecker".to_string(), Path::new("tests/data/contracts/cheat_block_hash_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn cheat_block_hash_with_span() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, CheatSpan, ContractClassTrait, DeclareResultTrait, cheat_block_hash, stop_cheat_block_hash, test_address}; use starknet::SyscallResultTrait; #[starknet::interface] trait ICheatBlockHashChecker { fn get_block_hash(ref self: TContractState, block_number: u64) -> felt252; } fn deploy_cheat_block_hash_checker() -> ICheatBlockHashCheckerDispatcher { let contract = declare("CheatBlockHashChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); ICheatBlockHashCheckerDispatcher { contract_address } } const BLOCK_NUMBER: u64 = 123; #[test] fn test_cheat_block_hash_once() { let old_block_hash = get_block_hash(BLOCK_NUMBER); let dispatcher = deploy_cheat_block_hash_checker(); let target_block_hash = 123; cheat_block_hash(dispatcher.contract_address, BLOCK_NUMBER, target_block_hash, CheatSpan::TargetCalls(1)); let block_hash = dispatcher.get_block_hash(BLOCK_NUMBER); assert_eq!(block_hash, target_block_hash.into()); let block_hash = dispatcher.get_block_hash(BLOCK_NUMBER); assert_eq!(block_hash, old_block_hash.into()); } #[test] fn test_cheat_block_hash_twice() { let old_block_hash = get_block_hash(BLOCK_NUMBER); let dispatcher = deploy_cheat_block_hash_checker(); let target_block_hash = 123; cheat_block_hash(dispatcher.contract_address, BLOCK_NUMBER, target_block_hash, CheatSpan::TargetCalls(2)); let block_hash = dispatcher.get_block_hash(BLOCK_NUMBER); assert_eq!(block_hash, target_block_hash.into()); let block_hash = dispatcher.get_block_hash(BLOCK_NUMBER); assert_eq!(block_hash, target_block_hash.into()); let block_hash = dispatcher.get_block_hash(BLOCK_NUMBER); assert_eq!(block_hash, old_block_hash.into()); } #[test] fn test_cheat_block_hash_test_address() { let old_block_hash = get_block_hash(BLOCK_NUMBER); let target_block_hash = 123; cheat_block_hash(test_address(), BLOCK_NUMBER, target_block_hash, CheatSpan::TargetCalls(1)); let block_hash = get_block_hash(BLOCK_NUMBER); assert_eq!(block_hash, target_block_hash.into()); stop_cheat_block_hash(test_address(), BLOCK_NUMBER); let block_hash = get_block_hash(BLOCK_NUMBER); assert_eq!(block_hash, old_block_hash.into()); } fn get_block_hash(block_number: u64) -> felt252 { starknet::syscalls::get_block_hash_syscall(block_number).unwrap_syscall() } "# ), Contract::from_code_path( "CheatBlockHashChecker".to_string(), Path::new("tests/data/contracts/cheat_block_hash_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/cheat_block_number.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn cheat_block_number_basic() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_block_number, stop_cheat_block_number, stop_cheat_block_number_global, start_cheat_block_number_global }; #[starknet::interface] trait ICheatBlockNumberChecker { fn get_block_number(ref self: TContractState) -> u64; } #[test] fn test_stop_cheat_block_number() { let contract = declare("CheatBlockNumberChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ICheatBlockNumberCheckerDispatcher { contract_address }; let old_block_number = dispatcher.get_block_number(); start_cheat_block_number(contract_address, 234); let new_block_number = dispatcher.get_block_number(); assert(new_block_number == 234, 'Wrong block number'); stop_cheat_block_number(contract_address); let new_block_number = dispatcher.get_block_number(); assert(new_block_number == old_block_number, 'Block num did not change back'); } #[test] fn test_cheat_block_number_multiple() { let contract = declare("CheatBlockNumberChecker").unwrap().contract_class(); let (contract_address1, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let (contract_address2, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let cheat_block_number_checker1 = ICheatBlockNumberCheckerDispatcher { contract_address: contract_address1 }; let cheat_block_number_checker2 = ICheatBlockNumberCheckerDispatcher { contract_address: contract_address2 }; let old_block_number2 = cheat_block_number_checker2.get_block_number(); start_cheat_block_number(cheat_block_number_checker1.contract_address, 123); let new_block_number1 = cheat_block_number_checker1.get_block_number(); let new_block_number2 = cheat_block_number_checker2.get_block_number(); assert(new_block_number1 == 123, 'Wrong block number #1'); assert(new_block_number2 == old_block_number2, 'Wrong block number #2'); stop_cheat_block_number(cheat_block_number_checker2.contract_address); let new_block_number1 = cheat_block_number_checker1.get_block_number(); let new_block_number2 = cheat_block_number_checker2.get_block_number(); assert(new_block_number1 == 123, 'Wrong block number #1'); assert(new_block_number2 == old_block_number2, 'Wrong block number #2'); } #[test] fn test_cheat_block_number_all() { let contract = declare("CheatBlockNumberChecker").unwrap().contract_class(); let (contract_address1, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let (contract_address2, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let cheat_block_number_checker1 = ICheatBlockNumberCheckerDispatcher { contract_address: contract_address1 }; let cheat_block_number_checker2 = ICheatBlockNumberCheckerDispatcher { contract_address: contract_address2 }; let old_block_number1 = cheat_block_number_checker1.get_block_number(); let old_block_number2 = cheat_block_number_checker2.get_block_number(); start_cheat_block_number_global(123); let new_block_number1 = cheat_block_number_checker1.get_block_number(); let new_block_number2 = cheat_block_number_checker2.get_block_number(); assert(new_block_number1 == 123, 'Wrong block number #1'); assert(new_block_number2 == 123, 'Wrong block number #2'); stop_cheat_block_number_global(); let new_block_number1 = cheat_block_number_checker1.get_block_number(); let new_block_number2 = cheat_block_number_checker2.get_block_number(); assert(new_block_number1 == old_block_number1, 'CheatBlockNumber not stopped #1'); assert(new_block_number2 == old_block_number2, 'CheatBlockNumber not stopped #2'); } #[test] fn test_cheat_block_number_all_stop_one() { let contract = declare("CheatBlockNumberChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ICheatBlockNumberCheckerDispatcher { contract_address }; let old_block_number = dispatcher.get_block_number(); start_cheat_block_number_global(234); let new_block_number = dispatcher.get_block_number(); assert(new_block_number == 234, 'Wrong block number'); stop_cheat_block_number(contract_address); let new_block_number = dispatcher.get_block_number(); assert(new_block_number == old_block_number, 'Block num did not change back'); } "# ), Contract::from_code_path( "CheatBlockNumberChecker".to_string(), Path::new("tests/data/contracts/cheat_block_number_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn cheat_block_number_complex() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_block_number, stop_cheat_block_number, start_cheat_block_number_global, stop_cheat_block_number_global }; #[starknet::interface] trait ICheatBlockNumberChecker { fn get_block_number(ref self: TContractState) -> u64; } fn deploy_cheat_block_number_checker() -> ICheatBlockNumberCheckerDispatcher { let contract = declare("CheatBlockNumberChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); ICheatBlockNumberCheckerDispatcher { contract_address } } #[test] fn cheat_block_number_complex() { let contract = declare("CheatBlockNumberChecker").unwrap().contract_class(); let (contract_address1, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let (contract_address2, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let cheat_block_number_checker1 = ICheatBlockNumberCheckerDispatcher { contract_address: contract_address1 }; let cheat_block_number_checker2 = ICheatBlockNumberCheckerDispatcher { contract_address: contract_address2 }; let old_block_number2 = cheat_block_number_checker2.get_block_number(); start_cheat_block_number_global(123); let new_block_number1 = cheat_block_number_checker1.get_block_number(); let new_block_number2 = cheat_block_number_checker2.get_block_number(); assert(new_block_number1 == 123, 'Wrong block number #1'); assert(new_block_number2 == 123, 'Wrong block number #2'); start_cheat_block_number(cheat_block_number_checker1.contract_address, 456); let new_block_number1 = cheat_block_number_checker1.get_block_number(); let new_block_number2 = cheat_block_number_checker2.get_block_number(); assert(new_block_number1 == 456, 'Wrong block number #3'); assert(new_block_number2 == 123, 'Wrong block number #4'); start_cheat_block_number(cheat_block_number_checker1.contract_address, 789); start_cheat_block_number(cheat_block_number_checker2.contract_address, 789); let new_block_number1 = cheat_block_number_checker1.get_block_number(); let new_block_number2 = cheat_block_number_checker2.get_block_number(); assert(new_block_number1 == 789, 'Wrong block number #5'); assert(new_block_number2 == 789, 'Wrong block number #6'); stop_cheat_block_number(cheat_block_number_checker2.contract_address); let new_block_number1 = cheat_block_number_checker1.get_block_number(); let new_block_number2 = cheat_block_number_checker2.get_block_number(); assert(new_block_number1 == 789, 'Wrong block number #7'); assert(new_block_number2 == old_block_number2, 'Wrong block number #8'); } "# ), Contract::from_code_path( "CheatBlockNumberChecker".to_string(), Path::new("tests/data/contracts/cheat_block_number_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn cheat_block_number_with_span() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ test_address, declare, ContractClassTrait, DeclareResultTrait, cheat_block_number, start_cheat_block_number, stop_cheat_block_number, CheatSpan }; #[starknet::interface] trait ICheatBlockNumberChecker { fn get_block_number(ref self: TContractState) -> felt252; } fn deploy_cheat_block_number_checker() -> ICheatBlockNumberCheckerDispatcher { let (contract_address, _) = declare("CheatBlockNumberChecker").unwrap().contract_class().deploy(@ArrayTrait::new()).unwrap(); ICheatBlockNumberCheckerDispatcher { contract_address } } #[test] fn test_cheat_block_number_once() { let old_block_number = get_block_number(); let dispatcher = deploy_cheat_block_number_checker(); let target_block_number = 123; cheat_block_number(dispatcher.contract_address, target_block_number, CheatSpan::TargetCalls(1)); let block_number = dispatcher.get_block_number(); assert_eq!(block_number, target_block_number.into()); let block_number = dispatcher.get_block_number(); assert_eq!(block_number, old_block_number.into()); } #[test] fn test_cheat_block_number_twice() { let old_block_number = get_block_number(); let dispatcher = deploy_cheat_block_number_checker(); let target_block_number = 123; cheat_block_number(dispatcher.contract_address, target_block_number, CheatSpan::TargetCalls(2)); let block_number = dispatcher.get_block_number(); assert_eq!(block_number, target_block_number.into()); let block_number = dispatcher.get_block_number(); assert_eq!(block_number, target_block_number.into()); let block_number = dispatcher.get_block_number(); assert_eq!(block_number, old_block_number.into()); } #[test] fn test_cheat_block_number_test_address() { let old_block_number = get_block_number(); let target_block_number = 123; cheat_block_number(test_address(), target_block_number, CheatSpan::TargetCalls(1)); let block_number = get_block_number(); assert_eq!(block_number, target_block_number.into()); let block_number = get_block_number(); assert_eq!(block_number, target_block_number.into()); stop_cheat_block_number(test_address()); let block_number = get_block_number(); assert_eq!(block_number, old_block_number.into()); } fn get_block_number() -> u64 { starknet::get_block_info().unbox().block_number } "# ), Contract::from_code_path( "CheatBlockNumberChecker".to_string(), Path::new("tests/data/contracts/cheat_block_number_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/cheat_block_timestamp.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn cheat_block_timestamp_basic() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use traits::Into; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_block_timestamp, stop_cheat_block_timestamp, start_cheat_block_number, start_cheat_block_timestamp_global, stop_cheat_block_timestamp_global }; #[starknet::interface] trait ICheatBlockTimestampChecker { fn get_block_timestamp(ref self: TContractState) -> u64; fn get_block_timestamp_and_emit_event(ref self: TContractState) -> u64; fn get_block_timestamp_and_number(ref self: TContractState) -> (u64, u64); } fn deploy_cheat_block_timestamp_checker() -> ICheatBlockTimestampCheckerDispatcher { let contract = declare("CheatBlockTimestampChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); ICheatBlockTimestampCheckerDispatcher { contract_address } } #[test] fn test_cheat_block_timestamp() { let cheat_block_timestamp_checker = deploy_cheat_block_timestamp_checker(); let old_block_timestamp = cheat_block_timestamp_checker.get_block_timestamp(); start_cheat_block_timestamp(cheat_block_timestamp_checker.contract_address, 123); let new_block_timestamp = cheat_block_timestamp_checker.get_block_timestamp(); assert(new_block_timestamp == 123, 'Wrong block timestamp'); stop_cheat_block_timestamp(cheat_block_timestamp_checker.contract_address); let new_block_timestamp = cheat_block_timestamp_checker.get_block_timestamp(); assert(new_block_timestamp == old_block_timestamp, 'Timestamp did not change back') } #[test] fn cheat_block_timestamp_all_stop_one() { let cheat_block_timestamp_checker = deploy_cheat_block_timestamp_checker(); let old_block_timestamp = cheat_block_timestamp_checker.get_block_timestamp(); start_cheat_block_timestamp_global(123); let new_block_timestamp = cheat_block_timestamp_checker.get_block_timestamp(); assert(new_block_timestamp == 123, 'Wrong block timestamp'); stop_cheat_block_timestamp(cheat_block_timestamp_checker.contract_address); let new_block_timestamp = cheat_block_timestamp_checker.get_block_timestamp(); assert(new_block_timestamp == old_block_timestamp, 'Timestamp did not change back') } #[test] fn cheat_block_timestamp_multiple() { let contract = declare("CheatBlockTimestampChecker").unwrap().contract_class(); let (contract_address1, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let (contract_address2, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let cheat_block_timestamp_checker1 = ICheatBlockTimestampCheckerDispatcher { contract_address: contract_address1 }; let cheat_block_timestamp_checker2 = ICheatBlockTimestampCheckerDispatcher { contract_address: contract_address2 }; let old_block_timestamp1 = cheat_block_timestamp_checker1.get_block_timestamp(); let old_block_timestamp2 = cheat_block_timestamp_checker2.get_block_timestamp(); start_cheat_block_timestamp(cheat_block_timestamp_checker1.contract_address, 123); start_cheat_block_timestamp(cheat_block_timestamp_checker2.contract_address, 123); let new_block_timestamp1 = cheat_block_timestamp_checker1.get_block_timestamp(); let new_block_timestamp2 = cheat_block_timestamp_checker2.get_block_timestamp(); assert(new_block_timestamp1 == 123, 'Wrong block timestamp #1'); assert(new_block_timestamp2 == 123, 'Wrong block timestamp #2'); stop_cheat_block_timestamp(cheat_block_timestamp_checker1.contract_address); stop_cheat_block_timestamp(cheat_block_timestamp_checker2.contract_address); let new_block_timestamp1 = cheat_block_timestamp_checker1.get_block_timestamp(); let new_block_timestamp2 = cheat_block_timestamp_checker2.get_block_timestamp(); assert(new_block_timestamp1 == old_block_timestamp1, 'not stopped #1'); assert(new_block_timestamp2 == old_block_timestamp2, 'not stopped #2'); } #[test] fn cheat_block_timestamp_all() { let contract = declare("CheatBlockTimestampChecker").unwrap().contract_class(); let (contract_address1, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let (contract_address2, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let cheat_block_timestamp_checker1 = ICheatBlockTimestampCheckerDispatcher { contract_address: contract_address1 }; let cheat_block_timestamp_checker2 = ICheatBlockTimestampCheckerDispatcher { contract_address: contract_address2 }; let old_block_timestamp1 = cheat_block_timestamp_checker1.get_block_timestamp(); let old_block_timestamp2 = cheat_block_timestamp_checker2.get_block_timestamp(); start_cheat_block_timestamp_global(123); let new_block_timestamp1 = cheat_block_timestamp_checker1.get_block_timestamp(); let new_block_timestamp2 = cheat_block_timestamp_checker2.get_block_timestamp(); assert(new_block_timestamp1 == 123, 'Wrong block timestamp #1'); assert(new_block_timestamp2 == 123, 'Wrong block timestamp #2'); stop_cheat_block_timestamp_global(); let new_block_timestamp1 = cheat_block_timestamp_checker1.get_block_timestamp(); let new_block_timestamp2 = cheat_block_timestamp_checker2.get_block_timestamp(); assert(new_block_timestamp1 == old_block_timestamp1, 'Wrong block timestamp #1'); assert(new_block_timestamp2 == old_block_timestamp2, 'Wrong block timestamp #2'); } "# ), Contract::from_code_path( "CheatBlockTimestampChecker".to_string(), Path::new("tests/data/contracts/cheat_block_timestamp_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn cheat_block_timestamp_complex() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use traits::Into; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_block_timestamp, stop_cheat_block_timestamp, start_cheat_block_number, start_cheat_block_timestamp_global }; #[starknet::interface] trait ICheatBlockTimestampChecker { fn get_block_timestamp(ref self: TContractState) -> u64; fn get_block_timestamp_and_emit_event(ref self: TContractState) -> u64; fn get_block_timestamp_and_number(ref self: TContractState) -> (u64, u64); } fn deploy_cheat_block_timestamp_checker() -> ICheatBlockTimestampCheckerDispatcher { let contract = declare("CheatBlockTimestampChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); ICheatBlockTimestampCheckerDispatcher { contract_address } } #[test] fn cheat_block_timestamp_complex() { let contract = declare("CheatBlockTimestampChecker").unwrap().contract_class(); let (contract_address1, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let (contract_address2, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let cheat_block_timestamp_checker1 = ICheatBlockTimestampCheckerDispatcher { contract_address: contract_address1 }; let cheat_block_timestamp_checker2 = ICheatBlockTimestampCheckerDispatcher { contract_address: contract_address2 }; let old_block_timestamp2 = cheat_block_timestamp_checker2.get_block_timestamp(); start_cheat_block_timestamp_global(123); let new_block_timestamp1 = cheat_block_timestamp_checker1.get_block_timestamp(); let new_block_timestamp2 = cheat_block_timestamp_checker2.get_block_timestamp(); assert(new_block_timestamp1 == 123, 'Wrong block timestamp #1'); assert(new_block_timestamp2 == 123, 'Wrong block timestamp #2'); start_cheat_block_timestamp(cheat_block_timestamp_checker1.contract_address, 456); let new_block_timestamp1 = cheat_block_timestamp_checker1.get_block_timestamp(); let new_block_timestamp2 = cheat_block_timestamp_checker2.get_block_timestamp(); assert(new_block_timestamp1 == 456, 'Wrong block timestamp #3'); assert(new_block_timestamp2 == 123, 'Wrong block timestamp #4'); start_cheat_block_timestamp(cheat_block_timestamp_checker1.contract_address, 789); start_cheat_block_timestamp(cheat_block_timestamp_checker2.contract_address, 789); let new_block_timestamp1 = cheat_block_timestamp_checker1.get_block_timestamp(); let new_block_timestamp2 = cheat_block_timestamp_checker2.get_block_timestamp(); assert(new_block_timestamp1 == 789, 'Wrong block timestamp #5'); assert(new_block_timestamp2 == 789, 'Wrong block timestamp #6'); stop_cheat_block_timestamp(cheat_block_timestamp_checker2.contract_address); let new_block_timestamp1 = cheat_block_timestamp_checker1.get_block_timestamp(); let new_block_timestamp2 = cheat_block_timestamp_checker2.get_block_timestamp(); assert(new_block_timestamp1 == 789, 'Wrong block timestamp #7'); assert(new_block_timestamp2 == old_block_timestamp2, 'Wrong block timestamp #8'); } "# ), Contract::from_code_path( "CheatBlockTimestampChecker".to_string(), Path::new("tests/data/contracts/cheat_block_timestamp_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn cheat_block_timestamp_with_span() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ test_address, declare, ContractClassTrait, DeclareResultTrait, cheat_block_timestamp, start_cheat_block_timestamp, stop_cheat_block_timestamp, CheatSpan }; #[starknet::interface] trait ICheatBlockTimestampChecker { fn get_block_timestamp(ref self: TContractState) -> felt252; } fn deploy_cheat_block_timestamp_checker() -> ICheatBlockTimestampCheckerDispatcher { let (contract_address, _) = declare("CheatBlockTimestampChecker").unwrap().contract_class().deploy(@ArrayTrait::new()).unwrap(); ICheatBlockTimestampCheckerDispatcher { contract_address } } #[test] fn test_cheat_block_timestamp_once() { let old_block_timestamp = get_block_timestamp(); let dispatcher = deploy_cheat_block_timestamp_checker(); let target_block_timestamp = 123; cheat_block_timestamp(dispatcher.contract_address, target_block_timestamp, CheatSpan::TargetCalls(1)); let block_timestamp = dispatcher.get_block_timestamp(); assert_eq!(block_timestamp, target_block_timestamp.into()); let block_timestamp = dispatcher.get_block_timestamp(); assert_eq!(block_timestamp, old_block_timestamp.into()); } #[test] fn test_cheat_block_timestamp_twice() { let old_block_timestamp = get_block_timestamp(); let dispatcher = deploy_cheat_block_timestamp_checker(); let target_block_timestamp = 123; cheat_block_timestamp(dispatcher.contract_address, target_block_timestamp, CheatSpan::TargetCalls(2)); let block_timestamp = dispatcher.get_block_timestamp(); assert_eq!(block_timestamp, target_block_timestamp.into()); let block_timestamp = dispatcher.get_block_timestamp(); assert_eq!(block_timestamp, target_block_timestamp.into()); let block_timestamp = dispatcher.get_block_timestamp(); assert_eq!(block_timestamp, old_block_timestamp.into()); } #[test] fn test_cheat_block_timestamp_test_address() { let old_block_timestamp = get_block_timestamp(); let target_block_timestamp = 123; cheat_block_timestamp(test_address(), target_block_timestamp, CheatSpan::TargetCalls(1)); let block_timestamp = get_block_timestamp(); assert_eq!(block_timestamp, target_block_timestamp.into()); let block_timestamp = get_block_timestamp(); assert_eq!(block_timestamp, target_block_timestamp.into()); stop_cheat_block_timestamp(test_address()); let block_timestamp = get_block_timestamp(); assert_eq!(block_timestamp, old_block_timestamp.into()); } fn get_block_timestamp() -> u64 { starknet::get_block_info().unbox().block_timestamp } "# ), Contract::from_code_path( "CheatBlockTimestampChecker".to_string(), Path::new("tests/data/contracts/cheat_block_timestamp_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/cheat_caller_address.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn cheat_caller_address() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_caller_address, stop_cheat_caller_address, stop_cheat_caller_address_global, start_cheat_caller_address_global }; #[starknet::interface] trait ICheatCallerAddressChecker { fn get_caller_address(ref self: TContractState) -> felt252; } #[test] fn test_stop_cheat_caller_address() { let contract = declare("CheatCallerAddressChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ICheatCallerAddressCheckerDispatcher { contract_address }; let target_caller_address: felt252 = 123; let target_caller_address: ContractAddress = target_caller_address.try_into().unwrap(); let old_caller_address = dispatcher.get_caller_address(); start_cheat_caller_address(contract_address, target_caller_address); let new_caller_address = dispatcher.get_caller_address(); assert(new_caller_address == 123, 'Wrong caller address'); stop_cheat_caller_address(contract_address); let new_caller_address = dispatcher.get_caller_address(); assert(old_caller_address == new_caller_address, 'Address did not change back'); } #[test] fn test_cheat_caller_address_all() { let contract = declare("CheatCallerAddressChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ICheatCallerAddressCheckerDispatcher { contract_address }; let target_caller_address: felt252 = 123; let target_caller_address: ContractAddress = target_caller_address.try_into().unwrap(); let old_caller_address = dispatcher.get_caller_address(); start_cheat_caller_address_global(target_caller_address); let new_caller_address = dispatcher.get_caller_address(); assert(new_caller_address == 123, 'Wrong caller address'); stop_cheat_caller_address_global(); let new_caller_address = dispatcher.get_caller_address(); assert(new_caller_address == old_caller_address, 'Wrong caller address'); } #[test] fn test_cheat_caller_address_all_stop_one() { let contract = declare("CheatCallerAddressChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ICheatCallerAddressCheckerDispatcher { contract_address }; let target_caller_address: felt252 = 123; let target_caller_address: ContractAddress = target_caller_address.try_into().unwrap(); let old_caller_address = dispatcher.get_caller_address(); start_cheat_caller_address_global(target_caller_address); let new_caller_address = dispatcher.get_caller_address(); assert(new_caller_address == 123, 'Wrong caller address'); stop_cheat_caller_address(contract_address); let new_caller_address = dispatcher.get_caller_address(); assert(old_caller_address == new_caller_address, 'Address did not change back'); } #[test] fn test_cheat_caller_address_multiple() { let contract = declare("CheatCallerAddressChecker").unwrap().contract_class(); let (contract_address1, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let (contract_address2, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher1 = ICheatCallerAddressCheckerDispatcher { contract_address: contract_address1 }; let dispatcher2 = ICheatCallerAddressCheckerDispatcher { contract_address: contract_address2 }; let target_caller_address: felt252 = 123; let target_caller_address: ContractAddress = target_caller_address.try_into().unwrap(); let old_caller_address1 = dispatcher1.get_caller_address(); let old_caller_address2 = dispatcher2.get_caller_address(); start_cheat_caller_address(contract_address1, target_caller_address); start_cheat_caller_address(contract_address2, target_caller_address); let new_caller_address1 = dispatcher1.get_caller_address(); let new_caller_address2 = dispatcher2.get_caller_address(); assert(new_caller_address1 == 123, 'Wrong caller address #1'); assert(new_caller_address2 == 123, 'Wrong caller address #2'); stop_cheat_caller_address(contract_address1); stop_cheat_caller_address(contract_address2); let new_caller_address1 = dispatcher1.get_caller_address(); let new_caller_address2 = dispatcher2.get_caller_address(); assert(old_caller_address1 == new_caller_address1, 'Address did not change back #1'); assert(old_caller_address2 == new_caller_address2, 'Address did not change back #2'); } "# ), Contract::from_code_path( "CheatCallerAddressChecker".to_string(), Path::new("tests/data/contracts/cheat_caller_address_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn cheat_caller_address_with_span() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ test_address, declare, ContractClassTrait, DeclareResultTrait, cheat_caller_address, start_cheat_caller_address, stop_cheat_caller_address, CheatSpan }; #[starknet::interface] trait ICheatCallerAddressChecker { fn get_caller_address(ref self: TContractState) -> felt252; } fn deploy_cheat_caller_address_checker() -> ICheatCallerAddressCheckerDispatcher { let (contract_address, _) = declare("CheatCallerAddressChecker").unwrap().contract_class().deploy(@ArrayTrait::new()).unwrap(); ICheatCallerAddressCheckerDispatcher { contract_address } } #[test] fn test_cheat_caller_address_once() { let dispatcher = deploy_cheat_caller_address_checker(); let target_caller_address: ContractAddress = 123.try_into().unwrap(); cheat_caller_address(dispatcher.contract_address, target_caller_address, CheatSpan::TargetCalls(1)); let caller_address = dispatcher.get_caller_address(); assert(caller_address == target_caller_address.into(), 'Wrong caller address'); let caller_address = dispatcher.get_caller_address(); assert(caller_address == test_address().into(), 'Address did not change back'); } #[test] fn test_cheat_caller_address_twice() { let dispatcher = deploy_cheat_caller_address_checker(); let target_caller_address: ContractAddress = 123.try_into().unwrap(); cheat_caller_address(dispatcher.contract_address, target_caller_address, CheatSpan::TargetCalls(2)); let caller_address = dispatcher.get_caller_address(); assert(caller_address == target_caller_address.into(), 'Wrong caller address'); let caller_address = dispatcher.get_caller_address(); assert(caller_address == target_caller_address.into(), 'Wrong caller address'); let caller_address = dispatcher.get_caller_address(); assert(caller_address == test_address().into(), 'Address did not change back'); } #[test] fn test_cheat_caller_address_test_address() { let old_caller_address = starknet::get_caller_address(); let target_caller_address: ContractAddress = 123.try_into().unwrap(); cheat_caller_address(test_address(), target_caller_address, CheatSpan::TargetCalls(1)); let caller_address = starknet::get_caller_address(); assert(caller_address == target_caller_address, 'Wrong caller address'); let caller_address = starknet::get_caller_address(); assert(caller_address == target_caller_address, 'Wrong caller address'); stop_cheat_caller_address(test_address()); let caller_address = starknet::get_caller_address(); assert(caller_address == old_caller_address, 'Wrong caller address'); } "# ), Contract::from_code_path( "CheatCallerAddressChecker".to_string(), Path::new("tests/data/contracts/cheat_caller_address_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } /// Verify that a cheat applied to `test_address()` is visible inside a library call made /// directly from test code. #[test] fn cheat_caller_address_direct_library_call_from_test() { let test = test_case!( indoc!( r#" use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_caller_address, stop_cheat_caller_address, test_address, }; #[starknet::interface] trait ICheatCallerAddressChecker { fn get_caller_address(ref self: TContractState) -> felt252; } #[test] fn test_cheat_applied_to_test_address_is_visible_in_library_call() { let class_hash = *declare("CheatCallerAddressChecker").unwrap().contract_class().class_hash; let lib_dispatcher = ICheatCallerAddressCheckerLibraryDispatcher { class_hash }; let original_caller = lib_dispatcher.get_caller_address(); assert(original_caller == 0.try_into().unwrap(), 'Caller should be 0'); let target_caller: ContractAddress = 123.try_into().unwrap(); start_cheat_caller_address(test_address(), target_caller); let caller = lib_dispatcher.get_caller_address(); assert(caller == 123, 'Wrong caller address'); stop_cheat_caller_address(test_address()); let caller = lib_dispatcher.get_caller_address(); assert(caller == original_caller, 'Caller did not reset'); } "# ), Contract::from_code_path( "CheatCallerAddressChecker".to_string(), Path::new("tests/data/contracts/cheat_caller_address_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); } /// Verify that a global cheat is visible inside a library call made directly from test code. #[test] fn cheat_caller_address_global_direct_library_call_from_test() { let test = test_case!( indoc!( r#" use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_caller_address_global, stop_cheat_caller_address_global, }; #[starknet::interface] trait ICheatCallerAddressChecker { fn get_caller_address(ref self: TContractState) -> felt252; } #[test] fn test_global_cheat_is_visible_in_library_call() { let class_hash = *declare("CheatCallerAddressChecker").unwrap().contract_class().class_hash; let lib_dispatcher = ICheatCallerAddressCheckerLibraryDispatcher { class_hash }; let original_caller = lib_dispatcher.get_caller_address(); assert(original_caller == 0.try_into().unwrap(), 'Caller should be 0'); let target_caller: ContractAddress = 456.try_into().unwrap(); start_cheat_caller_address_global(target_caller); let caller = lib_dispatcher.get_caller_address(); assert(caller == 456, 'Wrong caller address'); stop_cheat_caller_address_global(); let caller = lib_dispatcher.get_caller_address(); assert(caller == original_caller, 'Caller did not reset'); } "# ), Contract::from_code_path( "CheatCallerAddressChecker".to_string(), Path::new("tests/data/contracts/cheat_caller_address_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); } /// Verify that a cheat applied to `test_address()` is visible when the library call is made /// via the raw `library_call_syscall` directly from test code. #[test] fn cheat_caller_address_via_syscall_from_test() { let test = test_case!( indoc!( r#" use starknet::ContractAddress; use starknet::{library_call_syscall, SyscallResultTrait}; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_caller_address, stop_cheat_caller_address, test_address, }; #[test] fn test_cheat_via_syscall_is_visible_in_library_call() { let class_hash = *declare("CheatCallerAddressChecker").unwrap().contract_class().class_hash; let original_caller = *library_call_syscall( class_hash, selector!("get_caller_address"), array![].span(), ).unwrap_syscall()[0]; let target_caller: ContractAddress = 123.try_into().unwrap(); start_cheat_caller_address(test_address(), target_caller); let caller = *library_call_syscall( class_hash, selector!("get_caller_address"), array![].span(), ).unwrap_syscall()[0]; assert(caller == 123, 'Wrong caller address'); stop_cheat_caller_address(test_address()); let caller = *library_call_syscall( class_hash, selector!("get_caller_address"), array![].span(), ).unwrap_syscall()[0]; assert(caller == original_caller, 'Caller did not reset'); } "# ), Contract::from_code_path( "CheatCallerAddressChecker".to_string(), Path::new("tests/data/contracts/cheat_caller_address_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); } /// Verify that a cheat applied to `test_address()` is visible when the library call is made /// via the safe library dispatcher directly from test code. #[test] fn cheat_caller_address_via_safe_dispatcher_from_test() { let test = test_case!( indoc!( r#" use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_caller_address, stop_cheat_caller_address, test_address, }; #[starknet::interface] trait ICheatCallerAddressChecker { fn get_caller_address(ref self: TContractState) -> felt252; } #[test] #[feature("safe_dispatcher")] fn test_cheat_via_safe_dispatcher_is_visible_in_library_call() { let class_hash = *declare("CheatCallerAddressChecker").unwrap().contract_class().class_hash; let safe_lib_dispatcher = ICheatCallerAddressCheckerSafeLibraryDispatcher { class_hash }; let original_caller = safe_lib_dispatcher.get_caller_address().unwrap(); let target_caller: ContractAddress = 123.try_into().unwrap(); start_cheat_caller_address(test_address(), target_caller); let caller = safe_lib_dispatcher.get_caller_address().unwrap(); assert(caller == 123, 'Wrong caller address'); stop_cheat_caller_address(test_address()); let caller = safe_lib_dispatcher.get_caller_address().unwrap(); assert(caller == original_caller, 'Caller did not reset'); } "# ), Contract::from_code_path( "CheatCallerAddressChecker".to_string(), Path::new("tests/data/contracts/cheat_caller_address_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); } /// Verify that a cheat applied to a contract address is visible when that contract internally /// makes a library call. #[test] fn cheat_caller_address_library_call_from_contract() { let test = test_case!( indoc!( r#" use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_caller_address, stop_cheat_caller_address, test_address, }; #[starknet::interface] trait ICheatCallerAddressLibraryCallChecker { fn get_caller_address_via_library_call( self: @TContractState, class_hash: starknet::ClassHash, ) -> felt252; } #[test] fn test_cheat_applied_to_contract_is_visible_in_its_library_call() { let checker_class_hash = *declare("CheatCallerAddressChecker").unwrap().contract_class().class_hash; let proxy = declare("CheatCallerAddressLibraryCallChecker").unwrap().contract_class(); let (proxy_address, _) = proxy.deploy(@array![]).unwrap(); let proxy_dispatcher = ICheatCallerAddressLibraryCallCheckerDispatcher { contract_address: proxy_address, }; let target_caller: ContractAddress = 789.try_into().unwrap(); start_cheat_caller_address(proxy_address, target_caller); let caller = proxy_dispatcher.get_caller_address_via_library_call(checker_class_hash); assert(caller == 789, 'Wrong caller address'); stop_cheat_caller_address(proxy_address); let caller = proxy_dispatcher.get_caller_address_via_library_call(checker_class_hash); assert(caller == test_address().into(), 'Caller did not reset'); } "# ), Contract::from_code_path( "CheatCallerAddressChecker".to_string(), Path::new("tests/data/contracts/cheat_caller_address_checker.cairo"), ) .unwrap(), Contract::from_code_path( "CheatCallerAddressLibraryCallChecker".to_string(), Path::new("tests/data/contracts/cheat_caller_address_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/cheat_execution_info.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn start_and_stop_cheat_transaction_hash_single_attribute() { let test = test_case!( indoc!( r#" use result::ResultTrait; use box::BoxTrait; use starknet::info::TxInfo; use serde::Serde; use starknet::ContractAddress; use array::SpanTrait; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_transaction_hash, start_cheat_transaction_hash_global, stop_cheat_transaction_hash }; use starknet::info::v2::ResourceBounds; #[starknet::interface] trait ICheatTxInfoChecker { fn get_tx_info(ref self: TContractState) -> starknet::info::v2::TxInfo; } #[test] fn start_cheat_transaction_hash_single_attribute() { let contract = declare("CheatTxInfoChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ICheatTxInfoCheckerDispatcher { contract_address }; let tx_info_before = dispatcher.get_tx_info(); start_cheat_transaction_hash(contract_address, 421); let mut expected_tx_info = tx_info_before; expected_tx_info.transaction_hash = 421; assert_tx_info(dispatcher.get_tx_info(), expected_tx_info); stop_cheat_transaction_hash(contract_address); assert_tx_info(dispatcher.get_tx_info(), tx_info_before); } #[test] fn test_cheat_transaction_hash_all_stop_one() { let contract = declare("CheatTxInfoChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ICheatTxInfoCheckerDispatcher { contract_address }; let tx_info_before = dispatcher.get_tx_info(); start_cheat_transaction_hash_global(421); let mut expected_tx_info = tx_info_before; expected_tx_info.transaction_hash = 421; assert_tx_info(dispatcher.get_tx_info(), expected_tx_info); stop_cheat_transaction_hash(contract_address); assert_tx_info(dispatcher.get_tx_info(), tx_info_before); } fn assert_tx_info(tx_info: starknet::info::v2::TxInfo, expected_tx_info: starknet::info::v2::TxInfo) { assert(tx_info.version == expected_tx_info.version, 'Invalid version'); assert(tx_info.account_contract_address == expected_tx_info.account_contract_address, 'Invalid account_contract_addr'); assert(tx_info.max_fee == expected_tx_info.max_fee, 'Invalid max_fee'); assert(tx_info.signature == expected_tx_info.signature, 'Invalid signature'); assert(tx_info.transaction_hash == expected_tx_info.transaction_hash, 'Invalid transaction_hash'); assert(tx_info.chain_id == expected_tx_info.chain_id, 'Invalid chain_id'); assert(tx_info.nonce == expected_tx_info.nonce, 'Invalid nonce'); let mut resource_bounds = array![]; tx_info.resource_bounds.serialize(ref resource_bounds); let mut expected_resource_bounds = array![]; expected_tx_info.resource_bounds.serialize(ref expected_resource_bounds); assert(resource_bounds == expected_resource_bounds, 'Invalid resource bounds'); assert(tx_info.tip == expected_tx_info.tip, 'Invalid tip'); assert(tx_info.paymaster_data == expected_tx_info.paymaster_data, 'Invalid paymaster_data'); assert(tx_info.nonce_data_availability_mode == expected_tx_info.nonce_data_availability_mode, 'Invalid nonce_data_av_mode'); assert(tx_info.fee_data_availability_mode == expected_tx_info.fee_data_availability_mode, 'Invalid fee_data_av_mode'); assert(tx_info.account_deployment_data == expected_tx_info.account_deployment_data, 'Invalid account_deployment_data'); } "# ), Contract::from_code_path( "CheatTxInfoChecker".to_string(), Path::new("tests/data/contracts/cheat_tx_info_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } // TODO(#2765): Test below was intended to test private `ExecutionInfoMock` and `cheat_execution_info` #[test] #[expect(clippy::too_many_lines)] fn start_cheat_execution_info_all_per_contract() { let test = test_case!( indoc!( r#" use array::SpanTrait; use option::OptionTrait; use result::ResultTrait; use serde::Serde; use snforge_std::{ ContractClassTrait, DeclareResultTrait, declare, start_cheat_account_contract_address, start_cheat_account_deployment_data, start_cheat_chain_id, start_cheat_fee_data_availability_mode, start_cheat_max_fee, start_cheat_nonce, start_cheat_nonce_data_availability_mode, start_cheat_paymaster_data, start_cheat_proof_facts, start_cheat_resource_bounds, start_cheat_signature, start_cheat_tip, start_cheat_transaction_hash, start_cheat_transaction_version, }; use starknet::info::TxInfo; use starknet::info::v3::ResourceBounds; use starknet::{ContractAddress, ContractAddressIntoFelt252, Felt252TryIntoContractAddress}; use traits::Into; #[starknet::interface] trait ICheatTxInfoChecker { fn get_tx_hash(ref self: TContractState) -> felt252; fn get_nonce(ref self: TContractState) -> felt252; fn get_account_contract_address(ref self: TContractState) -> ContractAddress; fn get_signature(ref self: TContractState) -> Span; fn get_version(ref self: TContractState) -> felt252; fn get_max_fee(ref self: TContractState) -> u128; fn get_chain_id(ref self: TContractState) -> felt252; fn get_resource_bounds(ref self: TContractState) -> Span; fn get_tip(ref self: TContractState) -> u128; fn get_paymaster_data(ref self: TContractState) -> Span; fn get_nonce_data_availability_mode(ref self: TContractState) -> u32; fn get_fee_data_availability_mode(ref self: TContractState) -> u32; fn get_account_deployment_data(ref self: TContractState) -> Span; fn get_proof_facts(self: @TContractState) -> Span; } #[test] fn start_cheat_execution_info_all_attributes_mocked() { let contract = declare("CheatTxInfoChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ICheatTxInfoCheckerDispatcher { contract_address }; start_cheat_nonce(contract_address, 411); start_cheat_account_contract_address(contract_address, 422.try_into().unwrap()); start_cheat_transaction_version(contract_address, 433); start_cheat_transaction_hash(contract_address, 444); start_cheat_chain_id(contract_address, 455); start_cheat_max_fee(contract_address, 466); start_cheat_signature(contract_address, array![477, 478].span()); start_cheat_resource_bounds( contract_address, array![ ResourceBounds { resource: 55, max_amount: 66, max_price_per_unit: 77 }, ResourceBounds { resource: 111, max_amount: 222, max_price_per_unit: 333 }, ] .span(), ); start_cheat_tip(contract_address, 123); start_cheat_paymaster_data(contract_address, array![22, 33, 44].span()); start_cheat_nonce_data_availability_mode(contract_address, 99); start_cheat_fee_data_availability_mode(contract_address, 88); start_cheat_account_deployment_data(contract_address, array![111, 222].span()); start_cheat_proof_facts(contract_address, array![19, 38, 57, 76].span()); let nonce = dispatcher.get_nonce(); assert(nonce == 411, 'Invalid nonce'); let account_contract_address: felt252 = dispatcher.get_account_contract_address().into(); assert(account_contract_address == 422, 'Invalid account address'); let version = dispatcher.get_version(); assert(version == 433, 'Invalid version'); let transaction_hash = dispatcher.get_tx_hash(); assert(transaction_hash == 444, 'Invalid tx hash'); let chain_id = dispatcher.get_chain_id(); assert(chain_id == 455, 'Invalid chain_id'); let max_fee = dispatcher.get_max_fee(); assert(max_fee == 466_u128, 'Invalid max_fee'); let signature = dispatcher.get_signature(); assert(signature.len() == 2, 'Invalid signature len'); assert(*signature.at(0) == 477, 'Invalid signature el[0]'); assert(*signature.at(1) == 478, 'Invalid signature el[1]'); let resource_bounds = dispatcher.get_resource_bounds(); assert(resource_bounds.len() == 2, 'Invalid resource_bounds len'); assert(*resource_bounds.at(0).resource == 55, 'Invalid resource_bounds[0][0]'); assert(*resource_bounds.at(0).max_amount == 66, 'Invalid resource_bounds[0][1]'); assert(*resource_bounds.at(0).max_price_per_unit == 77, 'Invalid resource_bounds[0][2]'); assert(*resource_bounds.at(1).resource == 111, 'Invalid resource_bounds[1][0]'); assert(*resource_bounds.at(1).max_amount == 222, 'Invalid resource_bounds[1][1]'); assert(*resource_bounds.at(1).max_price_per_unit == 333, 'Invalid resource_bounds[1][2]'); let tip = dispatcher.get_tip(); assert(tip == 123, 'Invalid tip'); let paymaster_data = dispatcher.get_paymaster_data(); assert(paymaster_data == array![22, 33, 44].span(), 'Invalid paymaster_data'); let nonce_data_availability_mode = dispatcher.get_nonce_data_availability_mode(); assert(nonce_data_availability_mode == 99, 'Invalid nonce data'); let fee_data_availability_mode = dispatcher.get_fee_data_availability_mode(); assert(fee_data_availability_mode == 88, 'Invalid fee data'); let account_deployment_data = dispatcher.get_account_deployment_data(); assert(account_deployment_data == array![111, 222].span(), 'Invalid account deployment'); let proof_facts = dispatcher.get_proof_facts(); assert(proof_facts == array![19, 38, 57, 76].span(), 'Invalid proof facts'); } "# ), Contract::from_code_path( "CheatTxInfoChecker".to_string(), Path::new("tests/data/contracts/cheat_tx_info_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn start_cheat_transaction_hash_cancel_mock_by_setting_attribute_to_none() { let test = test_case!( indoc!( r#" use result::ResultTrait; use box::BoxTrait; use starknet::info::TxInfo; use serde::Serde; use starknet::ContractAddress; use array::SpanTrait; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_transaction_hash, stop_cheat_transaction_hash, CheatSpan }; use starknet::info::v3::ResourceBounds; #[starknet::interface] trait ICheatTxInfoChecker { fn get_tx_info_v3(ref self: TContractState) -> starknet::info::v3::TxInfo; } #[test] fn start_cheat_transaction_hash_cancel_mock_by_setting_attribute_to_none() { let contract = declare("CheatTxInfoChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ICheatTxInfoCheckerDispatcher { contract_address }; let tx_info_before_mock = dispatcher.get_tx_info_v3(); start_cheat_transaction_hash(contract_address, 421); let mut expected_tx_info = tx_info_before_mock; expected_tx_info.transaction_hash = 421; assert_tx_info(dispatcher.get_tx_info_v3(), expected_tx_info); stop_cheat_transaction_hash(contract_address); assert_tx_info(dispatcher.get_tx_info_v3(), tx_info_before_mock); } fn assert_tx_info(tx_info: starknet::info::v3::TxInfo, expected_tx_info: starknet::info::v3::TxInfo) { assert(tx_info.version == expected_tx_info.version, 'Invalid version'); assert(tx_info.account_contract_address == expected_tx_info.account_contract_address, 'Invalid account_contract_addr'); assert(tx_info.max_fee == expected_tx_info.max_fee, 'Invalid max_fee'); assert(tx_info.signature == expected_tx_info.signature, 'Invalid signature'); assert(tx_info.transaction_hash == expected_tx_info.transaction_hash, 'Invalid transaction_hash'); assert(tx_info.chain_id == expected_tx_info.chain_id, 'Invalid chain_id'); assert(tx_info.nonce == expected_tx_info.nonce, 'Invalid nonce'); let mut resource_bounds = array![]; tx_info.resource_bounds.serialize(ref resource_bounds); let mut expected_resource_bounds = array![]; expected_tx_info.resource_bounds.serialize(ref expected_resource_bounds); assert(resource_bounds == expected_resource_bounds, 'Invalid resource bounds'); assert(tx_info.tip == expected_tx_info.tip, 'Invalid tip'); assert(tx_info.paymaster_data == expected_tx_info.paymaster_data, 'Invalid paymaster_data'); assert(tx_info.nonce_data_availability_mode == expected_tx_info.nonce_data_availability_mode, 'Invalid nonce_data_av_mode'); assert(tx_info.fee_data_availability_mode == expected_tx_info.fee_data_availability_mode, 'Invalid fee_data_av_mode'); assert(tx_info.account_deployment_data == expected_tx_info.account_deployment_data, 'Invalid account_deployment_data'); assert(tx_info.proof_facts == expected_tx_info.proof_facts, 'Invalid proof_facts'); } "# ), Contract::from_code_path( "CheatTxInfoChecker".to_string(), Path::new("tests/data/contracts/cheat_tx_info_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn start_cheat_transaction_hash_multiple() { let test = test_case!( indoc!( r#" use result::ResultTrait; use option::OptionTrait; use starknet::info::TxInfo; use serde::Serde; use traits::Into; use starknet::ContractAddress; use starknet::ContractAddressIntoFelt252; use array::SpanTrait; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_transaction_hash, CheatSpan}; #[starknet::interface] trait ICheatTxInfoChecker { fn get_tx_hash(ref self: TContractState) -> felt252; } #[test] fn start_cheat_transaction_hash_multiple() { let contract = declare("CheatTxInfoChecker").unwrap().contract_class(); let (contract_address_1, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher_1 = ICheatTxInfoCheckerDispatcher { contract_address: contract_address_1 }; let (contract_address_2, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher_2 = ICheatTxInfoCheckerDispatcher { contract_address: contract_address_2 }; start_cheat_transaction_hash(contract_address_1, 421); start_cheat_transaction_hash(contract_address_2, 421); let transaction_hash = dispatcher_1.get_tx_hash(); assert(transaction_hash == 421, 'Invalid tx hash'); let transaction_hash = dispatcher_2.get_tx_hash(); assert(transaction_hash == 421, 'Invalid tx hash'); } "# ), Contract::from_code_path( "CheatTxInfoChecker".to_string(), Path::new("tests/data/contracts/cheat_tx_info_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } // TODO(#2765): Test below was intended to test private `ExecutionInfoMock` and `cheat_execution_info` #[test] #[expect(clippy::too_many_lines)] fn start_cheat_execution_info_all_global() { let test = test_case!( indoc!( r#" use array::SpanTrait; use option::OptionTrait; use result::ResultTrait; use serde::Serde; use snforge_std::{ ContractClassTrait, DeclareResultTrait, declare, start_cheat_account_contract_address_global, start_cheat_account_deployment_data_global, start_cheat_chain_id_global, start_cheat_fee_data_availability_mode_global, start_cheat_max_fee_global, start_cheat_nonce_data_availability_mode_global, start_cheat_nonce_global, start_cheat_paymaster_data_global, start_cheat_proof_facts_global, start_cheat_resource_bounds_global, start_cheat_signature_global, start_cheat_tip_global, start_cheat_transaction_hash_global, start_cheat_transaction_version_global, }; use starknet::info::TxInfo; use starknet::info::v2::ResourceBounds; use starknet::{ContractAddress, ContractAddressIntoFelt252}; use traits::Into; #[starknet::interface] trait ICheatTxInfoChecker { fn get_tx_hash(self: @TContractState) -> felt252; fn get_nonce(self: @TContractState) -> felt252; fn get_account_contract_address(self: @TContractState) -> ContractAddress; fn get_signature(self: @TContractState) -> Span; fn get_version(self: @TContractState) -> felt252; fn get_max_fee(self: @TContractState) -> u128; fn get_chain_id(self: @TContractState) -> felt252; fn get_resource_bounds(self: @TContractState) -> Span; fn get_tip(self: @TContractState) -> u128; fn get_paymaster_data(self: @TContractState) -> Span; fn get_nonce_data_availability_mode(self: @TContractState) -> u32; fn get_fee_data_availability_mode(self: @TContractState) -> u32; fn get_account_deployment_data(self: @TContractState) -> Span; fn get_proof_facts(self: @TContractState) -> Span; fn get_tx_info(self: @TContractState) -> starknet::info::v2::TxInfo; fn get_tx_info_v3(self: @TContractState) -> starknet::info::v3::TxInfo; } #[test] fn start_cheat_execution_info_all_one_param() { let contract = declare("CheatTxInfoChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ICheatTxInfoCheckerDispatcher { contract_address }; start_cheat_transaction_hash_global(421); let transaction_hash = dispatcher.get_tx_hash(); assert(transaction_hash == 421, 'Invalid tx hash'); } #[test] fn start_cheat_execution_info_all_multiple_params() { let contract = declare("CheatTxInfoChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ICheatTxInfoCheckerDispatcher { contract_address }; start_cheat_nonce_global(411); start_cheat_account_contract_address_global(422.try_into().unwrap()); start_cheat_transaction_version_global(433); start_cheat_transaction_hash_global(444); start_cheat_chain_id_global(455); start_cheat_max_fee_global(466_u128); start_cheat_signature_global(array![477, 478].span()); start_cheat_resource_bounds_global( array![ ResourceBounds { resource: 55, max_amount: 66, max_price_per_unit: 77 }, ResourceBounds { resource: 111, max_amount: 222, max_price_per_unit: 333 }, ] .span(), ); start_cheat_tip_global(123); start_cheat_paymaster_data_global(array![22, 33, 44].span()); start_cheat_nonce_data_availability_mode_global(99); start_cheat_fee_data_availability_mode_global(88); start_cheat_account_deployment_data_global(array![111, 222].span()); start_cheat_proof_facts_global(array![999, 1001].span()); let nonce = dispatcher.get_nonce(); assert(nonce == 411, 'Invalid nonce'); let account_contract_address: felt252 = dispatcher.get_account_contract_address().into(); assert(account_contract_address == 422, 'Invalid account address'); let version = dispatcher.get_version(); assert(version == 433, 'Invalid version'); let transaction_hash = dispatcher.get_tx_hash(); assert(transaction_hash == 444, 'Invalid tx hash'); let chain_id = dispatcher.get_chain_id(); assert(chain_id == 455, 'Invalid chain_id'); let max_fee = dispatcher.get_max_fee(); assert(max_fee == 466_u128, 'Invalid max_fee'); let signature = dispatcher.get_signature(); assert(signature.len() == 2, 'Invalid signature len'); assert(*signature.at(0) == 477, 'Invalid signature el[0]'); assert(*signature.at(1) == 478, 'Invalid signature el[1]'); let resource_bounds = dispatcher.get_resource_bounds(); assert(resource_bounds.len() == 2, 'Invalid resource_bounds len'); assert(*resource_bounds.at(0).resource == 55, 'Invalid resource_bounds[0][0]'); assert(*resource_bounds.at(0).max_amount == 66, 'Invalid resource_bounds[0][1]'); assert(*resource_bounds.at(0).max_price_per_unit == 77, 'Invalid resource_bounds[0][2]'); assert(*resource_bounds.at(1).resource == 111, 'Invalid resource_bounds[1][0]'); assert(*resource_bounds.at(1).max_amount == 222, 'Invalid resource_bounds[1][1]'); assert(*resource_bounds.at(1).max_price_per_unit == 333, 'Invalid resource_bounds[1][2]'); let tip = dispatcher.get_tip(); assert(tip == 123, 'Invalid tip'); let paymaster_data = dispatcher.get_paymaster_data(); assert(paymaster_data == array![22, 33, 44].span(), 'Invalid paymaster_data'); let nonce_data_availability_mode = dispatcher.get_nonce_data_availability_mode(); assert(nonce_data_availability_mode == 99, 'Invalid nonce data'); let fee_data_availability_mode = dispatcher.get_fee_data_availability_mode(); assert(fee_data_availability_mode == 88, 'Invalid fee data'); let account_deployment_data = dispatcher.get_account_deployment_data(); assert(account_deployment_data == array![111, 222].span(), 'Invalid account deployment'); let proof_facts = dispatcher.get_proof_facts(); assert(proof_facts == array![999, 1001].span(), 'Invalid proof facts'); } "# ), Contract::from_code_path( "CheatTxInfoChecker".to_string(), Path::new("tests/data/contracts/cheat_tx_info_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn start_cheat_transaction_hash_complex() { let test = test_case!( indoc!( r#" use result::ResultTrait; use option::OptionTrait; use starknet::info::TxInfo; use serde::Serde; use traits::Into; use starknet::ContractAddress; use starknet::ContractAddressIntoFelt252; use array::SpanTrait; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_transaction_hash, start_cheat_transaction_hash_global, CheatSpan }; #[starknet::interface] trait ICheatTxInfoChecker { fn get_tx_hash(ref self: TContractState) -> felt252; } #[test] fn start_cheat_transaction_hash_complex() { let contract = declare("CheatTxInfoChecker").unwrap().contract_class(); let (contract_address_1, _) = contract.deploy(@array![]).unwrap(); let (contract_address_2, _) = contract.deploy(@array![]).unwrap(); let dispatcher_1 = ICheatTxInfoCheckerDispatcher { contract_address: contract_address_1 }; let dispatcher_2 = ICheatTxInfoCheckerDispatcher { contract_address: contract_address_2 }; start_cheat_transaction_hash_global(421); let transaction_hash_1 = dispatcher_1.get_tx_hash(); let transaction_hash_2 = dispatcher_2.get_tx_hash(); assert(transaction_hash_1 == 421, 'Invalid tx hash'); assert(transaction_hash_2 == 421, 'Invalid tx hash'); start_cheat_transaction_hash(contract_address_2, 621); let transaction_hash_1 = dispatcher_1.get_tx_hash(); let transaction_hash_2 = dispatcher_2.get_tx_hash(); assert(transaction_hash_1 == 421, 'Invalid tx hash'); assert(transaction_hash_2 == 621, 'Invalid tx hash'); start_cheat_transaction_hash(contract_address_1, 821); start_cheat_transaction_hash(contract_address_2, 821); let transaction_hash_1 = dispatcher_1.get_tx_hash(); let transaction_hash_2 = dispatcher_2.get_tx_hash(); assert(transaction_hash_1 == 821, 'Invalid tx hash'); assert(transaction_hash_2 == 821, 'Invalid tx hash'); } "# ), Contract::from_code_path( "CheatTxInfoChecker".to_string(), Path::new("tests/data/contracts/cheat_tx_info_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn cheat_transaction_hash_with_span() { let test = test_case!( indoc!( r#" use result::ResultTrait; use box::BoxTrait; use starknet::info::TxInfo; use serde::Serde; use starknet::ContractAddress; use array::SpanTrait; use snforge_std::{ test_address, declare, ContractClassTrait, DeclareResultTrait, cheat_transaction_hash, stop_cheat_transaction_hash, CheatSpan }; use starknet::info::v2::ResourceBounds; use starknet::syscalls::get_execution_info_v2_syscall; use starknet::SyscallResultTrait; #[starknet::interface] trait ICheatTxInfoChecker { fn get_tx_info(ref self: TContractState) -> starknet::info::v2::TxInfo; } fn deploy_cheat_transaction_hash_checker() -> ICheatTxInfoCheckerDispatcher { let (contract_address, _) = declare("CheatTxInfoChecker").unwrap().contract_class().deploy(@ArrayTrait::new()).unwrap(); ICheatTxInfoCheckerDispatcher { contract_address } } fn assert_tx_info(tx_info: starknet::info::v2::TxInfo, expected_tx_info: starknet::info::v2::TxInfo) { assert(tx_info.version == expected_tx_info.version, 'Invalid version'); assert(tx_info.account_contract_address == expected_tx_info.account_contract_address, 'Invalid account_contract_addr'); assert(tx_info.max_fee == expected_tx_info.max_fee, 'Invalid max_fee'); assert(tx_info.signature == expected_tx_info.signature, 'Invalid signature'); assert(tx_info.transaction_hash == expected_tx_info.transaction_hash, 'Invalid transaction_hash'); assert(tx_info.chain_id == expected_tx_info.chain_id, 'Invalid chain_id'); assert(tx_info.nonce == expected_tx_info.nonce, 'Invalid nonce'); let mut resource_bounds = array![]; tx_info.resource_bounds.serialize(ref resource_bounds); let mut expected_resource_bounds = array![]; expected_tx_info.resource_bounds.serialize(ref expected_resource_bounds); assert(resource_bounds == expected_resource_bounds, 'Invalid resource bounds'); assert(tx_info.tip == expected_tx_info.tip, 'Invalid tip'); assert(tx_info.paymaster_data == expected_tx_info.paymaster_data, 'Invalid paymaster_data'); assert(tx_info.nonce_data_availability_mode == expected_tx_info.nonce_data_availability_mode, 'Invalid nonce_data_av_mode'); assert(tx_info.fee_data_availability_mode == expected_tx_info.fee_data_availability_mode, 'Invalid fee_data_av_mode'); assert(tx_info.account_deployment_data == expected_tx_info.account_deployment_data, 'Invalid account_deployment_data'); } #[test] fn test_cheat_transaction_hash_once() { let dispatcher = deploy_cheat_transaction_hash_checker(); let tx_info_before = dispatcher.get_tx_info(); cheat_transaction_hash(dispatcher.contract_address, 421, CheatSpan::TargetCalls(1)); let mut expected_tx_info = tx_info_before; expected_tx_info.transaction_hash = 421; assert_tx_info(dispatcher.get_tx_info(), expected_tx_info); assert_tx_info(dispatcher.get_tx_info(), tx_info_before); } #[test] fn test_cheat_transaction_hash_twice() { let dispatcher = deploy_cheat_transaction_hash_checker(); let tx_info_before = dispatcher.get_tx_info(); cheat_transaction_hash(dispatcher.contract_address, 421, CheatSpan::TargetCalls(2)); let mut expected_tx_info = tx_info_before; expected_tx_info.transaction_hash = 421; assert_tx_info(dispatcher.get_tx_info(), expected_tx_info); assert_tx_info(dispatcher.get_tx_info(), expected_tx_info); assert_tx_info(dispatcher.get_tx_info(), tx_info_before); } #[test] fn test_cheat_transaction_hash_test_address() { let tx_info_before = get_execution_info_v2_syscall().unwrap_syscall().unbox().tx_info.unbox(); cheat_transaction_hash(test_address(), 421,CheatSpan::TargetCalls(1) ); let mut expected_tx_info = tx_info_before; expected_tx_info.transaction_hash = 421; let tx_info = get_execution_info_v2_syscall().unwrap_syscall().unbox().tx_info.unbox(); assert_tx_info(tx_info, expected_tx_info); stop_cheat_transaction_hash(test_address()); let tx_info = get_execution_info_v2_syscall().unwrap_syscall().unbox().tx_info.unbox(); assert_tx_info(tx_info, tx_info_before); } "# ), Contract::from_code_path( "CheatTxInfoChecker".to_string(), Path::new("tests/data/contracts/cheat_tx_info_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } /// Verify that `block_number`, `block_timestamp`, `sequencer_address` and `transaction_hash` cheats /// applied to `test_address()` are visible inside library calls made directly from test code. #[test] #[allow(clippy::too_many_lines)] fn cheat_execution_info_direct_library_call_from_test() { let test = test_case!( indoc!( r#" use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, test_address, start_cheat_block_number, stop_cheat_block_number, start_cheat_block_timestamp, stop_cheat_block_timestamp, start_cheat_sequencer_address, stop_cheat_sequencer_address, start_cheat_transaction_hash, stop_cheat_transaction_hash, }; #[starknet::interface] trait ICheatBlockNumberChecker { fn get_block_number(ref self: TContractState) -> u64; } #[starknet::interface] trait ICheatBlockTimestampChecker { fn get_block_timestamp(ref self: TContractState) -> u64; } #[starknet::interface] trait ICheatSequencerAddressChecker { fn get_sequencer_address(ref self: TContractState) -> ContractAddress; } #[starknet::interface] trait ICheatTxInfoChecker { fn get_tx_hash(self: @TContractState) -> felt252; } #[test] fn test_cheat_block_number_direct_library_call_from_test() { let class_hash = *declare("CheatBlockNumberChecker").unwrap().contract_class().class_hash; let lib_dispatcher = ICheatBlockNumberCheckerLibraryDispatcher { class_hash }; let original = lib_dispatcher.get_block_number(); start_cheat_block_number(test_address(), 1234_u64); let cheated = lib_dispatcher.get_block_number(); assert(cheated == 1234, 'Wrong block number'); stop_cheat_block_number(test_address()); let restored = lib_dispatcher.get_block_number(); assert(restored == original, 'Block number not restored'); } #[test] fn test_cheat_block_timestamp_direct_library_call_from_test() { let class_hash = *declare("CheatBlockTimestampChecker").unwrap().contract_class().class_hash; let lib_dispatcher = ICheatBlockTimestampCheckerLibraryDispatcher { class_hash }; let original = lib_dispatcher.get_block_timestamp(); start_cheat_block_timestamp(test_address(), 9999_u64); let cheated = lib_dispatcher.get_block_timestamp(); assert(cheated == 9999, 'Wrong block timestamp'); stop_cheat_block_timestamp(test_address()); let restored = lib_dispatcher.get_block_timestamp(); assert(restored == original, 'Block timestamp not restored'); } #[test] fn test_cheat_sequencer_address_direct_library_call_from_test() { let class_hash = *declare("CheatSequencerAddressChecker").unwrap().contract_class().class_hash; let lib_dispatcher = ICheatSequencerAddressCheckerLibraryDispatcher { class_hash }; let original = lib_dispatcher.get_sequencer_address(); let target_sequencer: ContractAddress = 42.try_into().unwrap(); start_cheat_sequencer_address(test_address(), target_sequencer); let cheated = lib_dispatcher.get_sequencer_address(); assert(cheated == target_sequencer, 'Wrong sequencer address'); stop_cheat_sequencer_address(test_address()); let restored = lib_dispatcher.get_sequencer_address(); assert(restored == original, 'Sequencer address not restored'); } #[test] fn test_cheat_transaction_hash_direct_library_call_from_test() { let class_hash = *declare("CheatTxInfoChecker").unwrap().contract_class().class_hash; let lib_dispatcher = ICheatTxInfoCheckerLibraryDispatcher { class_hash }; let original = lib_dispatcher.get_tx_hash(); start_cheat_transaction_hash(test_address(), 0xdeadbeef); let cheated = lib_dispatcher.get_tx_hash(); assert(cheated == 0xdeadbeef, 'Wrong tx hash'); stop_cheat_transaction_hash(test_address()); let restored = lib_dispatcher.get_tx_hash(); assert(restored == original, 'Tx hash not restored'); } "# ), Contract::from_code_path( "CheatBlockNumberChecker".to_string(), Path::new("tests/data/contracts/cheat_block_number_checker.cairo"), ) .unwrap(), Contract::from_code_path( "CheatBlockTimestampChecker".to_string(), Path::new("tests/data/contracts/cheat_block_timestamp_checker.cairo"), ) .unwrap(), Contract::from_code_path( "CheatSequencerAddressChecker".to_string(), Path::new("tests/data/contracts/cheat_sequencer_address_checker.cairo"), ) .unwrap(), Contract::from_code_path( "CheatTxInfoChecker".to_string(), Path::new("tests/data/contracts/cheat_tx_info_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); } /// Verify that cheats applied to a contract address are visible when that contract internally /// makes library calls. Tests `block_number`, `block_timestamp`, `sequencer_address` and `tx_hash`. #[test] #[allow(clippy::too_many_lines)] fn cheat_execution_info_library_call_from_contract() { let test = test_case!( indoc!( r#" use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, test_address, start_cheat_block_number, stop_cheat_block_number, start_cheat_block_timestamp, stop_cheat_block_timestamp, start_cheat_sequencer_address, stop_cheat_sequencer_address, start_cheat_transaction_hash, stop_cheat_transaction_hash, }; #[starknet::interface] trait ICheatExecutionInfoLibraryCallChecker { fn get_block_number_via_library_call( ref self: TContractState, class_hash: starknet::ClassHash, ) -> u64; fn get_block_timestamp_via_library_call( ref self: TContractState, class_hash: starknet::ClassHash, ) -> u64; fn get_sequencer_address_via_library_call( ref self: TContractState, class_hash: starknet::ClassHash, ) -> ContractAddress; fn get_tx_hash_via_library_call( self: @TContractState, class_hash: starknet::ClassHash, ) -> felt252; } fn deploy_proxy() -> (ICheatExecutionInfoLibraryCallCheckerDispatcher, ContractAddress) { let proxy = declare("CheatExecutionInfoLibraryCallChecker").unwrap().contract_class(); let (proxy_address, _) = proxy.deploy(@array![]).unwrap(); (ICheatExecutionInfoLibraryCallCheckerDispatcher { contract_address: proxy_address }, proxy_address) } #[test] fn test_cheat_block_number_library_call_from_contract() { let block_number_class_hash = *declare("CheatBlockNumberChecker").unwrap().contract_class().class_hash; let (proxy, proxy_address) = deploy_proxy(); let original = proxy.get_block_number_via_library_call(block_number_class_hash); start_cheat_block_number(proxy_address, 5678_u64); let cheated = proxy.get_block_number_via_library_call(block_number_class_hash); assert(cheated == 5678, 'Wrong block number'); stop_cheat_block_number(proxy_address); let restored = proxy.get_block_number_via_library_call(block_number_class_hash); assert(restored == original, 'Block number not restored'); } #[test] fn test_cheat_block_timestamp_library_call_from_contract() { let class_hash = *declare("CheatBlockTimestampChecker").unwrap().contract_class().class_hash; let (proxy, proxy_address) = deploy_proxy(); let original = proxy.get_block_timestamp_via_library_call(class_hash); start_cheat_block_timestamp(proxy_address, 7777_u64); let cheated = proxy.get_block_timestamp_via_library_call(class_hash); assert(cheated == 7777, 'Wrong block timestamp'); stop_cheat_block_timestamp(proxy_address); let restored = proxy.get_block_timestamp_via_library_call(class_hash); assert(restored == original, 'Block timestamp not restored'); } #[test] fn test_cheat_sequencer_address_library_call_from_contract() { let class_hash = *declare("CheatSequencerAddressChecker").unwrap().contract_class().class_hash; let (proxy, proxy_address) = deploy_proxy(); let original = proxy.get_sequencer_address_via_library_call(class_hash); let target_sequencer: ContractAddress = 99.try_into().unwrap(); start_cheat_sequencer_address(proxy_address, target_sequencer); let cheated = proxy.get_sequencer_address_via_library_call(class_hash); assert(cheated == target_sequencer, 'Wrong sequencer address'); stop_cheat_sequencer_address(proxy_address); let restored = proxy.get_sequencer_address_via_library_call(class_hash); assert(restored == original, 'Sequencer address not restored'); } #[test] fn test_cheat_transaction_hash_library_call_from_contract() { let class_hash = *declare("CheatTxInfoChecker").unwrap().contract_class().class_hash; let (proxy, proxy_address) = deploy_proxy(); let original = proxy.get_tx_hash_via_library_call(class_hash); start_cheat_transaction_hash(proxy_address, 0xcafebabe); let cheated = proxy.get_tx_hash_via_library_call(class_hash); assert(cheated == 0xcafebabe, 'Wrong tx hash'); stop_cheat_transaction_hash(proxy_address); let restored = proxy.get_tx_hash_via_library_call(class_hash); assert(restored == original, 'Tx hash not restored'); } "# ), Contract::from_code_path( "CheatBlockNumberChecker".to_string(), Path::new("tests/data/contracts/cheat_block_number_checker.cairo"), ) .unwrap(), Contract::from_code_path( "CheatBlockTimestampChecker".to_string(), Path::new("tests/data/contracts/cheat_block_timestamp_checker.cairo"), ) .unwrap(), Contract::from_code_path( "CheatSequencerAddressChecker".to_string(), Path::new("tests/data/contracts/cheat_sequencer_address_checker.cairo"), ) .unwrap(), Contract::from_code_path( "CheatTxInfoChecker".to_string(), Path::new("tests/data/contracts/cheat_tx_info_checker.cairo"), ) .unwrap(), Contract::from_code_path( "CheatExecutionInfoLibraryCallChecker".to_string(), Path::new("tests/data/contracts/cheat_tx_info_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/cheat_fork.rs ================================================ use crate::utils::runner::assert_passed; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::formatdoc; use shared::test_utils::node_url::node_rpc_url; #[test] fn cheat_caller_address_cairo0_contract() { let test = test_case!(formatdoc!( r#" use starknet::{{class_hash::Felt252TryIntoClassHash, SyscallResultTrait}}; use snforge_std::{{start_cheat_caller_address, stop_cheat_caller_address, test_address}}; const CAIRO0_CLASS_HASH: felt252 = 0x029c0caff0aef71bd089d58b25bcc5c23458d080b2d1b75e423de86f95176818; const LIB_CALL_SELECTOR: felt252 = 219972792400094465318120350250971259539342451068659710037080072200128459645; #[test] #[fork(url: "{}", block_number: 54060)] fn cheat_caller_address_cairo0_contract() {{ let caller = starknet::library_call_syscall( CAIRO0_CLASS_HASH.try_into().unwrap(), LIB_CALL_SELECTOR, array![].span(), ).unwrap_syscall()[0]; start_cheat_caller_address(test_address(), 123.try_into().unwrap()); let cheated_caller_address = starknet::library_call_syscall( CAIRO0_CLASS_HASH.try_into().unwrap(), LIB_CALL_SELECTOR, array![].span(), ).unwrap_syscall()[0]; stop_cheat_caller_address(test_address()); let uncheated_caller_address = starknet::library_call_syscall( CAIRO0_CLASS_HASH.try_into().unwrap(), LIB_CALL_SELECTOR, array![].span(), ).unwrap_syscall()[0]; assert(*cheated_caller_address == 123, 'does not work'); assert(uncheated_caller_address == caller, 'does not work'); }} "#, node_rpc_url(), ) .as_str()); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn cheat_block_number_cairo0_contract() { let test = test_case!(formatdoc!( r#" use starknet::{{class_hash::Felt252TryIntoClassHash, SyscallResultTrait}}; use snforge_std::{{start_cheat_block_number, stop_cheat_block_number, test_address}}; const CAIRO0_CLASS_HASH: felt252 = 0x029c0caff0aef71bd089d58b25bcc5c23458d080b2d1b75e423de86f95176818; const LIB_CALL_SELECTOR: felt252 = 1043360521069001059812816533306435120284814797591254795559962622467917544215; #[test] #[fork(url: "{}", block_number: 54060)] fn cheat_block_number_cairo0_contract() {{ let block_number = starknet::library_call_syscall( CAIRO0_CLASS_HASH.try_into().unwrap(), LIB_CALL_SELECTOR, array![].span(), ).unwrap_syscall()[0]; start_cheat_block_number(test_address(), 123); let cheated_block_number = starknet::library_call_syscall( CAIRO0_CLASS_HASH.try_into().unwrap(), LIB_CALL_SELECTOR, array![].span(), ).unwrap_syscall()[0]; stop_cheat_block_number(test_address()); let uncheated_block_number = starknet::library_call_syscall( CAIRO0_CLASS_HASH.try_into().unwrap(), LIB_CALL_SELECTOR, array![].span(), ).unwrap_syscall()[0]; assert(*cheated_block_number == 123, 'does not work'); assert(uncheated_block_number == block_number, 'does not work'); }} "#, node_rpc_url(), ) .as_str()); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn cheat_block_timestamp_cairo0_contract() { let test = test_case!(formatdoc!( r#" use starknet::{{class_hash::Felt252TryIntoClassHash, SyscallResultTrait}}; use snforge_std::{{start_cheat_block_timestamp, stop_cheat_block_timestamp, test_address}}; const CAIRO0_CLASS_HASH: felt252 = 0x029c0caff0aef71bd089d58b25bcc5c23458d080b2d1b75e423de86f95176818; const LIB_CALL_SELECTOR: felt252 = 1104673410415683966349700971986586038248888383055081852378797598061780438342; #[test] #[fork(url: "{}", block_number: 54060)] fn cheat_block_timestamp_cairo0_contract() {{ let block_timestamp = starknet::library_call_syscall( CAIRO0_CLASS_HASH.try_into().unwrap(), LIB_CALL_SELECTOR, array![].span(), ).unwrap_syscall()[0]; start_cheat_block_timestamp( test_address(), 123 ); let cheated_block_timestamp = starknet::library_call_syscall( CAIRO0_CLASS_HASH.try_into().unwrap(), LIB_CALL_SELECTOR, array![].span(), ).unwrap_syscall()[0]; stop_cheat_block_timestamp(test_address()); let uncheated_block_timestamp = starknet::library_call_syscall( CAIRO0_CLASS_HASH.try_into().unwrap(), LIB_CALL_SELECTOR, array![].span(), ).unwrap_syscall()[0]; assert(*cheated_block_timestamp == 123, 'does not work'); assert(uncheated_block_timestamp == block_timestamp, 'does not work'); }} "#, node_rpc_url(), ) .as_str()); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn mock_call_cairo0_contract() { let test = test_case!( formatdoc!( r#" use starknet::{{contract_address_const}}; use snforge_std::{{start_mock_call, stop_mock_call}}; #[starknet::interface] trait IERC20 {{ fn name(self: @TContractState) -> felt252; }} #[test] #[fork(url: "{}", block_number: 54060)] fn mock_call_cairo0_contract() {{ let eth_dispatcher = IERC20Dispatcher {{ contract_address: contract_address_const::< 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 >() }}; assert(eth_dispatcher.name() == 'Ether', 'invalid name'); start_mock_call(eth_dispatcher.contract_address, selector!("name"), 'NotEther'); assert(eth_dispatcher.name() == 'NotEther', 'invalid mocked name'); stop_mock_call(eth_dispatcher.contract_address, selector!("name")); assert(eth_dispatcher.name() == 'Ether', 'invalid name after mock'); }} "#, node_rpc_url(), ) .as_str() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn store_load_cairo0_contract() { let test = test_case!(formatdoc!( r#" use starknet::{{contract_address_const}}; use snforge_std::{{store, load}}; #[starknet::interface] trait IERC20 {{ fn name(self: @TContractState) -> felt252; }} #[test] #[fork(url: "{}", block_number: 54060)] fn mock_call_cairo0_contract() {{ let eth_dispatcher = IERC20Dispatcher {{ contract_address: contract_address_const::< 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 >() }}; assert(eth_dispatcher.name() == 'Ether', 'invalid name'); let name = load(eth_dispatcher.contract_address, selector!("ERC20_name"), 1); assert(name == array!['Ether'], 'invalid load value'); store(eth_dispatcher.contract_address, selector!("ERC20_name"), array!['NotEther'].span()); assert(eth_dispatcher.name() == 'NotEther', 'invalid store name'); let name = load(eth_dispatcher.contract_address, selector!("ERC20_name"), 1); assert(name == array!['NotEther'], 'invalid load2 name'); }} "#, node_rpc_url(), ) .as_str()); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/cheat_sequencer_address.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn cheat_sequencer_address_basic() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_sequencer_address, start_cheat_sequencer_address_global, stop_cheat_sequencer_address_global, stop_cheat_sequencer_address }; #[starknet::interface] trait ICheatSequencerAddressChecker { fn get_sequencer_address(ref self: TContractState) -> ContractAddress; } #[test] fn test_stop_cheat_sequencer_address() { let contract = declare("CheatSequencerAddressChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ICheatSequencerAddressCheckerDispatcher { contract_address }; let old_sequencer_address = dispatcher.get_sequencer_address(); start_cheat_sequencer_address(contract_address, 234.try_into().unwrap()); let new_sequencer_address = dispatcher.get_sequencer_address(); assert(new_sequencer_address == 234.try_into().unwrap(), 'Wrong sequencer address'); stop_cheat_sequencer_address(contract_address); let new_sequencer_address = dispatcher.get_sequencer_address(); assert(new_sequencer_address == old_sequencer_address, 'Sequencer addr did not revert'); } #[test] fn test_cheat_sequencer_address_multiple() { let contract = declare("CheatSequencerAddressChecker").unwrap().contract_class(); let (contract_address1, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let (contract_address2, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let cheat_sequencer_address_checker1 = ICheatSequencerAddressCheckerDispatcher { contract_address: contract_address1 }; let cheat_sequencer_address_checker2 = ICheatSequencerAddressCheckerDispatcher { contract_address: contract_address2 }; let old_seq_addr1 = cheat_sequencer_address_checker1.get_sequencer_address(); let old_seq_addr2 = cheat_sequencer_address_checker2.get_sequencer_address(); start_cheat_sequencer_address(cheat_sequencer_address_checker1.contract_address, 123.try_into().unwrap()); start_cheat_sequencer_address(cheat_sequencer_address_checker2.contract_address, 123.try_into().unwrap()); let new_seq_addr1 = cheat_sequencer_address_checker1.get_sequencer_address(); let new_seq_addr2 = cheat_sequencer_address_checker2.get_sequencer_address(); assert(new_seq_addr1 == 123.try_into().unwrap(), 'Wrong seq addr #1'); assert(new_seq_addr2 == 123.try_into().unwrap(), 'Wrong seq addr #2'); stop_cheat_sequencer_address(cheat_sequencer_address_checker1.contract_address); stop_cheat_sequencer_address(cheat_sequencer_address_checker2.contract_address); let new_seq_addr1 = cheat_sequencer_address_checker1.get_sequencer_address(); let new_seq_addr2 = cheat_sequencer_address_checker2.get_sequencer_address(); assert(new_seq_addr1 == old_seq_addr1, 'not stopped #1'); assert(new_seq_addr2 == old_seq_addr2, 'not stopped #2'); } #[test] fn test_cheat_sequencer_address_all() { let contract = declare("CheatSequencerAddressChecker").unwrap().contract_class(); let (contract_address1, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let (contract_address2, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let cheat_sequencer_address_checker1 = ICheatSequencerAddressCheckerDispatcher { contract_address: contract_address1 }; let cheat_sequencer_address_checker2 = ICheatSequencerAddressCheckerDispatcher { contract_address: contract_address2 }; let old_seq_addr1 = cheat_sequencer_address_checker1.get_sequencer_address(); let old_seq_addr2 = cheat_sequencer_address_checker2.get_sequencer_address(); start_cheat_sequencer_address_global(123.try_into().unwrap()); let new_seq_addr1 = cheat_sequencer_address_checker1.get_sequencer_address(); let new_seq_addr2 = cheat_sequencer_address_checker2.get_sequencer_address(); assert(new_seq_addr1 == 123.try_into().unwrap(), 'Wrong seq addr #1'); assert(new_seq_addr2 == 123.try_into().unwrap(), 'Wrong seq addr #2'); stop_cheat_sequencer_address_global(); let new_seq_addr1 = cheat_sequencer_address_checker1.get_sequencer_address(); let new_seq_addr2 = cheat_sequencer_address_checker2.get_sequencer_address(); assert(new_seq_addr1 == old_seq_addr1, 'Wrong seq addr #1'); assert(new_seq_addr2 == old_seq_addr2, 'Wrong seq addr #2'); } #[test] fn test_cheat_sequencer_address_all_stop_one() { let contract = declare("CheatSequencerAddressChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ICheatSequencerAddressCheckerDispatcher { contract_address }; let target_seq_addr: felt252 = 123; let target_seq_addr: ContractAddress = target_seq_addr.try_into().unwrap(); let old_seq_addr = dispatcher.get_sequencer_address(); start_cheat_sequencer_address_global(target_seq_addr); let new_seq_addr = dispatcher.get_sequencer_address(); assert(new_seq_addr == 123.try_into().unwrap(), 'Wrong seq addr'); stop_cheat_sequencer_address(contract_address); let new_seq_addr = dispatcher.get_sequencer_address(); assert(old_seq_addr == new_seq_addr, 'Address did not change back'); } "# ), Contract::from_code_path( "CheatSequencerAddressChecker".to_string(), Path::new("tests/data/contracts/cheat_sequencer_address_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn cheat_sequencer_address_complex() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_sequencer_address, start_cheat_sequencer_address_global, stop_cheat_sequencer_address }; #[starknet::interface] trait ICheatSequencerAddressChecker { fn get_sequencer_address(ref self: TContractState) -> ContractAddress; } #[test] fn test_cheat_sequencer_address_complex() { let contract = declare("CheatSequencerAddressChecker").unwrap().contract_class(); let (contract_address1, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let (contract_address2, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let cheat_sequencer_address_checker1 = ICheatSequencerAddressCheckerDispatcher { contract_address: contract_address1 }; let cheat_sequencer_address_checker2 = ICheatSequencerAddressCheckerDispatcher { contract_address: contract_address2 }; let old_seq_addr2 = cheat_sequencer_address_checker2.get_sequencer_address(); start_cheat_sequencer_address_global(123.try_into().unwrap()); let new_seq_addr1 = cheat_sequencer_address_checker1.get_sequencer_address(); let new_seq_addr2 = cheat_sequencer_address_checker2.get_sequencer_address(); assert(new_seq_addr1 == 123.try_into().unwrap(), 'Wrong seq addr #1'); assert(new_seq_addr2 == 123.try_into().unwrap(), 'Wrong seq addr #2'); start_cheat_sequencer_address(cheat_sequencer_address_checker1.contract_address, 456.try_into().unwrap()); let new_seq_addr1 = cheat_sequencer_address_checker1.get_sequencer_address(); let new_seq_addr2 = cheat_sequencer_address_checker2.get_sequencer_address(); assert(new_seq_addr1 == 456.try_into().unwrap(), 'Wrong seq addr #3'); assert(new_seq_addr2 == 123.try_into().unwrap(), 'Wrong seq addr #4'); start_cheat_sequencer_address(cheat_sequencer_address_checker1.contract_address, 789.try_into().unwrap()); start_cheat_sequencer_address(cheat_sequencer_address_checker2.contract_address, 789.try_into().unwrap()); let new_seq_addr1 = cheat_sequencer_address_checker1.get_sequencer_address(); let new_seq_addr2 = cheat_sequencer_address_checker2.get_sequencer_address(); assert(new_seq_addr1 == 789.try_into().unwrap(), 'Wrong seq addr #5'); assert(new_seq_addr2 == 789.try_into().unwrap(), 'Wrong seq addr #6'); stop_cheat_sequencer_address(cheat_sequencer_address_checker2.contract_address); let new_seq_addr1 = cheat_sequencer_address_checker1.get_sequencer_address(); let new_seq_addr2 = cheat_sequencer_address_checker2.get_sequencer_address(); assert(new_seq_addr1 == 789.try_into().unwrap(), 'Wrong seq addr #7'); assert(new_seq_addr2 == old_seq_addr2, 'Wrong seq addr #8'); } "# ), Contract::from_code_path( "CheatSequencerAddressChecker".to_string(), Path::new("tests/data/contracts/cheat_sequencer_address_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn cheat_sequencer_address_with_span() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ test_address, declare, ContractClassTrait, DeclareResultTrait, cheat_sequencer_address, start_cheat_sequencer_address, stop_cheat_sequencer_address, CheatSpan }; #[starknet::interface] trait ICheatSequencerAddressChecker { fn get_sequencer_address(ref self: TContractState) -> felt252; } fn deploy_cheat_sequencer_address_checker() -> ICheatSequencerAddressCheckerDispatcher { let (contract_address, _) = declare("CheatSequencerAddressChecker").unwrap().contract_class().deploy(@ArrayTrait::new()).unwrap(); ICheatSequencerAddressCheckerDispatcher { contract_address } } #[test] fn test_cheat_sequencer_address_once() { let old_sequencer_address = get_sequencer_address(); let dispatcher = deploy_cheat_sequencer_address_checker(); let target_sequencer_address: ContractAddress = 123.try_into().unwrap(); cheat_sequencer_address(dispatcher.contract_address, target_sequencer_address, CheatSpan::TargetCalls(1)); let sequencer_address = dispatcher.get_sequencer_address(); assert(sequencer_address == target_sequencer_address.into(), 'Wrong sequencer address'); let sequencer_address = dispatcher.get_sequencer_address(); assert(sequencer_address == old_sequencer_address.into(), 'Address did not change back'); } #[test] fn test_cheat_sequencer_address_twice() { let old_sequencer_address = get_sequencer_address(); let dispatcher = deploy_cheat_sequencer_address_checker(); let target_sequencer_address: ContractAddress = 123.try_into().unwrap(); cheat_sequencer_address(dispatcher.contract_address, target_sequencer_address, CheatSpan::TargetCalls(2)); let sequencer_address = dispatcher.get_sequencer_address(); assert(sequencer_address == target_sequencer_address.into(), 'Wrong sequencer address'); let sequencer_address = dispatcher.get_sequencer_address(); assert(sequencer_address == target_sequencer_address.into(), 'Wrong sequencer address'); let sequencer_address = dispatcher.get_sequencer_address(); assert(sequencer_address == old_sequencer_address.into(), 'Address did not change back'); } #[test] fn test_cheat_sequencer_address_test_address() { let old_sequencer_address = get_sequencer_address(); let target_sequencer_address: ContractAddress = 123.try_into().unwrap(); cheat_sequencer_address(test_address(), target_sequencer_address, CheatSpan::TargetCalls(1)); let sequencer_address = get_sequencer_address(); assert(sequencer_address == target_sequencer_address, 'Wrong sequencer address'); let sequencer_address = get_sequencer_address(); assert(sequencer_address == target_sequencer_address, 'Wrong sequencer address'); stop_cheat_sequencer_address(test_address()); let sequencer_address = get_sequencer_address(); assert(sequencer_address == old_sequencer_address, 'Wrong sequencer address'); } fn get_sequencer_address() -> ContractAddress { starknet::get_block_info().unbox().sequencer_address } "# ), Contract::from_code_path( "CheatSequencerAddressChecker".to_string(), Path::new("tests/data/contracts/cheat_sequencer_address_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/config.rs ================================================ use crate::e2e::common::runner::setup_package; use assert_fs::TempDir; use assert_fs::fixture::{FileWriteStr, PathChild}; use cheatnet::runtime_extensions::forge_config_extension::config::BlockId; use forge::scarb::config::{ForgeConfigFromScarb, ForkTarget}; use forge::scarb::load_package_config; use forge_runner::forge_config::ForgeTrackedResource; use indoc::{formatdoc, indoc}; use scarb_api::metadata::metadata_for_dir; use scarb_metadata::PackageId; use std::{env, fs}; fn setup_package_with_toml() -> TempDir { let temp = setup_package("simple_package"); let manifest_path = temp.child("Scarb.toml"); let manifest_contents = fs::read_to_string(&manifest_path).unwrap(); let manifest_contents = formatdoc!( r#" {manifest_contents} [[tool.snforge.fork]] name = "FIRST_FORK_NAME" url = "http://some.rpc.url" block_id.number = "1" [[tool.snforge.fork]] name = "SECOND_FORK_NAME" url = "http://some.rpc.url" block_id.hash = "0xa" [[tool.snforge.fork]] name = "THIRD_FORK_NAME" url = "http://some.rpc.url" block_id.hash = "10" [[tool.snforge.fork]] name = "FOURTH_FORK_NAME" url = "http://some.rpc.url" block_id.tag = "latest" "# ); manifest_path.write_str(manifest_contents.as_str()).unwrap(); temp } #[test] fn get_forge_config_for_package() { let temp = setup_package_with_toml(); let scarb_metadata = metadata_for_dir(temp.path()).unwrap(); let config = load_package_config::( &scarb_metadata, &scarb_metadata.workspace.members[0], ) .unwrap(); assert_eq!( config, ForgeConfigFromScarb { exit_first: false, fork: vec![ ForkTarget { name: "FIRST_FORK_NAME".to_string(), url: "http://some.rpc.url".parse().expect("Should be valid url"), block_id: BlockId::BlockNumber(1), }, ForkTarget { name: "SECOND_FORK_NAME".to_string(), url: "http://some.rpc.url".parse().expect("Should be valid url"), block_id: BlockId::BlockHash(0xa.into()), }, ForkTarget { name: "THIRD_FORK_NAME".to_string(), url: "http://some.rpc.url".parse().expect("Should be valid url"), block_id: BlockId::BlockHash(10.into()), }, ForkTarget { name: "FOURTH_FORK_NAME".to_string(), url: "http://some.rpc.url".parse().expect("Should be valid url"), block_id: BlockId::BlockTag, }, ], fuzzer_runs: None, fuzzer_seed: None, max_n_steps: None, tracked_resource: ForgeTrackedResource::SierraGas, detailed_resources: false, save_trace_data: false, build_profile: false, coverage: false, gas_report: false, } ); } #[test] fn get_forge_config_for_package_err_on_invalid_package() { let temp = setup_package_with_toml(); let scarb_metadata = metadata_for_dir(temp.path()).unwrap(); let result = load_package_config::( &scarb_metadata, &PackageId::from(String::from("12345679")), ); let err = result.unwrap_err(); assert!( err.to_string() .contains("Failed to find metadata for package") ); } #[test] fn get_forge_config_for_package_default_on_missing_config() { let temp = setup_package_with_toml(); let content = indoc!( r#" [package] name = "simple_package" version = "0.1.0" "# ); temp.child("Scarb.toml").write_str(content).unwrap(); let scarb_metadata = metadata_for_dir(temp.path()).unwrap(); let config = load_package_config::( &scarb_metadata, &scarb_metadata.workspace.members[0], ) .unwrap(); assert_eq!(config, ForgeConfigFromScarb::default()); } #[test] fn get_forge_config_for_package_fails_on_same_fork_name() { let temp = setup_package_with_toml(); let content = indoc!( r#" [package] name = "simple_package" version = "0.1.0" [[tool.snforge.fork]] name = "SAME_NAME" url = "http://some.rpc.url" block_id.number = "1" [[tool.snforge.fork]] name = "SAME_NAME" url = "http://some.rpc.url" block_id.hash = "1" "# ); temp.child("Scarb.toml").write_str(content).unwrap(); let scarb_metadata = metadata_for_dir(temp.path()).unwrap(); let err = load_package_config::( &scarb_metadata, &scarb_metadata.workspace.members[0], ) .unwrap_err(); assert!(format!("{err:?}").contains("Some fork names are duplicated")); } #[test] fn get_forge_config_for_package_fails_on_multiple_block_id() { let temp = setup_package_with_toml(); let content = indoc!( r#" [package] name = "simple_package" version = "0.1.0" [[tool.snforge.fork]] name = "SAME_NAME" url = "http://some.rpc.url" block_id.number = "1" block_id.hash = "2" "# ); temp.child("Scarb.toml").write_str(content).unwrap(); let scarb_metadata = metadata_for_dir(temp.path()).unwrap(); let err = load_package_config::( &scarb_metadata, &scarb_metadata.workspace.members[0], ) .unwrap_err(); assert!( format!("{err:?}") .contains("block_id must contain exactly one key: 'tag', 'hash', or 'number'") ); } #[test] fn get_forge_config_for_package_fails_on_wrong_block_id() { let temp = setup_package_with_toml(); let content = indoc!( r#" [package] name = "simple_package" version = "0.1.0" [[tool.snforge.fork]] name = "SAME_NAME" url = "http://some.rpc.url" block_id.wrong_variant = "1" "# ); temp.child("Scarb.toml").write_str(content).unwrap(); let scarb_metadata = metadata_for_dir(temp.path()).unwrap(); let err = load_package_config::( &scarb_metadata, &scarb_metadata.workspace.members[0], ) .unwrap_err(); assert!( format!("{err:?}") .contains("unknown field `wrong_variant`, expected one of `tag`, `hash`, `number`") ); } #[test] fn get_forge_config_for_package_fails_on_wrong_block_tag() { let temp = setup_package_with_toml(); let content = indoc!( r#" [package] name = "simple_package" version = "0.1.0" [[tool.snforge.fork]] name = "SAME_NAME" url = "http://some.rpc.url" block_id.tag = "Preconfirmed" "# ); temp.child("Scarb.toml").write_str(content).unwrap(); let scarb_metadata = metadata_for_dir(temp.path()).unwrap(); let err = load_package_config::( &scarb_metadata, &scarb_metadata.workspace.members[0], ) .unwrap_err(); assert!(format!("{err:?}").contains("block_id.tag can only be equal to latest")); } #[test] fn get_forge_config_for_package_with_block_tag() { let temp = setup_package_with_toml(); let content = indoc!( r#" [package] name = "simple_package" version = "0.1.0" [[tool.snforge.fork]] name = "SAME_NAME" url = "http://some.rpc.url" block_id.tag = "latest" "# ); temp.child("Scarb.toml").write_str(content).unwrap(); let scarb_metadata = metadata_for_dir(temp.path()).unwrap(); let forge_config = load_package_config::( &scarb_metadata, &scarb_metadata.workspace.members[0], ) .unwrap(); assert_eq!(forge_config.fork[0].block_id, BlockId::BlockTag); } #[test] fn get_forge_config_resolves_env_variables() { let temp = setup_package_with_toml(); let content = indoc!( r#" [package] name = "simple_package" version = "0.1.0" [[tool.snforge.fork]] name = "ENV_URL_FORK" url = "$ENV_URL_FORK234980670176" block_id.number = "1" "# ); temp.child("Scarb.toml").write_str(content).unwrap(); let scarb_metadata = metadata_for_dir(temp.path()).unwrap(); // SAFETY: This value is only read here and is not modified by other tests. unsafe { env::set_var("ENV_URL_FORK234980670176", "http://some.rpc.url_from_env"); } let config = load_package_config::( &scarb_metadata, &scarb_metadata.workspace.members[0], ) .unwrap(); assert_eq!( config, ForgeConfigFromScarb { exit_first: false, fork: vec![ForkTarget { name: "ENV_URL_FORK".to_string(), url: "http://some.rpc.url_from_env" .parse() .expect("Should be valid url"), block_id: BlockId::BlockNumber(1), }], fuzzer_runs: None, fuzzer_seed: None, max_n_steps: None, tracked_resource: ForgeTrackedResource::SierraGas, detailed_resources: false, save_trace_data: false, build_profile: false, coverage: false, gas_report: false, } ); } ================================================ FILE: crates/forge/tests/integration/declare.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn simple_declare() { let test = test_case!( indoc!( r#" use core::clone::Clone; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use result::ResultTrait; use traits::Into; use starknet::ClassHashIntoFelt252; use snforge_std::declare; #[test] fn simple_declare() { assert(1 == 1, 'simple check'); let contract = declare("HelloStarknet").unwrap().contract_class().clone(); assert(contract.class_hash.into() != 0, 'proper class hash'); } "# ), Contract::new( "HelloStarknet", indoc!( r" #[starknet::interface] trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn decrease_balance(ref self: TContractState, amount: felt252); } #[starknet::contract] mod HelloStarknet { #[storage] struct Storage { balance: felt252, } // Increases the balance by the given amount #[abi(embed_v0)] impl HelloStarknetImpl of super::IHelloStarknet { fn increase_balance(ref self: ContractState, amount: felt252) { self.balance.write(self.balance.read() + amount); } // Decreases the balance by the given amount. fn decrease_balance(ref self: ContractState, amount: felt252) { self.balance.write(self.balance.read() - amount); } } } " ) ) ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn declare_simple() { let contract = Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet.cairo"), ) .unwrap(); let test = test_case!( indoc!( r#" use core::clone::Clone; use result::ResultTrait; use traits::Into; use starknet::ClassHashIntoFelt252; use snforge_std::{declare, DeclareResultTrait}; #[test] fn declare_simple() { assert(1 == 1, 'simple check'); let contract = declare("HelloStarknet").unwrap().contract_class().clone(); let class_hash = contract.class_hash.into(); assert(class_hash != 0, 'proper class hash'); } "# ), contract ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn redeclare() { let contract = Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet.cairo"), ) .unwrap(); let test = test_case!( indoc!( r#" use core::clone::Clone; use result::ResultTrait; use traits::Into; use starknet::ClassHashIntoFelt252; use snforge_std::{declare, DeclareResultTrait, DeclareResult}; #[test] fn redeclare() { assert(1 == 1, 'simple check'); let contract = match declare("HelloStarknet") { Result::Ok(result) => result.contract_class().clone(), Result::Err(_) => panic!("Failed to declare contract"), }; let class_hash = contract.class_hash.into(); assert(class_hash != 0, 'proper class hash'); match declare("HelloStarknet").unwrap() { DeclareResult::AlreadyDeclared(_) => {}, _ => panic!("Contract redeclared") } } "# ), contract ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/deploy.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn deploy_syscall_check() { let test = test_case!( indoc!( r#" use core::clone::Clone; use snforge_std::{declare, test_address, DeclareResultTrait}; use starknet::{SyscallResult, deploy_syscall}; #[starknet::interface] trait IDeployChecker { fn get_balance(self: @T) -> felt252; fn get_caller(self: @T) -> starknet::ContractAddress; } #[test] fn deploy_syscall_check() { let contract = declare("DeployChecker").unwrap().contract_class().clone(); let salt = 1; let calldata = array![10]; let (contract_address, span) = deploy_syscall(contract.class_hash, salt, calldata.span(), false).unwrap(); assert(*span[0] == test_address().into() && *span[1] == 10, 'constructor return mismatch'); let dispatcher = IDeployCheckerDispatcher { contract_address }; assert(dispatcher.get_balance() == 10, 'balance mismatch'); assert(dispatcher.get_caller() == test_address(), 'caller mismatch'); let (contract_address_from_zero, _) = deploy_syscall(contract.class_hash, salt, calldata.span(), true).unwrap(); assert(contract_address != contract_address_from_zero, 'deploy from zero no effect'); } "# ), Contract::from_code_path( "DeployChecker".to_string(), Path::new("tests/data/contracts/deploy_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn constructor_retdata_span() { let test = test_case!( indoc!( r#" use result::ResultTrait; use snforge_std::{ declare, ContractClass, ContractClassTrait, DeclareResultTrait }; use array::ArrayTrait; #[test] fn constructor_retdata_span() { let contract = declare("ConstructorRetdata").unwrap().contract_class(); let (_contract_address, retdata) = contract.deploy(@ArrayTrait::new()).unwrap(); assert_eq!(retdata, array![3, 2, 3, 4].span()); } "# ), Contract::new( "ConstructorRetdata", indoc!( r" #[starknet::contract] mod ConstructorRetdata { use array::ArrayTrait; #[storage] struct Storage {} #[constructor] fn constructor(ref self: ContractState) -> Span { array![2, 3, 4].span() } } " ) ) ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn constructor_retdata_felt() { let test = test_case!( indoc!( r#" use result::ResultTrait; use snforge_std::{ declare, ContractClass, ContractClassTrait, DeclareResultTrait }; use array::ArrayTrait; #[test] fn constructor_retdata_felt() { let contract = declare("ConstructorRetdata").unwrap().contract_class(); let (_contract_address, retdata) = contract.deploy(@ArrayTrait::new()).unwrap(); assert_eq!(retdata, array![5].span()); } "# ), Contract::new( "ConstructorRetdata", indoc!( r" #[starknet::contract] mod ConstructorRetdata { use array::ArrayTrait; #[storage] struct Storage {} #[constructor] fn constructor(ref self: ContractState) -> felt252 { 5 } } " ) ) ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn constructor_retdata_struct() { let test = test_case!( indoc!( r#" use result::ResultTrait; use snforge_std::{ declare, ContractClass, ContractClassTrait, DeclareResultTrait }; use array::ArrayTrait; #[test] fn constructor_retdata_struct() { let contract = declare("ConstructorRetdata").unwrap().contract_class(); let (_contract_address, retdata) = contract.deploy(@ArrayTrait::new()).unwrap(); assert_eq!(retdata, array![0, 6, 2, 7, 8, 9].span()); } "# ), Contract::new( "ConstructorRetdata", indoc!( r" #[starknet::contract] mod ConstructorRetdata { use array::ArrayTrait; #[storage] struct Storage {} #[derive(Serde, Drop)] struct Retdata { a: felt252, b: Span, c: felt252, } #[constructor] fn constructor(ref self: ContractState) -> Option { Option::Some( Retdata { a: 6, b: array![7, 8].span(), c: 9 } ) } } " ) ) ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn deploy_twice() { let test = test_case!( indoc!( r#" use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; #[test] fn deploy_twice() { let contract = declare("DeployChecker").unwrap().contract_class(); let constructor_calldata = array![1]; let (contract_address_1, _) = contract.deploy(@constructor_calldata).unwrap(); let (contract_address_2, _) = contract.deploy(@constructor_calldata).unwrap(); assert(contract_address_1 != contract_address_2, 'Addresses should differ'); } "# ), Contract::from_code_path( "DeployChecker".to_string(), Path::new("tests/data/contracts/deploy_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn verify_precalculate_address() { let test = test_case!( indoc!( r#" use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; #[test] fn precalculate_and_deploy() { let contract = declare("DeployChecker").unwrap().contract_class(); let constructor_calldata = array![1234]; let precalculated_address = contract.precalculate_address(@constructor_calldata); let (contract_address, _) = contract.deploy(@constructor_calldata).unwrap(); assert(precalculated_address == contract_address, 'Addresses should not differ'); } "# ), Contract::from_code_path( "DeployChecker".to_string(), Path::new("tests/data/contracts/deploy_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn deploy_constructor_panic_catchable() { let test = test_case!( indoc!( r#" use snforge_std::{declare, ContractClassTrait, DeclareResultTrait}; #[test] fn deploy_constructor_panic_returns_error_in_result() { let contract = declare("DeployChecker").unwrap().contract_class(); let constructor_calldata = array![0]; let result = contract.deploy(@constructor_calldata); match result { Result::Ok(_) => panic!("Expected deployment to fail"), Result::Err(panic_data) => { assert!(*panic_data.at(0) == 'Initial balance cannot be 0', "Wrong panic message"); } } } "# ), Contract::from_code_path( "DeployChecker".to_string(), Path::new("tests/data/contracts/deploy_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn deploy_constructor_panic_catchable_via_should_panic() { let test = test_case!( indoc!( r#" use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; use starknet::SyscallResultTrait; #[test] #[should_panic(expected: 'Initial balance cannot be 0')] fn deploy_constructor_panic_should_be_catchable() { let contract = declare("DeployChecker").unwrap().contract_class(); let constructor_calldata = array![0]; contract.deploy(@constructor_calldata).unwrap_syscall(); } "# ), Contract::from_code_path( "DeployChecker".to_string(), Path::new("tests/data/contracts/deploy_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/deploy_at.rs ================================================ use crate::utils::runner::{Contract, assert_case_output_contains, assert_failed, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn deploy_at_correct_address() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; use starknet::ContractAddress; #[starknet::interface] trait IProxy { fn get_caller_address(ref self: TContractState, checker_address: ContractAddress) -> felt252; } #[test] fn deploy_at_correct_address() { let contract = declare("CheatCallerAddressChecker").unwrap().contract_class(); let (cheat_caller_address_checker, _) = contract.deploy(@array![]).unwrap(); let contract = declare("Proxy").unwrap().contract_class(); let deploy_at_address = 123; let (contract_address, _) = contract.deploy_at(@array![], deploy_at_address.try_into().unwrap()).unwrap(); assert(deploy_at_address == contract_address.into(), 'addresses should be the same'); let real_address = IProxyDispatcher{ contract_address }.get_caller_address(cheat_caller_address_checker); assert(real_address == contract_address.into(), 'addresses should be the same'); } "# ), Contract::new( "Proxy", indoc!( r" use starknet::ContractAddress; #[starknet::interface] trait IProxy { fn get_caller_address(ref self: TContractState, checker_address: ContractAddress) -> felt252; } #[starknet::contract] mod Proxy { use starknet::ContractAddress; #[storage] struct Storage {} #[starknet::interface] trait ICheatCallerAddressChecker { fn get_caller_address(ref self: TContractState) -> felt252; } #[abi(embed_v0)] impl ProxyImpl of super::IProxy { fn get_caller_address(ref self: ContractState, checker_address: ContractAddress) -> felt252 { ICheatCallerAddressCheckerDispatcher{ contract_address: checker_address}.get_caller_address() } } } " ) ), Contract::from_code_path( "CheatCallerAddressChecker".to_string(), Path::new("tests/data/contracts/cheat_caller_address_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn deploy_two_at_the_same_address() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; use starknet::ContractAddress; #[test] fn deploy_two_at_the_same_address() { let contract_address = 123; let contract = declare("HelloStarknet").unwrap().contract_class(); let (real_address, _) = contract.deploy_at(@array![], contract_address.try_into().unwrap()).unwrap(); assert(real_address.into() == contract_address, 'addresses should be the same'); contract.deploy_at(@array![], contract_address.try_into().unwrap()).unwrap(); } "# ), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "deploy_two_at_the_same_address", "Deployment failed: contract already deployed at address 0x000000000000000000000000000000000000000000000000000000000000007b", ); } #[test] fn fail_to_deploy_at_0() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[test] fn deploy_at_0() { let contract = declare("HelloStarknet").unwrap().contract_class(); contract.deploy_at(@array![], 0.try_into().unwrap()).unwrap(); } "# ), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "deploy_at_0", "Cannot deploy contract at address 0", ); } #[test] fn deploy_at_constructor_panic_catchable_via_should_panic() { let test = test_case!( indoc!( r#" use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; use starknet::{SyscallResultTrait, ContractAddress}; #[test] #[should_panic(expected: 'Initial balance cannot be 0')] fn deploy_at_constructor_panic_should_be_catchable() { let contract = declare("DeployChecker").unwrap().contract_class(); let constructor_calldata = array![0]; let deploy_at_address = 123; contract.deploy_at(@constructor_calldata, deploy_at_address.try_into().unwrap()).unwrap_syscall(); } "# ), Contract::from_code_path( "DeployChecker".to_string(), Path::new("tests/data/contracts/deploy_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/dict.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn using_dict() { let test = test_case!( indoc!( r#" use result::ResultTrait; use snforge_std::{ declare, ContractClass, ContractClassTrait, DeclareResultTrait }; use array::ArrayTrait; #[starknet::interface] trait IDictUsingContract { fn get_unique(self: @TContractState) -> u8; fn write_unique(self: @TContractState, values: Array); } #[test] fn using_dict() { let contract = declare("DictUsingContract").unwrap().contract_class(); let numbers = array![1, 2, 3, 3, 3, 3 ,3, 4, 4, 4, 4, 4, 5, 5, 5, 5]; let mut inputs: Array = array![]; numbers.serialize(ref inputs); let (contract_address, _) = contract.deploy(@inputs).unwrap(); let dispatcher = IDictUsingContractDispatcher { contract_address }; let unq = dispatcher.get_unique(); assert(unq == 5, 'wrong unique count'); numbers.serialize(ref inputs); dispatcher.write_unique(array![1, 2, 3, 3, 3, 3 ,3, 4, 4, 4, 4, 4]); let unq = dispatcher.get_unique(); assert(unq == 4, 'wrong unique count'); } "# ), Contract::from_code_path( "DictUsingContract".to_string(), Path::new("tests/data/contracts/dict_using_contract.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/dispatchers.rs ================================================ use crate::utils::runner::{Contract, assert_case_output_contains, assert_failed, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn simple_call_and_invoke() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); } #[test] fn simple_call_and_invoke() { let contract = declare("HelloStarknet").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IHelloStarknetDispatcher { contract_address }; let balance = dispatcher.get_balance(); assert(balance == 0, 'balance == 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'balance == 100'); } "# ), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn advanced_types() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_caller_address }; #[starknet::interface] trait IERC20 { fn get_name(self: @TContractState) -> felt252; fn get_symbol(self: @TContractState) -> felt252; fn get_decimals(self: @TContractState) -> u8; fn get_total_supply(self: @TContractState) -> u256; fn balance_of(self: @TContractState, account: ContractAddress) -> u256; fn allowance(self: @TContractState, owner: ContractAddress, spender: ContractAddress) -> u256; fn transfer(ref self: TContractState, recipient: ContractAddress, amount: u256); fn transfer_from( ref self: TContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256 ); fn approve(ref self: TContractState, spender: ContractAddress, amount: u256); fn increase_allowance(ref self: TContractState, spender: ContractAddress, added_value: u256); fn decrease_allowance( ref self: TContractState, spender: ContractAddress, subtracted_value: u256 ); } #[test] fn advanced_types() { let mut calldata = ArrayTrait::new(); calldata.append('token'); // name calldata.append('TKN'); // symbol calldata.append(18); // decimals calldata.append(1111); // initial supply low calldata.append(0); // initial supply high calldata.append(1234); // recipient let contract = declare("ERC20").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@calldata).unwrap(); let dispatcher = IERC20Dispatcher { contract_address }; let user_address: ContractAddress = 1234.try_into().unwrap(); let other_user_address: ContractAddress = 9999.try_into().unwrap(); let balance = dispatcher.balance_of(user_address); assert(balance == 1111_u256, 'balance == 1111'); start_cheat_caller_address(contract_address, user_address); dispatcher.transfer(other_user_address, 1000_u256); let balance = dispatcher.balance_of(user_address); assert(balance == 111_u256, 'balance == 111'); let balance = dispatcher.balance_of(other_user_address); assert(balance == 1000_u256, 'balance == 1000'); } "# ), Contract::from_code_path( "ERC20".to_string(), Path::new("tests/data/contracts/erc20.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn handling_errors() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; use starknet::contract_address_const; #[starknet::interface] trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); } #[test] #[feature("safe_dispatcher")] fn handling_execution_errors() { let contract = declare("HelloStarknet").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let safe_dispatcher = IHelloStarknetSafeDispatcher { contract_address }; match safe_dispatcher.do_a_panic() { Result::Ok(_) => panic_with_felt252('shouldve panicked'), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'PANIC', *panic_data.at(0)); assert(*panic_data.at(1) == 'DAYTAH', *panic_data.at(1)); } }; let mut panic_data = ArrayTrait::new(); panic_data.append('capybara'); match safe_dispatcher.do_a_panic_with(panic_data) { Result::Ok(_) => panic_with_felt252('shouldve panicked'), Result::Err(panic_data) => { assert(panic_data.len() == 2, 'Wrong panic_data len'); assert(*panic_data.at(0) == 'capybara', *panic_data.at(0)); assert(*panic_data.at(1) == 'ENTRYPOINT_FAILED', *panic_data.at(1)); } }; match safe_dispatcher.do_a_panic_with(ArrayTrait::new()) { Result::Ok(_) => panic_with_felt252('shouldve panicked'), Result::Err(panic_data) => { assert(panic_data.len() == 1, 'Should be generic panic'); assert(*panic_data.at(0) == 'ENTRYPOINT_FAILED', *panic_data.at(0)); } }; } #[test] #[feature("safe_dispatcher")] fn handling_missing_contract_error() { let safe_dispatcher = IHelloStarknetSafeDispatcher { contract_address: contract_address_const::<371937219379012>() }; match safe_dispatcher.do_a_panic() { Result::Ok(_) => panic_with_felt252('shouldve panicked'), Result::Err(_) => { // Would be nice to assert the error here once it is possible in cairo } }; } "# ), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn handling_bytearray_based_errors() { let test = test_case!( indoc!( r#" use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; use snforge_std::byte_array::try_deserialize_bytearray_error; use core::byte_array::BYTE_ARRAY_MAGIC; #[starknet::interface] trait IHelloStarknet { fn do_a_panic_with_bytearray(self: @TContractState); fn do_a_panic_with(self: @TContractState, args: Array); } #[test] #[feature("safe_dispatcher")] fn handling_errors() { let contract = declare("HelloStarknet").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let safe_dispatcher = IHelloStarknetSafeDispatcher { contract_address }; let panic_data = safe_dispatcher.do_a_panic_with_bytearray().unwrap_err(); let str_err = try_deserialize_bytearray_error(panic_data.span()).expect('wrong format'); assert( str_err == "This is a very long\n and multiline message that is certain to fill the buffer", 'wrong string received' ); // Not a bytearray let panic_data = safe_dispatcher.do_a_panic_with(array![123, 321]).unwrap_err(); try_deserialize_bytearray_error(panic_data.span()).expect_err('Parsing unexpectedy succeeded'); // Malformed bytearray let panic_data = safe_dispatcher.do_a_panic_with(array![BYTE_ARRAY_MAGIC, 321]).unwrap_err(); try_deserialize_bytearray_error(panic_data.span()).expect_err('Parsing unexpectedy succeeded'); } "# ), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn serding() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[derive(Drop, Serde)] struct NestedStruct { d: felt252, } #[derive(Drop, Serde)] struct CustomStruct { a: felt252, b: felt252, c: NestedStruct, } #[derive(Drop, Serde)] struct AnotherCustomStruct { e: felt252, } #[starknet::interface] trait ISerding { fn add_multiple_parts( self: @T, custom_struct: CustomStruct, another_struct: AnotherCustomStruct, standalone_arg: felt252 ) -> felt252; } #[test] fn serding() { let contract = declare("Serding").unwrap().contract_class(); let (contract_address, _) = contract.deploy( @ArrayTrait::new()).unwrap(); let dispatcher = ISerdingDispatcher { contract_address }; let ns = NestedStruct { d: 1 }; let cs = CustomStruct { a: 2, b: 3, c: ns }; let acs = AnotherCustomStruct { e: 4 }; let standalone_arg = 5; let result = dispatcher.add_multiple_parts(cs, acs, standalone_arg); assert(result == 1 + 2 + 3 + 4 + 5, 'Invalid sum'); } "# ), Contract::from_code_path( "Serding".to_string(), Path::new("tests/data/contracts/serding.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] #[expect(clippy::too_many_lines)] fn proxy_storage() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[derive(Drop, Serde, PartialEq, Copy)] struct NestedStruct { d: felt252, } #[derive(Drop, Serde, PartialEq, Copy)] struct CustomStruct { a: felt252, b: felt252, c: NestedStruct, } fn deploy_contract(name: ByteArray) -> ContractAddress { let contract = declare(name).unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); contract_address } #[starknet::interface] trait ICaller { fn call_executor( self: @T, executor_address: starknet::ContractAddress, custom_struct: CustomStruct ) -> felt252; } #[starknet::interface] trait IExecutor { fn read_storage(ref self: T) -> CustomStruct; } #[test] fn proxy_storage() { let caller_address = deploy_contract("Caller"); let executor_address = deploy_contract("Executor"); let caller_dispatcher = ICallerDispatcher { contract_address: caller_address }; let executor_dispatcher = IExecutorDispatcher { contract_address: executor_address }; let ns = NestedStruct { d: 6 }; let cs = CustomStruct { a: 2, b: 3, c: ns }; let result = caller_dispatcher.call_executor(executor_address, cs); assert(result == 6 + 5, 'Invalid result'); let storage_after = executor_dispatcher.read_storage(); assert(storage_after == cs, 'Invalid storage'); } "# ), Contract::new( "Caller", indoc!( r" use starknet::ContractAddress; #[derive(Drop, Serde, starknet::Store)] struct NestedStruct { d: felt252, } #[derive(Drop, Serde, starknet::Store)] struct CustomStruct { a: felt252, b: felt252, c: NestedStruct, } #[starknet::interface] trait ICaller { fn call_executor( self: @TContractState, executor_address: ContractAddress, custom_struct: CustomStruct ) -> felt252; } #[starknet::contract] mod Caller { use super::CustomStruct; use result::ResultTrait; use starknet::ContractAddress; #[starknet::interface] trait IExecutor { fn store_and_add_5(self: @T, custom_struct: CustomStruct) -> felt252; } #[storage] struct Storage {} #[abi(embed_v0)] impl CallerImpl of super::ICaller { fn call_executor( self: @ContractState, executor_address: ContractAddress, custom_struct: CustomStruct ) -> felt252 { let safe_dispatcher = IExecutorDispatcher { contract_address: executor_address }; safe_dispatcher.store_and_add_5(custom_struct) } } } " ) ), Contract::new( "Executor", indoc!( r" #[derive(Drop, Serde, starknet::Store)] struct NestedStruct { d: felt252, } #[derive(Drop, Serde, starknet::Store)] struct CustomStruct { a: felt252, b: felt252, c: NestedStruct, } #[starknet::interface] trait IExecutor { fn store_and_add_5(ref self: TContractState, custom_struct: CustomStruct) -> felt252; fn read_storage(ref self: TContractState) -> CustomStruct; } #[starknet::contract] mod Executor { use super::CustomStruct; #[storage] struct Storage { thing: CustomStruct } #[abi(embed_v0)] impl ExecutorImpl of super::IExecutor { fn store_and_add_5(ref self: ContractState, custom_struct: CustomStruct) -> felt252 { self.thing.write(custom_struct); 5 + self.thing.read().c.d } fn read_storage(ref self: ContractState) -> CustomStruct { self.thing.read() } } } " ) ) ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn proxy_dispatcher_panic() { let test = test_case!( indoc!( r#" use snforge_std::DeclareResultTrait; use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait }; use core::panic_with_felt252; fn deploy_contract(name: ByteArray, constructor_calldata: @Array) -> ContractAddress { let contract = declare(name).unwrap().contract_class(); let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); contract_address } #[starknet::interface] trait ICaller { fn invoke_executor(ref self: T); } #[test] fn proxy_dispatcher_panic() { let executor_address = deploy_contract("Executor", @ArrayTrait::new()); let caller_constructor_calldata: Array = array![executor_address.into()]; let caller_address = deploy_contract("Caller", @caller_constructor_calldata); let caller_dispatcher = ICallerSafeDispatcher { contract_address: caller_address }; match caller_dispatcher.invoke_executor() { Result::Ok(_) => panic_with_felt252('should have panicked'), Result::Err(x) => assert(*x.at(0) == 'panic_msg', 'wrong panic msg') } } "# ), Contract::new( "Caller", indoc!( r" #[starknet::interface] trait ICaller { fn invoke_executor( self: @TContractState, ); } #[starknet::contract] mod Caller { use starknet::ContractAddress; #[starknet::interface] trait IExecutor { fn invoke_with_panic(self: @T); } #[storage] struct Storage { executor_address: ContractAddress } #[constructor] fn constructor(ref self: ContractState, executor_address: ContractAddress) { self.executor_address.write(executor_address); } #[abi(embed_v0)] impl CallerImpl of super::ICaller { fn invoke_executor( self: @ContractState, ) { let dispatcher = IExecutorDispatcher { contract_address: self.executor_address.read() }; dispatcher.invoke_with_panic() } } } " ) ), Contract::new( "Executor", indoc!( r" #[starknet::interface] trait IExecutor { fn invoke_with_panic(ref self: TContractState); } #[starknet::contract] mod Executor { use core::panic_with_felt252; #[storage] struct Storage {} #[abi(embed_v0)] impl ExecutorImpl of super::IExecutor { fn invoke_with_panic(ref self: ContractState) { panic_with_felt252('panic_msg'); } } } " ) ) ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn nonexistent_method_call() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use option::OptionTrait; use traits::TryInto; use traits::Into; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; fn deploy_contract(name: ByteArray, constructor_calldata: @Array) -> ContractAddress { let contract = declare(name).unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); contract_address } #[starknet::interface] trait ICaller { fn invoke_nonexistent(ref self: T); } #[test] fn nonexistent_method_call() { let contract_address = deploy_contract("Contract", @ArrayTrait::new()); let caller_dispatcher = ICallerDispatcher { contract_address }; caller_dispatcher.invoke_nonexistent(); } "# ), Contract::new( "Contract", indoc!( r" #[starknet::contract] mod Contract { #[storage] struct Storage { } } " ) ) ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "nonexistent_method_call", "0x454e545259504f494e545f4e4f545f464f554e44 ('ENTRYPOINT_NOT_FOUND')", ); } #[test] fn nonexistent_libcall_function() { let test = test_case!( indoc!( r#" use core::clone::Clone; use array::ArrayTrait; use result::ResultTrait; use option::OptionTrait; use traits::TryInto; use traits::Into; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use starknet::ClassHash; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; fn deploy_contract(name: ByteArray) -> ContractAddress { let contract = declare(name).unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); contract_address } #[starknet::interface] trait IContract { fn invoke_nonexistent_libcall_from_contract(ref self: T, class_hash: ClassHash); } #[test] fn nonexistent_libcall_function() { let class = declare("Contract").unwrap().contract_class().clone(); let contract_address = deploy_contract("LibCaller"); let dispatcher = IContractDispatcher { contract_address }; dispatcher.invoke_nonexistent_libcall_from_contract(class.class_hash); } "# ), Contract::new( "LibCaller", indoc!( r" use starknet::ClassHash; #[starknet::interface] trait IContract { fn invoke_nonexistent_libcall_from_contract(ref self: TContractState, class_hash: ClassHash); } #[starknet::contract] mod LibCaller { use starknet::ClassHash; use result::ResultTrait; use array::ArrayTrait; #[storage] struct Storage {} #[starknet::interface] trait ICaller { fn invoke_nonexistent(ref self: T); } #[abi(embed_v0)] impl ContractImpl of super::IContract { fn invoke_nonexistent_libcall_from_contract(ref self: ContractState, class_hash: ClassHash) { let lib_dispatcher = ICallerLibraryDispatcher { class_hash }; lib_dispatcher.invoke_nonexistent(); } } } " ) ), Contract::new( "Contract", indoc!( r" #[starknet::contract] mod Contract { #[storage] struct Storage { } } " ) ) ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "nonexistent_libcall_function", "0x454e545259504f494e545f4e4f545f464f554e44 ('ENTRYPOINT_NOT_FOUND')", ); } #[test] fn undeclared_class_call() { let test = test_case!(indoc!( r" use starknet::ContractAddress; use traits::TryInto; use option::OptionTrait; #[starknet::interface] trait IContract { fn invoke_nonexistent(ref self: T); } #[test] fn undeclared_class_call() { let dispatcher = IContractDispatcher { contract_address: 5.try_into().unwrap() }; dispatcher.invoke_nonexistent(); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "undeclared_class_call", "Contract not deployed at address: 0x5", ); } #[test] fn nonexistent_class_libcall() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use option::OptionTrait; use starknet::ContractAddress; use starknet::ClassHash; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; fn deploy_contract(name: ByteArray) -> ContractAddress { let contract = declare(name).unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); contract_address } #[starknet::interface] trait IContract { fn invoke_nonexistent_libcall_from_contract(ref self: T); } #[test] fn test_nonexistent_libcall() { let contract_address = deploy_contract("LibCaller"); let dispatcher = IContractDispatcher { contract_address }; dispatcher.invoke_nonexistent_libcall_from_contract(); } "# ), Contract::new( "LibCaller", indoc!( r" #[starknet::interface] trait IContract { fn invoke_nonexistent_libcall_from_contract(ref self: TContractState); } #[starknet::contract] mod LibCaller { use starknet::class_hash::class_hash_try_from_felt252; use starknet::ClassHash; use result::ResultTrait; use array::ArrayTrait; use traits::TryInto; use option::OptionTrait; #[storage] struct Storage {} #[starknet::interface] trait ICaller { fn invoke_nonexistent(ref self: T); } #[abi(embed_v0)] impl ContractImpl of super::IContract { fn invoke_nonexistent_libcall_from_contract(ref self: ContractState) { let target_class_hash: ClassHash = class_hash_try_from_felt252(5_felt252).unwrap(); let lib_dispatcher = ICallerLibraryDispatcher { class_hash: target_class_hash }; lib_dispatcher.invoke_nonexistent(); } } } " ) ), Contract::new( "Contract", indoc!( r" #[starknet::contract] mod Contract { #[storage] struct Storage { } } " ) ) ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains(&result, "test_nonexistent_libcall", "Class with hash"); assert_case_output_contains(&result, "test_nonexistent_libcall", "is not declared."); } #[test] fn dispatcher_in_nested_call() { let test = test_case!( indoc!( r#" use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; use starknet::ContractAddress; #[starknet::interface] pub trait ITop { fn call_panic_contract( self: @TContractState, panic_contract_address: starknet::ContractAddress, ); } fn deploy_contracts() -> (ContractAddress, ContractAddress) { let contract = declare("Top").unwrap().contract_class(); let (top_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let contract = declare("Nested").unwrap().contract_class(); let (nested_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); (top_address, nested_address) } #[test] fn test_error_handled_inside_contract() { let (top_address, nested_address) = deploy_contracts(); let dispatcher = ITopDispatcher { contract_address: top_address }; dispatcher.call_panic_contract(nested_address); } "# ), Contract::from_code_path( "Top".to_string(), Path::new("tests/data/contracts/catching_error.cairo"), ) .unwrap(), Contract::from_code_path( "Nested".to_string(), Path::new("tests/data/contracts/catching_error.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/env.rs ================================================ use crate::utils::running_tests::run_test_case; use crate::utils::{ runner::{assert_case_output_contains, assert_failed, assert_passed}, test_case, }; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use num_bigint::BigUint; use starknet_types_core::felt::Felt; #[test] fn read_short_string() { let mut test = test_case!(indoc!( r#" use snforge_std::env::var; #[test] fn read_short_string() { let result = var("MY_ENV_VAR"); assert(result == array!['env_var_value'], 'failed reading env var'); } "# )); test.set_env("MY_ENV_VAR", "'env_var_value'"); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn read_felt252() { let mut test = test_case!(indoc!( r#" use snforge_std::env::var; #[test] fn read_felt252() { let result = var("MY_ENV_VAR"); assert(result == array![1234567], 'failed reading env var'); } "# )); test.set_env("MY_ENV_VAR", "1234567"); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn read_bytearray() { let mut test = test_case!(indoc!( r#" use snforge_std::env::var; #[test] fn read_bytearray() { let mut result = var("MY_ENV_VAR").span(); let result_bytearray = Serde::::deserialize(ref result).unwrap(); assert(result_bytearray == "very long string literal very very long very very long", 'failed reading env var'); } "# )); test.set_env( "MY_ENV_VAR", r#""very long string literal very very long very very long""#, ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn read_overflow_felt252() { let mut test = test_case!(indoc!( r#" use snforge_std::env::var; #[test] fn read_overflow_felt252() { let result = var("MY_ENV_VAR"); assert(result == array![1], ''); } "# )); let value = (Felt::prime() + BigUint::from(1_u32)).to_string(); test.set_env("MY_ENV_VAR", &value); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn read_invalid_short_string() { let mut test = test_case!(indoc!( r#" use snforge_std::env::var; #[test] fn read_invalid_short_string() { var("MY_ENV_VAR"); } "# )); let value = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; test.set_env("MY_ENV_VAR", value); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "read_invalid_short_string", &format!("Failed to parse value = {value} to felt"), ); } #[test] fn read_non_existent() { let test = test_case!(indoc!( r#" use snforge_std::env::var; #[test] fn read_invalid_short_string() { var("MY_ENV_VAR"); } "# )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "read_invalid_short_string", "Failed to read from env var = MY_ENV_VAR", ); } ================================================ FILE: crates/forge/tests/integration/fuzzing.rs ================================================ use crate::utils::runner::{TestCase, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use forge_runner::test_case_summary::{AnyTestCaseSummary, TestCaseSummary}; use indoc::indoc; const ALLOWED_ERROR: f64 = 0.05; #[test] fn fuzzed_argument() { let test = test_case!(indoc!( r" fn adder(a: felt252, b: felt252) -> felt252 { a + b } #[test] #[fuzzer] fn fuzzed_argument(b: felt252) { let result = adder(2, b); assert(result == 2 + b, '2 + b == 2 + b'); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn fuzzer_different_types() { let test = test_case!(indoc!( r" #[test] #[fuzzer] fn fuzzer_different_types(a: u256) { if a <= 5_u256 { assert(2 == 2, '2 == 2'); } else { let x = a - 5_u256; assert(x == a - 5_u256, 'x != a - 5'); } } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn fuzzed_while_loop() { let test = test_case!(indoc!( r" #[test] #[fuzzer(runs: 256, seed: 100)] fn fuzzed_while_loop(a: u8) { let mut i: u8 = 0; while i != a { i += 1; }; assert(1 == 1, ''); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); let test_target_summary = TestCase::find_test_result(&result); let AnyTestCaseSummary::Fuzzing(TestCaseSummary::Passed { gas_info, .. }) = &test_target_summary.test_case_summaries[0] else { panic!() }; // TODO (#2926) assert_eq!(gas_info.l1_gas.min, 0); assert_eq!(gas_info.l1_gas.max, 0); assert!(gas_info.l1_gas.mean < ALLOWED_ERROR); assert!(gas_info.l1_gas.std_deviation < ALLOWED_ERROR); assert_eq!(gas_info.l1_data_gas.min, 0); assert_eq!(gas_info.l1_data_gas.max, 0); assert!(gas_info.l1_data_gas.mean < ALLOWED_ERROR); assert!(gas_info.l1_data_gas.std_deviation < ALLOWED_ERROR); // different scarbs yield different results here, we do not care about the values that much assert!(gas_info.l2_gas.min < gas_info.l2_gas.max); assert!(gas_info.l2_gas.mean > 0.0); assert!(gas_info.l2_gas.std_deviation > 0.0); } ================================================ FILE: crates/forge/tests/integration/gas.rs ================================================ use crate::utils::runner::{Contract, assert_gas, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::{formatdoc, indoc}; use scarb_api::version::scarb_version; use semver::Version; use shared::test_utils::node_url::node_rpc_url; use starknet_api::execution_resources::{GasAmount, GasVector}; use std::path::Path; // all calculations are based on formulas from // https://docs.starknet.io/architecture-and-concepts/fees/#overall_fee // important info from this link regarding gas calculations: // 1 cairo step = 0.0025 L1 gas = 100 L2 gas // 1 sierra gas = 1 l2 gas // Costs of syscalls (if provided) are taken from versioned_constants (blockifier) // In Sierra gas tests, only the most significant costs are considered // Asserted values should be slightly greater than the computed values, // but must remain within a reasonable range #[test] fn declare_cost_is_omitted_cairo_steps() { let test = test_case!( indoc!( r#" use snforge_std::declare; #[test] fn declare_cost_is_omitted() { declare("GasChecker").unwrap(); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 1 = cost of 230 steps (because int(0.0025 * 230) = 1) // -> as stated in the top comment, 1 cairo step = 0.0025 L1 gas = 100 L2 gas // 0.0025 * 230 = 0,575 (BUT rounding up to 1, since this is as little as possible) // since 230 steps = 1 gas, to convert this to l2 gas we need to multiply by 40000 (100/0.0025) // 0 l1_gas + 0 l1_data_gas + 1 * (100 / 0.0025) l2 gas assert_gas( &result, "declare_cost_is_omitted", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(0), l2_gas: GasAmount(40000), }, ); } #[test] fn deploy_syscall_cost_cairo_steps() { let test = test_case!( indoc!( r#" use snforge_std::{declare, DeclareResultTrait}; use starknet::syscalls::deploy_syscall; #[test] fn deploy_syscall_cost() { let contract = declare("GasConstructorChecker").unwrap().contract_class().clone(); let (address, _) = deploy_syscall(contract.class_hash, 0, array![1].span(), false).unwrap(); assert(address != 0.try_into().unwrap(), 'wrong deployed addr'); } "# ), Contract::from_code_path( "GasConstructorChecker".to_string(), Path::new("tests/data/contracts/gas_constructor_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // l = 1 (updated contract class) // n = 1 (unique contracts updated - in this case it's the new contract address) // ( l + n * 2 ) * felt_size_in_bytes(32) = 96 (total l1 data cost) // 11 = cost of 2 keccak builtins from constructor (because int(5.12 * 2) = 11) // 0 l1_gas + 96 l1_data_gas + 11 * (100 / 0.0025) l2 gas assert_gas( &result, "deploy_syscall_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(96), l2_gas: GasAmount(440_000), }, ); } #[test] fn snforge_std_deploy_cost_cairo_steps() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[test] fn deploy_cost() { let contract = declare("GasConstructorChecker").unwrap().contract_class(); let (address, _) = contract.deploy(@array![1]).unwrap(); assert(address != 0.try_into().unwrap(), 'wrong deployed addr'); } "# ), Contract::from_code_path( "GasConstructorChecker".to_string(), Path::new("tests/data/contracts/gas_constructor_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 96 = gas cost of onchain data (deploy cost) // 11 = cost of 2 keccak builtins = 11 (because int(5.12 * 2) = 11) // 0 l1_gas + 96 l1_data_gas + 11 * (100 / 0.0025) l2 gas assert_gas( &result, "deploy_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(96), l2_gas: GasAmount(440_000), }, ); } #[test] fn keccak_cost_cairo_steps() { let test = test_case!(indoc!( r" #[test] fn keccak_cost() { keccak::keccak_u256s_le_inputs(array![1].span()); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 6 = cost of 1 keccak builtin (because int(5.12 * 1) = 6) // 0 l1_gas + 0 l1_data_gas + 6 * (100 / 0.0025) l2 gas assert_gas( &result, "keccak_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(0), l2_gas: GasAmount(240_000), }, ); } #[test] fn contract_keccak_cost_cairo_steps() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IGasChecker { fn keccak(self: @TContractState, repetitions: u32); } #[test] fn contract_keccak_cost() { let contract = declare("GasChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IGasCheckerDispatcher { contract_address }; dispatcher.keccak(7); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 96 = cost of deploy (see snforge_std_deploy_cost test) // 26 = cost of 5 keccak builtins (because int(5.12 * 7) = 36) // 0 l1_gas + 96 l1_data_gas + 36 * (100 / 0.0025) l2 gas assert_gas( &result, "contract_keccak_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(96), l2_gas: GasAmount(1_440_000), }, ); } #[test] fn range_check_cost_cairo_steps() { let test = test_case!(indoc!( r" #[test] fn range_check_cost() { assert(1_u8 >= 1_u8, 'error message'); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 1 = cost of 1 range check builtin (because int(0.04 * 1) = 1) // 0 l1_gas + 0 l1_data_gas + 1 * (100 / 0.0025) l2 gas assert_gas( &result, "range_check_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(0), l2_gas: GasAmount(40000), }, ); } #[test] fn contract_range_check_cost_cairo_steps() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IGasChecker { fn range_check(self: @TContractState); } #[test] fn contract_range_check_cost() { let contract = declare("GasChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IGasCheckerDispatcher { contract_address }; dispatcher.range_check(); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); let scarb_version = scarb_version().expect("Failed to get scarb version").scarb; // TODO(#4087): Remove this when bumping minimal recommended Scarb version to 2.14.0 if scarb_version >= Version::new(2, 14, 0) { // 96 = cost of deploy (see snforge_std_deploy_cost test) // 43 = cost of 1052 range check builtins (because int(0.04 * 1052) = 43) // 0 l1_gas + 96 l1_data_gas + 43 * (100 / 0.0025) l2 gas assert_gas( &result, "contract_range_check_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(96), l2_gas: GasAmount(1_720_000), }, ); } else { assert_gas( &result, "contract_range_check_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(96), l2_gas: GasAmount(6_520_000), }, ); } } #[test] fn bitwise_cost_cairo_steps() { let test = test_case!(indoc!( r" #[test] fn bitwise_cost() { let _bitwise = 1_u8 & 1_u8; assert(1 == 1, 'error message'); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 1 = cost of 1 bitwise builtin, because int(0.16 * 1) = 1 // 0 l1_gas + 0 l1_data_gas + 1 * (100 / 0.0025) l2 gas assert_gas( &result, "bitwise_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(0), l2_gas: GasAmount(40000), }, ); } /// We have to use 6 bitwise operations in the `bitwise` function to exceed steps cost #[test] fn contract_bitwise_cost_cairo_steps() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IGasChecker { fn bitwise(self: @TContractState, repetitions: u32); } #[test] fn contract_bitwise_cost() { let contract = declare("GasChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IGasCheckerDispatcher { contract_address }; dispatcher.bitwise(300); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 96 = cost of deploy l1 cost (see snforge_std_deploy_cost test) // 96 = cost of 600 bitwise builtins (because int(0.16 * 600) = 96) // 0 l1_gas + 96 l1_data_gas + 96 * (100 / 0.0025) l2 gas assert_gas( &result, "contract_bitwise_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(96), l2_gas: GasAmount(3_840_000), }, ); } #[test] fn pedersen_cost_cairo_steps() { let test = test_case!(indoc!( r" #[test] fn pedersen_cost() { core::pedersen::pedersen(1, 2); assert(1 == 1, 'error message'); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 1 = cost of 1 pedersen builtin (because int(0.16 * 1) = 1) // 0 l1_gas + 0 l1_data_gas + 1 * (100 / 0.0025) l2 gas assert_gas( &result, "pedersen_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(0), l2_gas: GasAmount(40000), }, ); } /// We have to use 12 pedersen operations in the `pedersen` function to exceed steps cost #[test] fn contract_pedersen_cost_cairo_steps() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IGasChecker { fn pedersen(self: @TContractState); } #[test] fn contract_pedersen_cost() { let contract = declare("GasChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IGasCheckerDispatcher { contract_address }; dispatcher.pedersen(); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 96 = cost of deploy (see snforge_std_deploy_cost test) // 10 = cost of 125 pedersen builtins (because int(0.08 * 125) = 10) // 0 l1_gas + 96 l1_data_gas + 10 * (100 / 0.0025) l2 gas assert_gas( &result, "contract_pedersen_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(96), l2_gas: GasAmount(400_000), }, ); } #[test] fn poseidon_cost_cairo_steps() { let test = test_case!(indoc!( r" #[test] fn poseidon_cost() { core::poseidon::hades_permutation(0, 0, 0); assert(1 == 1, 'error message'); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 1 = cost of 1 poseidon builtin (because int(0.08 * 1) = 1) // 0 l1_gas + 0 l1_data_gas + 1 * (100 / 0.0025) l2 gas assert_gas( &result, "poseidon_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(0), l2_gas: GasAmount(40000), }, ); } /// We have to use 12 poseidon operations in the `poseidon` function to exceed steps cost #[test] fn contract_poseidon_cost_cairo_steps() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IGasChecker { fn poseidon(self: @TContractState); } #[test] fn contract_poseidon_cost() { let contract = declare("GasChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IGasCheckerDispatcher { contract_address }; dispatcher.poseidon(); dispatcher.poseidon(); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 96 = cost of deploy (see snforge_std_deploy_cost test) // 13 = cost of 160 poseidon builtins (because int(0.08 * 160) = 13) // 0 l1_gas + 96 l1_data_gas + 13 * (100 / 0.0025) l2 gas assert_gas( &result, "contract_poseidon_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(96), l2_gas: GasAmount(520_000), }, ); } #[test] fn ec_op_cost_cairo_steps() { let test = test_case!(indoc!( r" use core::{ec, ec::{EcPoint, EcPointTrait}}; #[test] fn ec_op_cost() { EcPointTrait::new_from_x(1).unwrap().mul(2); assert(1 == 1, 'error message'); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 3 = cost of 1 ec_op builtin (because int(2.56 * 1) = 3) // 0 l1_gas + 0 l1_data_gas + 3 * (100 / 0.0025) l2 gas assert_gas( &result, "ec_op_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(0), l2_gas: GasAmount(120_000), }, ); } #[test] fn contract_ec_op_cost_cairo_steps() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IGasChecker { fn ec_op(self: @TContractState, repetitions: u32); } #[test] fn contract_ec_op_cost() { let contract = declare("GasChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IGasCheckerDispatcher { contract_address }; dispatcher.ec_op(10); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 96 = cost of deploy (see snforge_std_deploy_cost test) // 26 = cost of 10 ec_op builtins (because int(2.56 * 10) = 26) // 0 l1_gas + 96 l1_data_gas + 26 * (100 / 0.0025) l2 gas assert_gas( &result, "contract_ec_op_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(96), l2_gas: GasAmount(1_040_000), }, ); } #[test] fn storage_write_cost_cairo_steps() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IGasChecker { fn change_balance(ref self: TContractState, new_balance: u64); } #[test] fn storage_write_cost() { let contract = declare("GasChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IGasCheckerDispatcher { contract_address }; dispatcher.change_balance(1); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 2853 * 0.0025 = 7.13 ~ 8 = gas cost of steps // 96 = gas cost of deployment // storage_updates(1) * 2 * 32 = 64 // storage updates from zero value(1) * 32 = 32 (https://community.starknet.io/t/starknet-v0-13-4-pre-release-notes/115257#p-2358763-da-costs-27) // allocation cost: 402_000 l2 gas // 0 l1_gas + (96 + 64 + 32) l1_data_gas + 8 * (100 / 0.0025) + 402_000 l2 gas assert_gas( &result, "storage_write_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(192), l2_gas: GasAmount(722_000), }, ); } #[test] fn storage_write_from_test_cost_cairo_steps() { let test = test_case!(indoc!( r" #[starknet::contract] mod Contract { #[storage] struct Storage { balance: felt252, } } #[test] fn storage_write_from_test_cost() { let mut state = Contract::contract_state_for_testing(); state.balance.write(10); } " ),); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 521 * 0.0025 = 1.3025 ~ 2 = gas cost of steps // n = unique contracts updated // m = values updated // So, as per formula: // n(1) * 2 * 32 = 64 // m(1) * 2 * 32 = 64 // storage updates from zero value(1) * 32 = 32 (https://community.starknet.io/t/starknet-v0-13-4-pre-release-notes/115257#p-2358763-da-costs-27) // allocation cost: 402_000 l2 gas // 0 l1_gas + (64 + 64 + 32) l1_data_gas + 2 * (100 / 0.0025) + 402_000 l2 gas assert_gas( &result, "storage_write_from_test_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(160), l2_gas: GasAmount(482_000), }, ); } #[test] fn multiple_storage_writes_cost_cairo_steps() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IGasChecker { fn change_balance(ref self: TContractState, new_balance: u64); } #[test] fn multiple_storage_writes_cost() { let contract = declare("GasChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IGasCheckerDispatcher { contract_address }; dispatcher.change_balance(1); dispatcher.change_balance(1); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 4264 * 0.0025 = 10.66 ~ 11 = gas cost of steps // l = number of class hash updates // n = unique contracts updated // m = unique(!) values updated // So, as per formula: // n(1) * 2 * 32 = 64 // m(1) * 2 * 32 = 64 // l(1) * 32 = 32 // storage updates from zero value(1) * 32 = 32 (https://community.starknet.io/t/starknet-v0-13-4-pre-release-notes/115257#p-2358763-da-costs-27) // allocation cost: 402_000 l2 gas // 0 l1_gas + (64 + 64 + 32 + 32) l1_data_gas + 11 * (100 / 0.0025) + 402_000 l2 gas assert_gas( &result, "multiple_storage_writes_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(192), l2_gas: GasAmount(842_000), }, ); } #[test] fn l1_message_cost_cairo_steps() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IGasChecker { fn send_l1_message(self: @TContractState); } #[test] fn l1_message_cost() { let contract = declare("GasChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IGasCheckerDispatcher { contract_address }; dispatcher.send_l1_message(); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 2614 * 0.0025 = 6.535 ~ 7 = gas cost of steps // 96 = gas cost of deployment // 29524 = gas cost of onchain data // 29524 l1_gas + 96 l1_data_gas + 7 * (100 / 0.0025) l2 gas assert_gas( &result, "l1_message_cost", GasVector { l1_gas: GasAmount(29524), l1_data_gas: GasAmount(96), l2_gas: GasAmount(280_000), }, ); } #[test] fn l1_message_from_test_cost_cairo_steps() { let test = test_case!(indoc!( r" #[test] fn l1_message_from_test_cost() { starknet::send_message_to_l1_syscall(1, array![1].span()).unwrap(); } " ),); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 224 * 0.0025 = 0.56 ~ 1 = gas cost of steps // 26764 = gas cost of onchain data // 26764 l1_gas + 0 l1_data_gas + 1 * (100 / 0.0025) l2 gas assert_gas( &result, "l1_message_from_test_cost", GasVector { l1_gas: GasAmount(26764), l1_data_gas: GasAmount(0), l2_gas: GasAmount(40000), }, ); } #[test] fn l1_message_cost_for_proxy_cairo_steps() { let test = test_case!( indoc!( r#" use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IGasCheckerProxy { fn send_l1_message_from_gas_checker( self: @TContractState, address: ContractAddress ); } #[test] fn l1_message_cost_for_proxy() { let contract = declare("GasChecker").unwrap().contract_class(); let (gas_checker_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let contract = declare("GasCheckerProxy").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IGasCheckerProxyDispatcher { contract_address }; dispatcher.send_l1_message_from_gas_checker(gas_checker_address); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap(), Contract::from_code_path( "GasCheckerProxy".to_string(), Path::new("tests/data/contracts/gas_checker_proxy.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // (4960 steps + 28 memory holes) * 0.0025 = 12.47 ~ 13 = gas cost of steps // l = number of class hash updates // n = unique contracts updated // So, as per formula: // n(2) * 2 * 32 = 128 // l(2) * 32 = 64 // 29524 = gas cost of message // 29524 l1_gas + (128 + 64) l1_data_gas + 13 * (100 / 0.0025) l2 gas assert_gas( &result, "l1_message_cost_for_proxy", GasVector { l1_gas: GasAmount(29524), l1_data_gas: GasAmount(192), l2_gas: GasAmount(520_000), }, ); } #[test] fn l1_handler_cost_cairo_steps() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, L1HandlerTrait }; #[test] fn l1_handler_cost() { let contract = declare("GasChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); let mut l1_handler = L1HandlerTrait::new(contract_address, selector!("handle_l1_message")); l1_handler.execute(123, array![].span()).unwrap(); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 96 = gas cost of onchain data (deploy cost) // int(5.12 * 4) = 21 = keccak cost from l1 handler // in this test, l1_handler_payload_size = 6 // 15923 = 12251 (gas used for processing L1<>L2 messages on L1) + 3672 (SHARP gas, 6 * 612) // https://github.com/starkware-libs/sequencer/blob/028db0341378147037b5e7236d8e136e4ca7c30d/crates/blockifier/src/fee/resources.rs#L338-L340 // 12251 = 3072 (6 * 512, 512 is gas per memory word) + // + 4179 (cost of `ConsumedMessageToL2` event for payload with length 6: // -> 375 const opcode cost // -> 4 * 375 topics cost (fromAddress, toAddress, selector and 1 default) // -> 9 * 256 data array cost (payload length, nonce and 2 required solidity params for array) // + 5000 (gas per counter decrease, fixed cost of L1 -> L2 message) // // 15923 l1_gas + 96 l1_data_gas + 21 * (100 / 0.0025) l2 gas assert_gas( &result, "l1_handler_cost", GasVector { l1_gas: GasAmount(15923), l1_data_gas: GasAmount(96), l2_gas: GasAmount(840_000), }, ); } #[test] fn events_cost_cairo_steps() { let test = test_case!(indoc!( r" use starknet::syscalls::emit_event_syscall; #[test] fn events_cost() { let mut keys = array![]; let mut values = array![]; let mut i: u32 = 0; while i < 50 { keys.append('key'); values.append(1); i += 1; }; emit_event_syscall(keys.span(), values.span()).unwrap(); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // 105 range_check_builtin = 105 * 0.04 = 4.2 = ~5 // 768_000 gas for 50 events // 512_000 = 10_240 * 50 (gas from 50 event keys) // 256_000 = 5120 * 50 (gas from 50 event data) // 0 l1_gas + 0 l1_data_gas + ((5) * (100 / 0.0025) + 768_000) l2 gas assert_gas( &result, "events_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(0), l2_gas: GasAmount(968_000), }, ); } #[test] fn events_contract_cost_cairo_steps() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IGasChecker { fn emit_event(ref self: TContractState, n_keys_and_vals: u32); } #[test] fn event_emission_cost() { let (contract_address, _) = declare("GasChecker").unwrap().contract_class().deploy(@array![]).unwrap(); let dispatcher = IGasCheckerDispatcher { contract_address }; dispatcher.emit_event(50); } "# ), Contract::from_code_path( "GasChecker", Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // (3736 steps + 14 memory holes) * 0.0025 = 9.375 ~ 10 = gas cost of steps // 96 = gas cost of onchain data (deploy cost) // 768_000 gas for 50 events // 512_000 = 10_240 * 50 (gas from 50 event keys) // 256_000 = 5120 * 50 (gas from 50 event data) // 0 l1_gas + 96 l1_data_gas + (10 * (100 / 0.0025) + 768_000) l2 gas assert_gas( &result, "event_emission_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(96), l2_gas: GasAmount(1_168_000), }, ); } #[test] fn nested_call_cost_cairo_steps() { let test = test_case!( indoc!( r#" use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; use starknet::{ContractAddress, SyscallResult}; #[starknet::interface] trait IGasCheckerProxy { fn call_other_contract( self: @TContractState, contract_address: ContractAddress, entry_point_selector: felt252, calldata: Array::, ) -> SyscallResult>; } fn deploy_contract(name: ByteArray) -> ContractAddress { let contract = declare(name).unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); contract_address } #[test] fn test_call_other_contract() { let contract_address_a = deploy_contract("GasCheckerProxy"); let contract_address_b = deploy_contract("GasCheckerProxy"); let hello_starknet_address = deploy_contract("HelloStarknet"); let dispatcher_a = IGasCheckerProxyDispatcher { contract_address: contract_address_a }; let _ = dispatcher_a .call_other_contract( contract_address_b, selector!("call_other_contract"), array![hello_starknet_address.into(), selector!("example_function"), 0], ); } "# ), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet_for_nested_calls.cairo"), ) .unwrap(), Contract::from_code_path( "GasCheckerProxy".to_string(), Path::new("tests/data/contracts/gas_checker_proxy.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // int(1121 * 0.16) = 180 = gas cost of bitwise builtins // 96 * 3 = gas cost of onchain data (deploy cost) // ~1 gas for 1 event key // ~1 gas for 1 event data // 0 l1_gas + (96 * 3) l1_data_gas + 180 * (100 / 0.0025) + 1 * 10240 + 1 * 5120 l2 gas assert_gas( &result, "test_call_other_contract", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(288), l2_gas: GasAmount(7_215_360), }, ); } #[test] fn nested_call_cost_in_forked_contract_cairo_steps() { let test = test_case!( formatdoc!( r#" use snforge_std::{{ContractClassTrait, DeclareResultTrait, declare}}; use starknet::{{ContractAddress, SyscallResult}}; #[starknet::interface] trait IGasCheckerProxy {{ fn call_other_contract( self: @TContractState, contract_address: ContractAddress, entry_point_selector: felt252, calldata: Array::, ) -> SyscallResult>; }} fn deploy_contract(name: ByteArray) -> ContractAddress {{ let contract = declare(name).unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); contract_address }} #[test] #[fork(url: "{}", block_number: 861_389)] fn test_call_other_contract_fork() {{ let contract_address_a = deploy_contract("GasCheckerProxy"); let contract_address_b = deploy_contract("GasCheckerProxy"); let hello_starknet_address: ContractAddress = 0x07f01bbebed8dfeb60944bd9273e2bd844e39b0106eb6ca05edaeee95a817c64.try_into().unwrap(); let dispatcher_a = IGasCheckerProxyDispatcher {{ contract_address: contract_address_a }}; let _ = dispatcher_a .call_other_contract( contract_address_b, selector!("call_other_contract"), array![hello_starknet_address.into(), selector!("example_function"), 0], ); }} "#, node_rpc_url() ).as_str(), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet_for_nested_calls.cairo"), ) .unwrap(), Contract::from_code_path( "GasCheckerProxy".to_string(), Path::new("tests/data/contracts/gas_checker_proxy.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // int(1121 * 0.16) = 180 = gas cost of bitwise builtins // 96 * 2 = gas cost of onchain data (deploy cost) // ~1 gas for 1 event key // ~1 gas for 1 event data // 0 l1_gas + (96 * 2) l1_data_gas + 180 * (100 / 0.0025) + 1 * 10240 + 1 * 5120 l2 gas assert_gas( &result, "test_call_other_contract_fork", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(192), l2_gas: GasAmount(7_215_360), }, ); } #[test] fn empty_test_sierra_gas() { let test = test_case!(indoc!( r" #[test] fn empty_test() {} " )); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); assert_gas( &result, "empty_test", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(0), l2_gas: GasAmount(13_840), }, ); } #[test] fn declare_cost_is_omitted_sierra_gas() { let test = test_case!( indoc!( r#" use snforge_std::declare; #[test] fn declare_cost_is_omitted() { declare("GasChecker").unwrap(); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); assert_gas( &result, "declare_cost_is_omitted", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(0), l2_gas: GasAmount(25_350), }, ); } #[test] fn deploy_syscall_cost_sierra_gas() { let test = test_case!( indoc!( r#" use snforge_std::{declare, DeclareResultTrait}; use starknet::syscalls::deploy_syscall; #[test] fn deploy_syscall_cost() { let contract = declare("GasConstructorChecker").unwrap().contract_class().clone(); let (address, _) = deploy_syscall(contract.class_hash, 0, array![0].span(), false).unwrap(); assert(address != 0.try_into().unwrap(), 'wrong deployed addr'); } "# ), Contract::from_code_path( "GasConstructorChecker".to_string(), Path::new("tests/data/contracts/gas_constructor_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); // l = 1 (updated contract class) // n = 1 (unique contracts updated - in this case it's the new contract address) // ( l + n * 2 ) * felt_size_in_bytes(32) = 96 (total l1 data cost) // // 151_970 = cost of 1 deploy syscall (because 1 * (1173 + 8) * 100 + (7 + 1) * 4050 + 21 * 70) // -> 1 deploy syscall costs 1132 cairo steps, 7 pedersen and 18 range check builtins // -> 1 calldata element costs 8 cairo steps and 1 pedersen // -> 1 pedersen costs 4050, 1 range check costs 70 // // 96 l1_data_gas // l2 gas > 151_970 assert_gas( &result, "deploy_syscall_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(96), l2_gas: GasAmount(184_360), }, ); } #[test] fn snforge_std_deploy_cost_sierra_gas() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[test] fn deploy_cost() { let contract = declare("GasConstructorChecker").unwrap().contract_class(); let (address, _) = contract.deploy(@array![0]).unwrap(); assert(address != 0.try_into().unwrap(), 'wrong deployed addr'); } "# ), Contract::from_code_path( "GasConstructorChecker".to_string(), Path::new("tests/data/contracts/gas_constructor_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); // 96 = gas cost of onchain data (see `deploy_syscall_cost_sierra_gas` test) // 151_970 = cost of 1 deploy syscall (see `deploy_syscall_cost_sierra_gas` test) // // 96 l1_data_gas // l2 gas > 151_970 assert_gas( &result, "deploy_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(96), l2_gas: GasAmount(190_800), }, ); } #[test] fn keccak_cost_sierra_gas() { let test = test_case!(indoc!( r" #[test] fn keccak_cost() { keccak::keccak_u256s_le_inputs(array![1].span()); } " )); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); // Note: To calculate the gas cost of the keccak syscall, we need to include the cost of keccak round // https://github.com/starkware-libs/sequencer/blob/028db0341378147037b5e7236d8e136e4ca7c30d/crates/blockifier/src/execution/syscalls/syscall_executor.rs#L190 // 10_000 = cost of 1 keccak syscall (1 * 100 * 100) // -> 1 keccak syscall costs 100 cairo steps // 171_707 = cost of 1 keccak round syscall (136_189 + 3498 + 3980 + 28_100) // -> 1 keccak builtin costs 136_189 // -> 6 bitwise builtin cost 6 * 583 = 3498 // -> 56 range check builtins cost 56 * 70 = 3980 // -> 281 steps cost 281 * 100 = 28_100 // // l2 gas > 181_707 assert_gas( &result, "keccak_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(0), l2_gas: GasAmount(226_727), }, ); } #[test] fn contract_keccak_cost_sierra_gas() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IGasChecker { fn keccak(self: @TContractState, repetitions: u32); } #[test] fn contract_keccak_cost() { let contract = declare("GasChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IGasCheckerDispatcher { contract_address }; dispatcher.keccak(5); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); // 96 = gas cost of onchain data (see `deploy_syscall_cost_sierra_gas` test) // 147_120 = cost of 1 deploy syscall (see `deploy_syscall_cost_sierra_gas` test) // 908_535 = 5 * 181_707 = cost of 5 keccak syscall (see `keccak_cost_sierra_gas` test) // 91_560 = cost of 1 call contract syscall (because 1 * 903 * 100 + 18 * 70) // -> 1 call contract syscall costs 903 cairo steps and 18 range check builtins // -> 1 range check costs 70 // // 96 l1_data_gas // l2 gas > 1_147_215 (= 147_120 + 908_535 + 91_560) assert_gas( &result, "contract_keccak_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(96), l2_gas: GasAmount(1_353_025), }, ); } #[test] fn storage_write_cost_sierra_gas() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IGasChecker { fn change_balance(ref self: TContractState, new_balance: u64); } #[test] fn storage_write_cost() { let contract = declare("GasChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IGasCheckerDispatcher { contract_address }; dispatcher.change_balance(1); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); // 96 = gas cost of onchain data (see `deploy_syscall_cost_sierra_gas` test) // 64 = storage_updates(1) * 2 * 32 // 32 = storage updates from zero value(1) * 32 (https://community.starknet.io/t/starknet-v0-13-4-pre-release-notes/115257#p-2358763-da-costs-27) // allocation cost: 402_000 l2 gas // 147_120 = cost of 1 deploy syscall (see `deploy_syscall_cost_sierra_gas` test) // 91_560 = cost of 1 call contract syscall (see `contract_keccak_cost_sierra_gas` test) // 44_970 = cost of 1 storage write syscall (because 1 * 449 * 100 + 1 * 70 = 9670) // -> 1 storage write syscall costs 449 cairo steps and 1 range check builtin // -> 1 range check costs 70 // // (96 + 64 + 32 =) 192 l1_data_gas // l2 gas > 685_650 = (147_120 + 91_560 + 44_970 + 402_000) assert_gas( &result, "storage_write_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(192), l2_gas: GasAmount(726_320), }, ); } #[test] fn multiple_storage_writes_cost_sierra_gas() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IGasChecker { fn change_balance(ref self: TContractState, new_balance: u64); } #[test] fn multiple_storage_writes_cost() { let contract = declare("GasChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IGasCheckerDispatcher { contract_address }; dispatcher.change_balance(1); dispatcher.change_balance(1); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); // 64 = n(1) * 2 * 32 // 64 = m(1) * 2 * 32 // 32 = l(1) * 32 // -> l = number of class hash updates // -> n = unique contracts updated // -> m = unique(!) values updated // 32 = storage updates from zero value(1) * 32 (https://community.starknet.io/t/starknet-v0-13-4-pre-release-notes/115257#p-2358763-da-costs-27) // 147_120 = cost of 1 deploy syscall (see `deploy_syscall_cost_sierra_gas` test) // 183_120 = 2 * 91_560 = cost of 2 call contract syscalls (see `contract_keccak_cost_sierra_gas` test) // 89_940 = cost of 2 storage write syscall (see `storage_write_cost_sierra_gas` test) // allocation cost: 402_000 l2 gas // 192 = (64 + 64 + 32 + 32) l1_data_gas // l2 gas > 822_180 (= 147_120 + 183_120 + 89_940 + 402_000) assert_gas( &result, "multiple_storage_writes_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(192), l2_gas: GasAmount(868_730), }, ); } #[test] fn l1_message_cost_sierra_gas() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IGasChecker { fn send_l1_message(self: @TContractState); } #[test] fn l1_message_cost() { let contract = declare("GasChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IGasCheckerDispatcher { contract_address }; dispatcher.send_l1_message(); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); // 29_524 = gas cost of l2 -> l1 message (payload length 3) // The calculation below covers L1 costs related to the state updates and emitting the event `LogMessageToL1`. // https://github.com/starkware-libs/sequencer/blob/028db0341378147037b5e7236d8e136e4ca7c30d/crates/blockifier/src/fee/resources.rs#L338-L340 // -> (3 + 3) * 1124 = state update costs // -> 375 const opcode cost // -> 3 * 375 = 1225 topics cost of `LogMessageToL1` event (fromAddress, toAddress and 1 default) // -> 5 * 256 = 1280 data array cost (payload length + 2 required solidity params for array) // -> 20_000 l1 storage write cost // 96 = gas cost of onchain data (see `deploy_syscall_cost_sierra_gas` test) // 147_120 = cost of 1 deploy syscall (see `deploy_syscall_cost_sierra_gas` test) // 91_560 = cost of 1 call contract syscall (see `contract_keccak_cost_sierra_gas` test) // 14_470 = cost of 1 SendMessageToL1 syscall (because 1 * 144 * 100 + 1 * 70 ) // -> 1 storage write syscall costs 144 cairo steps and 1 range check builtin // -> 1 range check costs 70 // // 29_524 l1_gas // 96 l1_data_gas // l2 gas > 253_150 (= 147_120 + 91_560 + 14_470) assert_gas( &result, "l1_message_cost", GasVector { l1_gas: GasAmount(29_524), l1_data_gas: GasAmount(96), l2_gas: GasAmount(293_180), }, ); } #[test] fn l1_message_cost_for_proxy_sierra_gas() { let test = test_case!( indoc!( r#" use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IGasCheckerProxy { fn send_l1_message_from_gas_checker( self: @TContractState, address: ContractAddress ); } #[test] fn l1_message_cost_for_proxy() { let contract = declare("GasChecker").unwrap().contract_class(); let (gas_checker_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let contract = declare("GasCheckerProxy").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IGasCheckerProxyDispatcher { contract_address }; dispatcher.send_l1_message_from_gas_checker(gas_checker_address); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap(), Contract::from_code_path( "GasCheckerProxy".to_string(), Path::new("tests/data/contracts/gas_checker_proxy.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); // 29_524 = gas cost of l2 -> l1 message (see `l1_message_cost_sierra_gas` test) // 128 = n(2) * 2 * 32 // 64 = l(2) * 32 // -> l = number of class hash updates // -> n = unique contracts updated // 294_240 = 2 * 147_120 = cost of 2 deploy syscall (see `deploy_syscall_cost_sierra_gas` test) // 183_120 = 2 * 91_560 = cost of 2 call contract syscalls (see `multiple_storage_writes_cost_sierra_gas` test) // 14_470 = cost of 1 SendMessageToL1 syscall (see `l1_message_cost_sierra_gas` test) // // 29_524 l1_gas // (128 + 64 =) 192 l1_data_gas // l2 gas > 491_830 (= 294_240 + 183_120 + 14_470) assert_gas( &result, "l1_message_cost_for_proxy", GasVector { l1_gas: GasAmount(29_524), l1_data_gas: GasAmount(192), l2_gas: GasAmount(557_260), }, ); } #[test] fn events_cost_sierra_gas() { let test = test_case!(indoc!( r" use starknet::syscalls::emit_event_syscall; #[test] fn events_cost() { let mut keys = array![]; let mut values = array![]; let mut i: u32 = 0; while i < 10 { keys.append('key'); values.append(1); i += 1; }; emit_event_syscall(keys.span(), values.span()).unwrap(); } " )); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); // 102_400 = 10 * 10_240 // -> we emit 50 keys, each taking up 1 felt of space // -> L2 gas cost for event key is 10240 gas/felt // 51_200 = 10 * 5120 // -> we emit 50 keys, each having 1 felt of data // -> L2 gas cost for event data is 5120 gas/felt // 10_000 = cost of 1 emit event syscall (because 1 * 61 * 100 + 1 * 70 = 6170) // -> 1 emit event syscall costs 61 cairo steps and 1 range check builtin // -> 1 range check costs 70 // -> the minimum total cost is `syscall_base_gas_cost`, which is pre-charged by the compiler (atm it is 100 * 100) // // l2 gas > 163_600 (= 102_400 + 51_200 + 10_000) assert_gas( &result, "events_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(0), l2_gas: GasAmount(205_510), }, ); } #[test] fn events_contract_cost_sierra_gas() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IGasChecker { fn emit_event(ref self: TContractState, n_keys_and_vals: u32); } #[test] fn event_emission_cost() { let (contract_address, _) = declare("GasChecker").unwrap().contract_class().deploy(@array![]).unwrap(); let dispatcher = IGasCheckerDispatcher { contract_address }; dispatcher.emit_event(10); } "# ), Contract::from_code_path( "GasChecker", Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); // 96 = gas cost of onchain data (see `deploy_syscall_cost_sierra_gas` test) // 102_400 = event keys cost (see `events_cost_sierra_gas` test) // 51_200 = event data cost (see `events_cost_sierra_gas` test) // 10_000 = cost of 1 emit event syscall (see `events_cost_sierra_gas` test) // 147_120 = cost of 1 deploy syscall (see `deploy_syscall_cost_sierra_gas` test) // 91_560 = cost of 1 call contract syscall (see `contract_keccak_cost_sierra_gas` test) // 159_810 = reported consumed sierra gas // // 96 l1_data_gas // l2 gas > 402_280 (= 102_400 + 51_200 + 10_000 + 147_120 + 91_560) assert_gas( &result, "event_emission_cost", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(96), l2_gas: GasAmount(471_090), }, ); } #[test] fn nested_call_cost_sierra_gas() { let test = test_case!( indoc!( r#" use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; use starknet::{ContractAddress, SyscallResult}; #[starknet::interface] trait IGasCheckerProxy { fn call_other_contract( self: @TContractState, contract_address: ContractAddress, entry_point_selector: felt252, calldata: Array::, ) -> SyscallResult>; } fn deploy_contract(name: ByteArray) -> ContractAddress { let contract = declare(name).unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); contract_address } #[test] fn test_call_other_contract() { let contract_address_a = deploy_contract("GasCheckerProxy"); let contract_address_b = deploy_contract("GasCheckerProxy"); let hello_starknet_address = deploy_contract("HelloStarknet"); let dispatcher_a = IGasCheckerProxyDispatcher { contract_address: contract_address_a }; let _ = dispatcher_a .call_other_contract( contract_address_b, selector!("call_other_contract"), array![hello_starknet_address.into(), selector!("example_function"), 0], ); } "# ), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet_for_nested_calls.cairo"), ) .unwrap(), Contract::from_code_path( "GasCheckerProxy".to_string(), Path::new("tests/data/contracts/gas_checker_proxy.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); // 10_240 = event keys cost (see `events_cost_sierra_gas` test) // 5120 = event data cost (see `events_cost_sierra_gas` test) // 10_000 = cost of 1 emit event syscall (see `events_cost_sierra_gas` test) // 181_707 = cost of 1 keccak syscall (see `keccak_cost_sierra_gas` test) // 10_840 = cost of 1 get block hash syscall (107 * 100 + 2 * 70) // 441_360 = 3 * 147_120 = cost of 3 deploy syscall (see `deploy_syscall_cost_sierra_gas` test) // 274_680 = 3 * 91_560 = cost of 3 call contract syscall (see `contract_keccak_cost_sierra_gas` test) // 841_295 = cost of 1 sha256_process_block_syscall syscall (1867 * 100 + 1115 * 583 + 65 * 70) // // 288 l1_data_gas // l2 gas > 1_775_242 (= 10_240 + 5120 + 10_000 + 181_707 + 10_840 + 441_360 + 274_680 + 841_295) assert_gas( &result, "test_call_other_contract", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(288), l2_gas: GasAmount(1_940_292), }, ); } #[test] fn nested_call_cost_in_forked_contract_sierra_gas() { let test = test_case!( formatdoc!( r#" use snforge_std::{{ContractClassTrait, DeclareResultTrait, declare}}; use starknet::{{ContractAddress, SyscallResult}}; #[starknet::interface] trait IGasCheckerProxy {{ fn call_other_contract( self: @TContractState, contract_address: ContractAddress, entry_point_selector: felt252, calldata: Array::, ) -> SyscallResult>; }} fn deploy_contract(name: ByteArray) -> ContractAddress {{ let contract = declare(name).unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); contract_address }} #[test] #[fork(url: "{}", block_number: 861_389)] fn test_call_other_contract_fork() {{ let contract_address_a = deploy_contract("GasCheckerProxy"); let contract_address_b = deploy_contract("GasCheckerProxy"); let hello_starknet_address: ContractAddress = 0x07f01bbebed8dfeb60944bd9273e2bd844e39b0106eb6ca05edaeee95a817c64.try_into().unwrap(); let dispatcher_a = IGasCheckerProxyDispatcher {{ contract_address: contract_address_a }}; let _ = dispatcher_a .call_other_contract( contract_address_b, selector!("call_other_contract"), array![hello_starknet_address.into(), selector!("example_function"), 0], ); }} "#, node_rpc_url() ).as_str(), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet_for_nested_calls.cairo"), ) .unwrap(), Contract::from_code_path( "GasCheckerProxy".to_string(), Path::new("tests/data/contracts/gas_checker_proxy.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); // 10_240 = event keys cost (see `events_cost_sierra_gas` test) // 5120 = event data cost (see `events_cost_sierra_gas` test) // 10_000 = cost of 1 emit event syscall (see `events_cost_sierra_gas` test) // 181_707 = cost of 1 keccak syscall (see `keccak_cost_sierra_gas` test) // 10_840 = cost of 1 get block hash syscall (107 * 100 + 2 * 70) // 294_240 = 2 * 147_120 = cost of 2 deploy syscall (see `deploy_syscall_cost_sierra_gas` test) // 274_680 = 3 * 91_560 = cost of 3 call contract syscall (see `contract_keccak_cost_sierra_gas` test) // 841_295 = cost of 1 sha256_process_block_syscall syscall (1867 * 100 + 1115 * 583 + 65 * 70) // // 192 l1_data_gas // l2 gas > 1_628_122 (= 10_240 + 5120 + 10_000 + 181_707 + 10_840 + 294_240 + 274_680 + 841_295) assert_gas( &result, "test_call_other_contract_fork", GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(192), l2_gas: GasAmount(1_812_052), }, ); } ================================================ FILE: crates/forge/tests/integration/generate_random_felt.rs ================================================ use crate::utils::runner::assert_passed; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; #[test] fn simple_generate_random_felt() { let test = test_case!(indoc!( r" use snforge_std::generate_random_felt; #[test] fn simple_generate_random_felt() { let mut random_values = array![]; let mut unique_values = array![]; let mut i = 10; while i != 0 { let random_value = generate_random_felt(); random_values.append(random_value); i -= 1; }; for element in random_values.span() { let mut k = 0; while k != random_values.len() { if element != random_values.at(k) { unique_values.append(element); }; k += 1; }; }; assert(unique_values.len() > 1, 'Identical values'); } " ),); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/get_available_gas.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn test_get_available_gas() { let test = test_case!( indoc!( r#" use snforge_std::{declare, DeclareResultTrait, ContractClassTrait}; #[test] fn check_available_gas() { let contract = declare("HelloStarknet").unwrap().contract_class().clone(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let gas_before = core::testing::get_available_gas(); core::gas::withdraw_gas().unwrap(); starknet::syscalls::call_contract_syscall( contract_address, selector!("increase_balance"), array![10].span(), ).unwrap(); let gas_after = core::testing::get_available_gas(); core::gas::withdraw_gas().unwrap(); let gas_diff = gas_before - gas_after; // call_contract syscall: 91_560 gas // storage_write syscall: 44_970 gas // storage_read syscall: 18_070 gas let min_expected_gas = 91_560 + 44_970 + 18_070; // Check that gas used is above the expected minimum assert(gas_diff > min_expected_gas, 'Incorrect gas'); // Allow an arbitrary margin of 10_000 gas assert(min_expected_gas + 10_000 > gas_diff, 'Incorrect gas'); } "# ), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/get_class_hash.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn get_class_hash_cheatcode() { let test = test_case!( indoc!( r#" use core::clone::Clone; use array::ArrayTrait; use result::ResultTrait; use snforge_std::{ declare, ContractClassTrait, get_class_hash, DeclareResultTrait }; #[test] fn get_class_hash_cheatcode() { let contract = declare("HelloStarknet").unwrap().contract_class().clone(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); assert(get_class_hash(contract_address) == contract.class_hash, 'Incorrect class hash'); } "# ), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/get_current_vm_step.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn test_get_current_vm_step() { let test = test_case!( indoc!( r#" use snforge_std::testing::get_current_vm_step; use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; const STEPS_MARGIN: u32 = 100; // 1173 = cost of 1 deploy syscall without calldata // https://github.com/starkware-libs/sequencer/blob/b29c0e8c61f7b2340209e256cf87dfe9f2c811aa/crates/blockifier/resources/blockifier_versioned_constants_0_14_1.json#L185 const DEPLOY_SYSCALL_STEPS: u32 = 1173; // 903 = steps of 1 call contract syscall // https://github.com/starkware-libs/sequencer/blob/b29c0e8c61f7b2340209e256cf87dfe9f2c811aa/crates/blockifier/resources/blockifier_versioned_constants_0_14_1.json#L162 const CALL_CONTRACT_SYSCALL_STEPS: u32 = 903; // 90 = steps of 1 call storage read syscall // https://github.com/starkware-libs/sequencer/blob/b29c0e8c61f7b2340209e256cf87dfe9f2c811aa/crates/blockifier/resources/blockifier_versioned_constants_0_14_1.json#L406 const STORAGE_READ_SYSCALL_STEPS: u32 = 90; #[test] fn check_current_vm_step() { let contract = declare("HelloStarknet").unwrap().contract_class(); let step_a = get_current_vm_step(); let (contract_address_a, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let (contract_address_b, _) = contract.deploy(@ArrayTrait::new()).unwrap(); // Sycalls between step_a and step_b: // top call: 2 x deploy syscall // inner call: -/- let step_b = get_current_vm_step(); let expected_steps_taken = 2 * DEPLOY_SYSCALL_STEPS + 130; // 130 are steps from VM let expected_lower = expected_steps_taken + step_a - STEPS_MARGIN; let expected_upper = expected_steps_taken + step_a + STEPS_MARGIN; assert!( expected_lower <= step_b && step_b <= expected_upper, "step_b ({step_b}) not in [{expected_lower}, {expected_upper}]", ); let dispatcher_a = IHelloStarknetDispatcher { contract_address: contract_address_a }; // contract A calls `get_balance` from contract B let _balance = dispatcher_a .call_other_contract( contract_address_b.try_into().unwrap(), selector!("get_balance"), None, ); // Sycalls between step_b and step_c: // top call: 1 x call contract syscall // inner calls: 1 x storage read syscall, 1 x call contract syscall let step_c = get_current_vm_step(); let expected_steps_taken = 2 * CALL_CONTRACT_SYSCALL_STEPS + 1 * STORAGE_READ_SYSCALL_STEPS + 277; // 277 are steps from VM let expected_lower = expected_steps_taken + step_b - STEPS_MARGIN; let expected_upper = expected_steps_taken + step_b + STEPS_MARGIN; assert!( expected_lower <= step_c && step_c <= expected_upper, "step_c ({step_c}) not in [{expected_lower}, {expected_upper}]", ); } #[starknet::interface] pub trait IHelloStarknet { fn get_balance(self: @TContractState) -> felt252; fn call_other_contract( self: @TContractState, other_contract_address: felt252, selector: felt252, calldata: Option>, ) -> Span; } "# ), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/interact_with_state.rs ================================================ use crate::utils::runner::{Contract, assert_case_output_contains, assert_failed, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; #[test] fn get_contract_address_in_interact_with_state() { let test = test_case!( indoc!( r#" use snforge_std::{ ContractClassTrait, DeclareResultTrait, declare, interact_with_state, test_address, }; use starknet::{ContractAddress, get_contract_address}; #[starknet::interface] trait IEmpty { fn get_address(ref self: TContractState) -> ContractAddress; } #[test] fn test_contract_address_set_correctly() { let contract = declare("Empty").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); let (other_empty_contract, _) = contract.deploy(@array![]).unwrap(); let dispatcher = IEmptyDispatcher { contract_address }; let other_dispatcher = IEmptyDispatcher { contract_address: other_empty_contract }; let assert_eq_addresses = |a: ContractAddress, b: ContractAddress| { assert(a == b, 'Incorrect address'); }; assert_eq_addresses(dispatcher.get_address(), contract_address); assert_eq_addresses(get_contract_address(), test_address()); interact_with_state( contract_address, || { assert_eq_addresses(dispatcher.get_address(), contract_address); assert_eq_addresses(get_contract_address(), contract_address); // Make sure other contracts are not modified assert_eq_addresses(other_dispatcher.get_address(), other_empty_contract); }, ); // Make sure `get_contract_address` was modified only for the `interact_with_state` execution assert_eq_addresses(dispatcher.get_address(), contract_address); assert_eq_addresses(get_contract_address(), test_address()); } "# ), Contract::new( "Empty", indoc!( r" #[starknet::interface] trait IEmpty { fn get_address(ref self: TContractState) -> starknet::ContractAddress; } #[starknet::contract] mod Empty { #[storage] struct Storage {} #[abi(embed_v0)] impl EmptyImpl of super::IEmpty { fn get_address(ref self: ContractState) -> starknet::ContractAddress { starknet::get_contract_address() } } } " ) ) ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn raise_error_if_non_existent_address() { let test = test_case!(indoc!( r" use snforge_std::interact_with_state; #[starknet::contract] mod SingleFelt { #[storage] pub struct Storage { pub field: felt252, } } #[test] fn test_single_felt() { interact_with_state( 0x123.try_into().unwrap(), || { let mut state = SingleFelt::contract_state_for_testing(); state.field.write(1); }, ) } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "test_single_felt", "Failed to interact with contract state because no contract is deployed at address 0x0000000000000000000000000000000000000000000000000000000000000123", ); } ================================================ FILE: crates/forge/tests/integration/l1_handler_executor.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn l1_handler_execute() { let test = test_case!( indoc!( r#" #[derive(Copy, Serde, Drop)] struct L1Data { balance: felt252, token_id: u256 } #[starknet::interface] trait IBalanceToken { fn get_balance(self: @TContractState) -> felt252; fn get_token_id(self: @TContractState) -> u256; } use serde::Serde; use array::{ArrayTrait, SpanTrait}; use core::result::ResultTrait; use snforge_std::{declare, ContractClassTrait, DeclareResultTrait, L1Handler, L1HandlerTrait}; use starknet::contract_address_const; #[test] fn l1_handler_execute() { let calldata = array![0x123]; let contract = declare("l1_handler_executor").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@calldata).unwrap(); let l1_data = L1Data { balance: 42, token_id: 8888_u256, }; let mut payload: Array = ArrayTrait::new(); l1_data.serialize(ref payload); let mut l1_handler = L1HandlerTrait::new( contract_address, selector!("process_l1_message") ); l1_handler.execute(0x123, payload.span()).unwrap(); let dispatcher = IBalanceTokenDispatcher { contract_address }; assert(dispatcher.get_balance() == 42, dispatcher.get_balance()); assert(dispatcher.get_token_id() == 8888_u256, 'Invalid token id'); } #[test] fn l1_handler_execute_panicking() { let calldata = array![0x123]; let contract = declare("l1_handler_executor").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@calldata).unwrap(); let mut l1_handler = L1HandlerTrait::new( contract_address, selector!("panicking_l1_handler") ); match l1_handler.execute(0x123, array![].span()) { Result::Ok(_) => panic_with_felt252('should have panicked'), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'custom', 'Wrong 1st panic datum'); assert(*panic_data.at(1) == 'panic', 'Wrong 2nd panic datum'); }, } } #[test] fn l1_handler_function_missing() { let calldata = array![0x123]; let contract = declare("l1_handler_executor").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@calldata).unwrap(); let mut l1_handler = L1HandlerTrait::new( contract_address, selector!("this_does_not_exist") ); match l1_handler.execute(0x123, array![].span()){ Result::Ok(_) => panic_with_felt252('should have panicked'), Result::Err(_) => { // Would be nice to assert the error here once it is be possible in cairo }, } } #[test] #[should_panic] fn l1_handler_contract_missing() { let dispatcher = IBalanceTokenDispatcher { contract_address: contract_address_const::<421984739218742310>() }; dispatcher.get_balance(); let mut l1_handler = L1HandlerTrait::new( contract_address_const::<421984739218742310>(), selector!("process_l1_message") ); l1_handler.execute(0x123, array![].span()); } "# ), Contract::from_code_path( "l1_handler_executor".to_string(), Path::new("tests/data/contracts/l1_handler_execute_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/message_to_l1.rs ================================================ use crate::utils::runner::{Contract, assert_case_output_contains, assert_failed, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn spy_messages_to_l1_simple() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use starknet::{ContractAddress, EthAddress}; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, spy_messages_to_l1, MessageToL1, MessageToL1SpyAssertionsTrait }; #[starknet::interface] trait IMessageToL1Checker { fn send_message(ref self: TContractState, some_data: Array, to_address: EthAddress); } fn deploy_message_to_l1_checker() -> IMessageToL1CheckerDispatcher { let declared = declare("MessageToL1Checker").unwrap().contract_class(); let (contract_address, _) = declared.deploy(@array![]).unwrap(); IMessageToL1CheckerDispatcher { contract_address } } #[test] fn spy_messages_to_l1_simple() { let message_to_l1_checker = deploy_message_to_l1_checker(); let mut spy = spy_messages_to_l1(); message_to_l1_checker.send_message( array![123, 321, 420], 0x123.try_into().unwrap() ); spy.assert_sent( @array![ ( message_to_l1_checker.contract_address, MessageToL1 { to_address: 0x123.try_into().unwrap(), payload: array![123, 321, 420] } ) ] ); } "# ), Contract::from_code_path( "MessageToL1Checker".to_string(), Path::new("tests/data/contracts/message_to_l1_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn spy_messages_to_l1_fails() { let test = test_case!(indoc!( r" use array::ArrayTrait; use result::ResultTrait; use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, spy_messages_to_l1, MessageToL1, MessageToL1SpyAssertionsTrait }; #[test] fn assert_sent_fails() { let mut spy = spy_messages_to_l1(); spy.assert_sent( @array![ ( 0x123.try_into().unwrap(), MessageToL1 { to_address: 0x123.try_into().unwrap(), payload: array![0x123, 0x420] } ) ] ); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "assert_sent_fails", "Message with matching data and", ); assert_case_output_contains( &result, "assert_sent_fails", "receiver was not emitted from", ); } #[test] fn expect_three_messages_while_two_sent() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use starknet::{ContractAddress, EthAddress}; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, spy_messages_to_l1, MessageToL1, MessageToL1SpyAssertionsTrait }; #[starknet::interface] trait IMessageToL1Checker { fn send_message(ref self: TContractState, some_data: Array, to_address: EthAddress); } fn deploy_message_to_l1_checker() -> IMessageToL1CheckerDispatcher { let declared = declare("MessageToL1Checker").unwrap().contract_class(); let (contract_address, _) = declared.deploy(@array![]).unwrap(); IMessageToL1CheckerDispatcher { contract_address } } #[test] fn expect_three_messages_while_two_were_sent() { let message_to_l1_checker = deploy_message_to_l1_checker(); let mut spy = spy_messages_to_l1(); message_to_l1_checker.send_message( array![123, 321, 420], 0x123.try_into().unwrap() ); message_to_l1_checker.send_message( array![420, 123, 321], 0x321.try_into().unwrap() ); spy.assert_sent( @array![ ( message_to_l1_checker.contract_address, MessageToL1 { to_address: 0x123.try_into().unwrap(), payload: array![123, 321, 420] } ), ( message_to_l1_checker.contract_address, MessageToL1 { to_address: 0x321.try_into().unwrap(), payload: array![420, 123, 321] } ), ( message_to_l1_checker.contract_address, MessageToL1 { to_address: 0x456.try_into().unwrap(), payload: array![567, 8910, 111213] } ), ] ); } "# ), Contract::from_code_path( "MessageToL1Checker".to_string(), Path::new("tests/data/contracts/message_to_l1_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "expect_three_messages_while_two_were_sent", "Message with matching data and", ); assert_case_output_contains( &result, "expect_three_messages_while_two_were_sent", "receiver was not emitted", ); } #[test] fn expect_two_messages_while_three_sent() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use starknet::{ContractAddress, EthAddress}; use snforge_std::{ ContractClassTrait, DeclareResultTrait, declare, spy_messages_to_l1, MessageToL1, MessageToL1SpyAssertionsTrait }; use traits::Into; #[starknet::interface] trait IMessageToL1Checker { fn send_message(ref self: TContractState, some_data: Array, to_address: EthAddress); } fn deploy_message_to_l1_checker() -> IMessageToL1CheckerDispatcher { let declared = declare("MessageToL1Checker").unwrap().contract_class(); let (contract_address, _) = declared.deploy(@array![]).unwrap(); IMessageToL1CheckerDispatcher { contract_address } } #[test] fn expect_two_messages_while_three_sent() { let message_to_l1_checker = deploy_message_to_l1_checker(); let mut spy = spy_messages_to_l1(); message_to_l1_checker.send_message( array![123, 321, 420], 0x123.try_into().unwrap() ); message_to_l1_checker.send_message( array![420, 123, 321], 0x321.try_into().unwrap() ); message_to_l1_checker.send_message( array![567, 8910, 111213], 0x456.try_into().unwrap() ); spy.assert_sent( @array![ ( message_to_l1_checker.contract_address, MessageToL1 { to_address: 0x123.try_into().unwrap(), payload: array![123, 321, 420] } ), ( message_to_l1_checker.contract_address, MessageToL1 { to_address: 0x321.try_into().unwrap(), payload: array![420, 123, 321] } ) ] ); } "# ), Contract::from_code_path( "MessageToL1Checker".to_string(), Path::new("tests/data/contracts/message_to_l1_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn message_sent_but_wrong_data_asserted() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use starknet::{ContractAddress, EthAddress}; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, spy_messages_to_l1, MessageToL1, MessageToL1SpyAssertionsTrait }; #[starknet::interface] trait IMessageToL1Checker { fn send_message(ref self: TContractState, some_data: Array, to_address: EthAddress); } fn deploy_message_to_l1_checker() -> IMessageToL1CheckerDispatcher { let declared = declare("MessageToL1Checker").unwrap().contract_class(); let (contract_address, _) = declared.deploy(@array![]).unwrap(); IMessageToL1CheckerDispatcher { contract_address } } #[test] fn message_sent_but_wrong_data_asserted() { let message_to_l1_checker = deploy_message_to_l1_checker(); let mut spy = spy_messages_to_l1(); message_to_l1_checker.send_message( array![123, 321, 420], 0x123.try_into().unwrap() ); spy.assert_sent( @array![ ( message_to_l1_checker.contract_address, MessageToL1 { to_address: 0x123.try_into().unwrap(), payload: array![420, 321, 123] } ) ] ); } "# ), Contract::from_code_path( "MessageToL1Checker".to_string(), Path::new("tests/data/contracts/message_to_l1_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "message_sent_but_wrong_data_asserted", "Message with matching data and", ); assert_case_output_contains( &result, "message_sent_but_wrong_data_asserted", "receiver was not emitted from", ); } #[test] fn assert_not_sent_pass() { let test = test_case!( indoc!( r" use array::ArrayTrait; use starknet::{ContractAddress, EthAddress}; use snforge_std::{ declare, spy_messages_to_l1, MessageToL1, MessageToL1SpyAssertionsTrait }; #[test] fn assert_not_sent_pass() { let mut spy = spy_messages_to_l1(); spy.assert_not_sent( @array![ ( 0x123.try_into().unwrap(), MessageToL1 { to_address: 0x123.try_into().unwrap(), payload: ArrayTrait::new() } ) ] ); } " ), Contract::from_code_path( "MessageToL1Checker".to_string(), Path::new("tests/data/contracts/message_to_l1_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn assert_not_sent_fails() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use starknet::{ContractAddress, EthAddress}; use snforge_std::{ ContractClassTrait, DeclareResultTrait, declare, spy_messages_to_l1, MessageToL1, MessageToL1SpyAssertionsTrait }; #[starknet::interface] trait IMessageToL1Checker { fn send_message(ref self: TContractState, some_data: Array, to_address: EthAddress); } fn deploy_message_to_l1_checker() -> IMessageToL1CheckerDispatcher { let declared = declare("MessageToL1Checker").unwrap().contract_class(); let (contract_address, _) = declared.deploy(@array![]).unwrap(); IMessageToL1CheckerDispatcher { contract_address } } #[test] fn assert_not_sent_fails() { let message_to_l1_checker = deploy_message_to_l1_checker(); let mut spy = spy_messages_to_l1(); message_to_l1_checker.send_message( array![123, 321, 420], 0x123.try_into().unwrap() ); spy.assert_not_sent( @array![ ( message_to_l1_checker.contract_address, MessageToL1 { to_address: 0x123.try_into().unwrap(), payload: array![123, 321, 420] } ) ] ); } "# ), Contract::from_code_path( "MessageToL1Checker".to_string(), Path::new("tests/data/contracts/message_to_l1_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "assert_not_sent_fails", "Message with matching data and", ); assert_case_output_contains(&result, "assert_not_sent_fails", "receiver was sent from"); } #[test] fn test_filtering() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use starknet::{ContractAddress, EthAddress}; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, spy_messages_to_l1, MessageToL1, MessageToL1SpyAssertionsTrait, MessageToL1FilterTrait, MessageToL1SpyTrait }; fn deploy_message_to_l1_checkers() -> (IMessageToL1CheckerDispatcher, IMessageToL1CheckerDispatcher) { let declared = declare("MessageToL1Checker").unwrap().contract_class(); let (contract_address_1, _) = declared.deploy(@array![]).unwrap(); let (contract_address_2, _) = declared.deploy(@array![]).unwrap(); ( IMessageToL1CheckerDispatcher { contract_address: contract_address_1 }, IMessageToL1CheckerDispatcher { contract_address: contract_address_2 } ) } #[starknet::interface] trait IMessageToL1Checker { fn send_message(ref self: TContractState, some_data: Array, to_address: EthAddress); } #[test] fn filter_events() { let (first_dispatcher, second_dispatcher) = deploy_message_to_l1_checkers(); let first_address = first_dispatcher.contract_address; let second_address = second_dispatcher.contract_address; let mut spy = spy_messages_to_l1(); // assert(spy._message_offset == 0, 'Message offset should be 0'); TODO(#2765) first_dispatcher.send_message( array![123, 421, 420], 0x123.try_into().unwrap() ); second_dispatcher.send_message( array![123, 124, 420], 0x125.try_into().unwrap() ); let messages_from_first_address = spy.get_messages().sent_by(first_address); let messages_from_second_address = spy.get_messages().sent_by(second_address); let (from, message) = messages_from_first_address.messages.at(0); assert!(from == @first_address, "Sent from wrong address"); assert!(message.payload.len() == 3, "There should be 3 items in the data"); assert!(*message.payload.at(1) == 421, "Expected 421 in payload"); let (from, message) = messages_from_second_address.messages.at(0); assert!(from == @second_address, "Sent from wrong address"); assert!(message.payload.len() == 3, "There should be 3 items in the data"); assert!(*message.payload.at(1) == 124, "Expected 124 in payload"); } "#, ), Contract::from_code_path( "MessageToL1Checker".to_string(), Path::new("tests/data/contracts/message_to_l1_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/meta_tx_v0.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn check_meta_tx_v0_syscall_work_without_cheats() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IMetaTxV0Test { fn execute_meta_tx_v0( ref self: TContractState, target: starknet::ContractAddress, signature: Span, ) -> felt252; } #[test] fn test_meta_tx_v0_verify_tx_context_modification() { let checker_contract = declare("SimpleCheckerMetaTxV0").unwrap().contract_class(); let (checker_address, _) = checker_contract.deploy(@ArrayTrait::new()).unwrap(); let meta_contract = declare("MetaTxV0Test").unwrap().contract_class(); let (meta_address, _) = meta_contract.deploy(@ArrayTrait::new()).unwrap(); let meta_dispatcher = IMetaTxV0TestDispatcher { contract_address: meta_address }; let mut signature = ArrayTrait::new(); let result = meta_dispatcher.execute_meta_tx_v0(checker_address, signature.span()); assert(result == 1234567890, 'Result should be 1234567890'); } "# ), Contract::from_code_path( "SimpleCheckerMetaTxV0".to_string(), Path::new("tests/data/contracts/meta_tx_v0_checkers.cairo"), ) .unwrap(), Contract::from_code_path( "MetaTxV0Test".to_string(), Path::new("tests/data/contracts/meta_tx_v0_test.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); } #[test] fn meta_tx_v0_with_cheat_caller_address() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_caller_address, stop_cheat_caller_address }; #[starknet::interface] trait IMetaTxV0Test { fn execute_meta_tx_v0( ref self: TContractState, target: starknet::ContractAddress, signature: Span, ) -> felt252; } #[test] fn test_meta_tx_v0_with_cheat_caller_address() { let checker_contract = declare("CheatCallerAddressCheckerMetaTxV0").unwrap().contract_class(); let (checker_address, _) = checker_contract.deploy(@ArrayTrait::new()).unwrap(); let meta_contract = declare("MetaTxV0Test").unwrap().contract_class(); let (meta_address, _) = meta_contract.deploy(@ArrayTrait::new()).unwrap(); let meta_dispatcher = IMetaTxV0TestDispatcher { contract_address: meta_address }; let signature = ArrayTrait::new(); let old_caller = meta_dispatcher.execute_meta_tx_v0(checker_address, signature.span()); let cheated_address: ContractAddress = 123.try_into().unwrap(); start_cheat_caller_address(checker_address, cheated_address); let meta_result = meta_dispatcher.execute_meta_tx_v0(checker_address, signature.span()); assert(meta_result == cheated_address.into(), 'Should see cheated addr'); stop_cheat_caller_address(checker_address); let meta_result = meta_dispatcher.execute_meta_tx_v0(checker_address, signature.span()); assert(meta_result == old_caller, 'Caller should revert back'); } "# ), Contract::from_code_path( "CheatCallerAddressCheckerMetaTxV0".to_string(), Path::new("tests/data/contracts/meta_tx_v0_checkers.cairo"), ) .unwrap(), Contract::from_code_path( "MetaTxV0Test".to_string(), Path::new("tests/data/contracts/meta_tx_v0_test.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[cfg_attr( feature = "cairo-native", ignore = "Cheats in `meta_tx_v0` are not supported on `cairo-native`" )] #[test] fn meta_tx_v0_with_cheat_block_hash() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_block_hash, stop_cheat_block_hash }; #[starknet::interface] trait IMetaTxV0Test { fn execute_meta_tx_v0_get_block_hash( ref self: TContractState, target: starknet::ContractAddress, block_number: u64, signature: Span, ) -> felt252; } #[test] fn test_meta_tx_v0_with_cheat_block_hash() { let checker_contract = declare("CheatBlockHashCheckerMetaTxV0").unwrap().contract_class(); let (checker_address, _) = checker_contract.deploy(@ArrayTrait::new()).unwrap(); let meta_contract = declare("MetaTxV0Test").unwrap().contract_class(); let (meta_address, _) = meta_contract.deploy(@ArrayTrait::new()).unwrap(); let meta_dispatcher = IMetaTxV0TestDispatcher { contract_address: meta_address }; let block_number = 100; let signature = ArrayTrait::new(); let old_block_hash = meta_dispatcher.execute_meta_tx_v0_get_block_hash(checker_address, block_number, signature.span()); let cheated_hash = 555; start_cheat_block_hash(checker_address, block_number, cheated_hash); let meta_result = meta_dispatcher.execute_meta_tx_v0_get_block_hash(checker_address, block_number, signature.span()); assert(meta_result == cheated_hash, 'Should see cheated hash'); stop_cheat_block_hash(checker_address, block_number); let new_block_hash = meta_dispatcher.execute_meta_tx_v0_get_block_hash(checker_address, block_number, signature.span()); assert(new_block_hash == old_block_hash, 'Block hash should revert back'); } "# ), Contract::from_code_path( "CheatBlockHashCheckerMetaTxV0".to_string(), Path::new("tests/data/contracts/meta_tx_v0_checkers.cairo"), ) .unwrap(), Contract::from_code_path( "MetaTxV0Test".to_string(), Path::new("tests/data/contracts/meta_tx_v0_test.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); } #[test] fn meta_tx_v0_verify_tx_context_modification() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IMetaTxV0Test { fn execute_meta_tx_v0( ref self: TContractState, target: starknet::ContractAddress, signature: Span, ) -> felt252; } #[starknet::interface] trait ITxInfoCheckerMetaTxV0 { fn __execute__(ref self: TContractState) -> felt252; } #[test] fn test_meta_tx_v0_verify_tx_context_modification() { let checker_contract = declare("TxInfoCheckerMetaTxV0").unwrap().contract_class(); let (checker_address, _) = checker_contract.deploy(@ArrayTrait::new()).unwrap(); let checker_dispatcher = ITxInfoCheckerMetaTxV0Dispatcher { contract_address: checker_address }; let meta_contract = declare("MetaTxV0Test").unwrap().contract_class(); let (meta_address, _) = meta_contract.deploy(@ArrayTrait::new()).unwrap(); let meta_dispatcher = IMetaTxV0TestDispatcher { contract_address: meta_address }; let direct_version = checker_dispatcher.__execute__(); let mut signature = ArrayTrait::new(); let meta_version = meta_dispatcher.execute_meta_tx_v0(checker_address, signature.span()); assert(meta_version == 0, 'Meta tx version should be 0'); assert(direct_version == 3, 'Direct call version should be 3'); } "# ), Contract::from_code_path( "TxInfoCheckerMetaTxV0".to_string(), Path::new("tests/data/contracts/meta_tx_v0_checkers.cairo"), ) .unwrap(), Contract::from_code_path( "MetaTxV0Test".to_string(), Path::new("tests/data/contracts/meta_tx_v0_test.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/mock_call.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn mock_call_simple() { let test = test_case!( indoc!( r#" use result::ResultTrait; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_mock_call, stop_mock_call }; #[starknet::interface] trait IMockChecker { fn get_thing(ref self: TContractState) -> felt252; } #[test] fn mock_call_simple() { let calldata = array![420]; let contract = declare("MockChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@calldata).unwrap(); let dispatcher = IMockCheckerDispatcher { contract_address }; let mock_ret_data = 421; start_mock_call(contract_address, selector!("get_thing"), mock_ret_data); let thing = dispatcher.get_thing(); assert(thing == 421, 'Incorrect thing'); stop_mock_call(contract_address, selector!("get_thing")); let thing = dispatcher.get_thing(); assert(thing == 420, 'Incorrect thing'); } #[test] fn mock_call_simple_before_dispatcher_created() { let calldata = array![420]; let contract = declare("MockChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@calldata).unwrap(); let mock_ret_data = 421; start_mock_call(contract_address, selector!("get_thing"), mock_ret_data); let dispatcher = IMockCheckerDispatcher { contract_address }; let thing = dispatcher.get_thing(); assert(thing == 421, 'Incorrect thing'); } "# ), Contract::from_code_path( "MockChecker".to_string(), Path::new("tests/data/contracts/mock_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn mock_call_complex_types() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use serde::Serde; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_mock_call }; #[starknet::interface] trait IMockChecker { fn get_struct_thing(ref self: TContractState) -> StructThing; fn get_arr_thing(ref self: TContractState) -> Array; } #[derive(Serde, Drop)] struct StructThing { item_one: felt252, item_two: felt252, } #[test] fn start_mock_call_return_struct() { let calldata = array![420]; let contract = declare("MockChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@calldata).unwrap(); let dispatcher = IMockCheckerDispatcher { contract_address }; let mock_ret_data = StructThing {item_one: 412, item_two: 421}; start_mock_call(contract_address, selector!("get_struct_thing"), mock_ret_data); let thing: StructThing = dispatcher.get_struct_thing(); assert(thing.item_one == 412, 'thing.item_one'); assert(thing.item_two == 421, 'thing.item_two'); } #[test] fn start_mock_call_return_arr() { let calldata = array![420]; let contract = declare("MockChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@calldata).unwrap(); let dispatcher = IMockCheckerDispatcher { contract_address }; let mock_ret_data = array![ StructThing {item_one: 112, item_two: 121}, StructThing {item_one: 412, item_two: 421} ]; start_mock_call(contract_address, selector!("get_arr_thing"), mock_ret_data); let things: Array = dispatcher.get_arr_thing(); let thing = things.at(0); assert(*thing.item_one == 112, 'thing1.item_one'); assert(*thing.item_two == 121, 'thing1.item_two'); let thing = things.at(1); assert(*thing.item_one == 412, 'thing2.item_one'); assert(*thing.item_two == 421, 'thing2.item_two'); } "# ), Contract::from_code_path( "MockChecker".to_string(), Path::new("tests/data/contracts/mock_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn mock_calls() { let test = test_case!( indoc!( r#" use result::ResultTrait; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, mock_call, start_mock_call, stop_mock_call }; #[starknet::interface] trait IMockChecker { fn get_thing(ref self: TContractState) -> felt252; } #[test] fn mock_call_one() { let calldata = array![420]; let contract = declare("MockChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@calldata).unwrap(); let dispatcher = IMockCheckerDispatcher { contract_address }; let mock_ret_data = 421; mock_call(contract_address, selector!("get_thing"), mock_ret_data, 1); let thing = dispatcher.get_thing(); assert_eq!(thing, 421); let thing = dispatcher.get_thing(); assert_eq!(thing, 420); } #[test] fn mock_call_twice() { let calldata = array![420]; let contract = declare("MockChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@calldata).unwrap(); let dispatcher = IMockCheckerDispatcher { contract_address }; let mock_ret_data = 421; mock_call(contract_address, selector!("get_thing"), mock_ret_data, 2); let thing = dispatcher.get_thing(); assert_eq!(thing, 421); let thing = dispatcher.get_thing(); assert_eq!(thing, 421); let thing = dispatcher.get_thing(); assert_eq!(thing, 420); } "# ), Contract::from_code_path( "MockChecker".to_string(), Path::new("tests/data/contracts/mock_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/mod.rs ================================================ mod available_gas; mod builtins; mod cheat_block_hash; mod cheat_block_number; mod cheat_block_timestamp; mod cheat_caller_address; mod cheat_execution_info; mod cheat_fork; mod cheat_sequencer_address; mod config; mod declare; mod deploy; mod deploy_at; mod dict; mod dispatchers; mod env; mod fuzzing; mod gas; mod generate_random_felt; mod get_available_gas; mod get_class_hash; mod get_current_vm_step; mod interact_with_state; mod l1_handler_executor; mod message_to_l1; mod meta_tx_v0; mod mock_call; mod precalculate_address; mod pure_cairo; mod replace_bytecode; mod resources; mod reverts; mod runtime; mod set_balance; mod setup_fork; mod should_panic; mod signing; mod spy_events; mod store_load; mod syscalls; mod test_state; mod too_many_events; mod trace; ================================================ FILE: crates/forge/tests/integration/precalculate_address.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn precalculate_address() { let test = test_case!( indoc!( r#" use result::ResultTrait; use snforge_std::{ declare, ContractClass, ContractClassTrait, DeclareResultTrait }; use array::ArrayTrait; use traits::Into; use traits::TryInto; use starknet::ContractAddressIntoFelt252; #[test] fn precalculate_address() { let mut calldata = ArrayTrait::new(); let contract = declare("HelloStarknet").unwrap().contract_class(); let contract_address_pre = contract.precalculate_address(@calldata); let (contract_address, _) = contract.deploy(@calldata).unwrap(); let contract_address_pre2 = contract.precalculate_address(@calldata); let (contract_address2, _) = contract.deploy(@calldata).unwrap(); assert(contract_address_pre == contract_address, 'must be eq'); assert(contract_address_pre2 == contract_address2, 'must be eq'); assert(contract_address != contract_address2, 'must be different'); } "# ), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/pure_cairo.rs ================================================ use crate::utils::running_tests::run_test_case; use crate::utils::{ runner::{assert_failed, assert_passed}, test_case, }; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; #[test] fn simple() { let test = test_case!(indoc!( r"#[test] fn simple() { assert(2 == 2, '2 == 2'); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn failing() { let test = test_case!(indoc!( r"#[test] fn failing() { assert(2 == 3, '2 == 3'); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); } ================================================ FILE: crates/forge/tests/integration/replace_bytecode.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn override_entrypoint() { let test = test_case!( indoc!( r#" use core::clone::Clone; use snforge_std::{declare, replace_bytecode, ContractClassTrait, DeclareResultTrait}; #[starknet::interface] trait IReplaceBytecode { fn get(self: @TContractState) -> felt252; } #[test] fn override_entrypoint() { let contract = declare("ReplaceBytecodeA").unwrap().contract_class(); let contract_b_class = declare("ReplaceBytecodeB").unwrap().contract_class().class_hash.clone(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IReplaceBytecodeDispatcher { contract_address }; assert(dispatcher.get() == 2137, ''); replace_bytecode(contract_address, contract_b_class); assert(dispatcher.get() == 420, ''); } "# ), Contract::from_code_path( "ReplaceBytecodeA", Path::new("tests/data/contracts/two_implementations.cairo"), ) .unwrap(), Contract::from_code_path( "ReplaceBytecodeB", Path::new("tests/data/contracts/two_implementations.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn libcall_in_cheated() { let test = test_case!( indoc!( r#" use core::clone::Clone; use snforge_std::{declare, replace_bytecode, ContractClassTrait, DeclareResultTrait}; #[starknet::interface] trait IReplaceBytecode { fn libcall(self: @TContractState, class_hash: starknet::ClassHash) -> felt252; } #[starknet::interface] trait ILib { fn get(self: @TContractState) -> felt252; } #[test] fn override_entrypoint() { let contract = declare("ReplaceBytecodeA").unwrap().contract_class(); let contract_b_class = declare("ReplaceBytecodeB").unwrap().contract_class().clone().class_hash; let lib = declare("Lib").unwrap().contract_class().clone().class_hash; let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IReplaceBytecodeDispatcher { contract_address }; assert(dispatcher.libcall(lib) == 123456789, ''); replace_bytecode(contract_address, contract_b_class); assert(dispatcher.libcall(lib) == 123456789, ''); } "# ), Contract::from_code_path( "Lib", Path::new("tests/data/contracts/two_implementations.cairo"), ) .unwrap(), Contract::from_code_path( "ReplaceBytecodeA", Path::new("tests/data/contracts/two_implementations.cairo"), ) .unwrap(), Contract::from_code_path( "ReplaceBytecodeB", Path::new("tests/data/contracts/two_implementations.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn contract_not_deployed() { let test = test_case!( indoc!( r#" use core::clone::Clone; use snforge_std::{declare, replace_bytecode, ReplaceBytecodeError, DeclareResultTrait}; use starknet::{ClassHash, contract_address_const}; #[test] fn contract_not_deployed() { let class_hash = declare("ReplaceBytecodeA").unwrap().contract_class().clone().class_hash; let non_existing_contract_address = contract_address_const::<0x2>(); match replace_bytecode(non_existing_contract_address, class_hash) { Result::Ok(()) => { panic!("Wrong return type"); }, Result::Err(err) => { assert(err == ReplaceBytecodeError::ContractNotDeployed(()), 'Wrong error type'); } } } "# ), Contract::from_code_path( "ReplaceBytecodeA", Path::new("tests/data/contracts/two_implementations.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn class_hash_not_declared() { let test = test_case!( indoc!( r#" use snforge_std::{declare, ContractClassTrait, replace_bytecode, ReplaceBytecodeError, DeclareResultTrait}; use starknet::{ClassHash, contract_address_const}; #[test] fn class_hash_not_declared() { let contract = declare("ReplaceBytecodeA").unwrap().contract_class(); let undeclared_class_hash: ClassHash = 0x5.try_into().unwrap(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); match replace_bytecode(contract_address, undeclared_class_hash) { Result::Ok(()) => { panic!("Wrong return type"); }, Result::Err(err) => { assert(err == ReplaceBytecodeError::UndeclaredClassHash(()), 'Wrong error type'); } } } "# ), Contract::from_code_path( "ReplaceBytecodeA", Path::new("tests/data/contracts/two_implementations.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/resources.rs ================================================ use crate::utils::runner::{Contract, assert_builtin, assert_passed, assert_syscall}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use blockifier::execution::syscalls::vm_syscall_utils::SyscallSelector; use cairo_vm::types::builtin_name::BuiltinName; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn builtins_count() { let test = test_case!(indoc!( r" #[test] fn empty() {} #[test] fn range_check() { assert((1_u8 + 1_u8) >= 1_u8, 'error message'); } #[test] fn bitwise() { let _bitwise = 1_u8 & 1_u8; assert(1 == 1, 'error message'); } #[test] fn pedersen() { core::pedersen::pedersen(1, 2); assert(1 == 1, 'error message'); } #[test] fn poseidon() { core::poseidon::hades_permutation(0, 0, 0); assert(1 == 1, 'error message'); } #[test] fn ec_op() { let ec_point = core::ec::EcPointTrait::new_from_x(1).unwrap(); core::ec::EcPointTrait::mul(ec_point, 2); assert(1 == 1, 'error message'); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // No ECDSA and Keccak builtins assert_builtin(&result, "empty", BuiltinName::range_check, 4); assert_builtin(&result, "range_check", BuiltinName::range_check, 4); assert_builtin(&result, "bitwise", BuiltinName::bitwise, 1); assert_builtin(&result, "pedersen", BuiltinName::pedersen, 1); assert_builtin(&result, "poseidon", BuiltinName::poseidon, 1); assert_builtin(&result, "ec_op", BuiltinName::ec_op, 1); } #[test] fn syscalls_count() { let test = test_case!( indoc!( r#" use core::clone::Clone; use starknet::syscalls::{ call_contract_syscall, keccak_syscall, deploy_syscall, get_block_hash_syscall, emit_event_syscall, send_message_to_l1_syscall, get_execution_info_syscall, get_execution_info_v2_syscall, get_execution_info_v3_syscall, SyscallResult }; use starknet::SyscallResultTrait; use snforge_std::{declare, ContractClass, ContractClassTrait, DeclareResultTrait}; #[test] fn keccak() { let input = array![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]; keccak_syscall(input.span()).unwrap_syscall(); } #[test] fn deploy() { let contract = declare("HelloStarknet").unwrap().contract_class().clone(); deploy_syscall(contract.class_hash, 0, array![].span(), false).unwrap_syscall(); } #[test] fn storage_read() { let contract = declare("HelloStarknet").unwrap().contract_class().clone(); let (address, _) = deploy_syscall(contract.class_hash, 0, array![].span(), false) .unwrap_syscall(); call_contract_syscall(address, selector!("get_balance"), array![].span()).unwrap_syscall(); } #[test] fn storage_write() { let contract = declare("HelloStarknet").unwrap().contract_class().clone(); let (address, _) = deploy_syscall(contract.class_hash, 0, array![].span(), false) .unwrap_syscall(); call_contract_syscall(address, selector!("increase_balance"), array![123].span()) .unwrap_syscall(); } #[test] fn get_block_hash() { get_block_hash_syscall(1).unwrap_syscall(); } #[test] fn get_execution_info() { get_execution_info_syscall().unwrap_syscall(); } #[test] fn get_execution_info_v2() { get_execution_info_v2_syscall().unwrap_syscall(); } #[test] fn get_execution_info_v3() { get_execution_info_v3_syscall().unwrap_syscall(); } #[test] fn send_message_to_l1() { send_message_to_l1_syscall(1, array![1].span()).unwrap_syscall(); } #[test] fn emit_event() { emit_event_syscall(array![1].span(), array![2].span()).unwrap_syscall(); } "# ), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); assert_syscall(&result, "keccak", SyscallSelector::Keccak, 1); assert_syscall(&result, "deploy", SyscallSelector::Deploy, 1); assert_syscall(&result, "storage_read", SyscallSelector::StorageRead, 1); assert_syscall(&result, "storage_write", SyscallSelector::StorageWrite, 1); assert_syscall(&result, "get_block_hash", SyscallSelector::GetBlockHash, 1); assert_syscall( &result, "get_execution_info", SyscallSelector::GetExecutionInfo, 1, ); assert_syscall( &result, "get_execution_info_v2", SyscallSelector::GetExecutionInfo, 1, ); assert_syscall( &result, "get_execution_info_v3", SyscallSelector::GetExecutionInfo, 1, ); assert_syscall( &result, "send_message_to_l1", SyscallSelector::SendMessageToL1, 1, ); assert_syscall(&result, "emit_event", SyscallSelector::EmitEvent, 1); } #[test] fn accumulate_syscalls() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IGasChecker { fn change_balance(ref self: TContractState, new_balance: u64); } #[test] fn single_write() { let contract = declare("GasChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IGasCheckerDispatcher { contract_address }; dispatcher.change_balance(1); } #[test] fn double_write() { let contract = declare("GasChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IGasCheckerDispatcher { contract_address }; dispatcher.change_balance(1); dispatcher.change_balance(2); } "# ), Contract::from_code_path( "GasChecker".to_string(), Path::new("tests/data/contracts/gas_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); assert_syscall(&result, "single_write", SyscallSelector::StorageWrite, 1); assert_syscall(&result, "double_write", SyscallSelector::StorageWrite, 2); } #[test] fn estimation_includes_os_resources() { let test = test_case!(indoc!( " use starknet::{SyscallResultTrait, StorageAddress}; #[test] fn syscall_storage_write() { let storage_address: StorageAddress = 10.try_into().unwrap(); starknet::storage_write_syscall(0, storage_address, 10).unwrap_syscall(); starknet::storage_write_syscall(0, storage_address, 10).unwrap_syscall(); starknet::storage_write_syscall(0, storage_address, 10).unwrap_syscall(); assert(1 == 1, 'haha'); } #[test] fn syscall_storage_write_baseline() { let _storage_address: StorageAddress = 10.try_into().unwrap(); // starknet::storage_write_syscall(0, storage_address, 10).unwrap_syscall(); // starknet::storage_write_syscall(0, storage_address, 10).unwrap_syscall(); // starknet::storage_write_syscall(0, storage_address, 10).unwrap_syscall(); assert(1 == 1, 'haha'); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); // Cost of storage write in builtins is 1 range check and 89 steps // Steps are pretty hard to verify so this test is based on range check diff assert_builtin( &result, "syscall_storage_write", BuiltinName::range_check, 10, ); assert_builtin( &result, "syscall_storage_write_baseline", BuiltinName::range_check, 7, ); } #[test] fn deploy_with_constructor_calldata() { let test = test_case!( indoc!( r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; use starknet::syscalls::deploy_syscall; #[test] fn deploy_with_syscall() { let contract = declare("DeployChecker").unwrap().contract_class().clone(); let (address, _) = deploy_syscall(contract.class_hash, 0, array![100].span(), false).unwrap(); assert(address != 0.try_into().unwrap(), 'Incorrect deployed address'); } "# ), Contract::from_code_path( "DeployChecker".to_string(), Path::new("tests/data/contracts/deploy_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); assert_syscall(&result, "deploy_with_syscall", SyscallSelector::Deploy, 1); // As of Starknet v0.13.5, deploy syscall uses constant 7 pedersen builtins + 1 additional as calldata factor in this case // https://github.com/starkware-libs/sequencer/blob/b9d99e118ad23664cda984505414d49c3cb6b19f/crates/blockifier/resources/blockifier_versioned_constants_0_13_5.json#L166 assert_builtin(&result, "deploy_with_syscall", BuiltinName::pedersen, 8); } ================================================ FILE: crates/forge/tests/integration/reverts.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; #[test] fn storage_is_reverted_in_test_call() { let test = test_case!( indoc! { r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IContract { fn read_storage(self: @TContractState) -> felt252; fn write_storage(ref self: TContractState, value: felt252); fn write_storage_and_panic(ref self: TContractState, value: felt252); } #[test] #[feature("safe_dispatcher")] fn test_call_storage_is_reverted() { let contract = declare("Contract").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); let dispatcher = IContractSafeDispatcher { contract_address }; dispatcher.write_storage(5).unwrap(); // Make sure storage value was written correctly let storage = dispatcher.read_storage().unwrap(); assert_eq!(storage, 5, "Incorrect storage value"); // Call storage modification and handle panic match dispatcher.write_storage_and_panic(11) { Result::Ok(_) => panic!("Should have panicked"), Result::Err(_) => { // handled }, } // Check storage change was reverted let storage = dispatcher.read_storage().unwrap(); assert_eq!(storage, 5, "Storage was not reverted"); } "# }, Contract::from_code_path("Contract", "tests/data/contracts/reverts_contract.cairo") .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); } #[test] fn storage_is_reverted_in_proxy_call() { let test = test_case!( indoc! { r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait IProxy { fn read_storage(self: @TContractState) -> felt252; fn write_storage(ref self: TContractState, value: felt252); fn write_storage_and_panic(ref self: TContractState, value: felt252); } #[test] #[feature("safe_dispatcher")] fn test_call_storage_is_reverted() { let contract = declare("Contract").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); let contract_proxy = declare("Proxy").unwrap().contract_class(); let mut calldata = array![]; contract_address.serialize(ref calldata); let (contract_address_proxy, _) = contract_proxy.deploy(@calldata).unwrap(); let dispatcher = IProxySafeDispatcher { contract_address: contract_address_proxy }; dispatcher.write_storage(5).unwrap(); // Make sure storage value was written correctly let storage = dispatcher.read_storage().unwrap(); assert_eq!(storage, 5, "Incorrect storage value"); // Try modifying storage and handle panic match dispatcher.write_storage_and_panic(1) { Result::Ok(_) => panic!("Should have panicked"), Result::Err(_panic_data) => { // handled }, } // Check storage change was reverted let storage = dispatcher.read_storage().unwrap(); assert_eq!(storage, 5, "Storage was not reverted"); } "# }, Contract::from_code_path("Proxy", "tests/data/contracts/reverts_proxy.cairo").unwrap(), Contract::from_code_path("Contract", "tests/data/contracts/reverts_contract.cairo") .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); } #[test] fn storage_is_reverted_in_safe_proxy_call() { let test = test_case!( indoc! { r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait ISafeProxy { fn read_storage(self: @TContractState) -> felt252; fn write_storage(ref self: TContractState, value: felt252); fn call_write_storage_and_handle_panic(ref self: TContractState, value: felt252); } #[test] #[feature("safe_dispatcher")] fn test_call_storage_is_reverted() { let contract = declare("Contract").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); let contract_proxy = declare("Proxy").unwrap().contract_class(); let mut calldata = array![]; contract_address.serialize(ref calldata); let (contract_address_proxy, _) = contract_proxy.deploy(@calldata).unwrap(); let dispatcher = ISafeProxyDispatcher { contract_address: contract_address_proxy }; dispatcher.write_storage(5); // Make sure storage value was written correctly let storage = dispatcher.read_storage(); assert_eq!(storage, 5, "Incorrect storage value"); dispatcher.call_write_storage_and_handle_panic(123); // Check storage change was reverted let storage = dispatcher.read_storage(); assert_eq!(storage, 5, "Storage was not reverted"); } "# }, Contract::from_code_path("Proxy", "tests/data/contracts/reverts_proxy.cairo").unwrap(), Contract::from_code_path("Contract", "tests/data/contracts/reverts_contract.cairo") .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); } #[test] fn storage_is_reverted_in_inner_call() { let test = test_case!( indoc! { r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; #[starknet::interface] trait ICaller { fn call(ref self: TContractState); } #[test] #[feature("safe_dispatcher")] fn test_call_storage_is_reverted() { let contract = declare("Contract").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); let contract_proxy = declare("Caller").unwrap().contract_class(); let mut calldata = array![]; contract_address.serialize(ref calldata); let (contract_address_caller, _) = contract_proxy.deploy(@calldata).unwrap(); let dispatcher = ICallerDispatcher { contract_address: contract_address_caller }; dispatcher.call(); } "# }, Contract::from_code_path("Caller", "tests/data/contracts/reverts_caller.cairo").unwrap(), Contract::from_code_path("Contract", "tests/data/contracts/reverts_contract.cairo") .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); } #[test] fn storage_is_reverted_in_library_call() { let test = test_case!( indoc! { r#" use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait }; use starknet::ContractAddress; #[starknet::interface] trait ILibraryProxy { fn library_read_storage(self: @TContractState, address: ContractAddress) -> felt252; fn library_write_storage(self: @TContractState, address: ContractAddress, value: felt252); fn library_write_storage_and_panic(self: @TContractState, address: ContractAddress, value: felt252); } #[test] #[feature("safe_dispatcher")] fn test_library_call_storage_is_reverted() { let contract = declare("Contract").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); let contract_proxy = declare("Proxy").unwrap().contract_class(); let dispatcher = ILibraryProxySafeLibraryDispatcher { class_hash: *contract_proxy.class_hash }; dispatcher.library_write_storage(contract_address, 5).unwrap(); // Make sure storage value was written correctly let storage = dispatcher.library_read_storage(contract_address).unwrap(); assert_eq!(storage, 5, "Incorrect storage value"); // Call storage modification and handle panic match dispatcher.library_write_storage_and_panic(contract_address, 11) { Result::Ok(_) => panic!("Should have panicked"), Result::Err(_) => { // handled }, } // Check storage change was reverted let storage = dispatcher.library_read_storage(contract_address).unwrap(); assert_eq!(storage, 5, "Storage was not reverted"); } "# }, Contract::from_code_path("Contract", "tests/data/contracts/reverts_contract.cairo") .unwrap(), Contract::from_code_path("Proxy", "tests/data/contracts/reverts_proxy.cairo").unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/runtime.rs ================================================ use crate::utils::runner::{assert_case_output_contains, assert_failed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; #[test] fn missing_cheatcode_error() { let test = test_case!(indoc!( r" use starknet::testing::cheatcode; use array::ArrayTrait; #[test] fn missing_cheatcode_error() { cheatcode::<'not_existing123'>(array![1, 2].span()); assert(1==1, 'nothing') } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "missing_cheatcode_error", indoc!( r" Function `not_existing123` is not supported in this runtime Check if used library (`snforge_std` or `sncast_std`) is compatible with used binary, probably one of them is not updated " ), ); } #[test] fn cairo_test_cheatcode_error() { let test = test_case!(indoc!( r" use starknet::testing::cheatcode; use array::ArrayTrait; #[test] fn missing_cheatcode_error() { cheatcode::<'set_version'>(array![1, 2].span()); assert(1==1, 'nothing') } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "missing_cheatcode_error", indoc!( r" Function `set_version` is not supported in this runtime Check if functions are imported from `snforge_std`/`sncast_std` NOT from `starknet::testing` " ), ); } #[test] #[ignore = "TODO(#2765)"] fn cheatcode_invalid_args() { let test = test_case!(indoc!( r" use starknet::testing::cheatcode; use snforge_std::_cheatcode::handle_cheatcode; #[test] fn cheatcode_invalid_args() { handle_cheatcode(cheatcode::<'replace_bytecode'>(array![].span())); assert(true,''); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_case_output_contains( &result, "cheatcode_invalid_args", indoc!( r#" "Reading from buffer failed, this can be caused by calling starknet::testing::cheatcode with invalid arguments. Probably `snforge_std`/`sncast_std` version is incompatible, check above for incompatibility warning. " "# ), ); assert_failed(&result); } ================================================ FILE: crates/forge/tests/integration/set_balance.rs ================================================ use crate::utils::runner::{Contract, assert_case_output_contains, assert_failed, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case as utils_test_case; use cheatnet::predeployment::erc20::eth::ETH_CONTRACT_ADDRESS; use cheatnet::predeployment::erc20::strk::STRK_CONTRACT_ADDRESS; use forge_runner::forge_config::ForgeTrackedResource; use indoc::{formatdoc, indoc}; use shared::test_utils::node_url::node_rpc_url; use std::path::Path; use test_case::test_case; #[test_case("STRK";"strk")] #[test_case("ETH";"eth")] fn test_set_balance_predefined_token(token: &str) { let test = utils_test_case!( formatdoc!( r#" use snforge_std::{{set_balance, Token, TokenTrait}}; use starknet::{{ContractAddress, syscalls, SyscallResultTrait}}; fn get_balance(contract_address: ContractAddress, token: Token) -> Span {{ let mut calldata: Array = array![contract_address.into()]; let balance = syscalls::call_contract_syscall( token.contract_address(), selector!("balance_of"), calldata.span(), ) .unwrap_syscall(); balance }} #[test] fn test_set_balance_predefined_token() {{ let contract_address: ContractAddress = 0x123.try_into().unwrap(); let balance_before = get_balance(contract_address, Token::{token}); assert_eq!(balance_before, array![0, 0].span(), "Balance should be 0"); set_balance(contract_address, 10, Token::{token}); let balance_after = get_balance(contract_address, Token::{token}); assert_eq!(balance_after, array![10, 0].span(), "Balance should be 10"); }} "# ) .as_str(), Contract::from_code_path( "HelloStarknet", Path::new("tests/data/simple_package/src/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn test_set_balance_custom_token() { let test = utils_test_case!( indoc!( r#" use snforge_std::{declare, set_balance, Token, TokenTrait, CustomToken, ContractClassTrait, DeclareResultTrait,}; use starknet::{ContractAddress, syscalls, SyscallResultTrait}; fn deploy_contract( name: ByteArray, constructor_calldata: Array, ) -> ContractAddress { let contract = declare(name).unwrap().contract_class(); let (contract_address, _) = contract.deploy(@constructor_calldata).unwrap(); contract_address } fn get_balance(contract_address: ContractAddress, token: Token) -> Span { let mut calldata: Array = array![contract_address.into()]; let balance = syscalls::call_contract_syscall( token.contract_address(), selector!("balance_of"), calldata.span(), ) .unwrap_syscall(); balance } #[test] fn test_set_balance_custom_token() { let contract_address: ContractAddress = 0x123.try_into().unwrap(); let constructor_calldata: Array = array![ 'CustomToken'.into(), 'CT'.into(), 18.into(), 1_000_000_000.into(), 0.into(), 123.into(), ]; let token_address = deploy_contract("ERC20", constructor_calldata); let custom_token = Token::Custom( CustomToken { contract_address: token_address, balances_variable_selector: selector!("balances"), }, ); let balance_before = get_balance(contract_address, custom_token); assert_eq!(balance_before, array![0, 0].span(), "Balance should be 0"); set_balance(contract_address, 10, custom_token); let balance_after = get_balance(contract_address, custom_token); assert_eq!(balance_after, array![10, 0].span(), "Balance should be 10"); } "# ), Contract::from_code_path("ERC20", Path::new("tests/data/contracts/erc20.cairo"),).unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test_case("STRK";"strk")] #[test_case("ETH";"eth")] fn test_set_balance_big_amount(token: &str) { let test = utils_test_case!( format!( r#" use core::num::traits::Pow; use snforge_std::{{set_balance, Token, TokenTrait}}; use starknet::{{ContractAddress, syscalls, SyscallResultTrait}}; fn get_balance(contract_address: ContractAddress, token: Token) -> Span {{ let mut calldata: Array = array![contract_address.into()]; let balance = syscalls::call_contract_syscall( token.contract_address(), selector!("balance_of"), calldata.span(), ) .unwrap_syscall(); balance }} #[test] fn test_set_balance_big_amount() {{ let contract_address: ContractAddress = 0x123.try_into().unwrap(); let balance_before = get_balance(contract_address, Token::{token}); assert_eq!(balance_before, array![0, 0].span(), "Balance should be 0"); set_balance(contract_address, (10.pow(50_u32)).try_into().unwrap(), Token::{token}); let balance_after = get_balance(contract_address, Token::{token}); assert_eq!( balance_after, array![194599656488044247630319707454198251520, 293873587705].span(), "Balance should should be 10^50", ); }} "# ) .as_str(), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/simple_package/src/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test_case("STRK", [109_394_843_313_476_728_397_u128, 0];"strk")] #[test_case("ETH", [24_969_862_322_663_205, 0];"eth")] fn test_set_balance_with_fork(token: &str, balance_before: [u128; 2]) { let balance_before_low = balance_before[0]; let balance_before_high = balance_before[1]; let test = utils_test_case!( formatdoc!( r#" use snforge_std::{{set_balance, Token, TokenTrait}}; use starknet::{{ContractAddress, syscalls, SyscallResultTrait}}; fn get_balance(contract_address: ContractAddress, token: Token) -> Span {{ let mut calldata: Array = array![contract_address.into()]; let balance = syscalls::call_contract_syscall( token.contract_address(), selector!("balance_of"), calldata.span(), ) .unwrap_syscall(); balance }} #[fork(url: "{}", block_number: 715_593)] #[test] fn test_set_balance_strk_with_fork() {{ let contract_address: ContractAddress = 0x0585dd8cab667ca8415fac8bead99c78947079aa72d9120140549a6f2edc4128 .try_into() .unwrap(); let balance_before = get_balance(contract_address, Token::{token}); assert_eq!(balance_before, array![{balance_before_low}, {balance_before_high}].span()); set_balance(contract_address, 10, Token::{token}); let balance_after = get_balance(contract_address, Token::{token}); assert_eq!(balance_after, array![10, 0].span(), "Balance should should be 10"); }} "#, node_rpc_url(), ) .as_str(), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/simple_package/src/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test_case("STRK", STRK_CONTRACT_ADDRESS; "strk")] #[test_case("ETH", ETH_CONTRACT_ADDRESS; "eth")] fn test_set_balance_with_disabled_predeployment(token: &str, contract_address: &str) { let test = utils_test_case!( formatdoc!( r#" use snforge_std::{{Token, TokenTrait}}; use starknet::{{ContractAddress, syscalls, SyscallResultTrait}}; fn get_balance(contract_address: ContractAddress, token: Token) -> Span {{ let mut calldata: Array = array![contract_address.into()]; let balance = syscalls::call_contract_syscall( token.contract_address(), selector!("balance_of"), calldata.span(), ) .unwrap_syscall(); balance }} #[test] #[disable_predeployed_contracts] fn test_set_balance_strk_with_disabled_predeployment() {{ let contract_address: ContractAddress = 0x123.try_into().unwrap(); get_balance(contract_address, Token::{}); }} "#, token ) .as_str(), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/simple_package/src/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); let asserted_msg = format!("Contract not deployed at address: {contract_address}"); assert_case_output_contains( &result, "test_set_balance_strk_with_disabled_predeployment", &asserted_msg, ); } ================================================ FILE: crates/forge/tests/integration/setup_fork.rs ================================================ use cheatnet::runtime_extensions::forge_config_extension::config::BlockId; use forge_runner::partition::PartitionConfig; use foundry_ui::UI; use indoc::{formatdoc, indoc}; use std::num::NonZeroU32; use std::path::Path; use std::sync::Arc; use camino::Utf8PathBuf; use forge::block_number_map::BlockNumberMap; use forge::run_tests::package::run_for_package; use forge::run_tests::test_target::ExitFirstChannel; use forge::scarb::config::ForkTarget; use forge::test_filter::TestsFilter; use tempfile::tempdir; use tokio::runtime::Runtime; use crate::utils::runner::{Contract, assert_case_output_contains, assert_failed, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; use forge::run_tests::package::RunForPackageArgs; use forge::shared_cache::FailedTestsCache; use forge_runner::CACHE_DIR; use forge_runner::debugging::TraceArgs; use forge_runner::forge_config::ForgeTrackedResource; use forge_runner::forge_config::{ ExecutionDataToSave, ForgeConfig, OutputConfig, TestRunnerConfig, }; use forge_runner::running::target::prepare_test_target; use forge_runner::scarb::load_test_artifacts; use scarb_api::ScarbCommand; use scarb_api::metadata::metadata_for_dir; use shared::test_utils::node_url::node_rpc_url; #[test] fn fork_simple_decorator() { let test = test_case!(formatdoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use starknet::contract_address_const; #[starknet::interface] trait IHelloStarknet {{ fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; }} #[test] #[fork(url: "{}", block_number: 54060)] fn fork_simple_decorator() {{ let dispatcher = IHelloStarknetDispatcher {{ contract_address: contract_address_const::<0x202de98471a4fae6bcbabb96cab00437d381abc58b02509043778074d6781e9>() }}; let balance = dispatcher.get_balance(); assert(balance == 0, 'Balance should be 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'Balance should be 100'); }} "#, node_rpc_url() ).as_str()); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[expect(clippy::too_many_lines)] #[test] fn fork_aliased_decorator() { let test = test_case!(indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use starknet::contract_address_const; #[starknet::interface] trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; } #[test] #[fork("FORK_NAME_FROM_SCARB_TOML")] fn fork_aliased_decorator() { let dispatcher = IHelloStarknetDispatcher { contract_address: contract_address_const::<0x202de98471a4fae6bcbabb96cab00437d381abc58b02509043778074d6781e9>() }; let balance = dispatcher.get_balance(); assert(balance == 0, 'Balance should be 0'); dispatcher.increase_balance(100); let balance = dispatcher.get_balance(); assert(balance == 100, 'Balance should be 100'); } "# )); let rt = Runtime::new().expect("Could not instantiate Runtime"); ScarbCommand::new_with_stdio() .current_dir(test.path().unwrap()) .arg("build") .arg("--test") .run() .unwrap(); let metadata = metadata_for_dir(test.path().unwrap()).unwrap(); let package = metadata .packages .iter() .find(|p| p.name == "test_package") .unwrap(); let raw_test_targets = load_test_artifacts(&test.path().unwrap().join("target/dev"), package).unwrap(); let ui = Arc::new(UI::default()); let result = rt .block_on(async { let target_handles = raw_test_targets .into_iter() .map(|t| { tokio::task::spawn_blocking(move || { prepare_test_target(t, &ForgeTrackedResource::CairoSteps) }) }) .collect(); run_for_package( RunForPackageArgs { target_handles, package_name: "test_package".to_string(), package_root: Utf8PathBuf::default(), tests_filter: TestsFilter::from_flags( None, false, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ), forge_config: Arc::new(ForgeConfig { test_runner_config: Arc::new(TestRunnerConfig { exit_first: false, deterministic_output: false, fuzzer_runs: NonZeroU32::new(256).unwrap(), fuzzer_seed: 12345, max_n_steps: None, is_vm_trace_needed: false, cache_dir: Utf8PathBuf::from_path_buf(tempdir().unwrap().keep()) .unwrap() .join(CACHE_DIR), contracts_data: ContractsData::try_from(test.contracts(&ui).unwrap()) .unwrap(), tracked_resource: ForgeTrackedResource::CairoSteps, environment_variables: test.env().clone(), launch_debugger: false, }), output_config: Arc::new(OutputConfig { detailed_resources: false, execution_data_to_save: ExecutionDataToSave::default(), trace_args: TraceArgs::default(), gas_report: false, }), }), fork_targets: vec![ForkTarget { name: "FORK_NAME_FROM_SCARB_TOML".to_string(), url: node_rpc_url().as_str().parse().unwrap(), block_id: BlockId::BlockTag, }], }, &BlockNumberMap::default(), ui, &mut ExitFirstChannel::default(), ) .await }) .expect("Runner fail") .summaries(); assert_passed(&result); } #[test] fn fork_aliased_decorator_overrding() { let test = test_case!(indoc!( r#" use starknet::syscalls::get_execution_info_syscall; #[test] #[fork("FORK_NAME_FROM_SCARB_TOML", block_number: 2137)] fn test_get_block_number() { let execution_info = get_execution_info_syscall().unwrap().deref(); let block_info = execution_info.block_info.deref(); let block_number = block_info.block_number; assert(block_number == 2137, 'Invalid block'); } "# )); let rt = Runtime::new().expect("Could not instantiate Runtime"); ScarbCommand::new_with_stdio() .current_dir(test.path().unwrap()) .arg("build") .arg("--test") .run() .unwrap(); let metadata = metadata_for_dir(test.path().unwrap()).unwrap(); let package = metadata .packages .iter() .find(|p| p.name == "test_package") .unwrap(); let raw_test_targets = load_test_artifacts(&test.path().unwrap().join("target/dev"), package).unwrap(); let ui = Arc::new(UI::default()); let result = rt .block_on(async { let target_handles = raw_test_targets .into_iter() .map(|t| { tokio::task::spawn_blocking(move || { prepare_test_target(t, &ForgeTrackedResource::CairoSteps) }) }) .collect(); run_for_package( RunForPackageArgs { target_handles, package_name: "test_package".to_string(), package_root: Utf8PathBuf::default(), tests_filter: TestsFilter::from_flags( None, false, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ), forge_config: Arc::new(ForgeConfig { test_runner_config: Arc::new(TestRunnerConfig { exit_first: false, deterministic_output: false, fuzzer_runs: NonZeroU32::new(256).unwrap(), fuzzer_seed: 12345, max_n_steps: None, is_vm_trace_needed: false, cache_dir: Utf8PathBuf::from_path_buf(tempdir().unwrap().keep()) .unwrap() .join(CACHE_DIR), contracts_data: ContractsData::try_from(test.contracts(&ui).unwrap()) .unwrap(), tracked_resource: ForgeTrackedResource::CairoSteps, environment_variables: test.env().clone(), launch_debugger: false, }), output_config: Arc::new(OutputConfig { detailed_resources: false, execution_data_to_save: ExecutionDataToSave::default(), trace_args: TraceArgs::default(), gas_report: false, }), }), fork_targets: vec![ForkTarget { name: "FORK_NAME_FROM_SCARB_TOML".to_string(), url: node_rpc_url().as_str().parse().unwrap(), block_id: BlockId::BlockNumber(12_341_234), }], }, &BlockNumberMap::default(), ui, &mut ExitFirstChannel::default(), ) .await }) .expect("Runner fail") .summaries(); assert_passed(&result); } #[test] fn fork_cairo0_contract() { let test = test_case!(formatdoc!( r#" use starknet::contract_address_const; #[starknet::interface] trait IERC20Camel {{ fn totalSupply(self: @TState) -> u256; }} #[test] #[fork(url: "{}", block_number: 54060)] fn fork_cairo0_contract() {{ let contract_address = contract_address_const::<0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7>(); let dispatcher = IERC20CamelDispatcher {{ contract_address }}; let total_supply = dispatcher.totalSupply(); assert(total_supply == 88730316280408105750094, 'Wrong total supply'); }} "#, node_rpc_url() ).as_str()); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn get_block_info_in_forked_block() { let test = test_case!(formatdoc!( r#" use starknet::ContractAddress; use starknet::ContractAddressIntoFelt252; use starknet::contract_address_const; use snforge_std::{{ declare, ContractClassTrait, DeclareResultTrait }}; #[starknet::interface] trait IBlockInfoChecker {{ fn read_block_number(self: @TContractState) -> u64; fn read_block_timestamp(self: @TContractState) -> u64; fn read_sequencer_address(self: @TContractState) -> ContractAddress; }} #[test] #[fork(url: "{node_rpc_url}", block_number: 54060)] fn test_fork_get_block_info_contract_on_testnet() {{ let dispatcher = IBlockInfoCheckerDispatcher {{ contract_address: contract_address_const::<0x3d80c579ad7d83ff46634abe8f91f9d2080c5c076d4f0f59dd810f9b3f01164>() }}; let timestamp = dispatcher.read_block_timestamp(); assert(timestamp == 1711645884, timestamp.into()); let block_number = dispatcher.read_block_number(); assert(block_number == 54060, block_number.into()); let expected_sequencer_addr = contract_address_const::<0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8>(); let sequencer_addr = dispatcher.read_sequencer_address(); assert(sequencer_addr == expected_sequencer_addr, sequencer_addr.into()); }} #[test] #[fork(url: "{node_rpc_url}", block_number: 54060)] fn test_fork_get_block_info_test_state() {{ let block_info = starknet::get_block_info().unbox(); assert(block_info.block_timestamp == 1711645884, block_info.block_timestamp.into()); assert(block_info.block_number == 54060, block_info.block_number.into()); let expected_sequencer_addr = contract_address_const::<0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8>(); assert(block_info.sequencer_address == expected_sequencer_addr, block_info.sequencer_address.into()); }} #[test] #[fork(url: "{node_rpc_url}", block_number: 54060)] fn test_fork_get_block_info_contract_deployed() {{ let contract = declare("BlockInfoChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IBlockInfoCheckerDispatcher {{ contract_address }}; let timestamp = dispatcher.read_block_timestamp(); assert(timestamp == 1711645884, timestamp.into()); let block_number = dispatcher.read_block_number(); assert(block_number == 54060, block_number.into()); let expected_sequencer_addr = contract_address_const::<0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8>(); let sequencer_addr = dispatcher.read_sequencer_address(); assert(sequencer_addr == expected_sequencer_addr, sequencer_addr.into()); }} #[test] #[fork(url: "{node_rpc_url}", block_tag: latest)] fn test_fork_get_block_info_latest_block() {{ let block_info = starknet::get_block_info().unbox(); assert(block_info.block_timestamp > 1711645884, block_info.block_timestamp.into()); assert(block_info.block_number > 54060, block_info.block_number.into()); }} #[test] #[fork(url: "{node_rpc_url}", block_hash: 0x06ae121e46f5375f93b00475fb130348ae38148e121f84b0865e17542e9485de)] fn test_fork_get_block_info_block_hash() {{ let block_info = starknet::get_block_info().unbox(); assert(block_info.block_timestamp == 1711645884, block_info.block_timestamp.into()); assert(block_info.block_number == 54060, block_info.block_number.into()); let expected_sequencer_addr = contract_address_const::<0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8>(); assert(block_info.sequencer_address == expected_sequencer_addr, block_info.sequencer_address.into()); }} #[test] #[fork(url: "{node_rpc_url}", block_hash: 3021433528476416000728121069095289682281028310523383289416465162415092565470)] fn test_fork_get_block_info_block_hash_with_number() {{ let block_info = starknet::get_block_info().unbox(); assert(block_info.block_timestamp == 1711645884, block_info.block_timestamp.into()); assert(block_info.block_number == 54060, block_info.block_number.into()); let expected_sequencer_addr = contract_address_const::<0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8>(); assert(block_info.sequencer_address == expected_sequencer_addr, block_info.sequencer_address.into()); }} "#, node_rpc_url = node_rpc_url() ).as_str(), Contract::from_code_path( "BlockInfoChecker".to_string(), Path::new("tests/data/contracts/block_info_checker.cairo"), ).unwrap()); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn fork_get_block_info_fails() { let test = test_case!( formatdoc!( r#" #[test] #[fork(url: "{}", block_number: 999999999999)] fn fork_get_block_info_fails() {{ starknet::get_block_info(); }} "#, node_rpc_url() ) .as_str() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "fork_get_block_info_fails", "Unable to get block with tx hashes from fork", ); } #[test] // found in: https://github.com/foundry-rs/starknet-foundry/issues/1175 fn incompatible_abi() { let test = test_case!(formatdoc!( r#" #[derive(Serde)] struct Response {{ payload: felt252, // there is second field on chain }} #[starknet::interface] trait IResponseWith2Felts {{ fn get(self: @State) -> Response; }} #[test] #[fork(url: "{}", block_tag: latest)] fn test_forking_functionality() {{ let gov_contract_addr: starknet::ContractAddress = 0x66e4b798c66160bd5fd04056938e5c9f65d67f183dfab9d7d0d2ed9413276fe.try_into().unwrap(); let dispatcher = IResponseWith2FeltsDispatcher {{ contract_address: gov_contract_addr }}; let propdetails = dispatcher.get(); assert(propdetails.payload == 8, 'payload not match'); }} "#, node_rpc_url() ) .as_str()); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/should_panic.rs ================================================ use forge_runner::forge_config::ForgeTrackedResource; use std::path::Path; use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use indoc::indoc; #[test] fn should_panic() { let test = test_case!(indoc!( r" use array::ArrayTrait; #[test] #[should_panic] fn should_panic_with_no_expected_data() { panic_with_felt252(0); } #[test] #[should_panic(expected: ('panic message', ))] fn should_panic_check_data() { panic_with_felt252('panic message'); } #[test] #[should_panic(expected: ('panic message', 'second message',))] fn should_panic_multiple_messages(){ let mut arr = ArrayTrait::new(); arr.append('panic message'); arr.append('second message'); panic(arr); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn should_panic_unknown_entry_point() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use starknet::{call_contract_syscall, ContractAddress, Felt252TryIntoContractAddress}; use result::ResultTrait; use snforge_std::{declare, ContractClass, ContractClassTrait, DeclareResultTrait}; #[test] #[should_panic] fn should_panic_with_no_expected_data() { let contract = declare("HelloStarknet").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); match call_contract_syscall( contract_address, 'inexistent_entry_point', ArrayTrait::::new().span() ) { Result::Ok(_) => panic_with_felt252('Expected an error'), Result::Err(err) => panic(err), }; } "# ), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/signing.rs ================================================ use crate::utils::running_tests::run_test_case; use crate::utils::{runner::assert_passed, test_case}; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; #[test] fn test_stark_sign_msg_hash_range() { let test = test_case!(indoc!( r" use snforge_std::signature::{KeyPairTrait, SignError}; use snforge_std::signature::stark_curve::{StarkCurveKeyPairImpl, StarkCurveSignerImpl, StarkCurveVerifierImpl}; const UPPER_BOUND: felt252 = 0x800000000000000000000000000000000000000000000000000000000000000; #[test] fn valid_range() { let key_pair = KeyPairTrait::::generate(); let msg_hash = UPPER_BOUND - 1; let (r, s): (felt252, felt252) = key_pair.sign(msg_hash).unwrap(); let is_valid = key_pair.verify(msg_hash, (r, s)); assert(is_valid, 'Signature should be valid'); } #[test] fn invalid_range() { let key_pair = KeyPairTrait::::generate(); // message_hash should be smaller than UPPER_BOUND // https://github.com/starkware-libs/crypto-cpp/blob/78e3ed8dc7a0901fe6d62f4e99becc6e7936adfd/src/starkware/crypto/ecdsa.cc#L65 let msg_hash = UPPER_BOUND; assert(key_pair.sign(msg_hash).unwrap_err() == SignError::HashOutOfRange, ''); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn test_stark_curve() { let test = test_case!(indoc!( r" use snforge_std::signature::KeyPairTrait; use snforge_std::signature::stark_curve::{StarkCurveKeyPairImpl, StarkCurveSignerImpl, StarkCurveVerifierImpl}; #[test] fn simple_signing_flow() { let key_pair = KeyPairTrait::::generate(); let msg_hash = 0xbadc0ffee; let (r, s): (felt252, felt252) = key_pair.sign(msg_hash).unwrap(); let is_valid = key_pair.verify(msg_hash, (r, s)); assert(is_valid, 'Signature should be valid'); let key_pair2 = KeyPairTrait::::from_secret_key(key_pair.secret_key); assert(key_pair.secret_key == key_pair2.secret_key, 'Secret keys should be equal'); assert(key_pair.public_key == key_pair2.public_key, 'Public keys should be equal'); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn test_secp256k1_curve() { let test = test_case!(indoc!( r" use snforge_std::signature::KeyPairTrait; use snforge_std::signature::secp256k1_curve::{Secp256k1CurveKeyPairImpl, Secp256k1CurveSignerImpl, Secp256k1CurveVerifierImpl}; use starknet::secp256k1::{Secp256k1Point, Secp256k1PointImpl}; use core::starknet::SyscallResultTrait; #[test] fn simple_signing_flow() { let key_pair = KeyPairTrait::::generate(); let msg_hash = 0xbadc0ffee; let (r, s): (u256, u256) = key_pair.sign(msg_hash).unwrap(); let is_valid = key_pair.verify(msg_hash, (r, s)); assert(is_valid, 'Signature should be valid'); let key_pair2 = KeyPairTrait::::from_secret_key(key_pair.secret_key); assert(key_pair.secret_key == key_pair2.secret_key, 'Secret keys should be equal'); assert(key_pair.public_key.get_coordinates().unwrap_syscall() == key_pair2.public_key.get_coordinates().unwrap_syscall(), 'Public keys should be equal'); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn test_secp256r1_curve() { let test = test_case!(indoc!( r" use snforge_std::signature::KeyPairTrait; use snforge_std::signature::secp256r1_curve::{Secp256r1CurveKeyPairImpl, Secp256r1CurveSignerImpl, Secp256r1CurveVerifierImpl}; use starknet::secp256r1::{Secp256r1Point, Secp256r1PointImpl}; use core::starknet::SyscallResultTrait; #[test] fn simple_signing_flow() { let key_pair = KeyPairTrait::::generate(); let msg_hash = 0xbadc0ffee; let (r, s): (u256, u256) = key_pair.sign(msg_hash).unwrap(); let is_valid = key_pair.verify(msg_hash, (r, s)); assert(is_valid, 'Signature should be valid'); let key_pair2 = KeyPairTrait::::from_secret_key(key_pair.secret_key); assert(key_pair.secret_key == key_pair2.secret_key, 'Secret keys should be equal'); assert(key_pair.public_key.get_coordinates().unwrap_syscall() == key_pair2.public_key.get_coordinates().unwrap_syscall(), 'Public keys should be equal'); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn test_secp256_curves() { let test = test_case!(indoc!( r" use snforge_std::signature::KeyPairTrait; use snforge_std::signature::secp256k1_curve::{Secp256k1CurveKeyPairImpl, Secp256k1CurveSignerImpl, Secp256k1CurveVerifierImpl}; use snforge_std::signature::secp256r1_curve::{Secp256r1CurveKeyPairImpl, Secp256r1CurveSignerImpl, Secp256r1CurveVerifierImpl}; use starknet::secp256k1::{Secp256k1Point, Secp256k1PointImpl}; use starknet::secp256r1::{Secp256r1Point, Secp256r1PointImpl}; use core::starknet::SyscallResultTrait; #[test] fn secp256_curves() { let secret_key = 554433; let key_pair_k1 = KeyPairTrait::::from_secret_key(secret_key); let key_pair_r1 = KeyPairTrait::::from_secret_key(secret_key); assert(key_pair_k1.secret_key == key_pair_r1.secret_key, 'Secret keys should equal'); assert(key_pair_k1.public_key.get_coordinates().unwrap_syscall() != key_pair_r1.public_key.get_coordinates().unwrap_syscall(), 'Public keys should be different'); let msg_hash: u256 = 0xbadc0ffee; let sig_k1 = key_pair_k1.sign(msg_hash).unwrap(); let sig_r1 = key_pair_r1.sign(msg_hash).unwrap(); assert(sig_k1 != sig_r1, 'Signatures should be different'); assert(key_pair_k1.verify(msg_hash, sig_k1) == true, 'Signature should be valid'); assert(key_pair_r1.verify(msg_hash, sig_r1) == true, 'Signature should be valid'); assert(key_pair_k1.verify(msg_hash, sig_r1) == false, 'Signature should be invalid'); assert(key_pair_r1.verify(msg_hash, sig_k1) == false, 'Signature should be invalid'); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn test_stark_secp256k1_curves() { let test = test_case!(indoc!( r" use snforge_std::signature::KeyPairTrait; use snforge_std::signature::stark_curve::{StarkCurveKeyPairImpl, StarkCurveSignerImpl, StarkCurveVerifierImpl}; use snforge_std::signature::secp256k1_curve::{Secp256k1CurveKeyPairImpl, Secp256k1CurveSignerImpl, Secp256k1CurveVerifierImpl}; use starknet::secp256k1::{Secp256k1Point, Secp256k1PointImpl}; use core::starknet::SyscallResultTrait; #[test] fn stark_secp256k1_curves() { let secret_key = 554433; let key_pair_stark = KeyPairTrait::::from_secret_key(secret_key); let key_pair_secp256k1 = KeyPairTrait::::from_secret_key(secret_key.into()); assert(key_pair_stark.secret_key.into() == key_pair_secp256k1.secret_key, 'Secret keys should equal'); let (pk_x_secp256k1, _pk_y_secp256k1) = key_pair_secp256k1.public_key.get_coordinates().unwrap_syscall(); assert(key_pair_stark.public_key.into() != pk_x_secp256k1, 'Public keys should be different'); let msg_hash = 0xbadc0ffee; let (r_stark, s_stark): (felt252, felt252) = key_pair_stark.sign(msg_hash).unwrap(); let (r_secp256k1, s_secp256k1): (u256, u256) = key_pair_secp256k1.sign(msg_hash.into()).unwrap(); assert(r_stark.into() != r_secp256k1, 'Signatures should be different'); assert(s_stark.into() != s_secp256k1, 'Signatures should be different'); assert(key_pair_stark.verify(msg_hash, (r_stark, s_stark)) == true, 'Signature should be valid'); assert(key_pair_secp256k1.verify(msg_hash.into(), (r_secp256k1, s_secp256k1)) == true, 'Signature should be valid'); assert(key_pair_secp256k1.verify(msg_hash.into(), (r_stark.into(), s_stark.into())) == false, 'Signature should be invalid'); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn test_invalid_secret_key() { let test = test_case!(indoc!( r" use snforge_std::signature::{KeyPair, KeyPairTrait, SignError}; use snforge_std::signature::stark_curve::{StarkCurveKeyPairImpl, StarkCurveSignerImpl}; use snforge_std::signature::secp256k1_curve::{Secp256k1CurveKeyPairImpl, Secp256k1CurveSignerImpl}; use snforge_std::signature::secp256r1_curve::{Secp256r1CurveKeyPairImpl, Secp256r1CurveSignerImpl}; use starknet::secp256k1::{Secp256k1Impl, Secp256k1Point}; use starknet::secp256r1::{Secp256r1Impl, Secp256r1Point}; #[test] #[should_panic(expected: ('invalid secret_key', ))] fn from_secret_key_stark() { KeyPairTrait::::from_secret_key(0); } #[test] #[should_panic(expected: ('invalid secret_key', ))] fn from_secret_key_secp256k1() { KeyPairTrait::::from_secret_key(0); } #[test] #[should_panic(expected: ('invalid secret_key', ))] fn from_secret_key_secp256r1() { KeyPairTrait::::from_secret_key(0); } #[test] fn sign_stark() { let key_pair = KeyPair { secret_key: 0, public_key: 0x321 } ; assert(key_pair.sign(123).unwrap_err() == SignError::InvalidSecretKey, ''); } #[test] fn sign_secp256k1() { let generator = Secp256k1Impl::get_generator_point(); let key_pair = KeyPair { secret_key: 0, public_key: generator } ; assert(key_pair.sign(123).unwrap_err() == SignError::InvalidSecretKey, ''); } #[test] fn sign_secp256r1() { let generator = Secp256r1Impl::get_generator_point(); let key_pair = KeyPair { secret_key: 0, public_key: generator } ; assert(key_pair.sign(123).unwrap_err() == SignError::InvalidSecretKey, ''); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/spy_events.rs ================================================ use crate::utils::runner::{Contract, assert_case_output_contains, assert_failed, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::{formatdoc, indoc}; use shared::test_utils::node_url::node_rpc_url; use std::path::Path; #[test] fn spy_events_simple() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, spy_events, Event, EventSpy, EventSpyTrait, EventSpyAssertionsTrait, EventsFilterTrait }; #[starknet::interface] trait ISpyEventsChecker { fn emit_one_event(ref self: TContractState, some_data: felt252); } #[starknet::contract] mod SpyEventsChecker { use starknet::ContractAddress; #[storage] struct Storage {} #[event] #[derive(Drop, starknet::Event)] enum Event { FirstEvent: FirstEvent } #[derive(Drop, starknet::Event)] struct FirstEvent { some_data: felt252 } } #[test] fn spy_events_simple() { let contract = declare("SpyEventsChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ISpyEventsCheckerDispatcher { contract_address }; let mut spy = spy_events(); // assert(spy._event_offset == 0, 'Event offset should be 0'); TODO(#2765) dispatcher.emit_one_event(123); spy.assert_emitted(@array![ ( contract_address, SpyEventsChecker::Event::FirstEvent( SpyEventsChecker::FirstEvent { some_data: 123 } ) ) ]); assert(spy.get_events().events.len() == 1, 'There should be one event'); } "# ), Contract::from_code_path( "SpyEventsChecker".to_string(), Path::new("tests/data/contracts/spy_events_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn assert_emitted_fails() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use starknet::ContractAddress; use snforge_std::{ declare, DeclareResultTrait, ContractClassTrait, spy_events, Event, EventSpy, EventSpyTrait, EventSpyAssertionsTrait, EventsFilterTrait }; #[starknet::interface] trait ISpyEventsChecker { fn do_not_emit(ref self: TContractState); } #[starknet::contract] mod SpyEventsChecker { use starknet::ContractAddress; #[storage] struct Storage {} #[event] #[derive(Drop, starknet::Event)] enum Event { FirstEvent: FirstEvent } #[derive(Drop, starknet::Event)] struct FirstEvent { some_data: felt252 } } #[test] fn assert_emitted_fails() { let contract = declare("SpyEventsChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ISpyEventsCheckerDispatcher { contract_address }; let mut spy = spy_events(); dispatcher.do_not_emit(); spy.assert_emitted(@array![ ( contract_address, SpyEventsChecker::Event::FirstEvent( SpyEventsChecker::FirstEvent { some_data: 123 } ) ) ]); } "# ), Contract::from_code_path( "SpyEventsChecker".to_string(), Path::new("tests/data/contracts/spy_events_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "assert_emitted_fails", "Event with matching data and", ); assert_case_output_contains(&result, "assert_emitted_fails", "keys was not emitted"); } #[test] fn expect_three_events_while_two_emitted() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use traits::Into; use starknet::contract_address_const; use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, spy_events, Event, EventSpy, EventSpyTrait, EventSpyAssertionsTrait, EventsFilterTrait }; #[starknet::interface] trait ISpyEventsChecker { fn emit_two_events(ref self: TContractState, some_data: felt252, some_more_data: ContractAddress); } #[starknet::contract] mod SpyEventsChecker { use starknet::ContractAddress; #[storage] struct Storage {} #[event] #[derive(Drop, starknet::Event)] enum Event { FirstEvent: FirstEvent, SecondEvent: SecondEvent, ThirdEvent: ThirdEvent, } #[derive(Drop, starknet::Event)] struct FirstEvent { some_data: felt252 } #[derive(Drop, starknet::Event)] struct SecondEvent { some_data: felt252, #[key] some_more_data: ContractAddress } #[derive(Drop, starknet::Event)] struct ThirdEvent { some_data: felt252, some_more_data: ContractAddress, even_more_data: u256 } } #[test] fn expect_three_events_while_two_emitted() { let contract = declare("SpyEventsChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); let dispatcher = ISpyEventsCheckerDispatcher { contract_address }; let some_data = 456; let some_more_data = contract_address_const::<789>(); let even_more_data = 0; let mut spy = spy_events(); dispatcher.emit_two_events(some_data, some_more_data); spy.assert_emitted(@array![ ( contract_address, SpyEventsChecker::Event::FirstEvent( SpyEventsChecker::FirstEvent { some_data } ) ), ( contract_address, SpyEventsChecker::Event::SecondEvent( SpyEventsChecker::SecondEvent { some_data, some_more_data } ) ), ( contract_address, SpyEventsChecker::Event::ThirdEvent( SpyEventsChecker::ThirdEvent { some_data, some_more_data, even_more_data } ) ) ]); } "# ), Contract::from_code_path( "SpyEventsChecker".to_string(), Path::new("tests/data/contracts/spy_events_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "expect_three_events_while_two_emitted", "Event with matching data and", ); assert_case_output_contains( &result, "expect_three_events_while_two_emitted", "keys was not emitted", ); } #[test] fn expect_two_events_while_three_emitted() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use traits::Into; use starknet::contract_address_const; use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, spy_events, Event, EventSpy, EventSpyTrait, EventSpyAssertionsTrait, EventsFilterTrait }; #[starknet::interface] trait ISpyEventsChecker { fn emit_three_events( ref self: TContractState, some_data: felt252, some_more_data: ContractAddress, even_more_data: u256 ); } #[starknet::contract] mod SpyEventsChecker { use starknet::ContractAddress; #[storage] struct Storage {} #[event] #[derive(Drop, starknet::Event)] enum Event { FirstEvent: FirstEvent, ThirdEvent: ThirdEvent, } #[derive(Drop, starknet::Event)] struct FirstEvent { some_data: felt252 } #[derive(Drop, starknet::Event)] struct ThirdEvent { some_data: felt252, some_more_data: ContractAddress, even_more_data: u256 } } #[test] fn expect_two_events_while_three_emitted() { let contract = declare("SpyEventsChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); let dispatcher = ISpyEventsCheckerDispatcher { contract_address }; let some_data = 456; let some_more_data = contract_address_const::<789>(); let even_more_data = u256 { low: 1, high: 0 }; let mut spy = spy_events(); dispatcher.emit_three_events(some_data, some_more_data, even_more_data); spy.assert_emitted(@array![ ( contract_address, SpyEventsChecker::Event::FirstEvent( SpyEventsChecker::FirstEvent { some_data } ), ), ( contract_address, SpyEventsChecker::Event::ThirdEvent( SpyEventsChecker::ThirdEvent { some_data, some_more_data, even_more_data } ) ) ]); } "# ), Contract::from_code_path( "SpyEventsChecker".to_string(), Path::new("tests/data/contracts/spy_events_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn event_emitted_wrong_data_asserted() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, spy_events, Event, EventSpy, EventSpyTrait, EventSpyAssertionsTrait, EventsFilterTrait }; #[starknet::interface] trait ISpyEventsChecker { fn emit_one_event(ref self: TContractState, some_data: felt252); } #[starknet::contract] mod SpyEventsChecker { use starknet::ContractAddress; #[storage] struct Storage {} #[event] #[derive(Drop, starknet::Event)] enum Event { FirstEvent: FirstEvent } #[derive(Drop, starknet::Event)] struct FirstEvent { some_data: felt252 } } #[test] fn event_emitted_wrong_data_asserted() { let contract = declare("SpyEventsChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ISpyEventsCheckerDispatcher { contract_address }; let mut spy = spy_events(); dispatcher.emit_one_event(123); spy.assert_emitted(@array![ ( contract_address, SpyEventsChecker::Event::FirstEvent( SpyEventsChecker::FirstEvent { some_data: 124 } ), ) ]); } "# ), Contract::from_code_path( "SpyEventsChecker".to_string(), Path::new("tests/data/contracts/spy_events_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "event_emitted_wrong_data_asserted", "Event with matching data and", ); assert_case_output_contains( &result, "event_emitted_wrong_data_asserted", "keys was not emitted from", ); } #[test] fn emit_unnamed_event() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use traits::Into; use starknet::contract_address_const; use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, spy_events, Event, EventSpy, EventSpyTrait, EventSpyAssertionsTrait, EventsFilterTrait }; #[starknet::interface] trait ISpyEventsChecker { fn emit_event_syscall(ref self: TContractState, some_key: felt252, some_data: felt252); } #[test] fn emit_unnamed_event() { let contract = declare("SpyEventsChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); let dispatcher = ISpyEventsCheckerDispatcher { contract_address }; let mut spy = spy_events(); dispatcher.emit_event_syscall(123, 456); spy.assert_emitted(@array![ ( contract_address, Event { keys: array![123], data: array![456] } ) ]); } "# ), Contract::from_code_path( "SpyEventsChecker".to_string(), Path::new("tests/data/contracts/spy_events_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn assert_not_emitted_pass() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, spy_events, Event, EventSpy, EventSpyTrait, EventSpyAssertionsTrait, EventsFilterTrait }; #[starknet::interface] trait ISpyEventsChecker { fn do_not_emit(ref self: TContractState); } #[starknet::contract] mod SpyEventsChecker { use starknet::ContractAddress; #[storage] struct Storage {} #[event] #[derive(Drop, starknet::Event)] enum Event { FirstEvent: FirstEvent, } #[derive(Drop, starknet::Event)] struct FirstEvent { some_data: felt252 } } #[test] fn assert_not_emitted_pass() { let contract = declare("SpyEventsChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ISpyEventsCheckerDispatcher { contract_address }; let mut spy = spy_events(); dispatcher.do_not_emit(); spy.assert_not_emitted(@array![ ( contract_address, SpyEventsChecker::Event::FirstEvent( SpyEventsChecker::FirstEvent { some_data: 123 } ) ) ]); } "# ), Contract::from_code_path( "SpyEventsChecker".to_string(), Path::new("tests/data/contracts/spy_events_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn assert_not_emitted_fails() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use starknet::ContractAddress; use snforge_std::{ declare, DeclareResultTrait, ContractClassTrait, spy_events, Event, EventSpy, EventSpyTrait, EventSpyAssertionsTrait, EventsFilterTrait }; #[starknet::interface] trait ISpyEventsChecker { fn emit_one_event(ref self: TContractState, some_data: felt252); } #[starknet::contract] mod SpyEventsChecker { use starknet::ContractAddress; #[storage] struct Storage {} #[event] #[derive(Drop, starknet::Event)] enum Event { FirstEvent: FirstEvent } #[derive(Drop, starknet::Event)] struct FirstEvent { some_data: felt252 } } #[test] fn assert_not_emitted_fails() { let contract = declare("SpyEventsChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ISpyEventsCheckerDispatcher { contract_address }; let mut spy = spy_events(); dispatcher.emit_one_event(123); spy.assert_not_emitted(@array![ ( contract_address, SpyEventsChecker::Event::FirstEvent( SpyEventsChecker::FirstEvent { some_data: 123 } ) ) ]); } "# ), Contract::from_code_path( "SpyEventsChecker".to_string(), Path::new("tests/data/contracts/spy_events_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "assert_not_emitted_fails", "Event with matching data and", ); assert_case_output_contains(&result, "assert_not_emitted_fails", "keys was emitted"); } #[test] fn capture_cairo0_event() { let test = test_case!( formatdoc!( r#" use array::ArrayTrait; use result::ResultTrait; use starknet::{{ContractAddress, contract_address_const}}; use snforge_std::{{ declare, ContractClassTrait, DeclareResultTrait, spy_events, Event, EventSpy, EventSpyTrait, EventSpyAssertionsTrait, EventsFilterTrait }}; #[starknet::interface] trait ISpyEventsChecker {{ fn emit_one_event(ref self: TContractState, some_data: felt252); fn test_cairo0_event_collection(ref self: TContractState, cairo0_address: felt252); }} #[starknet::contract] mod SpyEventsChecker {{ use starknet::ContractAddress; #[storage] struct Storage {{}} #[event] #[derive(Drop, starknet::Event)] enum Event {{ FirstEvent: FirstEvent, my_event: Cairo0Event, }} #[derive(Drop, starknet::Event)] struct FirstEvent {{ some_data: felt252 }} #[derive(Drop, starknet::Event)] struct Cairo0Event {{ some_data: felt252 }} }} #[test] #[fork(url: "{}", block_tag: latest)] fn capture_cairo0_event() {{ let cairo0_contract_address = contract_address_const::<0x2c77ca97586968c6651a533bd5f58042c368b14cf5f526d2f42f670012e10ac>(); let contract = declare("SpyEventsChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ISpyEventsCheckerDispatcher {{ contract_address }}; let mut spy = spy_events(); dispatcher.test_cairo0_event_collection(cairo0_contract_address.into()); dispatcher.emit_one_event(420); dispatcher.test_cairo0_event_collection(cairo0_contract_address.into()); spy.assert_emitted(@array![ ( cairo0_contract_address, SpyEventsChecker::Event::my_event( SpyEventsChecker::Cairo0Event {{ some_data: 123456789 }} ) ), ( contract_address, SpyEventsChecker::Event::FirstEvent( SpyEventsChecker::FirstEvent {{ some_data: 420 }} ) ), ( cairo0_contract_address, SpyEventsChecker::Event::my_event( SpyEventsChecker::Cairo0Event {{ some_data: 123456789 }} ) ) ]); }} "#, node_rpc_url() ).as_str(), Contract::from_code_path( "SpyEventsChecker".to_string(), Path::new("tests/data/contracts/spy_events_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn test_filtering() { let test = test_case!( indoc!( r#" use array::ArrayTrait; use result::ResultTrait; use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, spy_events, Event, EventSpy, EventSpyTrait, EventSpyAssertionsTrait, EventsFilterTrait }; #[starknet::interface] trait ISpyEventsChecker { fn emit_one_event(ref self: TContractState, some_data: felt252); } #[starknet::contract] mod SpyEventsChecker { use starknet::ContractAddress; #[storage] struct Storage {} #[event] #[derive(Drop, starknet::Event)] enum Event { FirstEvent: FirstEvent } #[derive(Drop, starknet::Event)] struct FirstEvent { some_data: felt252 } } #[test] fn filter_events() { let contract = declare("SpyEventsChecker").unwrap().contract_class(); let (first_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let (second_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let first_dispatcher = ISpyEventsCheckerDispatcher { contract_address: first_address }; let second_dispatcher = ISpyEventsCheckerDispatcher { contract_address: second_address }; let mut spy = spy_events(); // assert(spy._event_offset == 0, 'Event offset should be 0'); TODO(#2765) first_dispatcher.emit_one_event(123); second_dispatcher.emit_one_event(234); let events_from_first_address = spy.get_events().emitted_by(first_address); let events_from_second_address = spy.get_events().emitted_by(second_address); let (from, event) = events_from_first_address.events.at(0); assert(from == @first_address, 'Emitted from wrong address'); assert(event.keys.len() == 1, 'There should be one key'); assert(event.keys.at(0) == @selector!("FirstEvent"), 'Wrong event name'); assert(event.data.len() == 1, 'There should be one data'); let (from, event) = events_from_second_address.events.at(0); assert(from == @second_address, 'Emitted from wrong address'); assert(event.keys.len() == 1, 'There should be one key'); assert(event.keys.at(0) == @selector!("FirstEvent"), 'Wrong event name'); assert(event.data.len() == 1, 'There should be one data'); } "#, ), Contract::from_code_path( "SpyEventsChecker".to_string(), Path::new("tests/data/contracts/spy_events_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/store_load.rs ================================================ use crate::utils::runner::{Contract, assert_case_output_contains, assert_failed, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::{formatdoc, indoc}; use shared::test_utils::node_url::node_rpc_url; use std::path::Path; #[test] fn store_load_simple() { let test = test_case!( indoc!( r#" use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, store, load }; #[starknet::interface] trait IHelloStarknet { fn get_balance(ref self: TContractState) -> felt252; fn increase_balance(ref self: TContractState, amount: felt252); } fn deploy_contract() -> IHelloStarknetDispatcher { let contract = declare("HelloStarknet").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); IHelloStarknetDispatcher { contract_address } } #[test] fn store_balance() { let deployed = deploy_contract(); store(deployed.contract_address, selector!("balance"), array![420].span()); let stored_balance = deployed.get_balance(); assert(stored_balance == 420, 'wrong balance stored'); } #[test] fn load_balance() { let deployed = deploy_contract(); deployed.increase_balance(421); let loaded = load(deployed.contract_address, selector!("balance"), 1); assert(*loaded.at(0) == 421, 'wrong balance stored'); } "# ), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn store_load_wrong_selector() { let test = test_case!( indoc!( r#" use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, store, load }; #[starknet::interface] trait IHelloStarknet { fn get_balance(ref self: TContractState) -> felt252; fn increase_balance(ref self: TContractState, amount: felt252); } fn deploy_contract() -> IHelloStarknetDispatcher { let contract = declare("HelloStarknet").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); IHelloStarknetDispatcher { contract_address } } #[test] fn store_load_wrong_selector() { let deployed = deploy_contract(); store(deployed.contract_address, selector!("i_made_a_typo"), array![420].span()); let stored_balance = deployed.get_balance(); assert(stored_balance == 0, 'wrong balance stored'); // No change expected let loaded = load(deployed.contract_address, selector!("i_made_a_typo"), 1); // Even though non-existing var selector is called, memory should be set assert(*loaded.at(0) == 420, 'wrong storage value'); // Uninitialized memory is expected on wrong selector let loaded = load(deployed.contract_address, selector!("i_made_another_typo"), 1); assert(*loaded.at(0) == 0, 'wrong storage value'); } "# ), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn store_load_wrong_data_length() { let test = test_case!( indoc!( r#" use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, store, load }; #[starknet::interface] trait IHelloStarknet { fn get_balance(ref self: TContractState) -> felt252; fn increase_balance(ref self: TContractState, amount: felt252); } fn deploy_contract() -> IHelloStarknetDispatcher { let contract = declare("HelloStarknet").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); IHelloStarknetDispatcher { contract_address } } #[test] fn store_load_wrong_data_length() { let deployed = deploy_contract(); let stored_balance = deployed.get_balance(); assert(stored_balance == 0, 'wrong balance stored'); // No change expected deployed.increase_balance(420); let loaded = load(deployed.contract_address, selector!("balance"), 2); // Even though wrong length is called, the first felt will be correct and second one uninitialized assert(*loaded.at(0) == 420, 'wrong storage value'); assert(*loaded.at(1) == 0, 'wrong storage value'); } "# ), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn store_load_max_boundaries_input() { let test = test_case!( indoc!( r#" use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, store, load }; #[starknet::interface] trait IHelloStarknet { fn get_balance(ref self: TContractState) -> felt252; fn increase_balance(ref self: TContractState, amount: felt252); } fn deploy_contract() -> IHelloStarknetDispatcher { let contract = declare("HelloStarknet").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); IHelloStarknetDispatcher { contract_address } } const MAX_STORAGE: felt252 = 3618502788666131106986593281521497120414687020801267626233049500247285301248; #[test] fn load_boundaries_max() { let deployed = deploy_contract(); load( deployed.contract_address, MAX_STORAGE + 1, 1 ); } #[test] fn store_boundaries_max() { let deployed = deploy_contract(); store( deployed.contract_address, MAX_STORAGE + 1, array![420].span() ); } #[test] fn load_boundaries_max_overflow() { let deployed = deploy_contract(); load( deployed.contract_address, MAX_STORAGE - 1, 5 ); } #[test] fn store_boundaries_max_overflow() { let deployed = deploy_contract(); store( deployed.contract_address, MAX_STORAGE - 1, array![420, 421, 422].span() ); } "# ), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "load_boundaries_max", "storage_address out of range", ); assert_case_output_contains( &result, "store_boundaries_max", "storage_address out of range", ); assert_case_output_contains( &result, "load_boundaries_max_overflow", "storage_address out of range", ); assert_case_output_contains( &result, "store_boundaries_max_overflow", "storage_address out of range", ); } #[test] fn store_load_structure() { let test = test_case!( indoc!( r#" use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, store, load }; #[derive(Serde, Copy, Drop, starknet::Store)] struct NestedStructure { c: felt252 } #[derive(Serde, Copy, Drop, starknet::Store)] struct StoredStructure { a: felt252, b: NestedStructure, } impl ToSerialized of Into> { fn into(self: StoredStructure) -> Span { let mut serialized_struct: Array = self.into(); serialized_struct.span() } } impl ToArray of Into> { fn into(self: StoredStructure) -> Array { let mut serialized_struct: Array = array![]; self.serialize(ref serialized_struct); serialized_struct } } #[starknet::interface] trait IStorageTester { fn insert_structure(ref self: TContractState, value: StoredStructure); fn read_structure(self: @TContractState) -> StoredStructure; } fn deploy_contract() -> IStorageTesterDispatcher { let contract = declare("StorageTester").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); IStorageTesterDispatcher { contract_address } } #[test] fn store_structure() { let deployed = deploy_contract(); let stored_structure = StoredStructure { a: 123, b: NestedStructure { c: 420 } }; store(deployed.contract_address, selector!("structure"), stored_structure.into()); let stored_structure = deployed.read_structure(); assert(stored_structure.a == 123, 'wrong stored_structure.a'); assert(stored_structure.b.c == 420, 'wrong stored_structure.b.c'); } #[test] fn load_structure() { let deployed = deploy_contract(); let stored_structure = StoredStructure { a: 123, b: NestedStructure { c: 420 } }; deployed.insert_structure(stored_structure); let loaded = load( deployed.contract_address, selector!("structure"), starknet::Store::::size().into() ); assert(loaded == stored_structure.into(), 'wrong structure stored'); } "# ), Contract::from_code_path( "StorageTester".to_string(), Path::new("tests/data/contracts/storage_tester.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn store_load_felt_to_structure() { let test = test_case!( indoc!( r#" use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, store, load, map_entry_address }; #[derive(Serde, Copy, Drop, starknet::Store)] struct NestedStructure { c: felt252 } #[derive(Serde, Copy, Drop, starknet::Store)] struct StoredStructure { a: felt252, b: NestedStructure, } impl ToSerialized of Into> { fn into(self: StoredStructure) -> Span { let mut serialized_struct: Array = self.into(); serialized_struct.span() } } impl ToArray of Into> { fn into(self: StoredStructure) -> Array { let mut serialized_struct: Array = array![]; self.serialize(ref serialized_struct); serialized_struct } } #[starknet::interface] trait IStorageTester { fn insert_felt_to_structure(ref self: TContractState, key: felt252, value: StoredStructure); fn read_felt_to_structure(self: @TContractState, key: felt252) -> StoredStructure; } fn deploy_contract() -> IStorageTesterDispatcher { let contract = declare("StorageTester").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); IStorageTesterDispatcher { contract_address } } #[test] fn store_felt_to_structure() { let deployed = deploy_contract(); let stored_structure = StoredStructure { a: 123, b: NestedStructure { c: 420 } }; store( deployed.contract_address, map_entry_address(selector!("felt_to_structure"), array![421].span()), stored_structure.into(), ); let read_structure = deployed.read_felt_to_structure(421); assert(read_structure.a == stored_structure.a, 'wrong stored_structure.a'); assert(read_structure.b.c == stored_structure.b.c, 'wrong stored_structure.b.c'); } #[test] fn load_felt_to_structure() { let deployed = deploy_contract(); let stored_structure = StoredStructure { a: 123, b: NestedStructure { c: 420 } }; deployed.insert_felt_to_structure(421, stored_structure); let loaded = load( deployed.contract_address, map_entry_address(selector!("felt_to_structure"), array![421].span()), starknet::Store::::size().into() ); assert(loaded == stored_structure.into(), 'wrong structure stored'); } "# ), Contract::from_code_path( "StorageTester".to_string(), Path::new("tests/data/contracts/storage_tester.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn store_load_structure_to_felt() { let test = test_case!( indoc!( r#" use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, store, load, map_entry_address, DeclareResultTrait }; #[derive(Serde, Copy, Drop, starknet::Store, Hash)] struct NestedKey { c: felt252 } #[derive(Serde, Copy, Drop, starknet::Store, Hash)] struct StructuredKey { a: felt252, b: NestedKey, } impl ToSerialized of Into> { fn into(self: StructuredKey) -> Span { let mut serialized_struct: Array = array![]; self.serialize(ref serialized_struct); serialized_struct.span() } } #[starknet::interface] trait IStorageTester { fn insert_structure_to_felt(ref self: TContractState, key: StructuredKey, value: felt252); fn read_structure_to_felt(self: @TContractState, key: StructuredKey) -> felt252; } fn deploy_contract() -> IStorageTesterDispatcher { let contract = declare("StorageTester").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); IStorageTesterDispatcher { contract_address } } #[test] fn store_structure_to_felt() { let deployed = deploy_contract(); let map_key = StructuredKey {a: 420, b: NestedKey { c: 421 }}; store( deployed.contract_address, map_entry_address(selector!("structure_to_felt"), map_key.into()), array![123].span() ); let stored_felt = deployed.read_structure_to_felt(map_key); assert(stored_felt == 123, 'wrong stored_felt'); } #[test] fn load_structure_to_felt() { let deployed = deploy_contract(); let map_key = StructuredKey { a: 420, b: NestedKey { c: 421 } }; deployed.insert_structure_to_felt(map_key, 123); let loaded = load( deployed.contract_address, map_entry_address(selector!("structure_to_felt"), map_key.into()), 1 ); assert(loaded == array![123], 'wrong felt stored'); } "# ), Contract::from_code_path( "StorageTester".to_string(), Path::new("tests/data/contracts/storage_tester.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn store_load_felt_to_felt() { let test = test_case!( indoc!( r#" use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, store, load, map_entry_address }; #[starknet::interface] trait IStorageTester { fn insert_felt_to_felt(ref self: TContractState, key: felt252, value: felt252); fn read_felt_to_felt(self: @TContractState, key: felt252) -> felt252; } fn deploy_contract() -> IStorageTesterDispatcher { let contract = declare("StorageTester").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); IStorageTesterDispatcher { contract_address } } #[test] fn store_felt_to_felt() { let deployed = deploy_contract(); store( deployed.contract_address, map_entry_address(selector!("felt_to_felt"), array![420].span()), array![123].span() ); let stored_felt = deployed.read_felt_to_felt(420); assert(stored_felt == 123, 'wrong stored_felt'); } #[test] fn load_felt_to_felt() { let deployed = deploy_contract(); deployed.insert_felt_to_felt(420, 123); let loaded = load( deployed.contract_address, map_entry_address(selector!("felt_to_felt"), array![420].span()), 1 ); assert(loaded == array![123], 'wrong felt stored'); } "# ), Contract::from_code_path( "StorageTester".to_string(), Path::new("tests/data/contracts/storage_tester.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn fork_store_load() { let test = test_case!(formatdoc!( r#" use starknet::{{ ContractAddress, contract_address_const }}; use snforge_std::{{ load, store }}; #[starknet::interface] trait IHelloStarknet {{ fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; }} #[test] #[fork(url: "{}", block_number: 54060)] fn fork_simple_decorator() {{ let dispatcher = IHelloStarknetDispatcher {{ contract_address: contract_address_const::<0x202de98471a4fae6bcbabb96cab00437d381abc58b02509043778074d6781e9>() }}; let balance = dispatcher.get_balance(); assert(balance == 0, 'Balance should be 0'); let result = load(dispatcher.contract_address, selector!("balance"), 1); assert(*result.at(0) == 0, 'Wrong balance loaded'); store(dispatcher.contract_address, selector!("balance"), array![100].span()); let balance = dispatcher.get_balance(); assert(balance == 100, 'Balance should be 100'); }} "#, node_rpc_url() ).as_str()); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/syscalls.rs ================================================ use crate::utils::runner::{Contract, assert_case_output_contains, assert_failed, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] #[expect(clippy::too_many_lines)] fn library_call_syscall_is_usable() { let test = test_case!( indoc!( r#" use core::clone::Clone; use starknet::ContractAddress; use starknet::ClassHash; use snforge_std::{ declare, DeclareResultTrait, ContractClassTrait }; #[starknet::interface] trait ICaller { fn call_add_two( self: @TContractState, class_hash: ClassHash, number: felt252 ) -> felt252; } #[starknet::interface] trait IExecutor { fn add_two(ref self: TContractState, number: felt252) -> felt252; fn get_thing(self: @TContractState) -> felt252; } fn deploy_contract(name: ByteArray) -> ContractAddress { let contract = declare(name).unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); contract_address } #[test] fn library_call_syscall_is_usable() { let caller_address = deploy_contract("Caller"); let caller_safe_dispatcher = ICallerDispatcher { contract_address: caller_address }; let executor_contract = declare("Executor").unwrap().contract_class(); let executor_class_hash = executor_contract.class_hash.clone(); let (executor_address, _) = executor_contract.deploy(@ArrayTrait::new()).unwrap(); let executor_safe_dispatcher = IExecutorDispatcher { contract_address: executor_address }; let thing = executor_safe_dispatcher.get_thing(); assert(thing == 5, 'invalid thing'); let result = caller_safe_dispatcher.call_add_two(executor_class_hash, 420); assert(result == 422, 'invalid result'); let thing = executor_safe_dispatcher.get_thing(); assert(thing == 5, 'invalid thing'); } "# ), Contract::new( "Caller", indoc!( r" use starknet::ClassHash; #[starknet::interface] trait ICaller { fn call_add_two( self: @TContractState, class_hash: ClassHash, number: felt252 ) -> felt252; } #[starknet::contract] mod Caller { use result::ResultTrait; use starknet::ClassHash; use starknet::library_call_syscall; #[starknet::interface] trait IExecutor { fn add_two(ref self: TContractState, number: felt252) -> felt252; } #[storage] struct Storage {} #[abi(embed_v0)] impl CallerImpl of super::ICaller { fn call_add_two( self: @ContractState, class_hash: ClassHash, number: felt252 ) -> felt252 { let safe_lib_dispatcher = IExecutorLibraryDispatcher { class_hash }; safe_lib_dispatcher.add_two(number) } } } " ) ), Contract::new( "Executor", indoc!( r" #[starknet::interface] trait IExecutor { fn add_two(ref self: TContractState, number: felt252) -> felt252; fn get_thing(self: @TContractState) -> felt252; } #[starknet::contract] mod Executor { #[storage] struct Storage { thing: felt252 } #[constructor] fn constructor(ref self: ContractState) { assert(self.thing.read() == 0, 'default value should be 0'); self.thing.write(5); } #[abi(embed_v0)] impl ExecutorImpl of super::IExecutor { fn add_two(ref self: ContractState, number: felt252) -> felt252 { self.thing.write(10); number + 2 } fn get_thing(self: @ContractState) -> felt252 { self.thing.read() } } } " ) ) ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn keccak_syscall_is_usable() { let test = test_case!(indoc!( r" use array::ArrayTrait; use starknet::syscalls::keccak_syscall; use starknet::SyscallResultTrait; #[test] fn keccak_syscall_is_usable() { let input = array![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]; assert( @keccak_syscall(input.span()).unwrap_syscall() == @u256 { low: 0xec687be9c50d2218388da73622e8fdd5, high: 0xd2eb808dfba4703c528d145dfe6571af }, 'Wrong hash value' ); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn keccak_syscall_too_small_input() { let test = test_case!(indoc!( r" use array::ArrayTrait; use starknet::syscalls::keccak_syscall; use starknet::SyscallResultTrait; #[test] fn keccak_syscall_too_small_input() { let input = array![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; assert( @keccak_syscall(input.span()).unwrap_syscall() == @u256 { low: 0xec687be9c50d2218388da73622e8fdd5, high: 0xd2eb808dfba4703c528d145dfe6571af }, 'Wrong hash value' ); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_case_output_contains( &result, "keccak_syscall_too_small_input", "Invalid input length", ); assert_failed(&result); } #[test] fn cairo_keccak_is_usable() { let test = test_case!(indoc!( r" use array::ArrayTrait; use keccak::cairo_keccak; #[test] fn cairo_keccak_is_usable() { let mut input = array![ 0x0000000000000001, 0x0000000000000002, 0x0000000000000003, 0x0000000000000004, 0x0000000000000005, 0x0000000000000006, 0x0000000000000007, 0x0000000000000008, 0x0000000000000009, 0x000000000000000a, 0x000000000000000b, 0x000000000000000c, 0x000000000000000d, 0x000000000000000e, 0x000000000000000f, 0x0000000000000010, 0x0000000000000011 ]; let res = keccak::cairo_keccak(ref input, 0, 0); assert(@res.low == @0x5d291eebae35b254ff50ec1fc57832e8, 'Wrong hash low'); assert(@res.high == @0x210740d45b1fe2ac908a497ef45509f5, 'Wrong hash high'); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn keccak_syscall_in_contract() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use snforge_std::{ declare, DeclareResultTrait, ContractClassTrait }; #[starknet::interface] trait IHelloKeccak { fn run_keccak(ref self: TContractState, input: Array) -> u256; } #[test] fn keccak_syscall_in_contract() { let contract = declare("HelloKeccak").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IHelloKeccakDispatcher { contract_address }; let res = dispatcher.run_keccak(array![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]); assert( res == u256 { low: 0xec687be9c50d2218388da73622e8fdd5, high: 0xd2eb808dfba4703c528d145dfe6571af }, 'Wrong hash value' ); } "# ), Contract::from_code_path( "HelloKeccak".to_string(), Path::new("tests/data/contracts/keccak_usage.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn compare_keccak_from_contract_with_plain_keccak() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::syscalls::keccak_syscall; use starknet::SyscallResultTrait; use snforge_std::{ declare, DeclareResultTrait, ContractClassTrait }; #[starknet::interface] trait IHelloKeccak { fn run_keccak(ref self: TContractState, input: Array) -> u256; } #[test] fn compare_keccak_from_contract_with_plain_keccak() { let contract = declare("HelloKeccak").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = IHelloKeccakDispatcher { contract_address }; let input = array![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]; let keccak = keccak_syscall(input.span()).unwrap_syscall(); let contract_keccak = dispatcher.run_keccak(input); assert(contract_keccak == keccak, 'Keccaks dont match'); } "# ), Contract::from_code_path( "HelloKeccak".to_string(), Path::new("tests/data/contracts/keccak_usage.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/test_state.rs ================================================ use crate::utils::runner::{Contract, assert_case_output_contains, assert_failed, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] fn storage_access_from_tests() { let test = test_case!(indoc!( r" #[starknet::contract] mod Contract { #[storage] struct Storage { balance: felt252, } #[generate_trait] impl InternalImpl of InternalTrait { fn internal_function(self: @ContractState) -> felt252 { self.balance.read() } } } #[test] fn storage_access_from_tests() { let mut state = Contract::contract_state_for_testing(); state.balance.write(10); let value = Contract::InternalImpl::internal_function(@state); assert(value == 10, 'Incorrect storage value'); } " ),); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn simple_syscalls() { let test = test_case!( indoc!( r#" use starknet::info::{get_execution_info, TxInfo}; use result::ResultTrait; use box::BoxTrait; use serde::Serde; use starknet::{ContractAddress, get_block_hash_syscall}; use array::SpanTrait; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, test_address }; #[starknet::interface] trait ICheatTxInfoChecker { fn get_tx_hash(ref self: TContractState) -> felt252; fn get_nonce(ref self: TContractState) -> felt252; fn get_account_contract_address(ref self: TContractState) -> ContractAddress; fn get_signature(ref self: TContractState) -> Span; fn get_version(ref self: TContractState) -> felt252; fn get_max_fee(ref self: TContractState) -> u128; fn get_chain_id(ref self: TContractState) -> felt252; } #[starknet::interface] trait ICheatBlockNumberChecker { fn get_block_number(ref self: TContractState) -> u64; } #[starknet::interface] trait ICheatBlockTimestampChecker { fn get_block_timestamp(ref self: TContractState) -> u64; } #[starknet::interface] trait ICheatSequencerAddressChecker { fn get_sequencer_address(ref self: TContractState) -> ContractAddress; } #[test] fn simple_syscalls() { let exec_info = get_execution_info().unbox(); assert(exec_info.caller_address.into() == 0, 'Incorrect caller address'); assert(exec_info.contract_address == test_address(), exec_info.contract_address.into()); // Hash of TEST_CASE_SELECTOR assert(exec_info.entry_point_selector.into() == 655947323460646800722791151288222075903983590237721746322261907338444055163, 'Incorrect entry point selector'); let block_info = exec_info.block_info.unbox(); let contract_cheat_block_number = declare("CheatBlockNumberChecker").unwrap().contract_class(); let (contract_address_cheat_block_number, _) = contract_cheat_block_number.deploy(@ArrayTrait::new()).unwrap(); let dispatcher_cheat_block_number = ICheatBlockNumberCheckerDispatcher { contract_address: contract_address_cheat_block_number }; let contract_cheat_block_timestamp = declare("CheatBlockTimestampChecker").unwrap().contract_class(); let (contract_address_cheat_block_timestamp, _) = contract_cheat_block_timestamp.deploy(@ArrayTrait::new()).unwrap(); let dispatcher_cheat_block_timestamp = ICheatBlockTimestampCheckerDispatcher { contract_address: contract_address_cheat_block_timestamp }; let contract_cheat_sequencer_address = declare("CheatSequencerAddressChecker").unwrap().contract_class(); let (contract_address_cheat_sequencer_address, _) = contract_cheat_sequencer_address.deploy(@ArrayTrait::new()).unwrap(); let dispatcher_cheat_sequencer_address = ICheatSequencerAddressCheckerDispatcher { contract_address: contract_address_cheat_sequencer_address }; assert(dispatcher_cheat_block_number.get_block_number() == block_info.block_number, 'Invalid block number'); assert(dispatcher_cheat_block_timestamp.get_block_timestamp() == block_info.block_timestamp, 'Invalid block timestamp'); assert(dispatcher_cheat_sequencer_address.get_sequencer_address() == block_info.sequencer_address, 'Invalid sequencer address'); let contract = declare("CheatTxInfoChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ICheatTxInfoCheckerDispatcher { contract_address }; let tx_info = exec_info.tx_info.unbox(); assert(tx_info.version == dispatcher.get_version(), 'Incorrect version'); assert(tx_info.account_contract_address == dispatcher.get_account_contract_address(), 'Incorrect acc_address'); assert(tx_info.max_fee == dispatcher.get_max_fee(), 'Incorrect max fee'); assert(tx_info.signature == dispatcher.get_signature(), 'Incorrect signature'); assert(tx_info.transaction_hash == dispatcher.get_tx_hash(), 'Incorrect transaction_hash'); assert(tx_info.chain_id == dispatcher.get_chain_id(), 'Incorrect chain_id'); assert(tx_info.nonce == dispatcher.get_nonce(), 'Incorrect nonce'); } "# ), Contract::from_code_path( "CheatTxInfoChecker".to_string(), Path::new("tests/data/contracts/cheat_tx_info_checker.cairo"), ) .unwrap(), Contract::from_code_path( "CheatBlockNumberChecker".to_string(), Path::new("tests/data/contracts/cheat_block_number_checker.cairo"), ) .unwrap(), Contract::from_code_path( "CheatBlockTimestampChecker".to_string(), Path::new("tests/data/contracts/cheat_block_timestamp_checker.cairo"), ) .unwrap(), Contract::from_code_path( "CheatSequencerAddressChecker".to_string(), Path::new("tests/data/contracts/cheat_sequencer_address_checker.cairo") ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn get_block_hash_syscall_in_dispatcher() { let test = test_case!( indoc!( r#" use starknet::info::{get_execution_info, TxInfo}; use result::ResultTrait; use box::BoxTrait; use serde::Serde; use starknet::{ContractAddress, get_block_hash_syscall}; use array::SpanTrait; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, test_address }; #[starknet::interface] trait BlockHashChecker { fn write_block(ref self: TContractState); fn read_block_hash(self: @TContractState) -> felt252; } #[test] fn get_block_hash_syscall_in_dispatcher() { let block_hash_checker = declare("BlockHashChecker").unwrap().contract_class(); let (block_hash_checker_address, _) = block_hash_checker.deploy(@ArrayTrait::new()).unwrap(); let block_hash_checker_dispatcher = BlockHashCheckerDispatcher { contract_address: block_hash_checker_address }; block_hash_checker_dispatcher.write_block(); let stored_blk_hash = block_hash_checker_dispatcher.read_block_hash(); assert(stored_blk_hash == 0, 'Wrong stored blk hash'); } "# ), Contract::from_code_path( "BlockHashChecker".to_string(), Path::new("tests/data/contracts/block_hash_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn library_calls() { let test = test_case!( indoc!( r#" use core::clone::Clone; use result::ResultTrait; use starknet::{ ClassHash, library_call_syscall, ContractAddress }; use snforge_std::{ declare, DeclareResultTrait }; #[starknet::interface] trait ILibraryContract { fn get_value( self: @TContractState, ) -> felt252; fn set_value( ref self: TContractState, number: felt252 ); } #[test] fn library_calls() { let class_hash = declare("LibraryContract").unwrap().contract_class().class_hash.clone(); let lib_dispatcher = ILibraryContractLibraryDispatcher { class_hash }; let value = lib_dispatcher.get_value(); assert(value == 0, 'Incorrect state'); lib_dispatcher.set_value(10); let value = lib_dispatcher.get_value(); assert(value == 10, 'Incorrect state'); } "# ), Contract::new( "LibraryContract", indoc!( r" #[starknet::interface] trait ILibraryContract { fn get_value( self: @TContractState, ) -> felt252; fn set_value( ref self: TContractState, number: felt252 ); } #[starknet::contract] mod LibraryContract { use result::ResultTrait; use starknet::ClassHash; use starknet::library_call_syscall; #[storage] struct Storage { value: felt252 } #[abi(embed_v0)] impl LibraryContractImpl of super::ILibraryContract { fn get_value( self: @ContractState, ) -> felt252 { self.value.read() } fn set_value( ref self: ContractState, number: felt252 ) { self.value.write(number); } } } " ) ) ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn disabled_syscalls() { let test = test_case!( indoc!( r" use result::ResultTrait; use starknet::{ClassHash, deploy_syscall, replace_class_syscall, get_block_hash_syscall}; use snforge_std::declare; #[test] fn disabled_syscalls() { let value : ClassHash = 'xd'.try_into().unwrap(); replace_class_syscall(value).unwrap(); } " ), Contract::from_code_path( "HelloStarknet".to_string(), Path::new("tests/data/contracts/hello_starknet.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains( &result, "disabled_syscalls", "Replace class can't be used in tests", ); } #[test] fn get_block_hash() { let test = test_case!(indoc!( r" use result::ResultTrait; use box::BoxTrait; use starknet::{get_block_hash_syscall, get_block_info}; #[test] fn get_block_hash() { let block_info = get_block_info().unbox(); let hash = get_block_hash_syscall(block_info.block_number - 10).unwrap(); assert(hash == 0, 'Hash not zero'); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn cant_call_test_contract() { let test = test_case!( indoc!( r#" use result::ResultTrait; use starknet::{ClassHash, ContractAddress, deploy_syscall, replace_class_syscall, get_block_hash_syscall}; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, test_address }; #[starknet::interface] trait ICallsBack { fn call_back(ref self: TContractState, address: ContractAddress); } #[test] fn cant_call_test_contract() { let contract = declare("CallsBack").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); let dispatcher = ICallsBackDispatcher { contract_address: contract_address }; dispatcher.call_back(test_address()); } "# ), Contract::new( "CallsBack", indoc!( r" use starknet::ContractAddress; #[starknet::interface] trait ICallsBack { fn call_back(ref self: TContractState, address: ContractAddress); } #[starknet::contract] mod CallsBack { use result::ResultTrait; use starknet::ClassHash; use starknet::{library_call_syscall, ContractAddress}; #[storage] struct Storage { } #[starknet::interface] trait IDontExist { fn test_calling_test_fails(ref self: TContractState); } #[abi(embed_v0)] impl CallsBackImpl of super::ICallsBack { fn call_back(ref self: ContractState, address: ContractAddress) { let dispatcher = IDontExistDispatcher{contract_address: address}; dispatcher.test_calling_test_fails(); } } } " ) ) ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_failed(&result); assert_case_output_contains(&result, "cant_call_test_contract", "ENTRYPOINT_NOT_FOUND"); } #[test] fn storage_access_default_values() { let test = test_case!(indoc!( r" #[starknet::contract] mod Contract { use starknet::storage::{ StoragePointerWriteAccess, StorageMapReadAccess, StoragePathEntry, Map }; #[derive(starknet::Store, Drop)] struct CustomStruct { a: felt252, b: felt252, } #[storage] struct Storage { balance: felt252, legacy_map: Map, custom_struct: CustomStruct, } } #[test] fn storage_access_default_values() { let mut state = Contract::contract_state_for_testing(); let default_felt252 = state.balance.read(); assert(default_felt252 == 0, 'Incorrect storage value'); let default_map_value = state.legacy_map.read(22); assert(default_map_value == 0, 'Incorrect map value'); let default_custom_struct = state.custom_struct.read(); assert(default_custom_struct.a == 0, 'Invalid cs.a value'); assert(default_custom_struct.b == 0, 'Invalid cs.b value'); } " ),); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn simple_cheatcodes() { let test = test_case!(indoc!( r" use result::ResultTrait; use box::BoxTrait; use serde::Serde; use starknet::ContractAddress; use array::SpanTrait; use starknet::ContractAddressIntoFelt252; use snforge_std::{ start_cheat_sequencer_address, stop_cheat_sequencer_address, start_cheat_caller_address, stop_cheat_caller_address, start_cheat_block_number, stop_cheat_block_number, start_cheat_block_timestamp, stop_cheat_block_timestamp, start_cheat_transaction_hash, stop_cheat_transaction_hash, test_address, CheatSpan }; use starknet::{ SyscallResultTrait, SyscallResult, syscalls::get_execution_info_v2_syscall, }; #[test] fn cheat_caller_address_test_state() { let test_address: ContractAddress = test_address(); let caller_addr_before = starknet::get_caller_address(); let target_caller_address: ContractAddress = (123_felt252).try_into().unwrap(); start_cheat_caller_address(test_address, target_caller_address); let caller_addr_after = starknet::get_caller_address(); assert(caller_addr_after==target_caller_address, caller_addr_after.into()); stop_cheat_caller_address(test_address); let caller_addr_after = starknet::get_caller_address(); assert(caller_addr_after==caller_addr_before, caller_addr_before.into()); } #[test] fn cheat_block_number_test_state() { let test_address: ContractAddress = test_address(); let old_block_number = starknet::get_block_info().unbox().block_number; start_cheat_block_number(test_address, 234); let new_block_number = starknet::get_block_info().unbox().block_number; assert(new_block_number == 234, 'Wrong block number'); stop_cheat_block_number(test_address); let new_block_number = starknet::get_block_info().unbox().block_number; assert(new_block_number == old_block_number, 'Block num did not change back'); } #[test] fn cheat_block_timestamp_test_state() { let test_address: ContractAddress = test_address(); let old_block_timestamp = starknet::get_block_info().unbox().block_timestamp; start_cheat_block_timestamp(test_address, 123); let new_block_timestamp = starknet::get_block_info().unbox().block_timestamp; assert(new_block_timestamp == 123, 'Wrong block timestamp'); stop_cheat_block_timestamp(test_address); let new_block_timestamp = starknet::get_block_info().unbox().block_timestamp; assert(new_block_timestamp == old_block_timestamp, 'Timestamp did not change back') } #[test] fn cheat_sequencer_address_test_state() { let test_address: ContractAddress = test_address(); let old_sequencer_address = starknet::get_block_info().unbox().sequencer_address; start_cheat_sequencer_address(test_address, 123.try_into().unwrap()); let new_sequencer_address = starknet::get_block_info().unbox().sequencer_address; assert(new_sequencer_address == 123.try_into().unwrap(), 'Wrong sequencer address'); stop_cheat_sequencer_address(test_address); let new_sequencer_address = starknet::get_block_info().unbox().sequencer_address; assert(new_sequencer_address == old_sequencer_address, 'Sequencer addr did not revert') } #[test] fn transaction_hash_test_state() { let test_address: ContractAddress = test_address(); let old_tx_info = starknet::get_tx_info().unbox(); let old_tx_info_v2 = get_tx_info_v2().unbox(); start_cheat_transaction_hash(test_address, 421); let new_tx_info = starknet::get_tx_info().unbox(); let new_tx_info_v2 = get_tx_info_v2().unbox(); assert(new_tx_info.nonce == old_tx_info.nonce, 'Wrong nonce'); assert(new_tx_info_v2.tip == old_tx_info_v2.tip, 'Wrong tip'); assert(new_tx_info.transaction_hash == 421, 'Wrong transaction_hash'); stop_cheat_transaction_hash(test_address); let new_tx_info = starknet::get_tx_info().unbox(); let new_tx_info_v2 = get_tx_info_v2().unbox(); assert(new_tx_info.nonce == old_tx_info.nonce, 'Wrong nonce'); assert(new_tx_info_v2.tip == old_tx_info_v2.tip, 'Wrong tip'); assert( new_tx_info.transaction_hash == old_tx_info.transaction_hash, 'Wrong transaction_hash' ) } fn get_execution_info_v2() -> Box { get_execution_info_v2_syscall().unwrap_syscall() } fn get_tx_info_v2() -> Box { get_execution_info_v2().unbox().tx_info } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn spy_events_simple() { let test = test_case!(indoc!( r" use array::ArrayTrait; use result::ResultTrait; use starknet::SyscallResultTrait; use starknet::ContractAddress; use snforge_std::{ declare, ContractClassTrait, spy_events, Event, EventSpy, EventSpyTrait, EventSpyAssertionsTrait, EventsFilterTrait, test_address }; #[test] fn spy_events_simple() { let contract_address = test_address(); let mut spy = spy_events(); // assert(spy._event_offset == 0, 'Events offset should be 0'); TODO(#2765) starknet::emit_event_syscall(array![1234].span(), array![2345].span()).unwrap_syscall(); spy.assert_emitted(@array![ ( contract_address, Event { keys: array![1234], data: array![2345] } ) ]); } " ),); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn spy_struct_events() { let test = test_case!(indoc!( r" use array::ArrayTrait; use snforge_std::{ declare, ContractClassTrait, spy_events, EventSpy, EventSpyTrait, EventSpyAssertionsTrait, EventsFilterTrait, test_address }; #[starknet::interface] trait IEmitter { fn emit_event(ref self: TContractState); } #[starknet::contract] mod Emitter { use result::ResultTrait; use starknet::ClassHash; #[event] #[derive(Drop, starknet::Event)] enum Event { ThingEmitted: ThingEmitted } #[derive(Drop, starknet::Event)] struct ThingEmitted { thing: felt252 } #[storage] struct Storage {} #[abi(embed_v0)] impl EmitterImpl of super::IEmitter { fn emit_event( ref self: ContractState, ) { self.emit(Event::ThingEmitted(ThingEmitted { thing: 420 })); } } } #[test] fn spy_struct_events() { let contract_address = test_address(); let mut spy = spy_events(); let mut testing_state = Emitter::contract_state_for_testing(); Emitter::EmitterImpl::emit_event(ref testing_state); spy.assert_emitted( @array![ ( contract_address, Emitter::Event::ThingEmitted(Emitter::ThingEmitted { thing: 420 }) ) ] ) } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn inconsistent_syscall_pointers() { let test = test_case!(indoc!( r#" use starknet::ContractAddress; use starknet::info::get_block_number; use snforge_std::start_mock_call; #[starknet::interface] trait IContract { fn get_value(self: @TContractState, arg: ContractAddress) -> u128; } #[test] fn inconsistent_syscall_pointers() { // verifies if SyscallHandler.syscal_ptr is incremented correctly when calling a contract let address = 'address'.try_into().unwrap(); start_mock_call(address, selector!("get_value"), 55); let contract = IContractDispatcher { contract_address: address }; contract.get_value(address); get_block_number(); } "# ),); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn caller_address_in_called_contract() { let test = test_case!( indoc!( r#" use result::ResultTrait; use array::ArrayTrait; use option::OptionTrait; use traits::TryInto; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, test_address }; #[starknet::interface] trait ICheatCallerAddressChecker { fn get_caller_address(ref self: TContractState) -> felt252; } #[starknet::interface] trait IConstructorCheatCallerAddressChecker { fn get_stored_caller_address(ref self: TContractState) -> ContractAddress; } #[test] fn caller_address_in_called_contract() { let cheat_caller_address_checker = declare("CheatCallerAddressChecker").unwrap().contract_class(); let (contract_address_cheat_caller_address_checker, _) = cheat_caller_address_checker.deploy(@ArrayTrait::new()).unwrap(); let dispatcher_cheat_caller_address_checker = ICheatCallerAddressCheckerDispatcher { contract_address: contract_address_cheat_caller_address_checker }; assert(dispatcher_cheat_caller_address_checker.get_caller_address() == test_address().into(), 'Incorrect caller address'); let constructor_cheat_caller_address_checker = declare("ConstructorCheatCallerAddressChecker").unwrap().contract_class(); let (contract_address_constructor_cheat_caller_address_checker, _) = constructor_cheat_caller_address_checker.deploy(@ArrayTrait::new()).unwrap(); let dispatcher_constructor_cheat_caller_address_checker = IConstructorCheatCallerAddressCheckerDispatcher { contract_address: contract_address_constructor_cheat_caller_address_checker }; assert(dispatcher_constructor_cheat_caller_address_checker.get_stored_caller_address() == test_address(), 'Incorrect caller address'); } "# ), Contract::from_code_path( "CheatCallerAddressChecker".to_string(), Path::new("tests/data/contracts/cheat_caller_address_checker.cairo"), ) .unwrap(), Contract::new( "ConstructorCheatCallerAddressChecker", indoc!( r" use starknet::ContractAddress; #[starknet::interface] trait IConstructorCheatCallerAddressChecker { fn get_stored_caller_address(ref self: TContractState) -> ContractAddress; } #[starknet::contract] mod ConstructorCheatCallerAddressChecker { use starknet::ContractAddress; #[storage] struct Storage { caller_address: ContractAddress, } #[constructor] fn constructor(ref self: ContractState) { let address = starknet::get_caller_address(); self.caller_address.write(address); } #[abi(embed_v0)] impl IConstructorCheatCallerAddressChecker of super::IConstructorCheatCallerAddressChecker { fn get_stored_caller_address(ref self: ContractState) -> ContractAddress { self.caller_address.read() } } } " ) ) ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] fn felt252_dict_usage() { let test = test_case!(indoc!( r" #[starknet::contract] mod DictUsingContract { use core::num::traits::{One}; fn unique_count(mut ary: Array) -> u32 { let mut dict: Felt252Dict = Default::default(); let mut counter = 0; // TODO loop { match ary.pop_front() { Option::Some(value) => { if dict.get(value).is_one() { continue; } dict.insert(value, One::one()); counter += 1; }, Option::None => { break; } } }; counter } #[storage] struct Storage { unique_count: u32 } #[constructor] fn constructor(ref self: ContractState, values: Array) { self.unique_count.write(unique_count(values)); } #[external(v0)] fn get_unique(self: @ContractState) -> u32 { self.unique_count.read() } #[external(v0)] fn write_unique(ref self: ContractState, values: Array) { self.unique_count.write(unique_count(values)); } } #[test] fn test_dict_in_constructor() { let mut testing_state = DictUsingContract::contract_state_for_testing(); DictUsingContract::constructor( ref testing_state, array![1, 2, 3, 3, 3, 3 ,3, 4, 4, 4, 4, 4, 5, 5, 5, 5] ); assert(DictUsingContract::get_unique(@testing_state) == 5_u32, 'wrong unq ctor'); DictUsingContract::write_unique( ref testing_state, array![1, 2, 3, 3, 3, 3 ,3, 4, 4, 4, 4, 4] ); assert(DictUsingContract::get_unique(@testing_state) == 4_u32, ' wrote wrong unq'); } " )); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/integration/too_many_events.rs ================================================ use crate::utils::runner::{Contract, assert_case_output_contains, assert_failed, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use blockifier::blockifier_versioned_constants::{EventLimits, VersionedConstants}; use forge_runner::forge_config::ForgeTrackedResource; use indoc::formatdoc; use starknet_api::versioned_constants_logic::VersionedConstantsTrait; use std::path::Path; #[test] fn ok_events() { let EventLimits { max_data_length, max_keys_length, max_n_emitted_events, } = VersionedConstants::latest_constants().tx_event_limits; let test = test_case!( &formatdoc!( r#" use starknet::ContractAddress; use snforge_std::{{ declare, ContractClassTrait, DeclareResultTrait, store, load }}; #[starknet::interface] trait ITooManyEvents {{ fn emit_too_many_events(self: @TContractState, count: felt252); fn emit_too_many_keys(self: @TContractState, count: felt252); fn emit_too_many_data(self: @TContractState, count: felt252); }} fn deploy_contract() -> ITooManyEventsDispatcher {{ let contract = declare("TooManyEvents").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); ITooManyEventsDispatcher {{ contract_address }} }} #[test] fn emit_ok_many_events() {{ let deployed = deploy_contract(); deployed.emit_too_many_events({max_n_emitted_events}); assert(1 == 1, ''); }} #[test] fn emit_ok_many_keys() {{ let deployed = deploy_contract(); deployed.emit_too_many_keys({max_keys_length}); assert(1 == 1, ''); }} #[test] fn emit_ok_many_data() {{ let deployed = deploy_contract(); deployed.emit_too_many_data({max_data_length}); assert(1 == 1, ''); }} "# ), Contract::from_code_path( "TooManyEvents".to_string(), Path::new("tests/data/contracts/too_many_events.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); } #[test] fn too_many_events() { let EventLimits { max_data_length, max_keys_length, max_n_emitted_events, } = VersionedConstants::latest_constants().tx_event_limits; let emit_too_many_events = max_n_emitted_events + 1; let emit_too_many_keys = max_keys_length + 1; let emit_too_many_data = max_data_length + 1; let test = test_case!( &formatdoc!( r#" use starknet::ContractAddress; use snforge_std::{{ declare, ContractClassTrait, DeclareResultTrait, store, load }}; #[starknet::interface] trait ITooManyEvents {{ fn emit_too_many_events(self: @TContractState, count: felt252); fn emit_too_many_keys(self: @TContractState, count: felt252); fn emit_too_many_data(self: @TContractState, count: felt252); }} fn deploy_contract() -> ITooManyEventsDispatcher {{ let contract = declare("TooManyEvents").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); ITooManyEventsDispatcher {{ contract_address }} }} #[test] fn emit_too_many_events() {{ let deployed = deploy_contract(); deployed.emit_too_many_events({emit_too_many_events}); assert(1 == 1, ''); }} #[test] fn emit_too_many_keys() {{ let deployed = deploy_contract(); deployed.emit_too_many_keys({emit_too_many_keys}); assert(1 == 1, ''); }} #[test] fn emit_too_many_data() {{ let deployed = deploy_contract(); deployed.emit_too_many_data({emit_too_many_data}); assert(1 == 1, ''); }} "# ), Contract::from_code_path( "TooManyEvents".to_string(), Path::new("tests/data/contracts/too_many_events.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_failed(&result); assert_case_output_contains( &result, "emit_too_many_events", &format!( "Exceeded the maximum number of events, number events: {emit_too_many_events}, max number events: {max_n_emitted_events}." ), ); assert_case_output_contains( &result, "emit_too_many_data", &format!( "Exceeded the maximum data length, data length: {emit_too_many_data}, max data length: {max_data_length}." ), ); assert_case_output_contains( &result, "emit_too_many_keys", &format!( "Exceeded the maximum keys length, keys length: {emit_too_many_keys}, max keys length: {max_keys_length}." ), ); } ================================================ FILE: crates/forge/tests/integration/trace.rs ================================================ use crate::utils::runner::{Contract, assert_passed}; use crate::utils::running_tests::run_test_case; use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; #[test] #[expect(clippy::too_many_lines)] fn trace_deploy() { let test = test_case!( indoc!( r#" use core::clone::Clone; use snforge_std::{declare, ContractClassTrait, DeclareResultTrait, test_address, test_selector}; use snforge_std::trace::{CallEntryPoint, CallType, EntryPointType, get_call_trace, CallTrace, CallResult}; use starknet::{SyscallResultTrait, deploy_syscall, ContractAddress}; #[test] fn test_deploy_trace_info() { let proxy = declare("TraceInfoProxy").unwrap().contract_class().clone(); let checker = declare("TraceInfoChecker").unwrap().contract_class(); let (checker_address, _) = checker.deploy(@array![]).unwrap(); let (proxy_address1, _) = proxy.deploy(@array![checker_address.into()]).unwrap(); let (proxy_address2, _) = deploy_syscall( proxy.class_hash, 0, array![checker_address.into()].span(), false ) .unwrap_syscall(); let (proxy_address_3, _) = proxy .deploy_at(@array![checker_address.into()], 123.try_into().unwrap()) .unwrap(); assert_trace( get_call_trace(), proxy_address1, proxy_address2, proxy_address_3, checker_address ); } fn assert_trace( trace: CallTrace, proxy_address1: ContractAddress, proxy_address2: ContractAddress, proxy_address3: ContractAddress, checker_address: ContractAddress ) { let expected_trace = CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: test_selector(), calldata: array![], contract_address: test_address(), caller_address: 0.try_into().unwrap(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::Constructor, entry_point_selector: selector!("constructor"), calldata: array![checker_address.into()], contract_address: proxy_address1, caller_address: test_address(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("from_proxy"), calldata: array![1], contract_address: checker_address, caller_address: proxy_address1, call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Success(array![101]) } ], result: CallResult::Success(array![]) }, CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::Constructor, entry_point_selector: selector!("constructor"), calldata: array![checker_address.into()], contract_address: proxy_address2, caller_address: test_address(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("from_proxy"), calldata: array![1], contract_address: checker_address, caller_address: proxy_address2, call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Success(array![101]) } ], result: CallResult::Success(array![]) }, CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::Constructor, entry_point_selector: selector!("constructor"), calldata: array![checker_address.into()], contract_address: proxy_address3, caller_address: test_address(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("from_proxy"), calldata: array![1], contract_address: checker_address, caller_address: proxy_address3, call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Success(array![101]) } ], result: CallResult::Success(array![]) } ], result: CallResult::Success(array![]) }; assert(trace == expected_trace, ''); } "# ), Contract::from_code_path( "TraceInfoProxy".to_string(), Path::new("tests/data/contracts/trace_info_proxy.cairo"), ) .unwrap(), Contract::from_code_path( "TraceInfoChecker".to_string(), Path::new("tests/data/contracts/trace_info_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] #[expect(clippy::too_many_lines)] fn trace_call() { let test = test_case!( indoc!( r#" use core::clone::Clone; use snforge_std::{declare, ContractClassTrait, DeclareResultTrait, test_address, test_selector, start_cheat_caller_address}; use snforge_std::trace::{CallTrace, CallEntryPoint, CallType, EntryPointType, get_call_trace, CallResult}; use starknet::{ContractAddress, ClassHash}; #[starknet::interface] trait ITraceInfoProxy { fn with_libcall(self: @T, class_hash: ClassHash) -> felt252; fn regular_call(self: @T, contract_address: ContractAddress) -> felt252; fn with_panic(self: @T, contract_address: ContractAddress); fn call_two(self: @T, checker_address: ContractAddress, dummy_address: ContractAddress); } #[starknet::interface] trait ITraceInfoChecker { fn from_proxy(self: @T, data: felt252) -> felt252; fn panic(self: @T); } #[starknet::interface] trait ITraceDummy { fn from_proxy(ref self: T); } #[test] fn test_call_trace_info() { let proxy = declare("TraceInfoProxy").unwrap().contract_class(); let checker = declare("TraceInfoChecker").unwrap().contract_class().clone(); let dummy = declare("TraceDummy").unwrap().contract_class(); let (checker_address, _) = checker.deploy(@array![]).unwrap(); let (proxy_address, _) = proxy.deploy(@array![checker_address.into()]).unwrap(); let (dummy_address, _) = dummy.deploy(@array![]).unwrap(); let proxy_dispatcher = ITraceInfoProxyDispatcher { contract_address: proxy_address }; proxy_dispatcher.regular_call(checker_address); proxy_dispatcher.with_libcall(checker.class_hash); proxy_dispatcher.call_two(checker_address, dummy_address); let chcecker_dispatcher = ITraceInfoCheckerDispatcher { contract_address: checker_address }; chcecker_dispatcher.from_proxy(4); assert_trace( get_call_trace(), proxy_address, checker_address, dummy_address, checker.class_hash ); } fn assert_trace( trace: CallTrace, proxy_address: ContractAddress, checker_address: ContractAddress, dummy_address: ContractAddress, checker_class_hash: ClassHash ) { let expected = CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: test_selector(), calldata: array![], contract_address: test_address(), caller_address: 0.try_into().unwrap(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::Constructor, entry_point_selector: selector!("constructor"), calldata: array![checker_address.into()], contract_address: proxy_address, caller_address: test_address(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("from_proxy"), calldata: array![1], contract_address: checker_address, caller_address: proxy_address, call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Success(array![101]) }, ], result: CallResult::Success(array![]) }, CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("regular_call"), calldata: array![checker_address.into()], contract_address: proxy_address, caller_address: test_address(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("from_proxy"), calldata: array![2], contract_address: checker_address, caller_address: proxy_address, call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Success(array![102]) } ], result: CallResult::Success(array![102]) }, CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("with_libcall"), calldata: array![checker_class_hash.into()], contract_address: proxy_address, caller_address: test_address(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("from_proxy"), calldata: array![3], contract_address: proxy_address, caller_address: test_address(), call_type: CallType::Delegate, }, nested_calls: array![], result: CallResult::Success(array![103]) } ], result: CallResult::Success(array![103]) }, CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("call_two"), calldata: array![checker_address.into(), dummy_address.into()], contract_address: proxy_address, caller_address: test_address(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("from_proxy"), calldata: array![42], contract_address: checker_address, caller_address: proxy_address, call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Success(array![142]) }, CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("from_proxy_dummy"), calldata: array![], contract_address: dummy_address, caller_address: proxy_address, call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Success(array![]) } ], result: CallResult::Success(array![]) }, CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("from_proxy"), calldata: array![4], contract_address: checker_address, caller_address: test_address(), call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Success(array![104]) } ], result: CallResult::Success(array![]) }; assert(expected == trace, 'traces are not equal'); } "# ), Contract::from_code_path( "TraceInfoProxy".to_string(), Path::new("tests/data/contracts/trace_info_proxy.cairo"), ) .unwrap(), Contract::from_code_path( "TraceInfoChecker".to_string(), Path::new("tests/data/contracts/trace_info_checker.cairo"), ) .unwrap(), Contract::from_code_path( "TraceDummy".to_string(), Path::new("tests/data/contracts/trace_dummy.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] #[expect(clippy::too_many_lines)] fn trace_failed_call() { let test = test_case!( indoc!( r#" use snforge_std::{declare, ContractClassTrait, DeclareResultTrait, test_address, test_selector}; use snforge_std::trace::{CallEntryPoint, CallType, EntryPointType, get_call_trace, CallTrace, CallResult, CallFailure}; use starknet::{ContractAddress, ClassHash}; #[starknet::interface] trait ITraceInfoProxy { fn with_libcall(self: @T, class_hash: ClassHash) -> felt252; fn regular_call(self: @T, contract_address: ContractAddress) -> felt252; fn with_panic(self: @T, contract_address: ContractAddress); } #[starknet::interface] trait ITraceInfoChecker { fn from_proxy(self: @T, data: felt252) -> felt252; fn panic(self: @T); } #[test] #[feature("safe_dispatcher")] fn test_failed_call_trace_info() { let proxy = declare("TraceInfoProxy").unwrap().contract_class(); let checker = declare("TraceInfoChecker").unwrap().contract_class(); let (checker_address, _) = checker.deploy(@array![]).unwrap(); let (proxy_address, _) = proxy.deploy(@array![checker_address.into()]).unwrap(); let proxy_dispatcher = ITraceInfoProxySafeDispatcher { contract_address: proxy_address }; match proxy_dispatcher.with_panic(checker_address) { Result::Ok(_) => panic_with_felt252('shouldve panicked'), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'panic', *panic_data.at(0)); } } let chcecker_dispatcher = ITraceInfoCheckerSafeDispatcher { contract_address: checker_address }; match chcecker_dispatcher.panic() { Result::Ok(_) => panic_with_felt252('shouldve panicked'), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'panic', *panic_data.at(0)); } } assert_trace(get_call_trace(), proxy_address, checker_address); } fn assert_trace( trace: CallTrace, proxy_address: ContractAddress, checker_address: ContractAddress ) { let expected = CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: test_selector(), calldata: array![], contract_address: test_address(), caller_address: 0.try_into().unwrap(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::Constructor, entry_point_selector: selector!("constructor"), calldata: array![checker_address.into()], contract_address: proxy_address, caller_address: test_address(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("from_proxy"), calldata: array![1], contract_address: checker_address, caller_address: proxy_address, call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Success(array![101]) }, ], result: CallResult::Success(array![]) }, CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("with_panic"), calldata: array![checker_address.into()], contract_address: proxy_address, caller_address: test_address(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("panic"), calldata: array![], contract_address: checker_address, caller_address: proxy_address, call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Failure(CallFailure::Panic(array![482670963043])) } ], result: CallResult::Failure(CallFailure::Panic(array![482670963043, 23583600924385842957889778338389964899652])) }, CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("panic"), calldata: array![], contract_address: checker_address, caller_address: test_address(), call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Failure(CallFailure::Panic(array![482670963043])) } ], result: CallResult::Success(array![]) }; assert(expected == trace, 'traces are not equal'); } "# ), Contract::from_code_path( "TraceInfoProxy".to_string(), Path::new("tests/data/contracts/trace_info_proxy.cairo"), ) .unwrap(), Contract::from_code_path( "TraceInfoChecker".to_string(), Path::new("tests/data/contracts/trace_info_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] #[expect(clippy::too_many_lines)] fn trace_library_call_from_test() { let test = test_case!( indoc!( r#" use core::clone::Clone; use snforge_std::{declare, ContractClassTrait, DeclareResultTrait, test_address, test_selector}; use snforge_std::trace::{CallEntryPoint, CallType, EntryPointType, get_call_trace, CallTrace, CallResult}; use starknet::{ContractAddress, ClassHash}; #[starknet::interface] trait ITraceInfoProxy { fn with_libcall(self: @T, class_hash: ClassHash) -> felt252; fn regular_call(self: @T, contract_address: ContractAddress) -> felt252; fn with_panic(self: @T, contract_address: ContractAddress); fn call_two(self: @T, checker_address: ContractAddress, dummy_address: ContractAddress); } #[starknet::interface] trait ITraceInfoChecker { fn from_proxy(self: @T, data: felt252) -> felt252; fn panic(self: @T); } #[starknet::interface] trait ITraceDummy { fn from_proxy(ref self: T); } #[test] fn test_library_call_trace_info() { let proxy_hash = declare("TraceInfoProxy").unwrap().contract_class().class_hash.clone(); let checker = declare("TraceInfoChecker").unwrap().contract_class().clone(); let dummy = declare("TraceDummy").unwrap().contract_class(); let (checker_address, _) = checker.deploy(@array![]).unwrap(); let (dummy_address, _) = dummy.deploy(@array![]).unwrap(); let proxy_lib_dispatcher = ITraceInfoProxyLibraryDispatcher { class_hash: proxy_hash }; proxy_lib_dispatcher.regular_call(checker_address); proxy_lib_dispatcher.with_libcall(checker.class_hash); proxy_lib_dispatcher.call_two(checker_address, dummy_address); let chcecker_lib_dispatcher = ITraceInfoCheckerLibraryDispatcher { class_hash: checker.class_hash }; chcecker_lib_dispatcher.from_proxy(4); assert_trace(get_call_trace(), checker_address, dummy_address, checker.class_hash); } fn assert_trace( trace: CallTrace, checker_address: ContractAddress, dummy_address: ContractAddress, checker_class_hash: ClassHash ) { let expected = CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: test_selector(), calldata: array![], contract_address: test_address(), caller_address: 0.try_into().unwrap(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("regular_call"), calldata: array![checker_address.into()], contract_address: test_address(), caller_address: 0.try_into().unwrap(), call_type: CallType::Delegate, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("from_proxy"), calldata: array![2], contract_address: checker_address, caller_address: test_address(), call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Success(array![102]) } ], result: CallResult::Success(array![102]) }, CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("with_libcall"), calldata: array![checker_class_hash.into()], contract_address: test_address(), caller_address: 0.try_into().unwrap(), call_type: CallType::Delegate, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("from_proxy"), calldata: array![3], contract_address: test_address(), caller_address: 0.try_into().unwrap(), call_type: CallType::Delegate, }, nested_calls: array![], result: CallResult::Success(array![103]) } ], result: CallResult::Success(array![103]) }, CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("call_two"), calldata: array![checker_address.into(), dummy_address.into()], contract_address: test_address(), caller_address: 0.try_into().unwrap(), call_type: CallType::Delegate, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("from_proxy"), calldata: array![42], contract_address: checker_address, caller_address: test_address(), call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Success(array![142]) }, CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("from_proxy_dummy"), calldata: array![], contract_address: dummy_address, caller_address: test_address(), call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Success(array![]) } ], result: CallResult::Success(array![]) }, CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("from_proxy"), calldata: array![4], contract_address: test_address(), caller_address: 0.try_into().unwrap(), call_type: CallType::Delegate, }, nested_calls: array![], result: CallResult::Success(array![104]) } ], result: CallResult::Success(array![]) }; assert(expected == trace, 'traces are not equal'); } "# ), Contract::from_code_path( "TraceInfoProxy".to_string(), Path::new("tests/data/contracts/trace_info_proxy.cairo"), ) .unwrap(), Contract::from_code_path( "TraceInfoChecker".to_string(), Path::new("tests/data/contracts/trace_info_checker.cairo"), ) .unwrap(), Contract::from_code_path( "TraceDummy".to_string(), Path::new("tests/data/contracts/trace_dummy.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] #[expect(clippy::too_many_lines)] fn trace_failed_library_call_from_test() { let test = test_case!( indoc!( r#" use snforge_std::{declare, ContractClassTrait, DeclareResultTrait, test_address, test_selector}; use snforge_std::trace::{CallEntryPoint, CallType, EntryPointType, get_call_trace, CallTrace, CallResult, CallFailure}; use starknet::{ContractAddress, ClassHash}; #[starknet::interface] trait ITraceInfoProxy { fn with_libcall(self: @T, class_hash: ClassHash) -> felt252; fn regular_call(self: @T, contract_address: ContractAddress) -> felt252; fn with_panic(self: @T, contract_address: ContractAddress); } #[starknet::interface] trait ITraceInfoChecker { fn from_proxy(self: @T, data: felt252) -> felt252; fn panic(self: @T); } #[test] #[feature("safe_dispatcher")] fn test_failed_call_trace_info() { let proxy = declare("TraceInfoProxy").unwrap().contract_class(); let checker = declare("TraceInfoChecker").unwrap().contract_class(); let (checker_address, _) = checker.deploy(@array![]).unwrap(); let (proxy_address, _) = proxy.deploy(@array![checker_address.into()]).unwrap(); let proxy_dispatcher = ITraceInfoProxySafeDispatcher { contract_address: proxy_address }; match proxy_dispatcher.with_panic(checker_address) { Result::Ok(_) => panic_with_felt252('shouldve panicked'), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'panic', *panic_data.at(0)); } } let chcecker_dispatcher = ITraceInfoCheckerSafeDispatcher { contract_address: checker_address }; match chcecker_dispatcher.panic() { Result::Ok(_) => panic_with_felt252('shouldve panicked'), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'panic', *panic_data.at(0)); } } assert_trace(get_call_trace(), proxy_address, checker_address); } fn assert_trace( trace: CallTrace, proxy_address: ContractAddress, checker_address: ContractAddress ) { let expected = CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: test_selector(), calldata: array![], contract_address: test_address(), caller_address: 0.try_into().unwrap(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::Constructor, entry_point_selector: selector!("constructor"), calldata: array![checker_address.into()], contract_address: proxy_address, caller_address: test_address(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("from_proxy"), calldata: array![1], contract_address: checker_address, caller_address: proxy_address, call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Success(array![101]) }, ], result: CallResult::Success(array![]) }, CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("with_panic"), calldata: array![checker_address.into()], contract_address: proxy_address, caller_address: test_address(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("panic"), calldata: array![], contract_address: checker_address, caller_address: proxy_address, call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Failure(CallFailure::Panic(array![482670963043])) } ], result: CallResult::Failure(CallFailure::Panic(array![482670963043, 23583600924385842957889778338389964899652])) }, CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("panic"), calldata: array![], contract_address: checker_address, caller_address: test_address(), call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Failure(CallFailure::Panic(array![482670963043])) } ], result: CallResult::Success(array![]) }; assert(expected == trace, 'traces are not equal'); } "# ), Contract::from_code_path( "TraceInfoProxy".to_string(), Path::new("tests/data/contracts/trace_info_proxy.cairo"), ) .unwrap(), Contract::from_code_path( "TraceInfoChecker".to_string(), Path::new("tests/data/contracts/trace_info_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } #[test] #[expect(clippy::too_many_lines)] fn trace_l1_handler() { let test = test_case!( indoc!( r#" use snforge_std::{declare, ContractClassTrait, DeclareResultTrait, test_address, test_selector, L1HandlerTrait,}; use snforge_std::trace::{CallEntryPoint, CallType, EntryPointType, get_call_trace, CallTrace, CallResult}; use starknet::ContractAddress; #[test] fn test_l1_handler_call_trace_info() { let proxy = declare("TraceInfoProxy").unwrap().contract_class(); let checker = declare("TraceInfoChecker").unwrap().contract_class(); let (checker_address, _) = checker.deploy(@array![]).unwrap(); let (proxy_address, _) = proxy.deploy(@array![checker_address.into()]).unwrap(); let mut l1_handler = L1HandlerTrait::new(checker_address, selector!("handle_l1_message")); l1_handler.execute(123, array![proxy_address.into()].span()).unwrap(); assert_trace(get_call_trace(), proxy_address, checker_address); } fn assert_trace( trace: CallTrace, proxy_address: ContractAddress, checker_address: ContractAddress ) { let expected_trace = CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: test_selector(), calldata: array![], contract_address: test_address(), caller_address: 0.try_into().unwrap(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::Constructor, entry_point_selector: selector!("constructor"), calldata: array![checker_address.into()], contract_address: proxy_address, caller_address: test_address(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("from_proxy"), calldata: array![1], contract_address: checker_address, caller_address: proxy_address, call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Success(array![101]) } ], result: CallResult::Success(array![]) }, CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::L1Handler, entry_point_selector: selector!("handle_l1_message"), calldata: array![123, proxy_address.into()], contract_address: checker_address, caller_address: 0.try_into().unwrap(), call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("regular_call"), calldata: array![checker_address.into()], contract_address: proxy_address, caller_address: checker_address, call_type: CallType::Call, }, nested_calls: array![ CallTrace { entry_point: CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: selector!("from_proxy"), calldata: array![2], contract_address: checker_address, caller_address: proxy_address, call_type: CallType::Call, }, nested_calls: array![], result: CallResult::Success(array![102]) } ], result: CallResult::Success(array![102]) } ], result: CallResult::Success(array![102]) } ], result: CallResult::Success(array![]) }; assert(trace == expected_trace, ''); } "# ), Contract::from_code_path( "TraceInfoProxy".to_string(), Path::new("tests/data/contracts/trace_info_proxy.cairo"), ) .unwrap(), Contract::from_code_path( "TraceInfoChecker".to_string(), Path::new("tests/data/contracts/trace_info_checker.cairo"), ) .unwrap() ); let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } ================================================ FILE: crates/forge/tests/main.rs ================================================ mod e2e; mod integration; pub mod utils; ================================================ FILE: crates/forge/tests/utils/mod.rs ================================================ pub mod runner; pub mod running_tests; pub use crate::test_case; use anyhow::Result; use assert_fs::fixture::PathCopy; use camino::Utf8PathBuf; use project_root::get_project_root; use scarb_api::version::scarb_version; use semver::Version; use std::str::FromStr; pub fn tempdir_with_tool_versions() -> Result { let project_root = get_project_root()?; let temp_dir = assert_fs::TempDir::new()?; temp_dir.copy_from(project_root, &[".tool-versions"])?; Ok(temp_dir) } pub fn get_assert_macros_version() -> Result { Ok(scarb_version()?.cairo) } #[must_use] pub fn get_std_name() -> String { "snforge_std".to_string() } pub fn get_std_path() -> Result { let name = get_std_name(); Ok(Utf8PathBuf::from_str(&format!("../../{name}"))? .canonicalize_utf8()? .to_string()) } pub fn get_snforge_std_entry() -> Result { let name = get_std_name(); let path = get_std_path()?; Ok(format!("{name} = {{ path = \"{path}\" }}")) } ================================================ FILE: crates/forge/tests/utils/runner.rs ================================================ use crate::utils::{ get_assert_macros_version, get_std_name, get_std_path, tempdir_with_tool_versions, }; use anyhow::{Context, Result, anyhow}; use assert_fs::{ TempDir, fixture::{FileTouch, FileWriteStr, PathChild}, }; use blockifier::execution::syscalls::vm_syscall_utils::{SyscallSelector, SyscallUsage}; use cairo_vm::types::builtin_name::BuiltinName; use camino::Utf8PathBuf; use forge_runner::{ test_case_summary::{AnyTestCaseSummary, TestCaseSummary}, test_target_summary::TestTargetSummary, }; use foundry_ui::UI; use indoc::formatdoc; use scarb_api::metadata::metadata_for_dir; use scarb_api::{ CompilationOpts, StarknetContractArtifacts, get_contracts_artifacts_and_source_sierra_paths, target_dir_for_workspace, }; use shared::command::CommandExt; use starknet_api::execution_resources::{GasAmount, GasVector}; use std::{ collections::HashMap, fs, path::{Path, PathBuf}, process::{Command, Stdio}, str::FromStr, }; /// Represents a dependency of a Cairo project #[derive(Debug, Clone)] pub struct LinkedLibrary { pub name: String, pub path: PathBuf, } #[derive(Debug, Clone)] pub struct Contract { name: String, code: String, } impl Contract { #[must_use] pub fn new(name: impl Into, code: impl Into) -> Self { Self { name: name.into(), code: code.into(), } } pub fn from_code_path(name: impl Into, path: impl AsRef) -> Result { let code = fs::read_to_string(path)?; Ok(Self { name: name.into(), code, }) } fn generate_contract_artifacts(self, ui: &UI) -> Result { let dir = tempdir_with_tool_versions()?; let contract_path = dir.child("src/lib.cairo"); contract_path.touch()?; contract_path.write_str(&self.code)?; let scarb_toml_path = dir.child("Scarb.toml"); scarb_toml_path .write_str(&formatdoc!( r#" [package] name = "contract" version = "0.1.0" [[target.starknet-contract]] sierra = true [dependencies] starknet = "2.6.4" "#, )) .unwrap(); Command::new("scarb") .current_dir(&dir) .arg("build") .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) .output_checked() .context("Failed to build contracts with Scarb")?; let scarb_metadata = metadata_for_dir(dir.path())?; let package = scarb_metadata .packages .iter() .find(|package| package.name == "contract") .unwrap(); let artifacts_dir = target_dir_for_workspace(&scarb_metadata).join("dev"); let artifacts = get_contracts_artifacts_and_source_sierra_paths( &artifacts_dir, package, ui, CompilationOpts { use_test_target_contracts: false, #[cfg(feature = "cairo-native")] run_native: true, }, ) .unwrap() .remove(&self.name) .ok_or(anyhow!("there is no contract with name {}", self.name))? .0; Ok(artifacts) } } #[derive(Debug)] pub struct TestCase { dir: TempDir, contracts: Vec, environment_variables: HashMap, } impl<'a> TestCase { pub const TEST_PATH: &'a str = "tests/test_case.cairo"; const PACKAGE_NAME: &'a str = "my_package"; pub fn from(test_code: &str, contracts: Vec) -> Result { let dir = tempdir_with_tool_versions()?; let test_file = dir.child(Self::TEST_PATH); test_file.touch()?; test_file.write_str(test_code)?; dir.child("src/lib.cairo").touch().unwrap(); let snforge_std_name = get_std_name(); let snforge_std_path = get_std_path().unwrap(); let assert_macros_version = get_assert_macros_version()?.to_string(); let scarb_toml_path = dir.child("Scarb.toml"); scarb_toml_path.write_str(&formatdoc!( r#" [package] name = "test_package" version = "0.1.0" [dependencies] starknet = "2.4.0" {snforge_std_name} = {{ path = "{snforge_std_path}" }} assert_macros = "{assert_macros_version}" "# ))?; Ok(Self { dir, contracts, environment_variables: HashMap::new(), }) } pub fn set_env(&mut self, key: &str, value: &str) { self.environment_variables.insert(key.into(), value.into()); } #[must_use] pub fn env(&self) -> &HashMap { &self.environment_variables } pub fn path(&self) -> Result { Utf8PathBuf::from_path_buf(self.dir.path().to_path_buf()) .map_err(|_| anyhow!("Failed to convert TestCase path to Utf8PathBuf")) } #[must_use] pub fn linked_libraries(&self) -> Vec { let snforge_std_path = PathBuf::from_str("../../snforge_std") .unwrap() .canonicalize() .unwrap(); vec![ LinkedLibrary { name: Self::PACKAGE_NAME.to_string(), path: self.dir.path().join("src"), }, LinkedLibrary { name: "snforge_std".to_string(), path: snforge_std_path.join("src"), }, ] } pub fn contracts( &self, ui: &UI, ) -> Result> { self.contracts .clone() .into_iter() .map(|contract| { let name = contract.name.clone(); let artifacts = contract.generate_contract_artifacts(ui)?; Ok((name, (artifacts, Utf8PathBuf::default()))) }) .collect() } #[must_use] pub fn find_test_result(results: &[TestTargetSummary]) -> &TestTargetSummary { results .iter() .find(|tc| !tc.test_case_summaries.is_empty()) .unwrap() } } #[expect(clippy::crate_in_macro_def)] #[macro_export] macro_rules! test_case { ( $test_code:expr ) => ({ use crate::utils::runner::TestCase; TestCase::from($test_code, vec![]).unwrap() }); ( $test_code:expr, $( $contract:expr ),*) => ({ use crate::utils::runner::TestCase; let contracts = vec![$($contract,)*]; TestCase::from($test_code, contracts).unwrap() }); } pub fn assert_passed(result: &[TestTargetSummary]) { let result = &TestCase::find_test_result(result).test_case_summaries; assert!(!result.is_empty(), "No test results found"); assert!( result.iter().all(AnyTestCaseSummary::is_passed), "Some tests didn't pass" ); } pub fn assert_failed(result: &[TestTargetSummary]) { let result = &TestCase::find_test_result(result).test_case_summaries; assert!(!result.is_empty(), "No test results found"); assert!( result.iter().all(AnyTestCaseSummary::is_failed), "Some tests didn't fail" ); } pub fn assert_case_output_contains( result: &[TestTargetSummary], test_case_name: &str, asserted_msg: &str, ) { let test_name_suffix = format!("::{test_case_name}"); let result = TestCase::find_test_result(result); assert!(result.test_case_summaries.iter().any(|any_case| { if any_case.is_passed() || any_case.is_failed() { return any_case.msg().unwrap().contains(asserted_msg) && any_case .name() .unwrap() .ends_with(test_name_suffix.as_str()); } false })); } pub fn assert_gas(result: &[TestTargetSummary], test_case_name: &str, asserted_gas: GasVector) { let test_name_suffix = format!("::{test_case_name}"); let result = TestCase::find_test_result(result); assert!(result.test_case_summaries.iter().any(|any_case| { match any_case { AnyTestCaseSummary::Fuzzing(_) => { panic!("Cannot use assert_gas! for fuzzing tests") } AnyTestCaseSummary::Single(case) => match case { TestCaseSummary::Passed { gas_info: gas, .. } => { assert_gas_with_margin(gas.gas_used, asserted_gas) && any_case .name() .unwrap() .ends_with(test_name_suffix.as_str()) } _ => false, }, } })); } // This logic is used to assert exact gas values in CI for the minimal supported Scarb version // and to assert gas values with a margin in scheduled tests, as values can vary for different Scarb versions // FOR LOCAL DEVELOPMENT ALWAYS USE EXACT CALCULATIONS fn assert_gas_with_margin(gas: GasVector, asserted_gas: GasVector) -> bool { if cfg!(feature = "non_exact_gas_assertions") { let diff = gas_vector_abs_diff(&gas, &asserted_gas); diff.l1_gas.0 <= 10 && diff.l1_data_gas.0 <= 10 && diff.l2_gas.0 <= 200_000 } else { gas == asserted_gas } } fn gas_vector_abs_diff(a: &GasVector, b: &GasVector) -> GasVector { GasVector { l1_gas: GasAmount(a.l1_gas.0.abs_diff(b.l1_gas.0)), l1_data_gas: GasAmount(a.l1_data_gas.0.abs_diff(b.l1_data_gas.0)), l2_gas: GasAmount(a.l2_gas.0.abs_diff(b.l2_gas.0)), } } pub fn assert_syscall( result: &[TestTargetSummary], test_case_name: &str, syscall: SyscallSelector, expected_count: usize, ) { let test_name_suffix = format!("::{test_case_name}"); let result = TestCase::find_test_result(result); assert!(result.test_case_summaries.iter().any(|any_case| { match any_case { AnyTestCaseSummary::Fuzzing(_) => { panic!("Cannot use assert_syscall! for fuzzing tests") } AnyTestCaseSummary::Single(case) => match case { TestCaseSummary::Passed { used_resources, .. } => { used_resources .syscall_usage .get(&syscall) .unwrap_or(&SyscallUsage::new(0, 0)) .call_count == expected_count && any_case .name() .unwrap() .ends_with(test_name_suffix.as_str()) } _ => false, }, } })); } pub fn assert_builtin( result: &[TestTargetSummary], test_case_name: &str, builtin: BuiltinName, expected_count: usize, ) { // TODO(#2806) let expected_count = if builtin == BuiltinName::range_check { expected_count - 1 } else { expected_count }; let test_name_suffix = format!("::{test_case_name}"); let result = TestCase::find_test_result(result); assert!(result.test_case_summaries.iter().any(|any_case| { match any_case { AnyTestCaseSummary::Fuzzing(_) => { panic!("Cannot use assert_builtin for fuzzing tests") } AnyTestCaseSummary::Single(case) => match case { TestCaseSummary::Passed { used_resources, .. } => { used_resources .execution_summary .charged_resources .extended_vm_resources .vm_resources .builtin_instance_counter .get(&builtin) .unwrap_or(&0) == &expected_count && any_case .name() .unwrap() .ends_with(test_name_suffix.as_str()) } _ => false, }, } })); } ================================================ FILE: crates/forge/tests/utils/running_tests.rs ================================================ use crate::utils::runner::TestCase; use camino::Utf8PathBuf; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; use forge::shared_cache::FailedTestsCache; use forge::{ block_number_map::BlockNumberMap, run_tests::package::{RunForPackageArgs, run_for_package}, run_tests::test_target::ExitFirstChannel, test_filter::TestsFilter, }; use forge_runner::CACHE_DIR; use forge_runner::debugging::TraceArgs; use forge_runner::forge_config::{ ExecutionDataToSave, ForgeConfig, ForgeTrackedResource, OutputConfig, TestRunnerConfig, }; use forge_runner::partition::PartitionConfig; use forge_runner::running::target::prepare_test_target; use forge_runner::scarb::load_test_artifacts; use forge_runner::test_target_summary::TestTargetSummary; use foundry_ui::UI; use scarb_api::ScarbCommand; use scarb_api::metadata::metadata_for_dir; use std::num::NonZeroU32; use std::sync::Arc; use tempfile::tempdir; use tokio::runtime::Runtime; #[must_use] pub fn run_test_case( test: &TestCase, tracked_resource: ForgeTrackedResource, ) -> Vec { ScarbCommand::new_with_stdio() .current_dir(test.path().unwrap()) .arg("build") .arg("--test") .run() .unwrap(); let metadata = metadata_for_dir(test.path().unwrap()).unwrap(); let package = metadata .packages .iter() .find(|p| p.name == "test_package") .unwrap(); let rt = Runtime::new().expect("Could not instantiate Runtime"); let raw_test_targets = load_test_artifacts(&test.path().unwrap().join("target/dev"), package).unwrap(); let ui = Arc::new(UI::default()); rt.block_on(async { let target_handles = raw_test_targets .into_iter() .map(|t| tokio::task::spawn_blocking(move || prepare_test_target(t, &tracked_resource))) .collect(); run_for_package( RunForPackageArgs { target_handles, package_name: "test_package".to_string(), package_root: Utf8PathBuf::default(), tests_filter: TestsFilter::from_flags( None, false, Vec::new(), false, false, false, FailedTestsCache::default(), PartitionConfig::default(), ), forge_config: Arc::new(ForgeConfig { test_runner_config: Arc::new(TestRunnerConfig { exit_first: false, deterministic_output: false, fuzzer_runs: NonZeroU32::new(256).unwrap(), fuzzer_seed: 12345, max_n_steps: None, is_vm_trace_needed: false, cache_dir: Utf8PathBuf::from_path_buf(tempdir().unwrap().keep()) .unwrap() .join(CACHE_DIR), contracts_data: ContractsData::try_from(test.contracts(&ui).unwrap()) .unwrap(), tracked_resource, environment_variables: test.env().clone(), launch_debugger: false, }), output_config: Arc::new(OutputConfig { detailed_resources: false, execution_data_to_save: ExecutionDataToSave::default(), trace_args: TraceArgs::default(), gas_report: false, }), }), fork_targets: vec![], }, &BlockNumberMap::default(), ui, &mut ExitFirstChannel::default(), ) .await }) .expect("Runner fail") .summaries() } ================================================ FILE: crates/forge-runner/Cargo.toml ================================================ [package] name = "forge_runner" version.workspace = true edition.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] anyhow.workspace = true cairo-lang-casm.workspace = true cairo-lang-sierra.workspace = true cairo-lang-utils.workspace = true cairo-lang-test-plugin.workspace = true cairo-lang-starknet-classes.workspace = true cairo-annotations.workspace = true starknet-types-core.workspace = true starknet_api.workspace = true starknet-rust.workspace = true futures.workspace = true tokio.workspace = true num-traits.workspace = true rand.workspace = true url.workspace = true blockifier.workspace = true cairo-vm.workspace = true itertools.workspace = true indoc.workspace = true camino.workspace = true serde_json.workspace = true console.workspace = true serde.workspace = true rayon.workspace = true cheatnet = { path = "../cheatnet" } runtime = { path = "../runtime" } conversions = { path = "../conversions" } shared = { path = "../shared" } debugging = { path = "../debugging" } universal-sierra-compiler-api = { path = "../universal-sierra-compiler-api" } which.workspace = true sanitize-filename.workspace = true clap.workspace = true foundry-ui = { path = "../foundry-ui" } strum.workspace = true strum_macros.workspace = true scarb-oracle-hint-service.workspace = true tracing.workspace = true comfy-table.workspace = true scarb-api = { path = "../scarb-api" } cairo-debugger.workspace = true [dev-dependencies] test-case.workspace = true ================================================ FILE: crates/forge-runner/src/backtrace/data.rs ================================================ use crate::backtrace::display::{Backtrace, BacktraceStack, render_fork_backtrace}; use anyhow::Context; use anyhow::Result; use cairo_annotations::annotations::TryFromDebugInfo; use cairo_annotations::annotations::coverage::{ CoverageAnnotationsV1, VersionedCoverageAnnotations, }; use cairo_annotations::annotations::profiler::{ ProfilerAnnotationsV1, VersionedProfilerAnnotations, }; use cairo_lang_sierra::program::StatementIdx; use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; use cairo_lang_starknet_classes::contract_class::ContractClass; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; use itertools::Itertools; use rayon::iter::IntoParallelIterator; use rayon::iter::ParallelIterator; use starknet_api::core::ClassHash; use std::collections::{HashMap, HashSet}; pub struct ContractBacktraceDataMapping(HashMap); impl ContractBacktraceDataMapping { pub fn new(contracts_data: &ContractsData, class_hashes: HashSet) -> Result { Ok(Self( class_hashes .into_par_iter() .map(|class_hash| { ContractOrigin::new(&class_hash, contracts_data) .map(|contract_data| (class_hash, contract_data)) }) .collect::>()?, )) } pub fn render_backtrace(&self, pcs: &[usize], class_hash: &ClassHash) -> Result { self.0 .get(class_hash) .expect("class hash should be present in the data mapping") .render_backtrace(pcs) } } enum ContractOrigin { Fork(ClassHash), Local(ContractBacktraceData), } impl ContractOrigin { fn new(class_hash: &ClassHash, contracts_data: &ContractsData) -> Result { if contracts_data.is_fork_class_hash(class_hash) { Ok(ContractOrigin::Fork(*class_hash)) } else { Ok(ContractOrigin::Local(ContractBacktraceData::new( class_hash, contracts_data, )?)) } } fn render_backtrace(&self, pcs: &[usize]) -> Result { match self { ContractOrigin::Fork(class_hash) => Ok(render_fork_backtrace(class_hash)), ContractOrigin::Local(data) => data.render_backtrace(pcs), } } } struct ContractBacktraceData { contract_name: String, casm_debug_info_start_offsets: Vec, coverage_annotations: CoverageAnnotationsV1, profiler_annotations: ProfilerAnnotationsV1, } impl ContractBacktraceData { fn new(class_hash: &ClassHash, contracts_data: &ContractsData) -> Result { let contract_name = contracts_data .get_contract_name(class_hash) .context(format!( "failed to get contract name for class hash: {class_hash}" ))? .clone(); let contract_artifacts = contracts_data .get_artifacts(&contract_name) .context(format!( "failed to get artifacts for contract name: {contract_name}" ))?; let contract_class = serde_json::from_str::(&contract_artifacts.sierra)?; let sierra_debug_info = contract_class .sierra_program_debug_info .as_ref() .context("debug info not found")?; let VersionedCoverageAnnotations::V1(coverage_annotations) = VersionedCoverageAnnotations::try_from_debug_info(sierra_debug_info) .expect("this should not fail, as we are doing validation in the `can_backtrace_be_generated` function"); let VersionedProfilerAnnotations::V1(profiler_annotations) = VersionedProfilerAnnotations::try_from_debug_info(sierra_debug_info).expect("this should not fail, as we are doing validation in the `can_backtrace_be_generated` function"); let extracted_sierra = contract_class .extract_sierra_program(false) .expect("extraction should succeed"); // FIXME(https://github.com/software-mansion/universal-sierra-compiler/issues/98): Use CASM debug info from USC once it provides it. let (_, debug_info) = CasmContractClass::from_contract_class_with_debug_info( contract_class, extracted_sierra, true, usize::MAX, )?; let casm_debug_info_start_offsets = debug_info .sierra_statement_info .iter() .map(|statement_debug_info| statement_debug_info.start_offset) .collect(); Ok(Self { contract_name, casm_debug_info_start_offsets, coverage_annotations, profiler_annotations, }) } fn backtrace_from(&self, pc: usize) -> Result>> { let sierra_statement_idx = StatementIdx( self.casm_debug_info_start_offsets .partition_point(|start_offset| *start_offset < pc - 1) .saturating_sub(1), ); let code_locations = self .coverage_annotations .statements_code_locations .get(&sierra_statement_idx) .with_context(|| { format!("failed to get code locations for statement idx: {sierra_statement_idx}") })?; let function_names = self .profiler_annotations .statements_functions .get(&sierra_statement_idx) .with_context(|| { format!("failed to get function names for statement idx: {sierra_statement_idx}") })?; let stack = code_locations .iter() .zip(function_names) .enumerate() .map(|(index, (code_location, function_name))| { let is_not_last = index != function_names.len() - 1; // `function_names is the stack of: // "functions that were inlined or generated along the way up // to the first non-inlined function from the original code. // The vector represents the stack from the least meaningful elements." // ~ from doc of `ProfilerAnnotationsV1` // So we need to check if the function name is not the last one then it is inlined Backtrace { inlined: is_not_last, code_location, function_name, } }) .collect(); Ok(stack) } fn render_backtrace(&self, pcs: &[usize]) -> Result { let stack = pcs .iter() .map(|pc| self.backtrace_from(*pc)) .flatten_ok() .collect::>>()?; let contract_name = &self.contract_name; let backtrace_stack = BacktraceStack { contract_name, stack, }; Ok(backtrace_stack.to_string()) } } ================================================ FILE: crates/forge-runner/src/backtrace/display.rs ================================================ use cairo_annotations::annotations::coverage::{CodeLocation, ColumnNumber, LineNumber}; use cairo_annotations::annotations::profiler::FunctionName; use starknet_api::core::ClassHash; use std::fmt; use std::fmt::Display; pub struct Backtrace<'a> { pub code_location: &'a CodeLocation, pub function_name: &'a FunctionName, pub inlined: bool, } pub struct BacktraceStack<'a> { pub contract_name: &'a str, pub stack: Vec>, } impl Display for Backtrace<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let function_name = &self.function_name.0; let path = &self.code_location.0; let line = self.code_location.1.start.line + LineNumber(1); // most editors start line numbers from 1 let col = self.code_location.1.start.col + ColumnNumber(1); // most editors start column numbers from 1 if self.inlined { write!(f, "(inlined) ")?; } write!(f, "{function_name}\n at {path}:{line}:{col}") } } impl Display for BacktraceStack<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "error occurred in contract '{}'", self.contract_name)?; writeln!(f, "stack backtrace:")?; for (i, backtrace) in self.stack.iter().enumerate() { writeln!(f, " {i}: {backtrace}")?; } Ok(()) } } pub fn render_fork_backtrace(contract_class_hash: &ClassHash) -> String { format!( "error occurred in forked contract with class hash: {:#x}\n", contract_class_hash.0 ) } ================================================ FILE: crates/forge-runner/src/backtrace/mod.rs ================================================ use crate::backtrace::data::ContractBacktraceDataMapping; use anyhow::Result; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; use cheatnet::state::EncounteredErrors; use std::env; mod data; mod display; const BACKTRACE_ENV: &str = "SNFORGE_BACKTRACE"; #[must_use] pub fn add_backtrace_footer( message: String, contracts_data: &ContractsData, encountered_errors: &EncounteredErrors, ) -> String { if encountered_errors.is_empty() { return message; } let backtrace = if is_backtrace_enabled() { get_backtrace(contracts_data, encountered_errors) } else { format!("note: run with `{BACKTRACE_ENV}=1` environment variable to display a backtrace") }; format!("{message}\n{backtrace}") } #[must_use] pub fn get_backtrace( contracts_data: &ContractsData, encountered_errors: &EncounteredErrors, ) -> String { let class_hashes = encountered_errors.keys().copied().collect(); ContractBacktraceDataMapping::new(contracts_data, class_hashes) .and_then(|data_mapping| { encountered_errors .iter() .map(|(class_hash, pcs)| data_mapping.render_backtrace(pcs, class_hash)) .collect::>>() .map(|backtrace| backtrace.join("\n")) }) .unwrap_or_else(|err| format!("failed to create backtrace: {err}")) } #[must_use] pub fn is_backtrace_enabled() -> bool { env::var(BACKTRACE_ENV).is_ok_and(|value| value == "1") } ================================================ FILE: crates/forge-runner/src/build_trace_data.rs ================================================ use anyhow::{Context, Result}; use blockifier::execution::deprecated_syscalls::DeprecatedSyscallSelector; use blockifier::execution::entry_point::{CallEntryPoint, CallType}; use blockifier::execution::syscalls::vm_syscall_utils::{ SyscallSelector, SyscallUsage, SyscallUsageMap, }; use blockifier::blockifier_versioned_constants::VersionedConstants; use blockifier::execution::call_info::{ExtendedExecutionResources, OrderedEvent}; use cairo_annotations::trace_data::{ CairoExecutionInfo, CallEntryPoint as ProfilerCallEntryPoint, CallTraceNode as ProfilerCallTraceNode, CallTraceV1 as ProfilerCallTrace, CallType as ProfilerCallType, CasmLevelInfo, ContractAddress, DeprecatedSyscallSelector as ProfilerDeprecatedSyscallSelector, EntryPointSelector as ProfilerEntryPointSelector, EntryPointType as ProfilerEntryPointType, ExecutionResources as ProfilerExecutionResources, SummedUpEvent, SyscallUsage as ProfilerSyscallUsage, TraceEntry as ProfilerTraceEntry, VersionedCallTrace as VersionedProfilerCallTrace, VmExecutionResources, }; use cairo_vm::vm::trace::trace_entry::RelocatedTraceEntry; use camino::{Utf8Path, Utf8PathBuf}; use cheatnet::forking::data::ForkData; use cheatnet::runtime_extensions::common::{get_syscalls_gas_consumed, sum_syscall_usage}; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; use cheatnet::trace_data::{CallTrace, CallTraceNode}; use conversions::IntoConv; use conversions::string::TryFromHexStr; use runtime::starknet::constants::{TEST_CONTRACT_CLASS_HASH, TEST_ENTRY_POINT_SELECTOR}; use starknet_api::contract_class::EntryPointType; use starknet_api::core::{ClassHash, EntryPointSelector}; use starknet_api::versioned_constants_logic::VersionedConstantsTrait; use starknet_rust::core::utils::get_selector_from_name; use starknet_types_core::felt::Felt; use std::cell::RefCell; use std::fs; use std::path::PathBuf; use std::rc::Rc; pub const TRACE_DIR: &str = "snfoundry_trace"; pub const TEST_CODE_CONTRACT_NAME: &str = "SNFORGE_TEST_CODE"; pub const TEST_CODE_FUNCTION_NAME: &str = "SNFORGE_TEST_CODE_FUNCTION"; pub fn build_profiler_call_trace( value: &Rc>, contracts_data: &ContractsData, fork_data: &ForkData, versioned_program_path: &Utf8Path, ) -> ProfilerCallTrace { let value = value.borrow(); let entry_point = build_profiler_call_entry_point( value.entry_point.clone(), contracts_data, fork_data, &value.events, &value.signature, ); let vm_trace = value .vm_trace .as_ref() .map(|trace_data| trace_data.iter().map(build_profiler_trace_entry).collect()); let cairo_execution_info = build_cairo_execution_info( &value.entry_point, vm_trace, contracts_data, versioned_program_path, ); ProfilerCallTrace { entry_point, cumulative_resources: build_profiler_execution_resources( &value.used_execution_resources, &value.used_syscalls_vm_resources, &value.used_syscalls_sierra_gas, value.gas_consumed, ), used_l1_resources: value.used_l1_resources.clone(), nested_calls: value .nested_calls .iter() .map(|c| { build_profiler_call_trace_node(c, contracts_data, fork_data, versioned_program_path) }) .collect(), cairo_execution_info, } } fn build_cairo_execution_info( entry_point: &CallEntryPoint, vm_trace: Option>, contracts_data: &ContractsData, versioned_program_path: &Utf8Path, ) -> Option { let contract_name = get_contract_name(entry_point.class_hash, contracts_data); let source_sierra_path = contract_name .and_then(|name| get_source_sierra_path(&name, contracts_data, versioned_program_path)); Some(CairoExecutionInfo { casm_level_info: CasmLevelInfo { run_with_call_header: false, vm_trace: vm_trace?, program_offset: None, }, source_sierra_path: source_sierra_path?, enable_gas: None, }) } fn get_source_sierra_path( contract_name: &str, contracts_data: &ContractsData, versioned_program_path: &Utf8Path, ) -> Option { if contract_name == TEST_CODE_CONTRACT_NAME { Some(versioned_program_path.into()) } else { contracts_data .get_source_sierra_path(contract_name) .cloned() } } fn build_profiler_call_trace_node( value: &CallTraceNode, contracts_data: &ContractsData, fork_data: &ForkData, versioned_program_path: &Utf8Path, ) -> ProfilerCallTraceNode { match value { CallTraceNode::EntryPointCall(trace) => ProfilerCallTraceNode::EntryPointCall(Box::new( build_profiler_call_trace(trace, contracts_data, fork_data, versioned_program_path), )), CallTraceNode::DeployWithoutConstructor => ProfilerCallTraceNode::DeployWithoutConstructor, } } #[must_use] pub fn build_profiler_execution_resources( execution_resources: &ExtendedExecutionResources, syscall_usage_vm_resources: &SyscallUsageMap, syscall_usage_sierra_gas: &SyscallUsageMap, gas_consumed: u64, ) -> ProfilerExecutionResources { // Subtract syscall related resources to get the values expected by the profiler. // The profiler operates on resources excluding syscall overhead. let versioned_constants = VersionedConstants::latest_constants(); let opcodes = execution_resources.opcode_instance_counter.clone(); let execution_resources = &execution_resources.vm_resources - &versioned_constants.get_additional_os_syscall_resources(syscall_usage_vm_resources); let gas_consumed = gas_consumed - get_syscalls_gas_consumed(syscall_usage_sierra_gas, versioned_constants); let syscall_usage = sum_syscall_usage(syscall_usage_vm_resources.clone(), syscall_usage_sierra_gas); let profiler_syscall_counter = syscall_usage .into_iter() .map(|(key, val)| { ( build_profiler_deprecated_syscall_selector(key), build_profiler_syscall_usage(val), ) }) .collect(); ProfilerExecutionResources { vm_resources: VmExecutionResources { n_steps: execution_resources.n_steps, n_memory_holes: execution_resources.n_memory_holes, builtin_instance_counter: execution_resources .builtin_instance_counter .into_iter() .map(|(key, value)| (key.to_str_with_suffix().to_owned(), value)) .chain( // Treat opcodes as builtin instances. opcodes .into_iter() .map(|(key, value)| (key.to_str_with_suffix().to_owned(), value)), ) .collect(), }, gas_consumed: Some(gas_consumed), syscall_counter: Some(profiler_syscall_counter), } } #[must_use] pub fn build_profiler_call_entry_point( value: CallEntryPoint, contracts_data: &ContractsData, fork_data: &ForkData, events: &[OrderedEvent], signature: &[Felt], ) -> ProfilerCallEntryPoint { let CallEntryPoint { class_hash, entry_point_type, entry_point_selector, storage_address, call_type, calldata, .. } = value; let contract_name = get_contract_name(class_hash, contracts_data); let function_name = get_function_name(&entry_point_selector, contracts_data, fork_data); let calldata_len = calldata.0.len(); let signature_len = signature.len(); ProfilerCallEntryPoint { class_hash: class_hash.map(|ch| cairo_annotations::trace_data::ClassHash(ch.0)), entry_point_type: build_profiler_entry_point_type(entry_point_type), entry_point_selector: ProfilerEntryPointSelector(entry_point_selector.0), contract_address: ContractAddress(*storage_address.0.key()), call_type: build_profiler_call_type(call_type), contract_name, function_name, calldata_len: Some(calldata_len), events_summary: Some(to_summed_up_events(events)), signature_len: Some(signature_len), } } fn get_contract_name( class_hash: Option, contracts_data: &ContractsData, ) -> Option { if class_hash == Some(TryFromHexStr::try_from_hex_str(TEST_CONTRACT_CLASS_HASH).unwrap()) { Some(String::from(TEST_CODE_CONTRACT_NAME)) } else { class_hash .and_then(|c| contracts_data.get_contract_name(&c)) .cloned() } } fn get_function_name( entry_point_selector: &EntryPointSelector, contracts_data: &ContractsData, fork_data: &ForkData, ) -> Option { if entry_point_selector.0 == get_selector_from_name(TEST_ENTRY_POINT_SELECTOR) .unwrap() .into_() { Some(TEST_CODE_FUNCTION_NAME.to_string()) } else if let Some(name) = contracts_data .get_function_name(entry_point_selector) .cloned() { Some(name) } else { fork_data.selectors.get(entry_point_selector).cloned() } } fn build_profiler_entry_point_type(value: EntryPointType) -> ProfilerEntryPointType { match value { EntryPointType::Constructor => ProfilerEntryPointType::Constructor, EntryPointType::External => ProfilerEntryPointType::External, EntryPointType::L1Handler => ProfilerEntryPointType::L1Handler, } } fn build_profiler_deprecated_syscall_selector( value: SyscallSelector, ) -> ProfilerDeprecatedSyscallSelector { // Warning: Do not add a default (`_`) arm here. // This match must remain exhaustive so that if a new syscall is introduced, // we will explicitly add support for it. match value { DeprecatedSyscallSelector::CallContract => ProfilerDeprecatedSyscallSelector::CallContract, DeprecatedSyscallSelector::DelegateCall => ProfilerDeprecatedSyscallSelector::DelegateCall, DeprecatedSyscallSelector::DelegateL1Handler => { ProfilerDeprecatedSyscallSelector::DelegateL1Handler } DeprecatedSyscallSelector::Deploy => ProfilerDeprecatedSyscallSelector::Deploy, DeprecatedSyscallSelector::EmitEvent => ProfilerDeprecatedSyscallSelector::EmitEvent, DeprecatedSyscallSelector::GetBlockHash => ProfilerDeprecatedSyscallSelector::GetBlockHash, DeprecatedSyscallSelector::GetBlockNumber => { ProfilerDeprecatedSyscallSelector::GetBlockNumber } DeprecatedSyscallSelector::GetBlockTimestamp => { ProfilerDeprecatedSyscallSelector::GetBlockTimestamp } DeprecatedSyscallSelector::GetCallerAddress => { ProfilerDeprecatedSyscallSelector::GetCallerAddress } DeprecatedSyscallSelector::GetContractAddress => { ProfilerDeprecatedSyscallSelector::GetContractAddress } DeprecatedSyscallSelector::GetExecutionInfo => { ProfilerDeprecatedSyscallSelector::GetExecutionInfo } DeprecatedSyscallSelector::GetSequencerAddress => { ProfilerDeprecatedSyscallSelector::GetSequencerAddress } DeprecatedSyscallSelector::GetTxInfo => ProfilerDeprecatedSyscallSelector::GetTxInfo, DeprecatedSyscallSelector::GetTxSignature => { ProfilerDeprecatedSyscallSelector::GetTxSignature } DeprecatedSyscallSelector::Keccak => ProfilerDeprecatedSyscallSelector::Keccak, DeprecatedSyscallSelector::LibraryCall => ProfilerDeprecatedSyscallSelector::LibraryCall, DeprecatedSyscallSelector::LibraryCallL1Handler => { ProfilerDeprecatedSyscallSelector::LibraryCallL1Handler } DeprecatedSyscallSelector::ReplaceClass => ProfilerDeprecatedSyscallSelector::ReplaceClass, DeprecatedSyscallSelector::Secp256k1Add => ProfilerDeprecatedSyscallSelector::Secp256k1Add, DeprecatedSyscallSelector::Secp256k1GetPointFromX => { ProfilerDeprecatedSyscallSelector::Secp256k1GetPointFromX } DeprecatedSyscallSelector::Secp256k1GetXy => { ProfilerDeprecatedSyscallSelector::Secp256k1GetXy } DeprecatedSyscallSelector::Secp256k1Mul => ProfilerDeprecatedSyscallSelector::Secp256k1Mul, DeprecatedSyscallSelector::Secp256k1New => ProfilerDeprecatedSyscallSelector::Secp256k1New, DeprecatedSyscallSelector::Secp256r1Add => ProfilerDeprecatedSyscallSelector::Secp256r1Add, DeprecatedSyscallSelector::Secp256r1GetPointFromX => { ProfilerDeprecatedSyscallSelector::Secp256r1GetPointFromX } DeprecatedSyscallSelector::Secp256r1GetXy => { ProfilerDeprecatedSyscallSelector::Secp256r1GetXy } DeprecatedSyscallSelector::Secp256r1Mul => ProfilerDeprecatedSyscallSelector::Secp256r1Mul, DeprecatedSyscallSelector::Secp256r1New => ProfilerDeprecatedSyscallSelector::Secp256r1New, DeprecatedSyscallSelector::SendMessageToL1 => { ProfilerDeprecatedSyscallSelector::SendMessageToL1 } DeprecatedSyscallSelector::StorageRead => ProfilerDeprecatedSyscallSelector::StorageRead, DeprecatedSyscallSelector::StorageWrite => ProfilerDeprecatedSyscallSelector::StorageWrite, DeprecatedSyscallSelector::Sha256ProcessBlock => { ProfilerDeprecatedSyscallSelector::Sha256ProcessBlock } DeprecatedSyscallSelector::GetClassHashAt => { ProfilerDeprecatedSyscallSelector::GetClassHashAt } DeprecatedSyscallSelector::KeccakRound => ProfilerDeprecatedSyscallSelector::KeccakRound, DeprecatedSyscallSelector::MetaTxV0 => ProfilerDeprecatedSyscallSelector::MetaTxV0, } } fn build_profiler_syscall_usage( SyscallUsage { call_count, linear_factor, }: SyscallUsage, ) -> ProfilerSyscallUsage { ProfilerSyscallUsage { call_count, linear_factor, } } fn build_profiler_call_type(value: CallType) -> ProfilerCallType { match value { CallType::Call => ProfilerCallType::Call, CallType::Delegate => ProfilerCallType::Delegate, } } fn build_profiler_trace_entry(value: &RelocatedTraceEntry) -> ProfilerTraceEntry { ProfilerTraceEntry { pc: value.pc, ap: value.ap, fp: value.fp, } } pub fn save_trace_data( test_name: &str, trace_data: &VersionedProfilerCallTrace, ) -> Result { let serialized_trace = serde_json::to_string(trace_data).expect("Failed to serialize call trace"); let dir_to_save_trace = PathBuf::from(TRACE_DIR); fs::create_dir_all(&dir_to_save_trace).context("Failed to create a .trace_data directory")?; let filename = format!("{test_name}.json"); fs::write(dir_to_save_trace.join(&filename), serialized_trace) .context("Failed to write call trace to a file")?; Ok(dir_to_save_trace.join(&filename)) } fn to_summed_up_events(events: &[OrderedEvent]) -> Vec { events .iter() .map(|ev| SummedUpEvent { keys_len: ev.event.keys.len(), data_len: ev.event.data.0.len(), }) .collect() } ================================================ FILE: crates/forge-runner/src/coverage_api.rs ================================================ use anyhow::{Context, Result, ensure}; use indoc::indoc; use shared::command::CommandExt; use std::ffi::OsString; use std::process::Stdio; use std::{env, fs, path::PathBuf, process::Command}; use which::which; pub const COVERAGE_DIR: &str = "coverage"; pub const OUTPUT_FILE_NAME: &str = "coverage.lcov"; pub fn run_coverage(saved_trace_data_paths: &[PathBuf], coverage_args: &[OsString]) -> Result<()> { let coverage = env::var("CAIRO_COVERAGE") .map(PathBuf::from) .ok() .unwrap_or_else(|| PathBuf::from("cairo-coverage")); ensure!( which(coverage.as_os_str()).is_ok(), indoc! { r"The 'cairo-coverage' binary was not found in PATH. It may not have been installed. Please refer to the documentation for installation instructions: https://github.com/software-mansion/cairo-coverage/blob/main/README.md" } ); let trace_files: Vec<&str> = saved_trace_data_paths .iter() .map(|trace_data_path| { trace_data_path .to_str() .expect("Failed to convert trace data path to string") }) .collect(); let mut command = Command::new(coverage); command.arg("run"); if coverage_args.iter().all(|arg| arg != "--output-path") { let dir_to_save_coverage = PathBuf::from(COVERAGE_DIR); fs::create_dir_all(&dir_to_save_coverage).context("Failed to create a coverage dir")?; let path_to_save_coverage = dir_to_save_coverage.join(OUTPUT_FILE_NAME); command.arg("--output-path").arg(&path_to_save_coverage); } command .args(trace_files) .args(coverage_args) .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) .output_checked() .with_context(|| { "cairo-coverage failed to generate coverage - inspect the errors above for more info" })?; Ok(()) } ================================================ FILE: crates/forge-runner/src/debugging/args.rs ================================================ use crate::debugging::TraceVerbosity; use crate::debugging::component::Component; use clap::Args; use debugging::Components; #[derive(Args, Debug, Clone, Default, Eq, PartialEq)] #[group(required = false, multiple = false)] pub struct TraceArgs { /// Trace verbosity level #[arg(long)] trace_verbosity: Option, /// Components to include in the trace. #[arg(long, num_args = 1.., value_delimiter = ' ')] trace_components: Option>, } impl TraceArgs { /// Returns the [`Option`] based on the provided arguments. #[must_use] pub fn to_components(&self) -> Option { match (&self.trace_components, &self.trace_verbosity) { (None, Some(verbosity)) => Some(build_components(verbosity.to_components_vec())), (Some(components), None) => Some(build_components(components)), (None, None) => None, (Some(_), Some(_)) => { unreachable!("this case is impossible, as it is handled by clap") } } } } fn build_components<'a>(iter: impl IntoIterator) -> Components { Components::new(iter.into_iter().map(debugging::Component::from).collect()) } ================================================ FILE: crates/forge-runner/src/debugging/component.rs ================================================ use crate::debugging::trace_verbosity::TraceVerbosity; use clap::ValueEnum; use strum_macros::VariantArray; /// Components that will be included in the trace. #[derive(ValueEnum, Clone, VariantArray, Debug, Eq, PartialEq)] pub enum Component { /// The name of the contract being called. ContractName, /// The type of the entry point being called (e.g., `External`, `L1Handler`, etc.). EntryPointType, /// The calldata of the call, transformed for display. Calldata, /// The address of the contract being called. ContractAddress, /// The address of the caller contract. CallerAddress, /// The type of the call (e.g., `Call`, `Delegate`, etc.). CallType, /// The result of the call, transformed for display. CallResult, /// The L2 gas used by the call. Gas, } impl Component { /// Returns minimal [`TraceVerbosity`] for the component. #[must_use] pub fn verbosity(&self) -> TraceVerbosity { match self { Component::ContractName => TraceVerbosity::Minimal, Component::Calldata | Component::CallResult => TraceVerbosity::Standard, Component::ContractAddress | Component::CallerAddress | Component::EntryPointType | Component::CallType | Component::Gas => TraceVerbosity::Detailed, } } } impl From<&Component> for debugging::Component { fn from(component: &Component) -> Self { match component { Component::ContractName => debugging::Component::ContractName, Component::EntryPointType => debugging::Component::EntryPointType, Component::Calldata => debugging::Component::Calldata, Component::ContractAddress => debugging::Component::ContractAddress, Component::CallerAddress => debugging::Component::CallerAddress, Component::CallType => debugging::Component::CallType, Component::CallResult => debugging::Component::CallResult, Component::Gas => debugging::Component::Gas, } } } ================================================ FILE: crates/forge-runner/src/debugging/mod.rs ================================================ mod args; mod component; mod trace_verbosity; use cheatnet::forking::data::ForkData; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; use cheatnet::trace_data::CallTrace; pub use args::TraceArgs; pub use trace_verbosity::TraceVerbosity; #[must_use] pub fn build_debugging_trace( call_trace: &CallTrace, contracts_data: &ContractsData, trace_args: &TraceArgs, test_name: String, fork_data: &ForkData, ) -> Option { let components = trace_args.to_components()?; let context = debugging::Context::new(contracts_data, fork_data, components); Some(debugging::Trace::new(call_trace, &context, test_name)) } ================================================ FILE: crates/forge-runner/src/debugging/trace_verbosity.rs ================================================ use crate::debugging::component::Component; use clap::ValueEnum; use strum::VariantArray; /// Trace verbosity level. #[derive(ValueEnum, Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)] pub enum TraceVerbosity { /// Display test name, contract name and selector. Minimal, /// Display test name, contract name, selector, calldata and call result. Standard, /// Display everything. Detailed, } impl TraceVerbosity { /// Converts the [`TraceVerbosity`] to a vector of [`Component`]. #[must_use] pub fn to_components_vec(&self) -> Vec<&Component> { Component::VARIANTS .iter() .filter(|component| component.verbosity() <= *self) .collect() } } ================================================ FILE: crates/forge-runner/src/expected_result.rs ================================================ // Our custom structs used to prevent name changes in structs on side of cairo compiler from breaking the test collector backwards compatibility use cairo_lang_test_plugin::test_config::{PanicExpectation, TestExpectation}; use serde::Deserialize; use starknet_types_core::felt::Felt; /// Expectation for a panic case. #[derive(Debug, Clone, PartialEq, Deserialize)] pub enum ExpectedPanicValue { /// Accept any panic value. Any, /// Accept only this specific vector of panics. Exact(Vec), } impl From for ExpectedPanicValue { fn from(value: PanicExpectation) -> Self { match value { PanicExpectation::Any => ExpectedPanicValue::Any, PanicExpectation::Exact(vec) => ExpectedPanicValue::Exact(vec), } } } /// Expectation for a result of a test. #[derive(Debug, Clone, PartialEq, Deserialize)] pub enum ExpectedTestResult { /// Running the test should not panic. Success, /// Running the test should result in a panic. Panics(ExpectedPanicValue), } impl From for ExpectedTestResult { fn from(value: TestExpectation) -> Self { match value { TestExpectation::Success => ExpectedTestResult::Success, TestExpectation::Panics(panic_expectation) => { ExpectedTestResult::Panics(panic_expectation.into()) } } } } ================================================ FILE: crates/forge-runner/src/filtering.rs ================================================ use crate::package_tests::TestCase; /// Result of filtering a test case. #[derive(Debug)] pub enum FilterResult { /// Test case should be included. Included, /// Test case should be excluded for the given reason. Excluded(ExcludeReason), } /// Reason for excluding a test case. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ExcludeReason { /// Test case is ignored. Ignored, /// Test case is excluded from the current partition. ExcludedFromPartition, } pub trait TestCaseFilter { fn filter(&self, test_case: &TestCase) -> FilterResult where T: TestCaseIsIgnored; } pub trait TestCaseIsIgnored { fn is_ignored(&self) -> bool; } ================================================ FILE: crates/forge-runner/src/forge_config.rs ================================================ use crate::debugging::TraceArgs; use blockifier::execution::contract_class::TrackedResource; use camino::Utf8PathBuf; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; use clap::ValueEnum; use serde::Deserialize; use std::collections::HashMap; use std::ffi::OsString; use std::num::NonZeroU32; use std::sync::Arc; #[derive(Debug, PartialEq)] pub struct ForgeConfig { pub test_runner_config: Arc, pub output_config: Arc, } #[expect(clippy::struct_excessive_bools)] #[derive(Debug, PartialEq)] pub struct TestRunnerConfig { pub exit_first: bool, pub deterministic_output: bool, pub fuzzer_runs: NonZeroU32, pub fuzzer_seed: u64, pub max_n_steps: Option, pub is_vm_trace_needed: bool, pub cache_dir: Utf8PathBuf, pub contracts_data: ContractsData, pub environment_variables: HashMap, pub tracked_resource: ForgeTrackedResource, pub launch_debugger: bool, } #[derive(Debug, PartialEq)] pub struct OutputConfig { pub trace_args: TraceArgs, pub detailed_resources: bool, pub execution_data_to_save: ExecutionDataToSave, pub gas_report: bool, } #[derive(Debug, PartialEq, Clone, Default)] pub struct ExecutionDataToSave { pub trace: bool, pub profile: bool, pub coverage: bool, pub additional_args: Vec, } #[derive(Clone, Copy, Debug, Default, PartialEq, Deserialize, Eq, ValueEnum)] pub enum ForgeTrackedResource { CairoSteps, #[default] SierraGas, } impl From<&ForgeTrackedResource> for TrackedResource { fn from(m: &ForgeTrackedResource) -> Self { match m { ForgeTrackedResource::CairoSteps => TrackedResource::CairoSteps, ForgeTrackedResource::SierraGas => TrackedResource::SierraGas, } } } impl ExecutionDataToSave { #[must_use] pub fn from_flags( save_trace_data: bool, build_profile: bool, coverage: bool, additional_args: &[OsString], ) -> Self { Self { trace: save_trace_data, profile: build_profile, coverage, additional_args: additional_args.to_vec(), } } #[must_use] pub fn is_vm_trace_needed(&self) -> bool { self.trace || self.profile || self.coverage } } /// This struct should be constructed on demand to pass only relevant information from /// [`TestRunnerConfig`] to another function. pub struct RuntimeConfig<'a> { pub max_n_steps: Option, pub is_vm_trace_needed: bool, pub cache_dir: &'a Utf8PathBuf, pub contracts_data: &'a ContractsData, pub environment_variables: &'a HashMap, pub tracked_resource: &'a ForgeTrackedResource, pub launch_debugger: bool, } impl<'a> RuntimeConfig<'a> { #[must_use] pub fn from(value: &'a TestRunnerConfig) -> RuntimeConfig<'a> { Self { max_n_steps: value.max_n_steps, is_vm_trace_needed: value.is_vm_trace_needed, cache_dir: &value.cache_dir, contracts_data: &value.contracts_data, environment_variables: &value.environment_variables, tracked_resource: &value.tracked_resource, launch_debugger: value.launch_debugger, } } } ================================================ FILE: crates/forge-runner/src/gas/report.rs ================================================ use crate::gas::stats::GasStats; use crate::gas::utils::shorten_felt; use cheatnet::trace_data::{CallTrace, CallTraceNode}; use comfy_table::modifiers::UTF8_ROUND_CORNERS; use comfy_table::{Attribute, Cell, Color, Table}; use debugging::ContractsDataStore; use starknet_api::core::{ClassHash, EntryPointSelector}; use starknet_api::execution_resources::GasVector; use std::collections::BTreeMap; use std::fmt; use std::fmt::Display; type ContractName = String; type Selector = String; #[derive(Debug, Clone, PartialOrd, PartialEq, Ord, Eq)] pub enum ContractId { LocalContract(ContractName), ForkedContract(ClassHash), } #[derive(Debug, Clone)] pub struct SingleTestGasInfo { pub gas_used: GasVector, pub report_data: Option, } #[derive(Debug, Clone, Default)] pub struct ReportData(BTreeMap); #[derive(Debug, Clone, Default)] pub struct ContractInfo { pub(super) gas_used: GasVector, pub(super) functions: BTreeMap, } #[derive(Debug, Clone, Default)] pub struct SelectorReportData { pub(super) gas_stats: GasStats, pub(super) n_calls: u64, pub(super) records: Vec, } impl SingleTestGasInfo { #[must_use] pub(crate) fn new(gas_used: GasVector) -> Self { Self { gas_used, report_data: None, } } pub(crate) fn get_with_report_data( self, trace: &CallTrace, contracts_data: &ContractsDataStore, ) -> Self { let mut report_data = ReportData::default(); let mut stack = trace.nested_calls.clone(); while let Some(call_trace_node) = stack.pop() { if let CallTraceNode::EntryPointCall(call) = call_trace_node { let call = call.borrow(); // Class hash should be `None` only in case of a mock call. // Otherwise, it is set in `execute_call_entry_point`. if let Some(class_hash) = call.entry_point.class_hash { let contract_id = get_contract_id(contracts_data, class_hash); let selector = get_selector(contracts_data, call.entry_point.entry_point_selector); let gas = call .gas_report_data .as_ref() .expect("Gas report data must be updated after test execution") .get_gas(); report_data.update_entry(contract_id, selector, gas); stack.extend(call.nested_calls.clone()); } } } report_data.finalize(); Self { gas_used: self.gas_used, report_data: Some(report_data), } } } impl ReportData { fn update_entry(&mut self, contract_id: ContractId, selector: Selector, gas_used: GasVector) { let contract_info = self.0.entry(contract_id).or_default(); let current_gas = contract_info.gas_used; contract_info.gas_used = current_gas.checked_add(gas_used).unwrap_or_else(|| { panic!("Gas addition overflow when adding {gas_used:?} to {current_gas:?}.") }); let entry = contract_info.functions.entry(selector).or_default(); entry.records.push(gas_used.l2_gas.0); entry.n_calls += 1; } fn finalize(&mut self) { for contract_info in self.0.values_mut() { for gas_info in contract_info.functions.values_mut() { gas_info.gas_stats = GasStats::new(&gas_info.records); } } } } impl Display for ReportData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.0.is_empty() { writeln!( f, "\nNo contract gas usage data to display, no contract calls made." )?; } for (name, contract_info) in &self.0 { let table = format_table_output(contract_info, name); writeln!(f, "\n{table}")?; } Ok(()) } } impl Display for ContractId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let name = match self { ContractId::LocalContract(name) => format!("{name} Contract"), ContractId::ForkedContract(class_hash) => { format!( "forked contract\n(class hash: {})", shorten_felt(class_hash.0) ) } }; write!(f, "{name}") } } fn get_contract_id(contracts_data: &ContractsDataStore, class_hash: ClassHash) -> ContractId { match contracts_data.get_contract_name(&class_hash) { Some(name) => ContractId::LocalContract(name.0.clone()), None => ContractId::ForkedContract(class_hash), } } fn get_selector(contracts_data: &ContractsDataStore, selector: EntryPointSelector) -> Selector { contracts_data .get_selector(&selector) .expect("`Selector` should be present") .0 .clone() } pub fn format_table_output(contract_info: &ContractInfo, contract_id: &ContractId) -> Table { let mut table = Table::new(); table.apply_modifier(UTF8_ROUND_CORNERS); table.set_header(vec![Cell::new(contract_id.to_string()).fg(Color::Magenta)]); table.add_row(vec![ Cell::new("Function Name").add_attribute(Attribute::Bold), Cell::new("Min").add_attribute(Attribute::Bold), Cell::new("Max").add_attribute(Attribute::Bold), Cell::new("Avg").add_attribute(Attribute::Bold), Cell::new("Std Dev").add_attribute(Attribute::Bold), Cell::new("# Calls").add_attribute(Attribute::Bold), ]); contract_info .functions .iter() .for_each(|(selector, report_data)| { table.add_row(vec![ Cell::new(selector), Cell::new(report_data.gas_stats.min.to_string()), Cell::new(report_data.gas_stats.max.to_string()), Cell::new(report_data.gas_stats.mean.round().to_string()), Cell::new(report_data.gas_stats.std_deviation.round().to_string()), Cell::new(report_data.n_calls.to_string()), ]); }); table } ================================================ FILE: crates/forge-runner/src/gas/resources.rs ================================================ use crate::forge_config::ForgeTrackedResource; use blockifier::abi::constants; use blockifier::execution::call_info::{EventSummary, ExtendedExecutionResources}; use blockifier::execution::syscalls::vm_syscall_utils::SyscallUsageMap; use blockifier::fee::resources::{ArchivalDataResources, ComputationResources, MessageResources}; use cairo_vm::vm::runners::cairo_runner::ExecutionResources; use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::rpc::UsedResources; use starknet_api::execution_resources::GasAmount; pub struct GasCalculationResources { pub sierra_gas: GasAmount, pub vm_resources: ExtendedExecutionResources, pub syscalls: SyscallUsageMap, pub events: EventSummary, pub l2_to_l1_payload_lengths: Vec, pub l1_handler_payload_lengths: Vec, } impl GasCalculationResources { pub fn from_used_resources(r: &UsedResources) -> Self { Self { sierra_gas: r.execution_summary.charged_resources.gas_consumed, vm_resources: r .execution_summary .charged_resources .extended_vm_resources .clone(), syscalls: r.syscall_usage.clone(), events: r.execution_summary.event_summary.clone(), l2_to_l1_payload_lengths: r.execution_summary.l2_to_l1_payload_lengths.clone(), l1_handler_payload_lengths: r.l1_handler_payload_lengths.clone(), } } pub fn to_computation_resources(&self) -> ComputationResources { ComputationResources { tx_extended_vm_resources: self.vm_resources.clone(), // OS resources (transaction type related costs) and fee transfer resources are not included // as they are not relevant for test execution (see documentation for details): // https://github.com/foundry-rs/starknet-foundry/blob/979caf23c5d1085349e253d75682dd0e2527e321/docs/src/testing/gas-and-resource-estimation.md?plain=1#L75 os_vm_resources: ExecutionResources::default(), n_reverted_steps: 0, // TODO(#3681) sierra_gas: self.sierra_gas, reverted_sierra_gas: GasAmount::ZERO, // TODO(#3681) } } // Put together from a few blockifier functions // In a transaction (blockifier), there's only one l1_handler possible so we have to calculate those costs manually // (it's not the case in a scope of the test) pub fn to_message_resources(&self) -> MessageResources { let l2_to_l1_segment_length = self .l2_to_l1_payload_lengths .iter() .map(|payload_length| constants::L2_TO_L1_MSG_HEADER_SIZE + payload_length) .sum::(); let l1_to_l2_segment_length = self .l1_handler_payload_lengths .iter() .map(|payload_length| constants::L1_TO_L2_MSG_HEADER_SIZE + payload_length) .sum::(); let message_segment_length = l2_to_l1_segment_length + l1_to_l2_segment_length; MessageResources { l2_to_l1_payload_lengths: self.l2_to_l1_payload_lengths.clone(), message_segment_length, // The logic for calculating gas vector treats `l1_handler_payload_size` being `Some` // as indication that L1 handler was used and adds gas cost for that. // // We need to set it to `None` if length is 0 to avoid including this extra cost. l1_handler_payload_size: if l1_to_l2_segment_length > 0 { Some(l1_to_l2_segment_length) } else { None }, } } pub fn to_archival_resources(&self) -> ArchivalDataResources { // extended calldata length, signature length, code size and client side proof // are not included in the estimation // ref: https://github.com/foundry-rs/starknet-foundry/blob/5ce15b029135545452588c00aae580c05eb11ca8/docs/src/testing/gas-and-resource-estimation.md?plain=1#L73 ArchivalDataResources { event_summary: self.events.clone(), extended_calldata_length: 0, signature_length: 0, code_size: 0, has_client_side_proof: false, } } pub fn format_for_display(&self, tracked_resource: ForgeTrackedResource) -> String { // Ensure all resources used for calculation are getting displayed. let Self { sierra_gas, vm_resources, syscalls, events, l2_to_l1_payload_lengths, l1_handler_payload_lengths, } = self; let syscalls = format_syscalls(syscalls); let events = format_events(events); let messages = format_messages(l2_to_l1_payload_lengths, l1_handler_payload_lengths); let vm_resources_output = format_vm_resources(vm_resources); match tracked_resource { ForgeTrackedResource::CairoSteps => { format!("{vm_resources_output}{syscalls}{events}{messages}\n") } ForgeTrackedResource::SierraGas => { let vm_output = if *vm_resources == ExtendedExecutionResources::default() { String::new() } else { vm_resources_output.clone() }; let sierra_gas = format!("\n sierra gas: {}", sierra_gas.0); format!("{sierra_gas}{syscalls}{events}{messages}{vm_output}\n") } } } } fn format_syscalls(syscalls: &SyscallUsageMap) -> String { let mut syscall_usage: Vec<_> = syscalls .iter() .map(|(selector, usage)| (selector, usage.call_count)) .collect(); // Sort syscalls by call count syscall_usage.sort_by_key(|b| std::cmp::Reverse(b.1)); let content = format_items(&syscall_usage); format!("\n syscalls: ({content})") } fn format_vm_resources(execution_resources: &ExtendedExecutionResources) -> String { let cairo_primitives = execution_resources.prover_cairo_primitives(); let sorted_builtins = sort_by_value(&cairo_primitives); let builtins = format_items(&sorted_builtins); format!( " steps: {} memory holes: {} builtins: ({})", execution_resources.vm_resources.n_steps, execution_resources.vm_resources.n_memory_holes, builtins ) } fn format_events(events: &EventSummary) -> String { format!( "\n events: (count: {}, keys: {}, data size: {})", events.n_events, events.total_event_keys, events.total_event_data_size ) } fn format_messages(l2_to_l1: &[usize], l1_handler: &[usize]) -> String { format!( "\n messages: (l2 to l1: {}, l1 handler: {})", l2_to_l1.len(), l1_handler.len() ) } fn sort_by_value<'a, K, V, M>(map: M) -> Vec<(&'a K, &'a V)> where M: IntoIterator, V: Ord, { let mut sorted: Vec<_> = map.into_iter().collect(); sorted.sort_by(|a, b| b.1.cmp(a.1)); sorted } fn format_items(items: &[(K, V)]) -> String where K: std::fmt::Debug, V: std::fmt::Display, { items .iter() .map(|(key, value)| format!("{key:?}: {value}")) .collect::>() .join(", ") } ================================================ FILE: crates/forge-runner/src/gas/stats.rs ================================================ use num_traits::Pow; #[derive(Debug, PartialEq, Clone, Default)] pub struct GasStats { pub min: u64, pub max: u64, pub mean: f64, pub std_deviation: f64, } impl GasStats { #[must_use] pub fn new(gas_usages: &[u64]) -> Self { let mean = mean(gas_usages); Self { min: *gas_usages.iter().min().unwrap(), max: *gas_usages.iter().max().unwrap(), mean, std_deviation: std_deviation(mean, gas_usages), } } } #[expect(clippy::cast_precision_loss)] fn mean(values: &[u64]) -> f64 { if values.is_empty() { return 0.0; } let sum: f64 = values.iter().map(|&x| x as f64).sum(); sum / values.len() as f64 } #[expect(clippy::cast_precision_loss)] fn std_deviation(mean: f64, values: &[u64]) -> f64 { if values.is_empty() { return 0.0; } let sum_squared_diff = values .iter() .map(|&x| (x as f64 - mean).pow(2)) .sum::(); (sum_squared_diff / values.len() as f64).sqrt() } #[cfg(test)] mod tests { use super::{mean, std_deviation}; const FLOAT_ERROR: f64 = 0.01; #[test] fn test_mean_basic() { let data = [1, 2, 3, 4, 5]; let result = mean(&data); assert!((result - 3.0).abs() < FLOAT_ERROR); } #[test] fn test_mean_single_element() { let data = [42]; let result = mean(&data); assert!((result - 42.0).abs() < FLOAT_ERROR); } #[test] fn test_std_deviation_basic() { let data = [1, 2, 3, 4, 5]; let mean_value = mean(&data); let result = std_deviation(mean_value, &data); assert!((result - 1.414).abs() < FLOAT_ERROR); } #[test] fn test_std_deviation_single_element() { let data = [10]; let mean_value = mean(&data); let result = std_deviation(mean_value, &data); assert!(result.abs() < FLOAT_ERROR); } } ================================================ FILE: crates/forge-runner/src/gas/utils.rs ================================================ use starknet_types_core::felt::Felt; pub(super) fn shorten_felt(felt: Felt) -> String { let padded = format!("{felt:#066x}"); let first = &padded[..6]; let last = &padded[padded.len() - 4..]; format!("{first}…{last}") } #[cfg(test)] mod tests { use super::*; #[test] fn test_long() { let felt = Felt::from_hex_unchecked( "0x01c902da594beda43db10142ecf1fc3a098b56e8d95f3cd28587a0c6ba05a451", ); assert_eq!("0x01c9…a451", &shorten_felt(felt)); } #[test] fn test_short() { let felt = Felt::from_hex_unchecked("0x123"); assert_eq!("0x0000…0123", &shorten_felt(felt)); } } ================================================ FILE: crates/forge-runner/src/gas.rs ================================================ use crate::gas::resources::GasCalculationResources; use crate::test_case_summary::{Single, TestCaseSummary}; use blockifier::context::TransactionContext; use blockifier::fee::resources::{StarknetResources, StateResources, TransactionResources}; use blockifier::state::cached_state::CachedState; use blockifier::state::errors::StateError; use blockifier::transaction::objects::HasRelatedFeeType; use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::rpc::UsedResources; use cheatnet::runtime_extensions::forge_config_extension::config::RawAvailableResourceBoundsConfig; use cheatnet::state::ExtendedStateReader; use starknet_api::execution_resources::GasVector; use starknet_api::transaction::fields::GasVectorComputationMode; pub mod report; pub mod resources; pub mod stats; mod utils; #[tracing::instrument(skip_all, level = "debug")] pub fn calculate_used_gas( transaction_context: &TransactionContext, state: &mut CachedState, used_resources: &UsedResources, ) -> Result { let versioned_constants = transaction_context.block_context.versioned_constants(); let resources = GasCalculationResources::from_used_resources(used_resources); let starknet_resources = StarknetResources { archival_data: resources.to_archival_resources(), messages: resources.to_message_resources(), state: get_state_resources(transaction_context, state)?, }; let transaction_resources = TransactionResources { starknet_resources, computation: resources.to_computation_resources(), }; let use_kzg_da = transaction_context.block_context.block_info().use_kzg_da; Ok(transaction_resources.to_gas_vector( versioned_constants, use_kzg_da, &GasVectorComputationMode::All, )) } fn get_state_resources( transaction_context: &TransactionContext, state: &mut CachedState, ) -> Result { let mut state_changes = state.to_state_diff()?; // compiled_class_hash_updates is used only for keeping track of declares // which we don't want to include in gas cost state_changes.state_maps.compiled_class_hashes.clear(); state_changes.state_maps.declared_contracts.clear(); let state_changes_count = state_changes.count_for_fee_charge( None, transaction_context .block_context .chain_info() .fee_token_address(&transaction_context.tx_info.fee_type()), ); Ok(StateResources { state_changes_for_fee: state_changes_count, }) } pub fn check_available_gas( available_gas: Option, summary: TestCaseSummary, ) -> TestCaseSummary { match summary { TestCaseSummary::Passed { name, gas_info, debugging_trace, .. } if available_gas.is_some_and(|available_gas| { let av_gas = available_gas.to_gas_vector(); gas_info.gas_used.l1_gas > av_gas.l1_gas || gas_info.gas_used.l1_data_gas > av_gas.l1_data_gas || gas_info.gas_used.l2_gas > av_gas.l2_gas }) => { TestCaseSummary::Failed { name, msg: Some(format!( "\n\tTest cost exceeded the available gas. Consumed l1_gas: ~{}, l1_data_gas: ~{}, l2_gas: ~{}", gas_info.gas_used.l1_gas, gas_info.gas_used.l1_data_gas, gas_info.gas_used.l2_gas )), fuzzer_args: Vec::default(), test_statistics: (), debugging_trace, } } _ => summary, } } ================================================ FILE: crates/forge-runner/src/lib.rs ================================================ use crate::coverage_api::run_coverage; use crate::forge_config::{ExecutionDataToSave, ForgeConfig}; use crate::running::{run_fuzz_test, run_test}; use crate::test_case_summary::TestCaseSummary; use anyhow::{Result, bail}; use build_trace_data::save_trace_data; use cairo_lang_sierra::program::{ConcreteTypeLongId, Function, TypeDeclaration}; use camino::Utf8PathBuf; use cheatnet::runtime_extensions::forge_config_extension::config::RawFuzzerConfig; use foundry_ui::UI; use foundry_ui::components::warning::WarningMessage; use futures::StreamExt; use futures::stream::FuturesUnordered; use package_tests::with_config_resolved::TestCaseWithResolvedConfig; use profiler_api::run_profiler; use rand::SeedableRng; use rand::prelude::StdRng; use shared::spinner::Spinner; use std::collections::HashMap; use std::path::PathBuf; use std::sync::{Arc, Mutex}; use test_case_summary::{AnyTestCaseSummary, Fuzzing}; use tokio::sync::mpsc::{Sender, channel}; use tokio::task::JoinHandle; use universal_sierra_compiler_api::representation::RawCasmProgram; pub mod backtrace; pub mod build_trace_data; pub mod coverage_api; pub mod debugging; pub mod expected_result; pub mod filtering; pub mod forge_config; mod gas; pub mod messages; pub mod package_tests; pub mod partition; pub mod profiler_api; pub mod running; pub mod scarb; pub mod test_case_summary; pub mod test_target_summary; pub mod tests_summary; pub const CACHE_DIR: &str = ".snfoundry_cache"; const BUILTINS: [&str; 11] = [ "Pedersen", "RangeCheck", "Bitwise", "EcOp", "Poseidon", "SegmentArena", "GasBuiltin", "System", "RangeCheck96", "AddMod", "MulMod", ]; pub fn maybe_save_trace_and_profile( result: &AnyTestCaseSummary, execution_data_to_save: &ExecutionDataToSave, ) -> Result> { if let AnyTestCaseSummary::Single(TestCaseSummary::Passed { name, trace_data, .. }) = result && execution_data_to_save.is_vm_trace_needed() { let name = sanitize_filename::sanitize(name.replace("::", "_")); let trace_path = save_trace_data(&name, trace_data)?; if execution_data_to_save.profile { // TODO(#3395): Use Ui spinner let _spinner = Spinner::create_with_message("Running cairo-profiler"); run_profiler(&name, &trace_path, &execution_data_to_save.additional_args)?; } return Ok(Some(trace_path)); } Ok(None) } pub fn maybe_generate_coverage( execution_data_to_save: &ExecutionDataToSave, saved_trace_data_paths: &[PathBuf], ui: &UI, ) -> Result<()> { if execution_data_to_save.coverage { if saved_trace_data_paths.is_empty() { ui.println(&WarningMessage::new( "No trace data to generate coverage from", )); } else { // TODO(#3395): Use Ui spinner let _spinner = Spinner::create_with_message("Running cairo-coverage"); run_coverage( saved_trace_data_paths, &execution_data_to_save.additional_args, )?; } } Ok(()) } #[must_use] #[tracing::instrument(skip_all, level = "debug")] pub fn run_for_test_case( case: Arc, casm_program: Arc, forge_config: Arc, versioned_program_path: Arc, send: Sender<()>, ) -> JoinHandle> { if case.config.fuzzer_config.is_none() { tokio::task::spawn(async move { let res = run_test( case, casm_program, forge_config, versioned_program_path, send, ) .await?; Ok(AnyTestCaseSummary::Single(res)) }) } else { tokio::task::spawn(async move { if forge_config.test_runner_config.launch_debugger { bail!("--launch-debugger is not supported for fuzzer tests"); } let res = run_with_fuzzing( case, casm_program, forge_config.clone(), versioned_program_path, send, ) .await??; Ok(AnyTestCaseSummary::Fuzzing(res)) }) } } #[tracing::instrument(skip_all, level = "debug")] fn run_with_fuzzing( case: Arc, casm_program: Arc, forge_config: Arc, versioned_program_path: Arc, send: Sender<()>, ) -> JoinHandle>> { tokio::task::spawn(async move { let test_runner_config = &forge_config.test_runner_config; if send.is_closed() { return Ok(TestCaseSummary::Interrupted {}); } let (fuzzing_send, mut fuzzing_rec) = channel(1); let (fuzzer_runs, fuzzer_seed) = match case.config.fuzzer_config { Some(RawFuzzerConfig { runs, seed }) => ( runs.unwrap_or(test_runner_config.fuzzer_runs), seed.unwrap_or(test_runner_config.fuzzer_seed), ), _ => ( test_runner_config.fuzzer_runs, test_runner_config.fuzzer_seed, ), }; let rng = Arc::new(Mutex::new(StdRng::seed_from_u64(fuzzer_seed))); let mut tasks = FuturesUnordered::new(); let program = case.try_into_program(&casm_program)?; for _ in 1..=fuzzer_runs.get() { tasks.push(run_fuzz_test( case.clone(), program.clone(), casm_program.clone(), forge_config.clone(), versioned_program_path.clone(), send.clone(), fuzzing_send.clone(), rng.clone(), )); } let mut results = vec![]; while let Some(task) = tasks.next().await { let result = task?; results.push(result.clone()); if let TestCaseSummary::Failed { .. } = result { fuzzing_rec.close(); break; } } let runs = u32::try_from( results .iter() .filter(|item| { matches!( item, TestCaseSummary::Passed { .. } | TestCaseSummary::Failed { .. } ) }) .count(), )?; let fuzzing_run_summary: TestCaseSummary = TestCaseSummary::from(results); if let TestCaseSummary::Passed { .. } = fuzzing_run_summary { // Because we execute tests parallel, it's possible to // get Passed after Skipped. To treat fuzzing a test as Passed // we have to ensure that all fuzzing subtests Passed if runs != fuzzer_runs.get() { return Ok(TestCaseSummary::Interrupted {}); } } Ok(fuzzing_run_summary) }) } #[expect(clippy::implicit_hasher)] #[must_use] pub fn function_args( function: &Function, type_declarations: &HashMap, ) -> Vec { function .signature .param_types .iter() .filter_map(|pt| match type_declarations.get(&pt.id) { Some(ty_declaration) if !BUILTINS.contains(&ty_declaration.long_id.generic_id.0.as_str()) => { Some(ty_declaration.long_id.clone()) } _ => None, }) .collect() } ================================================ FILE: crates/forge-runner/src/messages.rs ================================================ use crate::forge_config::ForgeTrackedResource; use crate::gas::resources::GasCalculationResources; use crate::test_case_summary::{AnyTestCaseSummary, FuzzingStatistics, TestCaseSummary}; use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::rpc::UsedResources; use console::style; use foundry_ui::Message; use serde::Serialize; use serde_json::{Value, json}; #[derive(Serialize)] enum TestResultStatus { Passed, Failed, Ignored, Interrupted, ExcludedFromPartition, } impl From<&AnyTestCaseSummary> for TestResultStatus { fn from(test_result: &AnyTestCaseSummary) -> Self { match test_result { AnyTestCaseSummary::Single(TestCaseSummary::Passed { .. }) | AnyTestCaseSummary::Fuzzing(TestCaseSummary::Passed { .. }) => Self::Passed, AnyTestCaseSummary::Single(TestCaseSummary::Failed { .. }) | AnyTestCaseSummary::Fuzzing(TestCaseSummary::Failed { .. }) => Self::Failed, AnyTestCaseSummary::Single(TestCaseSummary::Ignored { .. }) | AnyTestCaseSummary::Fuzzing(TestCaseSummary::Ignored { .. }) => Self::Ignored, AnyTestCaseSummary::Single(TestCaseSummary::Interrupted { .. }) | AnyTestCaseSummary::Fuzzing(TestCaseSummary::Interrupted { .. }) => Self::Interrupted, AnyTestCaseSummary::Single(TestCaseSummary::ExcludedFromPartition { .. }) | AnyTestCaseSummary::Fuzzing(TestCaseSummary::ExcludedFromPartition { .. }) => { Self::ExcludedFromPartition } } } } #[derive(Serialize)] pub struct TestResultMessage { status: TestResultStatus, name: String, msg: Option, debugging_trace: String, fuzzer_report: String, gas_usage: String, used_resources: String, gas_report: String, } impl TestResultMessage { pub fn new( test_result: &AnyTestCaseSummary, show_detailed_resources: bool, tracked_resource: ForgeTrackedResource, ) -> Self { let name = test_result .name() .expect("Test result must have a name") .to_string(); let debugging_trace = test_result .debugging_trace() .map(|trace| format!("\n{trace}")) .unwrap_or_default(); let fuzzer_report = if let AnyTestCaseSummary::Fuzzing(test_result) = test_result { match test_result { TestCaseSummary::Passed { test_statistics: FuzzingStatistics { runs }, gas_info, .. } => format!(" (runs: {runs}, {gas_info})"), TestCaseSummary::Failed { fuzzer_args, test_statistics: FuzzingStatistics { runs }, .. } => format!(" (runs: {runs}, arguments: {fuzzer_args:?})"), _ => String::new(), } } else { String::new() }; let (gas_usage, gas_report) = match test_result { AnyTestCaseSummary::Single(TestCaseSummary::Passed { gas_info, .. }) => { let gas_report = gas_info .report_data .as_ref() .map(std::string::ToString::to_string) .unwrap_or_default(); ( format!( " (l1_gas: ~{}, l1_data_gas: ~{}, l2_gas: ~{})", gas_info.gas_used.l1_gas, gas_info.gas_used.l1_data_gas, gas_info.gas_used.l2_gas ), gas_report, ) } _ => (String::new(), String::new()), }; let used_resources = match (show_detailed_resources, &test_result) { (true, AnyTestCaseSummary::Single(TestCaseSummary::Passed { used_resources, .. })) => { format_detailed_resources(used_resources, tracked_resource) } _ => String::new(), }; let msg = test_result.msg().map(std::string::ToString::to_string); let status = TestResultStatus::from(test_result); Self { status, name, msg, debugging_trace, fuzzer_report, gas_usage, used_resources, gas_report, } } fn result_message(&self) -> String { if let Some(msg) = &self.msg { match self.status { TestResultStatus::Passed => return format!("\n\n{msg}"), TestResultStatus::Failed => return format!("\n\nFailure data:{msg}"), TestResultStatus::Ignored | TestResultStatus::Interrupted | TestResultStatus::ExcludedFromPartition => return String::new(), } } String::new() } fn result_header(&self) -> String { match self.status { TestResultStatus::Passed => format!("[{}]", style("PASS").green()), TestResultStatus::Failed => format!("[{}]", style("FAIL").red()), TestResultStatus::Ignored => format!("[{}]", style("IGNORE").yellow()), TestResultStatus::Interrupted => { unreachable!("Interrupted tests should not have visible message representation") } TestResultStatus::ExcludedFromPartition => { unreachable!( "Tests excluded from partition should not have visible message representation" ) } } } } impl Message for TestResultMessage { fn text(&self) -> String { let result_name = &self.name; let result_header = self.result_header(); let result_msg = self.result_message(); let result_debug_trace = &self.debugging_trace; let fuzzer_report = &self.fuzzer_report; let gas_usage = &self.gas_usage; let gas_report = &self.gas_report; let used_resources = &self.used_resources; format!( "{result_header} {result_name}{fuzzer_report}{gas_usage}{used_resources}{result_msg}{result_debug_trace}{gas_report}" ) } fn json(&self) -> Value { json!(self) } } fn format_detailed_resources( used_resources: &UsedResources, tracked_resource: ForgeTrackedResource, ) -> String { GasCalculationResources::from_used_resources(used_resources) .format_for_display(tracked_resource) } ================================================ FILE: crates/forge-runner/src/package_tests/raw.rs ================================================ use super::TestTargetLocation; use cairo_lang_sierra::program::ProgramArtifact; use camino::Utf8PathBuf; /// these structs are representation of scarb output for `scarb build --test` /// produced by scarb pub struct TestTargetRaw { pub sierra_program: ProgramArtifact, pub sierra_program_path: Utf8PathBuf, pub tests_location: TestTargetLocation, } ================================================ FILE: crates/forge-runner/src/package_tests/with_config.rs ================================================ use super::{TestCase, TestTarget}; use crate::{ expected_result::{ExpectedPanicValue, ExpectedTestResult}, filtering::TestCaseIsIgnored, }; use cheatnet::runtime_extensions::forge_config_extension::config::{ Expected, RawAvailableResourceBoundsConfig, RawForgeConfig, RawForkConfig, RawFuzzerConfig, RawShouldPanicConfig, }; use conversions::serde::serialize::SerializeToFeltVec; pub type TestTargetWithConfig = TestTarget; pub type TestCaseWithConfig = TestCase; /// Test case with config that has not yet been resolved /// see [`super::with_config_resolved::TestCaseResolvedConfig`] for more info #[derive(Debug, Clone)] pub struct TestCaseConfig { pub available_gas: Option, pub ignored: bool, pub expected_result: ExpectedTestResult, pub fork_config: Option, pub fuzzer_config: Option, pub disable_predeployed_contracts: bool, } impl TestCaseIsIgnored for TestCaseConfig { fn is_ignored(&self) -> bool { self.ignored } } impl From for TestCaseConfig { fn from(value: RawForgeConfig) -> Self { Self { available_gas: value.available_gas, ignored: value.ignore.is_some_and(|v| v.is_ignored), expected_result: value.should_panic.into(), fork_config: value.fork, fuzzer_config: value.fuzzer, disable_predeployed_contracts: value .disable_predeployed_contracts .is_some_and(|v| v.is_disabled), } } } impl From> for ExpectedTestResult { fn from(value: Option) -> Self { match value { None => Self::Success, Some(RawShouldPanicConfig { expected }) => Self::Panics(match expected { Expected::Any => ExpectedPanicValue::Any, Expected::Array(arr) => ExpectedPanicValue::Exact(arr), Expected::ByteArray(arr) => ExpectedPanicValue::Exact(arr.serialize_with_magic()), Expected::ShortString(str) => ExpectedPanicValue::Exact(str.serialize_to_vec()), }), } } } ================================================ FILE: crates/forge-runner/src/package_tests/with_config_resolved.rs ================================================ use super::{TestCase, TestTarget}; use crate::{ expected_result::ExpectedTestResult, filtering::TestCaseIsIgnored, package_tests::TestDetails, }; use anyhow::Result; use cairo_vm::types::program::Program; use cheatnet::runtime_extensions::forge_config_extension::config::{ RawAvailableResourceBoundsConfig, RawFuzzerConfig, }; use starknet_api::block::BlockNumber; use universal_sierra_compiler_api::representation::RawCasmProgram; use url::Url; pub type TestTargetWithResolvedConfig = TestTarget; pub type TestCaseWithResolvedConfig = TestCase; #[must_use] pub fn sanitize_test_case_name(name: &str) -> String { // Test names generated by `#[test]` and `#[fuzzer]` macros contain internal suffixes name.replace("__snforge_internal_test_generated", "") .replace("__snforge_internal_fuzzer_generated", "") } impl TestCaseWithResolvedConfig { #[must_use] pub fn new(name: &str, test_details: TestDetails, config: TestCaseResolvedConfig) -> Self { Self { name: sanitize_test_case_name(name), test_details, config, } } pub fn try_into_program(&self, casm_program: &RawCasmProgram) -> Result { self.test_details.try_into_program(casm_program) } } #[derive(Debug, Clone, PartialEq)] pub struct ResolvedForkConfig { pub url: Url, pub block_number: BlockNumber, } /// Test case with config that has been resolved, that is /// `#[fork("name")]` -> url and block id /// fetches block number #[derive(Debug, Clone, PartialEq)] pub struct TestCaseResolvedConfig { pub available_gas: Option, pub ignored: bool, pub expected_result: ExpectedTestResult, pub fork_config: Option, pub fuzzer_config: Option, pub disable_predeployed_contracts: bool, } impl TestCaseIsIgnored for TestCaseResolvedConfig { fn is_ignored(&self) -> bool { self.ignored } } ================================================ FILE: crates/forge-runner/src/package_tests.rs ================================================ use crate::running::hints_to_params; use anyhow::Result; use cairo_lang_sierra::extensions::NamedType; use cairo_lang_sierra::extensions::bitwise::BitwiseType; use cairo_lang_sierra::extensions::circuit::{AddModType, MulModType}; use cairo_lang_sierra::extensions::ec::EcOpType; use cairo_lang_sierra::extensions::pedersen::PedersenType; use cairo_lang_sierra::extensions::poseidon::PoseidonType; use cairo_lang_sierra::extensions::range_check::{RangeCheck96Type, RangeCheckType}; use cairo_lang_sierra::extensions::segment_arena::SegmentArenaType; use cairo_lang_sierra::ids::GenericTypeId; use cairo_lang_sierra::program::ProgramArtifact; use cairo_vm::serde::deserialize_program::ReferenceManager; use cairo_vm::types::builtin_name::BuiltinName; use cairo_vm::types::program::Program; use cairo_vm::types::relocatable::MaybeRelocatable; use camino::Utf8PathBuf; use serde::Serialize; use starknet_types_core::felt::Felt; use std::collections::HashMap; use std::sync::Arc; use universal_sierra_compiler_api::representation::RawCasmProgram; pub mod raw; pub mod with_config; pub mod with_config_resolved; // If modifying this, make sure that the order of builtins matches that from // `#[implicit_precedence(...)` in generated test code. const BUILTIN_ORDER: [(BuiltinName, GenericTypeId); 9] = [ (BuiltinName::pedersen, PedersenType::ID), (BuiltinName::range_check, RangeCheckType::ID), (BuiltinName::bitwise, BitwiseType::ID), (BuiltinName::ec_op, EcOpType::ID), (BuiltinName::poseidon, PoseidonType::ID), (BuiltinName::segment_arena, SegmentArenaType::ID), (BuiltinName::range_check96, RangeCheck96Type::ID), (BuiltinName::add_mod, AddModType::ID), (BuiltinName::mul_mod, MulModType::ID), ]; #[derive(Debug, PartialEq, Clone, Copy, Hash, Eq, Serialize, PartialOrd, Ord)] pub enum TestTargetLocation { /// Main crate in a package Lib, /// Crate in the `tests/` directory Tests, } #[derive(Debug, PartialEq, Clone, Default)] pub struct TestDetails { pub sierra_entry_point_statement_idx: usize, pub parameter_types: Vec, } impl TestDetails { #[must_use] pub fn builtins(&self) -> Vec { let mut builtins = vec![]; for (builtin_name, builtin_ty) in BUILTIN_ORDER { if self.parameter_types.iter().any(|ty| ty == &builtin_ty) { builtins.push(builtin_name); } } builtins } pub fn try_into_program(&self, casm_program: &RawCasmProgram) -> Result { let builtins = self.builtins(); let assembled_program = &casm_program.assembled_cairo_program; let hints_dict = hints_to_params(assembled_program); let data: Vec = assembled_program .bytecode .iter() .map(Felt::from) .map(MaybeRelocatable::from) .collect(); Program::new( builtins.clone(), data, Some(0), hints_dict, ReferenceManager { references: Vec::new(), }, HashMap::new(), vec![], None, ) .map_err(std::convert::Into::into) } } #[derive(Debug, Clone)] pub struct TestTarget { pub tests_location: TestTargetLocation, pub sierra_program: ProgramArtifact, pub sierra_program_path: Arc, pub casm_program: Arc, pub test_cases: Vec>, } #[derive(Debug, Clone, PartialEq)] pub struct TestCase { pub test_details: TestDetails, pub name: String, pub config: C, } ================================================ FILE: crates/forge-runner/src/partition.rs ================================================ use std::collections::HashMap; use std::num::NonZeroUsize; use std::str::FromStr; use std::sync::Arc; use crate::package_tests::raw::TestTargetRaw; use crate::package_tests::with_config_resolved::sanitize_test_case_name; use crate::scarb::load_test_artifacts; use anyhow::{Result, anyhow}; use camino::Utf8Path; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use scarb_api::metadata::PackageMetadata; use serde::Serialize; /// Enum representing configuration for partitioning test cases. /// Used only when `--partition` flag is provided. /// If `Disabled`, partitioning is disabled. /// If `Enabled`, contains the partition information and map of test case names and their partition numbers. #[derive(Debug, PartialEq, Clone, Default)] pub enum PartitionConfig { #[default] Disabled, Enabled { partition: Partition, partition_map: Arc, }, } impl PartitionConfig { pub fn new( partition: Partition, packages: &[PackageMetadata], artifacts_dir: &Utf8Path, ) -> Result { let partition_map = PartitionMap::build(packages, artifacts_dir, partition.total)?; Ok(Self::Enabled { partition, partition_map: Arc::new(partition_map), }) } } /// Represents a specific partition in a partitioned test run. #[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)] pub struct Partition { /// The 1-based index of the partition to run. index: NonZeroUsize, /// The total number of partitions in the run. total: NonZeroUsize, } impl Partition { #[must_use] pub fn index(&self) -> NonZeroUsize { self.index } #[must_use] pub fn total(&self) -> NonZeroUsize { self.total } } impl FromStr for Partition { type Err = String; fn from_str(s: &str) -> std::result::Result { let (index_str, total_str) = s .split_once('/') .ok_or_else(|| "Partition must be in the format /".to_string())?; if total_str.contains('/') { return Err("Partition must be in the format /".to_string()); } let index = index_str .parse::() .map_err(|_| "INDEX must be a positive integer".to_string())?; let total = total_str .parse::() .map_err(|_| "TOTAL must be a positive integer".to_string())?; if index > total { return Err("Invalid partition values: ensure 1 <= INDEX <= TOTAL".to_string()); } Ok(Partition { index, total }) } } /// Map containing test full paths and their assigned partition numbers. #[derive(Serialize, Debug, PartialEq)] pub struct PartitionMap(HashMap); impl PartitionMap { /// Builds a partition map from the provided packages and artifacts directory. /// Test full paths are sorted to ensure consistent assignment across runs. /// Each test case is assigned to a partition with round-robin. pub fn build( packages: &[PackageMetadata], artifacts_dir: &Utf8Path, total_partitions: NonZeroUsize, ) -> Result { let mut test_full_paths: Vec = packages .iter() .map(|package| -> Result> { let raw_test_targets = load_test_artifacts(artifacts_dir, package)?; let full_paths: Vec = raw_test_targets .iter() .map(collect_test_full_paths) .collect::>>>()? .into_iter() .flatten() .collect(); Ok(full_paths) }) .collect::>>>()? .into_iter() .flatten() .collect(); test_full_paths.sort(); let mut partition_map = HashMap::with_capacity(test_full_paths.len()); for (i, test_full_path) in test_full_paths.iter().enumerate() { let sanitized_full_path = sanitize_test_case_name(test_full_path); let partition_index_1_based = (i % total_partitions) + 1; let partition_index_1_based = NonZeroUsize::try_from(partition_index_1_based)?; if partition_map .insert(sanitized_full_path, partition_index_1_based) .is_some() { unreachable!("Test case full path should be unique"); } } Ok(Self(partition_map)) } #[must_use] pub fn get_assigned_index(&self, test_full_path: &str) -> Option { self.0.get(test_full_path).copied() } /// Counts the number of included tests for a given partition based on the partition map. #[must_use] pub fn included_tests_count(&self, run_partition: NonZeroUsize) -> usize { self.0 .values() .filter(|assigned_partition| **assigned_partition == run_partition) .count() } /// Counts the total number of tests assigned to any partition in the partition map. #[must_use] pub fn total_tests_count(&self) -> usize { self.0.len() } } /// Collects test full paths from a raw test target. fn collect_test_full_paths(test_target_raw: &TestTargetRaw) -> Result> { let executables = test_target_raw .sierra_program .debug_info .as_ref() .and_then(|info| info.executables.get("snforge_internal_test_executable")) .map(Vec::as_slice) .unwrap_or_default(); executables .par_iter() .map(|case| { case.debug_name .clone() .map(Into::into) .ok_or_else(|| anyhow!("Missing debug name for test executable entry")) }) .collect() } #[cfg(test)] mod tests { use super::*; use test_case::test_case; #[test_case("1/1", 1, 1)] #[test_case("2/5", 2, 5)] fn test_happy_case(partition: &str, expected_index: usize, expected_total: usize) { let partition = partition.parse::().unwrap(); assert_eq!( partition.index, NonZeroUsize::try_from(expected_index).unwrap() ); assert_eq!( partition.total, NonZeroUsize::try_from(expected_total).unwrap() ); } #[test_case("1-2"; "using hyphen instead of slash")] #[test_case("1/2/3"; "too many parts")] #[test_case("12"; "no separator")] fn test_invalid_format(partition: &str) { let err = partition.parse::().unwrap_err(); assert_eq!(err, "Partition must be in the format /"); } #[test_case("-1/5", "INDEX")] #[test_case("2/-5", "TOTAL")] #[test_case("a/5", "INDEX")] #[test_case("2/b", "TOTAL")] fn test_invalid_integer(partition: &str, invalid_part: &str) { let err = partition.parse::().unwrap_err(); assert_eq!(err, format!("{invalid_part} must be a positive integer")); } #[test_case("0/5")] #[test_case("6/5")] #[test_case("2/0")] fn test_out_of_bounds(partition: &str) { assert!(partition.parse::().is_err()); } } ================================================ FILE: crates/forge-runner/src/printing.rs ================================================ ================================================ FILE: crates/forge-runner/src/profiler_api.rs ================================================ use anyhow::{Context, Result}; use shared::command::CommandExt; use std::ffi::OsString; use std::process::Stdio; use std::{env, fs, path::PathBuf, process::Command}; pub const PROFILE_DIR: &str = "profile"; pub fn run_profiler( test_name: &str, trace_path: &PathBuf, profiler_args: &[OsString], ) -> Result<()> { let profiler = env::var("CAIRO_PROFILER") .map(PathBuf::from) .ok() .unwrap_or_else(|| PathBuf::from("cairo-profiler")); let mut command = Command::new(profiler); if profiler_args.iter().all(|arg| arg != "--output-path") { let dir_to_save_profile = PathBuf::from(PROFILE_DIR); fs::create_dir_all(&dir_to_save_profile).context("Failed to create a profile dir")?; let path_to_save_profile = dir_to_save_profile.join(format!("{test_name}.pb.gz")); command.arg("--output-path").arg(&path_to_save_profile); } command .arg(trace_path) .args(profiler_args) .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) .output_checked() .with_context(||format!("cairo-profiler failed to generate the profile for test {test_name} - inspect the errors above for more info"))?; Ok(()) } ================================================ FILE: crates/forge-runner/src/running/config_run.rs ================================================ use super::hints::hints_by_representation; use crate::running::execution::finalize_execution; use crate::running::setup::{ VmExecutionContext, build_test_call_and_entry_point, entry_point_initial_budget, initialize_execution_context, }; use crate::{forge_config::ForgeTrackedResource, package_tests::TestDetails}; use anyhow::Result; use blockifier::execution::contract_class::TrackedResource; use blockifier::execution::entry_point_execution::{prepare_call_arguments, run_entry_point}; use blockifier::state::{cached_state::CachedState, state_api::StateReader}; use cheatnet::runtime_extensions::forge_config_extension::{ ForgeConfigExtension, config::RawForgeConfig, }; use runtime::{ExtendedRuntime, StarknetRuntime, starknet::context::build_context}; use starknet_api::block::{ BlockInfo, BlockNumber, BlockTimestamp, GasPrice, GasPriceVector, GasPrices, NonzeroGasPrice, StarknetVersion, }; use starknet_types_core::felt::Felt; use std::default::Default; use universal_sierra_compiler_api::representation::RawCasmProgram; struct PhantomStateReader; impl StateReader for PhantomStateReader { fn get_storage_at( &self, _contract_address: starknet_api::core::ContractAddress, _key: starknet_api::state::StorageKey, ) -> blockifier::state::state_api::StateResult { unreachable!() } fn get_nonce_at( &self, _contract_address: starknet_api::core::ContractAddress, ) -> blockifier::state::state_api::StateResult { unreachable!() } fn get_class_hash_at( &self, _contract_address: starknet_api::core::ContractAddress, ) -> blockifier::state::state_api::StateResult { unreachable!() } fn get_compiled_class( &self, _class_hash: starknet_api::core::ClassHash, ) -> blockifier::state::state_api::StateResult< blockifier::execution::contract_class::RunnableCompiledClass, > { unreachable!() } fn get_compiled_class_hash( &self, _class_hash: starknet_api::core::ClassHash, ) -> blockifier::state::state_api::StateResult { unreachable!() } } #[tracing::instrument(skip_all, level = "debug")] pub fn run_config_pass( test_details: &TestDetails, casm_program: &RawCasmProgram, tracked_resource: &ForgeTrackedResource, ) -> Result { let program = test_details.try_into_program(casm_program)?; let (call, entry_point) = build_test_call_and_entry_point(test_details, casm_program, &program); let mut cached_state = CachedState::new(PhantomStateReader); let gas_price_vector = GasPriceVector { l1_gas_price: NonzeroGasPrice::new(GasPrice(2))?, l1_data_gas_price: NonzeroGasPrice::new(GasPrice(2))?, l2_gas_price: NonzeroGasPrice::new(GasPrice(2))?, }; let block_info = BlockInfo { block_number: BlockNumber(0), block_timestamp: BlockTimestamp(0), gas_prices: GasPrices { eth_gas_prices: gas_price_vector.clone(), strk_gas_prices: gas_price_vector, }, sequencer_address: 0_u8.into(), use_kzg_da: true, starknet_version: StarknetVersion::LATEST, }; let mut context = build_context(&block_info, None, &TrackedResource::from(tracked_resource)); let hints = hints_by_representation(&casm_program.assembled_cairo_program); let VmExecutionContext { mut runner, syscall_handler, initial_syscall_ptr, program_extra_data_length, } = initialize_execution_context( call.clone(), &hints, &program, &mut cached_state, &mut context, )?; let mut config = RawForgeConfig::default(); let mut forge_config_runtime = ExtendedRuntime { extension: ForgeConfigExtension { config: &mut config, }, extended_runtime: StarknetRuntime { hint_handler: syscall_handler, panic_traceback: None, }, }; let tracked_resource = TrackedResource::from(tracked_resource); let entry_point_initial_budget = entry_point_initial_budget(&forge_config_runtime.extended_runtime.hint_handler); let args = prepare_call_arguments( &forge_config_runtime.extended_runtime.hint_handler.base.call, &mut runner, initial_syscall_ptr, &mut forge_config_runtime .extended_runtime .hint_handler .read_only_segments, &entry_point, entry_point_initial_budget, )?; let n_total_args = args.len(); let bytecode_length = program.data_len(); let program_segment_size = bytecode_length + program_extra_data_length; run_entry_point( &mut runner, &mut forge_config_runtime, entry_point, args, program_segment_size, )?; finalize_execution( &mut runner, &mut forge_config_runtime.extended_runtime.hint_handler, n_total_args, program_extra_data_length, tracked_resource, )?; Ok(config) } ================================================ FILE: crates/forge-runner/src/running/execution.rs ================================================ use blockifier::execution::call_info::{CallExecution, CallInfo, ExtendedExecutionResources}; use blockifier::execution::contract_class::TrackedResource; use blockifier::execution::entry_point_execution::{ extract_extended_vm_resources, finalize_runner, get_call_result, total_vm_resources, }; use blockifier::execution::errors::PostExecutionError; use blockifier::execution::syscalls::hint_processor::SyscallHintProcessor; use cairo_vm::vm::runners::cairo_runner::CairoRunner; // Based on the code from blockifer #[tracing::instrument(skip_all, level = "debug")] pub fn finalize_execution( // region: Modified blockifier code runner: &mut CairoRunner, syscall_handler: &mut SyscallHintProcessor<'_>, // endregion n_total_args: usize, program_extra_data_length: usize, tracked_resource: TrackedResource, ) -> Result { finalize_runner(runner, n_total_args, program_extra_data_length)?; syscall_handler .read_only_segments .mark_as_accessed(runner)?; let call_result = get_call_result(runner, syscall_handler, &tracked_resource)?; // Take into account the resources of the current call, without inner calls. // Has to happen after marking holes in segments as accessed. let extended_resources_without_inner_calls = extract_extended_vm_resources(runner, syscall_handler)?; let tracked_vm_resources_without_inner_calls = match tracked_resource { TrackedResource::CairoSteps => &extended_resources_without_inner_calls, TrackedResource::SierraGas => &ExtendedExecutionResources::default(), }; syscall_handler.finalize(); let vm_resources = total_vm_resources( tracked_vm_resources_without_inner_calls, &syscall_handler.base.inner_calls, ); let syscall_handler_base = &syscall_handler.base; // region: Modified blockifier code - added clones due to different function signature Ok(CallInfo { call: syscall_handler_base.call.clone().into(), execution: CallExecution { retdata: call_result.retdata, events: syscall_handler_base.events.clone(), l2_to_l1_messages: syscall_handler_base.l2_to_l1_messages.clone(), cairo_native: false, failed: call_result.failed, gas_consumed: call_result.gas_consumed, }, inner_calls: syscall_handler_base.inner_calls.clone(), tracked_resource, resources: vm_resources, storage_access_tracker: syscall_handler_base.storage_access_tracker.clone(), builtin_counters: extended_resources_without_inner_calls.prover_cairo_primitives(), syscalls_usage: syscall_handler_base.syscalls_usage.clone(), }) // endregion } ================================================ FILE: crates/forge-runner/src/running/hints.rs ================================================ use cairo_lang_casm::hints::Hint; use cairo_vm::serde::deserialize_program::{ApTracking, FlowTrackingData, HintParams}; use std::collections::HashMap; use universal_sierra_compiler_api::representation::AssembledCairoProgram; pub fn hints_by_representation(assembled_program: &AssembledCairoProgram) -> HashMap { assembled_program .hints .iter() .flat_map(|(_, hints)| hints.iter().cloned()) .map(|hint| (hint.representing_string(), hint)) .collect() } #[must_use] pub fn hints_to_params( assembled_program: &AssembledCairoProgram, ) -> HashMap> { assembled_program .hints .iter() .map(|(offset, hints)| { ( *offset, hints .iter() .map(|hint| HintParams { code: hint.representing_string(), accessible_scopes: vec![], flow_tracking_data: FlowTrackingData { ap_tracking: ApTracking::new(), reference_ids: HashMap::new(), }, }) .collect::>(), ) }) .collect() } ================================================ FILE: crates/forge-runner/src/running/setup.rs ================================================ use crate::package_tests::TestDetails; use blockifier::execution::contract_class::EntryPointV1; use blockifier::execution::entry_point::{EntryPointExecutionContext, ExecutableCallEntryPoint}; use blockifier::execution::entry_point_execution::prepare_program_extra_data; use blockifier::execution::errors::PreExecutionError; use blockifier::execution::execution_utils::ReadOnlySegments; use blockifier::execution::syscalls::hint_processor::SyscallHintProcessor; use blockifier::state::state_api::State; use cairo_lang_casm::hints::Hint; use cairo_vm::types::builtin_name::BuiltinName; use cairo_vm::types::layout_name::LayoutName; use cairo_vm::types::program::Program; use cairo_vm::types::relocatable::Relocatable; use cairo_vm::vm::runners::cairo_runner::CairoRunner; use cheatnet::constants::build_test_entry_point; use starknet_api::deprecated_contract_class::EntryPointOffset; use std::collections::HashMap; use universal_sierra_compiler_api::representation::RawCasmProgram; // Based on structure from https://github.com/starkware-libs/sequencer/blob/e417a9e7d50cbd78065d357763df2fbc2ad41f7c/crates/blockifier/src/execution/entry_point_execution.rs#L39 // Logic of `initialize_execution_context` had to be modified so this struct ended up modified as well. // Probably won't be possible to upstream it. pub struct VmExecutionContext<'a> { pub runner: CairoRunner, pub syscall_handler: SyscallHintProcessor<'a>, pub initial_syscall_ptr: Relocatable, // Additional data required for execution is appended after the program bytecode. pub program_extra_data_length: usize, } // Based on code from https://github.com/starkware-libs/sequencer/blob/e417a9e7d50cbd78065d357763df2fbc2ad41f7c/crates/blockifier/src/execution/entry_point_execution.rs#L122 // Enough of the logic of this had to be changed that probably it won't be possible to upstream it #[tracing::instrument(skip_all, level = "debug")] pub fn initialize_execution_context<'a>( call: ExecutableCallEntryPoint, hints: &'a HashMap, program: &Program, state: &'a mut dyn State, context: &'a mut EntryPointExecutionContext, ) -> Result, PreExecutionError> { // Instantiate Cairo runner. let proof_mode = false; let trace_enabled = true; let dynamic_layout_params = None; let disable_trace_padding = false; let mut runner = CairoRunner::new( program, LayoutName::all_cairo, dynamic_layout_params, proof_mode, trace_enabled, disable_trace_padding, )?; runner.initialize_function_runner_cairo_1(&builtins_from_program(program))?; let mut read_only_segments = ReadOnlySegments::default(); let program_extra_data_length = prepare_program_extra_data( &mut runner, program.data_len(), &mut read_only_segments, &context.versioned_constants().os_constants.gas_costs, )?; // Instantiate syscall handler. let initial_syscall_ptr = runner.vm.add_memory_segment(); let syscall_handler = SyscallHintProcessor::new( state, context, initial_syscall_ptr, call, hints, read_only_segments, ); Ok(VmExecutionContext { runner, syscall_handler, initial_syscall_ptr, program_extra_data_length, }) } // Builtins field is private in program, so we need this workaround fn builtins_from_program(program: &Program) -> Vec { program.iter_builtins().copied().collect::>() } pub fn entry_point_initial_budget(syscall_hint_processor: &SyscallHintProcessor) -> u64 { syscall_hint_processor .base .context .gas_costs() .base .entry_point_initial_budget } pub fn build_test_call_and_entry_point( test_details: &TestDetails, casm_program: &RawCasmProgram, program: &Program, ) -> (ExecutableCallEntryPoint, EntryPointV1) { let sierra_instruction_idx = test_details.sierra_entry_point_statement_idx; let casm_entry_point_offset = casm_program.debug_info[sierra_instruction_idx].0; let call = build_test_entry_point(); let entry_point = EntryPointV1 { selector: call.entry_point_selector, offset: EntryPointOffset(casm_entry_point_offset), builtins: builtins_from_program(program), }; (call, entry_point) } ================================================ FILE: crates/forge-runner/src/running/syscall_handler.rs ================================================ use cairo_lang_sierra::extensions::NoGenericArgsGenericType; use cairo_lang_sierra::{extensions::segment_arena::SegmentArenaType, ids::GenericTypeId}; #[must_use] pub fn has_segment_arena(test_param_types: &[(GenericTypeId, i16)]) -> bool { test_param_types .iter() .any(|(ty, _)| ty == &SegmentArenaType::ID) } #[must_use] pub fn syscall_handler_offset(builtins_len: usize, has_segment_arena: bool) -> usize { // * Segment arena is allocated conditionally, so segment index is automatically moved (+2 segments) // * Each used builtin moves the offset by +1 // * Line `let mut builtin_offset = 3;` in `create_entry_code_from_params` // * TODO(#2967) Where is remaining +2 in base offset coming from? Maybe System builtin and Gas builtin which seem to be always included let base_offset = 5; if has_segment_arena { base_offset + builtins_len + 2 } else { base_offset + builtins_len } } ================================================ FILE: crates/forge-runner/src/running/target.rs ================================================ use crate::{ forge_config::ForgeTrackedResource, package_tests::{ TestDetails, raw::TestTargetRaw, with_config::{TestCaseWithConfig, TestTargetWithConfig}, }, running::config_run::run_config_pass, }; use anyhow::Result; use cairo_lang_sierra::{ ids::ConcreteTypeId, program::{GenFunction, StatementIdx, TypeDeclaration}, }; use rayon::iter::IntoParallelRefIterator; use rayon::iter::ParallelIterator; use std::{collections::HashMap, sync::Arc}; use universal_sierra_compiler_api::compile_raw_sierra_at_path; #[tracing::instrument(skip_all, level = "debug")] pub fn prepare_test_target( test_target_raw: TestTargetRaw, tracked_resource: &ForgeTrackedResource, ) -> Result { macro_rules! by_id { ($field:ident) => {{ let temp: HashMap<_, _> = test_target_raw .sierra_program .program .$field .iter() .map(|f| (f.id.id, f)) .collect(); temp }}; } let funcs = by_id!(funcs); let type_declarations = by_id!(type_declarations); let casm_program = Arc::new(compile_raw_sierra_at_path( test_target_raw.sierra_program_path.as_std_path(), )?); let default_executables = vec![]; let executables = test_target_raw .sierra_program .debug_info .as_ref() .and_then(|info| info.executables.get("snforge_internal_test_executable")) .unwrap_or(&default_executables); let test_cases = executables .par_iter() .map(|case| -> Result { let func = funcs[&case.id]; let test_details = build_test_details(func, &type_declarations); let raw_config = run_config_pass(&test_details, &casm_program, tracked_resource)?; Ok(TestCaseWithConfig { config: raw_config.into(), name: case.debug_name.clone().unwrap().into(), test_details, }) }) .collect::>()?; Ok(TestTargetWithConfig { tests_location: test_target_raw.tests_location, test_cases, sierra_program: test_target_raw.sierra_program, sierra_program_path: test_target_raw.sierra_program_path.into(), casm_program, }) } #[tracing::instrument(skip_all, level = "debug")] fn build_test_details( func: &GenFunction, type_declarations: &HashMap, ) -> TestDetails { let map_types = |concrete_types: &[ConcreteTypeId]| { concrete_types .iter() .map(|ty| { let ty = type_declarations[&ty.id]; ty.long_id.generic_id.clone() }) .collect() }; TestDetails { sierra_entry_point_statement_idx: func.entry_point.0, parameter_types: map_types(&func.signature.param_types), } } ================================================ FILE: crates/forge-runner/src/running.rs ================================================ use crate::backtrace::add_backtrace_footer; use crate::forge_config::{ForgeConfig, RuntimeConfig}; use crate::gas::calculate_used_gas; use crate::package_tests::with_config_resolved::{ResolvedForkConfig, TestCaseWithResolvedConfig}; use crate::test_case_summary::{Single, TestCaseSummary}; use anyhow::{Result, bail}; use blockifier::execution::call_info::CallInfo; use blockifier::execution::contract_class::TrackedResource; use blockifier::execution::entry_point::EntryPointExecutionContext; use blockifier::execution::entry_point_execution::{ extract_extended_vm_resources, prepare_call_arguments, run_entry_point, }; use blockifier::execution::errors::EntryPointExecutionError; use blockifier::state::cached_state::CachedState; use cairo_vm::Felt252; use cairo_vm::types::program::Program; use cairo_vm::vm::errors::cairo_run_errors::CairoRunError; use cairo_vm::vm::errors::vm_errors::VirtualMachineError; use camino::{Utf8Path, Utf8PathBuf}; use cheatnet::constants as cheatnet_constants; use cheatnet::forking::data::ForkData; use cheatnet::forking::state::ForkStateReader; use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::CallToBlockifierExtension; use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::rpc::UsedResources; use cheatnet::runtime_extensions::cheatable_starknet_runtime_extension::CheatableStarknetRuntimeExtension; use cheatnet::runtime_extensions::forge_runtime_extension::{ ForgeExtension, ForgeRuntime, add_resources_to_top_call, compute_and_store_execution_summary, get_all_used_resources, update_top_call_l1_resources, update_top_call_resources, update_top_call_vm_trace, }; use cheatnet::state::{BlockInfoReader, CheatnetState, EncounteredErrors, ExtendedStateReader}; use cheatnet::trace_data::CallTrace; use execution::finalize_execution; use hints::hints_by_representation; use rand::prelude::StdRng; use runtime::starknet::context::{build_context, set_max_steps}; use runtime::{ExtendedRuntime, StarknetRuntime}; use scarb_oracle_hint_service::OracleHintService; use starknet_api::execution_resources::GasVector; use std::cell::RefCell; use std::default::Default; use std::marker::PhantomData; use std::rc::Rc; use std::sync::{Arc, Mutex}; use tokio::sync::mpsc::Sender; use tokio::task::JoinHandle; use universal_sierra_compiler_api::representation::RawCasmProgram; use cairo_debugger::CairoDebugger; pub mod config_run; mod execution; mod hints; mod setup; mod syscall_handler; pub mod target; use crate::debugging::build_debugging_trace; pub use hints::hints_to_params; use setup::VmExecutionContext; pub use syscall_handler::has_segment_arena; pub use syscall_handler::syscall_handler_offset; #[must_use] #[tracing::instrument(skip_all, level = "debug")] pub fn run_test( case: Arc, casm_program: Arc, forge_config: Arc, versioned_program_path: Arc, send: Sender<()>, ) -> JoinHandle> { tokio::task::spawn_blocking(move || { // Due to the inability of spawn_blocking to be abruptly cancelled, // a channel is used to receive information indicating // that the execution of the task is no longer necessary. if send.is_closed() { return TestCaseSummary::Interrupted {}; } let run_result = case.try_into_program(&casm_program).and_then(|program| { run_test_case( &case, &program, &casm_program, &RuntimeConfig::from(&forge_config.test_runner_config), None, &versioned_program_path, ) }); if send.is_closed() { return TestCaseSummary::Interrupted {}; } extract_test_case_summary(run_result, &case, &forge_config, &versioned_program_path) }) } #[tracing::instrument(skip_all, level = "debug")] #[allow(clippy::too_many_arguments)] pub(crate) fn run_fuzz_test( case: Arc, program: Program, casm_program: Arc, forge_config: Arc, versioned_program_path: Arc, send: Sender<()>, fuzzing_send: Sender<()>, rng: Arc>, ) -> JoinHandle> { tokio::task::spawn_blocking(move || { // Due to the inability of spawn_blocking to be abruptly cancelled, // a channel is used to receive information indicating // that the execution of the task is no longer necessary. if send.is_closed() | fuzzing_send.is_closed() { return TestCaseSummary::Interrupted {}; } let run_result = run_test_case( &case, &program, &casm_program, &RuntimeConfig::from(&forge_config.test_runner_config), Some(rng), &versioned_program_path, ); // TODO: code below is added to fix snforge tests // remove it after improve exit-first tests // issue #1043 if send.is_closed() { return TestCaseSummary::Interrupted {}; } extract_test_case_summary(run_result, &case, &forge_config, &versioned_program_path) }) } pub enum RunStatus { Success(Vec), Panic(Vec), } pub struct RunCompleted { pub(crate) status: RunStatus, pub(crate) call_trace: Rc>, pub(crate) gas_used: GasVector, pub(crate) used_resources: UsedResources, pub(crate) encountered_errors: EncounteredErrors, pub(crate) fuzzer_args: Vec, pub(crate) fork_data: ForkData, } pub struct RunError { pub(crate) error: Box, pub(crate) call_trace: Rc>, pub(crate) encountered_errors: EncounteredErrors, pub(crate) fuzzer_args: Vec, pub(crate) fork_data: ForkData, } pub enum RunResult { Completed(Box), Error(RunError), } #[expect(clippy::too_many_lines)] #[tracing::instrument(skip_all, level = "debug")] pub fn run_test_case( case: &TestCaseWithResolvedConfig, program: &Program, casm_program: &RawCasmProgram, runtime_config: &RuntimeConfig, fuzzer_rng: Option>>, versioned_program_path: &Utf8Path, ) -> Result { let (call, entry_point) = setup::build_test_call_and_entry_point(&case.test_details, casm_program, program); let debugger = runtime_config .launch_debugger .then(|| CairoDebugger::connect_and_initialize(versioned_program_path.as_std_path())) .transpose()?; let mut state_reader = ExtendedStateReader { dict_state_reader: cheatnet_constants::build_testing_state(), fork_state_reader: get_fork_state_reader( runtime_config.cache_dir, case.config.fork_config.as_ref(), )?, }; if !case.config.disable_predeployed_contracts { state_reader.predeploy_contracts(); } let block_info = state_reader.get_block_info()?; let chain_id = state_reader.get_chain_id()?; let tracked_resource = TrackedResource::from(runtime_config.tracked_resource); let mut context = build_context(&block_info, chain_id, &tracked_resource); if let Some(max_n_steps) = runtime_config.max_n_steps { set_max_steps(&mut context, max_n_steps); } let mut cached_state = CachedState::new(state_reader); let hints = hints_by_representation(&casm_program.assembled_cairo_program); let VmExecutionContext { mut runner, syscall_handler, initial_syscall_ptr, program_extra_data_length, } = setup::initialize_execution_context( call.clone(), &hints, program, &mut cached_state, &mut context, )?; if let Some(debugger) = debugger { runner.set_vm_hooks(Box::new(debugger)); } let mut cheatnet_state = CheatnetState { block_info, ..Default::default() }; cheatnet_state.trace_data.is_vm_trace_needed = runtime_config.is_vm_trace_needed; let cheatable_runtime = ExtendedRuntime { extension: CheatableStarknetRuntimeExtension { cheatnet_state: &mut cheatnet_state, }, extended_runtime: StarknetRuntime { hint_handler: syscall_handler, panic_traceback: None, }, }; let call_to_blockifier_runtime = ExtendedRuntime { extension: CallToBlockifierExtension { lifetime: &PhantomData, }, extended_runtime: cheatable_runtime, }; let forge_extension = ForgeExtension { environment_variables: runtime_config.environment_variables, contracts_data: runtime_config.contracts_data, fuzzer_rng, oracle_hint_service: OracleHintService::new(Some(versioned_program_path.as_std_path())), }; let mut forge_runtime = ExtendedRuntime { extension: forge_extension, extended_runtime: call_to_blockifier_runtime, }; let entry_point_initial_budget = setup::entry_point_initial_budget( &forge_runtime .extended_runtime .extended_runtime .extended_runtime .hint_handler, ); let args = prepare_call_arguments( &forge_runtime .extended_runtime .extended_runtime .extended_runtime .hint_handler .base .call .clone(), &mut runner, initial_syscall_ptr, &mut forge_runtime .extended_runtime .extended_runtime .extended_runtime .hint_handler .read_only_segments, &entry_point, entry_point_initial_budget, )?; let n_total_args = args.len(); // Execute. let bytecode_length = program.data_len(); let program_segment_size = bytecode_length + program_extra_data_length; let result: Result = match run_entry_point( &mut runner, &mut forge_runtime, entry_point, args, program_segment_size, ) { Ok(()) => { let call_info = finalize_execution( &mut runner, &mut forge_runtime .extended_runtime .extended_runtime .extended_runtime .hint_handler, n_total_args, program_extra_data_length, tracked_resource, )?; // TODO(#3744): Confirm if this is needed for the profiler let vm_resources_without_inner_calls = extract_extended_vm_resources( &runner, &forge_runtime .extended_runtime .extended_runtime .extended_runtime .hint_handler, )?; add_resources_to_top_call( &mut forge_runtime, &vm_resources_without_inner_calls, &tracked_resource, ); update_top_call_vm_trace(&mut forge_runtime, &mut runner); Ok(call_info) } Err(error) => Err(match error { EntryPointExecutionError::CairoRunError(err_box) => match *err_box { CairoRunError::VmException(err) => CairoRunError::VirtualMachine(err.inner_exc), other => other, }, err => bail!(err), }), }; let encountered_errors = forge_runtime .extended_runtime .extended_runtime .extension .cheatnet_state .encountered_errors .clone(); let call_trace_ref = get_call_trace_ref(&mut forge_runtime); update_top_call_resources(&mut forge_runtime, tracked_resource); update_top_call_l1_resources(&mut forge_runtime); compute_and_store_execution_summary(&call_trace_ref); let fuzzer_args = forge_runtime .extended_runtime .extended_runtime .extension .cheatnet_state .fuzzer_args .clone(); let transaction_context = get_context(&forge_runtime).tx_context.clone(); let fork_data = cached_state .state .fork_state_reader .as_ref() .map(|fork_state_reader| ForkData::new(&fork_state_reader.compiled_contract_class_map())) .unwrap_or_default(); Ok(match result { Ok(result) => { let used_resources = get_all_used_resources(&result, &call_trace_ref, &transaction_context); let gas_used = calculate_used_gas(&transaction_context, &mut cached_state, &used_resources)?; RunResult::Completed(Box::new(RunCompleted { status: if result.execution.failed { RunStatus::Panic(result.execution.retdata.0) } else { RunStatus::Success(result.execution.retdata.0) }, call_trace: call_trace_ref, gas_used, used_resources, encountered_errors, fuzzer_args, fork_data, })) } Err(error) => RunResult::Error(RunError { error: Box::new(error), call_trace: call_trace_ref, encountered_errors, fuzzer_args, fork_data, }), }) } fn extract_test_case_summary( run_result: Result, case: &TestCaseWithResolvedConfig, forge_config: &ForgeConfig, versioned_program_path: &Utf8Path, ) -> TestCaseSummary { let contracts_data = &forge_config.test_runner_config.contracts_data; let trace_args = &forge_config.output_config.trace_args; match run_result { Ok(run_result) => match run_result { RunResult::Completed(run_completed) => TestCaseSummary::from_run_completed( *run_completed, case, contracts_data, versioned_program_path, trace_args, forge_config.output_config.gas_report, ), RunResult::Error(run_error) => { let mut message = format!( "\n {}\n", run_error .error .to_string() .replace(" Custom Hint Error: ", "\n ") ); if let CairoRunError::VirtualMachine(VirtualMachineError::UnfinishedExecution) = *run_error.error { message.push_str( "\n Suggestion: Consider using the flag `--max-n-steps` to increase allowed limit of steps", ); } TestCaseSummary::Failed { name: case.name.clone(), msg: Some(message).map(|msg| { add_backtrace_footer(msg, contracts_data, &run_error.encountered_errors) }), fuzzer_args: run_error.fuzzer_args, test_statistics: (), debugging_trace: build_debugging_trace( &run_error.call_trace.borrow(), contracts_data, trace_args, case.name.clone(), &run_error.fork_data, ), } } }, // `ForkStateReader.get_block_info`, `get_fork_state_reader, `calculate_used_gas` may return an error // `available_gas` may be specified with Scarb ~2.4 Err(error) => TestCaseSummary::Failed { name: case.name.clone(), msg: Some(error.to_string()), fuzzer_args: Vec::default(), test_statistics: (), debugging_trace: None, }, } } fn get_fork_state_reader( cache_dir: &Utf8Path, fork_config: Option<&ResolvedForkConfig>, ) -> Result> { fork_config .map(|ResolvedForkConfig { url, block_number }| { ForkStateReader::new(url.clone(), *block_number, cache_dir) }) .transpose() } fn get_context<'a>(runtime: &'a ForgeRuntime) -> &'a EntryPointExecutionContext { runtime .extended_runtime .extended_runtime .extended_runtime .hint_handler .base .context } fn get_call_trace_ref(runtime: &mut ForgeRuntime) -> Rc> { runtime .extended_runtime .extended_runtime .extension .cheatnet_state .trace_data .current_call_stack .top() } ================================================ FILE: crates/forge-runner/src/scarb.rs ================================================ use anyhow::Result; use cairo_lang_sierra::program::VersionedProgram; use camino::Utf8Path; use scarb_api::{metadata::PackageMetadata, test_targets_by_name}; use std::{fs, io::ErrorKind}; use crate::package_tests::{TestTargetLocation, raw::TestTargetRaw}; #[tracing::instrument(skip_all, level = "debug")] pub fn load_test_artifacts( target_dir: &Utf8Path, package: &PackageMetadata, ) -> Result> { let mut targets = vec![]; let dedup_targets = test_targets_by_name(package); for (target_name, target) in dedup_targets { let tests_location = if target.params.get("test-type").and_then(|v| v.as_str()) == Some("unit") { TestTargetLocation::Lib } else { TestTargetLocation::Tests }; let target_file = format!("{target_name}.test.sierra.json"); let sierra_program_path = target_dir.join(target_file); match fs::read_to_string(&sierra_program_path) { Ok(value) => { let versioned_program = serde_json::from_str::(&value)?; let sierra_program = match versioned_program { VersionedProgram::V1 { program, .. } => program, }; let test_target = TestTargetRaw { sierra_program, sierra_program_path, tests_location, }; targets.push(test_target); } Err(err) if err.kind() == ErrorKind::NotFound => {} Err(err) => Err(err)?, } } Ok(targets) } ================================================ FILE: crates/forge-runner/src/test_case_summary.rs ================================================ use crate::backtrace::{add_backtrace_footer, get_backtrace, is_backtrace_enabled}; use crate::build_trace_data::build_profiler_call_trace; use crate::debugging::{TraceArgs, build_debugging_trace}; use crate::expected_result::{ExpectedPanicValue, ExpectedTestResult}; use crate::gas::check_available_gas; use crate::gas::report::SingleTestGasInfo; use crate::gas::stats::GasStats; use crate::package_tests::with_config_resolved::TestCaseWithResolvedConfig; use crate::running::{RunCompleted, RunStatus}; use blockifier::execution::syscalls::hint_processor::ENTRYPOINT_FAILED_ERROR_FELT; use cairo_annotations::trace_data::VersionedCallTrace as VersionedProfilerCallTrace; use camino::Utf8Path; use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::rpc::UsedResources; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; use conversions::byte_array::ByteArray; use conversions::felt::ToShortString; use debugging::ContractsDataStore; use shared::utils::build_readable_text; use starknet_api::execution_resources::GasVector; use starknet_types_core::felt::Felt; use std::fmt; use std::option::Option; #[derive(Debug, PartialEq, Clone, Default)] pub struct GasFuzzingInfo { pub l1_gas: GasStats, pub l1_data_gas: GasStats, pub l2_gas: GasStats, } impl fmt::Display for GasFuzzingInfo { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "(l1_gas: {}, l1_data_gas: {}, l2_gas: {})", self.l1_gas, self.l1_data_gas, self.l2_gas ) } } impl fmt::Display for GasStats { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{{max: ~{}, min: ~{}, mean: ~{:.0}, std deviation: ~{:.0}}}", self.max, self.min, self.mean, self.std_deviation ) } } impl GasFuzzingInfo { #[must_use] pub fn new(gas_usages: &[GasVector]) -> Self { let l1_gas_values: Vec = gas_usages.iter().map(|gv| gv.l1_gas.0).collect(); let l1_data_gas_values: Vec = gas_usages.iter().map(|gv| gv.l1_data_gas.0).collect(); let l2_gas_values: Vec = gas_usages.iter().map(|gv| gv.l2_gas.0).collect(); GasFuzzingInfo { l1_gas: { GasStats::new(l1_gas_values.as_ref()) }, l1_data_gas: { GasStats::new(l1_data_gas_values.as_ref()) }, l2_gas: { GasStats::new(l2_gas_values.as_ref()) }, } } } #[derive(Debug, PartialEq, Clone)] pub struct FuzzingStatistics { pub runs: usize, } pub trait TestType { type GasInfo: fmt::Debug + Clone; type TestStatistics: fmt::Debug + Clone; type TraceData: fmt::Debug + Clone; } #[derive(Debug, PartialEq, Clone)] pub struct Fuzzing; impl TestType for Fuzzing { type GasInfo = GasFuzzingInfo; type TestStatistics = FuzzingStatistics; type TraceData = (); } #[derive(Debug, PartialEq, Clone)] pub struct Single; impl TestType for Single { type GasInfo = SingleTestGasInfo; type TestStatistics = (); type TraceData = VersionedProfilerCallTrace; } /// Summary of running a single test case #[expect(clippy::large_enum_variant)] #[derive(Debug, Clone)] pub enum TestCaseSummary { /// Test case passed Passed { /// Name of the test case name: String, /// Message to be printed after the test case run msg: Option, /// Trace of the test case run debugging_trace: Option, /// Information on used gas gas_info: ::GasInfo, /// Resources used during test used_resources: UsedResources, /// Statistics of the test run test_statistics: ::TestStatistics, /// Test trace data trace_data: ::TraceData, }, /// Test case failed Failed { /// Name of the test case name: String, /// Message returned by the test case run msg: Option, /// Trace of the test case run debugging_trace: Option, /// Random arguments used in the fuzz test case run fuzzer_args: Vec, /// Statistics of the test run test_statistics: ::TestStatistics, }, /// Test case ignored due to `#[ignored]` attribute or `--ignored` flag Ignored { /// Name of the test case name: String, }, /// Test case skipped due to exit first or execution interrupted, test result is ignored. Interrupted {}, /// Test case excluded from current partition ExcludedFromPartition {}, } #[derive(Debug)] // We allow large enum variant because `Single` is the bigger variant and it is used most often #[expect(clippy::large_enum_variant)] pub enum AnyTestCaseSummary { Fuzzing(TestCaseSummary), Single(TestCaseSummary), } impl TestCaseSummary { #[must_use] pub fn name(&self) -> Option<&str> { match self { TestCaseSummary::Failed { name, .. } | TestCaseSummary::Passed { name, .. } | TestCaseSummary::Ignored { name, .. } => Some(name), TestCaseSummary::Interrupted { .. } | TestCaseSummary::ExcludedFromPartition { .. } => { None } } } #[must_use] pub fn msg(&self) -> Option<&str> { match self { TestCaseSummary::Failed { msg: Some(msg), .. } | TestCaseSummary::Passed { msg: Some(msg), .. } => Some(msg), _ => None, } } #[must_use] pub fn debugging_trace(&self) -> Option<&debugging::Trace> { match self { TestCaseSummary::Passed { debugging_trace, .. } | TestCaseSummary::Failed { debugging_trace, .. } => debugging_trace.as_ref(), _ => None, } } } impl TestCaseSummary { #[must_use] pub fn from(results: Vec>) -> Self { let last: TestCaseSummary = results .iter() .last() .cloned() .expect("Fuzz test should always run at least once"); // Only the last result matters as fuzzing is cancelled after first fail match last { TestCaseSummary::Passed { name, msg, gas_info: _, used_resources: _, test_statistics: (), trace_data: _, debugging_trace, } => { let runs = results.len(); let gas_usages: Vec = results .into_iter() .map(|a| match a { TestCaseSummary::Passed { gas_info, .. } => gas_info.gas_used, _ => unreachable!(), }) .collect(); TestCaseSummary::Passed { name, msg, gas_info: GasFuzzingInfo::new(gas_usages.as_ref()), used_resources: UsedResources::default(), test_statistics: FuzzingStatistics { runs }, trace_data: (), debugging_trace, } } TestCaseSummary::Failed { name, msg, fuzzer_args, debugging_trace, test_statistics: (), } => TestCaseSummary::Failed { name, msg, fuzzer_args, test_statistics: FuzzingStatistics { runs: results.len(), }, debugging_trace, }, TestCaseSummary::Ignored { name } => TestCaseSummary::Ignored { name: name.clone() }, TestCaseSummary::Interrupted {} => TestCaseSummary::Interrupted {}, TestCaseSummary::ExcludedFromPartition {} => TestCaseSummary::ExcludedFromPartition {}, } } } fn build_expected_panic_message(expected_panic_value: &ExpectedPanicValue) -> String { match expected_panic_value { ExpectedPanicValue::Any => "\n Expected to panic, but no panic occurred\n".into(), ExpectedPanicValue::Exact(panic_data) => { let panic_string = join_short_strings(panic_data); format!( "\n Expected to panic, but no panic occurred\n Expected panic data: {panic_data:?} ({panic_string})\n" ) } } } fn check_if_matching_and_get_message( actual_panic_value: &[Felt], expected_panic_value: &ExpectedPanicValue, ) -> (bool, Option) { let expected_data = match expected_panic_value { ExpectedPanicValue::Exact(data) => Some(data), ExpectedPanicValue::Any => None, }; match expected_data { Some(expected) if is_matching_should_panic_data(actual_panic_value, expected) => { (true, None) } Some(expected) => { let panic_string = convert_felts_to_byte_array_string(actual_panic_value) .unwrap_or_else(|| join_short_strings(actual_panic_value)); let expected_string = convert_felts_to_byte_array_string(expected) .unwrap_or_else(|| join_short_strings(expected)); let message = Some(format!( "\n Incorrect panic data\n {}\n {}\n", format_args!("Actual: {actual_panic_value:?} ({panic_string})"), format_args!("Expected: {expected:?} ({expected_string})") )); (false, message) } None => (true, None), } } impl TestCaseSummary { #[must_use] pub(crate) fn from_run_completed( RunCompleted { status, call_trace, gas_used, used_resources, encountered_errors, fuzzer_args, fork_data, }: RunCompleted, test_case: &TestCaseWithResolvedConfig, contracts_data: &ContractsData, versioned_program_path: &Utf8Path, trace_args: &TraceArgs, gas_report_enabled: bool, ) -> Self { let name = test_case.name.clone(); let debugging_trace = build_debugging_trace( &call_trace.borrow(), contracts_data, trace_args, name.clone(), &fork_data, ); let gas_info = SingleTestGasInfo::new(gas_used); let gas_info = if gas_report_enabled { gas_info.get_with_report_data( &call_trace.borrow(), &ContractsDataStore::new(contracts_data, &fork_data), ) } else { gas_info }; match status { RunStatus::Success(data) => match &test_case.config.expected_result { ExpectedTestResult::Success => { let summary = TestCaseSummary::Passed { name, msg: build_readable_text(&data), test_statistics: (), gas_info, used_resources, trace_data: VersionedProfilerCallTrace::V1(build_profiler_call_trace( &call_trace, contracts_data, &fork_data, versioned_program_path, )), debugging_trace, }; check_available_gas(test_case.config.available_gas, summary) } ExpectedTestResult::Panics(expected_panic_value) => TestCaseSummary::Failed { name, msg: Some(build_expected_panic_message(expected_panic_value)), fuzzer_args, test_statistics: (), debugging_trace, }, }, RunStatus::Panic(value) => match &test_case.config.expected_result { ExpectedTestResult::Success => TestCaseSummary::Failed { name, msg: build_readable_text(&value) .map(|msg| add_backtrace_footer(msg, contracts_data, &encountered_errors)), fuzzer_args, test_statistics: (), debugging_trace, }, ExpectedTestResult::Panics(expected_panic_value) => { let (matching, msg) = check_if_matching_and_get_message(&value, expected_panic_value); if matching { TestCaseSummary::Passed { name, msg: is_backtrace_enabled() .then(|| get_backtrace(contracts_data, &encountered_errors)), test_statistics: (), gas_info, used_resources, trace_data: VersionedProfilerCallTrace::V1(build_profiler_call_trace( &call_trace, contracts_data, &fork_data, versioned_program_path, )), debugging_trace, } } else { TestCaseSummary::Failed { name, msg: msg.map(|msg| { add_backtrace_footer(msg, contracts_data, &encountered_errors) }), fuzzer_args, test_statistics: (), debugging_trace, } } } }, } } } fn join_short_strings(data: &[Felt]) -> String { data.iter() .map(|felt| felt.to_short_string().unwrap_or_default()) .collect::>() .join(", ") } fn is_matching_should_panic_data(data: &[Felt], pattern: &[Felt]) -> bool { let data_str = convert_felts_to_byte_array_string(data); let pattern_str = convert_felts_to_byte_array_string(pattern); if let (Some(data), Some(pattern)) = (data_str, pattern_str) { data.contains(&pattern) // If both data and pattern are byte arrays, pattern should be a substring of data } else { // Compare logic depends on the presence of `ENTRYPOINT_FAILED_ERROR` in the expected data. if pattern.contains(&ENTRYPOINT_FAILED_ERROR_FELT) { // If data includes `ENTRYPOINT_FAILED_ERROR` compare as is. data == pattern } else { // Otherwise, remove all generic errors and then compare. let filtered: Vec = data .iter() .copied() .filter(|f| f != &ENTRYPOINT_FAILED_ERROR_FELT) .collect(); filtered.as_slice() == pattern } } } fn convert_felts_to_byte_array_string(data: &[Felt]) -> Option { ByteArray::deserialize_with_magic(data) .map(|byte_array| byte_array.to_string()) .ok() } impl AnyTestCaseSummary { #[must_use] pub fn name(&self) -> Option<&str> { match self { AnyTestCaseSummary::Fuzzing(case) => case.name(), AnyTestCaseSummary::Single(case) => case.name(), } } #[must_use] pub fn msg(&self) -> Option<&str> { match self { AnyTestCaseSummary::Fuzzing(case) => case.msg(), AnyTestCaseSummary::Single(case) => case.msg(), } } #[must_use] pub fn debugging_trace(&self) -> Option<&debugging::Trace> { match self { AnyTestCaseSummary::Fuzzing(case) => case.debugging_trace(), AnyTestCaseSummary::Single(case) => case.debugging_trace(), } } #[must_use] pub fn is_passed(&self) -> bool { matches!( self, AnyTestCaseSummary::Single(TestCaseSummary::Passed { .. }) | AnyTestCaseSummary::Fuzzing(TestCaseSummary::Passed { .. }) ) } #[must_use] pub fn is_failed(&self) -> bool { matches!( self, AnyTestCaseSummary::Single(TestCaseSummary::Failed { .. }) | AnyTestCaseSummary::Fuzzing(TestCaseSummary::Failed { .. }) ) } #[must_use] pub fn is_interrupted(&self) -> bool { matches!( self, AnyTestCaseSummary::Single(TestCaseSummary::Interrupted { .. }) | AnyTestCaseSummary::Fuzzing(TestCaseSummary::Interrupted { .. }) ) } #[must_use] pub fn is_ignored(&self) -> bool { matches!( self, AnyTestCaseSummary::Single(TestCaseSummary::Ignored { .. }) | AnyTestCaseSummary::Fuzzing(TestCaseSummary::Ignored { .. }) ) } #[must_use] pub fn is_excluded_from_partition(&self) -> bool { matches!( self, AnyTestCaseSummary::Single(TestCaseSummary::ExcludedFromPartition { .. }) | AnyTestCaseSummary::Fuzzing(TestCaseSummary::ExcludedFromPartition { .. }) ) } } #[cfg(test)] mod tests { use crate::test_case_summary::*; use starknet_api::execution_resources::{GasAmount, GasVector}; const FLOAT_ERROR: f64 = 0.01; #[test] fn test_gas_statistics_new() { let gas_usages = vec![ GasVector { l1_gas: GasAmount(10), l1_data_gas: GasAmount(20), l2_gas: GasAmount(30), }, GasVector { l1_gas: GasAmount(20), l1_data_gas: GasAmount(40), l2_gas: GasAmount(60), }, GasVector { l1_gas: GasAmount(30), l1_data_gas: GasAmount(60), l2_gas: GasAmount(90), }, ]; let stats = GasFuzzingInfo::new(&gas_usages); assert_eq!(stats.l1_gas.min, 10); assert_eq!(stats.l1_data_gas.min, 20); assert_eq!(stats.l2_gas.min, 30); assert_eq!(stats.l1_gas.max, 30); assert_eq!(stats.l1_data_gas.max, 60); assert_eq!(stats.l2_gas.max, 90); assert!((stats.l1_gas.mean - 20.0).abs() < FLOAT_ERROR); assert!((stats.l1_data_gas.mean - 40.0).abs() < FLOAT_ERROR); assert!((stats.l2_gas.mean - 60.0).abs() < FLOAT_ERROR); assert!((stats.l1_gas.std_deviation - 8.165).abs() < FLOAT_ERROR); assert!((stats.l1_data_gas.std_deviation - 16.33).abs() < FLOAT_ERROR); assert!((stats.l2_gas.std_deviation - 24.49).abs() < FLOAT_ERROR); } #[test] fn test_is_matching_should_panic_data_entrypoint_failed() { let data = vec![ Felt::from(11_u8), Felt::from(22_u8), Felt::from(33_u8), ENTRYPOINT_FAILED_ERROR_FELT, ENTRYPOINT_FAILED_ERROR_FELT, ]; assert!(is_matching_should_panic_data(&data, &data)); let non_matching_pattern = vec![ Felt::from(11_u8), Felt::from(22_u8), Felt::from(33_u8), ENTRYPOINT_FAILED_ERROR_FELT, ]; assert!(!is_matching_should_panic_data(&data, &non_matching_pattern)); let pattern = vec![Felt::from(11_u8), Felt::from(22_u8), Felt::from(33_u8)]; assert!(is_matching_should_panic_data(&data, &pattern)); let non_matching_pattern = vec![Felt::from(11_u8), Felt::from(22_u8)]; assert!(!is_matching_should_panic_data(&data, &non_matching_pattern)); } } ================================================ FILE: crates/forge-runner/src/test_target_summary.rs ================================================ use crate::test_case_summary::AnyTestCaseSummary; /// Summary of the test run in the file #[derive(Debug)] pub struct TestTargetSummary { /// Summaries of each test case in the file pub test_case_summaries: Vec, } impl TestTargetSummary { #[must_use] pub fn count_passed(&self) -> usize { self.test_case_summaries .iter() .filter(|tu| tu.is_passed()) .count() } #[must_use] pub fn count_failed(&self) -> usize { self.test_case_summaries .iter() .filter(|tu| tu.is_failed()) .count() } #[must_use] pub fn count_interrupted(&self) -> usize { self.test_case_summaries .iter() .filter(|tu| tu.is_interrupted()) .count() } #[must_use] pub fn count_ignored(&self) -> usize { self.test_case_summaries .iter() .filter(|tu| tu.is_ignored()) .count() } } ================================================ FILE: crates/forge-runner/src/tests_summary.rs ================================================ use crate::test_target_summary::TestTargetSummary; use serde::Serialize; // TODO(#2574): Bring back "filtered out" number in tests summary when running with `--exact` flag #[derive(Serialize)] pub struct TestsSummary { passed: usize, failed: usize, interrupted: usize, ignored: usize, filtered: Option, } impl TestsSummary { #[must_use] pub fn new(summaries: &[TestTargetSummary], filtered: Option) -> Self { let passed = summaries.iter().map(TestTargetSummary::count_passed).sum(); let failed = summaries.iter().map(TestTargetSummary::count_failed).sum(); let interrupted = summaries .iter() .map(TestTargetSummary::count_interrupted) .sum(); let ignored = summaries.iter().map(TestTargetSummary::count_ignored).sum(); Self { passed, failed, interrupted, ignored, filtered, } } #[must_use] pub fn format_summary_message(&self) -> String { let filtered = self .filtered .map_or_else(|| "other".to_string(), |v| v.to_string()); let interrupted = if self.interrupted > 0 { format!("\nInterrupted execution of {} test(s).", self.interrupted) } else { String::new() }; format!( "{} passed, {} failed, {} ignored, {filtered} filtered out{interrupted}", self.passed, self.failed, self.ignored, ) } } ================================================ FILE: crates/foundry-ui/Cargo.toml ================================================ [package] name = "foundry-ui" version.workspace = true edition.workspace = true [dependencies] serde.workspace = true serde_json.workspace = true anyhow.workspace = true starknet-types-core.workspace = true console.workspace = true ================================================ FILE: crates/foundry-ui/src/components/error.rs ================================================ use anyhow::Error; use console::style; use serde::Serialize; use serde_json::{Value, json}; use crate::Message; use super::tagged::TaggedMessage; /// Error message. #[derive(Serialize)] pub struct ErrorMessage(T); impl ErrorMessage { #[must_use] pub fn new(message: T) -> Self { Self(message) } } impl Message for ErrorMessage { fn text(&self) -> String { let tag = style("ERROR").red().to_string(); let tagged_message = TaggedMessage::new(&tag, &self.0); tagged_message.text() } fn json(&self) -> Value { json!({ "message_type": "error", "message": self.0.json(), }) } } impl From for ErrorMessage { fn from(error: Error) -> Self { Self::new(format!("{error:#}")) } } ================================================ FILE: crates/foundry-ui/src/components/labeled.rs ================================================ use serde::Serialize; use serde_json::{Value, json}; use crate::Message; /// Generic message with `label` prefix. /// /// e.g. "Tests: 1 passed, 1 failed" #[derive(Serialize)] pub struct LabeledMessage<'a, T: Message> { label: &'a str, message: &'a T, } impl<'a, T: Message> LabeledMessage<'a, T> { #[must_use] pub fn new(label: &'a str, message: &'a T) -> Self { Self { label, message } } } impl Message for LabeledMessage<'_, T> { fn text(&self) -> String { format!("{}: {}", self.label, self.message.text()) } fn json(&self) -> Value { json!( { "message_type": "labeled", "label": self.label, "message": self.message.json(), } ) } } ================================================ FILE: crates/foundry-ui/src/components/mod.rs ================================================ //! This module provides various ready to use message types for use with //! a [`UI`]. pub mod error; pub mod labeled; pub mod tagged; pub mod warning; ================================================ FILE: crates/foundry-ui/src/components/tagged.rs ================================================ use serde::Serialize; use serde_json::{Value, json}; use crate::Message; /// Generic message with `tag` prefix. /// /// e.g. "[WARNING]: An example warning message" #[derive(Serialize)] pub struct TaggedMessage<'a, T: Message> { tag: &'a str, message: &'a T, } impl<'a, T: Message> TaggedMessage<'a, T> { #[must_use] pub fn new(tag: &'a str, message: &'a T) -> Self { Self { tag, message } } } impl Message for TaggedMessage<'_, T> { fn text(&self) -> String { format!("[{}] {}", self.tag, self.message.text()) } fn json(&self) -> Value { json!( { "message_type": "tagged", "tag": self.tag, "message": self.message.json(), } ) } } ================================================ FILE: crates/foundry-ui/src/components/warning.rs ================================================ use console::style; use serde::Serialize; use serde_json::{Value, json}; use crate::Message; use super::tagged::TaggedMessage; /// Warning message. #[derive(Serialize)] pub struct WarningMessage(T); impl WarningMessage { #[must_use] pub fn new(message: T) -> Self { Self(message) } } impl Message for WarningMessage { fn text(&self) -> String { let tag = style("WARNING").yellow().to_string(); let tagged_message = TaggedMessage::new(&tag, &self.0); tagged_message.text() } fn json(&self) -> Value { json!({ "message_type": "warning", "message": self.0.json(), }) } } ================================================ FILE: crates/foundry-ui/src/lib.rs ================================================ pub use message::*; pub mod components; pub mod message; pub mod styling; #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum OutputFormat { #[default] Human, Json, } /// An abstraction around console output which stores preferences for output format (human vs JSON), /// colour, etc. /// /// All messaging (basically all writes to `stdout`) must go through this object. #[derive(Debug, Default)] pub struct UI { output_format: OutputFormat, // TODO(#3395): Add state here, that can be used for spinner } impl UI { /// Create a new [`UI`] instance configured with the given output format. #[must_use] pub fn new(output_format: OutputFormat) -> Self { Self { output_format } } /// Create a [`String`] representation of the given message based on the configured output format. fn format_message(&self, message: &impl Message) -> String { match self.output_format { OutputFormat::Human => message.text(), OutputFormat::Json => message.json().to_string(), } } /// Print the given message to stdout using the configured output format. pub fn println(&self, message: &impl Message) { println!("{}", self.format_message(message)); } /// Print the given message to stderr using the configured output format. pub fn eprintln(&self, message: &impl Message) { eprintln!("{}", self.format_message(message)); } /// Print a blank line to stdout. pub fn print_blank_line(&self) { match self.output_format { OutputFormat::Human => println!(), OutputFormat::Json => {} } } #[must_use] pub fn output_format(&self) -> OutputFormat { self.output_format } } ================================================ FILE: crates/foundry-ui/src/message.rs ================================================ use serde::Serialize; use serde_json::{Value, json}; /// A typed object that can be either printed as a human-readable message or serialized as JSON. pub trait Message { /// Return textual (human) representation of this message. fn text(&self) -> String; /// Return JSON representation of this message. fn json(&self) -> Value; } impl Message for T where T: Serialize, { fn text(&self) -> String { self.to_string() } fn json(&self) -> Value { json!({ "message": self.to_string() }) } } ================================================ FILE: crates/foundry-ui/src/output.rs ================================================ ================================================ FILE: crates/foundry-ui/src/styling.rs ================================================ use console::style; use starknet_types_core::felt::Felt; use std::fmt::Write; #[derive(Debug, Clone)] enum OutputEntry { SuccessMessage(String), ErrorMessage(String), Field { label: String, value: String }, BlankLine, Text(String), } pub struct OutputBuilder { entries: Vec, } impl Default for OutputBuilder { fn default() -> Self { Self::new() } } impl OutputBuilder { #[must_use] pub fn new() -> Self { Self { entries: Vec::new(), } } fn calculate_field_label_width(&self) -> usize { self.entries .iter() .filter_map(|entry| { if let OutputEntry::Field { label, .. } = entry { Some(label.len() + 1) } else { None } }) .max() .unwrap_or(0) } #[must_use] pub fn success_message(mut self, message: &str) -> Self { self.entries .push(OutputEntry::SuccessMessage(message.to_string())); self } #[must_use] pub fn error_message(mut self, message: &str) -> Self { self.entries .push(OutputEntry::ErrorMessage(message.to_string())); self } #[must_use] pub fn field(mut self, label_text: &str, value: &str) -> Self { self.entries.push(OutputEntry::Field { label: label_text.to_string(), value: value.to_string(), }); self } #[must_use] pub fn blank_line(mut self) -> Self { self.entries.push(OutputEntry::BlankLine); self } #[must_use] pub fn text_field(mut self, text: &str) -> Self { self.entries.push(OutputEntry::Text(text.to_string())); self } #[must_use] pub fn if_some(mut self, option: Option<&T>, f: F) -> Self where F: FnOnce(Self, &T) -> Self, { if let Some(value) = option { self = f(self, value); } self } #[must_use] pub fn padded_felt_field(self, label: &str, felt: &Felt) -> Self { self.field(label, &felt.to_fixed_hex_string()) } #[must_use] pub fn felt_field(self, label: &str, felt: &Felt) -> Self { self.field(label, &felt.to_hex_string()) } #[must_use] pub fn felt_list_field(self, label: &str, felts: &[Felt]) -> Self { let felts = felts.iter().map(Felt::to_hex_string).collect::>(); self.field(label, &format!("[{}]", felts.join(", "))) } #[must_use] pub fn build(self) -> String { let field_width = self.calculate_field_label_width(); let mut content = String::new(); for entry in self.entries { match entry { OutputEntry::SuccessMessage(message) => { writeln!(content, "{}: {}", style("Success").green(), message).unwrap(); } OutputEntry::ErrorMessage(message) => { writeln!(content, "{}: {}", style("Error").red(), message).unwrap(); } OutputEntry::Field { label, value } => { writeln!( content, "{:field_width$} {}", format!("{}:", label), style(value).yellow(), ) .unwrap(); } OutputEntry::BlankLine => { content.push('\n'); } OutputEntry::Text(text) => { if !content.is_empty() && !content.ends_with('\n') { content.push('\n'); } content.push_str(&text); content.push('\n'); } } } if content.ends_with('\n') { content.pop(); } content } } ================================================ FILE: crates/native-api/Cargo.toml ================================================ [package] name = "native-api" version.workspace = true edition.workspace = true [dependencies] thiserror.workspace = true starknet_api.workspace = true cairo-lang-starknet-classes.workspace = true cairo-native.workspace = true ================================================ FILE: crates/native-api/src/lib.rs ================================================ //! This module provides functionality related to `cairo-native`. //! //! Currently, it includes: //! - Compiling Sierra contract classes into `cairo-native` executors. use cairo_lang_starknet_classes::contract_class::ContractClass; use cairo_native::executor::AotContractExecutor; use starknet_api::contract_class::SierraVersion; /// Compiles a given Sierra [`ContractClass`] into an [`AotContractExecutor`] for `cairo-native` execution. #[must_use] pub fn compile_contract_class(contract_class: &ContractClass) -> AotContractExecutor { let sierra_version = extract_sierra_version(contract_class); let extracted = contract_class .extract_sierra_program(false) .expect("extraction should succeed"); AotContractExecutor::new( &extracted.program, &contract_class.entry_points_by_type, sierra_version.clone().into(), cairo_native::OptLevel::Default, None, ) .expect("compilation should succeed") } /// Extracts the Sierra version from the given [`ContractClass`]. fn extract_sierra_version(contract_class: &ContractClass) -> SierraVersion { let sierra_version_values = contract_class .sierra_program .iter() .take(3) .map(|x| x.value.clone()) .collect::>(); SierraVersion::extract_from_program(&sierra_version_values) .expect("version extraction should succeed") } ================================================ FILE: crates/runtime/Cargo.toml ================================================ [package] name = "runtime" version = "1.0.0" edition.workspace = true [features] testing = [] [dependencies] indoc.workspace = true anyhow.workspace = true conversions.workspace = true cairo-lang-casm.workspace = true cairo-lang-utils.workspace = true starknet-types-core.workspace = true starknet_api.workspace = true starknet-rust.workspace = true blockifier.workspace = true cairo-vm.workspace = true serde_json.workspace = true serde.workspace = true thiserror.workspace = true num-traits.workspace = true shared.workspace = true ================================================ FILE: crates/runtime/src/lib.rs ================================================ use crate::vm::{cell_ref_to_relocatable, extract_relocatable, get_val, vm_get_range}; use anyhow::Result; use blockifier::execution::syscalls::hint_processor::SyscallHintProcessor; use blockifier::execution::syscalls::vm_syscall_utils::SyscallSelector; use blockifier::state::errors::StateError; use cairo_lang_casm::hints::{ExternalHint, Hint, StarknetHint}; use cairo_lang_casm::operand::{CellRef, ResOperand}; use cairo_lang_utils::bigint::BigIntAsHex; use cairo_vm::hint_processor::hint_processor_definition::{ HintProcessor, HintProcessorLogic, HintReference, }; use cairo_vm::serde::deserialize_program::ApTracking; use cairo_vm::types::exec_scope::ExecutionScopes; use cairo_vm::types::relocatable::Relocatable; use cairo_vm::vm::errors::hint_errors::HintError; use cairo_vm::vm::errors::hint_errors::HintError::CustomHint; use cairo_vm::vm::errors::vm_errors::VirtualMachineError; use cairo_vm::vm::runners::cairo_runner::{ResourceTracker, RunResources}; use cairo_vm::vm::vm_core::VirtualMachine; use conversions::byte_array::ByteArray; use conversions::serde::SerializedValue; use conversions::serde::deserialize::BufferReadError; use conversions::serde::deserialize::BufferReader; use conversions::serde::serialize::{CairoSerialize, SerializeToFeltVec}; use indoc::indoc; use shared::vm::VirtualMachineExt; use starknet_api::StarknetApiError; use starknet_types_core::felt::Felt; use std::any::Any; use std::collections::HashMap; use std::io; use std::sync::Arc; use thiserror::Error; pub mod starknet; mod vm; // from core/src/starknet/testing.cairo const CAIRO_TEST_CHEATCODES: [&str; 14] = [ "set_block_number", "set_caller_address", "set_contract_address", "set_sequencer_address", "set_block_timestamp", "set_version", "set_account_contract_address", "set_max_fee", "set_transaction_hash", "set_chain_id", "set_nonce", "set_signature", "pop_log", "pop_l2_to_l1_message", ]; pub trait SyscallPtrAccess { fn get_mut_syscall_ptr(&mut self) -> &mut Relocatable; } pub struct StarknetRuntime<'a> { pub hint_handler: SyscallHintProcessor<'a>, pub panic_traceback: Option>, } impl SyscallPtrAccess for StarknetRuntime<'_> { fn get_mut_syscall_ptr(&mut self) -> &mut Relocatable { &mut self.hint_handler.syscall_ptr } } impl ResourceTracker for StarknetRuntime<'_> { fn consumed(&self) -> bool { self.hint_handler.base.context.vm_run_resources.consumed() } fn consume_step(&mut self) { self.hint_handler .base .context .vm_run_resources .consume_step(); } fn get_n_steps(&self) -> Option { self.hint_handler .base .context .vm_run_resources .get_n_steps() } fn run_resources(&self) -> &RunResources { self.hint_handler .base .context .vm_run_resources .run_resources() } } impl SignalPropagator for StarknetRuntime<'_> { fn propagate_system_call_signal( &mut self, _selector: SyscallSelector, _vm: &mut VirtualMachine, ) { } fn propagate_cheatcode_signal(&mut self, _selector: &str, _inputs: &[Felt]) {} } fn parse_selector(selector: &BigIntAsHex) -> Result { let selector = &selector.value.to_bytes_be().1; let selector = std::str::from_utf8(selector) .map_err(|_| CustomHint(Box::from("Failed to parse the cheatcode selector")))?; Ok(String::from(selector)) } fn fetch_cheatcode_input( vm: &mut VirtualMachine, input_start: &ResOperand, input_end: &ResOperand, ) -> Result, HintError> { let input_start = extract_relocatable(vm, input_start)?; let input_end = extract_relocatable(vm, input_end)?; let inputs = vm_get_range(vm, input_start, input_end) .map_err(|_| CustomHint(Box::from("Failed to read input data")))?; Ok(inputs) } impl HintProcessorLogic for StarknetRuntime<'_> { fn execute_hint( &mut self, vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, hint_data: &Box, ) -> Result<(), HintError> { let maybe_extended_hint = hint_data.downcast_ref::(); if let Some(extended_hint) = maybe_extended_hint { match extended_hint { Hint::Starknet(StarknetHint::Cheatcode { selector, input_start: _, input_end: _, output_start: _, output_end: _, }) => { let selector = parse_selector(selector)?; let is_cairo_test_fn = CAIRO_TEST_CHEATCODES.contains(&selector.as_str()); let error = format!( "Function `{selector}` is not supported in this runtime\n{}", if is_cairo_test_fn { "Check if functions are imported from `snforge_std`/`sncast_std` NOT from `starknet::testing`" } else { "Check if used library (`snforge_std` or `sncast_std`) is compatible with used binary, probably one of them is not updated" } ); return Err(CustomHint(error.into())); } Hint::External(ExternalHint::AddTrace { flag }) => { const PANIC_IN_BYTES: Felt = Felt::from_hex_unchecked("0x70616e6963"); let flag = get_val(vm, flag)?; // Setting the panic backtrace if the given flag is panic. if flag == PANIC_IN_BYTES { self.panic_traceback = Some(vm.get_reversed_pc_traceback()); } return Ok(()); } _ => {} } } self.hint_handler.execute_hint(vm, exec_scopes, hint_data) } fn compile_hint( &self, hint_code: &str, ap_tracking_data: &ApTracking, reference_ids: &HashMap, references: &[HintReference], accessible_scopes: &[String], constants: Arc>, ) -> Result, VirtualMachineError> { self.hint_handler.compile_hint( hint_code, ap_tracking_data, reference_ids, references, accessible_scopes, constants, ) } } pub struct ExtendedRuntime { pub extension: Extension, pub extended_runtime: ::Runtime, } impl HintProcessorLogic for ExtendedRuntime { fn execute_hint( &mut self, vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, hint_data: &Box, ) -> Result<(), HintError> { let maybe_extended_hint = hint_data.downcast_ref::(); match maybe_extended_hint { Some(Hint::Starknet(starknet_hint)) => match starknet_hint { StarknetHint::Cheatcode { selector, input_start, input_end, output_start, output_end, } => self.execute_cheatcode_hint( vm, exec_scopes, hint_data, selector, &VmIoPointers { input_start, input_end, output_start, output_end, }, ), StarknetHint::SystemCall { .. } => { self.execute_syscall_hint(vm, exec_scopes, hint_data) } }, _ => self .extended_runtime .execute_hint(vm, exec_scopes, hint_data), } } fn compile_hint( &self, hint_code: &str, ap_tracking_data: &ApTracking, reference_ids: &HashMap, references: &[HintReference], accessible_scopes: &[String], constants: Arc>, ) -> Result, VirtualMachineError> { self.extended_runtime.compile_hint( hint_code, ap_tracking_data, reference_ids, references, accessible_scopes, constants, ) } } struct VmIoPointers<'a> { input_start: &'a ResOperand, input_end: &'a ResOperand, output_start: &'a CellRef, output_end: &'a CellRef, } impl ExtendedRuntime { fn execute_cheatcode_hint( &mut self, vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, hint_data: &Box, selector: &BigIntAsHex, vm_io_ptrs: &VmIoPointers, ) -> Result<(), HintError> { let selector = parse_selector(selector)?; let inputs = fetch_cheatcode_input(vm, vm_io_ptrs.input_start, vm_io_ptrs.input_end)?; let result = self.extension.handle_cheatcode( &selector, BufferReader::new(&inputs), &mut self.extended_runtime, vm, ); let res = match result { Ok(CheatcodeHandlingResult::Forwarded) => { let res = self .extended_runtime .execute_hint(vm, exec_scopes, hint_data); self.extension.handle_cheatcode_signal( &selector, &inputs, &mut self.extended_runtime, ); return res; } // it is serialized again to add `Result` discriminator Ok(CheatcodeHandlingResult::Handled(res)) => Ok(SerializedValue::new(res)), Err(err) => Err(ByteArray::from(err.to_string().as_str())), } .serialize_to_vec(); let WrittenData { start: result_start, end: result_end, } = write_data(res, vm)?; let output_start = vm_io_ptrs.output_start; let output_end = vm_io_ptrs.output_end; vm.insert_value(cell_ref_to_relocatable(*output_start, vm), result_start)?; vm.insert_value(cell_ref_to_relocatable(*output_end, vm), result_end)?; self.propagate_cheatcode_signal(&selector, &inputs); Ok(()) } fn execute_syscall_hint( &mut self, vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, hint_data: &Box, ) -> Result<(), HintError> { // We peek into memory to check the selector let selector = SyscallSelector::try_from( vm.get_integer(*self.get_mut_syscall_ptr()) .unwrap() .into_owned(), )?; if let SyscallHandlingResult::Handled = self.extension .override_system_call(selector, vm, &mut self.extended_runtime)? { self.propagate_system_call_signal(selector, vm); Ok(()) } else { self.extended_runtime .execute_hint(vm, exec_scopes, hint_data)?; self.extension .handle_system_call_signal(selector, vm, &mut self.extended_runtime); Ok(()) } } } pub trait SignalPropagator { fn propagate_system_call_signal(&mut self, selector: SyscallSelector, vm: &mut VirtualMachine); fn propagate_cheatcode_signal(&mut self, selector: &str, inputs: &[Felt]); } impl SignalPropagator for ExtendedRuntime { fn propagate_system_call_signal(&mut self, selector: SyscallSelector, vm: &mut VirtualMachine) { self.extended_runtime .propagate_system_call_signal(selector, vm); self.extension .handle_system_call_signal(selector, vm, &mut self.extended_runtime); } fn propagate_cheatcode_signal(&mut self, selector: &str, inputs: &[Felt]) { self.extended_runtime .propagate_cheatcode_signal(selector, inputs); self.extension .handle_cheatcode_signal(selector, inputs, &mut self.extended_runtime); } } impl SyscallPtrAccess for ExtendedRuntime { fn get_mut_syscall_ptr(&mut self) -> &mut Relocatable { self.extended_runtime.get_mut_syscall_ptr() } } impl ResourceTracker for ExtendedRuntime { fn consumed(&self) -> bool { self.extended_runtime.consumed() } fn consume_step(&mut self) { self.extended_runtime.consume_step(); } fn get_n_steps(&self) -> Option { self.extended_runtime.get_n_steps() } fn run_resources(&self) -> &RunResources { self.extended_runtime.run_resources() } } #[derive(Debug)] pub enum SyscallHandlingResult { Forwarded, Handled, } #[derive(Debug)] pub enum CheatcodeHandlingResult { Forwarded, Handled(Vec), } impl CheatcodeHandlingResult { pub fn from_serializable(serializable: impl CairoSerialize) -> Self { Self::Handled(serializable.serialize_to_vec()) } } pub trait ExtensionLogic { type Runtime: HintProcessor + SyscallPtrAccess + SignalPropagator; fn override_system_call( &mut self, _selector: SyscallSelector, _vm: &mut VirtualMachine, _extended_runtime: &mut Self::Runtime, ) -> Result { Ok(SyscallHandlingResult::Forwarded) } fn handle_cheatcode( &mut self, _selector: &str, _input_reader: BufferReader, _extended_runtime: &mut Self::Runtime, _vm: &VirtualMachine, ) -> Result { Ok(CheatcodeHandlingResult::Forwarded) } /// Different from `override_system_call` because it cannot be overridden, /// always receives a signal and cannot return an error /// Signals are executed in reverse order to normal syscall handlers /// Signals are executed after syscall is handled fn handle_system_call_signal( &mut self, _selector: SyscallSelector, _vm: &mut VirtualMachine, _extended_runtime: &mut Self::Runtime, ) { } /// Different from `handle_cheadcode` because it cannot be overridden, /// always receives a signal and cannot return an error /// Signals are executed in reverse order to normal cheatcode handlers /// Signals are executed after cheatcode is handled fn handle_cheatcode_signal( &mut self, _selector: &str, _inputs: &[Felt], _extended_runtime: &mut Self::Runtime, ) { } } // All errors that can be thrown from the hint executor have to be added here, // to prevent the whole runner from panicking #[derive(Error, Debug)] pub enum EnhancedHintError { #[error(transparent)] Hint(#[from] HintError), #[error(transparent)] Io(#[from] io::Error), #[error(transparent)] Anyhow(#[from] anyhow::Error), #[error(transparent)] State(#[from] StateError), #[error(transparent)] SerdeJson(#[from] serde_json::Error), #[error(transparent)] StarknetApi(#[from] StarknetApiError), #[error("Failed to parse {path} file")] FileParsing { path: String }, #[error("{error}")] OracleError { error: ByteArray }, } impl From for EnhancedHintError { fn from(value: BufferReadError) -> Self { EnhancedHintError::Anyhow( anyhow::Error::from(value) .context( indoc!(r" Reading from buffer failed, this can be caused by calling starknet::testing::cheatcode with invalid arguments. Probably `snforge_std`/`sncast_std` version is incompatible, check above for incompatibility warning. ") ) ) } } struct WrittenData { start: Relocatable, end: Relocatable, } fn write_data(data: Vec, vm: &mut VirtualMachine) -> Result { let mut ptr = vm.add_memory_segment(); let start = ptr; for data in data { vm.insert_value(ptr, data)?; ptr += 1; } Ok(WrittenData { start, end: ptr }) } ================================================ FILE: crates/runtime/src/starknet/constants.rs ================================================ pub const TEST_ENTRY_POINT_SELECTOR: &str = "TEST_CONTRACT_SELECTOR"; pub const TEST_CONTRACT_CLASS_HASH: &str = "0x117"; // snforge_std/src/cheatcodes.cairo::test_address pub const TEST_ADDRESS: &str = "0x01724987234973219347210837402"; ================================================ FILE: crates/runtime/src/starknet/context.rs ================================================ use crate::starknet::constants::{TEST_ADDRESS, TEST_CONTRACT_CLASS_HASH}; use blockifier::blockifier_versioned_constants::VersionedConstants; use blockifier::bouncer::BouncerConfig; use blockifier::context::{BlockContext, ChainInfo, FeeTokenAddresses, TransactionContext}; use blockifier::execution::common_hints::ExecutionMode; use blockifier::execution::contract_class::TrackedResource; use blockifier::execution::entry_point::{ EntryPointExecutionContext, EntryPointRevertInfo, SierraGasRevertTracker, }; use blockifier::transaction::objects::{ CommonAccountFields, CurrentTransactionInfo, TransactionInfo, }; use cairo_vm::vm::runners::cairo_runner::RunResources; use conversions::string::TryFromHexStr; use serde::{Deserialize, Serialize}; use starknet_api::block::{BlockInfo, BlockNumber, BlockTimestamp, GasPrice, StarknetVersion}; use starknet_api::block::{GasPriceVector, GasPrices, NonzeroGasPrice}; use starknet_api::data_availability::DataAvailabilityMode; use starknet_api::execution_resources::GasAmount; use starknet_api::transaction::fields::{AccountDeploymentData, PaymasterData, ProofFacts, Tip}; use starknet_api::transaction::fields::{AllResourceBounds, ResourceBounds, ValidResourceBounds}; use starknet_api::versioned_constants_logic::VersionedConstantsTrait; use starknet_api::{ core::{ChainId, ContractAddress, Nonce}, transaction::TransactionVersion, }; use starknet_types_core::felt::Felt; use std::sync::Arc; pub const DEFAULT_CHAIN_ID: &str = "SN_SEPOLIA"; pub const DEFAULT_BLOCK_NUMBER: u64 = 2000; pub const SEQUENCER_ADDRESS: &str = "0x1000"; pub const ERC20_CONTRACT_ADDRESS: &str = "0x1001"; pub const STRK_CONTRACT_ADDRESS: &str = "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d"; pub const ETH_CONTRACT_ADDRESS: &str = "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"; fn default_chain_id() -> ChainId { ChainId::from(String::from(DEFAULT_CHAIN_ID)) } #[must_use] pub fn build_block_context(block_info: &BlockInfo, chain_id: Option) -> BlockContext { BlockContext::new( block_info.clone(), ChainInfo { chain_id: chain_id.unwrap_or_else(default_chain_id), fee_token_addresses: FeeTokenAddresses { strk_fee_token_address: ContractAddress::try_from_hex_str(STRK_CONTRACT_ADDRESS) .unwrap(), eth_fee_token_address: ContractAddress::try_from_hex_str(ETH_CONTRACT_ADDRESS) .unwrap(), }, is_l3: false, }, VersionedConstants::latest_constants().clone(), BouncerConfig::default(), ) } fn build_tx_info() -> TransactionInfo { TransactionInfo::Current(CurrentTransactionInfo { common_fields: CommonAccountFields { version: TransactionVersion::THREE, nonce: Nonce(Felt::from(0_u8)), ..Default::default() }, resource_bounds: ValidResourceBounds::AllResources(AllResourceBounds { l1_gas: ResourceBounds { max_amount: GasAmount::from(0_u8), max_price_per_unit: GasPrice::from(1_u8), }, l2_gas: ResourceBounds { max_amount: GasAmount::from(0_u8), max_price_per_unit: GasPrice::from(0_u8), }, l1_data_gas: ResourceBounds { max_amount: GasAmount::from(0_u8), max_price_per_unit: GasPrice::from(1_u8), }, }), tip: Tip::default(), nonce_data_availability_mode: DataAvailabilityMode::L1, fee_data_availability_mode: DataAvailabilityMode::L1, paymaster_data: PaymasterData::default(), account_deployment_data: AccountDeploymentData::default(), proof_facts: ProofFacts::default(), }) } #[must_use] pub fn build_transaction_context( block_info: &BlockInfo, chain_id: Option, ) -> TransactionContext { TransactionContext { block_context: Arc::new(build_block_context(block_info, chain_id)), tx_info: build_tx_info(), } } #[must_use] pub fn build_context( block_info: &BlockInfo, chain_id: Option, tracked_resource: &TrackedResource, ) -> EntryPointExecutionContext { let transaction_context = Arc::new(build_transaction_context(block_info, chain_id)); let mut context = EntryPointExecutionContext::new( transaction_context, ExecutionMode::Execute, false, SierraGasRevertTracker::new(GasAmount::from(i64::MAX as u64)), ); context.revert_infos.0.push(EntryPointRevertInfo::new( ContractAddress::try_from(Felt::from_hex(TEST_ADDRESS).unwrap()).unwrap(), starknet_api::core::ClassHash(Felt::from_hex(TEST_CONTRACT_CLASS_HASH).unwrap()), context.n_emitted_events, context.n_sent_messages_to_l1, )); context.tracked_resource_stack.push(*tracked_resource); context } pub fn set_max_steps(entry_point_ctx: &mut EntryPointExecutionContext, max_n_steps: u32) { // override it to omit [`EntryPointExecutionContext::max_steps`] restrictions entry_point_ctx.vm_run_resources = RunResources::new(max_n_steps as usize); } // We need to be copying those 1:1 for serialization (caching purposes) #[derive(Clone, Serialize, Deserialize, Debug)] pub struct SerializableBlockInfo { pub block_number: BlockNumber, pub block_timestamp: BlockTimestamp, pub sequencer_address: ContractAddress, pub gas_prices: SerializableGasPrices, // A field which indicates if EIP-4844 blobs are used for publishing state diffs to l1 // This has influence on the cost of publishing the data on l1 pub use_kzg_da: bool, } #[expect(clippy::struct_field_names)] #[derive(Clone, Debug, Serialize, Deserialize)] pub struct SerializableGasPrices { eth_l1_gas_price: NonzeroGasPrice, strk_l1_gas_price: NonzeroGasPrice, eth_l1_data_gas_price: NonzeroGasPrice, strk_l1_data_gas_price: NonzeroGasPrice, eth_l2_gas_price: NonzeroGasPrice, strk_l2_gas_price: NonzeroGasPrice, } impl Default for SerializableGasPrices { fn default() -> Self { Self { eth_l1_gas_price: NonzeroGasPrice::new(GasPrice::from(100 * u128::pow(10, 9))).unwrap(), strk_l1_gas_price: NonzeroGasPrice::new(GasPrice::from(100 * u128::pow(10, 9))) .unwrap(), eth_l1_data_gas_price: NonzeroGasPrice::new(GasPrice::from(u128::pow(10, 6))).unwrap(), strk_l1_data_gas_price: NonzeroGasPrice::new(GasPrice::from(u128::pow(10, 9))).unwrap(), eth_l2_gas_price: NonzeroGasPrice::new(GasPrice::from(10000 * u128::pow(10, 9))) .unwrap(), strk_l2_gas_price: NonzeroGasPrice::new(GasPrice::from(10000 * u128::pow(10, 9))) .unwrap(), } } } impl Default for SerializableBlockInfo { fn default() -> Self { Self { block_number: BlockNumber(DEFAULT_BLOCK_NUMBER), block_timestamp: BlockTimestamp::default(), sequencer_address: TryFromHexStr::try_from_hex_str(SEQUENCER_ADDRESS).unwrap(), gas_prices: SerializableGasPrices::default(), use_kzg_da: true, } } } impl From for BlockInfo { fn from(forge_block_info: SerializableBlockInfo) -> Self { Self { block_number: forge_block_info.block_number, block_timestamp: forge_block_info.block_timestamp, sequencer_address: forge_block_info.sequencer_address, gas_prices: forge_block_info.gas_prices.into(), use_kzg_da: forge_block_info.use_kzg_da, starknet_version: StarknetVersion::LATEST, } } } impl From for SerializableBlockInfo { fn from(block_info: BlockInfo) -> Self { Self { block_number: block_info.block_number, block_timestamp: block_info.block_timestamp, sequencer_address: block_info.sequencer_address, gas_prices: block_info.gas_prices.into(), use_kzg_da: block_info.use_kzg_da, } } } impl From for GasPrices { fn from(forge_gas_prices: SerializableGasPrices) -> Self { GasPrices { eth_gas_prices: GasPriceVector { l1_gas_price: forge_gas_prices.eth_l1_gas_price, l1_data_gas_price: forge_gas_prices.eth_l1_data_gas_price, l2_gas_price: forge_gas_prices.eth_l2_gas_price, }, strk_gas_prices: GasPriceVector { l1_gas_price: forge_gas_prices.strk_l1_gas_price, l1_data_gas_price: forge_gas_prices.strk_l1_data_gas_price, l2_gas_price: forge_gas_prices.strk_l2_gas_price, }, } } } impl From for SerializableGasPrices { fn from(gas_prices: GasPrices) -> Self { SerializableGasPrices { eth_l1_gas_price: gas_prices.eth_gas_prices.l1_gas_price, strk_l1_gas_price: gas_prices.strk_gas_prices.l1_gas_price, eth_l1_data_gas_price: gas_prices.eth_gas_prices.l1_data_gas_price, strk_l1_data_gas_price: gas_prices.strk_gas_prices.l1_data_gas_price, eth_l2_gas_price: gas_prices.eth_gas_prices.l2_gas_price, strk_l2_gas_price: gas_prices.strk_gas_prices.l2_gas_price, } } } ================================================ FILE: crates/runtime/src/starknet/mod.rs ================================================ pub mod constants; pub mod context; pub mod state; ================================================ FILE: crates/runtime/src/starknet/state.rs ================================================ use blockifier::execution::contract_class::RunnableCompiledClass; use blockifier::state::cached_state::StorageEntry; use blockifier::state::errors::StateError; use blockifier::state::state_api::{StateReader, StateResult}; use starknet_api::contract_class::ContractClass; use starknet_api::core::CompiledClassHash; use starknet_api::core::{ClassHash, ContractAddress, Nonce}; use starknet_api::state::StorageKey; use starknet_types_core::felt::Felt; use std::collections::HashMap; /// A simple implementation of `StateReader` using `HashMap`s as storage. #[derive(Debug, Default)] pub struct DictStateReader { pub storage_view: HashMap, pub address_to_class_hash: HashMap, pub class_hash_to_class: HashMap, } impl StateReader for DictStateReader { fn get_storage_at( &self, contract_address: ContractAddress, key: StorageKey, ) -> StateResult { self.storage_view .get(&(contract_address, key)) .copied() .ok_or(StateError::StateReadError(format!( "Unable to get storage at address: {contract_address:?} and key: {key:?} from DictStateReader" ))) } fn get_nonce_at(&self, contract_address: ContractAddress) -> StateResult { Err(StateError::StateReadError(format!( "Unable to get nonce at {contract_address:?} from DictStateReader" ))) } fn get_class_hash_at(&self, contract_address: ContractAddress) -> StateResult { self.address_to_class_hash .get(&contract_address) .copied() .ok_or(StateError::UnavailableContractAddress(contract_address)) } fn get_compiled_class(&self, class_hash: ClassHash) -> StateResult { let contract_class = self.class_hash_to_class.get(&class_hash).cloned(); match contract_class { Some(contract_class) => Ok(contract_class.try_into()?), _ => Err(StateError::UndeclaredClassHash(class_hash)), } } fn get_compiled_class_hash(&self, class_hash: ClassHash) -> StateResult { Err(StateError::StateReadError(format!( "Unable to get compiled class hash at {class_hash:?} from DictStateReader" ))) } } ================================================ FILE: crates/runtime/src/vm.rs ================================================ //! Contains code copied from `cairo-lang-runner` use cairo_lang_casm::operand::{ BinOpOperand, CellRef, DerefOrImmediate, Operation, Register, ResOperand, }; use cairo_lang_utils::extract_matches; use cairo_vm::Felt252; use cairo_vm::types::relocatable::Relocatable; use cairo_vm::vm::errors::hint_errors::HintError; use cairo_vm::vm::errors::vm_errors::VirtualMachineError; use cairo_vm::vm::vm_core::VirtualMachine; pub fn cell_ref_to_relocatable(cell_ref: CellRef, vm: &VirtualMachine) -> Relocatable { let base = match cell_ref.register { Register::AP => vm.get_ap(), Register::FP => vm.get_fp(), }; (base + i32::from(cell_ref.offset)).unwrap() } /// Extracts a parameter assumed to be a buffer. fn extract_buffer(buffer: &ResOperand) -> (CellRef, Felt252) { let (cell, base_offset) = match buffer { ResOperand::Deref(cell) => (cell, 0.into()), ResOperand::BinOp(BinOpOperand { op: Operation::Add, a, b, }) => ( a, extract_matches!(b, DerefOrImmediate::Immediate) .clone() .value .into(), ), _ => panic!("Illegal argument for a buffer."), }; (*cell, base_offset) } /// Fetches the value of a cell plus an offset from the vm, useful for pointers. fn get_ptr( vm: &VirtualMachine, cell: CellRef, offset: &Felt252, ) -> Result { Ok((vm.get_relocatable(cell_ref_to_relocatable(cell, vm))? + offset)?) } /// Extracts a parameter assumed to be a buffer, and converts it into a relocatable. pub fn extract_relocatable( vm: &VirtualMachine, buffer: &ResOperand, ) -> Result { let (base, offset) = extract_buffer(buffer); get_ptr(vm, base, &offset) } pub fn vm_get_range( vm: &mut VirtualMachine, mut calldata_start_ptr: Relocatable, calldata_end_ptr: Relocatable, ) -> Result, HintError> { let mut values = vec![]; while calldata_start_ptr != calldata_end_ptr { let val = *vm.get_integer(calldata_start_ptr)?; values.push(val); calldata_start_ptr.offset += 1; } Ok(values) } pub(crate) fn get_cell_val( vm: &VirtualMachine, cell: CellRef, ) -> Result { Ok(*vm.get_integer(cell_ref_to_relocatable(cell, vm))?.as_ref()) } fn get_double_deref_val( vm: &VirtualMachine, cell: CellRef, offset: &Felt252, ) -> Result { Ok(*vm.get_integer(get_ptr(vm, cell, offset)?)?) } /// Fetches the value of `res_operand` from the vm. pub fn get_val( vm: &VirtualMachine, res_operand: &ResOperand, ) -> Result { match res_operand { ResOperand::Deref(cell) => get_cell_val(vm, *cell), ResOperand::DoubleDeref(cell, offset) => get_double_deref_val(vm, *cell, &(*offset).into()), ResOperand::Immediate(x) => Ok(Felt252::from(x.value.clone())), ResOperand::BinOp(op) => { let a = get_cell_val(vm, op.a)?; let b = match &op.b { DerefOrImmediate::Deref(cell) => get_cell_val(vm, *cell)?, DerefOrImmediate::Immediate(x) => Felt252::from(x.value.clone()), }; match op.op { Operation::Add => Ok(a + b), Operation::Mul => Ok(a * b), } } } } ================================================ FILE: crates/scarb-api/Cargo.toml ================================================ [package] name = "scarb-api" version = "1.0.0" edition.workspace = true [features] cairo-native = ["dep:cairo-native", "dep:native-api"] [dependencies] anyhow.workspace = true shared.workspace = true camino.workspace = true scarb-metadata.workspace = true scarb-ui.workspace = true serde.workspace = true serde_json.workspace = true thiserror.workspace = true which.workspace = true semver.workspace = true regex.workspace = true rayon.workspace = true itertools.workspace = true universal-sierra-compiler-api = { path = "../universal-sierra-compiler-api" } foundry-ui = { path = "../foundry-ui" } cairo-native = { workspace = true, optional = true } cairo-lang-starknet-classes.workspace = true native-api = { path = "../native-api", optional = true } tracing.workspace = true toml_edit.workspace = true [dev-dependencies] assert_fs.workspace = true indoc.workspace = true num-bigint.workspace = true ================================================ FILE: crates/scarb-api/src/artifacts/deserialized.rs ================================================ use anyhow::{Context, Result}; use camino::{Utf8Path, Utf8PathBuf}; use serde::Deserialize; use std::fs; #[derive(Deserialize, Debug, PartialEq, Clone)] pub struct StarknetArtifacts { pub version: u32, pub contracts: Vec, } #[derive(Deserialize, Debug, PartialEq, Clone)] pub struct StarknetContract { pub id: String, pub package_name: String, pub contract_name: String, pub module_path: String, pub artifacts: StarknetContractArtifactPaths, } #[derive(Deserialize, Debug, PartialEq, Clone)] pub struct StarknetContractArtifactPaths { pub sierra: Utf8PathBuf, pub casm: Option, } /// Get deserialized contents of `starknet_artifacts.json` file generated by Scarb /// /// # Arguments /// /// * `path` - A path to `starknet_artifacts.json` file. pub fn artifacts_for_package(path: &Utf8Path) -> Result { let starknet_artifacts = fs::read_to_string(path).with_context(|| format!("Failed to read {path:?} contents"))?; let starknet_artifacts: StarknetArtifacts = serde_json::from_str(starknet_artifacts.as_str()) .with_context(|| format!("Failed to parse starknet artifacts from path = {path:?}."))?; Ok(starknet_artifacts) } ================================================ FILE: crates/scarb-api/src/artifacts/representation.rs ================================================ use crate::artifacts::deserialized::{StarknetArtifacts, artifacts_for_package}; use anyhow::anyhow; use camino::{Utf8Path, Utf8PathBuf}; pub struct StarknetArtifactsRepresentation { pub(crate) base_path: Utf8PathBuf, pub(crate) artifacts: StarknetArtifacts, } impl StarknetArtifactsRepresentation { pub fn try_from_path(artifacts_path: &Utf8Path) -> anyhow::Result { let artifacts = artifacts_for_package(artifacts_path)?; let path = artifacts_path .parent() .ok_or_else(|| anyhow!("Failed to get parent for path = {}", &artifacts_path))? .to_path_buf(); Ok(Self { base_path: path, artifacts, }) } pub fn artifacts(self) -> Vec<(String, Utf8PathBuf)> { self.artifacts .contracts .into_iter() .map(|contract| { ( contract.contract_name, self.base_path.join(contract.artifacts.sierra.as_path()), ) }) .collect() } } #[cfg(test)] mod tests { use crate::ScarbCommand; use assert_fs::TempDir; use assert_fs::fixture::{FileTouch, FileWriteStr, PathChild, PathCopy}; use camino::Utf8PathBuf; use super::*; #[test] fn parsing_starknet_artifacts() { let temp = crate::tests::setup_package("basic_package"); ScarbCommand::new_with_stdio() .current_dir(temp.path()) .arg("build") .run() .unwrap(); let artifacts_path = temp .path() .join("target/dev/basic_package.starknet_artifacts.json"); let artifacts_path = Utf8PathBuf::from_path_buf(artifacts_path).unwrap(); let artifacts = artifacts_for_package(&artifacts_path).unwrap(); assert!(!artifacts.contracts.is_empty()); } #[test] fn parsing_starknet_artifacts_on_invalid_file() { let temp = TempDir::new().unwrap(); temp.copy_from("../../", &[".tool-versions"]).unwrap(); let path = temp.child("wrong.json"); path.touch().unwrap(); path.write_str("\"aa\": {}").unwrap(); let artifacts_path = Utf8PathBuf::from_path_buf(path.to_path_buf()).unwrap(); let result = artifacts_for_package(&artifacts_path); let err = result.unwrap_err(); assert!(err.to_string().contains(&format!( "Failed to parse starknet artifacts from path = {artifacts_path:?}." ))); } } ================================================ FILE: crates/scarb-api/src/artifacts.rs ================================================ use anyhow::Result; use crate::artifacts::representation::StarknetArtifactsRepresentation; use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; #[cfg(feature = "cairo-native")] use cairo_native::executor::AotContractExecutor; use camino::{Utf8Path, Utf8PathBuf}; use itertools::Itertools; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use std::collections::HashMap; use std::fs; use universal_sierra_compiler_api::compile_contract_sierra_at_path; pub mod deserialized; mod representation; /// Contains compiled Starknet artifacts #[derive(Debug, Clone)] pub struct StarknetContractArtifacts { /// Compiled sierra code pub sierra: String, /// Compiled casm code pub casm: CasmContractClass, #[cfg(feature = "cairo-native")] /// Optional AOT compiled native executor pub executor: Option, } impl PartialEq for StarknetContractArtifacts { fn eq(&self, other: &Self) -> bool { let eq = self.sierra == other.sierra && self.casm == other.casm; #[cfg(feature = "cairo-native")] let eq = eq && self.executor.is_some() == other.executor.is_some(); eq } } #[derive(PartialEq, Debug)] pub(crate) struct StarknetArtifactsFiles { base: Utf8PathBuf, other: Vec, #[cfg(feature = "cairo-native")] compile_native: bool, } impl StarknetArtifactsFiles { pub(crate) fn new(base_file: Utf8PathBuf, other_files: Vec) -> Self { Self { base: base_file, other: other_files, #[cfg(feature = "cairo-native")] compile_native: false, } } #[cfg(feature = "cairo-native")] pub(crate) fn compile_native(mut self, compile_native: bool) -> Self { self.compile_native = compile_native; self } #[tracing::instrument(skip_all, level = "debug")] pub(crate) fn load_contracts_artifacts( self, ) -> Result> { // TODO(#2626) handle duplicates let mut base_artifacts: HashMap = self .compile_artifacts( StarknetArtifactsRepresentation::try_from_path(self.base.as_path())?.artifacts(), )?; let other_artifact_representations: Vec = self .other .iter() .map(|path| StarknetArtifactsRepresentation::try_from_path(path.as_path())) .collect::>()?; let other_artifacts: Vec<(String, Utf8PathBuf)> = unique_artifacts(other_artifact_representations, &base_artifacts); let compiled_artifacts = self.compile_artifacts(other_artifacts)?; base_artifacts.extend(compiled_artifacts); Ok(base_artifacts) } #[tracing::instrument(skip_all, level = "debug")] fn compile_artifacts( &self, artifacts: Vec<(String, Utf8PathBuf)>, ) -> Result> { artifacts .into_par_iter() .map(|(name, path)| { self.compile_artifact_at_path(&path) .map(|artifact| (name.clone(), (artifact, path))) }) .collect::>() } #[tracing::instrument(skip_all, level = "debug")] #[cfg_attr(not(feature = "cairo-native"), expect(clippy::unused_self))] fn compile_artifact_at_path(&self, path: &Utf8Path) -> Result { let sierra = fs::read_to_string(path)?; let casm = compile_contract_sierra_at_path(path.as_std_path())?; #[cfg(feature = "cairo-native")] let executor = self.compile_to_native(&sierra)?; Ok(StarknetContractArtifacts { sierra, casm, #[cfg(feature = "cairo-native")] executor, }) } #[cfg(feature = "cairo-native")] #[tracing::instrument(skip_all, level = "debug")] fn compile_to_native(&self, sierra: &str) -> Result> { Ok(if self.compile_native { Some(native_api::compile_contract_class(&serde_json::from_str( sierra, )?)) } else { None }) } } #[tracing::instrument(skip_all, level = "debug")] fn unique_artifacts( artifact_representations: Vec, current_artifacts: &HashMap, ) -> Vec<(String, Utf8PathBuf)> { artifact_representations .into_iter() .flat_map(StarknetArtifactsRepresentation::artifacts) .unique_by(|(name, _)| name.clone()) .filter(|(name, _)| !current_artifacts.contains_key(name)) .collect() } #[cfg(test)] mod tests { use super::*; use crate::ScarbCommand; use crate::tests::setup_package; use assert_fs::TempDir; use assert_fs::fixture::{FileWriteStr, PathChild}; use cairo_lang_starknet_classes::casm_contract_class::CasmContractEntryPoints; use camino::Utf8PathBuf; use deserialized::{StarknetArtifacts, StarknetContract, StarknetContractArtifactPaths}; use indoc::indoc; use num_bigint::BigUint; #[test] fn test_unique_artifacts() { // Mock StarknetArtifactsRepresentation let mock_base_artifacts = HashMap::from([( "contract1".to_string(), ( StarknetContractArtifacts { sierra: "sierra1".to_string(), casm: CasmContractClass { prime: BigUint::default(), compiler_version: String::default(), bytecode: vec![], bytecode_segment_lengths: None, hints: vec![], pythonic_hints: None, entry_points_by_type: CasmContractEntryPoints::default(), }, #[cfg(feature = "cairo-native")] executor: None, }, Utf8PathBuf::from("path1"), ), )]); let mock_representation_1 = StarknetArtifactsRepresentation { base_path: Utf8PathBuf::from("mock/path1"), artifacts: StarknetArtifacts { version: 1, contracts: vec![StarknetContract { id: "1".to_string(), package_name: "package1".to_string(), contract_name: "contract1".to_string(), module_path: "package1::contract1".to_string(), artifacts: StarknetContractArtifactPaths { sierra: Utf8PathBuf::from("mock/path1/contract1.sierra"), casm: None, }, }], }, }; let mock_representation_2 = StarknetArtifactsRepresentation { base_path: Utf8PathBuf::from("mock/path2"), artifacts: StarknetArtifacts { version: 1, contracts: vec![StarknetContract { id: "2".to_string(), package_name: "package2".to_string(), contract_name: "contract2".to_string(), module_path: "package2::contract2".to_string(), artifacts: StarknetContractArtifactPaths { sierra: Utf8PathBuf::from("mock/path2/contract2.sierra"), casm: None, }, }], }, }; let representations = vec![mock_representation_1, mock_representation_2]; let result = unique_artifacts(representations, &mock_base_artifacts); assert_eq!(result.len(), 1); assert_eq!(result[0].0, "contract2"); } fn setup() -> (TempDir, StarknetArtifactsFiles) { let temp = setup_package("basic_package"); let tests_dir = temp.join("tests"); fs::create_dir(&tests_dir).unwrap(); temp.child(tests_dir.join("test.cairo")) .write_str(indoc!( r" #[test] fn mock_test() { assert!(true); } " )) .unwrap(); ScarbCommand::new_with_stdio() .current_dir(temp.path()) .arg("build") .arg("--test") .run() .unwrap(); // Define path to the generated artifacts let base_artifacts_path = temp.to_path_buf().join("target").join("dev"); // Get the base artifact let base_file = Utf8PathBuf::from_path_buf( base_artifacts_path.join("basic_package_integrationtest.test.starknet_artifacts.json"), ) .unwrap(); // Load other artifact files and add them to the temporary directory let other_files = vec![ Utf8PathBuf::from_path_buf( base_artifacts_path.join("basic_package_unittest.test.starknet_artifacts.json"), ) .unwrap(), ]; // Create `StarknetArtifactsFiles` let artifacts_files = StarknetArtifactsFiles::new(base_file, other_files); (temp, artifacts_files) } #[test] fn test_load_contracts_artifacts() { let (_temp, artifacts_files) = setup(); // Load the contracts let result = artifacts_files.load_contracts_artifacts().unwrap(); // Assert the Contract Artifacts are loaded. assert!(result.contains_key("ERC20")); assert!(result.contains_key("HelloStarknet")); } #[test] #[cfg(feature = "cairo-native")] fn test_load_contracts_artifacts_native() { let (_temp, artifacts_files) = setup(); let artifacts_files = artifacts_files.compile_native(true); // Load the contracts let result = artifacts_files.load_contracts_artifacts().unwrap(); // Assert the Contract Artifacts are loaded. assert!(result.contains_key("ERC20")); assert!(result.contains_key("HelloStarknet")); assert!(result.get("ERC20").unwrap().0.executor.is_some()); } } ================================================ FILE: crates/scarb-api/src/command.rs ================================================ use scarb_ui::args::{FeaturesSpec, PackagesFilter, ProfileSpec, ToEnvVars}; use std::collections::HashMap; use std::ffi::{OsStr, OsString}; use std::path::PathBuf; use std::process::{Command, Stdio}; use std::{env, io}; use thiserror::Error; /// Error thrown while trying to execute `scarb` command. #[derive(Error, Debug)] #[non_exhaustive] pub enum ScarbCommandError { /// Failed to read `scarb` output. #[error("failed to read `scarb` output")] Io(#[from] io::Error), /// Error during execution of `scarb` command. #[error("`scarb` exited with error")] ScarbError, } /// A builder for `scarb` command invocation. #[derive(Clone, Debug, Default)] #[expect(clippy::struct_excessive_bools)] pub struct ScarbCommand { args: Vec, current_dir: Option, env: HashMap>, inherit_stderr: bool, inherit_stdout: bool, json: bool, offline: bool, manifest_path: Option, } impl ScarbCommand { /// Creates a default `scarb` command, which will look for `scarb` in `$PATH` and /// for `Scarb.toml` in the current directory or its ancestors. #[must_use] pub fn new() -> Self { Self::default() } /// Creates a default `scarb` command, with inherited standard error and standard output. #[must_use] pub fn new_with_stdio() -> Self { let mut cmd = Self::new(); cmd.inherit_stderr(); cmd.inherit_stdout(); cmd } /// Path to `Scarb.toml`. /// /// If not set, this will look for `Scarb.toml` in the current directory or its ancestors. pub fn manifest_path(&mut self, path: impl Into) -> &mut Self { self.manifest_path = Some(path.into()); self } /// Pass packages filter to `scarb` call. pub fn packages_filter(&mut self, filter: PackagesFilter) -> &mut Self { self.envs(filter.to_env_vars()); self } /// Pass features specification filter to `scarb` call. pub fn features(&mut self, features: FeaturesSpec) -> &mut Self { self.envs(features.to_env_vars()); self } /// Pass profile to `scarb` call. pub fn profile(&mut self, profile: ProfileSpec) -> &mut Self { self.envs(profile.to_env_vars()); self } /// Current directory of the `scarb` process. pub fn current_dir(&mut self, path: impl Into) -> &mut Self { self.current_dir = Some(path.into()); self } /// Adds an argument to pass to `scarb`. pub fn arg>(&mut self, arg: S) -> &mut Self { self.args.push(arg.as_ref().to_os_string()); self } /// Adds multiple arguments to pass to `scarb`. pub fn args(&mut self, args: I) -> &mut Self where I: IntoIterator, S: AsRef, { self.args .extend(args.into_iter().map(|s| s.as_ref().to_os_string())); self } /// Inserts or updates an environment variable mapping. pub fn env(&mut self, key: impl AsRef, val: impl AsRef) -> &mut Self { self.env.insert( key.as_ref().to_os_string(), Some(val.as_ref().to_os_string()), ); self } /// Adds or updates multiple environment variable mappings. pub fn envs(&mut self, vars: I) -> &mut Self where I: IntoIterator, K: AsRef, V: AsRef, { for (ref key, ref val) in vars { self.env(key, val); } self } /// Removes an environment variable mapping. pub fn env_remove(&mut self, key: impl AsRef) -> &mut Self { let key = key.as_ref(); self.env.insert(key.to_os_string(), None); self } /// Inherit standard error, i.e. show Scarb errors in this process's standard error. pub fn inherit_stderr(&mut self) -> &mut Self { self.inherit_stderr = true; self } /// Inherit standard output, i.e. show Scarb output in this process's standard output. pub fn inherit_stdout(&mut self) -> &mut Self { self.inherit_stdout = true; self } /// Set output format to JSON. pub fn json(&mut self) -> &mut Self { self.json = true; self } /// Enables offline mode. pub fn offline(&mut self) -> &mut Self { self.offline = true; self } /// Build executable `scarb` command. #[must_use] pub fn command(&self) -> Command { let scarb = binary_path(); let mut cmd = Command::new(scarb); if self.json { cmd.arg("--json"); } if self.offline { cmd.arg("--offline"); } if let Some(manifest_path) = &self.manifest_path { cmd.arg("--manifest-path").arg(manifest_path); } cmd.args(&self.args); if let Some(path) = &self.current_dir { cmd.current_dir(path); } for (key, val) in &self.env { if let Some(val) = val { cmd.env(key, val); } else { cmd.env_remove(key); } } if self.inherit_stderr { cmd.stderr(Stdio::inherit()); } if self.inherit_stdout { cmd.stdout(Stdio::inherit()); } cmd } /// Runs configured `scarb` command. pub fn run(&self) -> Result<(), ScarbCommandError> { let mut cmd = self.command(); if cmd.status()?.success() { Ok(()) } else { Err(ScarbCommandError::ScarbError) } } } #[derive(Error, Debug)] pub enum ScarbUnavailableError { #[error( "Cannot find `scarb` binary. Make sure you have Scarb installed https://github.com/software-mansion/scarb" )] NotFound(which::Error), } pub fn ensure_scarb_available() -> anyhow::Result<(), ScarbUnavailableError> { which::which(binary_path()) .map(|_| ()) .map_err(ScarbUnavailableError::NotFound) } fn binary_path() -> PathBuf { env::var("SCARB") .map(PathBuf::from) .ok() .unwrap_or_else(|| PathBuf::from("scarb")) } ================================================ FILE: crates/scarb-api/src/lib.rs ================================================ use crate::artifacts::StarknetArtifactsFiles; use anyhow::{Context, Result, anyhow}; use camino::{Utf8Path, Utf8PathBuf}; pub use command::*; use foundry_ui::UI; use foundry_ui::components::warning::WarningMessage; use scarb_metadata::{Metadata, PackageId, PackageMetadata, TargetMetadata}; use scarb_ui::args::PackagesFilter; use semver::{BuildMetadata, Prerelease, Version, VersionReq}; use std::collections::HashMap; use std::str::FromStr; pub mod artifacts; mod command; pub mod manifest; pub mod metadata; pub mod version; pub use crate::artifacts::StarknetContractArtifacts; const INTEGRATION_TEST_TYPE: &str = "integration"; /// Constructs `StarknetArtifactsFiles` from contracts built using test target. /// /// If artifacts with `test_type` of `INTEGRATION_TEST_TYPE` are present, we use them base path /// and extend with paths to other artifacts. /// If `INTEGRATION_TEST_TYPE` is not present, we take first artifacts found. #[tracing::instrument(skip_all, level = "debug")] fn get_starknet_artifacts_paths_from_test_targets( target_dir: &Utf8Path, test_targets: &HashMap, ) -> Option { let artifact = |name: &str, metadata: &TargetMetadata| -> Option<(Utf8PathBuf, Option)> { let path = format!("{name}.test.starknet_artifacts.json"); let path = target_dir.join(&path); let path = path.exists().then_some(path); let test_type = metadata .params .get("test-type") .and_then(|value| value.as_str()) .map(ToString::to_string); path.map(|path| (Utf8PathBuf::from_str(path.as_str()).unwrap(), test_type)) }; let artifacts = test_targets .iter() .filter_map(|(target_name, metadata)| artifact(target_name, metadata)) .collect::>(); let base_artifact_path = artifacts .iter() .find(|(_, test_type)| test_type.as_deref() == Some(INTEGRATION_TEST_TYPE)) .cloned() .or_else(|| artifacts.first().cloned()); if let Some(base_artifact) = base_artifact_path { let other_artifacts_paths = artifacts .into_iter() .filter(|artifact| artifact != &base_artifact) .map(|(path, _)| path) .collect(); let (base_artifact_path, _) = base_artifact; Some(StarknetArtifactsFiles::new( base_artifact_path, other_artifacts_paths, )) } else { None } } /// Try getting the path to `starknet_artifacts.json` related to `starknet-contract` target. This file that is generated by `scarb build` command. /// If the file is not present, `None` is returned. #[tracing::instrument(skip_all, level = "debug")] fn get_starknet_artifacts_path( target_dir: &Utf8Path, target_name: &str, ui: &UI, ) -> Option { let path = format!("{target_name}.starknet_artifacts.json"); let path = target_dir.join(&path); let path = if path.exists() { Some(path) } else { ui.println(&WarningMessage::new(&format!( "File = {path} missing. \ This is most likely caused by `[[target.starknet-contract]]` being undefined in Scarb.toml \ No contracts will be available for deployment" ))); None }; path.map(|path| StarknetArtifactsFiles::new(path, vec![])) } #[derive(Default)] pub struct CompilationOpts { pub use_test_target_contracts: bool, #[cfg(feature = "cairo-native")] pub run_native: bool, } /// Get the map with `StarknetContractArtifacts` for the given package #[tracing::instrument(skip_all, level = "debug")] pub fn get_contracts_artifacts_and_source_sierra_paths( artifacts_dir: &Utf8Path, package: &PackageMetadata, ui: &UI, CompilationOpts { use_test_target_contracts, #[cfg(feature = "cairo-native")] run_native, }: CompilationOpts, ) -> Result> { let starknet_artifact_files = if use_test_target_contracts { let test_targets = test_targets_by_name(package); get_starknet_artifacts_paths_from_test_targets(artifacts_dir, &test_targets) } else { let starknet_target_name = package .targets .iter() .find(|target| target.kind == "starknet-contract") .map(|target| target.name.clone()); starknet_target_name.and_then(|starknet_target_name| { get_starknet_artifacts_path(artifacts_dir, starknet_target_name.as_str(), ui) }) }; if let Some(starknet_artifact_files) = starknet_artifact_files { #[cfg(feature = "cairo-native")] let starknet_artifact_files = starknet_artifact_files.compile_native(run_native); starknet_artifact_files.load_contracts_artifacts() } else { Ok(HashMap::default()) } } #[must_use] pub fn target_dir_for_workspace(metadata: &Metadata) -> Utf8PathBuf { metadata .target_dir .clone() .unwrap_or_else(|| metadata.workspace.root.join("target")) } /// Get a name of the given package pub fn name_for_package(metadata: &Metadata, package: &PackageId) -> Result { let package = metadata .get_package(package) .ok_or_else(|| anyhow!("Failed to find metadata for package = {package}"))?; Ok(package.name.clone()) } pub fn packages_from_filter( metadata: &Metadata, packages_filter: &PackagesFilter, ) -> Result> { packages_filter .match_many(metadata) .context("Failed to find any packages matching the specified filter") } fn matches_version_with_special_rules( package_name: &str, package_version: &Version, version_req: &VersionReq, ) -> bool { if package_name == "snforge_std" { let normalized_version = Version { major: package_version.major, minor: package_version.minor, patch: package_version.patch, // Clear pre-release and build metadata to handle exceptions in nightly builds and smoke tests pre: Prerelease::EMPTY, build: BuildMetadata::EMPTY, }; version_req.matches(&normalized_version) } else { version_req.matches(package_version) } } /// Checks if the specified package has version compatible with the specified requirement pub fn package_matches_version_requirement( metadata: &Metadata, name: &str, version_req: &VersionReq, ) -> Result { let mut packages = metadata .packages .iter() .filter(|package| package.name == name); match (packages.next(), packages.next()) { (Some(package), None) => Ok(matches_version_with_special_rules( name, &package.version, version_req, )), (None, None) => Err(anyhow!("Package {name} is not present in dependencies.")), _ => Err(anyhow!("Package {name} is duplicated in dependencies")), } } /// collecting by name allow us to dedup targets /// we do it because they use same sierra and we display them without distinction anyway #[must_use] pub fn test_targets_by_name(package: &PackageMetadata) -> HashMap { fn test_target_name(target: &TargetMetadata) -> String { // this is logic copied from scarb: https://github.com/software-mansion/scarb/blob/90ab01cb6deee48210affc2ec1dc94d540ab4aea/extensions/scarb-cairo-test/src/main.rs#L115 target .params .get("group-id") // by unit tests grouping .and_then(|v| v.as_str()) .map(ToString::to_string) .unwrap_or(target.name.clone()) // else by integration test name } package .targets .iter() .filter(|target| target.kind == "test") .map(|target| (test_target_name(target), target)) .collect() } #[cfg(test)] mod tests { use super::*; use crate::metadata::metadata_for_dir; use assert_fs::TempDir; use assert_fs::fixture::{FileWriteStr, PathChild, PathCopy}; use camino::Utf8PathBuf; use indoc::{formatdoc, indoc}; use std::fs; use std::str::FromStr; pub(crate) fn setup_package(package_name: &str) -> TempDir { let temp = TempDir::new().unwrap(); temp.copy_from( format!("tests/data/{package_name}"), &["**/*.cairo", "**/*.toml"], ) .unwrap(); temp.copy_from("../../", &[".tool-versions"]).unwrap(); let snforge_std_path = Utf8PathBuf::from_str("../../snforge_std") .unwrap() .canonicalize_utf8() .unwrap() .to_string() .replace('\\', "/"); let manifest_path = temp.child("Scarb.toml"); manifest_path .write_str(&formatdoc!( r#" [package] name = "{}" version = "0.1.0" [dependencies] starknet = "2.4.0" snforge_std = {{ path = "{}" }} [[target.starknet-contract]] [[tool.snforge.fork]] name = "FIRST_FORK_NAME" url = "http://some.rpc.url" block_id.number = "1" [[tool.snforge.fork]] name = "SECOND_FORK_NAME" url = "http://some.rpc.url" block_id.hash = "1" [[tool.snforge.fork]] name = "THIRD_FORK_NAME" url = "http://some.rpc.url" block_id.tag = "latest" "#, package_name, snforge_std_path )) .unwrap(); temp } #[test] fn get_starknet_artifacts_path_for_standard_build() { let temp = setup_package("basic_package"); ScarbCommand::new_with_stdio() .current_dir(temp.path()) .arg("build") .run() .unwrap(); let ui = UI::default(); let path = get_starknet_artifacts_path( &Utf8PathBuf::from_path_buf(temp.to_path_buf().join("target").join("dev")).unwrap(), "basic_package", &ui, ) .unwrap(); assert_eq!( path, StarknetArtifactsFiles::new( Utf8PathBuf::from_path_buf( temp.path() .join("target/dev/basic_package.starknet_artifacts.json") ) .unwrap(), vec![] ) ); } #[test] fn get_starknet_artifacts_path_for_test_build() { let temp = setup_package("basic_package"); ScarbCommand::new_with_stdio() .current_dir(temp.path()) .arg("build") .arg("--test") .run() .unwrap(); let metadata = metadata_for_dir(temp.path()).unwrap(); let package = metadata .packages .iter() .find(|p| p.name == "basic_package") .unwrap(); let path = get_starknet_artifacts_paths_from_test_targets( &Utf8PathBuf::from_path_buf(temp.join("target").join("dev")).unwrap(), &test_targets_by_name(package), ) .unwrap(); assert_eq!( path, StarknetArtifactsFiles::new( Utf8PathBuf::from_path_buf( temp.path() .join("target/dev/basic_package_unittest.test.starknet_artifacts.json") ) .unwrap(), vec![], ) ); } #[test] fn get_starknet_artifacts_path_for_test_build_when_integration_tests_exist() { let temp = setup_package("basic_package"); let tests_dir = temp.join("tests"); fs::create_dir(&tests_dir).unwrap(); temp.child(tests_dir.join("test.cairo")) .write_str(indoc!( r" #[test] fn mock_test() { assert!(true); } " )) .unwrap(); ScarbCommand::new_with_stdio() .current_dir(temp.path()) .arg("build") .arg("--test") .run() .unwrap(); let metadata = metadata_for_dir(temp.path()).unwrap(); let package = metadata .packages .iter() .find(|p| p.name == "basic_package") .unwrap(); let path = get_starknet_artifacts_paths_from_test_targets( &Utf8PathBuf::from_path_buf(temp.to_path_buf().join("target").join("dev")).unwrap(), &test_targets_by_name(package), ) .unwrap(); assert_eq!( path, StarknetArtifactsFiles::new( Utf8PathBuf::from_path_buf( temp.path().join( "target/dev/basic_package_integrationtest.test.starknet_artifacts.json" ) ) .unwrap(), vec![ Utf8PathBuf::from_path_buf( temp.path() .join("target/dev/basic_package_unittest.test.starknet_artifacts.json") ) .unwrap(), ] ), ); } #[test] fn package_matches_version_requirement_test() { let temp = setup_package("basic_package"); let manifest_path = temp.child("Scarb.toml"); manifest_path .write_str(&formatdoc!( r#" [package] name = "version_checker" version = "0.1.0" [[target.starknet-contract]] sierra = true [dependencies] starknet = "2.9.4" "#, )) .unwrap(); let scarb_metadata = metadata_for_dir(temp.path()).unwrap(); assert!( package_matches_version_requirement( &scarb_metadata, "starknet", &VersionReq::parse("2.12.0").unwrap(), ) .unwrap() ); assert!( package_matches_version_requirement( &scarb_metadata, "not_existing", &VersionReq::parse("2.5").unwrap(), ) .is_err() ); assert!( !package_matches_version_requirement( &scarb_metadata, "starknet", &VersionReq::parse("2.20").unwrap(), ) .unwrap() ); } #[test] fn get_starknet_artifacts_path_for_project_with_different_package_and_target_name() { let temp = setup_package("basic_package"); let snforge_std_path = Utf8PathBuf::from_str("../../snforge_std") .unwrap() .canonicalize_utf8() .unwrap() .to_string() .replace('\\', "/"); let scarb_path = temp.child("Scarb.toml"); scarb_path .write_str(&formatdoc!( r#" [package] name = "basic_package" version = "0.1.0" [dependencies] starknet = "2.4.0" snforge_std = {{ path = "{}" }} [[target.starknet-contract]] name = "essa" sierra = true "#, snforge_std_path )) .unwrap(); ScarbCommand::new_with_stdio() .current_dir(temp.path()) .arg("build") .run() .unwrap(); let ui = UI::default(); let path = get_starknet_artifacts_path( &Utf8PathBuf::from_path_buf(temp.to_path_buf().join("target").join("dev")).unwrap(), "essa", &ui, ) .unwrap(); assert_eq!( path, StarknetArtifactsFiles::new( Utf8PathBuf::from_path_buf( temp.path().join("target/dev/essa.starknet_artifacts.json") ) .unwrap(), vec![] ) ); } #[test] fn get_starknet_artifacts_path_for_project_without_starknet_target() { let temp = setup_package("empty_lib"); let manifest_path = temp.child("Scarb.toml"); manifest_path .write_str(indoc!( r#" [package] name = "empty_lib" version = "0.1.0" "#, )) .unwrap(); ScarbCommand::new_with_stdio() .current_dir(temp.path()) .arg("build") .run() .unwrap(); let ui = UI::default(); let path = get_starknet_artifacts_path( &Utf8PathBuf::from_path_buf(temp.to_path_buf().join("target").join("dev")).unwrap(), "empty_lib", &ui, ); assert!(path.is_none()); } #[test] fn get_starknet_artifacts_path_for_project_without_scarb_build() { let temp = setup_package("basic_package"); let ui = UI::default(); let path = get_starknet_artifacts_path( &Utf8PathBuf::from_path_buf(temp.to_path_buf().join("target").join("dev")).unwrap(), "basic_package", &ui, ); assert!(path.is_none()); } #[test] fn get_contracts() { let temp = setup_package("basic_package"); ScarbCommand::new_with_stdio() .current_dir(temp.path()) .arg("build") .run() .unwrap(); let metadata = metadata_for_dir(temp.path()).unwrap(); let target_dir = target_dir_for_workspace(&metadata).join("dev"); let package = metadata.packages.first().unwrap(); let ui = UI::default(); let contracts = get_contracts_artifacts_and_source_sierra_paths( target_dir.as_path(), package, &ui, CompilationOpts { use_test_target_contracts: false, #[cfg(feature = "cairo-native")] run_native: true, }, ) .unwrap(); assert!(contracts.contains_key("ERC20")); assert!(contracts.contains_key("HelloStarknet")); let sierra_contents_erc20 = fs::read_to_string(temp.join("target/dev/basic_package_ERC20.contract_class.json")) .unwrap(); let contract = contracts.get("ERC20").unwrap(); assert_eq!(&sierra_contents_erc20, &contract.0.sierra); let sierra_contents_erc20 = fs::read_to_string( temp.join("target/dev/basic_package_HelloStarknet.contract_class.json"), ) .unwrap(); let contract = contracts.get("HelloStarknet").unwrap(); assert_eq!(&sierra_contents_erc20, &contract.0.sierra); } #[test] fn get_name_for_package() { let temp = setup_package("basic_package"); let scarb_metadata = metadata_for_dir(temp.path()).unwrap(); let package_name = name_for_package(&scarb_metadata, &scarb_metadata.workspace.members[0]).unwrap(); assert_eq!(&package_name, "basic_package"); } } ================================================ FILE: crates/scarb-api/src/manifest.rs ================================================ use anyhow::{Context, Result}; use camino::{Utf8Path, Utf8PathBuf}; use std::fs; use toml_edit::{DocumentMut, Item, value}; pub struct ManifestEditor { path: Utf8PathBuf, } impl ManifestEditor { #[must_use] pub fn new(manifest_path: &Utf8Path) -> Self { Self { path: manifest_path.to_owned(), } } pub fn set_inlining_strategy(&self, threshold: u32, profile: &str) -> Result<()> { let content = fs::read_to_string(&self.path)?; let mut doc = content .parse::() .context("Failed to parse Scarb.toml")?; let profile_table = doc .entry("profile") .or_insert(toml_edit::Item::Table(toml_edit::Table::new())) .as_table_mut() .context("Invalid profile section")?; let profile_entry = profile_table .entry(profile) .or_insert(toml_edit::Item::Table(toml_edit::Table::new())) .as_table_mut() .context("Invalid profile entry")?; let cairo_table = profile_entry .entry("cairo") .or_insert(toml_edit::Item::Table(toml_edit::Table::new())) .as_table_mut() .context("Invalid cairo section")?; cairo_table["inlining-strategy"] = value(i64::from(threshold)); fs::write(&self.path, doc.to_string())?; Ok(()) } } pub fn overwrite_starknet_contract_target_flags(doc: &mut DocumentMut) -> bool { let Some(target_item) = doc.as_table_mut().get_mut("target") else { return false; }; let Item::Table(target_table) = target_item else { return false; }; let Some(starknet_contract_item) = target_table.get_mut("starknet-contract") else { return false; }; let Item::ArrayOfTables(starknet_contract_tables) = starknet_contract_item else { return false; }; let keys = ["casm", "sierra"]; let mut changed = false; for target in starknet_contract_tables.iter_mut() { for key in keys { if target.get(key).and_then(Item::as_bool) != Some(true) { target[key] = value(true); changed = true; } } } changed } ================================================ FILE: crates/scarb-api/src/metadata.rs ================================================ //! Functionality for fetching `scarb` metadata. //! //! # Note: //! To allow more flexibility when changing internals, please make public as few items as possible //! and try using more general functions like `metadata` and `metadata_for_dir` //! instead of `metadata_with_opts`. use crate::{ScarbUnavailableError, ensure_scarb_available}; use anyhow::Result; pub use scarb_metadata::{Metadata, MetadataCommand, MetadataCommandError, PackageMetadata}; use std::path::PathBuf; /// Errors that can occur when fetching `scarb` metadata. #[derive(thiserror::Error, Debug)] pub enum MetadataError { #[error(transparent)] ScarbNotFound(#[from] ScarbUnavailableError), #[error(transparent)] ScarbExecutionFailed(#[from] MetadataCommandError), } /// Options for fetching `scarb` metadata. #[derive(Default)] pub struct MetadataOpts { pub current_dir: Option, pub no_deps: bool, pub profile: Option, } /// Fetches `scarb` metadata for a specific directory. pub fn metadata_for_dir(dir: impl Into) -> Result { metadata_with_opts(MetadataOpts { current_dir: Some(dir.into()), ..MetadataOpts::default() }) } /// Fetches `scarb` metadata for the current directory. pub fn metadata() -> Result { metadata_with_opts(MetadataOpts::default()) } /// Fetches `scarb` metadata with specified options. pub fn metadata_with_opts( MetadataOpts { current_dir, no_deps, profile, }: MetadataOpts, ) -> Result { ensure_scarb_available()?; let mut command = MetadataCommand::new(); if let Some(dir) = current_dir { command.current_dir(dir); } if let Some(profile) = profile { command.profile(profile); } if no_deps { command.no_deps(); } command .inherit_stderr() .inherit_stdout() .exec() .map_err(MetadataError::ScarbExecutionFailed) } ================================================ FILE: crates/scarb-api/src/version.rs ================================================ //! Functionality for fetching and parsing `scarb` version information. use crate::{ScarbCommand, ScarbUnavailableError, ensure_scarb_available}; use regex::Regex; use semver::Version; use shared::command::{CommandError, CommandExt}; use std::collections::HashMap; use std::path::Path; use std::process::Output; use std::sync::LazyLock; use thiserror::Error; #[derive(Debug)] pub struct ScarbVersionOutput { pub scarb: Version, pub cairo: Version, pub sierra: Version, } /// Errors that can occur when fetching `scarb` version information. #[derive(Error, Debug)] pub enum VersionError { #[error(transparent)] ScarbNotFound(#[from] ScarbUnavailableError), #[error("Failed to execute `scarb --version`: {0}")] CommandExecutionError(#[from] CommandError), #[error("Could not parse version for tool `{0}`: {1}")] VersionParseError(String, #[source] semver::Error), #[error("Missing version entry for `{0}`")] MissingToolVersion(String), } /// Fetches `scarb` version information for the current directory. pub fn scarb_version() -> Result { scarb_version_internal(None) } /// Fetches `scarb` version information for a specific directory. pub fn scarb_version_for_dir(dir: impl AsRef) -> Result { scarb_version_internal(Some(dir.as_ref())) } /// Internal function to fetch `scarb` version information. fn scarb_version_internal(dir: Option<&Path>) -> Result { let output = run_version_command(dir)?; parse_version_output(&output) } /// Runs the `scarb --version` command in the specified directory. fn run_version_command(dir: Option<&Path>) -> Result { ensure_scarb_available()?; let mut scarb_version_command = ScarbCommand::new().arg("--version").command(); if let Some(path) = dir { scarb_version_command.current_dir(path); } Ok(scarb_version_command.output_checked()?) } /// Parses the output of the `scarb --version` command. fn parse_version_output(output: &Output) -> Result { let output_str = str::from_utf8(&output.stdout).expect("valid UTF-8 from scarb"); let mut versions = extract_versions(output_str)?; let mut get_version = |tool: &str| { versions .remove(tool) .ok_or_else(|| VersionError::MissingToolVersion(tool.into())) }; Ok(ScarbVersionOutput { scarb: get_version("scarb")?, cairo: get_version("cairo")?, sierra: get_version("sierra")?, }) } /// Extracts tool versions from the version output string. fn extract_versions(version_output: &str) -> Result, VersionError> { // https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string static VERSION_REGEX: LazyLock = LazyLock::new(|| { Regex::new(r"(?P[\w-]+):?\s(?P\d+\.\d+\.\d+(?:-[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*)?(?:\+[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*)?)").expect("this should be a valid regex") }); VERSION_REGEX .captures_iter(version_output) .map(|cap| { let tool = cap .name("tool") .expect("regex ensures tool name exists") .as_str() .to_string(); let ver_str = cap .name("ver") .expect("regex ensures version string exists") .as_str(); Version::parse(ver_str) .map_err(|e| VersionError::VersionParseError(tool.clone(), e)) .map(|version| (tool, version)) }) .collect() } #[cfg(test)] mod tests { use super::*; use std::process::{ExitStatus, Output}; #[test] fn test_happy_case() { scarb_version().unwrap(); } #[test] fn test_extract_versions_basic_output() { let version_output = "scarb 0.2.1\ncairo 1.1.0\nsierra 1.0.0"; let map = extract_versions(version_output).unwrap(); assert_eq!(map["scarb"], Version::parse("0.2.1").unwrap()); assert_eq!(map["cairo"], Version::parse("1.1.0").unwrap()); assert_eq!(map["sierra"], Version::parse("1.0.0").unwrap()); } #[test] fn test_extract_versions_with_colons_and_prerelease() { let version_output = "scarb: 0.2.1-alpha.1\ncairo: 1.1.0+meta\nsierra: 1.0.0-beta+exp.build"; let map = extract_versions(version_output).unwrap(); assert_eq!(map["scarb"], Version::parse("0.2.1-alpha.1").unwrap()); assert_eq!(map["cairo"], Version::parse("1.1.0+meta").unwrap()); assert_eq!( map["sierra"], Version::parse("1.0.0-beta+exp.build").unwrap() ); } #[test] fn test_missing_tool_versions() { let version_output = "scarb 0.2.1\ncairo 1.1.0"; // Missing sierra let output = Output { status: ExitStatus::default(), stdout: version_output.as_bytes().to_vec(), stderr: vec![], }; let result = parse_version_output(&output); assert!( matches!(result, Err(VersionError::MissingToolVersion(ref tool)) if tool == "sierra") ); } #[test] fn test_extract_versions_extra_tools() { let version_output = "scarb 0.2.1\ncairo 1.1.0\nsierra 1.0.0\nextra-tool: 2.3.4"; let map = extract_versions(version_output).unwrap(); assert_eq!(map["extra-tool"], Version::parse("2.3.4").unwrap()); } } ================================================ FILE: crates/scarb-api/tests/data/basic_package/Scarb.toml ================================================ [package] name = "basic_package" version = "0.1.0" edition = "2024_07" [dependencies] starknet = "2.8.5" snforge_std = { path = "../../../../../snforge_std" } [[target.starknet-contract]] ================================================ FILE: crates/scarb-api/tests/data/basic_package/src/lib.cairo ================================================ #[starknet::contract] mod HelloStarknet { #[storage] struct Storage { balance: felt252, } } #[starknet::contract] mod ERC20 { #[storage] struct Storage { balance: felt252, } } ================================================ FILE: crates/scarb-api/tests/data/empty_lib/Scarb.toml ================================================ [package] name = "empty_lib" version = "0.1.0" edition = "2024_07" ================================================ FILE: crates/scarb-api/tests/data/empty_lib/src/lib.cairo ================================================ ================================================ FILE: crates/shared/Cargo.toml ================================================ [package] name = "shared" version = "0.1.0" edition.workspace = true [dependencies] starknet_api.workspace = true anyhow.workspace = true starknet-types-core.workspace = true console.workspace = true semver.workspace = true starknet-rust.workspace = true url.workspace = true regex.workspace = true snapbox.workspace = true indicatif.workspace = true clap.workspace = true clap_complete.workspace = true cairo-vm.workspace = true num-traits.workspace = true foundry-ui ={ path = "../foundry-ui" } thiserror = "2.0.17" [features] testing = [] ================================================ FILE: crates/shared/src/auto_completions.rs ================================================ use anyhow::Result; use clap::{Args, Command}; use clap_complete::{Generator, Shell}; use std::io; #[derive(Args, Debug)] pub struct Completions { pub shell: Option, } pub fn generate_completions_to_stdout(shell: G, cmd: &mut Command) { clap_complete::generate(shell, cmd, cmd.get_name().to_string(), &mut io::stdout()); } // TODO(#3960) JSON output support pub fn generate_completions(shell: Option, cmd: &mut Command) -> Result<()> { let Some(shell) = shell.or_else(Shell::from_env) else { anyhow::bail!("Unsupported shell") }; generate_completions_to_stdout(shell, cmd); Ok(()) } ================================================ FILE: crates/shared/src/command.rs ================================================ use std::io; use std::process::{Command, ExitStatus, Output}; use thiserror::Error; /// Error type for command execution failures #[derive(Debug, Error)] pub enum CommandError { #[error("Failed to run {command}: {source}")] IoError { command: String, #[source] source: io::Error, }, #[error("Command {command} failed with status {status}")] FailedStatus { command: String, status: ExitStatus }, } /// Trait extension for Command to check output status pub trait CommandExt { fn output_checked(&mut self) -> Result; } impl CommandExt for Command { fn output_checked(&mut self) -> Result { let command = self.get_program().to_string_lossy().to_string(); match self.output() { Ok(output) => { if output.status.success() { Ok(output) } else { Err(CommandError::FailedStatus { command, status: output.status, }) } } Err(source) => Err(CommandError::IoError { command, source }), } } } ================================================ FILE: crates/shared/src/consts.rs ================================================ pub const EXPECTED_RPC_VERSION: &str = "0.10.0"; pub const RPC_URL_VERSION: &str = "v0_10"; pub const SNFORGE_TEST_FILTER: &str = "SNFORGE_TEST_FILTER"; pub const FREE_RPC_PROVIDER_URL: &str = "https://api.zan.top/public/starknet-sepolia/rpc/v0_10"; ================================================ FILE: crates/shared/src/lib.rs ================================================ use crate::consts::EXPECTED_RPC_VERSION; use crate::rpc::{get_rpc_version, is_expected_version}; use anyhow::Result; use foundry_ui::UI; use foundry_ui::components::warning::WarningMessage; use starknet_rust::providers::JsonRpcClient; use starknet_rust::providers::jsonrpc::HttpTransport; use std::fmt::Display; pub mod auto_completions; pub mod command; pub mod consts; pub mod rpc; pub mod spinner; pub mod test_utils; pub mod utils; pub mod vm; pub async fn verify_and_warn_if_incompatible_rpc_version( client: &JsonRpcClient, url: impl Display, ui: &UI, ) -> Result<()> { let node_spec_version = get_rpc_version(client).await?; if !is_expected_version(&node_spec_version) { ui.println(&WarningMessage::new(&format!( "RPC node with the url {url} uses incompatible version {node_spec_version}. Expected version: {EXPECTED_RPC_VERSION}"))); } Ok(()) } ================================================ FILE: crates/shared/src/rpc.rs ================================================ use crate::consts::EXPECTED_RPC_VERSION; use anyhow::{Context, Result}; use semver::{Version, VersionReq}; use starknet_rust::core::types::{BlockId, BlockTag}; use starknet_rust::providers::jsonrpc::HttpTransport; use starknet_rust::providers::{JsonRpcClient, Provider}; use std::str::FromStr; use url::Url; pub fn create_rpc_client(url: &Url) -> Result> { let client = JsonRpcClient::new(HttpTransport::new(url.clone())); Ok(client) } #[must_use] pub fn is_expected_version(version: &Version) -> bool { VersionReq::from_str(EXPECTED_RPC_VERSION) .expect("Failed to parse the expected RPC version") .matches(version) } pub async fn get_rpc_version(client: &JsonRpcClient) -> Result { client .spec_version() .await .context("Error while calling RPC method spec_version")? .parse::() .context("Failed to parse RPC spec version") } pub async fn get_starknet_version(client: &JsonRpcClient) -> Result { client .starknet_version(BlockId::Tag(BlockTag::PreConfirmed)) .await .context("Error while getting Starknet version from the RPC provider") } ================================================ FILE: crates/shared/src/spinner.rs ================================================ use indicatif::{ProgressBar, ProgressStyle}; use std::borrow::Cow; use std::time::Duration; /// Styled spinner that uses [`ProgressBar`]. /// Automatically finishes and clears itself when dropped. pub struct Spinner(ProgressBar); impl Spinner { /// Create [`Spinner`] with a message. pub fn create_with_message(message: impl Into>) -> Self { let spinner = ProgressBar::new_spinner(); let style = ProgressStyle::with_template("\n{spinner} {msg}\n") .expect("template is static str and should be valid"); spinner.set_style(style); spinner.enable_steady_tick(Duration::from_millis(100)); spinner.set_message(message); Self(spinner) } } impl Drop for Spinner { fn drop(&mut self) { self.0.finish_and_clear(); } } ================================================ FILE: crates/shared/src/test_utils/mod.rs ================================================ pub mod node_url; pub mod output_assert; ================================================ FILE: crates/shared/src/test_utils/node_url.rs ================================================ use url::Url; /// #### Note: /// - `node_rpc_url()` -> /// - `node_url()` -> #[must_use] pub fn node_rpc_url() -> Url { Url::parse("http://188.34.188.184:7070/rpc/v0_10").expect("Failed to parse the sepolia RPC URL") } /// returning URL with no slug (`rpc/v0_7` suffix). #[must_use] pub fn node_url() -> Url { let mut node_url = node_rpc_url(); node_url.set_path(""); node_url.set_query(None); node_url } ================================================ FILE: crates/shared/src/test_utils/output_assert.rs ================================================ use regex::Regex; use snapbox::cmd::OutputAssert; use std::fmt::Write as _; pub trait AsOutput { fn as_stdout(&self) -> &str; fn as_stderr(&self) -> &str; } impl AsOutput for OutputAssert { fn as_stdout(&self) -> &str { std::str::from_utf8(&self.get_output().stdout).unwrap() } fn as_stderr(&self) -> &str { std::str::from_utf8(&self.get_output().stderr).unwrap() } } impl AsOutput for String { fn as_stdout(&self) -> &str { self } fn as_stderr(&self) -> &str { self } } fn find_with_wildcard(line: &str, actual: &[&str]) -> Option { let escaped = regex::escape(line); let replaced = escaped.replace("\\[\\.\\.\\]", ".*"); let wrapped = format!("^{replaced}$"); let re = Regex::new(wrapped.as_str()).unwrap(); actual.iter().position(|other| re.is_match(other)) } fn is_present(line: &str, actual: &mut Vec<&str>) -> bool { let position = find_with_wildcard(line, actual); if let Some(position) = position { actual.remove(position); return true; } false } fn assert_output_contains(output: &str, lines: &str, assert_prefix: Option) { let asserted_lines = lines.lines(); let mut actual_lines: Vec<&str> = output.lines().collect(); let mut contains = true; let mut out = String::new(); for line in asserted_lines { if is_present(line, &mut actual_lines) { writeln!(out, "| {line}").unwrap(); } else { contains = false; writeln!(out, "- {line}").unwrap(); } } if !contains { for line in &actual_lines { writeln!(out, "+ {line}").unwrap(); } let full_output = output; if let Some(assert_prefix) = assert_prefix { panic!( "{assert_prefix} Output does not match.\n\n\ --- FULL OUTPUT ---\n\ {full_output}\n\n\ --- DIFF ---\n\ {out}" ); } else { panic!( "Output does not match.\n\n\ --- FULL OUTPUT ---\n\ {full_output}\n\n\ --- DIFF ---\n\ {out}" ); } } } #[expect(clippy::needless_pass_by_value)] pub fn case_assert_stdout_contains(case: String, output: impl AsOutput, lines: impl AsRef) { let stdout = output.as_stdout(); assert_output_contains(stdout, lines.as_ref(), Some(format!("Case {case}: "))); } #[expect(clippy::needless_pass_by_value)] pub fn assert_stdout_contains(output: impl AsOutput, lines: impl AsRef) { let stdout = output.as_stdout(); assert_output_contains(stdout, lines.as_ref(), None); } #[expect(clippy::needless_pass_by_value)] pub fn assert_stderr_contains(output: impl AsOutput, lines: impl AsRef) { let stderr = output.as_stderr(); assert_output_contains(stderr, lines.as_ref(), None); } fn assert_output(output: &str, lines: &str) { let converted_pattern = regex::escape(lines).replace(r"\[\.\.\]", ".*"); let re = Regex::new(&converted_pattern).unwrap(); assert!( re.is_match(output), "Pattern not found in output. Expected pattern:\n{lines}\n\nGot:\n{output}", ); } #[expect(clippy::needless_pass_by_value)] pub fn assert_stdout(output: impl AsOutput, lines: impl AsRef) { let stdout = output.as_stdout(); assert_output(stdout, lines.as_ref()); } ================================================ FILE: crates/shared/src/utils.rs ================================================ use starknet_api::execution_utils::format_panic_data; use starknet_types_core::felt::Felt; /// Helper function to build readable text from run data. #[must_use] pub fn build_readable_text(data: &[Felt]) -> Option { if data.is_empty() { return None; } let string = format_panic_data(data); let mut result = indent_string(&format!("\n{string}")); result.push('\n'); Some(result) } fn indent_string(string: &str) -> String { let without_trailing = string.strip_suffix('\n').unwrap_or(string); let indented = without_trailing.replace('\n', "\n "); let should_append_newline = string.ends_with('\n'); if should_append_newline { format!("{indented}\n") } else { indented } } #[cfg(test)] mod tests { use super::indent_string; #[test] fn test_indent_string() { let s = indent_string("\nabc\n"); assert_eq!(s, "\n abc\n"); let s = indent_string("\nabc"); assert_eq!(s, "\n abc"); let s = indent_string("\nabc\nd"); assert_eq!(s, "\n abc\n d"); } } ================================================ FILE: crates/shared/src/vm.rs ================================================ use cairo_vm::vm::vm_core::VirtualMachine; use std::iter::once; pub trait VirtualMachineExt { /// Return the relocated pc values corresponding to each call instruction in the traceback. /// Returns the most recent call first. /// # Note /// Only call instructions from segment 0 (the main program segment) are included in the result. /// To get the relocated PC values, we add 1 to each entry in the returned list. /// /// This approach works specifically because all PCs are in segment 0. And relocation table for this /// segment is 1. [Ref](https://github.com/lambdaclass/cairo-vm/blob/82e465bc90f9f32a3b368e8336cc9d0963bbdca3/vm/src/vm/vm_memory/memory_segments.rs#L113) /// /// If the PCs were located in other segments, we would need to use a relocation table /// to compute their relocated values. However, the relocation table is not publicly /// accessible from within the VM, and accessing it would require replicating significant /// amounts of internal implementation code. fn get_reversed_pc_traceback(&self) -> Vec; } impl VirtualMachineExt for VirtualMachine { fn get_reversed_pc_traceback(&self) -> Vec { self.get_traceback_entries() .into_iter() // The cairo-vm implementation doesn't include the start location, so we add it manually. .chain(once((self.get_fp(), self.get_pc()))) .rev() .map(|(_, pc)| pc) .filter(|pc| pc.segment_index == 0) .map(|pc| pc.offset + 1) .collect() } } ================================================ FILE: crates/sncast/.cargo/config.toml ================================================ [alias] "test e2e" = "test --test main e2e" "test integration" = "test --test main integration" ================================================ FILE: crates/sncast/.gitignore ================================================ .idea tests/utils/cairo tests/utils/scarb* /tests/data/contracts/**/*.tool-versions /tests/utils/compiler/ .env* tests/data/contracts/constructor_with_params[0-9]/ tests/data/contracts/map[0-9]/ tests/utils/devnet tests/**/*state.json ================================================ FILE: crates/sncast/Cargo.toml ================================================ [package] name = "sncast" description = "sncast - a Starknet Foundry CLI client" version.workspace = true edition.workspace = true [dependencies] anyhow.workspace = true camino.workspace = true bigdecimal.workspace = true clap.workspace = true clap_complete.workspace = true console.workspace = true serde_json.workspace = true serde.workspace = true starknet-rust = { workspace = true, features = ["ledger"] } tokio.workspace = true url.workspace = true rand.workspace = true scarb-metadata.workspace = true thiserror.workspace = true shellexpand.workspace = true toml.workspace = true rpassword.workspace = true promptly.workspace = true scarb-api = { path = "../scarb-api" } scarb-ui.workspace = true reqwest.workspace = true indoc.workspace = true tempfile.workspace = true runtime = { path = "../runtime" } conversions = { path = "../conversions" } data-transformer = { path = "../data-transformer" } configuration = { path = "../configuration" } shared = { path = "../shared" } forge_runner = { path = "../forge-runner" } cairo-lang-runner = "2.17.0" cairo-lang-runnable-utils = "2.17.0" cairo-lang-utils.workspace = true cairo-lang-sierra.workspace = true cairo-lang-casm.workspace = true itertools.workspace = true starknet-types-core.workspace = true cairo-vm.workspace = true blockifier.workspace = true semver.workspace = true sha3.workspace = true sha2.workspace = true base16ct.workspace = true starknet-rust-crypto.workspace = true async-trait.workspace = true serde_path_to_error.workspace = true walkdir.workspace = true const-hex.workspace = true regex.workspace = true dirs.workspace = true dialoguer.workspace = true toml_edit.workspace = true num-traits.workspace = true foundry-ui = { path = "../foundry-ui" } universal-sierra-compiler-api = { path = "../universal-sierra-compiler-api" } tracing-subscriber.workspace = true tracing.workspace = true tracing-log.workspace = true strum.workspace = true strum_macros.workspace = true primitive-types.workspace = true coins-ledger.workspace = true mimalloc.workspace = true [dev-dependencies] ctor.workspace = true snapbox.workspace = true indoc.workspace = true project-root.workspace = true tempfile.workspace = true test-case.workspace = true fs_extra.workspace = true wiremock.workspace = true docs = { workspace = true, features = ["testing"] } shared = { path = "../shared", features = ["testing"] } packages_validation = { path = "../testing/packages_validation" } speculos-client.workspace = true [features] default = [] ledger-emulator = [] [[bin]] name = "sncast" path = "src/main.rs" ================================================ FILE: crates/sncast/README.md ================================================ # sncast - Starknet Foundry CLI Starknet Foundry `sncast` is a command line tool for performing Starknet RPC calls. With it, you can easily interact with Starknet contracts! Note, that `sncast` only officially supports contracts written in Cairo 2. ## Table of contents * [Installation](#installation) * [Documentation](#documentation) * [Example usages](#example-usages) * [Declaring contracts](#declare-a-contract) * [Deploying contracts](#deploy-a-contract) * [Invoking contracts](#invoke-a-contract) * [Calling contracts](#call-a-contract) * [Development](#development) ## Installation You can download latest version of `sncast` [here](https://github.com/foundry-rs/starknet-foundry/releases). ## Documentation For more details on Starknet Foundry `sncast`, please visit [our docs](https://foundry-rs.github.io/starknet-foundry/starknet/sncast-overview.html) ## Example usages All subcommand usages are shown for two scenarios - when all necessary arguments are supplied using CLI, and when `url`, `accounts-file` and `account` arguments are taken from `snfoundry.toml`. To learn more about configuring profiles with parameters in `snfoundry.toml` file, please refer to the [documentation](https://foundry-rs.github.io/starknet-foundry/projects/configuration.html#defining-profiles-in-snfoundrytoml). ### Declare a contract ```shell $ sncast --account my_account \ declare \ --contract-name HelloSncast ```
Output: ```shell Success: Declaration completed Class Hash: 0x[..] Transaction Hash: 0x[..] ```

With arguments taken from `snfoundry.toml` file (default profile name): ```shell $ sncast declare \ --contract-name HelloSncast ```
Output: ```shell Success: Declaration completed Class Hash: 0x[..] Transaction Hash: 0x[..] ```

### Deploy a contract ```shell $ sncast --account my_account \ deploy --class-hash 0x0227f52a4d2138816edf8231980d5f9e6e0c8a3deab45b601a1fcee3d4427b02 \ --url http://127.0.0.1:5055 ```
Output: ```shell Success: Deployment completed Contract Address: 0x0[..] Transaction Hash: 0x0[..] ```

With arguments taken from `snfoundry.toml` file (default profile name): ```shell $ sncast deploy \ --class-hash 0x0227f52a4d2138816edf8231980d5f9e6e0c8a3deab45b601a1fcee3d4427b02 \ ```
Output: ```shell Success: Deployment completed Contract Address: 0x0[..] Transaction Hash: 0x0[..] ```

### Invoke a contract ```shell $ sncast \ --account my_account \ invoke \ --contract-address 0x0589a8b8bf819b7820cb699ea1f6c409bc012c9b9160106ddc3dacd6a89653cf \ --function "sum_numbers" \ --arguments '1, 2, 3' \ --url http://127.0.0.1:5055/rpc ```
Output: ```shell Success: Invoke completed Transaction Hash: [..] To see invocation details, visit: transaction: https://sepolia.voyager.online/tx/[..] ```

With arguments taken from `snfoundry.toml` file (default profile name): ```shell $ sncast invoke \ --contract-address 0x0589a8b8bf819b7820cb699ea1f6c409bc012c9b9160106ddc3dacd6a89653cf \ --function "sum_numbers" \ --arguments '1, 2, 3' \ --url http://127.0.0.1:5055/rpc ```
Output: ```shell Success: Invoke completed Transaction Hash: [..] To see invocation details, visit: transaction: https://sepolia.voyager.online/tx/[..] ```

### Call a contract ```shell $ sncast \ call \ --contract-address 0x0589a8b8bf819b7820cb699ea1f6c409bc012c9b9160106ddc3dacd6a89653cf \ --function "sum_numbers" \ --arguments '1, 2, 3' \ --url http://127.0.0.1:5055/rpc ```
Output: ```shell Success: Call completed Response: 0x6 Response Raw: [0x6] ```

With arguments taken from `snfoundry.toml` file (default profile name): ```shell $ sncast call \ --contract-address 0x0589a8b8bf819b7820cb699ea1f6c409bc012c9b9160106ddc3dacd6a89653cf \ --function "sum_numbers" \ --arguments '1, 2, 3' \ --url http://127.0.0.1:5055/rpc ```
Output: ```shell Success: Call completed Response: 0x6 Response Raw: [0x6] ```

## Development Refer to [documentation](https://foundry-rs.github.io/starknet-foundry/development/environment-setup.html) to make sure you have all the pre-requisites, and to obtain an information on how to help to develop `sncast`. Please make sure you're using scarb installed via asdf - otherwise some tests may fail. To verify, run: ```shell $ which scarb ```
Output: ```shell $HOME/.asdf/shims/scarb ```

If you previously installed scarb using official installer, you may need to remove this installation or modify your PATH to make sure asdf installed one is always used. ================================================ FILE: crates/sncast/src/helpers/account.rs ================================================ use crate::{ NestedMap, build_account, check_account_file_exists, helpers::devnet::provider::DevnetProvider, }; use anyhow::{Result, ensure}; use camino::Utf8PathBuf; use starknet_rust::{ accounts::SingleOwnerAccount, providers::{JsonRpcClient, Provider, jsonrpc::HttpTransport}, signers::LocalWallet, }; use std::collections::{HashMap, HashSet}; use std::fs; use url::Url; use crate::{AccountData, read_and_parse_json_file}; use anyhow::Context; use serde_json::{Value, json}; pub fn generate_account_name(accounts_file: &Utf8PathBuf) -> Result { let mut id = 1; if !accounts_file.exists() { return Ok(format!("account-{id}")); } let networks: NestedMap = read_and_parse_json_file(accounts_file)?; let mut result = HashSet::new(); for (_, accounts) in networks { for (name, _) in accounts { if let Some(id) = name .strip_prefix("account-") .and_then(|id| id.parse::().ok()) { result.insert(id); } } } while result.contains(&id) { id += 1; } Ok(format!("account-{id}")) } pub fn load_accounts(accounts_file: &Utf8PathBuf) -> Result { let contents = fs::read_to_string(accounts_file).context("Failed to read accounts file")?; if contents.trim().is_empty() { return Ok(json!({})); } let accounts = serde_json::from_str(&contents) .with_context(|| format!("Failed to parse accounts file at = {accounts_file}"))?; Ok(accounts) } pub fn check_account_exists( account_name: &str, network_name: &str, accounts_file: &Utf8PathBuf, ) -> Result { check_account_file_exists(accounts_file)?; let accounts: HashMap> = read_and_parse_json_file(accounts_file)?; accounts .get(network_name) .map(|network_accounts| network_accounts.contains_key(account_name)) .ok_or_else(|| { anyhow::anyhow!("Network with name {network_name} does not exist in accounts file") }) } #[must_use] pub fn is_devnet_account(account: &str) -> bool { account.starts_with("devnet-") } pub async fn get_account_from_devnet<'a>( account: &str, provider: &'a JsonRpcClient, url: &Url, ) -> Result, LocalWallet>> { let account_number: u8 = account .strip_prefix("devnet-") .map(|s| s.parse::().expect("Invalid devnet account number")) .context("Failed to parse devnet account number")?; let devnet_provider = DevnetProvider::new(url.as_ref()); devnet_provider.ensure_alive().await?; let devnet_config = devnet_provider.get_config().await; let devnet_config = match devnet_config { Ok(config) => config, Err(err) => { return Err(err); } }; ensure!( account_number <= devnet_config.total_accounts && account_number != 0, "Devnet account number must be between 1 and {}", devnet_config.total_accounts ); let devnet_accounts = devnet_provider.get_predeployed_accounts().await?; let predeployed_account = devnet_accounts .get((account_number - 1) as usize) .expect("Failed to get devnet account") .to_owned(); let account_data = AccountData::from(predeployed_account); let chain_id = provider.chain_id().await?; build_account(account_data, chain_id, provider).await } ================================================ FILE: crates/sncast/src/helpers/artifacts.rs ================================================ /// Contains compiled Starknet artifacts #[derive(Debug, Clone)] pub struct CastStarknetContractArtifacts { /// Compiled sierra code pub sierra: String, /// Compiled casm code pub casm: String, } ================================================ FILE: crates/sncast/src/helpers/block_explorer.rs ================================================ use crate::{Network, response::explorer_link::ExplorerError}; use conversions::padded_felt::PaddedFelt; use serde::{Deserialize, Serialize}; const VOYAGER: &str = "voyager.online"; const VIEWBLOCK: &str = "https://viewblock.io/starknet"; const OKLINK: &str = "https://www.oklink.com/starknet"; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum Service { #[default] Voyager, StarkScan, ViewBlock, OkLink, } impl Service { pub fn as_provider(&self, network: Network) -> Result, ExplorerError> { match (self, network) { (Service::StarkScan, _) => unreachable!("Should be caught by config validation"), (Service::Voyager, _) => Ok(Box::new(Voyager { network })), (Service::ViewBlock, Network::Mainnet) => Ok(Box::new(ViewBlock)), (Service::OkLink, Network::Mainnet) => Ok(Box::new(OkLink)), (_, Network::Sepolia) => Err(ExplorerError::SepoliaNotSupported), (_, Network::Devnet) => Err(ExplorerError::DevnetNotSupported), } } } pub trait LinkProvider { fn transaction(&self, hash: PaddedFelt) -> String; fn class(&self, hash: PaddedFelt) -> String; fn contract(&self, address: PaddedFelt) -> String; } const fn network_subdomain(network: Network) -> &'static str { match network { Network::Sepolia => "sepolia.", Network::Mainnet | Network::Devnet => "", } } pub struct Voyager { network: Network, } impl LinkProvider for Voyager { fn transaction(&self, hash: PaddedFelt) -> String { format!( "https://{}{VOYAGER}/tx/{hash:#x}", network_subdomain(self.network) ) } fn class(&self, hash: PaddedFelt) -> String { format!( "https://{}{VOYAGER}/class/{hash:#x}", network_subdomain(self.network) ) } fn contract(&self, address: PaddedFelt) -> String { format!( "https://{}{VOYAGER}/contract/{address:#x}", network_subdomain(self.network) ) } } pub struct ViewBlock; impl LinkProvider for ViewBlock { fn transaction(&self, hash: PaddedFelt) -> String { format!("{VIEWBLOCK}/tx/{hash:#x}") } fn class(&self, hash: PaddedFelt) -> String { format!("{VIEWBLOCK}/class/{hash:#x}") } fn contract(&self, address: PaddedFelt) -> String { format!("{VIEWBLOCK}/contract/{address:#x}") } } pub struct OkLink; impl LinkProvider for OkLink { fn transaction(&self, hash: PaddedFelt) -> String { format!("{OKLINK}/tx/{hash:#x}") } fn class(&self, hash: PaddedFelt) -> String { format!("{OKLINK}/class/{hash:#x}") } fn contract(&self, address: PaddedFelt) -> String { format!("{OKLINK}/contract/{address:#x}") } } #[cfg(test)] mod tests { use crate::response::deploy::DeployResponse; use crate::{ Network, helpers::block_explorer::Service, response::{deploy::StandardDeployResponse, explorer_link::OutputLink}, }; use conversions::padded_felt::PaddedFelt; use regex::Regex; use starknet_rust::macros::felt; use test_case::test_case; const MAINNET_RESPONSE: DeployResponse = DeployResponse::Standard(StandardDeployResponse { contract_address: PaddedFelt(felt!( "0x03241d40a2af53a34274dd411e090ccac1ea80e0380a0303fe76d71b046cfecb" )), transaction_hash: PaddedFelt(felt!( "0x7605291e593e0c6ad85681d09e27a601befb85033bdf1805aabf5d84617cf68" )), }); const SEPOLIA_RESPONSE: DeployResponse = DeployResponse::Standard(StandardDeployResponse { contract_address: PaddedFelt(felt!( "0x0716b5f1e3bd760c489272fd6530462a09578109049e26e3f4c70492676eae17" )), transaction_hash: PaddedFelt(felt!( "0x1cde70aae10f79d2d1289c923a1eeca7b81a2a6691c32551ec540fa2cb29c33" )), }); async fn assert_valid_links(input: &str) { let pattern = Regex::new(r"transaction: |contract: |class: ").unwrap(); let links = pattern.replace_all(input, ""); let mut links = links.split('\n'); let contract = links.next().unwrap(); let transaction = links.next().unwrap(); let (contract_response, transaction_response) = tokio::join!(reqwest::get(contract), reqwest::get(transaction)); assert!(contract_response.is_ok()); assert!(transaction_response.is_ok()); } #[test_case(Network::Mainnet, &MAINNET_RESPONSE; "mainnet")] #[test_case(Network::Sepolia, &SEPOLIA_RESPONSE; "sepolia")] #[tokio::test] async fn test_happy_case_voyager(network: Network, response: &DeployResponse) { let provider = Service::Voyager.as_provider(network).unwrap(); let result = response.format_links(provider); assert_valid_links(&result).await; } #[test_case(Service::ViewBlock; "viewblock")] #[test_case(Service::OkLink; "oklink")] #[tokio::test] async fn test_happy_case_mainnet(explorer: Service) { let result = MAINNET_RESPONSE.format_links(explorer.as_provider(Network::Mainnet).unwrap()); assert_valid_links(&result).await; } #[test_case(Service::ViewBlock; "viewblock")] #[test_case(Service::OkLink; "oklink")] #[tokio::test] async fn test_fail_on_sepolia(explorer: Service) { assert!(explorer.as_provider(Network::Sepolia).is_err()); } } ================================================ FILE: crates/sncast/src/helpers/braavos.rs ================================================ use async_trait::async_trait; use starknet_rust::{ accounts::{AccountFactory, PreparedAccountDeploymentV3, RawAccountDeploymentV3}, core::types::{BlockId, BlockTag}, providers::Provider, signers::Signer, }; use starknet_rust_crypto::poseidon_hash_many; use starknet_types_core::felt::Felt; // Adapted from strakli as there is currently no implementation of braavos account factory in starknet-rs pub struct BraavosAccountFactory { class_hash: Felt, base_class_hash: Felt, chain_id: Felt, signer_public_key: Felt, signer: S, provider: P, block_id: BlockId, } impl BraavosAccountFactory where S: Signer, { pub async fn new( class_hash: Felt, base_class_hash: Felt, chain_id: Felt, signer: S, provider: P, ) -> Result { let signer_public_key = signer.get_public_key().await?; Ok(Self { class_hash, base_class_hash, chain_id, signer_public_key: signer_public_key.scalar(), signer, provider, block_id: BlockId::Tag(BlockTag::Latest), }) } pub fn set_block_id(&mut self, block_id: BlockId) -> &Self { self.block_id = block_id; self } async fn sign_deployment(&self, tx_hash: Felt) -> Result, S::SignError> { let signature = self.signer.sign_hash(&tx_hash).await?; // You can see params here: // https://github.com/myBraavos/braavos-account-cairo/blob/6efdfd597bb051e99c79a512fccd14ee2523c898/src/presets/braavos_account.cairo#L104 // Order of the params is important, you can see way and order of deserialization here: // https://github.com/myBraavos/braavos-account-cairo/blob/6efdfd597bb051e99c79a512fccd14ee2523c898/src/presets/braavos_account.cairo#L141 // first 3 elements in sig are always [tx hash(r, s), account impl, ...] // last 2 elements are sig on the aux data sent in the sig preceded by chain id: // [..., account_impl, ..., chain_id, aux(r, s)] // ref: https://github.com/myBraavos/braavos-account-cairo/blob/6efdfd597bb051e99c79a512fccd14ee2523c898/src/presets/braavos_base_account.cairo#L74 let aux_data: Vec = vec![ // account_implementation self.class_hash, // signer_type Felt::ZERO, // secp256r1_signer.x.low Felt::ZERO, // secp256r1_signer.x.high Felt::ZERO, // secp256r1_signer.y.low Felt::ZERO, // secp256r1_signer.y.high Felt::ZERO, // multisig_threshold Felt::ZERO, // withdrawal_limit_low Felt::ZERO, // fee_rate Felt::ZERO, // stark_fee_rate Felt::ZERO, // chain_id self.chain_id, ]; let aux_hash = poseidon_hash_many(&aux_data[..]); let aux_signature = self.signer.sign_hash(&aux_hash).await?; Ok([ vec![signature.r, signature.s], aux_data, vec![aux_signature.r, aux_signature.s], ] .concat()) } } #[async_trait] impl AccountFactory for BraavosAccountFactory where S: Signer + Sync + Send, P: Provider + Sync + Send, { type Provider = P; type SignError = S::SignError; #[expect(clippy::misnamed_getters)] fn class_hash(&self) -> Felt { self.base_class_hash } fn calldata(&self) -> Vec { vec![self.signer_public_key] } fn chain_id(&self) -> Felt { self.chain_id } fn provider(&self) -> &Self::Provider { &self.provider } fn block_id(&self) -> BlockId { self.block_id } async fn sign_deployment_v3( &self, deployment: &RawAccountDeploymentV3, query_only: bool, ) -> Result, Self::SignError> { let tx_hash = PreparedAccountDeploymentV3::from_raw(deployment.clone(), self) .transaction_hash(query_only); self.sign_deployment(tx_hash).await } fn is_signer_interactive(&self) -> bool { // Braavos' __validate_deploy__ requires a fully structured signature (minimum number of // elements) even for fee estimation query transactions. If we report the signer as // interactive, starknet-rust will send an empty signature for estimation, which causes the // Braavos contract to panic with "Index out of bounds". Returning false forces a real // signature to always be produced, at the cost of an extra Ledger confirmation during // fee estimation. false } } ================================================ FILE: crates/sncast/src/helpers/command.rs ================================================ use crate::response::cast_message::SncastMessage; use crate::response::{errors::ResponseError, explorer_link::ExplorerLinksMessage}; use anyhow::Result; use crate::response::cast_message::SncastCommandMessage; use crate::response::ui::UI; use foundry_ui::Message; use serde::Serialize; use std::process::ExitCode; pub fn process_command_result( command: &str, result: Result, ui: &UI, block_explorer_link: Option, ) -> ExitCode where T: SncastCommandMessage + Serialize, SncastMessage: Message, { let cast_msg = result.map(|command_response| SncastMessage(command_response)); match cast_msg { Ok(response) => { ui.print_message(command, response); if let Some(link) = block_explorer_link { ui.print_notification(link); } ExitCode::SUCCESS } Err(err) => { let err = ResponseError::new(command.to_string(), format!("{err:#}")); ui.print_error(command, err); ExitCode::FAILURE } } } ================================================ FILE: crates/sncast/src/helpers/config.rs ================================================ use crate::ValidatedWaitParams; use crate::helpers::configuration::{CastConfig, show_explorer_links_default}; use crate::helpers::constants::DEFAULT_ACCOUNTS_FILE; use anyhow::Result; use camino::Utf8PathBuf; use indoc::formatdoc; use std::fs; use std::fs::File; use std::io::Write; pub fn get_global_config_path() -> Result { let global_config_dir = { if cfg!(target_os = "windows") { dirs::config_dir() .ok_or_else(|| anyhow::anyhow!("Could not determine config directory"))? .join("starknet-foundry") } else { dirs::home_dir() .ok_or_else(|| anyhow::anyhow!("Could not determine home directory"))? .join(".config/starknet-foundry") } }; if !global_config_dir.exists() { fs::create_dir_all(&global_config_dir)?; } let global_config_path = Utf8PathBuf::from_path_buf(global_config_dir.join("snfoundry.toml")) .expect("Failed to convert PathBuf to Utf8PathBuf for global configuration"); if !global_config_path.exists() { create_global_config(global_config_path.clone())?; } Ok(global_config_path) } fn build_default_manifest() -> String { let default_wait_params = ValidatedWaitParams::default(); formatdoc! {r#" # Visit https://foundry-rs.github.io/starknet-foundry/appendix/snfoundry-toml.html # and https://foundry-rs.github.io/starknet-foundry/projects/configuration.html for more information # [sncast.default] # url = "" # block-explorer = "{default_block_explorer}" # wait-params = {{ timeout = {default_wait_timeout}, retry-interval = {default_wait_retry_interval} }} # show-explorer-links = {default_show_explorer_links} # accounts-file = "{default_accounts_file}" # account = "" # keystore = "" # Configure custom network addresses # [sncast.default.networks] # mainnet = "https://mainnet.your-node.com" # sepolia = "https://sepolia.your-node.com" # devnet = "http://127.0.0.1:5050/rpc" "#, default_accounts_file = DEFAULT_ACCOUNTS_FILE, default_wait_timeout = default_wait_params.timeout, default_wait_retry_interval = default_wait_params.retry_interval, default_block_explorer = "Voyager", default_show_explorer_links = show_explorer_links_default(), } } fn create_global_config(global_config_path: Utf8PathBuf) -> Result<()> { let mut file = File::create(global_config_path)?; file.write_all(build_default_manifest().as_bytes())?; Ok(()) } macro_rules! clone_field { ($global_config:expr, $local_config:expr, $default_config:expr, $field:ident) => { if $local_config.$field != $default_config.$field { $local_config.$field.clone() } else { $global_config.$field.clone() } }; } #[must_use] pub fn combine_cast_configs(global_config: &CastConfig, local_config: &CastConfig) -> CastConfig { let default_cast_config = CastConfig::default(); let mut networks = global_config.networks.clone(); networks.override_with(&local_config.networks); CastConfig { url: clone_field!(global_config, local_config, default_cast_config, url), network: clone_field!(global_config, local_config, default_cast_config, network), account: clone_field!(global_config, local_config, default_cast_config, account), accounts_file: clone_field!( global_config, local_config, default_cast_config, accounts_file ), keystore: clone_field!(global_config, local_config, default_cast_config, keystore), wait_params: clone_field!( global_config, local_config, default_cast_config, wait_params ), block_explorer: clone_field!( global_config, local_config, default_cast_config, block_explorer ), show_explorer_links: clone_field!( global_config, local_config, default_cast_config, show_explorer_links ), networks, } } ================================================ FILE: crates/sncast/src/helpers/configuration.rs ================================================ use super::block_explorer; use crate::{Network, ValidatedWaitParams}; use anyhow::Result; use camino::Utf8PathBuf; use configuration::Config; use serde::{Deserialize, Serialize}; use url::Url; #[must_use] pub const fn show_explorer_links_default() -> bool { true } #[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Default)] #[serde(deny_unknown_fields)] pub struct NetworksConfig { pub mainnet: Option, pub sepolia: Option, pub devnet: Option, } impl NetworksConfig { #[must_use] pub fn get_url(&self, network: Network) -> Option<&Url> { match network { Network::Mainnet => self.mainnet.as_ref(), Network::Sepolia => self.sepolia.as_ref(), Network::Devnet => self.devnet.as_ref(), } } pub fn override_with(&mut self, other: &NetworksConfig) { if other.mainnet.is_some() { self.mainnet.clone_from(&other.mainnet); } if other.sepolia.is_some() { self.sepolia.clone_from(&other.sepolia); } if other.devnet.is_some() { self.devnet.clone_from(&other.devnet); } } } #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] pub struct CastConfig { #[serde(default)] /// RPC url pub url: Option, #[serde(default)] pub network: Option, #[serde(default)] pub account: String, #[serde( default, rename(serialize = "accounts-file", deserialize = "accounts-file") )] pub accounts_file: Utf8PathBuf, pub keystore: Option, #[serde( default, rename(serialize = "wait-params", deserialize = "wait-params") )] pub wait_params: ValidatedWaitParams, #[serde( default, rename(serialize = "block-explorer", deserialize = "block-explorer") )] /// A block explorer service, used to display links to transaction details pub block_explorer: Option, #[serde( default = "show_explorer_links_default", rename(serialize = "show-explorer-links", deserialize = "show-explorer-links") )] /// Print links pointing to pages with transaction details in the chosen block explorer pub show_explorer_links: bool, #[serde(default)] /// Configurable urls of predefined networks - mainnet, sepolia, and devnet are supported pub networks: NetworksConfig, } // TODO(#4027) impl CastConfig { pub fn validate(&self) -> anyhow::Result<()> { if self.block_explorer.unwrap_or_default() == block_explorer::Service::StarkScan { anyhow::bail!( "starkscan.co was terminated and `'StarkScan'` is no longer available. Please set `block-explorer` to `'Voyager'` or other explorer of your choice." ) } match (&self.url, &self.network) { (Some(_), Some(_)) => { anyhow::bail!("Only one of `url` or `network` may be specified") } _ => Ok(()), } } } impl Default for CastConfig { fn default() -> Self { Self { url: None, network: None, account: String::default(), accounts_file: Utf8PathBuf::default(), keystore: None, wait_params: ValidatedWaitParams::default(), block_explorer: Some(block_explorer::Service::default()), show_explorer_links: show_explorer_links_default(), networks: NetworksConfig::default(), } } } impl Config for CastConfig { fn tool_name() -> &'static str { "sncast" } fn from_raw(config: serde_json::Value) -> Result { let config = serde_json::from_value::(config)?; config.validate()?; Ok(config) } } #[cfg(test)] mod tests { use super::*; use url::Url; #[test] fn test_networks_config_get() { let networks = NetworksConfig { mainnet: Some(Url::parse("https://mainnet.example.com").unwrap()), sepolia: Some(Url::parse("https://sepolia.example.com").unwrap()), devnet: Some(Url::parse("https://devnet.example.com").unwrap()), }; assert_eq!( networks.get_url(Network::Mainnet), Some(&Url::parse("https://mainnet.example.com").unwrap()) ); assert_eq!( networks.get_url(Network::Sepolia), Some(&Url::parse("https://sepolia.example.com").unwrap()) ); assert_eq!( networks.get_url(Network::Devnet), Some(&Url::parse("https://devnet.example.com").unwrap()) ); } #[test] fn test_networks_config_override() { let mut global = NetworksConfig { mainnet: Some(Url::parse("https://global-mainnet.example.com").unwrap()), sepolia: Some(Url::parse("https://global-sepolia.example.com").unwrap()), devnet: None, }; let local = NetworksConfig { mainnet: Some(Url::parse("https://local-mainnet.example.com").unwrap()), sepolia: None, devnet: None, }; global.override_with(&local); // Local mainnet should override global assert_eq!( global.mainnet, Some(Url::parse("https://local-mainnet.example.com").unwrap()) ); // Global sepolia should remain assert_eq!( global.sepolia, Some(Url::parse("https://global-sepolia.example.com").unwrap()) ); } #[test] fn test_networks_config_rejects_unknown_fields_and_typos() { // Unknown fields should cause an error let toml_str = r#" mainnet = "https://mainnet.example.com" custom = "https://custom.example.com" wrong_key = "https://sepolia.example.com" "#; let result: Result = toml::from_str(toml_str); assert!(result.is_err()); assert!(result.unwrap_err().to_string().contains("unknown field")); } } ================================================ FILE: crates/sncast/src/helpers/constants.rs ================================================ use starknet_rust::macros::felt; use starknet_types_core::felt::Felt; pub static DEFAULT_MULTICALL_CONTENTS: &str = r#"[[call]] call_type = "deploy" class_hash = "" inputs = [] id = "" unique = false [[call]] call_type = "invoke" contract_address = "" function = "" inputs = [] "#; pub const UDC_ADDRESS: Felt = felt!("0x02ceed65a4bd731034c01113685c831b01c15d7d432f71afb1cf1634b53a2125"); pub const OZ_CLASS_HASH: Felt = felt!("0x05b4b537eaa2399e3aa99c4e2e0208ebd6c71bc1467938cd52c798c601e43564"); // v1.0.0 pub const READY_CLASS_HASH: Felt = felt!("0x036078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f"); // v0.4.0 pub const BRAAVOS_CLASS_HASH: Felt = felt!("0x03957f9f5a1cbfe918cedc2015c85200ca51a5f7506ecb6de98a5207b759bf8a"); // v1.2.0 pub const BRAAVOS_BASE_ACCOUNT_CLASS_HASH: Felt = felt!("0x03d16c7a9a60b0593bd202f660a28c5d76e0403601d9ccc7e4fa253b6a70c201"); // v1.2.0 // used in wait_for_tx. Txs will be fetched every 5s with timeout of 300s - so 60 attempts pub const WAIT_TIMEOUT: u16 = 300; pub const WAIT_RETRY_INTERVAL: u8 = 5; pub const DEFAULT_ACCOUNTS_FILE: &str = "~/.starknet_accounts/starknet_open_zeppelin_accounts.json"; pub const KEYSTORE_PASSWORD_ENV_VAR: &str = "KEYSTORE_PASSWORD"; pub const CREATE_KEYSTORE_PASSWORD_ENV_VAR: &str = "CREATE_KEYSTORE_PASSWORD"; pub const SCRIPT_LIB_ARTIFACT_NAME: &str = "__sncast_script_lib"; pub const STATE_FILE_VERSION: u8 = 1; pub const INIT_SCRIPTS_DIR: &str = "scripts"; pub const DEFAULT_STATE_FILE_SUFFIX: &str = "state.json"; ================================================ FILE: crates/sncast/src/helpers/devnet/detection/direct.rs ================================================ use crate::helpers::devnet::detection::flag_parsing::{ extract_port_from_flag, extract_string_from_flag, }; use crate::helpers::devnet::detection::{ DEFAULT_DEVNET_HOST, DEFAULT_DEVNET_PORT, DevnetDetectionError, ProcessInfo, }; pub fn extract_devnet_info_from_direct_run( cmdline: &str, ) -> Result { let mut port = extract_port_from_flag(cmdline, "--port"); let mut host = extract_string_from_flag(cmdline, "--host"); if port.is_none() && let Ok(port_env) = std::env::var("PORT") { port = Some( port_env .parse() .map_err(|_| DevnetDetectionError::ProcessNotReachable)?, ); } if host.is_none() && let Ok(host_env) = std::env::var("HOST") && !host_env.is_empty() { host = Some(host_env); } let final_port = port.unwrap_or(DEFAULT_DEVNET_PORT); let final_host = host.unwrap_or_else(|| DEFAULT_DEVNET_HOST.to_string()); Ok(ProcessInfo { host: final_host, port: final_port, }) } #[cfg(test)] mod tests { use super::*; // These tests are marked to run serially to avoid interference from environment variables #[test] fn test_direct_devnet_parsing() { test_extract_devnet_info_from_cmdline(); test_extract_devnet_info_with_both_envs(); test_invalid_env(); test_cmdline_args_override_env(); test_wrong_env_var(); } fn test_extract_devnet_info_from_cmdline() { let cmdline1 = "starknet-devnet --port 6000 --host 127.0.0.1"; let info1 = extract_devnet_info_from_direct_run(cmdline1).unwrap(); assert_eq!(info1.port, 6000); assert_eq!(info1.host, "127.0.0.1"); let cmdline2 = "/usr/bin/starknet-devnet --port=5000"; let info2 = extract_devnet_info_from_direct_run(cmdline2).unwrap(); assert_eq!(info2.port, 5000); assert_eq!(info2.host, "127.0.0.1"); let cmdline3 = "starknet-devnet --host 127.0.0.1"; let info3 = extract_devnet_info_from_direct_run(cmdline3).unwrap(); assert_eq!(info3.port, 5050); assert_eq!(info3.host, "127.0.0.1"); } fn test_extract_devnet_info_with_both_envs() { // SAFETY: Variables are only modified within this test and cleaned up afterwards unsafe { std::env::set_var("PORT", "9999"); std::env::set_var("HOST", "9.9.9.9"); } let cmdline = "starknet-devnet"; let info = extract_devnet_info_from_direct_run(cmdline).unwrap(); assert_eq!(info.port, 9999); assert_eq!(info.host, "9.9.9.9"); // SAFETY: Clean up environment variables to prevent interference unsafe { std::env::remove_var("PORT"); std::env::remove_var("HOST"); } } fn test_invalid_env() { // SAFETY: Variables are only modified within this test and cleaned up afterwards unsafe { std::env::set_var("PORT", "asdf"); std::env::set_var("HOST", "9.9.9.9"); } let cmdline = "starknet-devnet"; let result = extract_devnet_info_from_direct_run(cmdline); assert!(result.is_err()); assert!(matches!( result.unwrap_err(), DevnetDetectionError::ProcessNotReachable )); // SAFETY: Clean up environment variables to prevent interference unsafe { std::env::remove_var("PORT"); std::env::remove_var("HOST"); } } fn test_cmdline_args_override_env() { // SAFETY: Variables are only modified within this test and cleaned up afterwards unsafe { std::env::set_var("PORT", "3000"); std::env::set_var("HOST", "7.7.7.7"); } let cmdline = "starknet-devnet --port 9999 --host 192.168.1.1"; let info = extract_devnet_info_from_direct_run(cmdline).unwrap(); assert_eq!(info.port, 9999); assert_eq!(info.host, "192.168.1.1"); // SAFETY: Clean up environment variables to prevent interference unsafe { std::env::remove_var("PORT"); std::env::remove_var("HOST"); } } fn test_wrong_env_var() { // SAFETY: Variables are only modified within this test and cleaned up afterwards unsafe { std::env::set_var("PORT", "asdf"); } // Empty HOST env var should be ignored and defaults should be used let cmdline = "starknet-devnet"; let result = extract_devnet_info_from_direct_run(cmdline); assert!(result.is_err()); assert!(matches!( result.unwrap_err(), DevnetDetectionError::ProcessNotReachable )); // SAFETY: Clean up environment variables to prevent interference unsafe { std::env::remove_var("PORT"); } } } ================================================ FILE: crates/sncast/src/helpers/devnet/detection/docker.rs ================================================ use regex::Regex; use crate::helpers::devnet::detection::flag_parsing::{ extract_port_from_flag, extract_string_from_flag, }; use crate::helpers::devnet::detection::{DEFAULT_DEVNET_HOST, DevnetDetectionError, ProcessInfo}; pub fn extract_docker_mapping(cmdline: &str) -> Option { let port_flags = ["-p", "--publish"]; // Port mapping patterns: // - host:host_port:container_port (e.g., "127.0.0.1:5055:5050") // - host_port:container_port (e.g., "5090:5050") let re = Regex::new(r"^(?:([^:]+):)?(\d+):\d+$").ok()?; for flag in &port_flags { if let Some(port_mapping) = extract_string_from_flag(cmdline, flag) && let Some(caps) = re.captures(&port_mapping) && let Ok(port) = caps.get(2)?.as_str().parse::() { let host = caps.get(1).map_or_else( || DEFAULT_DEVNET_HOST.to_string(), |m| m.as_str().to_string(), ); return Some(ProcessInfo { host, port }); } } None } pub fn extract_devnet_info_from_docker_run( cmdline: &str, ) -> Result { if let Some(docker_info) = extract_docker_mapping(cmdline) { return Ok(docker_info); } if extract_string_from_flag(cmdline, "--network").is_some_and(|network| network == "host") && let Some(port) = extract_port_from_flag(cmdline, "--port") { return Ok(ProcessInfo { host: DEFAULT_DEVNET_HOST.to_string(), port, }); } // If connection info was not provided (e.g., docker run shardlabs/starknet-devnet-rs), // we cannot connect to the process from outside the container Err(DevnetDetectionError::ProcessNotReachable) } #[cfg(test)] mod tests { use super::*; #[test] fn test_extract_devnet_info_from_docker_line() { let cmdline1 = "docker run -p 127.0.0.1:5055:5050 shardlabs/starknet-devnet-rs"; let info1 = extract_devnet_info_from_docker_run(cmdline1).unwrap(); assert_eq!(info1.port, 5055); assert_eq!(info1.host, "127.0.0.1"); let cmdline2 = "docker run --publish 8080:5050 shardlabs/starknet-devnet-rs"; let info2 = extract_devnet_info_from_docker_run(cmdline2).unwrap(); assert_eq!(info2.port, 8080); assert_eq!(info2.host, "127.0.0.1"); let cmdline3 = "podman run --network host shardlabs/starknet-devnet-rs --port 5055"; let info3 = extract_devnet_info_from_docker_run(cmdline3).unwrap(); assert_eq!(info3.port, 5055); assert_eq!(info3.host, "127.0.0.1"); } #[test] fn test_extract_docker_mapping_helper() { let line = "docker run -p 127.0.0.1:5055:5050 shardlabs/starknet-devnet-rs"; let info = extract_docker_mapping(line).expect("mapping should parse"); assert_eq!(info.host, "127.0.0.1"); assert_eq!(info.port, 5055); let line2 = "docker run --publish 8080:5050 shardlabs/starknet-devnet-rs"; let info2 = extract_docker_mapping(line2).expect("mapping should parse"); assert_eq!(info2.host, "127.0.0.1"); assert_eq!(info2.port, 8080); } #[test] fn test_docker_without_port_mapping() { let cmdline = "docker run shardlabs/starknet-devnet-rs"; let result = extract_devnet_info_from_docker_run(cmdline); assert!(result.is_err()); assert!(matches!( result.unwrap_err(), DevnetDetectionError::ProcessNotReachable )); } #[test] fn test_docker_with_invalid_port_mapping() { let cmdline = "docker run -p invalid shardlabs/starknet-devnet-rs"; let result = extract_devnet_info_from_docker_run(cmdline); assert!(result.is_err()); assert!(matches!( result.unwrap_err(), DevnetDetectionError::ProcessNotReachable )); } #[test] fn test_docker_network_host_without_port() { let cmdline = "docker run --network host shardlabs/starknet-devnet-rs"; let result = extract_devnet_info_from_docker_run(cmdline); assert!(result.is_err()); assert!(matches!( result.unwrap_err(), DevnetDetectionError::ProcessNotReachable )); } } ================================================ FILE: crates/sncast/src/helpers/devnet/detection/flag_parsing.rs ================================================ use regex::Regex; pub fn extract_string_from_flag(cmdline: &str, flag: &str) -> Option { let pattern = format!(r"{}\s*=?\s*(\S+)", regex::escape(flag)); let re = Regex::new(&pattern).ok()?; re.captures(cmdline) .and_then(|caps| caps.get(1)) .map(|m| m.as_str().to_string()) } pub fn extract_port_from_flag(cmdline: &str, flag: &str) -> Option { extract_string_from_flag(cmdline, flag).and_then(|port_str| port_str.parse().ok()) } ================================================ FILE: crates/sncast/src/helpers/devnet/detection.rs ================================================ mod direct; mod docker; mod flag_parsing; use std::process::Command; use url::Url; use crate::helpers::devnet::provider::DevnetProvider; pub(super) const DEFAULT_DEVNET_HOST: &str = "127.0.0.1"; pub(super) const DEFAULT_DEVNET_PORT: u16 = 5050; #[derive(Debug, Clone)] pub(super) struct ProcessInfo { pub host: String, pub port: u16, } #[derive(Debug, thiserror::Error)] pub enum DevnetDetectionError { #[error( "Could not detect running starknet-devnet instance. Please use `--url ` instead or start devnet." )] NoInstance, #[error( "Multiple starknet-devnet instances found. Please use `--url ` to specify which one to use." )] MultipleInstances, #[error("Failed to execute process detection command.")] CommandFailed, #[error( "Found starknet-devnet process, but could not reach it. Please use `--url ` to specify the correct URL." )] ProcessNotReachable, #[error("Failed to parse devnet URL: {0}")] InvalidUrl(#[from] url::ParseError), } pub async fn detect_devnet_url() -> Result { detect_devnet_from_processes().await } #[must_use] pub async fn is_devnet_running() -> bool { detect_devnet_from_processes().await.is_ok() } async fn detect_devnet_from_processes() -> Result { match find_devnet_process_info() { Ok(info) => { if is_devnet_url_reachable(&info.host, info.port).await { Ok(Url::parse(&format!("http://{}:{}", info.host, info.port))?) } else { Err(DevnetDetectionError::ProcessNotReachable) } } Err(DevnetDetectionError::NoInstance | DevnetDetectionError::CommandFailed) => { // Fallback to default starknet-devnet URL if reachable if is_devnet_url_reachable(DEFAULT_DEVNET_HOST, DEFAULT_DEVNET_PORT).await { Ok(Url::parse(&format!( "http://{DEFAULT_DEVNET_HOST}:{DEFAULT_DEVNET_PORT}" ))?) } else { Err(DevnetDetectionError::NoInstance) } } Err(e) => Err(e), } } fn find_devnet_process_info() -> Result { let output = Command::new("sh") .args(["-c", "ps aux | grep starknet-devnet | grep -v grep"]) .output() .map_err(|_| DevnetDetectionError::CommandFailed)?; let ps_output = String::from_utf8_lossy(&output.stdout); let devnet_processes: Result, DevnetDetectionError> = ps_output .lines() .map(|line| { if line.contains("docker") || line.contains("podman") { docker::extract_devnet_info_from_docker_run(line) } else { direct::extract_devnet_info_from_direct_run(line) } }) .collect(); let devnet_processes = devnet_processes?; match devnet_processes.as_slice() { [single] => Ok(single.clone()), [] => Err(DevnetDetectionError::NoInstance), _ => Err(DevnetDetectionError::MultipleInstances), } } async fn is_devnet_url_reachable(host: &str, port: u16) -> bool { let url = format!("http://{host}:{port}"); let provider = DevnetProvider::new(&url); provider.ensure_alive().await.is_ok() } #[cfg(test)] mod tests { use super::*; #[tokio::test] async fn test_detect_devnet_url() { let result = detect_devnet_url().await; assert!(result.is_err()); assert!(matches!( result.unwrap_err(), DevnetDetectionError::NoInstance )); } } ================================================ FILE: crates/sncast/src/helpers/devnet/mod.rs ================================================ pub mod detection; pub mod provider; ================================================ FILE: crates/sncast/src/helpers/devnet/provider.rs ================================================ use crate::{AccountData, SignerType}; use ::serde::{Deserialize, Serialize, de::DeserializeOwned}; use anyhow::{Context, Error, ensure}; use reqwest::Client; use serde_json::json; use starknet_types_core::felt::Felt; use url::Url; /// A Devnet-RPC client. #[derive(Debug, Clone)] pub struct DevnetProvider { client: Client, url: Url, } /// All Devnet-RPC methods as listed in the official docs. #[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub enum DevnetProviderMethod { #[serde(rename = "devnet_getConfig")] GetConfig, #[serde(rename = "devnet_getPredeployedAccounts")] GetPredeployedAccounts, } impl DevnetProvider { #[must_use] pub fn new(url: &str) -> Self { let url = Url::parse(url).expect("Invalid URL"); Self { client: Client::new(), url, } } } impl DevnetProvider { async fn send_request(&self, method: DevnetProviderMethod, params: P) -> anyhow::Result where P: Serialize + Send + Sync, R: DeserializeOwned, { let res = self .client .post(self.url.clone()) .header("Content-Type", "application/json") .json(&json!({ "jsonrpc": "2.0", "method": method, "params": params, "id": 1, })) .send() .await .context("Failed to send request")? .json::() .await .context("Failed to parse response")?; if let Some(error) = res.get("error") { Err(anyhow::anyhow!(error.to_string())) } else if let Some(result) = res.get("result") { serde_json::from_value(result.clone()).map_err(anyhow::Error::from) } else { panic!("Malformed RPC response: {res}") } } /// Fetches the current Devnet configuration. pub async fn get_config(&self) -> Result { self.send_request(DevnetProviderMethod::GetConfig, json!({})) .await } /// Fetches the list of predeployed accounts in Devnet. pub async fn get_predeployed_accounts(&self) -> Result, Error> { self.send_request(DevnetProviderMethod::GetPredeployedAccounts, json!({})) .await } /// Ensures the Devnet instance is alive. pub async fn ensure_alive(&self) -> Result<(), Error> { let is_alive = self .client .get(format!( "{}/is_alive", self.url .to_string() .trim_end_matches('/') .replace("/rpc", "") )) .send() .await .is_ok_and(|res| res.status().is_success()); ensure!( is_alive, "Node at {} is not responding to the Devnet health check (GET `/is_alive`). It may not be a Devnet instance or it may be down.", self.url ); Ok(()) } } #[derive(Debug, Serialize, Deserialize)] pub struct Config { pub seed: u32, pub account_contract_class_hash: Felt, pub total_accounts: u8, } #[derive(Debug, Serialize, Deserialize)] pub struct PredeployedAccount { pub address: Felt, pub private_key: Felt, pub public_key: Felt, } impl From<&PredeployedAccount> for AccountData { fn from(predeployed_account: &PredeployedAccount) -> Self { Self { address: Some(predeployed_account.address), public_key: predeployed_account.public_key, class_hash: None, salt: None, deployed: None, legacy: None, account_type: None, signer_type: SignerType::Local { private_key: predeployed_account.private_key, }, } } } ================================================ FILE: crates/sncast/src/helpers/fee.rs ================================================ use anyhow::{Result, ensure}; use clap::Args; use conversions::serde::deserialize::CairoDeserialize; use starknet_rust::core::types::FeeEstimate; use starknet_types_core::felt::{Felt, NonZeroFelt}; #[derive(Args, Debug, Clone)] pub struct FeeArgs { /// Max fee for the transaction. If not provided, will be automatically estimated. #[arg(value_parser = parse_non_zero_felt, short, long, conflicts_with_all = ["l1_gas", "l1_gas_price", "l2_gas", "l2_gas_price", "l1_data_gas", "l1_data_gas_price"])] pub max_fee: Option, /// Max L1 gas amount. If not provided, will be automatically estimated. #[arg(long)] pub l1_gas: Option, /// Max L1 gas price in Fri. If not provided, will be automatically estimated. #[arg(long)] pub l1_gas_price: Option, /// Max L2 gas amount. If not provided, will be automatically estimated. #[arg(long)] pub l2_gas: Option, /// Max L2 gas price in Fri. If not provided, will be automatically estimated. #[arg(long)] pub l2_gas_price: Option, /// Max L1 data gas amount. If not provided, will be automatically estimated. #[arg(long)] pub l1_data_gas: Option, /// Max L1 data gas price in Fri. If not provided, will be automatically estimated. #[arg(long)] pub l1_data_gas_price: Option, /// Tip for the transaction. Defaults to 0 unless `--estimate-tip` is used. #[arg(long, conflicts_with = "estimate_tip")] pub tip: Option, /// If passed, an estimated tip will be added to pay for the transaction. #[arg(long)] pub estimate_tip: bool, } impl From for FeeArgs { fn from(script_fee_settings: ScriptFeeSettings) -> Self { let ScriptFeeSettings { max_fee, l1_gas, l1_gas_price, l2_gas, l2_gas_price, l1_data_gas, l1_data_gas_price, } = script_fee_settings; Self { max_fee, l1_gas, l1_gas_price, l2_gas, l2_gas_price, l1_data_gas, l1_data_gas_price, tip: Some(0), estimate_tip: false, } } } impl FeeArgs { pub fn try_into_fee_settings(&self, fee_estimate: Option<&FeeEstimate>) -> Result { // If some resource bounds values are lacking, starknet-rs will estimate them automatically // but in case someone passes --max-fee flag, we need to make estimation on our own // to check if the fee estimate isn't higher than provided max fee if let Some(max_fee) = self.max_fee { let fee_estimate = fee_estimate.expect("Fee estimate must be passed when max_fee is provided"); ensure!( Felt::from(max_fee) >= Felt::from(fee_estimate.overall_fee), "Estimated fee ({}) is higher than provided max fee ({})", fee_estimate.overall_fee, Felt::from(max_fee) ); let fee_settings = FeeSettings::try_from(fee_estimate.clone()) .expect("Failed to convert FeeEstimate to FeeSettings") .with_resolved_tip(self.tip, self.estimate_tip); Ok(fee_settings) } else { let fee_settings = FeeSettings::from(self.clone()); Ok(fee_settings) } } } /// Struct used in `sncast script` for deserializing from cairo, `FeeSettings` can't be /// used as it missing `max_fee` #[derive(Debug, PartialEq, CairoDeserialize)] pub struct ScriptFeeSettings { max_fee: Option, l1_gas: Option, l1_gas_price: Option, l2_gas: Option, l2_gas_price: Option, l1_data_gas: Option, l1_data_gas_price: Option, } #[derive(Debug, PartialEq)] pub struct FeeSettings { pub l1_gas: Option, pub l1_gas_price: Option, pub l2_gas: Option, pub l2_gas_price: Option, pub l1_data_gas: Option, pub l1_data_gas_price: Option, pub tip: Option, } impl FeeSettings { #[must_use] pub fn with_resolved_tip(self, tip: Option, estimate_tip: bool) -> FeeSettings { let tip = if estimate_tip { None // If we leave it as None, the tip will be estimated before sending the transaction } else { Some(tip.unwrap_or(0)) // If a tip is not provided, set it to 0 }; FeeSettings { tip, ..self } } } impl TryFrom for FeeSettings { type Error = anyhow::Error; fn try_from(fee_estimate: FeeEstimate) -> Result { Ok(FeeSettings { l1_gas: Some(fee_estimate.l1_gas_consumed), l1_gas_price: Some(fee_estimate.l1_gas_price), l2_gas: Some(fee_estimate.l2_gas_consumed), l2_gas_price: Some(fee_estimate.l2_gas_price), l1_data_gas: Some(fee_estimate.l1_data_gas_consumed), l1_data_gas_price: Some(fee_estimate.l1_data_gas_price), tip: None, }) } } impl From for FeeSettings { fn from(fee_args: FeeArgs) -> FeeSettings { FeeSettings { l1_gas: fee_args.l1_gas, l1_gas_price: fee_args.l1_gas_price, l2_gas: fee_args.l2_gas, l2_gas_price: fee_args.l2_gas_price, l1_data_gas: fee_args.l1_data_gas, l1_data_gas_price: fee_args.l1_data_gas_price, tip: None, } .with_resolved_tip(fee_args.tip, fee_args.estimate_tip) } } fn parse_non_zero_felt(s: &str) -> Result { let felt: Felt = s.parse().map_err(|_| "Failed to parse value")?; felt.try_into() .map_err(|_| "Value should be greater than 0".to_string()) } #[cfg(test)] mod tests { use super::FeeSettings; use starknet_rust::core::types::FeeEstimate; use std::convert::TryFrom; #[tokio::test] async fn test_from_fee_estimate() { let mock_fee_estimate = FeeEstimate { l1_gas_consumed: 1, l1_gas_price: 2, l2_gas_consumed: 3, l2_gas_price: 4, l1_data_gas_consumed: 5, l1_data_gas_price: 6, overall_fee: 44, }; let settings = FeeSettings::try_from(mock_fee_estimate).unwrap(); assert_eq!( settings, FeeSettings { l1_gas: Some(1), l1_gas_price: Some(2), l2_gas: Some(3), l2_gas_price: Some(4), l1_data_gas: Some(5), l1_data_gas_price: Some(6), tip: None, } ); } } ================================================ FILE: crates/sncast/src/helpers/interactive.rs ================================================ use crate::helpers::config::get_global_config_path; use anyhow::{Context, Result}; use camino::Utf8PathBuf; use configuration::search_config_upwards_relative_to; use dialoguer::Select; use dialoguer::theme::ColorfulTheme; use std::fmt::Display; use std::{env, fs}; use toml_edit::{DocumentMut, Item, Table, Value}; enum PromptSelection { GlobalDefault(Utf8PathBuf), LocalDefault(Utf8PathBuf), No, } impl Display for PromptSelection { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { PromptSelection::LocalDefault(path) => { write!(f, "Yes, local default ({})", to_tilde_path(path)) } PromptSelection::GlobalDefault(path) => { write!(f, "Yes, global default ({})", to_tilde_path(path)) } PromptSelection::No => write!(f, "No"), } } } pub fn prompt_to_add_account_as_default(account: &str) -> Result<()> { let mut options = Vec::new(); if let Ok(global_path) = get_global_config_path() { options.push(PromptSelection::GlobalDefault(global_path)); } if let Some(local_path) = env::current_dir() .ok() .and_then(|current_path| Utf8PathBuf::from_path_buf(current_path.clone()).ok()) .and_then(|current_path_utf8| search_config_upwards_relative_to(¤t_path_utf8).ok()) { options.push(PromptSelection::LocalDefault(local_path)); } options.push(PromptSelection::No); let selection = Select::with_theme(&ColorfulTheme::default()) .with_prompt("Do you want to make this account default?") .items(&options) .default(0) .interact() .context("Failed to display selection dialog")?; match &options[selection] { PromptSelection::GlobalDefault(path) | PromptSelection::LocalDefault(path) => { edit_config(path, "default", "account", account)?; } PromptSelection::No => {} } Ok(()) } fn edit_config(config_path: &Utf8PathBuf, profile: &str, key: &str, value: &str) -> Result<()> { let file_content = fs::read_to_string(config_path).context("Failed to read config file")?; let mut toml_doc = file_content .parse::() .context("Failed to parse TOML")?; update_config(&mut toml_doc, profile, key, value); fs::write(config_path, toml_doc.to_string()).context("Failed to write to config file")?; Ok(()) } fn update_config(toml_doc: &mut DocumentMut, profile: &str, key: &str, value: &str) { let sncast_section = toml_doc .entry("sncast") .or_insert(Item::Table(Table::new())); let sncast_table = sncast_section .as_table_mut() .expect("Failed to create or access 'sncast' table"); sncast_table.set_implicit(true); let profile_table = sncast_table .entry(profile) .or_insert(Item::Table(Table::new())) .as_table_mut() .expect("Failed to create or access profile table"); profile_table[key] = Value::from(value).into(); } fn to_tilde_path(path: &Utf8PathBuf) -> String { if cfg!(not(target_os = "windows")) && let Some(home_dir) = dirs::home_dir() && let Ok(canonical_path) = path.canonicalize() && let Ok(stripped_path) = canonical_path.strip_prefix(&home_dir) { return format!("~/{}", stripped_path.to_string_lossy()); } path.to_string() } #[cfg(test)] mod tests { use super::update_config; use indoc::formatdoc; use toml_edit::DocumentMut; #[test] fn test_update_value() { let original = formatdoc! {r#" [snfoundry] key = 2137 [sncast.default] account = "mainnet" url = "https://localhost:5050" # comment [sncast.testnet] account = "testnet-account" # comment url = "https://swmansion.com/" "#}; let expected = formatdoc! {r#" [snfoundry] key = 2137 [sncast.default] account = "testnet" url = "https://localhost:5050" # comment [sncast.testnet] account = "testnet-account" # comment url = "https://swmansion.com/" "#}; let mut toml_doc = original.parse::().unwrap(); update_config(&mut toml_doc, "default", "account", "testnet"); assert_eq!(toml_doc.to_string(), expected); } #[test] fn test_create_key() { let original = formatdoc! {r#" [snfoundry] key = 2137 [sncast.default] url = "https://localhost:5050" [sncast.testnet] account = "testnet-account" # comment url = "https://swmansion.com/" "#}; let expected = formatdoc! {r#" [snfoundry] key = 2137 [sncast.default] url = "https://localhost:5050" account = "testnet" [sncast.testnet] account = "testnet-account" # comment url = "https://swmansion.com/" "#}; let mut toml_doc = original.parse::().unwrap(); update_config(&mut toml_doc, "default", "account", "testnet"); assert_eq!(toml_doc.to_string(), expected); } #[test] fn test_create_table_key() { let original = formatdoc! {r#" [snfoundry] key = 2137 [sncast.testnet] account = "testnet-account" # comment url = "https://swmansion.com/" "#}; let expected = formatdoc! {r#" [snfoundry] key = 2137 [sncast.testnet] account = "testnet-account" # comment url = "https://swmansion.com/" [sncast.default] account = "testnet" "#}; let mut toml_doc = original.parse::().unwrap(); update_config(&mut toml_doc, "default", "account", "testnet"); assert_eq!(toml_doc.to_string(), expected); } #[test] fn test_create_table() { let original = formatdoc! {r" [snfoundry] key = 2137 "}; let expected = formatdoc! { r#" [snfoundry] key = 2137 [sncast.default] account = "testnet" "#}; let mut toml_doc = original.parse::().unwrap(); update_config(&mut toml_doc, "default", "account", "testnet"); assert_eq!(toml_doc.to_string(), expected); } #[test] fn test_create_table_empty_file() { let original = ""; let expected = formatdoc! { r#" [sncast.default] account = "testnet" "#}; let mut toml_doc = original.parse::().unwrap(); update_config(&mut toml_doc, "default", "account", "testnet"); assert_eq!(toml_doc.to_string(), expected); } } ================================================ FILE: crates/sncast/src/helpers/ledger/account.rs ================================================ use super::{SncastLedgerTransport, create_ledger_app}; use crate::response::ui::UI; use anyhow::{Context, Result, bail}; use starknet_rust::{ accounts::{ExecutionEncoding, SingleOwnerAccount}, core::types::{BlockId, BlockTag}, providers::jsonrpc::{HttpTransport, JsonRpcClient}, signers::{DerivationPath, LedgerSigner}, }; use starknet_types_core::felt::Felt; const LEDGER_SIGNER_ERROR: &str = "Failed to create Ledger signer. Ensure the derivation path is correct and the Ledger app is ready."; pub async fn create_ledger_signer( ledger_path: &DerivationPath, ui: &UI, print_message: bool, ) -> Result> { let ledger_app = create_ledger_app().await?; if print_message { ui.print_notification( "Ledger device will display a confirmation screen. Please approve it to continue...\n", ); } LedgerSigner::new_with_app(ledger_path.clone(), ledger_app).context(LEDGER_SIGNER_ERROR) } pub fn verify_ledger_public_key(ledger_public_key: Felt, stored_public_key: Felt) -> Result<()> { if ledger_public_key != stored_public_key { bail!( "Public key mismatch!\n\ Ledger public key: {ledger_public_key:#x}\n\ Stored public key: {stored_public_key:#x}\n\ \n\ This account was created with a different Ledger derivation path or public key.\n\ Make sure you're using the same derivation path that was used during account creation." ); } Ok(()) } pub async fn ledger_account<'a>( ledger_path: &DerivationPath, address: Felt, chain_id: Felt, encoding: ExecutionEncoding, provider: &'a JsonRpcClient, ui: &UI, ) -> Result, LedgerSigner>> { let signer = create_ledger_signer(ledger_path, ui, true).await?; let mut account = SingleOwnerAccount::new(provider, signer, address, chain_id, encoding); account.set_block_id(BlockId::Tag(BlockTag::PreConfirmed)); Ok(account) } pub async fn get_ledger_public_key(ledger_path: &DerivationPath, ui: &UI) -> Result { let ledger_app = create_ledger_app().await?; ui.print_notification("Please confirm the public key on your Ledger device...\n"); let public_key = ledger_app .get_public_key(ledger_path.clone(), true) .await .context("Failed to get public key from Ledger")?; Ok(public_key.scalar()) } ================================================ FILE: crates/sncast/src/helpers/ledger/emulator_transport.rs ================================================ //! This module is only used behind the `ledger-emulator` feature flag. //! It is used to test the ledger commands locally without having to connect to a real Ledger device. use async_trait::async_trait; use coins_ledger::{ APDUAnswer, APDUCommand, LedgerError, transports::{LedgerAsync, native::NativeTransportError}, }; use reqwest::Client; use serde::Serialize; use starknet_rust::signers::ledger::{LedgerError as StarknetLedgerError, LedgerStarknetApp}; use std::io::Error as IoError; #[derive(Debug)] pub struct SpeculosHttpTransport { client: Client, base_url: String, } #[derive(Serialize)] struct ApduRequest { data: String, } fn io_err(e: impl Into>) -> LedgerError { LedgerError::NativeTransportError(NativeTransportError::Io(IoError::other(e))) } impl SpeculosHttpTransport { pub fn new(url: String) -> Result { let client = Client::builder().build().map_err(io_err)?; Ok(Self { client, base_url: url, }) } } #[async_trait] impl LedgerAsync for SpeculosHttpTransport { async fn init() -> Result { let url = std::env::var("LEDGER_EMULATOR_URL") .unwrap_or_else(|_| "http://127.0.0.1:5001".to_string()); Self::new(url) } async fn exchange(&self, command: &APDUCommand) -> Result { let request = ApduRequest { data: const_hex::encode(command.serialize()), }; let resp = self .client .post(format!("{}/apdu", self.base_url)) .json(&request) .send() .await .map_err(io_err)?; if !resp.status().is_success() { return Err(io_err(format!("HTTP Error: {}", resp.status()))); } let body: serde_json::Value = resp.json().await.map_err(io_err)?; if let Some(err) = body.get("error").and_then(|v| v.as_str()) { return Err(io_err(format!("Speculos Error: {err}"))); } let data_str = body.get("data") .and_then(|v| v.as_str()) .ok_or(LedgerError::NativeTransportError( NativeTransportError::Comm("Missing data field"), ))?; let answer = const_hex::decode(data_str).map_err(io_err)?; APDUAnswer::from_answer(answer).map_err(|_| { LedgerError::NativeTransportError(NativeTransportError::Comm("Invalid APDU response")) }) } fn close(self) {} } pub async fn emulator_ledger_app() -> Result, StarknetLedgerError> { let transport = SpeculosHttpTransport::init().await?; Ok(LedgerStarknetApp::from_transport(transport)) } ================================================ FILE: crates/sncast/src/helpers/ledger/hd_path.rs ================================================ use std::str::FromStr; use crate::response::ui::UI; use anyhow::{Result, anyhow, bail}; use clap::{Arg, Command, Error, builder::TypedValueParser, error::ErrorKind}; use foundry_ui::components::warning::WarningMessage; use sha2::{Digest, Sha256}; use starknet_rust::signers::DerivationPath; const EIP2645_LENGTH: usize = 6; /// BIP-32 encoding of `2645'` const EIP_2645_PURPOSE: u32 = 0x8000_0a55; /// The BIP-32 hardened derivation bit. const HARDENED_BIT: u32 = 1 << 31; /// Mask to extract the non-hardened index from a BIP-32 path component. const NON_HARDENED_MASK: u32 = 0x7fff_ffff; #[derive(Clone)] pub struct DerivationPathParser; /// A parsed derivation path together with any warnings produced during parsing. #[derive(Debug, Clone)] pub struct ParsedDerivationPath { pub path: DerivationPath, pub warnings: Vec, } impl ParsedDerivationPath { pub fn print_warnings(&self, ui: &UI) { for warning in &self.warnings { ui.print_warning(WarningMessage::new(warning)); } } } /// An EIP-2645 HD path, required by the Starknet Ledger app. This type allows users to write /// hash-based segments in text, instead of manually finding out the lowest 31 bits of the hash. /// /// The Ledger app requires that the path: /// - starts with `2645'`; and /// - has 6 levels /// /// Supports shorthand `m//` to omit `2645'`: /// `m//starknet'/sncast'/0'/0'/0` #[derive(Debug, Clone)] struct Eip2645Path { layer: Eip2645Level, application: Eip2645Level, eth_address_1: Eip2645Level, eth_address_2: Eip2645Level, index: Eip2645Level, } #[derive(Debug, Clone)] enum Eip2645Level { Hash(HashLevel), Raw(u32), } #[derive(Debug, Clone)] struct HashLevel { text: String, hardened: bool, } impl TypedValueParser for DerivationPathParser { type Value = ParsedDerivationPath; fn parse_ref( &self, cmd: &Command, _arg: Option<&Arg>, value: &std::ffi::OsStr, ) -> Result { if value.is_empty() { return Err(cmd .clone() .error(ErrorKind::InvalidValue, "empty Ledger derivation path")); } match value.to_str() { Some(value) => match Eip2645Path::from_str_with_warnings(value) { Ok((path, warnings)) => Ok(ParsedDerivationPath { path: path.into(), warnings, }), Err(err) => Err(cmd.clone().error( ErrorKind::InvalidValue, format!("invalid Ledger derivation path: {err}"), )), }, None => Err(cmd.clone().error( ErrorKind::InvalidValue, "invalid Ledger derivation path: not UTF-8", )), } } } impl Eip2645Path { fn from_str_with_warnings(s: &str) -> Result<(Self, Vec)> { let path = s.parse::()?; let mut warnings = Vec::new(); // These are allowed but we should serve a warning. match &path.eth_address_1 { Eip2645Level::Hash(_) => { warnings.push( "using a non-numerical value for \"eth_address_1\" might make \ automatic key discovery difficult or impossible" .to_string(), ); } Eip2645Level::Raw(raw) => { if raw & NON_HARDENED_MASK != 0 { warnings.push( "using any value other than `0'` for \"eth_address_1\" might \ make automatic key discovery difficult or impossible" .to_string(), ); } } } match &path.eth_address_2 { Eip2645Level::Hash(_) => { warnings.push( "using a non-numerical value for \"eth_address_2\" might make \ automatic key discovery difficult or impossible" .to_string(), ); } Eip2645Level::Raw(raw) => { if raw & NON_HARDENED_MASK > 100 { warnings.push( "using a large value for \"eth_address_2\" might make \ automatic key discovery difficult" .to_string(), ); } } } if path.index.is_hardened() { warnings.push( "hardening \"index\" is non-standard and it might make \ automatic key discovery difficult or impossible" .to_string(), ); } if u32::from(&path.index) & NON_HARDENED_MASK > 100 { warnings.push( "using a large value for \"index\" might make \ automatic key discovery difficult" .to_string(), ); } if !warnings.is_empty() { warnings.push( "Learn more at \ https://foundry-rs.github.io/starknet-foundry/starknet/eip-2645-hd-paths.html" .to_string(), ); } Ok((path, warnings)) } } impl FromStr for Eip2645Path { type Err = anyhow::Error; fn from_str(s: &str) -> Result { // Handle m// prefix (omitted 2645') let s = if s.starts_with("m//") { s.replacen("m//", "m/2645'/", 1) } else { s.to_string() }; let segments: Vec<&str> = s.split('/').collect(); if segments.len() != EIP2645_LENGTH + 1 { bail!("EIP-2645 paths must have {EIP2645_LENGTH} levels"); } if segments[0] != "m" { bail!("HD wallet paths must start with \"m/\""); } let prefix = segments[1].parse()?; if u32::from(&prefix) != EIP_2645_PURPOSE { bail!("EIP-2645 paths must start with \"m/2645'/\""); } let path = Self { layer: segments[2].parse()?, application: segments[3].parse()?, eth_address_1: segments[4].parse()?, eth_address_2: segments[5].parse()?, index: segments[6].parse()?, }; // These are not enforced by Ledger (for now) but are nice to have security properties if !path.layer.is_hardened() { bail!("the \"layer\" level of an EIP-2645 path must be hardened"); } if !path.application.is_hardened() { bail!("the \"application\" level of an EIP-2645 path must be hardened"); } if !path.eth_address_1.is_hardened() { bail!("the \"eth_address_1\" level of an EIP-2645 path must be hardened"); } if !path.eth_address_2.is_hardened() { bail!("the \"eth_address_2\" level of an EIP-2645 path must be hardened"); } // In the future, certain wallets might utilize sequential `index` values for key discovery, // so it might be a good idea for us to disallow using hash-based values for `index` here. if matches!(path.index, Eip2645Level::Hash(_)) { bail!("the \"index\" level must be a number"); } Ok(path) } } impl Eip2645Level { fn is_hardened(&self) -> bool { match self { Self::Hash(hash) => hash.hardened, Self::Raw(raw) => raw & HARDENED_BIT > 0, } } } impl FromStr for Eip2645Level { type Err = anyhow::Error; fn from_str(s: &str) -> Result { if s.trim() != s || s.split_whitespace().count() != 1 { bail!("path must not contain whitespaces"); } let (body, harden_notation) = if s.ends_with('\'') { (&s[0..(s.len() - 1)], true) } else { (s, false) }; if body.chars().all(|char| char.is_ascii_digit()) { let raw_node = body .parse::() .map_err(|err| anyhow!("invalid path level \"{body}\": {err}"))?; if harden_notation { if raw_node & HARDENED_BIT > 0 { bail!("`'` appended to an already-hardened value of {raw_node}"); } Ok(Self::Raw(raw_node | HARDENED_BIT)) } else { Ok(Self::Raw(raw_node)) } } else { Ok(Self::Hash(HashLevel { text: body.to_owned(), hardened: harden_notation, })) } } } impl From for DerivationPath { fn from(value: Eip2645Path) -> Self { vec![ EIP_2645_PURPOSE, (&value.layer).into(), (&value.application).into(), (&value.eth_address_1).into(), (&value.eth_address_2).into(), (&value.index).into(), ] .into() } } impl From<&Eip2645Level> for u32 { fn from(value: &Eip2645Level) -> Self { match value { Eip2645Level::Hash(level) => { let mut hasher = Sha256::new(); hasher.update(level.text.as_bytes()); let hash = hasher.finalize(); // Safe to unwrap: SHA256 output is fixed size let node = u32::from_be_bytes(hash.as_slice()[28..].try_into().unwrap()); if level.hardened { node | HARDENED_BIT } else { // SHA-256 may produce values >= 2^31; mask off the hardened bit to keep the // index in the non-hardened range. node & NON_HARDENED_MASK } } Eip2645Level::Raw(raw) => *raw, } } } /// Constructs the canonical sncast derivation path for a given account ID: /// `m//starknet'/sncast'/0'/'/0` pub(super) fn account_id_to_derivation_path(account_id: u32) -> DerivationPath { Eip2645Path::from_str(&format!("m//starknet'/sncast'/0'/{account_id}'/0")) .expect("account_id_to_derivation_path: generated path must be valid") .into() } #[cfg(test)] mod tests { use super::*; fn hash_segment(s: &str) -> u32 { let level = Eip2645Level::Hash(HashLevel { text: s.to_owned(), hardened: false, }); u32::from(&level) } #[test] fn test_hash_starknet() { assert_eq!(hash_segment("starknet"), 1_195_502_025); } #[test] fn test_hash_bounded() { assert!(hash_segment("sncast") < (1 << 31)); } #[test] fn test_path_with_omitted_2645() { let path = Eip2645Path::from_str("m//starknet'/sncast'/0'/0'/0").unwrap(); let dp: DerivationPath = path.into(); let s = dp.derivation_string(); assert!(s.contains("2645'")); } #[test] fn test_path_with_explicit_2645() { let input = "m/2645'/starknet'/sncast'/0'/0'/0"; let path = Eip2645Path::from_str(input).unwrap(); let dp: DerivationPath = path.into(); let s = dp.derivation_string(); assert!(s.contains("2645'")); assert!(s.contains("1195502025'")); } #[test] fn test_numeric_path() { let input = "m/2645'/1195502025'/355113700'/0'/0'/0"; let path = Eip2645Path::from_str(input).unwrap(); let dp: DerivationPath = path.into(); let s = dp.derivation_string(); assert!(s.contains("1195502025'")); assert!(s.contains("355113700'")); } #[test] fn test_wrong_level_count_errors() { assert!(Eip2645Path::from_str("m/2645'/starknet'/sncast'/0'/0'").is_err()); } #[test] fn test_unhardened_layer_errors() { assert!(Eip2645Path::from_str("m/2645'/1195502025/355113700'/0'/0'/0").is_err()); } #[test] fn test_hash_index_errors() { assert!(Eip2645Path::from_str("m/2645'/starknet'/sncast'/0'/0'/myindex").is_err()); } #[test] fn test_path_warnings() { let warnings_for = |path: &str| { Eip2645Path::from_str_with_warnings(path) .expect("path should be valid") .1 }; // Canonical path — no warnings expected assert!(warnings_for("m//starknet'/sncast'/0'/0'/0").is_empty()); // eth_address_1: hash segment warns assert!( warnings_for("m//starknet'/sncast'/myapp'/0'/0") .iter() .any(|w| w.contains("eth_address_1")) ); // eth_address_1: any non-zero raw value warns assert!( warnings_for("m//starknet'/sncast'/1'/0'/0") .iter() .any(|w| w.contains("eth_address_1")) ); // eth_address_1: 0' is standard — no warning assert!( !warnings_for("m//starknet'/sncast'/0'/0'/0") .iter() .any(|w| w.contains("eth_address_1")) ); // eth_address_2: hash segment warns assert!( warnings_for("m//starknet'/sncast'/0'/wallet'/0") .iter() .any(|w| w.contains("eth_address_2")) ); // eth_address_2: value > 100 warns assert!( warnings_for("m//starknet'/sncast'/0'/101'/0") .iter() .any(|w| w.contains("eth_address_2")) ); // eth_address_2: value <= 100 — no warning assert!( !warnings_for("m//starknet'/sncast'/0'/1'/0") .iter() .any(|w| w.contains("eth_address_2")) ); // index: value > 100 warns assert!( warnings_for("m//starknet'/sncast'/0'/0'/101") .iter() .any(|w| w.contains("index")) ); // index: value <= 100 — no warning assert!( !warnings_for("m//starknet'/sncast'/0'/0'/5") .iter() .any(|w| w.contains("index")) ); } #[test] fn test_account_id_to_derivation_path() { // account_id 5 should equal m//starknet'/sncast'/0'/5'/0 let from_id = account_id_to_derivation_path(5); let from_str: DerivationPath = Eip2645Path::from_str("m//starknet'/sncast'/0'/5'/0") .unwrap() .into(); assert_eq!(from_id.derivation_string(), from_str.derivation_string()); } #[test] fn test_account_id_zero() { let from_id = account_id_to_derivation_path(0); let from_str: DerivationPath = Eip2645Path::from_str("m//starknet'/sncast'/0'/0'/0") .unwrap() .into(); assert_eq!(from_id.derivation_string(), from_str.derivation_string()); } } ================================================ FILE: crates/sncast/src/helpers/ledger/key_locator.rs ================================================ use clap::Args; use starknet_rust::signers::DerivationPath; use crate::response::ui::UI; use super::hd_path::{DerivationPathParser, ParsedDerivationPath, account_id_to_derivation_path}; #[derive(Args, Debug)] #[group(multiple = false, required = true)] pub struct LedgerKeyLocator { /// Ledger derivation path in EIP-2645 format #[arg(long, value_parser = DerivationPathParser)] pub path: Option, /// Account index, expands to "m//starknet'/sncast'/0'/'/0" #[arg(long)] pub account_id: Option, } #[derive(Args, Debug)] #[group(id = "ledger_key_locator_account", multiple = false)] pub struct LedgerKeyLocatorAccount { /// Ledger derivation path in EIP-2645 format #[arg(long = "ledger-path", value_parser = DerivationPathParser)] pub path: Option, /// Account index, expands to "m//starknet'/sncast'/0'/'/0" #[arg(long = "ledger-account-id")] pub account_id: Option, } impl LedgerKeyLocator { #[must_use] pub fn resolve(&self, ui: &UI) -> DerivationPath { // This expect is unreachable - clap's `required = true` group guarantees // that at least one of `--path` or `--account-id` is provided resolve_key_locator(self.path.as_ref(), self.account_id, ui) .expect("clap requires one of --path or --account-id") } } impl LedgerKeyLocatorAccount { #[must_use] pub fn resolve(&self, ui: &UI) -> Option { resolve_key_locator(self.path.as_ref(), self.account_id, ui) } } fn resolve_key_locator( path: Option<&ParsedDerivationPath>, account_id: Option, ui: &UI, ) -> Option { if let Some(parsed) = path { parsed.print_warnings(ui); Some(parsed.path.clone()) } else { account_id.map(account_id_to_derivation_path) } } ================================================ FILE: crates/sncast/src/helpers/ledger/mod.rs ================================================ mod account; mod hd_path; mod key_locator; #[cfg(feature = "ledger-emulator")] mod emulator_transport; pub use account::{ create_ledger_signer, get_ledger_public_key, ledger_account, verify_ledger_public_key, }; pub use hd_path::{DerivationPathParser, ParsedDerivationPath}; pub use key_locator::{LedgerKeyLocator, LedgerKeyLocatorAccount}; use starknet_rust::signers::ledger::LedgerStarknetApp; #[cfg(feature = "ledger-emulator")] pub type SncastLedgerTransport = emulator_transport::SpeculosHttpTransport; #[cfg(not(feature = "ledger-emulator"))] pub type SncastLedgerTransport = coins_ledger::transports::Ledger; pub async fn create_ledger_app() -> anyhow::Result> { #[cfg(feature = "ledger-emulator")] let app = emulator_transport::emulator_ledger_app().await?; #[cfg(not(feature = "ledger-emulator"))] let app = LedgerStarknetApp::new().await?; Ok(app) } ================================================ FILE: crates/sncast/src/helpers/mod.rs ================================================ pub mod account; pub mod artifacts; pub mod block_explorer; pub mod braavos; pub mod command; pub mod config; pub mod configuration; pub mod constants; pub mod devnet; pub mod fee; pub mod interactive; pub mod ledger; pub mod output_format; pub mod proof; pub mod rpc; pub mod scarb_utils; pub mod signer; pub mod token; ================================================ FILE: crates/sncast/src/helpers/output_format.rs ================================================ use foundry_ui::OutputFormat; #[must_use] pub fn output_format_from_json_flag(json: bool) -> OutputFormat { if json { OutputFormat::Json } else { OutputFormat::Human } } ================================================ FILE: crates/sncast/src/helpers/proof.rs ================================================ use anyhow::{Context, Result, bail}; use camino::Utf8PathBuf; use clap::Args; use starknet_types_core::felt::Felt; #[derive(Args, Debug, Clone, Default)] pub struct ProofArgs { /// Path to a file containing the proof (base64-encoded string) for the transaction. #[arg(long, requires = "proof_facts_file")] pub proof_file: Option, /// Path to a file containing proof facts (comma-separated felts) for the transaction. #[arg(long, requires = "proof_file")] pub proof_facts_file: Option, } impl ProofArgs { #[must_use] pub fn none() -> Self { Self { proof_file: None, proof_facts_file: None, } } pub fn resolve_proof(&self) -> Result> { match &self.proof_file { Some(path) => { let contents = std::fs::read_to_string(path) .with_context(|| format!("Failed to read proof file at {path}"))?; let stripped = strip_quotes(contents.trim()).trim(); if stripped.is_empty() { bail!("Proof file is empty. Expected base64 string."); } Ok(Some(stripped.to_string())) } None => Ok(None), } } pub fn resolve_proof_facts(&self) -> Result>> { match &self.proof_facts_file { Some(path) => { let contents = std::fs::read_to_string(path) .with_context(|| format!("Failed to read proof facts file at {path}"))?; let trimmed = contents.trim(); if trimmed.is_empty() { bail!("Proof facts file is empty. Expected comma separated felts."); } let felts: Vec = trimmed .split(',') .map(str::trim) .map(|s| { let stripped = strip_quotes(s); stripped .parse::() .with_context(|| format!("Failed to parse felt from '{stripped}'")) }) .collect::>>()?; Ok(Some(felts)) } None => Ok(None), } } } fn strip_quotes(value: &str) -> &str { for quote in ['"', '\''] { if let Some(stripped) = value .strip_prefix(quote) .and_then(|s| s.strip_suffix(quote)) { return stripped; } } value } #[cfg(test)] mod tests { use super::*; use std::io::Write; use tempfile::NamedTempFile; #[test] fn proof_file() { let mut file = NamedTempFile::new().unwrap(); write!(file, "SGVsbG8gd29ybGQhCg==").unwrap(); let path = Utf8PathBuf::try_from(file.path().to_path_buf()).unwrap(); let args = ProofArgs { proof_file: Some(path), ..Default::default() }; let result = args.resolve_proof().unwrap(); assert_eq!(result, Some("SGVsbG8gd29ybGQhCg==".to_string())); } #[test] fn missing_proof_file() { let missing_path = "/nonexistent/proof.txt"; let args = ProofArgs { proof_file: Some(Utf8PathBuf::from(missing_path)), ..Default::default() }; let err = args.resolve_proof().unwrap_err().to_string(); assert!(err.contains(&format!("Failed to read proof file at {missing_path}"))); } #[test] fn empty_proof_file() { let mut file = NamedTempFile::new().unwrap(); writeln!(file, " ").unwrap(); let path = Utf8PathBuf::try_from(file.path().to_path_buf()).unwrap(); let args = ProofArgs { proof_file: Some(path), ..Default::default() }; let err = args.resolve_proof().unwrap_err().to_string(); assert_eq!(err, "Proof file is empty. Expected base64 string."); } #[test] fn proof_facts_file() { let mut file = NamedTempFile::new().unwrap(); write!(file, "0x1, 0x2, 0x3").unwrap(); let path = Utf8PathBuf::try_from(file.path().to_path_buf()).unwrap(); let args = ProofArgs { proof_facts_file: Some(path), ..Default::default() }; let result = args.resolve_proof_facts().unwrap(); assert_eq!( result, Some(vec![Felt::from(1), Felt::from(2), Felt::from(3)]) ); } #[test] fn proof_facts_file_quoted() { let mut file = NamedTempFile::new().unwrap(); write!(file, "\"0x1\", '0x2', 0x3").unwrap(); let path = Utf8PathBuf::try_from(file.path().to_path_buf()).unwrap(); let args = ProofArgs { proof_facts_file: Some(path), ..Default::default() }; let result = args.resolve_proof_facts().unwrap(); assert_eq!( result, Some(vec![Felt::from(1), Felt::from(2), Felt::from(3)]) ); } #[test] fn proof_facts_malformed() { let mut file = NamedTempFile::new().unwrap(); write!(file, "0x1, invalid, 0x3").unwrap(); let path = Utf8PathBuf::try_from(file.path().to_path_buf()).unwrap(); let args = ProofArgs { proof_facts_file: Some(path), ..Default::default() }; let err = args.resolve_proof_facts().unwrap_err().to_string(); assert_eq!(err, "Failed to parse felt from 'invalid'"); } #[test] fn missing_proof_facts_file() { let missing_path = "/nonexistent/path/proof_facts.txt"; let args = ProofArgs { proof_facts_file: Some(Utf8PathBuf::from(missing_path)), ..Default::default() }; let err = args.resolve_proof_facts().unwrap_err().to_string(); assert_eq!( err, "Failed to read proof facts file at /nonexistent/path/proof_facts.txt" ); } #[test] fn empty_proof_facts_file() { let mut file = NamedTempFile::new().unwrap(); write!(file, " ").unwrap(); let path = Utf8PathBuf::try_from(file.path().to_path_buf()).unwrap(); let args = ProofArgs { proof_facts_file: Some(path), ..Default::default() }; let err = args.resolve_proof_facts().unwrap_err().to_string(); assert_eq!( err, "Proof facts file is empty. Expected comma separated felts." ); } } ================================================ FILE: crates/sncast/src/helpers/rpc.rs ================================================ use crate::helpers::configuration::CastConfig; use crate::helpers::devnet::detection; use crate::response::ui::UI; use crate::{Network, get_provider}; use anyhow::{Result, bail}; use clap::Args; use shared::consts::RPC_URL_VERSION; use shared::verify_and_warn_if_incompatible_rpc_version; use starknet_rust::providers::{JsonRpcClient, jsonrpc::HttpTransport}; use url::Url; #[derive(Args, Clone, Debug, Default)] #[group(required = false, multiple = false)] pub struct RpcArgs { /// RPC provider url address; overrides url from snfoundry.toml #[arg(short, long)] pub url: Option, /// Use predefined network with a public provider. Note that this option may result in rate limits or other unexpected behavior. /// For devnet, attempts to auto-detect running starknet-devnet instance. #[arg(long)] pub network: Option, } impl RpcArgs { pub async fn get_provider( &self, config: &CastConfig, ui: &UI, ) -> Result> { let url = self.get_url(config).await?; let provider = get_provider(&url)?; // TODO(#3959) Remove `base_ui` verify_and_warn_if_incompatible_rpc_version(&provider, url, ui.base_ui()).await?; Ok(provider) } pub async fn get_url(&self, config: &CastConfig) -> Result { match (&self.network, &self.url, &config.url, &config.network) { (Some(network), None, _, _) => self.resolve_network_url(network, config).await, (None, Some(url), _, _) => Ok(url.clone()), (None, None, Some(config_url), None) => Ok(config_url.clone()), (None, None, None, Some(config_network)) => { self.resolve_network_url(config_network, config).await } _ => bail!("Either `--network` or `--url` must be provided."), } } async fn resolve_network_url(&self, network: &Network, config: &CastConfig) -> Result { if let Some(custom_url) = config.networks.get_url(*network) { Ok(custom_url.clone()) } else { network.url(&FreeProvider::semi_random()).await } } } pub enum FreeProvider { Zan, } impl FreeProvider { #[must_use] pub fn semi_random() -> Self { Self::Zan } #[must_use] pub fn mainnet_rpc(&self) -> Url { match self { FreeProvider::Zan => Url::parse(&format!( "https://api.zan.top/public/starknet-mainnet/rpc/{RPC_URL_VERSION}" )) .expect("Failed to parse URL"), } } #[must_use] pub fn sepolia_rpc(&self) -> Url { match self { FreeProvider::Zan => Url::parse(&format!( "https://api.zan.top/public/starknet-sepolia/rpc/{RPC_URL_VERSION}" )) .expect("Failed to parse URL"), } } } impl Network { pub async fn url(self, provider: &FreeProvider) -> Result { match self { Network::Mainnet => Ok(Self::free_mainnet_rpc(provider)), Network::Sepolia => Ok(Self::free_sepolia_rpc(provider)), Network::Devnet => Self::devnet_rpc(provider).await, } } fn free_mainnet_rpc(provider: &FreeProvider) -> Url { provider.mainnet_rpc() } fn free_sepolia_rpc(provider: &FreeProvider) -> Url { provider.sepolia_rpc() } async fn devnet_rpc(_provider: &FreeProvider) -> Result { detection::detect_devnet_url() .await .map_err(|e| anyhow::anyhow!(e)) } } #[must_use] pub fn generate_network_flag(rpc_args: &RpcArgs, config: &CastConfig) -> String { if let Some(network) = &rpc_args.network { format!("--network {network}") } else if let Some(rpc_url) = &rpc_args.url { format!("--url {rpc_url}") } else if config.url.is_some() || config.network.is_some() { // Since url or network is defined in config, no need to pass any flag String::new() } else { unreachable!( "`--url` or `--network` must be provided or one of url or network field must be set in snfoundry.toml" ); } } #[cfg(test)] mod tests { use super::*; use semver::Version; use shared::rpc::is_expected_version; use starknet_rust::providers::Provider; #[tokio::test] async fn test_mainnet_url_happy_case() { let provider = get_provider(&Network::free_mainnet_rpc(&FreeProvider::Zan)).unwrap(); let spec_version = provider.spec_version().await.unwrap(); assert!(is_expected_version(&Version::parse(&spec_version).unwrap())); } #[tokio::test] async fn test_sepolia_url_happy_case() { let provider = get_provider(&Network::free_sepolia_rpc(&FreeProvider::Zan)).unwrap(); let spec_version = provider.spec_version().await.unwrap(); assert!(is_expected_version(&Version::parse(&spec_version).unwrap())); } #[tokio::test] async fn test_custom_network_url_from_config() { let mut config = CastConfig::default(); config.networks.mainnet = Some(Url::parse("https://starknet-mainnet.infura.io/v3/custom-api-key").unwrap()); config.networks.sepolia = Some(Url::parse("https://starknet-sepolia.g.alchemy.com/v2/custom-api-key").unwrap()); let rpc_args = RpcArgs { url: None, network: Some(Network::Mainnet), }; let url = rpc_args.get_url(&config).await.unwrap(); assert_eq!( url, Url::parse("https://starknet-mainnet.infura.io/v3/custom-api-key").unwrap() ); } #[tokio::test] async fn test_fallback_to_default_network_url() { let config = CastConfig::default(); let rpc_args = RpcArgs { url: None, network: Some(Network::Mainnet), }; let url = rpc_args.get_url(&config).await.unwrap(); assert_eq!(url, Network::free_mainnet_rpc(&FreeProvider::Zan).clone()); } } ================================================ FILE: crates/sncast/src/helpers/scarb_utils.rs ================================================ use crate::helpers::artifacts::CastStarknetContractArtifacts; use anyhow::{Context, Result, anyhow}; use camino::{Utf8Path, Utf8PathBuf}; use foundry_ui::{UI, components::warning::WarningMessage}; use scarb_api::metadata::{MetadataError, MetadataOpts, metadata_for_dir, metadata_with_opts}; use scarb_api::{ CompilationOpts, ScarbCommand, ScarbCommandError, ensure_scarb_available, get_contracts_artifacts_and_source_sierra_paths, metadata::{Metadata, PackageMetadata}, target_dir_for_workspace, }; use scarb_ui::args::PackagesFilter; use shared::command::CommandExt; use std::collections::HashMap; use std::str::FromStr; pub fn get_scarb_manifest() -> Result { get_scarb_manifest_for(<&Utf8Path>::from(".")) } pub fn get_scarb_manifest_for(dir: &Utf8Path) -> Result { ensure_scarb_available()?; let output = ScarbCommand::new() .current_dir(dir) .arg("manifest-path") .command() .output_checked() .context("Failed to execute the `scarb manifest-path` command")?; let output_str = String::from_utf8(output.stdout) .context("`scarb manifest-path` command failed to provide valid output")?; let path = Utf8PathBuf::from_str(output_str.trim()) .context("`scarb manifest-path` failed. Invalid location returned")?; Ok(path) } pub fn get_scarb_metadata(manifest_path: &Utf8PathBuf) -> Result { metadata_with_opts(MetadataOpts { current_dir: Some( manifest_path .parent() .expect("manifest should have parent") .into(), ), no_deps: true, ..MetadataOpts::default() }) } pub fn get_scarb_metadata_with_deps( manifest_path: &Utf8PathBuf, ) -> Result { metadata_for_dir(manifest_path.parent().expect("manifest should have parent")) } pub fn get_cairo_version(manifest_path: &Utf8PathBuf) -> Result { let scarb_metadata = get_scarb_metadata(manifest_path)?; Ok(scarb_metadata.app_version_info.cairo.version.to_string()) } pub fn assert_manifest_path_exists() -> Result { let manifest_path = get_scarb_manifest().expect("Failed to obtain manifest path from scarb"); if !manifest_path.exists() { return Err(anyhow!( "Path to Scarb.toml manifest does not exist = {manifest_path}" )); } Ok(manifest_path) } fn get_package_metadata_by_name<'a>( metadata: &'a Metadata, package_name: &str, ) -> Result<&'a PackageMetadata> { metadata .packages .iter() .find(|package| package.name == package_name) .ok_or(anyhow!( "Package {} not found in scarb metadata", &package_name )) } fn get_default_package_metadata(metadata: &Metadata) -> Result<&PackageMetadata> { match metadata.packages.iter().collect::>().as_slice() { [package] => Ok(package), [] => Err(anyhow!("No package found in scarb metadata")), _ => Err(anyhow!( "More than one package found in scarb metadata - specify package using --package flag" )), } } pub fn get_package_metadata( manifest_path: &Utf8PathBuf, package_name: &Option, ) -> Result { let metadata = get_scarb_metadata(manifest_path)?; match &package_name { Some(package_name) => Ok(get_package_metadata_by_name(&metadata, package_name)?.clone()), None => Ok(get_default_package_metadata(&metadata)?.clone()), } } pub struct BuildConfig { pub scarb_toml_path: Utf8PathBuf, pub json: bool, pub profile: String, } pub fn build( package: &PackageMetadata, config: &BuildConfig, default_profile: &str, ) -> Result<(), ScarbCommandError> { let filter = PackagesFilter::generate_for::([package].into_iter()); let mut cmd = ScarbCommand::new_with_stdio(); let metadata = get_scarb_metadata_with_deps(&config.scarb_toml_path).expect("Failed to obtain metadata"); let profile = if metadata.profiles.contains(&config.profile) { &config.profile } else { default_profile }; cmd.arg("--profile") .arg(profile) .arg("build") .manifest_path(&config.scarb_toml_path) .packages_filter(filter); if config.json { cmd.json(); } cmd.run() } pub fn build_and_load_artifacts( package: &PackageMetadata, config: &BuildConfig, build_for_script: bool, ui: &UI, ) -> Result> { // TODO (#2042): Remove this logic, always use release as default let default_profile = if build_for_script { "dev" } else { "release" }; build(package, config, default_profile) .map_err(|e| anyhow!(format!("Failed to build using scarb; {e}")))?; let metadata = get_scarb_metadata_with_deps(&config.scarb_toml_path)?; let target_dir = target_dir_for_workspace(&metadata); if metadata.profiles.contains(&config.profile) { Ok(get_contracts_artifacts_and_source_sierra_paths( &target_dir.join(&config.profile), package, ui, CompilationOpts::default() ).context("Failed to load artifacts. Make sure you have enabled sierra code generation in Scarb.toml")? .into_iter() .map(|(name, (artifacts, _))| (name, CastStarknetContractArtifacts { sierra: artifacts.sierra, casm: serde_json::to_string(&artifacts.casm).expect("valid serialization") })) .collect()) } else { let profile = &config.profile; ui.println(&WarningMessage::new(&format!( "Profile {profile} does not exist in scarb, using '{default_profile}' profile." ))); Ok(get_contracts_artifacts_and_source_sierra_paths( &target_dir.join(default_profile), package, ui, CompilationOpts::default(), ).context("Failed to load artifacts. Make sure you have enabled sierra code generation in Scarb.toml")? .into_iter() .map(|(name, (artifacts, _))| (name, CastStarknetContractArtifacts { sierra: artifacts.sierra, casm: serde_json::to_string(&artifacts.casm).expect("valid serialization") })) .collect()) } } #[cfg(test)] mod tests { use crate::helpers::scarb_utils::{get_package_metadata, get_scarb_metadata}; #[test] fn test_get_scarb_metadata() { let metadata = get_scarb_metadata(&"tests/data/contracts/constructor_with_params/Scarb.toml".into()); assert!(metadata.is_ok()); } #[test] fn test_get_package_metadata_happy_default() { let metadata = get_package_metadata( &"tests/data/contracts/constructor_with_params/Scarb.toml".into(), &None, ) .unwrap(); assert_eq!(metadata.name, "constructor_with_params"); } #[test] fn test_get_package_metadata_happy_by_name() { let metadata = get_package_metadata( &"tests/data/contracts/multiple_packages/Scarb.toml".into(), &Some("package2".into()), ) .unwrap(); assert_eq!(metadata.name, "package2"); } #[test] #[should_panic( expected = "More than one package found in scarb metadata - specify package using --package flag" )] fn test_get_package_metadata_more_than_one_default() { get_package_metadata( &"tests/data/contracts/multiple_packages/Scarb.toml".into(), &None, ) .unwrap(); } #[test] #[should_panic(expected = "Package whatever not found in scarb metadata")] fn test_get_package_metadata_no_such_package() { let metadata = get_package_metadata( &"tests/data/contracts/multiple_packages/Scarb.toml".into(), &Some("whatever".into()), ) .unwrap(); assert_eq!(metadata.name, "package2"); } } ================================================ FILE: crates/sncast/src/helpers/signer.rs ================================================ use crate::helpers::ledger; use anyhow::{Result, bail}; use camino::Utf8PathBuf; use serde::{Deserialize, Serialize}; use starknet_rust::{ accounts::SingleOwnerAccount, providers::jsonrpc::{HttpTransport, JsonRpcClient}, signers::{DerivationPath, LedgerSigner, LocalWallet}, }; use starknet_types_core::felt::Felt; /// Represents the type of signer stored in the accounts file // Uses `untagged` + `flatten` for backward compatibility with the existing accounts file format. // Downside: deserialization errors are less descriptive than with tagged variants, // and field name collisions across variants would silently misbehave. #[derive(Deserialize, Serialize, Clone, Debug)] #[serde(untagged)] pub enum SignerType { Local { private_key: Felt }, Ledger { ledger_path: DerivationPath }, } impl SignerType { #[must_use] pub fn private_key(&self) -> Option { match self { SignerType::Local { private_key } => Some(*private_key), SignerType::Ledger { .. } => None, } } #[must_use] pub fn ledger_path(&self) -> Option<&DerivationPath> { match self { SignerType::Ledger { ledger_path } => Some(ledger_path), SignerType::Local { .. } => None, } } } #[derive(Debug)] /// Represents the `SingleOwnerAccount` variant with either `LocalWallet` or `LedgerSigner` as signer pub enum AccountVariant<'a> { LocalWallet(SingleOwnerAccount<&'a JsonRpcClient, LocalWallet>), Ledger( SingleOwnerAccount< &'a JsonRpcClient, LedgerSigner, >, ), } impl AccountVariant<'_> { #[must_use] pub fn address(&self) -> Felt { use starknet_rust::accounts::Account; match self { AccountVariant::LocalWallet(account) => account.address(), AccountVariant::Ledger(account) => account.address(), } } #[must_use] pub fn chain_id(&self) -> Felt { use starknet_rust::accounts::Account; match self { AccountVariant::LocalWallet(account) => account.chain_id(), AccountVariant::Ledger(account) => account.chain_id(), } } } #[macro_export] macro_rules! with_account { ($variant:expr, |$account:ident| $body:expr) => { match $variant { &$crate::AccountVariant::LocalWallet(ref $account) => $body, &$crate::AccountVariant::Ledger(ref $account) => $body, } }; } /// Represents the source of the signer for account operations #[derive(Debug, Clone, Default)] pub enum SignerSource { /// Use a keystore file at the given path Keystore(Utf8PathBuf), /// Use a Ledger device with the given derivation path Ledger(DerivationPath), /// Use the accounts file (default) #[default] AccountsFile, } impl SignerSource { pub fn new(keystore: Option, signer_type: Option<&SignerType>) -> Result { let ledger_path = signer_type.and_then(SignerType::ledger_path); match (keystore, ledger_path) { (Some(path), None) => Ok(SignerSource::Keystore(path)), (None, Some(path)) => Ok(SignerSource::Ledger(path.clone())), (None, None) => Ok(SignerSource::AccountsFile), (Some(_), Some(_)) => { bail!("keystore and ledger cannot be used together") } } } } ================================================ FILE: crates/sncast/src/helpers/token.rs ================================================ use clap::ValueEnum; use serde::Serialize; use starknet_rust::macros::felt; use starknet_types_core::felt::Felt; const STRK_CONTRACT_ADDRESS: Felt = felt!("0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d"); const ETH_CONTRACT_ADDRESS: Felt = felt!("0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"); #[derive(Default, Clone, Copy, Debug, ValueEnum, strum_macros::Display)] #[strum(serialize_all = "lowercase")] pub enum Token { #[default] Strk, Eth, } impl Token { #[must_use] pub fn contract_address(&self) -> Felt { match self { Token::Strk => STRK_CONTRACT_ADDRESS, Token::Eth => ETH_CONTRACT_ADDRESS, } } #[must_use] pub fn as_token_unit(&self) -> TokenUnit { match self { Token::Strk => TokenUnit::Fri, Token::Eth => TokenUnit::Wei, } } } // Smallest non-divisible unit of the token. #[derive(Serialize, Clone, Copy, Debug, ValueEnum, strum_macros::Display)] #[strum(serialize_all = "lowercase")] #[serde(rename_all = "lowercase")] pub enum TokenUnit { Fri, Wei, } ================================================ FILE: crates/sncast/src/lib.rs ================================================ use crate::helpers::account::{check_account_exists, get_account_from_devnet, is_devnet_account}; use crate::helpers::configuration::CastConfig; use crate::helpers::constants::{DEFAULT_STATE_FILE_SUFFIX, WAIT_RETRY_INTERVAL, WAIT_TIMEOUT}; use crate::helpers::rpc::RpcArgs; use crate::response::errors::SNCastProviderError; use anyhow::{Context, Error, Result, anyhow, bail}; use camino::Utf8PathBuf; use clap::ValueEnum; use conversions::serde::serialize::CairoSerialize; use helpers::constants::{KEYSTORE_PASSWORD_ENV_VAR, UDC_ADDRESS}; use rand::RngCore; use rand::rngs::OsRng; use response::errors::SNCastStarknetError; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use serde_json::{Deserializer, Value}; use shared::rpc::create_rpc_client; use starknet_rust::accounts::{AccountFactory, AccountFactoryError}; use starknet_rust::core::types::{ BlockId, BlockTag, BlockTag::{Latest, PreConfirmed}, ContractClass, ContractErrorData, StarknetError::{ClassHashNotFound, ContractNotFound, TransactionHashNotFound}, }; use starknet_rust::core::types::{ContractExecutionError, ExecutionResult}; use starknet_rust::core::utils::UdcUniqueness::{NotUnique, Unique}; use starknet_rust::core::utils::{UdcUniqueSettings, UdcUniqueness}; use starknet_rust::{ accounts::{ExecutionEncoding, SingleOwnerAccount}, providers::{ Provider, ProviderError, ProviderError::StarknetError, jsonrpc::{HttpTransport, JsonRpcClient}, }, signers::{DerivationPath, LocalWallet, SigningKey}, }; use starknet_types_core::felt::Felt; use std::collections::HashMap; use std::fmt::Display; use std::str::FromStr; use std::thread::sleep; use std::time::Duration; use std::{env, fs}; use thiserror::Error; use url::Url; pub mod helpers; pub mod response; pub mod state; use crate::helpers::ledger; use crate::response::ui::UI; use conversions::byte_array::ByteArray; use foundry_ui::components::warning::WarningMessage; pub use helpers::signer::{AccountVariant, SignerSource, SignerType}; pub type NestedMap = HashMap>; #[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, ValueEnum)] #[serde(rename_all = "lowercase")] pub enum AccountType { #[serde(rename = "open_zeppelin")] OpenZeppelin, Argent, Ready, Braavos, } impl FromStr for AccountType { type Err = anyhow::Error; fn from_str(s: &str) -> Result { match s { "open_zeppelin" | "open-zeppelin" | "oz" => Ok(AccountType::OpenZeppelin), "argent" => Ok(AccountType::Argent), "ready" => Ok(AccountType::Ready), "braavos" => Ok(AccountType::Braavos), account_type => Err(anyhow!("Invalid account type = {account_type}")), } } } impl Display for AccountType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{self:?}") } } pub const MAINNET: Felt = Felt::from_hex_unchecked(const_hex::const_encode::<7, true>(b"SN_MAIN").as_str()); pub const SEPOLIA: Felt = Felt::from_hex_unchecked(const_hex::const_encode::<10, true>(b"SN_SEPOLIA").as_str()); #[derive(ValueEnum, Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum Network { Mainnet, Sepolia, Devnet, } impl Display for Network { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Network::Mainnet => write!(f, "mainnet"), Network::Sepolia => write!(f, "sepolia"), Network::Devnet => write!(f, "devnet"), } } } impl TryFrom for Network { type Error = anyhow::Error; fn try_from(value: Felt) -> std::result::Result { if value == MAINNET { Ok(Network::Mainnet) } else if value == SEPOLIA { Ok(Network::Sepolia) } else { bail!("Given network is neither Mainnet nor Sepolia") } } } #[derive(Deserialize, Serialize, Clone, Debug)] pub struct AccountData { pub public_key: Felt, pub address: Option, pub salt: Option, pub deployed: Option, pub class_hash: Option, pub legacy: Option, #[serde(default, rename(serialize = "type", deserialize = "type"))] pub account_type: Option, #[serde(flatten)] pub signer_type: SignerType, } #[derive(Clone, Copy)] pub struct WaitForTx { pub wait: bool, pub wait_params: ValidatedWaitParams, pub show_ui_outputs: bool, } #[derive(Deserialize, Serialize, Clone, Debug, Copy, PartialEq)] pub struct ValidatedWaitParams { #[serde(default)] timeout: u16, #[serde( default, rename(serialize = "retry-interval", deserialize = "retry-interval") )] retry_interval: u8, } impl ValidatedWaitParams { #[must_use] pub fn new(retry_interval: u8, timeout: u16) -> Self { assert!( !(retry_interval == 0 || timeout == 0 || u16::from(retry_interval) > timeout), "Invalid values for retry_interval and/or timeout!" ); Self { timeout, retry_interval, } } #[must_use] pub fn get_retries(&self) -> u16 { self.timeout / u16::from(self.retry_interval) } #[must_use] pub fn remaining_time(&self, steps_done: u16) -> u16 { steps_done * u16::from(self.retry_interval) } #[must_use] pub fn get_retry_interval(&self) -> u8 { self.retry_interval } #[must_use] pub fn get_timeout(&self) -> u16 { self.timeout } } impl Default for ValidatedWaitParams { fn default() -> Self { Self::new(WAIT_RETRY_INTERVAL, WAIT_TIMEOUT) } } pub fn get_provider(url: &Url) -> Result> { create_rpc_client(url) } pub async fn get_chain_id(provider: &JsonRpcClient) -> Result { provider .chain_id() .await .context("Failed to fetch chain_id") } pub fn get_keystore_password(env_var: &str) -> std::io::Result { match env::var(env_var) { Ok(password) => Ok(password), _ => rpassword::prompt_password("Enter password: "), } } #[must_use] pub fn chain_id_to_network_name(chain_id: Felt) -> String { let decoded = decode_chain_id(chain_id); match &decoded[..] { "SN_MAIN" => "alpha-mainnet".into(), "SN_SEPOLIA" => "alpha-sepolia".into(), "SN_INTEGRATION_SEPOLIA" => "alpha-integration-sepolia".into(), _ => decoded, } } #[must_use] pub fn decode_chain_id(chain_id: Felt) -> String { let non_zero_bytes: Vec = chain_id .to_bytes_be() .iter() .copied() .filter(|&byte| byte != 0) .collect(); String::from_utf8(non_zero_bytes).unwrap_or_default() } pub async fn get_nonce( provider: &JsonRpcClient, block_id: &str, address: Felt, ) -> Result { provider .get_nonce( get_block_id(block_id).context("Failed to obtain block id")?, address, ) .await .context("Failed to get a nonce") } pub async fn get_account<'a>( config: &'a CastConfig, provider: &'a JsonRpcClient, rpc_args: &RpcArgs, ui: &UI, ) -> Result> { let chain_id = get_chain_id(provider).await?; let network_name = chain_id_to_network_name(chain_id); let account = &config.account; let is_devnet_account = is_devnet_account(account); if is_devnet_account && let Some(network) = rpc_args.network && (network == Network::Mainnet || network == Network::Sepolia) { bail!(format!( "Devnet accounts cannot be used with `--network {network}`" )); } let accounts_file = &config.accounts_file; let exists_in_accounts_file = check_account_exists(account, &network_name, accounts_file)?; match (is_devnet_account, exists_in_accounts_file) { (true, true) => { ui.print_warning(WarningMessage::new(format!( "Using account {account} from accounts file {accounts_file}. \ To use an inbuilt devnet account, please rename your existing account or use an account with a different number." ))); ui.print_blank_line(); get_account_from_accounts_file( account, accounts_file, provider, config.keystore.as_ref(), ui, ) .await } (true, false) => { let url = rpc_args .get_url(config) .await .context("Failed to get url")?; let local_account = get_account_from_devnet(account, provider, &url).await?; Ok(AccountVariant::LocalWallet(local_account)) } _ => { get_account_from_accounts_file( account, accounts_file, provider, config.keystore.as_ref(), ui, ) .await } } } pub async fn get_account_from_accounts_file<'a>( account: &str, accounts_file: &Utf8PathBuf, provider: &'a JsonRpcClient, keystore: Option<&Utf8PathBuf>, ui: &UI, ) -> Result> { let chain_id = get_chain_id(provider).await?; let account_data = if let Some(keystore) = keystore { get_account_data_from_keystore(account, keystore)? } else { get_account_data_from_accounts_file(account, chain_id, accounts_file)? }; if let SignerSource::Ledger(ledger_path) = SignerSource::new(keystore.cloned(), Some(&account_data.signer_type))? { return build_ledger_account(ledger_path, account_data, chain_id, provider, ui).await; } let account = build_account(account_data, chain_id, provider).await?; Ok(AccountVariant::LocalWallet(account)) } pub async fn get_contract_class( class_hash: Felt, provider: &JsonRpcClient, ) -> Result { let result = provider .get_class(BlockId::Tag(BlockTag::Latest), class_hash) .await; if let Err(ProviderError::StarknetError(ClassHashNotFound)) = result { // Imitate error thrown on chain to achieve particular error message (Issue #2554) let artificial_transaction_revert_error = SNCastProviderError::StarknetError( SNCastStarknetError::ContractError(ContractErrorData { revert_error: ContractExecutionError::Message(format!( "Class with hash {class_hash:#x} is not declared" )), }), ); return Err(handle_rpc_error(artificial_transaction_revert_error)); } result.map_err(handle_rpc_error) } async fn build_account( account_data: AccountData, chain_id: Felt, provider: &JsonRpcClient, ) -> Result, LocalWallet>> { let private_key = account_data .signer_type .private_key() .context("Private key not found")?; let signer = LocalWallet::from(SigningKey::from_secret_scalar(private_key)); let address = account_data .address .context("Failed to get account address")?; verify_account_address(address, chain_id, provider).await?; let class_hash = account_data.class_hash; let account_encoding = get_account_encoding(account_data.legacy, class_hash, address, provider).await?; let mut account = SingleOwnerAccount::new(provider, signer, address, chain_id, account_encoding); account.set_block_id(BlockId::Tag(PreConfirmed)); Ok(account) } async fn build_ledger_account<'a>( ledger_path: DerivationPath, account_data: AccountData, chain_id: Felt, provider: &'a JsonRpcClient, ui: &UI, ) -> Result> { let address = account_data .address .context("Failed to get account address")?; verify_account_address(address, chain_id, provider).await?; let encoding = get_account_encoding( account_data.legacy, account_data.class_hash, address, provider, ) .await?; let account = ledger::ledger_account(&ledger_path, address, chain_id, encoding, provider, ui).await?; Ok(AccountVariant::Ledger(account)) } async fn verify_account_address( address: Felt, chain_id: Felt, provider: &JsonRpcClient, ) -> Result<()> { match provider .get_nonce(BlockId::Tag(PreConfirmed), address) .await { Ok(_) => Ok(()), Err(error) => { if let StarknetError(ContractNotFound) = error { let decoded_chain_id = decode_chain_id(chain_id); Err(anyhow!( "Account with address {address:#x} not found on network {decoded_chain_id}" )) } else { Err(handle_rpc_error(error)) } } } } pub async fn check_class_hash_exists( provider: &JsonRpcClient, class_hash: Felt, ) -> Result<()> { match provider .get_class(BlockId::Tag(BlockTag::Latest), class_hash) .await { Ok(_) => Ok(()), Err(err) => match err { StarknetError(ClassHashNotFound) => Err(anyhow!( "Class with hash {class_hash:#x} is not declared, try using --class-hash with a hash of the declared class" )), _ => Err(handle_rpc_error(err)), }, } } pub fn get_account_data_from_keystore( account: &str, keystore_path: &Utf8PathBuf, ) -> Result { check_keystore_and_account_files_exist(keystore_path, account)?; let path_to_account = Utf8PathBuf::from(account); let private_key = SigningKey::from_keystore( keystore_path, get_keystore_password(KEYSTORE_PASSWORD_ENV_VAR)?.as_str(), )? .secret_scalar(); let account_info: Value = read_and_parse_json_file(&path_to_account)?; let parse_to_felt = |pointer: &str| -> Option { get_string_value_from_json(&account_info, pointer).and_then(|value| value.parse().ok()) }; let address = parse_to_felt("/deployment/address"); let class_hash = parse_to_felt("/deployment/class_hash"); let salt = parse_to_felt("/deployment/salt"); let deployed = get_string_value_from_json(&account_info, "/deployment/status") .map(|status| status == "deployed"); let legacy = account_info .pointer("/variant/legacy") .and_then(Value::as_bool); let account_type = get_string_value_from_json(&account_info, "/variant/type") .and_then(|account_type| account_type.parse().ok()); let public_key = match account_type.context("Failed to get type key")? { AccountType::Argent | AccountType::Ready => parse_to_felt("/variant/owner"), AccountType::OpenZeppelin => parse_to_felt("/variant/public_key"), AccountType::Braavos => get_braavos_account_public_key(&account_info)?, } .context("Failed to get public key from account JSON file")?; Ok(AccountData { public_key, address, salt, deployed, class_hash, legacy, account_type, signer_type: SignerType::Local { private_key }, }) } fn get_braavos_account_public_key(account_info: &Value) -> Result> { get_string_value_from_json(account_info, "/variant/multisig/status") .filter(|status| status == "off") .context("Braavos accounts cannot be deployed with multisig on")?; account_info .pointer("/variant/signers") .and_then(Value::as_array) .filter(|signers| signers.len() == 1) .context("Braavos accounts can only be deployed with one seed signer")?; Ok( get_string_value_from_json(account_info, "/variant/signers/0/public_key") .and_then(|value| value.parse().ok()), ) } fn get_string_value_from_json(json: &Value, pointer: &str) -> Option { json.pointer(pointer) .and_then(Value::as_str) .map(str::to_string) } pub fn get_account_data_from_accounts_file( name: &str, chain_id: Felt, path: &Utf8PathBuf, ) -> Result { if name.is_empty() { bail!("Account name not passed nor found in snfoundry.toml") } check_account_file_exists(path)?; let accounts: HashMap> = read_and_parse_json_file(path)?; let network_name = chain_id_to_network_name(chain_id); accounts .get(&network_name) .and_then(|accounts_map| accounts_map.get(name)) .cloned() .ok_or_else(|| anyhow!("Account = {name} not found under network = {network_name}")) } pub fn read_and_parse_json_file(path: &Utf8PathBuf) -> Result where T: DeserializeOwned + Default, { let file_content = fs::read_to_string(path).with_context(|| format!("Failed to read a file = {path}"))?; if file_content.trim().is_empty() { return Ok(T::default()); } let deserializer = &mut Deserializer::from_str(&file_content); serde_path_to_error::deserialize(deserializer).map_err(|err| { let path_to_field = err.path().to_string(); anyhow!( "Failed to parse field `{path_to_field}` in file '{path}': {}", err.into_inner() ) }) } async fn get_account_encoding( legacy: Option, class_hash: Option, address: Felt, provider: &JsonRpcClient, ) -> Result { if let Some(legacy) = legacy { Ok(map_encoding(legacy)) } else { let legacy = check_if_legacy_contract(class_hash, address, provider).await?; Ok(map_encoding(legacy)) } } pub async fn check_if_legacy_contract( class_hash: Option, address: Felt, provider: &JsonRpcClient, ) -> Result { let contract_class = match class_hash { Some(class_hash) => { provider .get_class(BlockId::Tag(PreConfirmed), class_hash) .await } None => { provider .get_class_at(BlockId::Tag(PreConfirmed), address) .await } } .map_err(handle_rpc_error)?; Ok(is_legacy_contract(&contract_class)) } pub async fn get_class_hash_by_address( provider: &JsonRpcClient, address: Felt, ) -> Result { let result = provider .get_class_hash_at(BlockId::Tag(PreConfirmed), address) .await; if let Err(ProviderError::StarknetError(ContractNotFound)) = result { // Imitate error thrown on chain to achieve particular error message (Issue #2554) let artificial_transaction_revert_error = SNCastProviderError::StarknetError( SNCastStarknetError::ContractError(ContractErrorData { revert_error: ContractExecutionError::Message(format!( "Requested contract address {address:#x} is not deployed", )), }), ); return Err(handle_rpc_error(artificial_transaction_revert_error)); } result.map_err(handle_rpc_error).with_context(|| { format!("Couldn't retrieve class hash of a contract with address {address:#x}") }) } #[must_use] pub fn is_legacy_contract(contract_class: &ContractClass) -> bool { match contract_class { ContractClass::Legacy(_) => true, ContractClass::Sierra(_) => false, } } fn map_encoding(legacy: bool) -> ExecutionEncoding { if legacy { ExecutionEncoding::Legacy } else { ExecutionEncoding::New } } pub fn get_block_id(value: &str) -> Result { match value { "pre_confirmed" => Ok(BlockId::Tag(PreConfirmed)), "latest" => Ok(BlockId::Tag(Latest)), _ if value.starts_with("0x") => Ok(BlockId::Hash(Felt::from_hex(value)?)), _ => match value.parse::() { Ok(value) => Ok(BlockId::Number(value)), Err(_) => Err(anyhow::anyhow!( "Incorrect value passed for block_id = {value}. Possible values are `pre_confirmed`, `latest`, block hash (hex) and block number (u64)" )), }, } } #[derive(Debug, CairoSerialize)] pub struct ErrorData { pub data: ByteArray, } #[derive(Error, Debug, CairoSerialize)] pub enum TransactionError { #[error("Transaction has been reverted = {}", .0.data)] Reverted(ErrorData), } #[derive(Error, Debug, CairoSerialize)] pub enum WaitForTransactionError { #[error(transparent)] TransactionError(TransactionError), #[error("sncast timed out while waiting for transaction to succeed")] TimedOut, #[error(transparent)] ProviderError(#[from] SNCastProviderError), } pub async fn wait_for_tx( provider: &JsonRpcClient, tx_hash: Felt, wait_params: ValidatedWaitParams, ui: Option<&UI>, ) -> Result { ui.inspect(|ui| ui.print_notification(format!("Transaction hash: {tx_hash:#x}"))); let retries = wait_params.get_retries(); for i in (1..retries).rev() { match provider.get_transaction_status(tx_hash).await { Ok(starknet_rust::core::types::TransactionStatus::PreConfirmed( ExecutionResult::Reverted { reason }, )) => { return Err(WaitForTransactionError::TransactionError( TransactionError::Reverted(ErrorData { data: ByteArray::from(reason.as_str()), }), )); } Ok( starknet_rust::core::types::TransactionStatus::AcceptedOnL2(execution_status) | starknet_rust::core::types::TransactionStatus::AcceptedOnL1(execution_status), ) => { return match execution_status { ExecutionResult::Succeeded => Ok("Transaction accepted".to_string()), ExecutionResult::Reverted { reason } => { Err(WaitForTransactionError::TransactionError( TransactionError::Reverted(ErrorData { data: ByteArray::from(reason.as_str()), }), )) } }; } Ok(starknet_rust::core::types::TransactionStatus::PreConfirmed( ExecutionResult::Succeeded, )) => { ui.inspect(|ui| { let remaining_time = wait_params.remaining_time(i); ui.print_notification("Transaction status: PRE_CONFIRMED".to_string()); ui.print_notification(format!( "Waiting for transaction to be accepted ({i} retries / {remaining_time}s left until timeout)" )); }); } Ok( starknet_rust::core::types::TransactionStatus::Received | starknet_rust::core::types::TransactionStatus::Candidate, ) | Err(StarknetError(TransactionHashNotFound)) => { ui.inspect(|ui| { let remaining_time = wait_params.remaining_time(i); ui.print_notification(format!( "Waiting for transaction to be accepted ({i} retries / {remaining_time}s left until timeout)" )); }); } Err(ProviderError::RateLimited) => { ui.inspect(|ui| { ui.print_notification( "Request rate limited while waiting for transaction to be accepted" .to_string(), ); }); sleep(Duration::from_secs(wait_params.get_retry_interval().into())); } Err(err) => return Err(WaitForTransactionError::ProviderError(err.into())), } sleep(Duration::from_secs(wait_params.get_retry_interval().into())); } Err(WaitForTransactionError::TimedOut) } #[must_use] pub fn handle_rpc_error(error: impl Into) -> Error { let err: SNCastProviderError = error.into(); err.into() } #[must_use] pub fn handle_account_factory_error(err: AccountFactoryError) -> anyhow::Error where T: AccountFactory + Sync, { match err { AccountFactoryError::Provider(error) => handle_rpc_error(error), error => anyhow!(error.to_string()), } } pub async fn handle_wait_for_tx( provider: &JsonRpcClient, transaction_hash: Felt, return_value: T, wait_config: WaitForTx, ui: &UI, ) -> Result { if wait_config.wait { return match wait_for_tx( provider, transaction_hash, wait_config.wait_params, wait_config.show_ui_outputs.then_some(ui), ) .await { Ok(_) => Ok(return_value), Err(error) => Err(error), }; } Ok(return_value) } pub fn check_account_file_exists(accounts_file_path: &Utf8PathBuf) -> Result<()> { if !accounts_file_path.exists() { bail! {"Accounts file = {accounts_file_path} does not exist! If you do not have an account create one with `account create` command \ or if you're using a custom accounts file, make sure to supply correct path to it with `--accounts-file` argument." } } Ok(()) } pub fn check_keystore_and_account_files_exist( keystore_path: &Utf8PathBuf, account: &str, ) -> Result<()> { if !keystore_path.exists() { bail!("Failed to find keystore file"); } if account.is_empty() { bail!("Argument `--account` must be passed and be a path when using `--keystore`"); } let path_to_account = Utf8PathBuf::from(account); if !path_to_account.exists() { bail!( "File containing the account does not exist: When using `--keystore` argument, the `--account` argument should be a path to the starkli JSON account file" ); } Ok(()) } #[must_use] pub fn extract_or_generate_salt(salt: Option) -> Felt { salt.unwrap_or(Felt::from(OsRng.next_u64())) } #[must_use] pub fn udc_uniqueness(unique: bool, account_address: Felt) -> UdcUniqueness { if unique { Unique(UdcUniqueSettings { deployer_address: account_address, udc_contract_address: UDC_ADDRESS, }) } else { NotUnique } } pub fn apply_optional T>(initial: T, option: Option, function: F) -> T { match option { Some(value) => function(initial, value), None => initial, } } #[macro_export] macro_rules! apply_optional_fields { ($initial:expr, $( $option:expr => $setter:expr ),* ) => { { let mut value = $initial; $( value = ::sncast::apply_optional(value, $option, $setter); )* value } }; } #[must_use] pub fn get_default_state_file_name(script_name: &str, chain_id: &str) -> String { format!("{script_name}_{chain_id}_{DEFAULT_STATE_FILE_SUFFIX}") } #[cfg(test)] mod tests { use crate::helpers::constants::KEYSTORE_PASSWORD_ENV_VAR; use crate::{ AccountType, chain_id_to_network_name, extract_or_generate_salt, get_account_data_from_accounts_file, get_account_data_from_keystore, get_block_id, udc_uniqueness, }; use camino::Utf8PathBuf; use conversions::string::IntoHexStr; use starknet_rust::core::types::{ BlockId, BlockTag::{Latest, PreConfirmed}, Felt, }; use starknet_rust::core::utils::UdcUniqueSettings; use starknet_rust::core::utils::UdcUniqueness::{NotUnique, Unique}; use std::env; #[test] fn test_get_block_id() { let pending_block = get_block_id("pre_confirmed").unwrap(); let latest_block = get_block_id("latest").unwrap(); assert_eq!(pending_block, BlockId::Tag(PreConfirmed)); assert_eq!(latest_block, BlockId::Tag(Latest)); } #[test] fn test_get_block_id_hex() { let block = get_block_id("0x0").unwrap(); assert_eq!( block, BlockId::Hash( Felt::from_hex( "0x0000000000000000000000000000000000000000000000000000000000000000" ) .unwrap() ) ); } #[test] fn test_get_block_id_num() { let block = get_block_id("0").unwrap(); assert_eq!(block, BlockId::Number(0)); } #[test] fn test_get_block_id_invalid() { let block = get_block_id("mariusz").unwrap_err(); assert!(block .to_string() .contains("Incorrect value passed for block_id = mariusz. Possible values are `pre_confirmed`, `latest`, block hash (hex) and block number (u64)")); } #[test] fn test_generate_salt() { let salt = extract_or_generate_salt(None); assert!(salt >= Felt::ZERO); } #[test] fn test_extract_salt() { let salt = extract_or_generate_salt(Some(Felt::THREE)); assert_eq!(salt, Felt::THREE); } #[test] fn test_udc_uniqueness_unique() { let uniqueness = udc_uniqueness(true, Felt::ONE); assert!(matches!(uniqueness, Unique(UdcUniqueSettings { .. }))); } #[test] fn test_udc_uniqueness_not_unique() { let uniqueness = udc_uniqueness(false, Felt::ONE); assert!(matches!(uniqueness, NotUnique)); } #[test] fn test_chain_id_to_network_name() { let network_name_katana = chain_id_to_network_name(Felt::from_bytes_be_slice("KATANA".as_bytes())); let network_name_sepolia = chain_id_to_network_name(Felt::from_bytes_be_slice("SN_SEPOLIA".as_bytes())); assert_eq!(network_name_katana, "KATANA"); assert_eq!(network_name_sepolia, "alpha-sepolia"); } #[test] fn test_get_account_data_from_accounts_file() { let account = get_account_data_from_accounts_file( "user1", Felt::from_bytes_be_slice("SN_SEPOLIA".as_bytes()), &Utf8PathBuf::from("tests/data/accounts/accounts.json"), ) .unwrap(); let private_key = account .signer_type .private_key() .expect("Private key should exist"); assert_eq!( private_key.into_hex_string(), "0xffd33878eed7767e7c546ce3fc026295" ); assert_eq!( account.public_key.into_hex_string(), "0x17b62d16ee2b9b5ccd3320e2c0b234dfbdd1d01d09d0aa29ce164827cddf46a" ); assert_eq!( account.address.map(IntoHexStr::into_hex_string), Some("0xf6ecd22832b7c3713cfa7826ee309ce96a2769833f093795fafa1b8f20c48b".to_string()) ); assert_eq!( account.salt.map(IntoHexStr::into_hex_string), Some("0x14b6b215424909f34f417ddd7cbaca48de2d505d03c92467367d275e847d252".to_string()) ); assert_eq!(account.deployed, Some(true)); assert_eq!(account.class_hash, None); assert_eq!(account.legacy, None); assert_eq!(account.account_type, Some(AccountType::OpenZeppelin)); } #[test] fn test_get_account_data_from_keystore() { set_keystore_password_env(); let account = get_account_data_from_keystore( "tests/data/keystore/my_account.json", &Utf8PathBuf::from("tests/data/keystore/my_key.json"), ) .unwrap(); let private_key = account .signer_type .private_key() .expect("Private key should exist"); assert_eq!( private_key.into_hex_string(), "0x55ae34c86281fbd19292c7e3bfdfceb4" ); assert_eq!( account.public_key.into_hex_string(), "0xe2d3d7080bfc665e0060a06e8e95c3db3ff78a1fec4cc81ddc87e49a12e0a" ); assert_eq!( account.address.map(IntoHexStr::into_hex_string), Some("0xcce3217e4aea0ab738b55446b1b378750edfca617db549fda1ede28435206c".to_string()) ); assert_eq!(account.salt, None); assert_eq!(account.deployed, Some(true)); assert_eq!(account.legacy, Some(true)); assert_eq!(account.account_type, Some(AccountType::OpenZeppelin)); } #[test] fn test_get_braavos_account_from_keystore_with_multisig_on() { set_keystore_password_env(); let err = get_account_data_from_keystore( "tests/data/keystore/my_account_braavos_invalid_multisig.json", &Utf8PathBuf::from("tests/data/keystore/my_key.json"), ) .unwrap_err(); assert!( err.to_string() .contains("Braavos accounts cannot be deployed with multisig on") ); } #[test] fn test_get_braavos_account_from_keystore_multiple_signers() { set_keystore_password_env(); let err = get_account_data_from_keystore( "tests/data/keystore/my_account_braavos_multiple_signers.json", &Utf8PathBuf::from("tests/data/keystore/my_key.json"), ) .unwrap_err(); assert!( err.to_string() .contains("Braavos accounts can only be deployed with one seed signer") ); } #[test] fn test_get_account_data_wrong_chain_id() { let account = get_account_data_from_accounts_file( "user1", Felt::from_hex("0x435553544f4d5f434841494e5f4944") .expect("Failed to convert chain id from hex"), &Utf8PathBuf::from("tests/data/accounts/accounts.json"), ); let err = account.unwrap_err(); assert!( err.to_string() .contains("Account = user1 not found under network = CUSTOM_CHAIN_ID") ); } fn set_keystore_password_env() { // SAFETY: Tests run in parallel and share the same environment variables. // However, we only set this variable once to a fixed value and never modify or unset it. // The only potential issue would be if a test explicitly required this variable to be unset, // but to the best of our knowledge, no such test exists. unsafe { env::set_var(KEYSTORE_PASSWORD_ENV_VAR, "123"); }; } } ================================================ FILE: crates/sncast/src/main.rs ================================================ use std::str::FromStr; use crate::starknet_commands::declare::declare; use crate::starknet_commands::declare_from::{ContractSource, DeclareFrom}; use crate::starknet_commands::deploy::{DeployArguments, DeployCommonArgs}; use crate::starknet_commands::get::Get; use crate::starknet_commands::get::balance::Balance; use crate::starknet_commands::invoke::InvokeCommonArgs; use crate::starknet_commands::script::run_script_command; use crate::starknet_commands::utils::{self, Utils}; use crate::starknet_commands::{ account, account::Account as AccountCommand, call::Call, declare::Declare, deploy::Deploy, get::tx_status::TxStatus, invoke::Invoke, multicall::Multicall, script::Script, show_config::ShowConfig, }; use crate::starknet_commands::{get, multicall}; use anyhow::{Context, Result, bail}; use camino::Utf8PathBuf; use clap::{CommandFactory, Parser, Subcommand}; use configuration::load_config; use conversions::IntoConv; use data_transformer::transform; use foundry_ui::components::warning::WarningMessage; use mimalloc::MiMalloc; use shared::auto_completions::{Completions, generate_completions}; use sncast::helpers::command::process_command_result; use sncast::helpers::config::{combine_cast_configs, get_global_config_path}; use sncast::helpers::configuration::CastConfig; use sncast::helpers::constants::DEFAULT_ACCOUNTS_FILE; use sncast::helpers::output_format::output_format_from_json_flag; use sncast::helpers::rpc::generate_network_flag; use sncast::helpers::scarb_utils::{ BuildConfig, assert_manifest_path_exists, build_and_load_artifacts, get_package_metadata, }; use sncast::response::declare::{ AlreadyDeclaredResponse, DeclareResponse, DeclareTransactionResponse, DeployCommandMessage, }; use sncast::response::deploy::{DeployResponse, DeployResponseWithDeclare}; use sncast::response::errors::handle_starknet_command_error; use sncast::response::explorer_link::block_explorer_link_if_allowed; use sncast::response::transformed_call::transform_response; use sncast::response::ui::UI; use sncast::{ ValidatedWaitParams, WaitForTx, get_account, get_block_id, get_class_hash_by_address, get_contract_class, with_account, }; use starknet_commands::ledger::{self, Ledger}; use starknet_commands::verify::Verify; use starknet_rust::core::types::ContractClass; use starknet_rust::core::types::contract::{AbiEntry, SierraClass}; use starknet_rust::core::utils::get_selector_from_name; use starknet_rust::providers::Provider; use starknet_types_core::felt::Felt; use tokio::runtime::Runtime; mod starknet_commands; #[global_allocator] static GLOBAL: MiMalloc = MiMalloc; #[derive(Parser)] #[command( version, help_template = "\ {name} {version} {author-with-newline}{about-with-newline} Use -h for short descriptions and --help for more details. {before-help}{usage-heading} {usage} {all-args}{after-help} ", after_help = "Read the docs: https://foundry-rs.github.io/starknet-foundry/", after_long_help = "\ Read the docs: - Starknet Foundry Book: https://foundry-rs.github.io/starknet-foundry/ - Cairo Book: https://book.cairo-lang.org/ - Starknet Book: https://book.starknet.io/ - Starknet Documentation: https://docs.starknet.io/ - Scarb Documentation: https://docs.swmansion.com/scarb/docs.html Join the community: - Follow core developers on X: https://twitter.com/swmansionxyz - Get support via Telegram: https://t.me/starknet_foundry_support - Or discord: https://discord.gg/starknet-community - Or join our general chat (Telegram): https://t.me/starknet_foundry Report bugs: https://github.com/foundry-rs/starknet-foundry/issues/new/choose\ " )] #[command(about = "sncast - All-in-one tool for interacting with Starknet smart contracts", long_about = None)] #[command(name = "sncast")] struct Cli { /// Profile name in snfoundry.toml config file #[arg(short, long)] profile: Option, /// Account to be used for contract declaration; /// When using keystore (`--keystore`), this should be a path to account file /// When using accounts file, this should be an account name #[arg(short = 'a', long)] account: Option, /// Path to the file holding accounts info #[arg(short = 'f', long = "accounts-file")] accounts_file_path: Option, /// Path to keystore file; if specified, --account should be a path to starkli JSON account file #[arg(short, long)] keystore: Option, /// If passed, output will be displayed in json format #[arg(short, long)] json: bool, /// If passed, command will wait until transaction is accepted or rejected #[arg(short = 'w', long)] wait: bool, /// Adjusts the time after which --wait assumes transaction was not received or rejected #[arg(long)] wait_timeout: Option, /// Adjusts the time between consecutive attempts to fetch transaction by --wait flag #[arg(long)] wait_retry_interval: Option, #[command(subcommand)] command: Commands, } impl Cli { fn command_name(&self) -> String { match self.command { Commands::Get(_) => "get", Commands::Declare(_) => "declare", Commands::DeclareFrom(_) => "declare-from", Commands::Deploy(_) => "deploy", Commands::Call(_) => "call", Commands::Invoke(_) => "invoke", Commands::Multicall(_) => "multicall", Commands::Account(_) => "account", Commands::ShowConfig(_) => "show-config", Commands::Script(_) => "script", Commands::TxStatus(_) => "tx-status", Commands::Verify(_) => "verify", Commands::Completions(_) => "completions", Commands::Utils(_) => "utils", Commands::Balance(_) => "balance", Commands::Ledger(_) => "ledger", } .to_string() } } #[derive(Subcommand)] enum Commands { /// Get various data from the network Get(Get), /// Declare a contract Declare(Declare), /// Declare a contract by fetching it from a different Starknet instance DeclareFrom(DeclareFrom), /// Deploy a contract Deploy(Deploy), /// Call a contract Call(Call), /// Invoke a contract Invoke(Invoke), /// Execute multiple calls Multicall(Multicall), /// Create and deploy an account Account(AccountCommand), /// Show current configuration being used ShowConfig(ShowConfig), /// Run or initialize a deployment script Script(Script), /// Get the status of a transaction TxStatus(TxStatus), /// Verify a contract Verify(Verify), /// Generate completions script Completions(Completions), /// Utility commands Utils(Utils), /// Fetch balance of the account for specified token Balance(Balance), /// Interact with Ledger hardware wallet Ledger(Ledger), } #[derive(Debug, Clone, clap::Args)] #[group(multiple = false)] pub struct Arguments { /// Arguments of the called function serialized as a series of felts #[arg(short, long, value_delimiter = ' ', num_args = 1..)] pub calldata: Option>, // Arguments of the called function as a comma-separated string of Cairo expressions #[arg(long, allow_hyphen_values = true)] pub arguments: Option, } impl Arguments { fn try_into_calldata( self, contract_class: &ContractClass, selector: &Felt, ) -> Result> { if let Some(calldata) = self.calldata { calldata_to_felts(&calldata) } else { let ContractClass::Sierra(sierra_class) = contract_class else { bail!("Transformation of arguments is not available for Cairo Zero contracts") }; let abi: Vec = serde_json::from_str(sierra_class.abi.as_str()) .context("Couldn't deserialize ABI received from network")?; transform(&self.arguments.unwrap_or_default(), &abi, selector) } } } pub fn calldata_to_felts(calldata: &[String]) -> Result> { calldata .iter() .map(|data| Felt::from_str(data).with_context(|| format!("Failed to parse {data} to felt"))) .collect() } impl From for Arguments { fn from(value: DeployArguments) -> Self { let DeployArguments { constructor_calldata, arguments, } = value; Self { calldata: constructor_calldata, arguments, } } } fn init_logging() { use std::io; use std::io::IsTerminal; use tracing_log::LogTracer; use tracing_subscriber::filter::{EnvFilter, LevelFilter}; use tracing_subscriber::fmt::Layer; use tracing_subscriber::fmt::time::Uptime; use tracing_subscriber::prelude::*; let fmt_layer = Layer::new() .with_writer(io::stderr) .with_ansi(io::stderr().is_terminal()) .with_timer(Uptime::default()) .with_filter( EnvFilter::builder() .with_default_directive(LevelFilter::WARN.into()) .with_default_directive("coins_ledger=off".parse().expect("valid directive")) .with_env_var("SNCAST_LOG") .from_env_lossy(), ); LogTracer::init().expect("could not initialize log tracer"); tracing::subscriber::set_global_default(tracing_subscriber::registry().with(fmt_layer)) .expect("could not set up global logger"); } fn main() -> Result<()> { init_logging(); let cli = Cli::parse(); let output_format = output_format_from_json_flag(cli.json); let ui = UI::new(output_format); let runtime = Runtime::new().expect("Failed to instantiate Runtime"); if let Commands::Script(script) = &cli.command { run_script_command(&cli, runtime, script, &ui) } else { let config = get_cast_config(&cli, &ui)?; runtime.block_on(run_async_command(cli, config, &ui)) } } #[expect(clippy::too_many_lines)] async fn run_async_command(cli: Cli, config: CastConfig, ui: &UI) -> Result<()> { let wait_config = WaitForTx { wait: cli.wait, wait_params: config.wait_params, show_ui_outputs: true, }; match cli.command { Commands::Declare(declare) => { let provider = declare.common.rpc.get_provider(&config, ui).await?; let rpc = declare.common.rpc.clone(); let account = get_account(&config, &provider, &declare.common.rpc, ui).await?; let manifest_path = assert_manifest_path_exists()?; let package_metadata = get_package_metadata(&manifest_path, &declare.package)?; let artifacts = build_and_load_artifacts( &package_metadata, &BuildConfig { scarb_toml_path: manifest_path, json: cli.json, profile: cli.profile.unwrap_or("release".to_string()), }, false, // TODO(#3959) Remove `base_ui` ui.base_ui(), ) .expect("Failed to build contract"); let result = with_account!(&account, |account| { starknet_commands::declare::declare( declare.contract_name.clone(), declare.common.fee_args, declare.common.nonce, account, &artifacts, wait_config, false, ui, ) .await }) .map_err(handle_starknet_command_error) .map(|result| match result { DeclareResponse::Success(declare_transaction_response) => { declare_transaction_response } DeclareResponse::AlreadyDeclared(_) => { unreachable!("Argument `skip_on_already_declared` is false") } }); let block_explorer_link = block_explorer_link_if_allowed(&result, provider.chain_id().await?, &config).await; let deploy_command_message = if let Ok(response) = &result { // TODO(#3785) let contract_artifacts = artifacts .get(&declare.contract_name) .expect("Failed to get contract artifacts"); let contract_definition: SierraClass = serde_json::from_str(&contract_artifacts.sierra) .context("Failed to parse sierra artifact")?; let network_flag = generate_network_flag(&rpc, &config); Some(DeployCommandMessage::new( &contract_definition.abi, response, &config.account, &config.accounts_file, network_flag, )) } else { None }; process_command_result("declare", result, ui, block_explorer_link); if let Some(deploy_command_message) = deploy_command_message { ui.print_notification(deploy_command_message?); } Ok(()) } Commands::DeclareFrom(declare_from) => { let provider = declare_from.common.rpc.get_provider(&config, ui).await?; let contract_source = if let Some(sierra_file) = declare_from.sierra_file { ContractSource::LocalFile { sierra_path: sierra_file, } } else { let source_provider = declare_from.source_rpc.get_provider(ui).await?; let block_id = get_block_id(&declare_from.block_id)?; let class_hash = declare_from.class_hash.expect("missing class_hash"); ContractSource::Network { source_provider, class_hash, block_id, } }; let account = get_account(&config, &provider, &declare_from.common.rpc, ui).await?; let result = with_account!(&account, |account| { starknet_commands::declare_from::declare_from( contract_source, &declare_from.common, account, wait_config, false, ui, ) .await }) .map_err(handle_starknet_command_error) .map(|result| match result { DeclareResponse::Success(declare_transaction_response) => { declare_transaction_response } DeclareResponse::AlreadyDeclared(_) => { unreachable!("Argument `skip_on_already_declared` is false") } }); let block_explorer_link = block_explorer_link_if_allowed(&result, provider.chain_id().await?, &config).await; process_command_result("declare-from", result, ui, block_explorer_link); Ok(()) } Commands::Deploy(deploy) => { let Deploy { common: DeployCommonArgs { contract_identifier: identifier, arguments, package, salt, unique, }, fee_args, rpc, mut nonce, .. } = deploy; let provider = rpc.get_provider(&config, ui).await?; let account = get_account(&config, &provider, &rpc, ui).await?; let (class_hash, declare_response) = if let Some(class_hash) = identifier.class_hash { (class_hash, None) } else if let Some(contract_name) = identifier.contract_name { let manifest_path = assert_manifest_path_exists()?; let package_metadata = get_package_metadata(&manifest_path, &package)?; let artifacts = build_and_load_artifacts( &package_metadata, &BuildConfig { scarb_toml_path: manifest_path, json: cli.json, profile: cli.profile.unwrap_or("release".to_string()), }, false, // TODO(#3959) Remove `base_ui` ui.base_ui(), ) .expect("Failed to build contract"); let declare_result = with_account!(&account, |account| { declare( contract_name, fee_args.clone(), nonce, account, &artifacts, WaitForTx { wait: true, wait_params: wait_config.wait_params, show_ui_outputs: wait_config.wait, }, true, ui, ) .await }) .map_err(handle_starknet_command_error); // Increment nonce after successful declare if it was explicitly provided nonce = nonce.map(|n| n + Felt::ONE); match declare_result { Ok(DeclareResponse::AlreadyDeclared(AlreadyDeclaredResponse { class_hash, })) => (class_hash.into_(), None), Ok(DeclareResponse::Success(declare_transaction_response)) => ( declare_transaction_response.class_hash.into_(), Some(declare_transaction_response), ), Err(err) => { // TODO(#3960) This will return json output saying that `deploy` command was run // even though the invoked command was declare. process_command_result::( "deploy", Err(err), ui, None, ); return Ok(()); } } } else { unreachable!("Either `--class_hash` or `--contract_name` must be provided"); }; // safe to unwrap because "constructor" is a standardized name let selector = get_selector_from_name("constructor").unwrap(); let contract_class = get_contract_class(class_hash, &provider).await?; let arguments: Arguments = arguments.into(); let calldata = arguments.try_into_calldata(&contract_class, &selector)?; let result = with_account!(&account, |account| { starknet_commands::deploy::deploy( class_hash, &calldata, salt, unique, fee_args, nonce, account, wait_config, ui, ) .await }) .map_err(handle_starknet_command_error); let result = if let Some(declare_response) = declare_response { result.map(|r| { DeployResponse::WithDeclare(DeployResponseWithDeclare::from_responses( &r, &declare_response, )) }) } else { result.map(DeployResponse::Standard) }; let block_explorer_link = block_explorer_link_if_allowed(&result, provider.chain_id().await?, &config).await; process_command_result("deploy", result, ui, block_explorer_link); Ok(()) } Commands::Call(Call { contract_address, function, arguments, block_id, rpc, }) => { let provider = rpc.get_provider(&config, ui).await?; let block_id = get_block_id(&block_id)?; let class_hash = get_class_hash_by_address(&provider, contract_address).await?; let contract_class = get_contract_class(class_hash, &provider).await?; let selector = get_selector_from_name(&function) .context("Failed to convert entry point selector to FieldElement")?; let calldata = arguments.try_into_calldata(&contract_class, &selector)?; let result = starknet_commands::call::call( contract_address, selector, calldata, &provider, block_id.as_ref(), ) .await .map_err(handle_starknet_command_error); if let Some(transformed_result) = transform_response(&result, &contract_class, &selector) { process_command_result("call", Ok(transformed_result), ui, None); } else { process_command_result("call", result, ui, None); } Ok(()) } Commands::Invoke(invoke) => { let Invoke { common: InvokeCommonArgs { contract_address, function, arguments, }, fee_args, proof_args, rpc, nonce, .. } = invoke; let provider = rpc.get_provider(&config, ui).await?; let account = get_account(&config, &provider, &rpc, ui).await?; let selector = get_selector_from_name(&function) .context("Failed to convert entry point selector to FieldElement")?; let contract_address = contract_address.try_into_felt()?; let class_hash = get_class_hash_by_address(&provider, contract_address).await?; let contract_class = get_contract_class(class_hash, &provider).await?; let calldata = arguments.try_into_calldata(&contract_class, &selector)?; let result = with_account!(&account, |account| { starknet_commands::invoke::invoke( contract_address, calldata, nonce, fee_args, proof_args, selector, account, wait_config, ui, ) .await }) .map_err(handle_starknet_command_error); let block_explorer_link = block_explorer_link_if_allowed(&result, provider.chain_id().await?, &config).await; process_command_result("invoke", result, ui, block_explorer_link); Ok(()) } Commands::Get(get) => get::get(get, config, ui).await, Commands::Utils(utils) => { utils::utils( utils, config, ui, cli.json, cli.profile.clone().unwrap_or("release".to_string()), ) .await } Commands::Multicall(multicall) => { multicall::multicall(multicall, config, ui, wait_config).await } Commands::Account(account) => account::account(account, config, ui, wait_config).await, Commands::ShowConfig(show) => { let provider = show.rpc.get_provider(&config, ui).await.ok(); let result = starknet_commands::show_config::show_config( &show, provider.as_ref(), config, cli.profile, ) .await; process_command_result("show-config", result, ui, None); Ok(()) } // TODO(#4214): Remove moved sncast commands Commands::TxStatus(tx_status) => { print_cmd_move_warning("tx-status", "get tx-status", ui); get::tx_status::tx_status(tx_status, config, ui).await } Commands::Verify(verify) => { let manifest_path = assert_manifest_path_exists()?; let package_metadata = get_package_metadata(&manifest_path, &verify.package)?; let artifacts = build_and_load_artifacts( &package_metadata, &BuildConfig { scarb_toml_path: manifest_path.clone(), json: cli.json, profile: cli.profile.unwrap_or("release".to_string()), }, false, // TODO(#3959) Remove `base_ui` ui.base_ui(), ) .expect("Failed to build contract"); let result = starknet_commands::verify::verify( verify, &package_metadata.manifest_path, &artifacts, &config, ui, ) .await; process_command_result("verify", result, ui, None); Ok(()) } Commands::Completions(completions) => { generate_completions(completions.shell, &mut Cli::command())?; Ok(()) } // TODO(#4214): Remove moved sncast commands Commands::Balance(balance) => { print_cmd_move_warning("balance", "get balance", ui); get::balance::balance(balance, config, ui).await } Commands::Ledger(ledger) => { let result = ledger::ledger(&ledger, ui).await?; process_command_result("ledger", Ok(result), ui, None); Ok(()) } Commands::Script(_) => unreachable!(), } } fn config_with_cli(config: &mut CastConfig, cli: &Cli) { macro_rules! clone_or_else { ($field:expr, $config_field:expr) => { $field.clone().unwrap_or_else(|| $config_field.clone()) }; } config.account = clone_or_else!(cli.account, config.account); config.keystore = cli.keystore.clone().or(config.keystore.clone()); if config.accounts_file == Utf8PathBuf::default() { config.accounts_file = Utf8PathBuf::from(DEFAULT_ACCOUNTS_FILE); } let new_accounts_file = clone_or_else!(cli.accounts_file_path, config.accounts_file); config.accounts_file = Utf8PathBuf::from(shellexpand::tilde(&new_accounts_file).to_string()); config.wait_params = ValidatedWaitParams::new( clone_or_else!( cli.wait_retry_interval, config.wait_params.get_retry_interval() ), clone_or_else!(cli.wait_timeout, config.wait_params.get_timeout()), ); } fn get_cast_config(cli: &Cli, ui: &UI) -> Result { let command = cli.command_name(); let global_config_path = get_global_config_path().unwrap_or_else(|err| { ui.print_error(&command, format!("Error getting global config path: {err}")); Utf8PathBuf::new() }); let global_config = load_config::(Some(&global_config_path.clone()), cli.profile.as_deref()) .or_else(|_| load_config::(Some(&global_config_path), None)) .map_err(|err| anyhow::anyhow!(format!("Failed to load config: {err}")))?; let local_config = load_config::(None, cli.profile.as_deref()) .map_err(|err| anyhow::anyhow!(format!("Failed to load config: {err}")))?; let mut combined_config = combine_cast_configs(&global_config, &local_config); config_with_cli(&mut combined_config, cli); Ok(combined_config) } fn print_cmd_move_warning(command_name: &str, new_command_name: &str, ui: &UI) { ui.print_warning(WarningMessage::new(format!( "`sncast {command_name}` has moved to `sncast {new_command_name}`. `sncast {command_name}` will be removed in the next version." ))); ui.print_blank_line(); } ================================================ FILE: crates/sncast/src/response/account/create.rs ================================================ use crate::response::cast_message::SncastCommandMessage; use crate::{helpers::block_explorer::LinkProvider, response::explorer_link::OutputLink}; use conversions::padded_felt::PaddedFelt; use conversions::string::IntoPaddedHexStr; use foundry_ui::styling; use serde::{Serialize, Serializer}; fn as_str(value: &u128, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&value.to_string()) } #[derive(Serialize, Debug, Clone)] pub struct AccountCreateResponse { pub address: PaddedFelt, #[serde(serialize_with = "as_str")] pub estimated_fee: u128, pub add_profile: Option, pub message: String, } impl SncastCommandMessage for AccountCreateResponse { fn text(&self) -> String { styling::OutputBuilder::new() .success_message("Account created") .blank_line() .field("Address", &self.address.into_padded_hex_str()) .if_some(self.add_profile.as_ref(), |builder, profile| { builder.field("Add Profile", profile) }) .blank_line() .text_field(&self.message) .build() } } impl OutputLink for AccountCreateResponse { const TITLE: &'static str = "account creation"; fn format_links(&self, provider: Box) -> String { format!("account: {}", provider.contract(self.address)) } } ================================================ FILE: crates/sncast/src/response/account/delete.rs ================================================ use crate::response::cast_message::SncastCommandMessage; use foundry_ui::styling; use serde::Serialize; #[derive(Serialize, Clone)] pub struct AccountDeleteResponse { pub result: String, } impl SncastCommandMessage for AccountDeleteResponse { fn text(&self) -> String { styling::OutputBuilder::new() .success_message("Account deleted") .blank_line() .text_field(&self.result) .build() } } ================================================ FILE: crates/sncast/src/response/account/deploy.rs ================================================ use crate::response::cast_message::SncastCommandMessage; use crate::{ helpers::block_explorer::LinkProvider, response::{explorer_link::OutputLink, invoke::InvokeResponse}, }; use conversions::string::IntoHexStr; use conversions::{padded_felt::PaddedFelt, serde::serialize::CairoSerialize}; use foundry_ui::styling; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, CairoSerialize, Clone, Debug, PartialEq)] pub struct AccountDeployResponse { pub transaction_hash: PaddedFelt, } impl From for AccountDeployResponse { fn from(value: InvokeResponse) -> Self { Self { transaction_hash: value.transaction_hash, } } } impl SncastCommandMessage for AccountDeployResponse { fn text(&self) -> String { styling::OutputBuilder::new() .success_message("Account deployed") .blank_line() .field("Transaction Hash", &self.transaction_hash.into_hex_string()) .build() } } impl OutputLink for AccountDeployResponse { const TITLE: &'static str = "account deployment"; fn format_links(&self, provider: Box) -> String { format!( "transaction: {}", provider.transaction(self.transaction_hash) ) } } ================================================ FILE: crates/sncast/src/response/account/import.rs ================================================ use crate::response::cast_message::SncastCommandMessage; use foundry_ui::styling; use serde::Serialize; #[derive(Serialize, Clone)] pub struct AccountImportResponse { pub add_profile: Option, pub account_name: String, } impl SncastCommandMessage for AccountImportResponse { fn text(&self) -> String { styling::OutputBuilder::new() .success_message("Account imported successfully") .blank_line() .field("Account Name", &self.account_name) .if_some(self.add_profile.as_ref(), |builder, profile| { builder.field("Add Profile", profile) }) .build() } } ================================================ FILE: crates/sncast/src/response/account/mod.rs ================================================ pub mod create; pub mod delete; pub mod deploy; pub mod import; ================================================ FILE: crates/sncast/src/response/balance.rs ================================================ use crate::helpers::token::TokenUnit; use crate::response::cast_message::SncastCommandMessage; use foundry_ui::styling; use primitive_types::U256; use serde::ser::{Serialize, SerializeStruct, Serializer}; #[derive(Debug)] pub struct BalanceResponse { pub balance: U256, pub token_unit: Option, } impl SncastCommandMessage for BalanceResponse { fn text(&self) -> String { let balance_str = if let Some(token_unit) = self.token_unit { format!("{} {token_unit}", self.balance) } else { self.balance.to_string() }; styling::OutputBuilder::new() .field("Balance", &balance_str) .build() } } // We need custom serialization because U256's default serialization is hex string impl Serialize for BalanceResponse { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let mut s = serializer.serialize_struct("BalanceResponse", 2)?; // Default U256 serialization uses hex string, we want decimal string s.serialize_field("balance", &self.balance.to_string())?; s.serialize_field("token_unit", &self.token_unit)?; s.end() } } ================================================ FILE: crates/sncast/src/response/call.rs ================================================ use crate::response::cast_message::SncastCommandMessage; use conversions::serde::serialize::CairoSerialize; use conversions::string::IntoHexStr; use foundry_ui::styling; use serde::Serialize; use starknet_types_core::felt::Felt; #[derive(Serialize, CairoSerialize, Clone)] pub struct CallResponse { pub response: Vec, } impl SncastCommandMessage for CallResponse { fn text(&self) -> String { let response_values = self .response .iter() .map(|felt| felt.into_hex_string()) .collect::>() .join(", "); styling::OutputBuilder::new() .success_message("Call completed") .blank_line() .field("Response", &format!("[{response_values}]")) .build() } } ================================================ FILE: crates/sncast/src/response/cast_message.rs ================================================ use foundry_ui::Message; use serde::Serialize; use serde_json::Value; pub struct SncastMessage(pub T); pub trait SncastCommandMessage { fn text(&self) -> String; } impl Message for SncastMessage where T: SncastCommandMessage + Serialize, { fn text(&self) -> String { self.0.text() } fn json(&self) -> Value { serde_json::to_value(&self.0).expect("Should be serializable to JSON") } } ================================================ FILE: crates/sncast/src/response/class_hash_at.rs ================================================ use crate::helpers::block_explorer::LinkProvider; use crate::response::cast_message::SncastCommandMessage; use crate::response::explorer_link::OutputLink; use conversions::padded_felt::PaddedFelt; use conversions::string::IntoPaddedHexStr; use foundry_ui::styling; use serde::Serialize; #[derive(Serialize, Clone)] pub struct ClassHashAtResponse { pub class_hash: PaddedFelt, } impl SncastCommandMessage for ClassHashAtResponse { fn text(&self) -> String { styling::OutputBuilder::new() .success_message("Class hash retrieved") .blank_line() .field("Class Hash", &self.class_hash.into_padded_hex_str()) .build() } } impl OutputLink for ClassHashAtResponse { const TITLE: &'static str = "class"; fn format_links(&self, provider: Box) -> String { format!("class: {}", provider.class(self.class_hash)) } } ================================================ FILE: crates/sncast/src/response/completions.rs ================================================ use foundry_ui::Message; use serde::Serialize; use serde_json::{Value, json}; #[derive(Serialize)] pub struct CompletionsMessage { pub completions: String, } impl Message for CompletionsMessage { fn text(&self) -> String { self.completions.clone() } fn json(&self) -> Value { json!(self) } } ================================================ FILE: crates/sncast/src/response/declare.rs ================================================ use super::explorer_link::OutputLink; use crate::helpers::block_explorer::LinkProvider; use crate::response::cast_message::SncastCommandMessage; use anyhow::Error; use camino::Utf8PathBuf; use conversions::string::IntoHexStr; use conversions::{IntoConv, padded_felt::PaddedFelt, serde::serialize::CairoSerialize}; use foundry_ui::{Message, styling}; use indoc::formatdoc; use serde::{Deserialize, Serialize}; use serde_json::Value; use starknet_rust::core::types::contract::{AbiConstructor, AbiEntry}; use starknet_types_core::felt::Felt; use std::fmt::Write; #[derive(Clone, Serialize, Deserialize, CairoSerialize, Debug, PartialEq)] pub struct DeclareTransactionResponse { pub class_hash: PaddedFelt, pub transaction_hash: PaddedFelt, } impl SncastCommandMessage for DeclareTransactionResponse { fn text(&self) -> String { styling::OutputBuilder::new() .success_message("Declaration completed") .blank_line() .field("Class Hash", &self.class_hash.into_hex_string()) .field("Transaction Hash", &self.transaction_hash.into_hex_string()) .build() } } #[derive(Clone, Serialize, Deserialize, CairoSerialize, Debug, PartialEq)] pub struct AlreadyDeclaredResponse { pub class_hash: PaddedFelt, } #[derive(Clone, Serialize, Deserialize, CairoSerialize, Debug, PartialEq)] #[serde(tag = "status")] pub enum DeclareResponse { AlreadyDeclared(AlreadyDeclaredResponse), #[serde(untagged)] Success(DeclareTransactionResponse), } impl DeclareResponse { #[must_use] pub fn class_hash(&self) -> Felt { match self { DeclareResponse::AlreadyDeclared(response) => response.class_hash.into_(), DeclareResponse::Success(response) => response.class_hash.into_(), } } } impl OutputLink for DeclareTransactionResponse { const TITLE: &'static str = "declaration"; fn format_links(&self, provider: Box) -> String { formatdoc!( " class: {} transaction: {} ", provider.class(self.class_hash), provider.transaction(self.transaction_hash) ) } } #[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DeployCommandMessage { accounts_file: Option, account: String, class_hash: PaddedFelt, arguments_flag: Option, network_flag: String, } impl DeployCommandMessage { pub fn new( abi: &[AbiEntry], response: &DeclareTransactionResponse, account: &str, accounts_file: &Utf8PathBuf, network_flag: String, ) -> Result { let arguments_flag: Option = generate_arguments_flag(abi); let accounts_file_str = accounts_file.to_string(); let accounts_file = (!accounts_file_str .contains("starknet_accounts/starknet_open_zeppelin_accounts.json")) .then_some(accounts_file_str); Ok(Self { account: account.to_string(), accounts_file, class_hash: response.class_hash, arguments_flag, network_flag, }) } } impl Message for DeployCommandMessage { fn text(&self) -> String { let mut command = String::from("sncast"); let accounts_file_flag = generate_accounts_file_flag(self.accounts_file.as_ref()); if let Some(flag) = accounts_file_flag { write!(command, " {flag}").unwrap(); } let account_flag = format!("--account {}", self.account); write!(command, " {account_flag}").unwrap(); write!(command, " deploy").unwrap(); write!( command, " --class-hash {}", self.class_hash.into_hex_string() ) .unwrap(); if let Some(arguments) = &self.arguments_flag { write!(command, " {arguments}").unwrap(); } write!(command, " {}", self.network_flag).unwrap(); let header = if self.arguments_flag.is_some() { "To deploy a contract of this class, replace the placeholders in `--arguments` with your actual values, then run:" } else { "To deploy a contract of this class, run:" }; formatdoc!( " {header} {command} " ) } fn json(&self) -> Value { // TODO(#3960) JSON output support // This message is only helpful in human mode, we don't need it in JSON mode. Value::Null } } fn generate_constructor_placeholder_arguments(constructor: AbiConstructor) -> String { constructor .inputs .into_iter() .map(|input| { let input_type = input .r#type .split("::") .last() .expect("Failed to get last part of input type"); format!("<{}: {}>", input.name, input_type) }) .collect::>() .join(", ") } fn generate_arguments_flag(abi: &[AbiEntry]) -> Option { let arguments = abi.iter().find_map(|entry| { if let AbiEntry::Constructor(constructor) = entry { let arguments = generate_constructor_placeholder_arguments(constructor.clone()); (!arguments.is_empty()).then_some(arguments) } else { None } }); arguments.map(|arguments| format!("--arguments '{arguments}'")) } fn generate_accounts_file_flag(accounts_file: Option<&String>) -> Option { accounts_file .as_ref() .map(|file| format!("--accounts-file {file}")) } ================================================ FILE: crates/sncast/src/response/deploy.rs ================================================ use crate::helpers::block_explorer::LinkProvider; use crate::response::cast_message::SncastCommandMessage; use crate::response::declare::DeclareTransactionResponse; use crate::response::explorer_link::OutputLink; use conversions::string::IntoPaddedHexStr; use conversions::{padded_felt::PaddedFelt, serde::serialize::CairoSerialize}; use foundry_ui::styling; use indoc::formatdoc; use serde::{Deserialize, Serialize}; #[derive(Clone, Serialize, Deserialize, CairoSerialize, Debug, PartialEq)] #[serde(untagged)] pub enum DeployResponse { Standard(StandardDeployResponse), WithDeclare(DeployResponseWithDeclare), } impl SncastCommandMessage for DeployResponse { fn text(&self) -> String { match &self { DeployResponse::Standard(response) => response.text(), DeployResponse::WithDeclare(response) => response.text(), } } } impl OutputLink for DeployResponse { const TITLE: &'static str = "deployment"; fn format_links(&self, provider: Box) -> String { match self { DeployResponse::Standard(deploy) => { formatdoc!( " contract: {} transaction: {} ", provider.contract(deploy.contract_address), provider.transaction(deploy.transaction_hash) ) } DeployResponse::WithDeclare(deploy_with_declare) => { formatdoc!( " contract: {} class: {} deploy transaction: {} declare transaction: {} ", provider.contract(deploy_with_declare.contract_address), provider.class(deploy_with_declare.class_hash), provider.transaction(deploy_with_declare.deploy_transaction_hash), provider.transaction(deploy_with_declare.declare_transaction_hash), ) } } } } #[derive(Clone, Serialize, Deserialize, CairoSerialize, Debug, PartialEq)] pub struct StandardDeployResponse { pub contract_address: PaddedFelt, pub transaction_hash: PaddedFelt, } impl StandardDeployResponse { fn text(&self) -> String { styling::OutputBuilder::new() .success_message("Deployment completed") .blank_line() .field( "Contract Address", &self.contract_address.into_padded_hex_str(), ) .field( "Transaction Hash", &self.transaction_hash.into_padded_hex_str(), ) .build() } } #[derive(Clone, Serialize, Deserialize, CairoSerialize, Debug, PartialEq)] pub struct DeployResponseWithDeclare { contract_address: PaddedFelt, class_hash: PaddedFelt, deploy_transaction_hash: PaddedFelt, declare_transaction_hash: PaddedFelt, } impl DeployResponseWithDeclare { #[must_use] pub fn from_responses( deploy: &StandardDeployResponse, declare: &DeclareTransactionResponse, ) -> Self { Self { contract_address: deploy.contract_address, class_hash: declare.class_hash, deploy_transaction_hash: deploy.transaction_hash, declare_transaction_hash: declare.transaction_hash, } } } impl DeployResponseWithDeclare { fn text(&self) -> String { styling::OutputBuilder::new() .success_message("Deployment completed") .blank_line() .field( "Contract Address", &self.contract_address.into_padded_hex_str(), ) .field("Class Hash", &self.class_hash.into_padded_hex_str()) .field( "Declare Transaction Hash", &self.declare_transaction_hash.into_padded_hex_str(), ) .field( "Deploy Transaction Hash", &self.deploy_transaction_hash.into_padded_hex_str(), ) .build() } } ================================================ FILE: crates/sncast/src/response/errors.rs ================================================ use crate::{ErrorData, WaitForTransactionError, handle_rpc_error}; use anyhow::anyhow; use console::style; use conversions::padded_felt::PaddedFelt; use conversions::serde::serialize::CairoSerialize; use conversions::byte_array::ByteArray; use foundry_ui::Message; use serde_json::{Value, json}; use starknet_rust::core::types::{ContractErrorData, StarknetError, TransactionExecutionErrorData}; use starknet_rust::providers::ProviderError; use thiserror::Error; #[derive(Debug)] pub struct ResponseError { command: String, error: String, } impl ResponseError { #[must_use] pub fn new(command: String, error: String) -> Self { Self { command, error } } } impl Message for ResponseError { fn text(&self) -> String { format!( "Command: {} {}: {}", self.command, style("Error").red(), self.error ) } fn json(&self) -> Value { json!({ "error": self.error, }) } } #[derive(Error, Debug, CairoSerialize)] pub enum StarknetCommandError { #[error(transparent)] UnknownError(#[from] anyhow::Error), #[error("Failed to find {} artifact in starknet_artifacts.json file. Please make sure you have specified correct package using `--package` flag.", .0.data)] ContractArtifactsNotFound(ErrorData), #[error(transparent)] WaitForTransactionError(#[from] WaitForTransactionError), #[error(transparent)] ProviderError(#[from] SNCastProviderError), } #[must_use] pub fn handle_starknet_command_error(error: StarknetCommandError) -> anyhow::Error { match error { StarknetCommandError::ProviderError(err) => handle_rpc_error(err), _ => error.into(), } } #[derive(Debug, Error, CairoSerialize)] pub enum SNCastProviderError { #[error(transparent)] StarknetError(SNCastStarknetError), #[error("Request rate limited")] RateLimited, #[error("Unknown RPC error: {0}")] UnknownError(#[from] anyhow::Error), } impl From for SNCastProviderError { fn from(value: ProviderError) -> Self { match value { ProviderError::StarknetError(err) => SNCastProviderError::StarknetError(err.into()), ProviderError::RateLimited => SNCastProviderError::RateLimited, ProviderError::ArrayLengthMismatch => { SNCastProviderError::UnknownError(anyhow!("Array length mismatch")) } ProviderError::Other(err) => SNCastProviderError::UnknownError(anyhow!("{err}")), } } } #[derive(Debug, Error, CairoSerialize)] pub enum SNCastStarknetError { #[error("Node failed to receive transaction")] FailedToReceiveTransaction, #[error("There is no contract at the specified address")] ContractNotFound, #[error("Requested entrypoint does not exist in the contract")] EntryPointNotFound, #[error("Block was not found")] BlockNotFound, #[error("There is no transaction with such an index")] InvalidTransactionIndex, #[error("Provided class hash does not exist")] ClassHashNotFound, #[error("Transaction with provided hash was not found (does not exist)")] TransactionHashNotFound, #[error("An error occurred in the called contract = {0:?}")] ContractError(ContractErrorData), #[error("Transaction execution error = {0:?}")] TransactionExecutionError(TransactionExecutionErrorData), #[error("Contract with class hash {0:#x} is already declared")] ClassAlreadyDeclared(PaddedFelt), #[error("Invalid transaction nonce")] InvalidTransactionNonce, #[error("The transaction's resources don't cover validation or the minimal transaction fee")] InsufficientResourcesForValidate, #[error("Account balance is too small to cover transaction fee")] InsufficientAccountBalance, #[error("Contract failed the validation = {0}")] ValidationFailure(ByteArray), #[error("Contract failed to compile in starknet")] CompilationFailed(ByteArray), #[error("Contract class size is too large")] ContractClassSizeIsTooLarge, #[error("No account")] NonAccount, #[error("Transaction already exists")] DuplicateTx, #[error("Compiled class hash mismatch")] CompiledClassHashMismatch, #[error("Unsupported transaction version")] UnsupportedTxVersion, #[error("Unsupported contract class version")] UnsupportedContractClassVersion, #[error("Unexpected RPC error occurred: {0}")] UnexpectedError(anyhow::Error), } impl From for SNCastStarknetError { fn from(value: StarknetError) -> Self { match value { StarknetError::FailedToReceiveTransaction => { SNCastStarknetError::FailedToReceiveTransaction } StarknetError::ContractNotFound => SNCastStarknetError::ContractNotFound, StarknetError::BlockNotFound => SNCastStarknetError::BlockNotFound, StarknetError::InvalidTransactionIndex => SNCastStarknetError::InvalidTransactionIndex, StarknetError::ClassHashNotFound => SNCastStarknetError::ClassHashNotFound, StarknetError::TransactionHashNotFound => SNCastStarknetError::TransactionHashNotFound, StarknetError::ContractError(err) => SNCastStarknetError::ContractError(err), StarknetError::TransactionExecutionError(err) => { SNCastStarknetError::TransactionExecutionError(err) } StarknetError::ClassAlreadyDeclared => { unreachable!( "ClassAlreadyDeclared error requires class hash parameter which is present in StarknetError::ClassAlreadyDeclared. This conversion should not be used." ) } StarknetError::InvalidTransactionNonce(_) => { SNCastStarknetError::InvalidTransactionNonce } StarknetError::InsufficientResourcesForValidate => { SNCastStarknetError::InsufficientResourcesForValidate } StarknetError::InsufficientAccountBalance => { SNCastStarknetError::InsufficientAccountBalance } StarknetError::ValidationFailure(err) => { SNCastStarknetError::ValidationFailure(ByteArray::from(err.as_str())) } StarknetError::CompilationFailed(msg) => { SNCastStarknetError::CompilationFailed(ByteArray::from(msg.as_str())) } StarknetError::ContractClassSizeIsTooLarge => { SNCastStarknetError::ContractClassSizeIsTooLarge } StarknetError::NonAccount => SNCastStarknetError::NonAccount, StarknetError::DuplicateTx => SNCastStarknetError::DuplicateTx, StarknetError::CompiledClassHashMismatch => { SNCastStarknetError::CompiledClassHashMismatch } StarknetError::UnsupportedTxVersion => SNCastStarknetError::UnsupportedTxVersion, StarknetError::UnsupportedContractClassVersion => { SNCastStarknetError::UnsupportedContractClassVersion } StarknetError::UnexpectedError(err) => { SNCastStarknetError::UnexpectedError(anyhow!(err)) } StarknetError::EntrypointNotFound => SNCastStarknetError::EntryPointNotFound, other => SNCastStarknetError::UnexpectedError(anyhow!(other)), } } } ================================================ FILE: crates/sncast/src/response/explorer_link.rs ================================================ use crate::Network; use crate::helpers::{block_explorer::LinkProvider, configuration::CastConfig, devnet::detection}; use foundry_ui::Message; use serde::Serialize; use serde_json::Value; use starknet_types_core::felt::Felt; const SNCAST_FORCE_SHOW_EXPLORER_LINKS_ENV: &str = "SNCAST_FORCE_SHOW_EXPLORER_LINKS"; // TODO(#3391): This code should be refactored to either use common `Message` trait or be directly // included in `sncast` output messages. pub trait OutputLink { const TITLE: &'static str; fn format_links(&self, provider: Box) -> String; } #[derive(Serialize)] pub struct ExplorerLinksMessage { title: String, links: String, } impl ExplorerLinksMessage { pub fn new(response: &T, provider: Box) -> Self where T: OutputLink, { Self { title: T::TITLE.to_string(), links: response.format_links(provider), } } } impl Message for ExplorerLinksMessage { fn text(&self) -> String { format!("\nTo see {} details, visit:\n{}", self.title, self.links) } fn json(&self) -> Value { // TODO(#3960) Fix JSON output support, currently links contain `\n` characters serde_json::to_value(self).unwrap() } } #[derive(Debug, PartialEq, Eq, thiserror::Error)] pub enum ExplorerError { #[error("The chosen block explorer service is not available for Sepolia Network")] SepoliaNotSupported, #[error("Custom network is not recognized by block explorer service")] UnrecognizedNetwork, #[error("Block explorer service is not available for Devnet Network")] DevnetNotSupported, } pub async fn block_explorer_link_if_allowed( result: &anyhow::Result, chain_id: Felt, config: &CastConfig, ) -> Option where T: OutputLink + Clone, { let Ok(response) = result else { return None; }; let network = chain_id.try_into().ok()?; let is_devnet = matches!(network, Network::Devnet) || detection::is_devnet_running().await; if (!config.show_explorer_links || is_devnet) && !is_explorer_link_overridden() { return None; } config .block_explorer .unwrap_or_default() .as_provider(network) .ok() .map(|provider| ExplorerLinksMessage::new(response, provider)) } #[must_use] pub fn is_explorer_link_overridden() -> bool { std::env::var(SNCAST_FORCE_SHOW_EXPLORER_LINKS_ENV).is_ok_and(|value| value == "1") } ================================================ FILE: crates/sncast/src/response/invoke.rs ================================================ use super::explorer_link::OutputLink; use crate::helpers::block_explorer::LinkProvider; use crate::response::cast_message::SncastCommandMessage; use conversions::string::IntoPaddedHexStr; use conversions::{padded_felt::PaddedFelt, serde::serialize::CairoSerialize}; use foundry_ui::styling; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, CairoSerialize, Clone, Debug, PartialEq)] pub struct InvokeResponse { pub transaction_hash: PaddedFelt, } impl SncastCommandMessage for InvokeResponse { fn text(&self) -> String { styling::OutputBuilder::new() .success_message("Invoke completed") .blank_line() .field( "Transaction Hash", &self.transaction_hash.into_padded_hex_str(), ) .build() } } impl OutputLink for InvokeResponse { const TITLE: &'static str = "invocation"; fn format_links(&self, provider: Box) -> String { format!( "transaction: {}", provider.transaction(self.transaction_hash) ) } } ================================================ FILE: crates/sncast/src/response/ledger.rs ================================================ use crate::response::cast_message::SncastCommandMessage; use foundry_ui::styling; use serde::Serialize; #[derive(Debug, Serialize)] pub struct PublicKeyResponse { pub public_key: String, } #[derive(Debug, Serialize)] pub struct SignatureResponse { pub r: String, pub s: String, } #[derive(Debug, Serialize)] pub struct VersionResponse { pub version: String, } #[derive(Debug, Serialize)] #[serde(untagged)] pub enum LedgerResponse { PublicKey(PublicKeyResponse), Signature(SignatureResponse), Version(VersionResponse), } impl SncastCommandMessage for LedgerResponse { fn text(&self) -> String { match self { LedgerResponse::PublicKey(resp) => styling::OutputBuilder::new() .field("Public Key", &resp.public_key) .build(), LedgerResponse::Signature(resp) => styling::OutputBuilder::new() .text_field("Hash signature:") .field("r", &resp.r) .field("s", &resp.s) .build(), LedgerResponse::Version(resp) => styling::OutputBuilder::new() .field("App Version", &resp.version) .build(), } } } ================================================ FILE: crates/sncast/src/response/mod.rs ================================================ pub mod account; pub mod balance; pub mod call; pub mod cast_message; pub mod class_hash_at; pub mod completions; pub mod declare; pub mod deploy; pub mod errors; pub mod explorer_link; pub mod invoke; pub mod ledger; pub mod multicall; pub mod nonce; pub mod script; pub mod show_config; pub mod transaction; pub mod transformed_call; pub mod tx_status; pub mod ui; pub mod utils; pub mod verify; ================================================ FILE: crates/sncast/src/response/multicall/mod.rs ================================================ pub mod new; pub mod run; ================================================ FILE: crates/sncast/src/response/multicall/new.rs ================================================ use crate::response::cast_message::SncastCommandMessage; use camino::Utf8PathBuf; use foundry_ui::styling; use serde::Serialize; #[derive(Serialize, Clone)] pub struct MulticallNewResponse { pub path: Utf8PathBuf, pub content: String, } impl SncastCommandMessage for MulticallNewResponse { fn text(&self) -> String { styling::OutputBuilder::new() .success_message("Multicall template created successfully") .blank_line() .field("Path", self.path.as_ref()) .field("Content", self.content.as_ref()) .build() } } ================================================ FILE: crates/sncast/src/response/multicall/run.rs ================================================ use crate::response::cast_message::SncastCommandMessage; use crate::{ helpers::block_explorer::LinkProvider, response::{explorer_link::OutputLink, invoke::InvokeResponse}, }; use conversions::string::IntoHexStr; use conversions::{padded_felt::PaddedFelt, serde::serialize::CairoSerialize}; use foundry_ui::styling; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, CairoSerialize, Clone, Debug, PartialEq)] pub struct MulticallRunResponse { pub transaction_hash: PaddedFelt, } impl SncastCommandMessage for MulticallRunResponse { fn text(&self) -> String { styling::OutputBuilder::new() .success_message("Multicall completed") .blank_line() .field("Transaction Hash", &self.transaction_hash.into_hex_string()) .build() } } impl From for MulticallRunResponse { fn from(value: InvokeResponse) -> Self { Self { transaction_hash: value.transaction_hash, } } } impl OutputLink for MulticallRunResponse { const TITLE: &'static str = "invocation"; fn format_links(&self, provider: Box) -> String { format!( "transaction: {}", provider.transaction(self.transaction_hash) ) } } ================================================ FILE: crates/sncast/src/response/nonce.rs ================================================ use crate::response::cast_message::SncastCommandMessage; use conversions::serde::serialize::CairoSerialize; use conversions::string::IntoHexStr; use foundry_ui::styling; use serde::Serialize; use starknet_types_core::felt::Felt; #[derive(Serialize, CairoSerialize, Clone)] pub struct NonceResponse { pub nonce: Felt, } impl SncastCommandMessage for NonceResponse { fn text(&self) -> String { styling::OutputBuilder::new() .success_message("Nonce retrieved") .blank_line() .field("Nonce", &self.nonce.into_hex_string()) .build() } } ================================================ FILE: crates/sncast/src/response/script/init.rs ================================================ use crate::response::cast_message::SncastCommandMessage; use foundry_ui::styling; use serde::Serialize; #[derive(Serialize, Clone)] pub struct ScriptInitResponse { pub message: String, } impl SncastCommandMessage for ScriptInitResponse { fn text(&self) -> String { styling::OutputBuilder::new() .success_message("Script initialization completed") .blank_line() .text_field(&self.message) .build() } } ================================================ FILE: crates/sncast/src/response/script/mod.rs ================================================ pub mod init; pub mod run; ================================================ FILE: crates/sncast/src/response/script/run.rs ================================================ use crate::response::cast_message::SncastCommandMessage; use foundry_ui::styling; use serde::Serialize; #[derive(Serialize, Debug, Clone)] pub struct ScriptRunResponse { pub status: String, pub message: Option, } impl SncastCommandMessage for ScriptRunResponse { fn text(&self) -> String { let mut builder = styling::OutputBuilder::new() .success_message("Script execution completed") .blank_line() .field("Status", &self.status); if let Some(message) = &self.message { builder = builder.blank_line().text_field(message); } builder.build() } } ================================================ FILE: crates/sncast/src/response/show_config.rs ================================================ use crate::Network; use crate::helpers::block_explorer; use crate::response::cast_message::SncastCommandMessage; use camino::Utf8PathBuf; use foundry_ui::styling; use serde::Serialize; use url::Url; #[derive(Serialize, Clone)] pub struct ShowConfigResponse { pub profile: Option, pub chain_id: Option, pub rpc_url: Option, pub network: Option, pub account: Option, pub accounts_file_path: Option, pub keystore: Option, pub wait_timeout: Option, pub wait_retry_interval: Option, pub show_explorer_links: bool, pub block_explorer: Option, } impl SncastCommandMessage for ShowConfigResponse { fn text(&self) -> String { let builder = styling::OutputBuilder::new() .if_some(self.profile.as_ref(), |b, profile| { b.field("Profile", profile) }) .if_some(self.chain_id.as_ref(), |b, chain_id| { b.field("Chain ID", chain_id) }) .if_some(self.rpc_url.as_ref(), |b, rpc_url| { b.field("RPC URL", rpc_url.as_ref()) }) .if_some(self.network.as_ref(), |b, network| { b.field("Network", network.to_string().as_ref()) }) .if_some(self.account.as_ref(), |b, account| { b.field("Account", account) }) .if_some(self.accounts_file_path.as_ref(), |b, path| { b.field("Accounts File Path", path.as_ref()) }) .if_some(self.keystore.as_ref(), |b, keystore| { b.field("Keystore", keystore.as_ref()) }) .if_some(self.wait_timeout.as_ref(), |b, timeout| { b.field("Wait Timeout", format!("{}s", &timeout).as_ref()) }) .if_some(self.wait_retry_interval.as_ref(), |b, interval| { b.field("Wait Retry Interval", format!("{}s", &interval).as_ref()) }) .field("Show Explorer Links", &self.show_explorer_links.to_string()) .if_some(self.block_explorer.as_ref(), |b, explorer| { b.field("Block Explorer", &format!("{explorer:?}")) }); builder.build() } } ================================================ FILE: crates/sncast/src/response/transaction.rs ================================================ use crate::helpers::block_explorer::LinkProvider; use crate::response::cast_message::SncastCommandMessage; use crate::response::explorer_link::OutputLink; use conversions::padded_felt::PaddedFelt; use conversions::string::IntoDecStr; use foundry_ui::styling::OutputBuilder; use serde::{Serialize, Serializer}; use starknet_rust::core::types::{ DataAvailabilityMode, DeclareTransaction, DeployAccountTransaction, InvokeTransaction, ResourceBoundsMapping, Transaction, }; use starknet_types_core::felt::Felt; #[derive(Clone)] pub struct TransactionResponse(pub Transaction); impl SncastCommandMessage for TransactionResponse { fn text(&self) -> String { match &self.0 { Transaction::Invoke(tx) => match tx { InvokeTransaction::V0(tx) => build_invoke_v0_response(tx), InvokeTransaction::V1(tx) => build_invoke_v1_response(tx), InvokeTransaction::V3(tx) => build_invoke_v3_response(tx), }, Transaction::Declare(tx) => match tx { DeclareTransaction::V0(tx) => build_declare_v0_response(tx), DeclareTransaction::V1(tx) => build_declare_v1_response(tx), DeclareTransaction::V2(tx) => build_declare_v2_response(tx), DeclareTransaction::V3(tx) => build_declare_v3_response(tx), }, Transaction::Deploy(tx) => build_deploy_response(tx), Transaction::DeployAccount(tx) => match tx { DeployAccountTransaction::V1(tx) => build_deploy_account_v1_response(tx), DeployAccountTransaction::V3(tx) => build_deploy_account_v3_response(tx), }, Transaction::L1Handler(tx) => build_l1_handler_response(tx), } } } impl OutputLink for TransactionResponse { const TITLE: &'static str = "transaction"; fn format_links(&self, provider: Box) -> String { let hash = PaddedFelt(*self.0.transaction_hash()); format!("transaction: {}", provider.transaction(hash)) } } impl Serialize for TransactionResponse { fn serialize(&self, serializer: S) -> Result where S: Serializer, { #[derive(Serialize)] struct Wrapper<'a> { transaction_type: &'static str, transaction: &'a Transaction, } Wrapper { transaction_type: json_transaction_type(&self.0), transaction: &self.0, } .serialize(serializer) } } fn json_transaction_type(tx: &Transaction) -> &'static str { match tx { Transaction::Invoke(InvokeTransaction::V0(_)) => "INVOKE_V0", Transaction::Invoke(InvokeTransaction::V1(_)) => "INVOKE_V1", Transaction::Invoke(InvokeTransaction::V3(_)) => "INVOKE_V3", Transaction::Declare(DeclareTransaction::V0(_)) => "DECLARE_V0", Transaction::Declare(DeclareTransaction::V1(_)) => "DECLARE_V1", Transaction::Declare(DeclareTransaction::V2(_)) => "DECLARE_V2", Transaction::Declare(DeclareTransaction::V3(_)) => "DECLARE_V3", Transaction::Deploy(_) => "DEPLOY", Transaction::DeployAccount(DeployAccountTransaction::V1(_)) => "DEPLOY_ACCOUNT_V1", Transaction::DeployAccount(DeployAccountTransaction::V3(_)) => "DEPLOY_ACCOUNT_V3", Transaction::L1Handler(_) => "L1_HANDLER", } } trait TransactionOutputBuilder { fn tx_header(self) -> Self; fn tx_type(self, tx_type: &str) -> Self; fn tx_version(self, version: &str) -> Self; fn tx_hash(self, hash: &Felt) -> Self; fn sender_address(self, addr: &Felt) -> Self; fn contract_address(self, addr: &Felt) -> Self; fn entry_point_selector(self, sel: &Felt) -> Self; fn class_hash(self, hash: &Felt) -> Self; fn compiled_class_hash(self, hash: &Felt) -> Self; fn contract_address_salt(self, salt: &Felt) -> Self; fn nonce(self, nonce: &Felt) -> Self; fn calldata(self, calldata: &[Felt]) -> Self; fn signature(self, sig: &[Felt]) -> Self; fn paymaster_data(self, data: &[Felt]) -> Self; fn account_deployment_data(self, data: &[Felt]) -> Self; fn constructor_calldata(self, data: &[Felt]) -> Self; fn resource_bounds(self, rb: &ResourceBoundsMapping) -> Self; fn max_fee(self, fee: &Felt) -> Self; fn tip(self, tip: u64) -> Self; fn nonce_da_mode(self, mode: DataAvailabilityMode) -> Self; fn fee_da_mode(self, mode: DataAvailabilityMode) -> Self; fn proof_facts(self, proof_facts: Option<&[Felt]>) -> Self; } impl TransactionOutputBuilder for OutputBuilder { fn tx_header(self) -> Self { self.success_message("Transaction found").blank_line() } fn tx_type(self, tx_type: &str) -> Self { self.field("Type", tx_type) } fn tx_version(self, version: &str) -> Self { self.field("Version", version) } fn tx_hash(self, hash: &Felt) -> Self { self.padded_felt_field("Transaction Hash", hash) } fn sender_address(self, addr: &Felt) -> Self { self.padded_felt_field("Sender Address", addr) } fn contract_address(self, addr: &Felt) -> Self { self.padded_felt_field("Contract Address", addr) } fn entry_point_selector(self, sel: &Felt) -> Self { self.padded_felt_field("Entry Point Selector", sel) } fn class_hash(self, hash: &Felt) -> Self { self.padded_felt_field("Class Hash", hash) } fn compiled_class_hash(self, hash: &Felt) -> Self { self.padded_felt_field("Compiled Class Hash", hash) } fn contract_address_salt(self, salt: &Felt) -> Self { self.padded_felt_field("Contract Address Salt", salt) } fn nonce(self, nonce: &Felt) -> Self { self.field("Nonce", &nonce.into_dec_string()) } fn calldata(self, calldata: &[Felt]) -> Self { self.felt_list_field("Calldata", calldata) } fn signature(self, sig: &[Felt]) -> Self { self.felt_list_field("Signature", sig) } fn paymaster_data(self, data: &[Felt]) -> Self { self.felt_list_field("Paymaster Data", data) } fn account_deployment_data(self, data: &[Felt]) -> Self { self.felt_list_field("Account Deployment Data", data) } fn constructor_calldata(self, data: &[Felt]) -> Self { self.felt_list_field("Constructor Calldata", data) } fn resource_bounds(self, rb: &ResourceBoundsMapping) -> Self { self.field( "Resource Bounds L1 Gas", &format!( "max_amount={}, max_price_per_unit={}", rb.l1_gas.max_amount, rb.l1_gas.max_price_per_unit ), ) .field( "Resource Bounds L1 Data Gas", &format!( "max_amount={}, max_price_per_unit={}", rb.l1_data_gas.max_amount, rb.l1_data_gas.max_price_per_unit ), ) .field( "Resource Bounds L2 Gas", &format!( "max_amount={}, max_price_per_unit={}", rb.l2_gas.max_amount, rb.l2_gas.max_price_per_unit ), ) } fn max_fee(self, fee: &Felt) -> Self { self.felt_field("Max Fee", fee) } fn tip(self, tip: u64) -> Self { self.field("Tip", &tip.to_string()) } fn nonce_da_mode(self, mode: DataAvailabilityMode) -> Self { self.field("Nonce DA Mode", fmt_da(mode)) } fn fee_da_mode(self, mode: DataAvailabilityMode) -> Self { self.field("Fee DA Mode", fmt_da(mode)) } fn proof_facts(self, proof_facts: Option<&[Felt]>) -> Self { if let Some(proof_facts) = proof_facts { self.felt_list_field("Proof Facts", proof_facts) } else { self } } } fn build_invoke_v0_response(tx: &starknet_rust::core::types::InvokeTransactionV0) -> String { let starknet_rust::core::types::InvokeTransactionV0 { transaction_hash, max_fee, signature, contract_address, entry_point_selector, calldata, } = tx; OutputBuilder::new() .tx_header() .tx_type("INVOKE") .tx_version("0") .tx_hash(transaction_hash) .contract_address(contract_address) .entry_point_selector(entry_point_selector) .calldata(calldata) .max_fee(max_fee) .signature(signature) .build() } fn build_invoke_v1_response(tx: &starknet_rust::core::types::InvokeTransactionV1) -> String { let starknet_rust::core::types::InvokeTransactionV1 { transaction_hash, sender_address, calldata, max_fee, signature, nonce, } = tx; OutputBuilder::new() .tx_header() .tx_type("INVOKE") .tx_version("1") .tx_hash(transaction_hash) .sender_address(sender_address) .nonce(nonce) .calldata(calldata) .max_fee(max_fee) .signature(signature) .build() } fn build_invoke_v3_response(tx: &starknet_rust::core::types::InvokeTransactionV3) -> String { let starknet_rust::core::types::InvokeTransactionV3 { transaction_hash, sender_address, calldata, signature, nonce, resource_bounds, tip, paymaster_data, account_deployment_data, nonce_data_availability_mode, fee_data_availability_mode, proof_facts, } = tx; OutputBuilder::new() .tx_header() .tx_type("INVOKE") .tx_version("3") .tx_hash(transaction_hash) .sender_address(sender_address) .nonce(nonce) .calldata(calldata) .account_deployment_data(account_deployment_data) .resource_bounds(resource_bounds) .tip(*tip) .paymaster_data(paymaster_data) .nonce_da_mode(*nonce_data_availability_mode) .fee_da_mode(*fee_data_availability_mode) .signature(signature) .proof_facts(proof_facts.as_deref()) .build() } fn build_declare_v0_response(tx: &starknet_rust::core::types::DeclareTransactionV0) -> String { let starknet_rust::core::types::DeclareTransactionV0 { transaction_hash, sender_address, max_fee, signature, class_hash, } = tx; OutputBuilder::new() .tx_header() .tx_type("DECLARE") .tx_version("0") .tx_hash(transaction_hash) .sender_address(sender_address) .class_hash(class_hash) .max_fee(max_fee) .signature(signature) .build() } fn build_declare_v1_response(tx: &starknet_rust::core::types::DeclareTransactionV1) -> String { let starknet_rust::core::types::DeclareTransactionV1 { transaction_hash, sender_address, max_fee, signature, nonce, class_hash, } = tx; OutputBuilder::new() .tx_header() .tx_type("DECLARE") .tx_version("1") .tx_hash(transaction_hash) .sender_address(sender_address) .nonce(nonce) .class_hash(class_hash) .max_fee(max_fee) .signature(signature) .build() } fn build_declare_v2_response(tx: &starknet_rust::core::types::DeclareTransactionV2) -> String { let starknet_rust::core::types::DeclareTransactionV2 { transaction_hash, sender_address, compiled_class_hash, max_fee, signature, nonce, class_hash, } = tx; OutputBuilder::new() .tx_header() .tx_type("DECLARE") .tx_version("2") .tx_hash(transaction_hash) .sender_address(sender_address) .nonce(nonce) .class_hash(class_hash) .compiled_class_hash(compiled_class_hash) .max_fee(max_fee) .signature(signature) .build() } fn build_declare_v3_response(tx: &starknet_rust::core::types::DeclareTransactionV3) -> String { let starknet_rust::core::types::DeclareTransactionV3 { transaction_hash, sender_address, compiled_class_hash, signature, nonce, class_hash, resource_bounds, tip, paymaster_data, account_deployment_data, nonce_data_availability_mode, fee_data_availability_mode, } = tx; OutputBuilder::new() .tx_header() .tx_type("DECLARE") .tx_version("3") .tx_hash(transaction_hash) .sender_address(sender_address) .nonce(nonce) .class_hash(class_hash) .compiled_class_hash(compiled_class_hash) .account_deployment_data(account_deployment_data) .resource_bounds(resource_bounds) .tip(*tip) .paymaster_data(paymaster_data) .nonce_da_mode(*nonce_data_availability_mode) .fee_da_mode(*fee_data_availability_mode) .signature(signature) .build() } fn build_deploy_response(tx: &starknet_rust::core::types::DeployTransaction) -> String { let starknet_rust::core::types::DeployTransaction { transaction_hash, version, contract_address_salt, constructor_calldata, class_hash, } = tx; OutputBuilder::new() .tx_header() .tx_type("DEPLOY") .tx_version(&version.to_string()) .tx_hash(transaction_hash) .class_hash(class_hash) .contract_address_salt(contract_address_salt) .constructor_calldata(constructor_calldata) .build() } fn build_deploy_account_v1_response( tx: &starknet_rust::core::types::DeployAccountTransactionV1, ) -> String { let starknet_rust::core::types::DeployAccountTransactionV1 { transaction_hash, max_fee, signature, nonce, contract_address_salt, constructor_calldata, class_hash, } = tx; OutputBuilder::new() .tx_header() .tx_type("DEPLOY ACCOUNT") .tx_version("1") .tx_hash(transaction_hash) .nonce(nonce) .class_hash(class_hash) .contract_address_salt(contract_address_salt) .constructor_calldata(constructor_calldata) .max_fee(max_fee) .signature(signature) .build() } fn build_deploy_account_v3_response( tx: &starknet_rust::core::types::DeployAccountTransactionV3, ) -> String { let starknet_rust::core::types::DeployAccountTransactionV3 { transaction_hash, signature, nonce, contract_address_salt, constructor_calldata, class_hash, resource_bounds, tip, paymaster_data, nonce_data_availability_mode, fee_data_availability_mode, } = tx; OutputBuilder::new() .tx_header() .tx_type("DEPLOY ACCOUNT") .tx_version("3") .tx_hash(transaction_hash) .nonce(nonce) .class_hash(class_hash) .contract_address_salt(contract_address_salt) .constructor_calldata(constructor_calldata) .resource_bounds(resource_bounds) .tip(*tip) .paymaster_data(paymaster_data) .nonce_da_mode(*nonce_data_availability_mode) .fee_da_mode(*fee_data_availability_mode) .signature(signature) .build() } fn build_l1_handler_response(tx: &starknet_rust::core::types::L1HandlerTransaction) -> String { let starknet_rust::core::types::L1HandlerTransaction { transaction_hash, version, nonce, contract_address, entry_point_selector, calldata, } = tx; OutputBuilder::new() .tx_header() .tx_type("L1 HANDLER") .tx_version(&version.to_string()) .tx_hash(transaction_hash) .contract_address(contract_address) .nonce(&Felt::from(*nonce)) .entry_point_selector(entry_point_selector) .calldata(calldata) .build() } fn fmt_da(mode: DataAvailabilityMode) -> &'static str { match mode { DataAvailabilityMode::L1 => "L1", DataAvailabilityMode::L2 => "L2", } } ================================================ FILE: crates/sncast/src/response/transformed_call.rs ================================================ use crate::response::call::CallResponse; use crate::response::cast_message::SncastCommandMessage; use anyhow::Result; use conversions::string::IntoHexStr; use data_transformer::reverse_transform_output; use foundry_ui::styling; use serde::Serialize; use starknet_rust::core::types::{ContractClass, contract::AbiEntry}; use starknet_types_core::felt::Felt; #[derive(Serialize, Clone)] pub struct TransformedCallResponse { pub response: String, pub response_raw: Vec, } impl SncastCommandMessage for TransformedCallResponse { fn text(&self) -> String { let response_raw_values = self .response_raw .iter() .map(|felt| felt.into_hex_string()) .collect::>() .join(", "); styling::OutputBuilder::new() .success_message("Call completed") .blank_line() .field("Response", &self.response) .field("Response Raw", &format!("[{response_raw_values}]")) .build() } } #[must_use] pub fn transform_response( result: &Result, contract_class: &ContractClass, selector: &Felt, ) -> Option { let Ok(CallResponse { response, .. }) = result else { return None; }; if response.is_empty() { return None; } let ContractClass::Sierra(sierra_class) = contract_class else { return None; }; let abi: Vec = serde_json::from_str(sierra_class.abi.as_str()).ok()?; let transformed_response = reverse_transform_output(response, &abi, selector).ok()?; Some(TransformedCallResponse { response_raw: response.clone(), response: transformed_response, }) } ================================================ FILE: crates/sncast/src/response/tx_status.rs ================================================ use crate::response::cast_message::SncastCommandMessage; use conversions::serde::serialize::CairoSerialize; use foundry_ui::styling; use serde::Serialize; #[derive(Serialize, CairoSerialize, Clone)] pub enum FinalityStatus { Received, Candidate, PreConfirmed, AcceptedOnL2, AcceptedOnL1, } #[derive(Serialize, CairoSerialize, Clone)] pub enum ExecutionStatus { Succeeded, Reverted, } #[derive(Serialize, CairoSerialize, Clone)] pub struct TransactionStatusResponse { pub finality_status: FinalityStatus, pub execution_status: Option, } impl SncastCommandMessage for TransactionStatusResponse { fn text(&self) -> String { let finality_status = match &self.finality_status { FinalityStatus::Received => "Received", FinalityStatus::Candidate => "Candidate", FinalityStatus::PreConfirmed => "Pre confirmed", FinalityStatus::AcceptedOnL2 => "Accepted on L2", FinalityStatus::AcceptedOnL1 => "Accepted on L1", }; let mut builder = styling::OutputBuilder::new() .success_message("Transaction status retrieved") .blank_line() .field("Finality Status", finality_status); if let Some(execution_status) = &self.execution_status { let execution_str = match execution_status { ExecutionStatus::Succeeded => "Succeeded", ExecutionStatus::Reverted => "Reverted", }; builder = builder.field("Execution Status", execution_str); } builder.build() } } ================================================ FILE: crates/sncast/src/response/ui.rs ================================================ use foundry_ui::{Message, OutputFormat, UI as BaseUI}; use serde::Serialize; use serde_json::Value; #[derive(Default)] pub struct UI { ui: BaseUI, } struct MessageWrapper { command: String, message: T, } struct ErrorWrapper { command: String, error: T, } struct NotificationWrapper { notification: T, } struct WarningWrapper { warning: T, } #[derive(Serialize)] struct OutputWithCommand<'a, T> { r#type: String, command: String, #[serde(flatten)] data: &'a T, } #[derive(Serialize)] struct Output<'a, T> { r#type: String, #[serde(flatten)] data: &'a T, } impl Message for MessageWrapper where T: Message, { fn text(&self) -> String { self.message.text() } fn json(&self) -> Value { let data = self.message.json(); serde_json::to_value(OutputWithCommand { r#type: "response".to_string(), command: self.command.clone(), data: &data, }) .expect("Failed to serialize message") } } impl Message for ErrorWrapper where T: Message, { fn text(&self) -> String { self.error.text() } fn json(&self) -> Value { let data = self.error.json(); serde_json::to_value(OutputWithCommand { r#type: "error".to_string(), command: self.command.clone(), data: &data, }) .expect("Failed to serialize message") } } impl Message for WarningWrapper where T: Message, { fn text(&self) -> String { self.warning.text() } fn json(&self) -> Value { let data = self.warning.json(); serde_json::to_value(Output { r#type: "warning".to_string(), data: &data, }) .expect("Failed to serialize message") } } impl Message for NotificationWrapper where T: Message, { fn text(&self) -> String { self.notification.text() } fn json(&self) -> Value { let data = self.notification.json(); serde_json::to_value(Output { r#type: "notification".to_string(), data: &data, }) .expect("Failed to serialize message") } } impl UI { #[must_use] pub fn new(output_format: OutputFormat) -> Self { let base_ui = BaseUI::new(output_format); Self { ui: base_ui } } fn should_skip_empty_json(&self, json: &Value) -> bool { self.ui.output_format() == OutputFormat::Json && *json == Value::Null } pub fn print_message(&self, command: &str, message: T) where T: Message, { // TODO(#3960) Add better handling for no JSON output if self.should_skip_empty_json(&message.json()) { return; } let internal_message = MessageWrapper { command: command.to_string(), message, }; self.ui.println(&internal_message); } pub fn print_error(&self, command: &str, message: T) where T: Message, { // TODO(#3960) Add better handling for no JSON output if self.should_skip_empty_json(&message.json()) { return; } let internal_message = ErrorWrapper { command: command.to_string(), error: message, }; self.ui.eprintln(&internal_message); } pub fn print_notification(&self, message: T) where T: Message, { // TODO(#3960) Add better handling for no JSON output if self.should_skip_empty_json(&message.json()) { return; } let internal_message = NotificationWrapper { notification: message, }; self.ui.println(&internal_message); } pub fn print_warning(&self, message: T) where T: Message, { // TODO(#3960) Add better handling for no JSON output if self.should_skip_empty_json(&message.json()) { return; } let internal_message = WarningWrapper { warning: message }; self.ui.println(&internal_message); } pub fn print_blank_line(&self) { self.ui.print_blank_line(); } #[must_use] pub fn base_ui(&self) -> &BaseUI { &self.ui } } ================================================ FILE: crates/sncast/src/response/utils/class_hash.rs ================================================ use crate::response::cast_message::SncastCommandMessage; use conversions::padded_felt::PaddedFelt; use conversions::{serde::serialize::CairoSerialize, string::IntoPaddedHexStr}; use foundry_ui::styling; use serde::{Deserialize, Serialize}; #[derive(Clone, Serialize, Deserialize, CairoSerialize, Debug, PartialEq)] pub struct ClassHashResponse { pub class_hash: PaddedFelt, } impl SncastCommandMessage for ClassHashResponse { fn text(&self) -> String { styling::OutputBuilder::new() .field("Class Hash", &self.class_hash.into_padded_hex_str()) .build() } } ================================================ FILE: crates/sncast/src/response/utils/mod.rs ================================================ pub mod class_hash; pub mod selector; pub mod serialize; ================================================ FILE: crates/sncast/src/response/utils/selector.rs ================================================ use crate::response::cast_message::SncastCommandMessage; use conversions::padded_felt::PaddedFelt; use conversions::string::IntoPaddedHexStr; use foundry_ui::styling; use serde::{Deserialize, Serialize}; #[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SelectorResponse { pub selector: PaddedFelt, } impl SncastCommandMessage for SelectorResponse { fn text(&self) -> String { styling::OutputBuilder::new() .field("Selector", &self.selector.into_padded_hex_str()) .build() } } ================================================ FILE: crates/sncast/src/response/utils/serialize.rs ================================================ use crate::response::cast_message::SncastCommandMessage; use conversions::serde::serialize::CairoSerialize; use foundry_ui::styling; use serde::{Deserialize, Serialize}; use starknet_types_core::felt::Felt; #[derive(Serialize, Deserialize, CairoSerialize, Clone, Debug, PartialEq)] pub struct SerializeResponse { pub calldata: Vec, } impl SncastCommandMessage for SerializeResponse { fn text(&self) -> String { let calldata = format!("{:?}", &self.calldata); styling::OutputBuilder::new() .field("Calldata", &calldata) .build() } } ================================================ FILE: crates/sncast/src/response/verify.rs ================================================ use crate::response::cast_message::SncastCommandMessage; use foundry_ui::styling; use serde::Serialize; #[derive(Serialize, Clone)] pub struct VerifyResponse { pub message: String, } impl SncastCommandMessage for VerifyResponse { fn text(&self) -> String { styling::OutputBuilder::new() .success_message("Verification completed") .blank_line() .text_field(&self.message) .build() } } ================================================ FILE: crates/sncast/src/starknet_commands/account/create.rs ================================================ use crate::starknet_commands::account::{ generate_add_profile_message, prepare_account_json, write_account_to_accounts_file, }; use anyhow::{Context, Result, anyhow, bail}; use bigdecimal::BigDecimal; use camino::Utf8PathBuf; use clap::Args; use console::style; use conversions::IntoConv; use foundry_ui::components::warning::WarningMessage; use serde_json::json; use sncast::helpers::braavos::BraavosAccountFactory; use sncast::helpers::configuration::CastConfig; use sncast::helpers::constants::{ BRAAVOS_BASE_ACCOUNT_CLASS_HASH, BRAAVOS_CLASS_HASH, CREATE_KEYSTORE_PASSWORD_ENV_VAR, OZ_CLASS_HASH, READY_CLASS_HASH, }; use sncast::helpers::ledger; use sncast::helpers::ledger::LedgerKeyLocatorAccount; use sncast::helpers::rpc::{RpcArgs, generate_network_flag}; use sncast::response::account::create::AccountCreateResponse; use sncast::response::ui::UI; use sncast::{ AccountType, SignerSource, SignerType, check_class_hash_exists, check_if_legacy_contract, extract_or_generate_salt, get_keystore_password, handle_account_factory_error, }; use starknet_rust::accounts::{ AccountDeploymentV3, AccountFactory, ArgentAccountFactory, OpenZeppelinAccountFactory, }; use starknet_rust::core::types::FeeEstimate; use starknet_rust::providers::JsonRpcClient; use starknet_rust::providers::jsonrpc::HttpTransport; use starknet_rust::signers::{LocalWallet, Signer, SigningKey}; use starknet_types_core::felt::Felt; use std::str::FromStr; #[derive(Args, Debug)] #[command(about = "Create an account with all important secrets")] pub struct Create { /// Type of the account #[arg(value_enum, short = 't', long = "type", value_parser = AccountType::from_str, default_value_t = AccountType::OpenZeppelin)] pub account_type: AccountType, /// Account name under which account information is going to be saved #[arg(short, long)] pub name: Option, /// Salt for the address #[arg(short, long)] pub salt: Option, /// If passed, a profile with provided name and corresponding data will be created in snfoundry.toml #[arg(long)] pub add_profile: Option, /// Custom contract class hash of declared contract #[arg(short, long, requires = "account_type")] pub class_hash: Option, #[command(flatten)] pub rpc: RpcArgs, #[command(flatten)] pub ledger_key_locator: LedgerKeyLocatorAccount, } #[allow(clippy::too_many_arguments, clippy::too_many_lines)] pub async fn create( account: &str, accounts_file: &Utf8PathBuf, provider: &JsonRpcClient, chain_id: Felt, create: &Create, config: &CastConfig, signer_source: &SignerSource, ui: &UI, ) -> Result { // TODO(#3556): Remove this warning once we drop Argent account type if create.account_type == AccountType::Argent { ui.print_warning(WarningMessage::new( "Argent has rebranded as Ready. The `argent` option for the `--type` flag in `account create` is deprecated, please use `ready` instead.", )); ui.print_blank_line(); } let salt = extract_or_generate_salt(create.salt); let class_hash = create.class_hash.unwrap_or(match create.account_type { AccountType::OpenZeppelin => OZ_CLASS_HASH, AccountType::Argent | AccountType::Ready => READY_CLASS_HASH, AccountType::Braavos => BRAAVOS_CLASS_HASH, }); check_class_hash_exists(provider, class_hash).await?; let (account_json, estimated_fee) = generate_account( provider, salt, class_hash, create.account_type, signer_source, chain_id, ui, ) .await?; let address: Felt = account_json["address"] .as_str() .context("Invalid address")? .parse()?; let estimated_fee_strk = BigDecimal::new(estimated_fee.into(), 18.into()); let mut message = format!( "Account successfully created but it needs to be deployed. The estimated deployment fee is {} STRK. Prefund the account to cover deployment transaction fee", style(estimated_fee_strk).magenta() ); match signer_source { SignerSource::Keystore(keystore) => { let account_path = Utf8PathBuf::from(&account); if account_path == Utf8PathBuf::default() { bail!("Argument `--account` must be passed and be a path when using `--keystore`"); } let private_key = account_json["private_key"] .as_str() .context("Invalid private_key")? .parse()?; let legacy = account_json["legacy"] .as_bool() .expect("Invalid legacy entry"); create_to_keystore( private_key, salt, class_hash, create.account_type, keystore, &account_path, legacy, )?; let deploy_command = generate_deploy_command_with_keystore(account, keystore, &create.rpc, config); message.push_str(&deploy_command); } SignerSource::Ledger(_) | SignerSource::AccountsFile => { write_account_to_accounts_file(account, accounts_file, chain_id, account_json.clone())?; let deploy_command = generate_deploy_command(accounts_file, &create.rpc, config, account); message.push_str(&deploy_command); } } let add_profile_message = generate_add_profile_message( create.add_profile.as_ref(), &create.rpc, account, accounts_file, match signer_source { SignerSource::Keystore(path) => Some(path.clone()), _ => None, }, config, )?; Ok(AccountCreateResponse { address: address.into_(), estimated_fee, add_profile: add_profile_message, message: if account_json["deployed"] == json!(false) { message } else { "Account already deployed".to_string() }, }) } async fn generate_account( provider: &JsonRpcClient, salt: Felt, class_hash: Felt, account_type: AccountType, signer_source: &SignerSource, chain_id: Felt, ui: &UI, ) -> Result<(serde_json::Value, u128)> { if let SignerSource::Ledger(ledger_path) = signer_source { let signer = ledger::create_ledger_signer(ledger_path, ui, false).await?; let signer_type = SignerType::Ledger { ledger_path: ledger_path.clone(), }; finalize_account_generation( provider, signer, signer_type, salt, class_hash, account_type, chain_id, ) .await } else { let private_key = SigningKey::from_random(); let signer = LocalWallet::from_signing_key(private_key.clone()); let signer_type = SignerType::Local { private_key: private_key.secret_scalar(), }; finalize_account_generation( provider, signer, signer_type, salt, class_hash, account_type, chain_id, ) .await } } #[allow(clippy::too_many_arguments)] async fn finalize_account_generation( provider: &JsonRpcClient, signer: S, signer_type: SignerType, salt: Felt, class_hash: Felt, account_type: AccountType, chain_id: Felt, ) -> Result<(serde_json::Value, u128)> where S: Signer + Send + Sync, ::GetPublicKeyError: 'static, { let public_key = signer.get_public_key().await?.scalar(); let (address, estimated_fee) = match account_type { AccountType::OpenZeppelin => { let factory = OpenZeppelinAccountFactory::new(class_hash, chain_id, signer, provider).await?; get_address_and_deployment_fee(factory, salt).await } AccountType::Argent | AccountType::Ready => { let factory = ArgentAccountFactory::new(class_hash, chain_id, None, signer, provider).await?; get_address_and_deployment_fee(factory, salt).await } AccountType::Braavos => { let factory = BraavosAccountFactory::new( class_hash, BRAAVOS_BASE_ACCOUNT_CLASS_HASH, chain_id, signer, provider, ) .await?; get_address_and_deployment_fee(factory, salt).await } }?; let legacy = check_if_legacy_contract(Some(class_hash), address, provider).await?; let account_json = prepare_account_json( &signer_type, public_key, address, false, legacy, account_type, Some(class_hash), Some(salt), ); Ok((account_json, estimated_fee.overall_fee)) } async fn get_address_and_deployment_fee( account_factory: T, salt: Felt, ) -> Result<(Felt, FeeEstimate)> where T: AccountFactory + Sync, { let deployment = account_factory.deploy_v3(salt); Ok((deployment.address(), get_deployment_fee(&deployment).await?)) } async fn get_deployment_fee( account_deployment: &AccountDeploymentV3<'_, T>, ) -> Result where T: AccountFactory + Sync, { let fee_estimate = account_deployment.estimate_fee().await; match fee_estimate { Ok(fee_estimate) => Ok(fee_estimate), Err(err) => Err(anyhow!( "Failed to estimate account deployment fee. Reason: {}", handle_account_factory_error::(err) )), } } fn create_to_keystore( private_key: Felt, salt: Felt, class_hash: Felt, account_type: AccountType, keystore_path: &Utf8PathBuf, account_path: &Utf8PathBuf, legacy: bool, ) -> Result<()> { if keystore_path.exists() { bail!("Keystore file {keystore_path} already exists"); } if account_path.exists() { bail!("Account file {account_path} already exists"); } let password = get_keystore_password(CREATE_KEYSTORE_PASSWORD_ENV_VAR)?; let private_key = SigningKey::from_secret_scalar(private_key); private_key.save_as_keystore(keystore_path, &password)?; let account_json = match account_type { AccountType::OpenZeppelin => { json!({ "version": 1, "variant": { "type": AccountType::OpenZeppelin, "version": 1, "public_key": format!("{:#x}", private_key.verifying_key().scalar()), "legacy": legacy, }, "deployment": { "status": "undeployed", "class_hash": format!("{class_hash:#x}"), "salt": format!("{salt:#x}"), } }) } AccountType::Argent | AccountType::Ready => { json!({ "version": 1, "variant": { // TODO(#3556): Remove hardcoded "argent" and use format! with `AccountType::Ready` "type": "argent", "version": 1, "owner": format!("{:#x}", private_key.verifying_key().scalar()), "guardian": "0x0", }, "deployment": { "status": "undeployed", "class_hash": format!("{class_hash:#x}"), "salt": format!("{salt:#x}"), } }) } AccountType::Braavos => { json!( { "version": 1, "variant": { "type": AccountType::Braavos, "version": 1, "multisig": { "status": "off" }, "signers": [ { "type": "stark", "public_key": format!("{:#x}", private_key.verifying_key().scalar()) } ] }, "deployment": { "status": "undeployed", "class_hash": format!("{class_hash:#x}"), "salt": format!("{salt:#x}"), "context": { "variant": "braavos", "base_account_class_hash": BRAAVOS_BASE_ACCOUNT_CLASS_HASH } } } ) } }; write_account_to_file(&account_json, account_path) } fn write_account_to_file( account_json: &serde_json::Value, account_file: &Utf8PathBuf, ) -> Result<()> { std::fs::create_dir_all(account_file.clone().parent().unwrap())?; std::fs::write( account_file.clone(), serde_json::to_string_pretty(&account_json).unwrap(), )?; Ok(()) } fn generate_deploy_command( accounts_file: &Utf8PathBuf, rpc_args: &RpcArgs, config: &CastConfig, account: &str, ) -> String { let accounts_flag = if accounts_file .to_string() .contains("starknet_accounts/starknet_open_zeppelin_accounts.json") { String::new() } else { format!(" --accounts-file {accounts_file}") }; let network_flag = generate_network_flag(rpc_args, config); format!( "\n\nAfter prefunding the account, run:\n\ sncast{accounts_flag} account deploy {network_flag} --name {account}" ) } fn generate_deploy_command_with_keystore( account: &str, keystore: &Utf8PathBuf, rpc_args: &RpcArgs, config: &CastConfig, ) -> String { let network_flag = generate_network_flag(rpc_args, config); format!( "\n\nAfter prefunding the account, run:\n\ sncast --account {account} --keystore {keystore} account deploy {network_flag}" ) } ================================================ FILE: crates/sncast/src/starknet_commands/account/delete.rs ================================================ use anyhow::{Result, bail}; use camino::Utf8PathBuf; use clap::{ArgGroup, Args}; use promptly::prompt; use sncast::helpers::account::load_accounts; use sncast::helpers::configuration::CastConfig; use sncast::helpers::rpc::RpcArgs; use sncast::response::account::delete::AccountDeleteResponse; use sncast::response::ui::UI; use sncast::{chain_id_to_network_name, get_chain_id}; #[derive(Args, Debug)] #[command(about = "Delete account information from the accounts file")] #[command(group(ArgGroup::new("networks") .args(&["url", "network", "network_name"]) .required(true) .multiple(false)))] pub struct Delete { /// Name of the account to be deleted #[arg(short, long)] pub name: String, /// Assume "yes" as answer to confirmation prompt and run non-interactively #[arg(long, default_value = "false")] pub yes: bool, #[command(flatten)] pub rpc: RpcArgs, /// Literal name of the network used in accounts file #[arg(long)] pub network_name: Option, } pub fn delete( name: &str, path: &Utf8PathBuf, network_name: &str, yes: bool, ) -> Result { let mut items = load_accounts(path)?; if items[&network_name].is_null() { bail!("No accounts defined for network = {network_name}"); } if items[&network_name][&name].is_null() { bail!("Account with name {name} does not exist") } // Let's ask confirmation if !yes { let prompt_text = format!( "Do you want to remove the account {name} deployed to network {network_name} from local file {path}? (Y/n)" ); let input: String = prompt(prompt_text)?; if !input.starts_with('Y') { bail!("Delete aborted"); } } // get to the nested object "nested" let nested = items .get_mut(network_name) .expect("Failed to find network") .as_object_mut() .expect("Failed to convert network"); // now remove the child from there nested.remove(name); std::fs::write(path.clone(), serde_json::to_string_pretty(&items).unwrap())?; let result = "Account successfully removed".to_string(); Ok(AccountDeleteResponse { result }) } pub(crate) async fn get_network_name( delete: &Delete, config: &CastConfig, ui: &UI, ) -> Result { if let Some(network_name) = &delete.network_name { return Ok(network_name.clone()); } let provider = delete.rpc.get_provider(config, ui).await?; Ok(chain_id_to_network_name(get_chain_id(&provider).await?)) } ================================================ FILE: crates/sncast/src/starknet_commands/account/deploy.rs ================================================ use anyhow::{Context, Result, anyhow, bail}; use camino::Utf8PathBuf; use clap::Args; use conversions::IntoConv; use serde_json::Map; use sncast::helpers::account::load_accounts; use sncast::helpers::braavos::BraavosAccountFactory; use sncast::helpers::constants::BRAAVOS_BASE_ACCOUNT_CLASS_HASH; use sncast::helpers::fee::{FeeArgs, FeeSettings}; use sncast::helpers::ledger; use sncast::helpers::rpc::RpcArgs; use sncast::response::account::deploy::AccountDeployResponse; use sncast::response::invoke::InvokeResponse; use sncast::response::ui::UI; use sncast::{ AccountData, AccountType, SignerType, WaitForTx, apply_optional_fields, chain_id_to_network_name, check_account_file_exists, get_account_data_from_accounts_file, get_account_data_from_keystore, handle_rpc_error, handle_wait_for_tx, }; use starknet_rust::accounts::{AccountDeploymentV3, AccountFactory, OpenZeppelinAccountFactory}; use starknet_rust::accounts::{AccountFactoryError, ArgentAccountFactory}; use starknet_rust::core::types::{ ContractExecutionError, StarknetError::ClassHashNotFound, StarknetError::TransactionExecutionError, TransactionExecutionErrorData, }; use starknet_rust::core::utils::get_contract_address; use starknet_rust::providers::jsonrpc::HttpTransport; use starknet_rust::providers::{JsonRpcClient, ProviderError::StarknetError}; use starknet_rust::signers::{LocalWallet, Signer, SigningKey}; use starknet_types_core::felt::Felt; #[derive(Args, Debug)] #[command(about = "Deploy an account to the Starknet")] pub struct Deploy { /// Name of the account to be deployed #[arg(short, long)] pub name: Option, #[command(flatten)] pub fee_args: FeeArgs, #[command(flatten)] pub rpc: RpcArgs, /// If passed, the command will not trigger an interactive prompt to add an account as a default #[arg(long)] pub silent: bool, } #[expect(clippy::too_many_arguments)] pub async fn deploy( provider: &JsonRpcClient, accounts_file: &Utf8PathBuf, deploy_args: &Deploy, chain_id: Felt, wait_config: WaitForTx, account: &str, keystore_path: Option, fee_args: FeeArgs, ui: &UI, ) -> Result { if let Some(keystore_path) = keystore_path { deploy_from_keystore( provider, chain_id, fee_args, wait_config, account, keystore_path, ui, ) .await .map(Into::into) } else { let account_name = deploy_args .name .clone() .ok_or_else(|| anyhow!("Required argument `--name` not provided"))?; check_account_file_exists(accounts_file)?; deploy_from_accounts_file( provider, accounts_file, account_name, chain_id, fee_args, wait_config, ui, ) .await .map(Into::into) } } async fn deploy_from_keystore( provider: &JsonRpcClient, chain_id: Felt, fee_args: FeeArgs, wait_config: WaitForTx, account: &str, keystore_path: Utf8PathBuf, ui: &UI, ) -> Result { let account_data = get_account_data_from_keystore(account, &keystore_path)?; let is_deployed = account_data .deployed .ok_or_else(|| anyhow!("Failed to get status key from account JSON file"))?; if is_deployed { bail!("Account already deployed"); } let private_key_felt = account_data .signer_type .private_key() .context("Private key not found in keystore account")?; let private_key = SigningKey::from_secret_scalar(private_key_felt); let public_key = account_data.public_key; if public_key != private_key.verifying_key().scalar() { bail!("Public key and private key from keystore do not match"); } let (account_type, class_hash, salt) = extract_deployment_fields(&account_data)?; let address = compute_account_address(salt, public_key, class_hash, account_type, chain_id); let signer = LocalWallet::from_signing_key(private_key); let result = create_factory_and_deploy( provider, account_type, class_hash, signer, salt, chain_id, fee_args, wait_config, ui, ) .await?; update_keystore_account(account, address)?; Ok(result) } async fn deploy_from_accounts_file( provider: &JsonRpcClient, accounts_file: &Utf8PathBuf, name: String, chain_id: Felt, fee_args: FeeArgs, wait_config: WaitForTx, ui: &UI, ) -> Result { let account_data = get_account_data_from_accounts_file(&name, chain_id, accounts_file)?; let (account_type, class_hash, salt) = extract_deployment_fields(&account_data)?; let result = match &account_data.signer_type { SignerType::Ledger { ledger_path } => { let signer = ledger::create_ledger_signer(ledger_path, ui, true).await?; ledger::verify_ledger_public_key( signer.get_public_key().await?.scalar(), account_data.public_key, )?; create_factory_and_deploy( provider, account_type, class_hash, signer, salt, chain_id, fee_args, wait_config, ui, ) .await? } SignerType::Local { private_key } => { let signer = LocalWallet::from_signing_key(SigningKey::from_secret_scalar(*private_key)); create_factory_and_deploy( provider, account_type, class_hash, signer, salt, chain_id, fee_args, wait_config, ui, ) .await? } }; update_account_in_accounts_file(accounts_file, &name, chain_id)?; Ok(result) } #[expect(clippy::too_many_arguments)] async fn create_factory_and_deploy( provider: &JsonRpcClient, account_type: AccountType, class_hash: Felt, signer: S, salt: Felt, chain_id: Felt, fee_args: FeeArgs, wait_config: WaitForTx, ui: &UI, ) -> Result where S: Signer + Send + Sync, S::GetPublicKeyError: 'static, S::SignError: 'static, { match account_type { AccountType::Argent | AccountType::Ready => { let factory = ArgentAccountFactory::new(class_hash, chain_id, None, signer, provider).await?; deploy_account( factory, provider, salt, fee_args, wait_config, class_hash, ui, ) .await } AccountType::OpenZeppelin => { let factory = OpenZeppelinAccountFactory::new(class_hash, chain_id, signer, provider).await?; deploy_account( factory, provider, salt, fee_args, wait_config, class_hash, ui, ) .await } AccountType::Braavos => { let factory = BraavosAccountFactory::new( class_hash, BRAAVOS_BASE_ACCOUNT_CLASS_HASH, chain_id, signer, provider, ) .await?; deploy_account( factory, provider, salt, fee_args, wait_config, class_hash, ui, ) .await } } } fn extract_deployment_fields(account_data: &AccountData) -> Result<(AccountType, Felt, Felt)> { let account_type = account_data .account_type .context("Failed to get account type from file")?; let class_hash = account_data .class_hash .context("Failed to get class hash from file")?; let salt = account_data.salt.context("Failed to get salt from file")?; Ok((account_type, class_hash, salt)) } fn execution_error_message(error: &ContractExecutionError) -> &str { match error { ContractExecutionError::Message(msg) => msg, ContractExecutionError::Nested(inner) => execution_error_message(&inner.error), } } async fn deploy_account( account_factory: T, provider: &JsonRpcClient, salt: Felt, fee_args: FeeArgs, wait_config: WaitForTx, class_hash: Felt, ui: &UI, ) -> Result where T: AccountFactory + Sync, { let deployment = account_factory.deploy_v3(salt); let fee_settings = if fee_args.max_fee.is_some() { let fee_estimate = deployment .estimate_fee() .await .expect("Failed to estimate fee"); fee_args.try_into_fee_settings(Some(&fee_estimate)) } else { fee_args.try_into_fee_settings(None) }; let FeeSettings { l1_gas, l1_gas_price, l2_gas, l2_gas_price, l1_data_gas, l1_data_gas_price, tip, } = fee_settings.expect("Failed to convert to fee settings"); let deployment = apply_optional_fields!( deployment, l1_gas => AccountDeploymentV3::l1_gas, l1_gas_price => AccountDeploymentV3::l1_gas_price, l2_gas => AccountDeploymentV3::l2_gas, l2_gas_price => AccountDeploymentV3::l2_gas_price, l1_data_gas => AccountDeploymentV3::l1_data_gas, l1_data_gas_price => AccountDeploymentV3::l1_data_gas_price, tip => AccountDeploymentV3::tip ); let result = deployment.send().await; match result { Err(AccountFactoryError::Provider(error)) => match error { StarknetError(ClassHashNotFound) => Err(anyhow!( "Provided class hash {class_hash:#x} does not exist", )), StarknetError(TransactionExecutionError(TransactionExecutionErrorData { ref execution_error, .. })) => Err(anyhow!(execution_error_message(execution_error).to_owned())), _ => Err(handle_rpc_error(error)), }, Err(_) => Err(anyhow!("Unknown AccountFactoryError")), Ok(result) => { let return_value = InvokeResponse { transaction_hash: result.transaction_hash.into_(), }; if let Err(message) = handle_wait_for_tx( provider, result.transaction_hash, return_value.clone(), wait_config, ui, ) .await { return Err(anyhow!(message)); } Ok(return_value) } } } fn update_account_in_accounts_file( accounts_file: &Utf8PathBuf, account_name: &str, chain_id: Felt, ) -> Result<()> { let network_name = chain_id_to_network_name(chain_id); let mut items = load_accounts(accounts_file)?; items[&network_name][account_name]["deployed"] = serde_json::Value::from(true); std::fs::write(accounts_file, serde_json::to_string_pretty(&items).unwrap()) .context("Failed to write to accounts file")?; Ok(()) } fn update_keystore_account(account: &str, address: Felt) -> Result<()> { let account_path = Utf8PathBuf::from(account.to_string()); let contents = std::fs::read_to_string(account_path.clone()).context("Failed to read account file")?; let mut items: Map = serde_json::from_str(&contents) .map_err(|_| anyhow!("Failed to parse account file at {account_path}"))?; items["deployment"]["status"] = serde_json::Value::from("deployed"); items .get_mut("deployment") .and_then(|deployment| deployment.as_object_mut()) .expect("Failed to get deployment as an object") .retain(|key, _| key != "salt" && key != "context"); items["deployment"]["address"] = format!("{address:#x}").into(); std::fs::write(&account_path, serde_json::to_string_pretty(&items).unwrap()) .context("Failed to write to account file")?; Ok(()) } pub(crate) fn compute_account_address( salt: Felt, public_key: Felt, class_hash: Felt, account_type: AccountType, chain_id: Felt, ) -> Felt { match account_type { AccountType::Argent | AccountType::Ready => { get_contract_address(salt, class_hash, &[public_key, Felt::ZERO], Felt::ZERO) } AccountType::OpenZeppelin => { get_contract_address(salt, class_hash, &[public_key], chain_id) } AccountType::Braavos => get_contract_address( salt, BRAAVOS_BASE_ACCOUNT_CLASS_HASH, &[public_key], chain_id, ), } } ================================================ FILE: crates/sncast/src/starknet_commands/account/import.rs ================================================ use std::str::FromStr; use super::deploy::compute_account_address; use crate::starknet_commands::account::{ generate_add_profile_message, prepare_account_json, write_account_to_accounts_file, }; use anyhow::{Context, Result, bail, ensure}; use camino::Utf8PathBuf; use clap::Args; use conversions::string::{TryFromDecStr, TryFromHexStr}; use foundry_ui::components::warning::WarningMessage; use sncast::check_if_legacy_contract; use sncast::helpers::account::generate_account_name; use sncast::helpers::configuration::CastConfig; use sncast::helpers::ledger; use sncast::helpers::ledger::LedgerKeyLocatorAccount; use sncast::helpers::rpc::RpcArgs; use sncast::response::account::import::AccountImportResponse; use sncast::response::ui::UI; use sncast::{AccountType, SignerType, check_class_hash_exists, get_chain_id, handle_rpc_error}; use starknet_rust::core::types::{BlockId, BlockTag, StarknetError}; use starknet_rust::providers::jsonrpc::{HttpTransport, JsonRpcClient}; use starknet_rust::providers::{Provider, ProviderError}; use starknet_rust::signers::SigningKey; use starknet_types_core::felt::Felt; #[derive(Args, Debug)] #[command(about = "Add an account to the accounts file")] pub struct Import { /// Name of the account to be imported #[arg(short, long)] pub name: Option, /// Address of the account #[arg(short, long)] pub address: Felt, /// Type of the account #[arg(short = 't', long = "type", value_parser = AccountType::from_str)] pub account_type: AccountType, /// Class hash of the account #[arg(short, long)] pub class_hash: Option, /// Account private key #[arg( long, group = "private_key_input", conflicts_with = "ledger_key_locator_account" )] pub private_key: Option, /// Path to the file holding account private key #[arg( long = "private-key-file", group = "private_key_input", conflicts_with = "ledger_key_locator_account" )] pub private_key_file_path: Option, /// Salt for the address #[arg(short, long)] pub salt: Option, /// If passed, a profile with the provided name and corresponding data will be created in snfoundry.toml #[arg(long)] pub add_profile: Option, #[command(flatten)] pub rpc: RpcArgs, /// If passed, the command will not trigger an interactive prompt to add an account as a default #[arg(long)] pub silent: bool, #[command(flatten)] pub ledger_key_locator: LedgerKeyLocatorAccount, } #[allow(clippy::too_many_lines)] pub async fn import( account: Option, accounts_file: &Utf8PathBuf, provider: &JsonRpcClient, import: &Import, config: &CastConfig, ui: &UI, ) -> Result { // TODO(#3556): Remove this warning once we drop Argent account type if import.account_type == AccountType::Argent { ui.print_warning(WarningMessage::new( "Argent has rebranded as Ready. The `argent` option for the `--type` flag in `account import` is deprecated, please use `ready` instead.", )); ui.print_blank_line(); } let (signer_type, public_key) = if let Some(ledger_path) = import.ledger_key_locator.resolve(ui) { let public_key = ledger::get_ledger_public_key(&ledger_path, ui).await?; (SignerType::Ledger { ledger_path }, public_key) } else { let key_felt = match (&import.private_key, &import.private_key_file_path) { (Some(key), _) => *key, (None, Some(path)) => get_private_key_from_file(path) .with_context(|| format!("Failed to obtain private key from the file {path}"))?, (None, None) => get_private_key_from_input()?, }; let signing_key = SigningKey::from_secret_scalar(key_felt); let public_key = signing_key.verifying_key().scalar(); ( SignerType::Local { private_key: key_felt, }, public_key, ) }; let account_name = account .clone() .unwrap_or_else(|| generate_account_name(accounts_file).unwrap()); let fetched_class_hash = match provider .get_class_hash_at(BlockId::Tag(BlockTag::PreConfirmed), import.address) .await { Ok(class_hash) => Ok(Some(class_hash)), Err(ProviderError::StarknetError(StarknetError::ContractNotFound)) => Ok(None), Err(err) => Err(handle_rpc_error(err)), }?; let deployed: bool = fetched_class_hash.is_some(); let class_hash = if let (Some(from_provider), Some(from_user)) = (fetched_class_hash, import.class_hash) { ensure!( from_provider == from_user, "Incorrect class hash {:#x} for account address {:#x} was provided", from_user, import.address ); from_provider } else if let Some(from_user) = import.class_hash { check_class_hash_exists(provider, from_user).await?; from_user } else if let Some(from_provider) = fetched_class_hash { from_provider } else { bail!( "Class hash for the account address {:#x} could not be found. Please provide the class hash", import.address ); }; let chain_id = get_chain_id(provider).await?; if let Some(salt) = import.salt { let computed_address = compute_account_address(salt, public_key, class_hash, import.account_type, chain_id); ensure!( computed_address == import.address, "Computed address {:#x} does not match the provided address {:#x}. Please ensure that the provided salt, class hash, and account type are correct.", computed_address, import.address ); } let legacy = check_if_legacy_contract(Some(class_hash), import.address, provider).await?; let account_json = prepare_account_json( &signer_type, public_key, import.address, deployed, legacy, import.account_type, Some(class_hash), import.salt, ); write_account_to_accounts_file(&account_name, accounts_file, chain_id, account_json.clone())?; let add_profile_message = generate_add_profile_message( import.add_profile.as_ref(), &import.rpc, &account_name, accounts_file, None, config, )?; Ok(AccountImportResponse { add_profile: add_profile_message, account_name, }) } fn get_private_key_from_file(file_path: &Utf8PathBuf) -> Result { let private_key_string = std::fs::read_to_string(file_path.clone())?; Ok(private_key_string.parse()?) } fn parse_input_to_felt(input: &str) -> Result { Felt::try_from_hex_str(input) .or_else(|_| Felt::try_from_dec_str(input)) .with_context(|| format!("Failed to parse the value {input} as a felt")) } fn get_private_key_from_input() -> Result { let input = rpassword::prompt_password("Type in your private key and press enter: ") .expect("Failed to read private key from input"); parse_input_to_felt(&input) } #[cfg(test)] mod tests { use crate::starknet_commands::account::import::parse_input_to_felt; use conversions::string::TryFromHexStr; use starknet_types_core::felt::Felt; #[test] fn test_parse_hex_str() { let hex_str = "0x0000000000000000000000000000000000000000000000000000000000000001"; let result = parse_input_to_felt(hex_str); assert_eq!(result.unwrap(), Felt::try_from_hex_str("0x1").unwrap()); } #[test] fn test_parse_hex_str_padded() { let hex_str = "0x1a2b3c"; let result = parse_input_to_felt(hex_str); assert_eq!(result.unwrap(), Felt::try_from_hex_str("0x1a2b3c").unwrap()); } #[test] fn test_parse_hex_str_invalid() { let hex_str = "0xz"; let result = parse_input_to_felt(hex_str); assert!(result.is_err()); let error_message = result.unwrap_err().to_string(); assert_eq!("Failed to parse the value 0xz as a felt", error_message); } #[test] fn test_parse_dec_str() { let dec_str = "123"; let result = parse_input_to_felt(dec_str); assert_eq!(result.unwrap(), Felt::from(123)); } #[test] fn test_parse_dec_str_negative() { let dec_str = "-123"; let result = parse_input_to_felt(dec_str); assert!(result.is_err()); let error_message = result.unwrap_err().to_string(); assert_eq!("Failed to parse the value -123 as a felt", error_message); } #[test] fn test_parse_invalid_str() { let invalid_str = "invalid"; let result = parse_input_to_felt(invalid_str); assert!(result.is_err()); let error_message = result.unwrap_err().to_string(); assert_eq!("Failed to parse the value invalid as a felt", error_message); } } ================================================ FILE: crates/sncast/src/starknet_commands/account/list.rs ================================================ use anyhow::Error; use camino::Utf8PathBuf; use clap::Args; use conversions::string::IntoHexStr; use foundry_ui::Message; use itertools::Itertools; use serde::Deserialize; use serde::Serialize; use serde_json::Value; use serde_json::json; use sncast::AccountType; use sncast::{ AccountData, NestedMap, SignerType, check_account_file_exists, read_and_parse_json_file, }; use std::collections::HashMap; use std::fmt::Write; #[derive(Args, Debug)] #[command( name = "list", about = "List available accounts", before_help = "Warning! This command may expose vulnerable cryptographic information, e.g. accounts' private keys" )] pub struct List { /// Display private keys #[arg(short = 'p', long = "display-private-keys")] pub display_private_keys: bool, } #[derive(Deserialize, Serialize, Clone, Debug)] pub struct AccountDataRepresentationMessage { pub public_key: String, #[serde(flatten, skip_serializing_if = "Option::is_none")] pub signer_type: Option, #[serde(skip_serializing_if = "Option::is_none")] pub network: Option, #[serde(skip_serializing_if = "Option::is_none")] pub address: Option, #[serde(skip_serializing_if = "Option::is_none")] pub salt: Option, #[serde(skip_serializing_if = "Option::is_none")] pub deployed: Option, #[serde(skip_serializing_if = "Option::is_none")] pub class_hash: Option, #[serde(skip_serializing_if = "Option::is_none")] pub legacy: Option, #[serde(default, rename(serialize = "type", deserialize = "type"))] #[serde(skip_serializing_if = "Option::is_none")] pub account_type: Option, } impl AccountDataRepresentationMessage { fn new(account: &AccountData, display_private_key: bool) -> Self { Self { signer_type: match &account.signer_type { SignerType::Local { .. } if !display_private_key => None, other => Some(other.clone()), }, public_key: account.public_key.into_hex_string(), network: None, address: account.address.map(IntoHexStr::into_hex_string), salt: account.salt.map(IntoHexStr::into_hex_string), deployed: account.deployed, class_hash: account.class_hash.map(IntoHexStr::into_hex_string), legacy: account.legacy, account_type: account.account_type, } } fn set_network(&mut self, network: &str) { self.network = Some(network.to_owned()); } } fn read_and_flatten( accounts_file: &Utf8PathBuf, display_private_keys: bool, ) -> anyhow::Result> { let networks: NestedMap = read_and_parse_json_file(accounts_file)?; let mut result = HashMap::new(); for (network, accounts) in networks.iter().sorted_by_key(|(name, _)| *name) { for (name, data) in accounts.iter().sorted_by_key(|(name, _)| *name) { let mut data_repr = AccountDataRepresentationMessage::new(data, display_private_keys); data_repr.set_network(network); result.insert(name.to_owned(), data_repr); } } Ok(result) } impl Message for AccountDataRepresentationMessage { fn text(&self) -> String { let mut result = String::new(); if let Some(ref network) = self.network { let _ = writeln!(result, " network: {network}"); } let _ = writeln!(result, " public key: {}", self.public_key); match &self.signer_type { Some(SignerType::Local { private_key }) => { let _ = writeln!(result, " private key: {}", private_key.into_hex_string()); } Some(SignerType::Ledger { ledger_path }) => { let _ = writeln!(result, " ledger path: {}", ledger_path.derivation_string()); } None => {} } if let Some(ref address) = self.address { let _ = writeln!(result, " address: {address}"); } if let Some(ref salt) = self.salt { let _ = writeln!(result, " salt: {salt}"); } if let Some(ref class_hash) = self.class_hash { let _ = writeln!(result, " class hash: {class_hash}"); } if let Some(ref deployed) = self.deployed { let _ = writeln!(result, " deployed: {deployed}"); } if let Some(ref legacy) = self.legacy { let _ = writeln!(result, " legacy: {legacy}"); } if let Some(ref account_type) = self.account_type { // TODO(#3556): Remove this adjustment when the `argent` account type is removed let displayed_type = if account_type == &AccountType::Argent { &AccountType::Ready } else { account_type }; let _ = writeln!(result, " type: {displayed_type:?}"); } result.trim_end().to_string() } fn json(&self) -> Value { json!(self) } } pub struct AccountsListMessage { accounts_file: Utf8PathBuf, accounts_file_path: String, display_private_keys: bool, } impl AccountsListMessage { pub fn new(accounts_file: Utf8PathBuf, display_private_keys: bool) -> Result { check_account_file_exists(&accounts_file)?; let accounts_file_path = accounts_file .canonicalize() .expect("Failed to resolve the accounts file path"); let accounts_file_path = accounts_file_path .to_str() .expect("Failed to resolve an absolute path to the accounts file"); Ok(Self { accounts_file, accounts_file_path: accounts_file_path.to_string(), display_private_keys, }) } } impl Message for AccountsListMessage { fn text(&self) -> String { let accounts = read_and_flatten(&self.accounts_file, self.display_private_keys).unwrap_or_default(); if accounts.is_empty() { format!("No accounts available at {}", self.accounts_file_path) } else { let mut result = format!("Available accounts (at {}):", self.accounts_file_path); for (name, data) in accounts.iter().sorted_by_key(|(name, _)| *name) { let _ = writeln!(result, "\n- {}:\n{}", name, data.text()); } if !self.display_private_keys { let _ = writeln!( result, "\nTo show private keys too, run with --display-private-keys or -p" ); } result } } fn json(&self) -> Value { let accounts = read_and_flatten(&self.accounts_file, self.display_private_keys).unwrap_or_default(); let mut accounts_map: HashMap = HashMap::new(); for (name, data) in &accounts { accounts_map.insert(name.clone(), data.clone()); } json!(&accounts_map) } } ================================================ FILE: crates/sncast/src/starknet_commands/account/mod.rs ================================================ use crate::starknet_commands::account::create::Create; use crate::starknet_commands::account::delete::Delete; use crate::starknet_commands::account::deploy::Deploy; use crate::starknet_commands::account::import::Import; use crate::starknet_commands::account::list::{AccountsListMessage, List}; use crate::{process_command_result, starknet_commands}; use anyhow::{Context, Result, bail}; use camino::Utf8PathBuf; use clap::{Args, Subcommand}; use configuration::resolve_config_file; use configuration::{load_config, search_config_upwards_relative_to}; use serde_json::json; use sncast::helpers::account::{generate_account_name, load_accounts}; use sncast::helpers::interactive::prompt_to_add_account_as_default; use sncast::helpers::rpc::RpcArgs; use sncast::response::explorer_link::block_explorer_link_if_allowed; use sncast::response::ui::UI; use sncast::{ AccountType, chain_id_to_network_name, decode_chain_id, helpers::configuration::CastConfig, }; use sncast::{SignerSource, SignerType, WaitForTx, get_chain_id}; use starknet_rust::providers::Provider; use starknet_types_core::felt::Felt; use std::io::{self, IsTerminal}; use std::{fs::OpenOptions, io::Write}; use toml::Value; pub mod create; pub mod delete; pub mod deploy; pub mod import; pub mod list; #[derive(Args)] #[command(about = "Creates and deploys an account to the Starknet")] pub struct Account { #[command(subcommand)] pub command: Commands, } #[derive(Debug, Subcommand)] pub enum Commands { Import(Import), Create(Create), Deploy(Deploy), Delete(Delete), List(List), } #[allow(clippy::too_many_arguments)] pub fn prepare_account_json( signer_type: &SignerType, public_key: Felt, address: Felt, deployed: bool, legacy: bool, account_type: AccountType, class_hash: Option, salt: Option, ) -> serde_json::Value { // TODO(#3556): Once `Argent` variant is deleted, use `account_type` directly let saved_account_type = match account_type { AccountType::Argent => AccountType::Ready, _ => account_type, }; let mut account_json = json!({ "public_key": format!("{public_key:#x}"), "address": format!("{address:#x}"), "type": format!("{saved_account_type}").to_lowercase().replace("openzeppelin", "open_zeppelin"), "deployed": deployed, "legacy": legacy, }); match signer_type { SignerType::Local { private_key } => { account_json["private_key"] = serde_json::Value::String(format!("{private_key:#x}")); } SignerType::Ledger { ledger_path } => { account_json["ledger_path"] = serde_json::Value::String(ledger_path.derivation_string()); } } if let Some(salt) = salt { account_json["salt"] = serde_json::Value::String(format!("{salt:#x}")); } if let Some(class_hash) = class_hash { account_json["class_hash"] = serde_json::Value::String(format!("{class_hash:#x}")); } account_json } pub fn write_account_to_accounts_file( account: &str, accounts_file: &Utf8PathBuf, chain_id: Felt, account_json: serde_json::Value, ) -> Result<()> { if !accounts_file.exists() { std::fs::create_dir_all(accounts_file.clone().parent().unwrap())?; std::fs::write(accounts_file.clone(), "{}")?; } let mut items = load_accounts(accounts_file)?; let network_name = chain_id_to_network_name(chain_id); if !items[&network_name][account].is_null() { bail!( "Account with name = {} already exists in network with chain_id = {}", account, decode_chain_id(chain_id) ); } items[&network_name][account] = account_json; std::fs::write( accounts_file.clone(), serde_json::to_string_pretty(&items).unwrap(), )?; Ok(()) } pub fn add_created_profile_to_configuration( profile: Option<&str>, cast_config: &CastConfig, path: &Utf8PathBuf, ) -> Result<()> { if !load_config::(Some(path), profile) .unwrap_or_default() .account .is_empty() { bail!( "Failed to add profile = {} to the snfoundry.toml. Profile already exists", profile.unwrap_or("default") ); } let toml_string = { let mut new_profile = toml::value::Table::new(); if let Some(url) = &cast_config.url { new_profile.insert("url".to_string(), Value::String(url.to_string())); } else if let Some(network) = &cast_config.network { new_profile.insert("network".to_string(), Value::String(network.to_string())); } new_profile.insert( "account".to_string(), Value::String(cast_config.account.clone()), ); if let Some(keystore) = cast_config.keystore.clone() { new_profile.insert("keystore".to_string(), Value::String(keystore.to_string())); } else { new_profile.insert( "accounts-file".to_string(), Value::String(cast_config.accounts_file.to_string()), ); } let mut profile_config = toml::value::Table::new(); profile_config.insert( profile.map_or_else(|| cast_config.account.clone(), ToString::to_string), Value::Table(new_profile), ); let mut sncast_config = toml::value::Table::new(); sncast_config.insert(String::from("sncast"), Value::Table(profile_config)); toml::to_string(&Value::Table(sncast_config)).context("Failed to convert toml to string")? }; let config_path = search_config_upwards_relative_to(path)?; let mut snfoundry_toml = OpenOptions::new() .create(true) .append(true) .open(config_path) .context("Failed to open snfoundry.toml")?; snfoundry_toml .write_all(format!("\n{toml_string}").as_bytes()) .context("Failed to write to the snfoundry.toml")?; Ok(()) } fn generate_add_profile_message( profile_name: Option<&String>, rpc_args: &RpcArgs, account_name: &str, accounts_file: &Utf8PathBuf, keystore: Option, config: &CastConfig, ) -> Result> { if let Some(profile_name) = profile_name { let (url, network) = if rpc_args.url.is_some() || rpc_args.network.is_some() { (rpc_args.url.clone(), rpc_args.network) } else { (config.url.clone(), config.network) }; let config = CastConfig { url, network, account: account_name.into(), accounts_file: accounts_file.into(), keystore, ..Default::default() }; let config_path = resolve_config_file(); add_created_profile_to_configuration(Some(profile_name), &config, &config_path)?; Ok(Some(format!( "Profile {profile_name} successfully added to {config_path}", ))) } else { Ok(None) } } #[allow(clippy::too_many_lines)] pub async fn account( account: Account, config: CastConfig, ui: &UI, wait_config: WaitForTx, ) -> anyhow::Result<()> { match account.command { Commands::Import(import) => { let provider = import.rpc.get_provider(&config, ui).await?; let result = starknet_commands::account::import::import( import.name.clone(), &config.accounts_file, &provider, &import, &config, ui, ) .await; let run_interactive_prompt = !import.silent && result.is_ok() && io::stdout().is_terminal(); if run_interactive_prompt && let Some(account_name) = result.as_ref().ok().map(|r| r.account_name.clone()) && let Err(err) = prompt_to_add_account_as_default(account_name.as_str()) { // TODO(#3436) ui.print_error( "account import", format!("Error: Failed to launch interactive prompt: {err}"), ); } process_command_result("account import", result, ui, None); Ok(()) } Commands::Create(create) => { let provider = create.rpc.get_provider(&config, ui).await?; let chain_id = get_chain_id(&provider).await?; let signer_type = create .ledger_key_locator .resolve(ui) .map(|ledger_path| SignerType::Ledger { ledger_path }); let signer_source = SignerSource::new(config.keystore.clone(), signer_type.as_ref())?; let account = if config.keystore.is_none() { create .name .clone() .unwrap_or_else(|| generate_account_name(&config.accounts_file).unwrap()) } else { config.account.clone() }; let result = starknet_commands::account::create::create( &account, &config.accounts_file, &provider, chain_id, &create, &config, &signer_source, ui, ) .await; let block_explorer_link = block_explorer_link_if_allowed(&result, provider.chain_id().await?, &config).await; process_command_result("account create", result, ui, block_explorer_link); Ok(()) } Commands::Deploy(deploy) => { let provider = deploy.rpc.get_provider(&config, ui).await?; let fee_args = deploy.fee_args.clone(); let chain_id = get_chain_id(&provider).await?; let result = starknet_commands::account::deploy::deploy( &provider, &config.accounts_file, &deploy, chain_id, wait_config, &config.account, config.keystore.clone(), fee_args, ui, ) .await; let run_interactive_prompt = !deploy.silent && result.is_ok() && io::stdout().is_terminal(); if config.keystore.is_none() && run_interactive_prompt && let Err(err) = prompt_to_add_account_as_default( deploy .name .as_ref() .expect("Must be provided when using accounts file"), ) { // TODO(#3436) ui.print_error( "account deploy", format!("Error: Failed to launch interactive prompt: {err}"), ); } let block_explorer_link = block_explorer_link_if_allowed(&result, provider.chain_id().await?, &config).await; process_command_result("account deploy", result, ui, block_explorer_link); Ok(()) } Commands::Delete(delete) => { let network_name = starknet_commands::account::delete::get_network_name(&delete, &config, ui).await?; let result = starknet_commands::account::delete::delete( &delete.name, &config.accounts_file, &network_name, delete.yes, ); process_command_result("account delete", result, ui, None); Ok(()) } Commands::List(options) => { ui.print_message( "account delete", AccountsListMessage::new(config.accounts_file, options.display_private_keys)?, ); Ok(()) } } } #[cfg(test)] mod tests { use camino::Utf8PathBuf; use configuration::test_utils::copy_config_to_tempdir; use sncast::helpers::{configuration::CastConfig, constants::DEFAULT_ACCOUNTS_FILE}; use std::fs; use url::Url; use crate::starknet_commands::account::add_created_profile_to_configuration; #[test] fn test_add_created_profile_to_configuration_happy_case() { let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); let path = Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap(); let config = CastConfig { url: Some(Url::parse("http://some-url.com/").unwrap()), network: None, account: String::from("some-name"), accounts_file: "accounts".into(), ..Default::default() }; let res = add_created_profile_to_configuration( Some(&String::from("some-name")), &config, &path.clone(), ); assert!(res.is_ok()); let contents = fs::read_to_string(path.join("snfoundry.toml")).expect("Failed to read snfoundry.toml"); assert!(contents.contains("[sncast.some-name]")); assert!(contents.contains("account = \"some-name\"")); assert!(contents.contains("url = \"http://some-url.com/\"")); assert!(contents.contains("accounts-file = \"accounts\"")); } #[test] fn test_add_created_profile_to_configuration_profile_already_exists() { let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); let config = CastConfig { url: Some(Url::parse("http://some-url.com/").unwrap()), network: None, account: String::from("user1"), accounts_file: DEFAULT_ACCOUNTS_FILE.into(), ..Default::default() }; let res = add_created_profile_to_configuration( Some(&String::from("default")), &config, &Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap(), ); assert!(res.is_err()); } } ================================================ FILE: crates/sncast/src/starknet_commands/call.rs ================================================ use crate::Arguments; use anyhow::Result; use clap::Args; use sncast::helpers::rpc::RpcArgs; use sncast::response::call::CallResponse; use sncast::response::errors::StarknetCommandError; use starknet_rust::core::types::{BlockId, FunctionCall}; use starknet_rust::providers::jsonrpc::HttpTransport; use starknet_rust::providers::{JsonRpcClient, Provider}; use starknet_types_core::felt::Felt; #[derive(Args)] #[command(about = "Call a contract instance on Starknet", long_about = None)] pub struct Call { /// Address of the called contract (hex) #[arg(short = 'd', long)] pub contract_address: Felt, /// Name of the contract function to be called #[arg(short, long)] pub function: String, #[command(flatten)] pub arguments: Arguments, /// Block identifier on which call should be performed. /// Possible values: `pre_confirmed`, `latest`, block hash (0x prefixed string) /// and block number (u64) #[arg(short, long, default_value = "pre_confirmed")] pub block_id: String, #[command(flatten)] pub rpc: RpcArgs, } pub async fn call( contract_address: Felt, entry_point_selector: Felt, calldata: Vec, provider: &JsonRpcClient, block_id: &BlockId, ) -> Result { let function_call = FunctionCall { contract_address, entry_point_selector, calldata, }; let res = provider.call(function_call, block_id).await; match res { Ok(response) => Ok(CallResponse { response }), Err(error) => Err(StarknetCommandError::ProviderError(error.into())), } } ================================================ FILE: crates/sncast/src/starknet_commands/declare.rs ================================================ use anyhow::{Context, Result, anyhow}; use clap::Args; use conversions::IntoConv; use conversions::byte_array::ByteArray; use shared::rpc::get_starknet_version; use sncast::helpers::artifacts::CastStarknetContractArtifacts; use sncast::helpers::fee::{FeeArgs, FeeSettings}; use sncast::helpers::rpc::RpcArgs; use sncast::response::declare::{ AlreadyDeclaredResponse, DeclareResponse, DeclareTransactionResponse, }; use sncast::response::errors::{SNCastProviderError, SNCastStarknetError, StarknetCommandError}; use sncast::response::ui::UI; use sncast::{ErrorData, WaitForTx, apply_optional_fields, handle_wait_for_tx}; use starknet_rust::accounts::AccountError::Provider; use starknet_rust::accounts::{ConnectedAccount, DeclarationV3}; use starknet_rust::core::types::{ ContractExecutionError, DeclareTransactionResult, StarknetError, TransactionExecutionErrorData, }; use starknet_rust::providers::ProviderError; use starknet_rust::{ accounts::{Account, SingleOwnerAccount}, core::types::contract::{CompiledClass, SierraClass}, providers::jsonrpc::{HttpTransport, JsonRpcClient}, signers::Signer, }; use starknet_types_core::felt::Felt; use std::collections::HashMap; use std::sync::Arc; use universal_sierra_compiler_api::compile_contract_sierra; /// Common args shared by declare command variants. #[derive(Args)] pub struct DeclareCommonArgs { #[command(flatten)] pub fee_args: FeeArgs, /// Nonce of the transaction. If not provided, nonce will be set automatically #[arg(short, long)] pub nonce: Option, #[command(flatten)] pub rpc: RpcArgs, } #[derive(Args)] #[command(about = "Declare a contract to starknet", long_about = None)] pub struct Declare { /// Contract name #[arg(short = 'c', long)] pub contract_name: String, /// Specifies scarb package to be used #[arg(long)] pub package: Option, #[command(flatten)] pub common: DeclareCommonArgs, } // TODO(#3785) #[expect(clippy::too_many_arguments)] pub async fn declare( contract_name: String, fee_args: FeeArgs, nonce: Option, account: &SingleOwnerAccount<&JsonRpcClient, S>, artifacts: &HashMap, wait_config: WaitForTx, skip_on_already_declared: bool, ui: &UI, ) -> Result where S: Signer + Sync + Send, { let contract_artifacts = artifacts .get(&contract_name) .ok_or(StarknetCommandError::ContractArtifactsNotFound(ErrorData { data: ByteArray::from(contract_name.as_str()), }))?; let contract_definition: SierraClass = serde_json::from_str(&contract_artifacts.sierra) .context("Failed to parse sierra artifact")?; let casm_contract_definition: CompiledClass = serde_json::from_str(&contract_artifacts.casm).context("Failed to parse casm artifact")?; declare_with_artifacts( contract_definition, casm_contract_definition, &fee_args, nonce, account, wait_config, skip_on_already_declared, ui, ) .await } #[expect(clippy::result_large_err)] pub fn compile_sierra_to_casm( sierra_class: &SierraClass, ) -> Result { let casm_json: String = serde_json::to_string( &compile_contract_sierra( &serde_json::to_value(sierra_class) .with_context(|| "Failed to convert sierra to JSON value".to_string())?, ) .with_context(|| "Failed to compile sierra to casm".to_string())?, ) .expect("serialization should succeed"); let casm: CompiledClass = serde_json::from_str(&casm_json) .with_context(|| "Failed to deserialize casm JSON into CompiledClass".to_string())?; Ok(casm) } #[allow(clippy::too_many_arguments)] pub async fn declare_with_artifacts( sierra_class: SierraClass, compiled_casm: CompiledClass, fee_args: &FeeArgs, nonce: Option, account: &SingleOwnerAccount<&JsonRpcClient, S>, wait_config: WaitForTx, skip_on_already_declared: bool, ui: &UI, ) -> Result where S: Signer + Sync + Send, { let starknet_version = get_starknet_version(account.provider()).await?; let hash_function = CompiledClass::hash_function_from_starknet_version(&starknet_version) .ok_or(anyhow!("Unsupported Starknet version: {starknet_version}"))?; let casm_class_hash = compiled_casm .class_hash_with_hash_function(hash_function) .map_err(anyhow::Error::from)?; let class_hash = sierra_class.class_hash().map_err(anyhow::Error::from)?; let declaration = account.declare_v3( Arc::new(sierra_class.flatten().map_err(anyhow::Error::from)?), casm_class_hash, ); let fee_settings = if fee_args.max_fee.is_some() { let fee_estimate = declaration .estimate_fee() .await .expect("Failed to estimate fee"); fee_args.try_into_fee_settings(Some(&fee_estimate)) } else { fee_args.try_into_fee_settings(None) }; let FeeSettings { l1_gas, l1_gas_price, l2_gas, l2_gas_price, l1_data_gas, l1_data_gas_price, tip, } = fee_settings.expect("Failed to convert to fee settings"); let declaration = apply_optional_fields!( declaration, l1_gas => DeclarationV3::l1_gas, l1_gas_price => DeclarationV3::l1_gas_price, l2_gas => DeclarationV3::l2_gas, l2_gas_price => DeclarationV3::l2_gas_price, l1_data_gas => DeclarationV3::l1_data_gas, l1_data_gas_price => DeclarationV3::l1_data_gas_price, tip => DeclarationV3::tip, nonce => DeclarationV3::nonce ); let declared = declaration.send().await; match declared { Ok(DeclareTransactionResult { transaction_hash, class_hash, }) => handle_wait_for_tx( account.provider(), transaction_hash, DeclareResponse::Success(DeclareTransactionResponse { class_hash: class_hash.into_(), transaction_hash: transaction_hash.into_(), }), wait_config, ui, ) .await .map_err(StarknetCommandError::from), Err(Provider(ProviderError::StarknetError(StarknetError::ClassAlreadyDeclared))) if skip_on_already_declared => { Ok(DeclareResponse::AlreadyDeclared(AlreadyDeclaredResponse { class_hash: class_hash.into_(), })) } Err(Provider(ProviderError::StarknetError(StarknetError::ClassAlreadyDeclared))) => Err( StarknetCommandError::ProviderError(SNCastProviderError::StarknetError( SNCastStarknetError::ClassAlreadyDeclared(class_hash.into_()), )), ), Err(Provider(ProviderError::StarknetError(StarknetError::TransactionExecutionError( TransactionExecutionErrorData { execution_error: ContractExecutionError::Message(message), .. }, )))) if message.contains("is already declared") => { if skip_on_already_declared { Ok(DeclareResponse::AlreadyDeclared(AlreadyDeclaredResponse { class_hash: class_hash.into_(), })) } else { Err(StarknetCommandError::ProviderError( SNCastProviderError::StarknetError(SNCastStarknetError::ClassAlreadyDeclared( class_hash.into_(), )), )) } } Err(Provider(error)) => Err(StarknetCommandError::ProviderError(error.into())), Err(error) => Err(anyhow!(format!("Unexpected error occurred: {error}")).into()), } } ================================================ FILE: crates/sncast/src/starknet_commands/declare_from.rs ================================================ use crate::starknet_commands::declare::{ DeclareCommonArgs, compile_sierra_to_casm, declare_with_artifacts, }; use anyhow::{Context, Result}; use clap::{ArgGroup, Args}; use shared::verify_and_warn_if_incompatible_rpc_version; use sncast::helpers::rpc::FreeProvider; use sncast::response::declare::DeclareResponse; use sncast::response::errors::{SNCastProviderError, StarknetCommandError}; use sncast::response::ui::UI; use sncast::{Network, WaitForTx, get_provider}; use starknet_rust::accounts::SingleOwnerAccount; use starknet_rust::core::types::contract::{AbiEntry, SierraClass, SierraClassDebugInfo}; use starknet_rust::core::types::{BlockId, ContractClass, FlattenedSierraClass}; use starknet_rust::providers::Provider; use starknet_rust::providers::jsonrpc::{HttpTransport, JsonRpcClient}; use starknet_rust::signers::Signer; use starknet_types_core::felt::Felt; use std::path::PathBuf; use url::Url; #[derive(Args)] #[command( about = "Declare a contract from either: a Sierra file, or by fetching it from a different Starknet instance", long_about = None, group( ArgGroup::new("contract_source") .args(["sierra_file", "class_hash"]) .required(true) .multiple(false) ) )] pub struct DeclareFrom { /// Path to the compiled Sierra contract class JSON file #[arg(long, conflicts_with_all = ["block_id", "source_url", "source_network"])] pub sierra_file: Option, /// Class hash of contract declared on a different Starknet instance #[arg(short = 'g', long)] pub class_hash: Option, #[command(flatten)] pub source_rpc: SourceRpcArgs, /// Block identifier from which the contract will be fetched. /// Possible values: `pre_confirmed`, `latest`, block hash (0x prefixed string) /// and block number (u64) #[arg(short, long, default_value = "latest")] pub block_id: String, #[command(flatten)] pub common: DeclareCommonArgs, } #[derive(Args, Clone, Debug, Default)] #[group(required = false, multiple = false)] pub struct SourceRpcArgs { /// RPC provider url address #[arg(short, long)] pub source_url: Option, /// Use predefined network with a public provider. Note that this option may result in rate limits or other unexpected behavior #[arg(long)] pub source_network: Option, } impl SourceRpcArgs { pub async fn get_provider(&self, ui: &UI) -> Result> { let url = self .get_url() .await .context("Either `--source-network` or `--source-url` must be provided")?; let provider = get_provider(&url)?; // TODO(#3959) Remove `base_ui` verify_and_warn_if_incompatible_rpc_version(&provider, url, ui.base_ui()).await?; Ok(provider) } #[must_use] async fn get_url(&self) -> Option { if let Some(network) = self.source_network { let free_provider = FreeProvider::semi_random(); Some(network.url(&free_provider).await.ok()?) } else { self.source_url.clone() } } } pub enum ContractSource { LocalFile { sierra_path: PathBuf, }, Network { source_provider: JsonRpcClient, class_hash: Felt, block_id: BlockId, }, } pub async fn declare_from( contract_source: ContractSource, common_args: &DeclareCommonArgs, account: &SingleOwnerAccount<&JsonRpcClient, S>, wait_config: WaitForTx, skip_on_already_declared: bool, ui: &UI, ) -> Result where S: Signer + Sync + Send, { let sierra = match &contract_source { ContractSource::LocalFile { sierra_path } => { let sierra_json = std::fs::read_to_string(sierra_path).with_context(|| { format!("Failed to read sierra file at {}", sierra_path.display()) })?; serde_json::from_str(&sierra_json) .with_context(|| "Failed to parse sierra file".to_string())? } ContractSource::Network { source_provider, class_hash, block_id, } => { let class = source_provider .get_class(*block_id, *class_hash) .await .map_err(SNCastProviderError::from) .map_err(StarknetCommandError::from)?; let flattened_sierra = match class { ContractClass::Sierra(c) => c, ContractClass::Legacy(_) => { return Err(StarknetCommandError::UnknownError(anyhow::anyhow!( "Declaring from Cairo 0 (legacy) contracts is not supported" ))); } }; let sierra: SierraClass = flattened_sierra_to_sierra(flattened_sierra) .expect("Failed to parse flattened sierra class"); let sierra_class_hash = sierra.class_hash().map_err(anyhow::Error::from)?; if *class_hash != sierra_class_hash { return Err(StarknetCommandError::UnknownError(anyhow::anyhow!( "The provided sierra class hash {class_hash:#x} does not match the computed class hash {sierra_class_hash:#x} from the fetched contract." ))); } sierra } }; let casm = compile_sierra_to_casm(&sierra)?; declare_with_artifacts( sierra, casm, &common_args.fee_args, common_args.nonce, account, wait_config, skip_on_already_declared, ui, ) .await } fn flattened_sierra_to_sierra(class: FlattenedSierraClass) -> Result { Ok(SierraClass { sierra_program: class.sierra_program, sierra_program_debug_info: SierraClassDebugInfo { type_names: vec![], libfunc_names: vec![], user_func_names: vec![], }, contract_class_version: class.contract_class_version, entry_points_by_type: class.entry_points_by_type, abi: serde_json::from_str::>(&class.abi)?, }) } ================================================ FILE: crates/sncast/src/starknet_commands/deploy.rs ================================================ use anyhow::{Result, anyhow}; use clap::Args; use conversions::IntoConv; use sncast::helpers::fee::{FeeArgs, FeeSettings}; use sncast::helpers::rpc::RpcArgs; use sncast::response::deploy::StandardDeployResponse; use sncast::response::errors::StarknetCommandError; use sncast::response::ui::UI; use sncast::{WaitForTx, apply_optional_fields, handle_wait_for_tx}; use sncast::{extract_or_generate_salt, udc_uniqueness}; use starknet_rust::accounts::AccountError::Provider; use starknet_rust::accounts::{Account, ConnectedAccount, SingleOwnerAccount}; use starknet_rust::contract::{ContractFactory, DeploymentV3, UdcSelector}; use starknet_rust::core::utils::get_udc_deployed_address; use starknet_rust::providers::JsonRpcClient; use starknet_rust::providers::jsonrpc::HttpTransport; use starknet_rust::signers::Signer; use starknet_types_core::felt::Felt; #[derive(Args, Debug, Clone)] #[group(required = true, multiple = false)] pub struct ContractIdentifier { /// Class hash of contract to deploy #[arg(short = 'g', long, conflicts_with = "package")] pub class_hash: Option, /// Contract name #[arg(long)] pub contract_name: Option, } #[derive(Args)] pub struct DeployCommonArgs { #[command(flatten)] pub contract_identifier: ContractIdentifier, #[command(flatten)] pub arguments: DeployArguments, /// Salt for the address #[arg(short, long)] pub salt: Option, /// If true, salt will be modified with an account address #[arg(long)] pub unique: bool, /// Specifies scarb package to be used. Only possible to use with `--contract-name`. #[arg(long, conflicts_with = "class_hash")] pub package: Option, } #[derive(Args)] #[command(about = "Deploy a contract on Starknet")] pub struct Deploy { #[command(flatten)] pub common: DeployCommonArgs, #[command(flatten)] pub fee_args: FeeArgs, /// Nonce of the transaction. If not provided, nonce will be set automatically #[arg(short, long)] pub nonce: Option, #[command(flatten)] pub rpc: RpcArgs, } #[derive(Debug, Clone, clap::Args)] #[group(multiple = false)] pub struct DeployArguments { /// Arguments of the called function serialized as a series of felts #[arg(short, long, value_delimiter = ' ', num_args = 1..)] pub constructor_calldata: Option>, // Arguments of the called function as a comma-separated string of Cairo expressions #[arg(long)] pub arguments: Option, } #[expect(clippy::ptr_arg, clippy::too_many_arguments)] pub async fn deploy( class_hash: Felt, calldata: &Vec, salt: Option, unique: bool, fee_args: FeeArgs, nonce: Option, account: &SingleOwnerAccount<&JsonRpcClient, S>, wait_config: WaitForTx, ui: &UI, ) -> Result where S: Signer + Sync + Send, { let salt = extract_or_generate_salt(salt); // TODO(#3628): Use `ContractFactory::new` once new UDC address is the default one in starknet-rs let factory = ContractFactory::new_with_udc(class_hash, account, UdcSelector::New); let deployment = factory.deploy_v3(calldata.clone(), salt, unique); let fee_settings = if fee_args.max_fee.is_some() { let fee_estimate = deployment .estimate_fee() .await .expect("Failed to estimate fee"); fee_args.try_into_fee_settings(Some(&fee_estimate)) } else { fee_args.try_into_fee_settings(None) }; let FeeSettings { l1_gas, l1_gas_price, l2_gas, l2_gas_price, l1_data_gas, l1_data_gas_price, tip, } = fee_settings.expect("Failed to convert to fee settings"); let deployment = apply_optional_fields!( deployment, l1_gas => DeploymentV3::l1_gas, l1_gas_price => DeploymentV3::l1_gas_price, l2_gas => DeploymentV3::l2_gas, l2_gas_price => DeploymentV3::l2_gas_price, l1_data_gas => DeploymentV3::l1_data_gas, l1_data_gas_price => DeploymentV3::l1_data_gas_price, tip => DeploymentV3::tip, nonce => DeploymentV3::nonce ); let result = deployment.send().await; match result { Ok(result) => handle_wait_for_tx( account.provider(), result.transaction_hash, StandardDeployResponse { contract_address: get_udc_deployed_address( salt, class_hash, &udc_uniqueness(unique, account.address()), calldata, ) .into_(), transaction_hash: result.transaction_hash.into_(), }, wait_config, ui, ) .await .map_err(StarknetCommandError::from), Err(Provider(error)) => Err(StarknetCommandError::ProviderError(error.into())), Err(error) => Err(anyhow!(format!("Unexpected error occurred: {error}")).into()), } } ================================================ FILE: crates/sncast/src/starknet_commands/get/balance.rs ================================================ use anyhow::{Error, Result}; use clap::Args; use primitive_types::U256; use sncast::helpers::command::process_command_result; use sncast::helpers::configuration::CastConfig; use sncast::helpers::rpc::RpcArgs; use sncast::helpers::token::Token; use sncast::response::balance::BalanceResponse; use sncast::response::errors::SNCastProviderError; use sncast::response::errors::StarknetCommandError; use sncast::response::ui::UI; use sncast::{get_account, get_block_id}; use starknet_rust::{ core::{types::FunctionCall, utils::get_selector_from_name}, providers::{JsonRpcClient, Provider, jsonrpc::HttpTransport}, }; use starknet_types_core::felt::Felt; #[derive(Args, Debug, Clone)] #[group(multiple = false)] pub struct TokenIdentifier { /// Symbol of the token to check the balance for. /// Supported tokens are: strk, eth. /// Defaults to strk. #[arg(value_enum, short = 't', long)] pub token: Option, /// Token contract address to check the balance for. Token needs to be compatible with ERC-20 standard. #[arg(short = 'd', long)] pub token_address: Option, } impl TokenIdentifier { pub fn contract_address(&self) -> Felt { if let Some(addr) = self.token_address { addr } else if let Some(tok) = self.token { tok.contract_address() } else { // Both token and token address are optional, hence we cannot have // default value for token at clap level. Token::default().contract_address() } } pub fn token_suffix(&self) -> Option { match (self.token, self.token_address) { (Some(token), None) => Some(token), (None, Some(_)) => None, (None, None) => Some(Token::default()), (Some(_), Some(_)) => unreachable!( "Clap should ensure that only one of `--token` or `--token-address` is provided" ), } } } #[derive(Args, Debug)] #[command(about = "Fetch balance of the account for specified token")] pub struct Balance { #[command(flatten)] pub token_identifier: TokenIdentifier, /// Block identifier on which balance should be fetched. /// Possible values: `pre_confirmed`, `latest`, block hash (0x prefixed string) /// and block number (u64) #[arg(short, long, default_value = "pre_confirmed")] pub block_id: String, #[command(flatten)] pub rpc: RpcArgs, } pub async fn balance(balance: Balance, config: CastConfig, ui: &UI) -> anyhow::Result<()> { let provider = balance.rpc.get_provider(&config, ui).await?; let account = get_account(&config, &provider, &balance.rpc, ui).await?; let result = get_balance(account.address(), &provider, &balance).await?; process_command_result("get balance", Ok(result), ui, None); Ok(()) } pub async fn get_balance( account_address: Felt, provider: &JsonRpcClient, balance: &Balance, ) -> Result { let call = FunctionCall { contract_address: balance.token_identifier.contract_address(), entry_point_selector: get_selector_from_name("balance_of").expect("Failed to get selector"), calldata: vec![account_address], }; let block_id = get_block_id(&balance.block_id)?; let res = provider .call(call, block_id) .await .map_err(|err| StarknetCommandError::ProviderError(SNCastProviderError::from(err)))?; let token_unit = balance .token_identifier .token_suffix() .map(|token| token.as_token_unit()); let balance = erc20_balance_to_u256(&res)?; Ok(BalanceResponse { balance, token_unit, }) } fn erc20_balance_to_u256(balance: &[Felt]) -> Result { if balance.len() != 2 { return Err(anyhow::anyhow!( "Balance response should contain exactly two values" )); } let low: u128 = balance[0].try_into()?; let high: u128 = balance[1].try_into()?; let mut bytes = [0u8; 32]; bytes[0..16].copy_from_slice(&low.to_le_bytes()); bytes[16..32].copy_from_slice(&high.to_le_bytes()); Ok(U256::from_little_endian(&bytes)) } #[cfg(test)] mod tests { use super::*; use primitive_types::U256; use starknet_rust::macros::felt; use starknet_types_core::felt::Felt; #[test] fn test_happy_case() { let balance = vec![ Felt::from_hex("0x1").unwrap(), Felt::from_hex("0x0").unwrap(), ]; let result = erc20_balance_to_u256(&balance).unwrap(); assert_eq!(result, U256::from(1u64)); let balance = vec![ Felt::from_hex("0xFFFFFFFFFFFFFFFF").unwrap(), Felt::from_hex("0x2").unwrap(), ]; let result = erc20_balance_to_u256(&balance).unwrap(); let expected = U256::from_dec_str("680564733841876926945195958937245974527").unwrap(); assert_eq!(result, expected); } #[test] fn test_invalid_length() { let balance = vec![felt!("0x1"), felt!("0x0"), felt!("0x0")]; let err = erc20_balance_to_u256(&balance).unwrap_err(); assert!( err.to_string() .contains("Balance response should contain exactly two values"), "Unexpected error: {err}" ); let balance = vec![Felt::from_hex("0x1").unwrap()]; let err = erc20_balance_to_u256(&balance).unwrap_err(); assert!( err.to_string() .contains("Balance response should contain exactly two values"), "Unexpected error: {err}" ); } } ================================================ FILE: crates/sncast/src/starknet_commands/get/class_hash_at.rs ================================================ use anyhow::Result; use clap::Args; use conversions::IntoConv; use sncast::get_block_id; use sncast::helpers::command::process_command_result; use sncast::helpers::configuration::CastConfig; use sncast::helpers::rpc::RpcArgs; use sncast::response::class_hash_at::ClassHashAtResponse; use sncast::response::errors::{SNCastProviderError, StarknetCommandError}; use sncast::response::explorer_link::block_explorer_link_if_allowed; use sncast::response::ui::UI; use starknet_rust::providers::jsonrpc::HttpTransport; use starknet_rust::providers::{JsonRpcClient, Provider}; use starknet_types_core::felt::Felt; #[derive(Debug, Args)] #[command(about = "Get the class hash of a contract deployed at a given address")] pub struct ClassHashAt { /// Address of the contract pub contract_address: Felt, /// Block identifier on which class hash should be fetched. /// Possible values: `pre_confirmed`, `latest`, block hash (0x prefixed string) /// and block number (u64) #[arg(short, long, default_value = "pre_confirmed")] pub block_id: String, #[command(flatten)] pub rpc: RpcArgs, } pub async fn class_hash_at(args: ClassHashAt, config: CastConfig, ui: &UI) -> anyhow::Result<()> { let provider = args.rpc.get_provider(&config, ui).await?; let result = get_class_hash_at(&provider, args.contract_address, &args.block_id).await; let chain_id = provider.chain_id().await?; let block_explorer_link = block_explorer_link_if_allowed(&result, chain_id, &config).await; process_command_result("get class-hash-at", result, ui, block_explorer_link); Ok(()) } async fn get_class_hash_at( provider: &JsonRpcClient, contract_address: Felt, block_id: &str, ) -> Result { let block_id = get_block_id(block_id)?; let class_hash = provider .get_class_hash_at(block_id, contract_address) .await .map_err(|err| StarknetCommandError::ProviderError(SNCastProviderError::from(err)))?; Ok(ClassHashAtResponse { class_hash: class_hash.into_(), }) } ================================================ FILE: crates/sncast/src/starknet_commands/get/mod.rs ================================================ use clap::{Args, Subcommand}; use sncast::helpers::configuration::CastConfig; use sncast::response::ui::UI; pub mod balance; pub mod class_hash_at; pub mod nonce; pub mod transaction; pub mod tx_status; #[derive(Args)] #[command(about = "Commands for querying Starknet state")] pub struct Get { #[command(subcommand)] pub command: GetCommands, } #[derive(Debug, Subcommand)] pub enum GetCommands { /// Get the status of a transaction TxStatus(tx_status::TxStatus), /// Get the transaction by hash Tx(transaction::Transaction), /// Fetch balance of the account for specified token Balance(balance::Balance), /// Get nonce of a contract Nonce(nonce::Nonce), /// Get class hash of a contract at a given address ClassHashAt(class_hash_at::ClassHashAt), } pub async fn get(get: Get, config: CastConfig, ui: &UI) -> anyhow::Result<()> { match get.command { GetCommands::TxStatus(status) => tx_status::tx_status(status, config, ui).await?, GetCommands::Tx(tx) => transaction::transaction(tx, config, ui).await?, GetCommands::Balance(balance) => balance::balance(balance, config, ui).await?, GetCommands::Nonce(nonce) => nonce::nonce(nonce, config, ui).await?, GetCommands::ClassHashAt(args) => class_hash_at::class_hash_at(args, config, ui).await?, } Ok(()) } ================================================ FILE: crates/sncast/src/starknet_commands/get/nonce.rs ================================================ use anyhow::Result; use clap::Args; use sncast::get_block_id; use sncast::helpers::command::process_command_result; use sncast::helpers::configuration::CastConfig; use sncast::helpers::rpc::RpcArgs; use sncast::response::errors::{SNCastProviderError, StarknetCommandError}; use sncast::response::nonce::NonceResponse; use sncast::response::ui::UI; use starknet_rust::providers::jsonrpc::HttpTransport; use starknet_rust::providers::{JsonRpcClient, Provider}; use starknet_types_core::felt::Felt; #[derive(Debug, Args)] #[command(about = "Get the nonce of a contract")] pub struct Nonce { /// Address of the contract pub contract_address: Felt, /// Block identifier on which nonce should be fetched. /// Possible values: `pre_confirmed`, `latest`, block hash (0x prefixed string) /// and block number (u64) #[arg(short, long, default_value = "pre_confirmed")] pub block_id: String, #[command(flatten)] pub rpc: RpcArgs, } pub async fn nonce(nonce: Nonce, config: CastConfig, ui: &UI) -> Result<()> { let provider = nonce.rpc.get_provider(&config, ui).await?; let result = get_nonce(&provider, nonce.contract_address, &nonce.block_id).await; process_command_result("get nonce", result, ui, None); Ok(()) } pub async fn get_nonce( provider: &JsonRpcClient, contract_address: Felt, block_id: &str, ) -> Result { let block_id = get_block_id(block_id)?; let nonce = provider .get_nonce(block_id, contract_address) .await .map_err(|err| StarknetCommandError::ProviderError(SNCastProviderError::from(err)))?; Ok(NonceResponse { nonce }) } ================================================ FILE: crates/sncast/src/starknet_commands/get/transaction.rs ================================================ use anyhow::Context; use clap::Args; use sncast::helpers::command::process_command_result; use sncast::helpers::configuration::CastConfig; use sncast::helpers::rpc::RpcArgs; use sncast::response::errors::StarknetCommandError; use sncast::response::explorer_link::block_explorer_link_if_allowed; use sncast::response::transaction::TransactionResponse; use sncast::response::ui::UI; use starknet_rust::core::types::TransactionResponseFlag; use starknet_rust::providers::jsonrpc::HttpTransport; use starknet_rust::providers::{JsonRpcClient, Provider}; use starknet_types_core::felt::Felt; #[derive(Debug, Args)] #[command(about = "Get the details of a transaction")] pub struct Transaction { #[allow(clippy::struct_field_names)] /// Hash of the transaction pub transaction_hash: Felt, /// Include proof facts in transaction response #[arg(long)] pub with_proof_facts: bool, #[command(flatten)] pub rpc: RpcArgs, } pub async fn transaction(tx: Transaction, config: CastConfig, ui: &UI) -> anyhow::Result<()> { let provider = tx.rpc.get_provider(&config, ui).await?; let result = get_transaction(&provider, tx.transaction_hash, tx.with_proof_facts) .await .context("Failed to get transaction"); let chain_id = provider.chain_id().await?; let block_explorer_link = block_explorer_link_if_allowed(&result, chain_id, &config).await; process_command_result("get tx", result, ui, block_explorer_link); Ok(()) } async fn get_transaction( provider: &JsonRpcClient, transaction_hash: Felt, with_proof_facts: bool, ) -> Result { let response_flags = if with_proof_facts { Some(&[TransactionResponseFlag::IncludeProofFacts][..]) } else { None }; provider .get_transaction_by_hash(transaction_hash, response_flags) .await .map(TransactionResponse) .map_err(|err| StarknetCommandError::ProviderError(err.into())) } ================================================ FILE: crates/sncast/src/starknet_commands/get/tx_status.rs ================================================ use anyhow::Context; use clap::Args; use sncast::helpers::command::process_command_result; use sncast::helpers::configuration::CastConfig; use sncast::helpers::rpc::RpcArgs; use sncast::response::errors::StarknetCommandError; use sncast::response::tx_status::{ExecutionStatus, FinalityStatus, TransactionStatusResponse}; use sncast::response::ui::UI; use starknet_rust::core::types::{TransactionExecutionStatus, TransactionStatus}; use starknet_rust::providers::jsonrpc::HttpTransport; use starknet_rust::providers::{JsonRpcClient, Provider}; use starknet_types_core::felt::Felt; #[derive(Debug, Args)] #[command(about = "Get the status of a transaction")] pub struct TxStatus { /// Hash of the transaction pub transaction_hash: Felt, #[command(flatten)] pub rpc: RpcArgs, } pub async fn tx_status(tx_status: TxStatus, config: CastConfig, ui: &UI) -> anyhow::Result<()> { let provider = tx_status.rpc.get_provider(&config, ui).await?; let result = get_tx_status(&provider, tx_status.transaction_hash) .await .context("Failed to get transaction status"); process_command_result("get tx-status", result, ui, None); Ok(()) } pub async fn get_tx_status( provider: &JsonRpcClient, transaction_hash: Felt, ) -> Result { provider .get_transaction_status(transaction_hash) .await .map(|status| build_transaction_status_response(&status)) .map_err(|error| StarknetCommandError::ProviderError(error.into())) } fn build_transaction_status_response(status: &TransactionStatus) -> TransactionStatusResponse { match status { TransactionStatus::Received => TransactionStatusResponse { finality_status: FinalityStatus::Received, execution_status: None, }, TransactionStatus::Candidate => TransactionStatusResponse { finality_status: FinalityStatus::Candidate, execution_status: None, }, TransactionStatus::PreConfirmed(tx_exec_result) => TransactionStatusResponse { finality_status: FinalityStatus::PreConfirmed, execution_status: Some(build_execution_status(tx_exec_result.status())), }, TransactionStatus::AcceptedOnL2(tx_exec_result) => TransactionStatusResponse { finality_status: FinalityStatus::AcceptedOnL2, execution_status: Some(build_execution_status(tx_exec_result.status())), }, TransactionStatus::AcceptedOnL1(tx_exec_result) => TransactionStatusResponse { finality_status: FinalityStatus::AcceptedOnL1, execution_status: Some(build_execution_status(tx_exec_result.status())), }, } } fn build_execution_status(status: TransactionExecutionStatus) -> ExecutionStatus { match status { TransactionExecutionStatus::Succeeded => ExecutionStatus::Succeeded, TransactionExecutionStatus::Reverted => ExecutionStatus::Reverted, } } ================================================ FILE: crates/sncast/src/starknet_commands/invoke.rs ================================================ use crate::Arguments; use crate::starknet_commands::utils::felt_or_id::FeltOrId; use anyhow::{Context, Result, anyhow}; use clap::Args; use conversions::IntoConv; use sncast::helpers::fee::{FeeArgs, FeeSettings}; use sncast::helpers::proof::ProofArgs; use sncast::helpers::rpc::RpcArgs; use sncast::response::errors::StarknetCommandError; use sncast::response::invoke::InvokeResponse; use sncast::response::ui::UI; use sncast::{WaitForTx, apply_optional_fields, handle_wait_for_tx}; use starknet_rust::accounts::AccountError::Provider; use starknet_rust::accounts::{Account, ConnectedAccount, ExecutionV3, SingleOwnerAccount}; use starknet_rust::core::types::{Call, InvokeTransactionResult}; use starknet_rust::providers::JsonRpcClient; use starknet_rust::providers::jsonrpc::HttpTransport; use starknet_rust::signers::Signer; use starknet_types_core::felt::Felt; #[derive(Args, Clone, Debug)] pub struct InvokeCommonArgs { /// Address of contract to invoke #[arg(short = 'd', long)] pub contract_address: FeltOrId, /// Name of the function to invoke #[arg(short, long)] pub function: String, #[command(flatten)] pub arguments: Arguments, } #[derive(Args, Clone, Debug)] #[command(about = "Invoke a contract on Starknet")] pub struct Invoke { #[command(flatten)] pub common: InvokeCommonArgs, #[command(flatten)] pub fee_args: FeeArgs, #[command(flatten)] pub proof_args: ProofArgs, /// Nonce of the transaction. If not provided, nonce will be set automatically #[arg(short, long)] pub nonce: Option, #[command(flatten)] pub rpc: RpcArgs, } #[expect(clippy::too_many_arguments)] pub async fn invoke( contract_address: Felt, calldata: Vec, nonce: Option, fee_args: FeeArgs, proof_args: ProofArgs, function_selector: Felt, account: &SingleOwnerAccount<&JsonRpcClient, S>, wait_config: WaitForTx, ui: &UI, ) -> Result where S: Signer + Sync + Send, { let call = Call { to: contract_address, selector: function_selector, calldata, }; execute_calls( account, vec![call], fee_args, proof_args, nonce, wait_config, ui, ) .await } pub async fn execute_calls( account: &SingleOwnerAccount<&JsonRpcClient, S>, calls: Vec, fee_args: FeeArgs, proof_args: ProofArgs, nonce: Option, wait_config: WaitForTx, ui: &UI, ) -> Result where S: Signer + Sync + Send, { let execution_calls = account.execute_v3(calls); let fee_settings = if fee_args.max_fee.is_some() { let fee_estimate = execution_calls .estimate_fee() .await .expect("Failed to estimate fee"); fee_args.try_into_fee_settings(Some(&fee_estimate)) } else { fee_args.try_into_fee_settings(None) }; let FeeSettings { l1_gas, l1_gas_price, l2_gas, l2_gas_price, l1_data_gas, l1_data_gas_price, tip, } = fee_settings.expect("Failed to convert to fee settings"); let proof = proof_args .resolve_proof() .context("Failed to resolve proof")?; let proof_facts = proof_args .resolve_proof_facts() .context("Failed to resolve proof facts")?; let execution = apply_optional_fields!( execution_calls, l1_gas => ExecutionV3::l1_gas, l1_gas_price => ExecutionV3::l1_gas_price, l2_gas => ExecutionV3::l2_gas, l2_gas_price => ExecutionV3::l2_gas_price, l1_data_gas => ExecutionV3::l1_data_gas, l1_data_gas_price => ExecutionV3::l1_data_gas_price, tip => ExecutionV3::tip, nonce => ExecutionV3::nonce, proof => ExecutionV3::proof, proof_facts => ExecutionV3::proof_facts ); let result = execution.send().await; match result { Ok(InvokeTransactionResult { transaction_hash }) => handle_wait_for_tx( account.provider(), transaction_hash, InvokeResponse { transaction_hash: transaction_hash.into_(), }, wait_config, ui, ) .await .map_err(StarknetCommandError::from), Err(Provider(error)) => Err(StarknetCommandError::ProviderError(error.into())), Err(error) => Err(anyhow!(format!("Unexpected error occurred: {error}")).into()), } } ================================================ FILE: crates/sncast/src/starknet_commands/ledger/app_version.rs ================================================ use anyhow::{Context, Result}; use clap::Args; use coins_ledger::transports::LedgerAsync; use starknet_rust::signers::ledger::LedgerStarknetApp; use sncast::response::ledger::{LedgerResponse, VersionResponse}; #[derive(Args, Debug)] pub struct AppVersion; pub async fn app_version( _args: &AppVersion, ledger: LedgerStarknetApp, ) -> Result { let version = ledger .get_version() .await .context("Failed to get app version from Ledger")?; Ok(LedgerResponse::Version(VersionResponse { version: version.to_string(), })) } ================================================ FILE: crates/sncast/src/starknet_commands/ledger/get_public_key.rs ================================================ use anyhow::{Context, Result}; use clap::Args; use coins_ledger::transports::LedgerAsync; use conversions::string::IntoHexStr; use sncast::helpers::ledger::LedgerKeyLocator; use sncast::response::ui::UI; use starknet_rust::signers::ledger::LedgerStarknetApp; use sncast::response::ledger::{LedgerResponse, PublicKeyResponse}; #[derive(Args, Debug)] pub struct GetPublicKey { #[command(flatten)] pub key_locator: LedgerKeyLocator, /// Do not display the public key on Ledger's screen for confirmation #[arg(long)] pub no_display: bool, } pub async fn get_public_key( args: &GetPublicKey, ledger: LedgerStarknetApp, ui: &UI, ) -> Result { let path = args.key_locator.resolve(ui); if !args.no_display { ui.print_notification("Please confirm the public key on your Ledger device...\n"); } let public_key = ledger .get_public_key(path, !args.no_display) .await .context("Failed to get public key from Ledger")?; Ok(LedgerResponse::PublicKey(PublicKeyResponse { public_key: public_key.scalar().into_hex_string(), })) } ================================================ FILE: crates/sncast/src/starknet_commands/ledger/mod.rs ================================================ use anyhow::Result; use clap::{Args, Subcommand}; use sncast::helpers::ledger::create_ledger_app; use sncast::response::ui::UI; use sncast::response::ledger::LedgerResponse; use app_version::{AppVersion, app_version}; use get_public_key::{GetPublicKey, get_public_key}; use sign_hash::{SignHash, sign_hash}; pub mod app_version; pub mod get_public_key; pub mod sign_hash; #[derive(Args, Debug)] #[command(about = "Interact with Ledger hardware wallet")] pub struct Ledger { #[command(subcommand)] subcommand: LedgerSubcommand, } #[derive(Subcommand, Debug)] enum LedgerSubcommand { /// Get public key from Ledger device GetPublicKey(GetPublicKey), /// Sign a hash using Ledger device SignHash(SignHash), /// Get Starknet app version from Ledger device AppVersion(AppVersion), } pub async fn ledger(ledger_args: &Ledger, ui: &UI) -> Result { let ledger = create_ledger_app().await?; match &ledger_args.subcommand { LedgerSubcommand::GetPublicKey(args) => get_public_key(args, ledger, ui).await, LedgerSubcommand::SignHash(args) => sign_hash(args, ledger, ui).await, LedgerSubcommand::AppVersion(args) => app_version(args, ledger).await, } } ================================================ FILE: crates/sncast/src/starknet_commands/ledger/sign_hash.rs ================================================ use anyhow::{Context, Result}; use clap::Args; use coins_ledger::transports::LedgerAsync; use conversions::string::IntoHexStr; use foundry_ui::components::warning::WarningMessage; use sncast::helpers::ledger::LedgerKeyLocator; use sncast::response::ui::UI; use starknet_rust::signers::ledger::LedgerStarknetApp; use starknet_types_core::felt::Felt; use sncast::response::ledger::{LedgerResponse, SignatureResponse}; #[derive(Args, Debug)] pub struct SignHash { #[command(flatten)] pub key_locator: LedgerKeyLocator, /// The raw hash to be signed pub hash: Felt, } pub async fn sign_hash( args: &SignHash, ledger: LedgerStarknetApp, ui: &UI, ) -> Result { let path = args.key_locator.resolve(ui); ui.print_warning(WarningMessage::new( "Blind signing a raw hash could be dangerous. Make sure you ONLY sign hashes \ from trusted sources. For better security, sign full transactions instead \ of raw hashes whenever possible.", )); ui.print_blank_line(); ui.print_notification("Please confirm the signing operation on your Ledger\n"); let signature = ledger .sign_hash(path, &args.hash) .await .context("Failed to sign hash with Ledger")?; Ok(LedgerResponse::Signature(SignatureResponse { r: signature.r.into_hex_string(), s: signature.s.into_hex_string(), })) } ================================================ FILE: crates/sncast/src/starknet_commands/mod.rs ================================================ pub mod account; pub mod call; pub mod declare; pub mod declare_from; pub mod deploy; pub mod get; pub mod invoke; pub mod ledger; pub mod multicall; pub mod script; pub mod show_config; pub mod utils; pub mod verify; ================================================ FILE: crates/sncast/src/starknet_commands/multicall/contract_registry.rs ================================================ use anyhow::{Context, Result, bail}; use sncast::{get_class_hash_by_address, get_contract_class}; use starknet_rust::{ core::types::ContractClass, providers::{JsonRpcClient, jsonrpc::HttpTransport}, }; use starknet_types_core::felt::Felt; use std::collections::{HashMap, hash_map::Entry}; /// Registry for managing contract information during multicall execution. /// It stores the following mappings: /// - user-defined ids to contract addresses /// - contract addresses to class hashes /// - class hashes to contract classes /// to allow referencing and re-using deployed contracts across multiple calls in a multicall sequence. pub struct ContractRegistry { id_to_address: HashMap, address_to_class_hash: HashMap, class_hash_to_contract_class: HashMap, provider: JsonRpcClient, } impl ContractRegistry { pub fn new(provider: &JsonRpcClient) -> Self { ContractRegistry { id_to_address: HashMap::new(), address_to_class_hash: HashMap::new(), class_hash_to_contract_class: HashMap::new(), provider: provider.clone(), } } /// Retrieves the contract address associated with the given id, if it exists. pub fn get_address_by_id(&self, id: &str) -> Option { self.id_to_address.get(id).copied() } /// Inserts a mapping from the given id to the specified contract address. /// Returns an error if the id already exists. pub fn insert_new_id_to_address(&mut self, id: String, address: Felt) -> Result<()> { if self.id_to_address.contains_key(&id) { anyhow::bail!("Duplicate id found: {id}"); } self.id_to_address.insert(id, address); Ok(()) } /// Retrieves the class hash associated with the given contract address. /// Checks the local cache first, and fetches from the provider if not cached. pub async fn get_class_hash_by_address(&mut self, address: &Felt) -> Result { if let Some(hash) = self.get_class_hash_by_address_local(address) { return Ok(hash); } let class_hash = get_class_hash_by_address(&self.provider, *address) .await .context("Failed to fetch class hash from provider")?; self.address_to_class_hash.insert(*address, class_hash); Ok(class_hash) } /// Retrieves the class hash associated with the given contract address from the local cache, if it exists. pub fn get_class_hash_by_address_local(&self, address: &Felt) -> Option { self.address_to_class_hash.get(address).copied() } /// Inserts a mapping from the given contract address to the specified class hash. /// Returns an error if the address already exists. pub fn insert_new_address(&mut self, address: Felt, class_hash: Felt) -> Result<()> { if let Entry::Vacant(e) = self.address_to_class_hash.entry(address) { e.insert(class_hash); Ok(()) } else { bail!("Duplicate address found: {address}") } } /// Retrieves the contract class associated with the given class hash, if it exists. /// If not found in the cache, it queries the provider and updates the cache. pub async fn get_contract_class_by_class_hash( &mut self, class_hash: &Felt, ) -> Result<&ContractClass> { if self.class_hash_to_contract_class.contains_key(class_hash) { return Ok(self .class_hash_to_contract_class .get(class_hash) .expect("Contract class should exist")); } let contract_class = get_contract_class(*class_hash, &self.provider) .await .context("Failed to fetch contract class from provider")?; Ok(self .class_hash_to_contract_class .entry(*class_hash) .or_insert(contract_class)) } } #[cfg(test)] mod tests { use super::ContractRegistry; use starknet_rust::providers::{JsonRpcClient, jsonrpc::HttpTransport}; use starknet_types_core::felt::Felt; use url::Url; #[test] fn test_insert_and_get() { let mock_provider = JsonRpcClient::new(HttpTransport::new( Url::parse("http://localhost:8545").unwrap(), )); let mut registry = ContractRegistry::new(&mock_provider); let id = "contract1".to_string(); let address = Felt::from(12345); assert!( registry .insert_new_id_to_address(id.clone(), address) .is_ok() ); assert_eq!(registry.get_address_by_id(&id), Some(address)); } #[test] fn test_duplicate_id() { let mock_provider = JsonRpcClient::new(HttpTransport::new( Url::parse("http://localhost:8545").unwrap(), )); let mut registry = ContractRegistry::new(&mock_provider); let id = "contract1".to_string(); let address1 = Felt::from(12345); let address2 = Felt::from(67890); assert!( registry .insert_new_id_to_address(id.clone(), address1) .is_ok() ); // Attempt to insert a duplicate id assert!( registry .insert_new_id_to_address(id.clone(), address2) .is_err() ); // Ensure the original address is still retrievable assert_eq!(registry.get_address_by_id(&id), Some(address1)); } } ================================================ FILE: crates/sncast/src/starknet_commands/multicall/deploy.rs ================================================ use anyhow::{Context, Result}; use clap::Args; use sncast::helpers::constants::UDC_ADDRESS; use sncast::{extract_or_generate_salt, udc_uniqueness}; use starknet_rust::accounts::{Account, SingleOwnerAccount}; use starknet_rust::core::types::Call; use starknet_rust::core::utils::{get_selector_from_name, get_udc_deployed_address}; use starknet_rust::providers::JsonRpcClient; use starknet_rust::providers::jsonrpc::HttpTransport; use starknet_rust::signers::Signer; use starknet_types_core::felt::Felt; use crate::starknet_commands::deploy::{ContractIdentifier, DeployArguments, DeployCommonArgs}; use crate::starknet_commands::multicall::contract_registry::ContractRegistry; use crate::starknet_commands::multicall::replaced_arguments; use crate::starknet_commands::multicall::run::{DeployItem, parse_inputs}; use crate::{Arguments, calldata_to_felts}; #[derive(Args)] pub struct MulticallDeploy { /// Optional identifier to reference this contract in later steps #[arg(long)] pub id: Option, #[command(flatten)] pub common: DeployCommonArgs, } impl MulticallDeploy { pub fn new_from_item(item: &DeployItem, contracts: &ContractRegistry) -> Result { let constructor_calldata = parse_inputs(&item.inputs, contracts)?; let deploy = MulticallDeploy { common: DeployCommonArgs { contract_identifier: ContractIdentifier { class_hash: Some(item.class_hash), contract_name: None, }, arguments: DeployArguments { constructor_calldata: Some( constructor_calldata .iter() .map(ToString::to_string) .collect(), ), arguments: None, }, salt: item.salt, unique: item.unique, package: None, }, id: item.id.clone(), }; Ok(deploy) } pub async fn build_call( &self, account: &SingleOwnerAccount<&JsonRpcClient, S>, contract_registry: &mut ContractRegistry, ) -> Result where S: Signer + Sync + Send, { let salt = extract_or_generate_salt(self.common.salt); let constructor_arguments = replaced_arguments( &Arguments::from(self.common.arguments.clone()), contract_registry, )?; let constructor_selector = get_selector_from_name("constructor")?; let class_hash = self .common .contract_identifier .class_hash .context("Using deploy with multicall requires providing class hash")?; let constructor_calldata = if let Some(raw_calldata) = &constructor_arguments.calldata { calldata_to_felts(raw_calldata)? } else { let contract_class = contract_registry .get_contract_class_by_class_hash(&class_hash) .await?; constructor_arguments.try_into_calldata(contract_class, &constructor_selector)? }; let mut calldata = vec![ class_hash, salt, Felt::from(u8::from(self.common.unique)), constructor_calldata.len().into(), ]; calldata.extend_from_slice(&constructor_calldata); let contract_address = get_udc_deployed_address( salt, class_hash, &udc_uniqueness(self.common.unique, account.address()), &constructor_calldata, ); if contract_registry .get_class_hash_by_address_local(&contract_address) .is_none() { contract_registry.insert_new_address(contract_address, class_hash)?; } // Store the contract address in the context with the provided id for later use in invoke calls if let Some(id) = &self.id { contract_registry.insert_new_id_to_address(id.clone(), contract_address)?; } Ok(Call { to: UDC_ADDRESS, selector: get_selector_from_name("deployContract")?, calldata, }) } } ================================================ FILE: crates/sncast/src/starknet_commands/multicall/execute.rs ================================================ use anyhow::{Context, Result, bail}; use clap::{Args, Command, FromArgMatches}; use sncast::{ WaitForTx, helpers::{fee::FeeArgs, rpc::RpcArgs}, response::{ errors::handle_starknet_command_error, multicall::run::MulticallRunResponse, ui::UI, }, }; use starknet_rust::{ accounts::SingleOwnerAccount, providers::{JsonRpcClient, jsonrpc::HttpTransport}, signers::Signer, }; use starknet_types_core::felt::Felt; use crate::starknet_commands::{ invoke::execute_calls, multicall::{ contract_registry::ContractRegistry, deploy::MulticallDeploy, invoke::MulticallInvoke, }, }; use sncast::helpers::proof::ProofArgs; const ALLOWED_MULTICALL_COMMANDS: [&str; 2] = ["deploy", "invoke"]; #[derive(Args, Debug, Clone)] #[command(about = "Execute a multicall with CLI arguments")] pub struct Execute { #[command(flatten)] pub fee_args: FeeArgs, #[command(flatten)] pub rpc: RpcArgs, /// Nonce of the transaction. If not provided, nonce will be set automatically #[arg(short, long)] pub nonce: Option, /// The multicall arguments. Subsequent calls should be separated by a '/' token. /// /// Supported commands: `invoke`, `deploy`. /// Their syntax is the same as `sncast invoke` and `sncast deploy`. /// Use `sncast invoke --help` and `sncast deploy --help` for reference. /// /// Additionally, `deploy` supports `--id ` argument to name the deployed contract in this multicall. /// In subsequent calls, `@` can be referenced in `--contract-address` and `--calldata` flags to reference the deployed contract address. /// /// For more, read the documentation: `` #[arg(allow_hyphen_values = true, num_args = 1..)] pub tokens: Vec, } pub async fn execute( execute: Execute, account: &SingleOwnerAccount<&JsonRpcClient, S>, provider: &JsonRpcClient, wait_config: WaitForTx, ui: &UI, ) -> Result where S: Signer + Sync + Send, { let command_groups = extract_commands_groups(&execute.tokens, "/", &ALLOWED_MULTICALL_COMMANDS); let mut contract_registry = ContractRegistry::new(provider); let mut calls = vec![]; for group in command_groups { let cmd_name = &group[0]; let cmd_args = &group[1..]; match cmd_name.as_str() { "deploy" => { let call = parse_args::(cmd_name, cmd_args)? .build_call(account, &mut contract_registry) .await?; calls.push(call); } "invoke" => { let call = parse_args::(cmd_name, cmd_args)? .build_call(&mut contract_registry) .await?; calls.push(call); } _ => bail!( "Unknown multicall command: '{}'. Allowed commands: {}", cmd_name, ALLOWED_MULTICALL_COMMANDS.join(", ") ), } } if calls.is_empty() { bail!("No valid multicall commands found to execute. Please check the provided commands."); } execute_calls( account, calls, execute.fee_args, ProofArgs::none(), execute.nonce, wait_config, ui, ) .await .map(Into::into) .map_err(handle_starknet_command_error) } /// Groups tokens into separate command groups based on the provided separator and allowed commands. fn extract_commands_groups( tokens: &[String], separator: &str, commands: &[&str], ) -> Vec> { let mut all_groups = Vec::new(); let mut current_group = Vec::new(); let mut i = 0; while i < tokens.len() { let token = &tokens[i]; if token == separator { // Look ahead to find the next non-separator token let mut j = i + 1; while j < tokens.len() && tokens[j] == separator { j += 1; } let is_at_end = j == tokens.len(); let next_is_command = !is_at_end && commands.contains(&tokens[j].as_str()); // If the sequence of separators leads to a command or the end of the input, // it acts as a valid boundary. if is_at_end || next_is_command { if !current_group.is_empty() { all_groups.push(current_group); current_group = Vec::new(); } // Fast-forward the index to skip all consecutive separators i = j; continue; } } current_group.push(token.clone()); i += 1; } if !current_group.is_empty() { all_groups.push(current_group); } all_groups } fn parse_args(command_name: &str, tokens: &[String]) -> anyhow::Result where T: Args + FromArgMatches, { let cmd = T::augment_args(Command::new(command_name.to_string())); let argv = std::iter::once(command_name) .chain(tokens.iter().map(String::as_str)) .collect::>(); let matches = cmd .try_get_matches_from(argv) .with_context(|| format!("Failed to parse args for `{command_name}`"))?; T::from_arg_matches(&matches).map_err(Into::into) } #[cfg(test)] mod tests { use super::*; fn create_tokens(commands: Vec<&str>) -> Vec { commands.into_iter().map(String::from).collect() } #[test] fn test_extract_commands_groups() { let tokens = create_tokens(vec![ "deploy", "--class-hash", "0x123", "/", "invoke", "--contract-address", "0xabc", "--function", "my_function", "/", "deploy", "--class-hash", "0x456", ]); let groups = extract_commands_groups(&tokens, "/", &ALLOWED_MULTICALL_COMMANDS); assert_eq!( groups, vec![ create_tokens(vec!["deploy", "--class-hash", "0x123"]), create_tokens(vec![ "invoke", "--contract-address", "0xabc", "--function", "my_function" ]), create_tokens(vec!["deploy", "--class-hash", "0x456"]) ] ); } #[test] fn test_extract_commands_groups_leading_slash() { let tokens = vec!["/", "deploy", "--class-hash", "0x123"] .into_iter() .map(String::from) .collect::>(); let groups = extract_commands_groups(&tokens, "/", &ALLOWED_MULTICALL_COMMANDS); assert_eq!( groups, vec![create_tokens(vec!["deploy", "--class-hash", "0x123"])] ); } #[test] fn test_extract_commands_groups_trailing_slash() { let tokens = create_tokens(vec!["deploy", "--class-hash", "0x123", "/"]); let groups = extract_commands_groups(&tokens, "/", &ALLOWED_MULTICALL_COMMANDS); assert_eq!( groups, vec![create_tokens(vec!["deploy", "--class-hash", "0x123"])] ); } #[test] fn test_extract_commands_groups_consecutive_slashes() { let tokens = create_tokens(vec![ "deploy", "--class-hash", "0x123", "/", "/", "invoke", "--contract-address", "0xabc", ]); let groups = extract_commands_groups(&tokens, "/", &ALLOWED_MULTICALL_COMMANDS); assert_eq!( groups, vec![ create_tokens(vec!["deploy", "--class-hash", "0x123"]), create_tokens(vec!["invoke", "--contract-address", "0xabc"]) ] ); } #[test] fn test_extract_commands_groups_only_slashes() { let tokens = create_tokens(vec!["/", "/", "/"]); let groups = extract_commands_groups(&tokens, "/", &ALLOWED_MULTICALL_COMMANDS); let expected: Vec> = vec![]; assert_eq!(groups, expected); } } ================================================ FILE: crates/sncast/src/starknet_commands/multicall/invoke.rs ================================================ use anyhow::{Context, Result}; use clap::Args; use starknet_rust::core::{types::Call, utils::get_selector_from_name}; use crate::{ Arguments, calldata_to_felts, starknet_commands::{ invoke::InvokeCommonArgs, multicall::{ contract_registry::ContractRegistry, replaced_arguments, run::{InvokeItem, parse_inputs}, }, }, }; #[derive(Args)] pub struct MulticallInvoke { #[command(flatten)] pub common: InvokeCommonArgs, } impl MulticallInvoke { pub fn new_from_item(item: &InvokeItem, contracts: &ContractRegistry) -> Result { let calldata = parse_inputs(&item.inputs, contracts)?; let invoke = MulticallInvoke { common: InvokeCommonArgs { contract_address: item.contract_address.clone(), function: item.function.clone(), arguments: Arguments { calldata: Some(calldata.iter().map(ToString::to_string).collect()), arguments: None, }, }, }; Ok(invoke) } pub async fn build_call(&self, contract_registry: &mut ContractRegistry) -> Result { let selector = get_selector_from_name(&self.common.function)?; let arguments = replaced_arguments(&self.common.arguments, contract_registry)?; let contract_address = if let Some(id) = self.common.contract_address.as_id() { contract_registry .get_address_by_id(id) .with_context(|| format!("Failed to find contract address for id: {id}")) } else { self.common.contract_address.try_into_felt() }?; let calldata = if let Some(raw_calldata) = &arguments.calldata { calldata_to_felts(raw_calldata)? } else { let class_hash = contract_registry .get_class_hash_by_address(&contract_address) .await?; let contract_class = contract_registry .get_contract_class_by_class_hash(&class_hash) .await?; arguments.try_into_calldata(contract_class, &selector)? }; Ok(Call { to: contract_address, selector, calldata, }) } } ================================================ FILE: crates/sncast/src/starknet_commands/multicall/mod.rs ================================================ use anyhow::{Context, Result}; use clap::{Args, Subcommand}; use serde::Serialize; use serde_json::{Value, json}; pub mod contract_registry; pub mod deploy; pub mod execute; pub mod invoke; pub mod new; pub mod run; use crate::starknet_commands::multicall::contract_registry::ContractRegistry; use crate::starknet_commands::multicall::execute::Execute; use crate::starknet_commands::utils::felt_or_id::FeltOrId; use crate::{Arguments, process_command_result, starknet_commands}; use foundry_ui::Message; use new::New; use run::Run; use sncast::response::ui::UI; use sncast::with_account; use sncast::{ WaitForTx, get_account, helpers::{configuration::CastConfig, constants::DEFAULT_MULTICALL_CONTENTS}, response::explorer_link::block_explorer_link_if_allowed, }; use starknet_rust::providers::Provider; #[derive(Args)] #[command(about = "Execute multiple calls at once", long_about = None)] pub struct Multicall { #[command(subcommand)] pub command: Commands, } #[derive(Debug, Subcommand)] pub enum Commands { Run(Box), New(New), Execute(Box), } pub async fn multicall( multicall: Multicall, config: CastConfig, ui: &UI, wait_config: WaitForTx, ) -> Result<()> { #[derive(Serialize)] struct MulticallMessage { file_contents: String, } impl Message for MulticallMessage { fn text(&self) -> String { self.file_contents.clone() } fn json(&self) -> Value { json!(self) } } match &multicall.command { starknet_commands::multicall::Commands::New(new) => { if let Some(output_path) = &new.output_path { let result = starknet_commands::multicall::new::write_empty_template( output_path, new.overwrite, ); process_command_result("multicall new", result, ui, None); } else { ui.print_message( "multicall_new", MulticallMessage { file_contents: DEFAULT_MULTICALL_CONTENTS.to_string(), }, ); } Ok(()) } starknet_commands::multicall::Commands::Run(run) => { let provider = run.rpc.get_provider(&config, ui).await?; let account = get_account(&config, &provider, &run.rpc, ui).await?; let result = with_account!(&account, |account| { starknet_commands::multicall::run::run( run.clone(), account, &provider, wait_config, ui, ) .await }); let block_explorer_link = block_explorer_link_if_allowed(&result, provider.chain_id().await?, &config).await; process_command_result("multicall run", result, ui, block_explorer_link); Ok(()) } starknet_commands::multicall::Commands::Execute(execute) => { let provider = execute.rpc.get_provider(&config, ui).await?; let account = get_account(&config, &provider, &execute.rpc, ui).await?; let result = with_account!(&account, |account| { starknet_commands::multicall::execute::execute( *execute.clone(), account, &provider, wait_config, ui, ) .await }); let block_explorer_link = block_explorer_link_if_allowed(&result, provider.chain_id().await?, &config).await; process_command_result("multicall execute", result, ui, block_explorer_link); Ok(()) } } } /// Replaces arguments that reference user-defined ids with their corresponding values from the contract registry. pub fn replaced_arguments( arguments: &Arguments, contracts: &ContractRegistry, ) -> Result { Ok(match (&arguments.calldata, &arguments.arguments) { (Some(calldata), None) => { let replaced_calldata = calldata .iter() .map(|raw_input| { let input = FeltOrId::new(raw_input.clone()); if let Some(id) = input.as_id() { contracts .get_address_by_id(id) .with_context(|| { format!("Failed to find contract address for id: {id}") }) .map(|f| f.to_string()) } else { Ok(raw_input.clone()) } }) .collect::>>()?; Arguments { calldata: Some(replaced_calldata), arguments: None, } } (None, _) => arguments.clone(), (Some(_), Some(_)) => { unreachable!("Only one of `calldata` or `arguments` can be provided, but both are set.") } }) } ================================================ FILE: crates/sncast/src/starknet_commands/multicall/new.rs ================================================ use anyhow::{Result, bail}; use camino::Utf8PathBuf; use clap::Args; use sncast::{ helpers::constants::DEFAULT_MULTICALL_CONTENTS, response::multicall::new::MulticallNewResponse, }; #[derive(Args, Debug)] #[command(about = "Generate a template for the multicall .toml file", long_about = None)] pub struct New { /// Output path to the file where the template is going to be saved #[arg(required = true, num_args = 1)] pub output_path: Option, /// If the file specified in output-path exists, this flag decides if it is going to be overwritten #[arg(short = 'o', long = "overwrite")] pub overwrite: bool, } pub fn write_empty_template( output_path: &Utf8PathBuf, overwrite: bool, ) -> Result { if output_path.exists() { if !output_path.is_file() { bail!("Output file cannot be a directory"); } if !overwrite { bail!( "Output file already exists, if you want to overwrite it, use the `--overwrite` flag" ); } } std::fs::write(output_path.clone(), DEFAULT_MULTICALL_CONTENTS)?; Ok(MulticallNewResponse { path: output_path.clone(), content: DEFAULT_MULTICALL_CONTENTS.to_string(), }) } ================================================ FILE: crates/sncast/src/starknet_commands/multicall/run.rs ================================================ use std::str::FromStr; use crate::starknet_commands::invoke::execute_calls; use crate::starknet_commands::multicall::contract_registry::ContractRegistry; use crate::starknet_commands::multicall::deploy::MulticallDeploy; use crate::starknet_commands::multicall::invoke::MulticallInvoke; use crate::starknet_commands::utils::felt_or_id::FeltOrId; use anyhow::{Context, Result}; use camino::Utf8PathBuf; use clap::Args; use serde::Deserialize; use serde_json::Number; use sncast::WaitForTx; use sncast::helpers::fee::FeeArgs; use sncast::helpers::proof::ProofArgs; use sncast::helpers::rpc::RpcArgs; use sncast::response::errors::handle_starknet_command_error; use sncast::response::multicall::run::MulticallRunResponse; use sncast::response::ui::UI; use starknet_rust::accounts::SingleOwnerAccount; use starknet_rust::core::types::Call; use starknet_rust::providers::JsonRpcClient; use starknet_rust::providers::jsonrpc::HttpTransport; use starknet_rust::signers::Signer; use starknet_types_core::felt::Felt; #[derive(Args, Debug, Clone)] #[command(about = "Execute a multicall from a .toml file", long_about = None)] pub struct Run { /// Path to the toml file with declared operations #[arg(short = 'p', long = "path")] pub path: Utf8PathBuf, #[command(flatten)] pub fee_args: FeeArgs, #[command(flatten)] pub rpc: RpcArgs, /// Nonce of the transaction. If not provided, nonce will be set automatically #[arg(short, long)] pub nonce: Option, } #[derive(Deserialize, Debug)] #[serde(untagged)] pub enum Input { String(String), Number(Number), } #[derive(Deserialize, Debug)] #[serde(tag = "call_type", rename_all = "lowercase")] enum CallItem { Deploy(DeployItem), Invoke(InvokeItem), } #[derive(Deserialize, Debug)] struct MulticallFile { #[serde(rename = "call")] calls: Vec, } #[derive(Deserialize, Debug)] pub struct DeployItem { pub class_hash: Felt, pub inputs: Vec, pub unique: bool, pub salt: Option, pub id: Option, } #[derive(Deserialize, Debug)] pub struct InvokeItem { pub contract_address: FeltOrId, pub function: String, pub inputs: Vec, } pub async fn run( run: Box, account: &SingleOwnerAccount<&JsonRpcClient, S>, provider: &JsonRpcClient, wait_config: WaitForTx, ui: &UI, ) -> Result where S: Signer + Sync + Send, { let fee_args = run.fee_args.clone(); let nonce = run.nonce; let contents = std::fs::read_to_string(&run.path)?; let multicall: MulticallFile = toml::from_str(&contents).with_context(|| format!("Failed to parse {}", run.path))?; let mut contracts = ContractRegistry::new(provider); let mut parsed_calls: Vec = vec![]; for call in multicall.calls { match call { CallItem::Deploy(item) => { let call = MulticallDeploy::new_from_item(&item, &contracts)? .build_call(account, &mut contracts) .await?; parsed_calls.push(call); } CallItem::Invoke(item) => { let call = MulticallInvoke::new_from_item(&item, &contracts)? .build_call(&mut contracts) .await?; parsed_calls.push(call); } } } execute_calls( account, parsed_calls, fee_args, ProofArgs::none(), nonce, wait_config, ui, ) .await .map(Into::into) .map_err(handle_starknet_command_error) } pub fn parse_inputs(inputs: &[Input], contract_registry: &ContractRegistry) -> Result> { let mut parsed_inputs = Vec::new(); for input in inputs { let felt_value = match input { Input::String(s) => contract_registry .get_address_by_id(s) .map_or_else(|| s.parse(), Ok)?, Input::Number(n) => Felt::from_str(&n.to_string()) .with_context(|| format!("Failed to parse {n} to felt"))?, }; parsed_inputs.push(felt_value); } Ok(parsed_inputs) } ================================================ FILE: crates/sncast/src/starknet_commands/script/init.rs ================================================ use anyhow::{Context, Result, ensure}; use camino::Utf8PathBuf; use sncast::response::ui::UI; use std::fs; use clap::Args; use foundry_ui::components::warning::WarningMessage; use indoc::{formatdoc, indoc}; use scarb_api::ScarbCommand; use scarb_api::version::scarb_version; use semver::Version; use sncast::helpers::constants::INIT_SCRIPTS_DIR; use sncast::helpers::scarb_utils::get_cairo_version; use sncast::response::script::init::ScriptInitResponse; use toml_edit::DocumentMut; #[derive(Args, Debug)] pub struct Init { /// Name of a script to create pub script_name: String, } pub fn init(init_args: &Init, ui: &UI) -> Result { let script_root_dir_path = get_script_root_dir_path(&init_args.script_name)?; init_scarb_project(&init_args.script_name, &script_root_dir_path)?; let modify_files_result = add_dependencies(&script_root_dir_path) .and_then(|()| modify_files_in_src_dir(&init_args.script_name, &script_root_dir_path)); ui.print_warning(WarningMessage::new( &"The newly created script isn't auto-added to the workspace. For more details, please see https://foundry-rs.github.io/starknet-foundry/starknet/script.html#initialize-a-script", )); match modify_files_result { Result::Ok(()) => Ok(ScriptInitResponse { message: format!( "Initialized `{}` at {}", init_args.script_name, script_root_dir_path ), }), Err(err) => { clean_created_dir_and_files(&script_root_dir_path, ui); Err(err) } } } fn get_script_root_dir_path(script_name: &str) -> Result { let current_dir = Utf8PathBuf::from_path_buf(std::env::current_dir()?) .expect("Failed to create Utf8PathBuf for the current directory"); let scripts_dir = current_dir.join(INIT_SCRIPTS_DIR); ensure!( !scripts_dir.exists(), "Scripts directory already exists at `{scripts_dir}`" ); Ok(scripts_dir.join(script_name)) } fn init_scarb_project(script_name: &str, script_root_dir: &Utf8PathBuf) -> Result<()> { const SCARB_WITHOUT_CAIRO_TEST_TEMPLATE: Version = Version::new(2, 13, 0); let version = scarb_version()?; // TODO(#3910) let test_runner = if version.scarb < SCARB_WITHOUT_CAIRO_TEST_TEMPLATE { "cairo-test" } else { "none" }; ScarbCommand::new() .args([ "new", "--name", script_name, "--no-vcs", "--quiet", script_root_dir.as_str(), ]) .env("SCARB_INIT_TEST_RUNNER", test_runner) .run() .context("Failed to init Scarb project")?; // TODO(#3910) if version.scarb < SCARB_WITHOUT_CAIRO_TEST_TEMPLATE { remove_cairo_test_dependency(script_root_dir)?; } Ok(()) } fn add_dependencies(script_root_dir: &Utf8PathBuf) -> Result<()> { add_sncast_std_dependency(script_root_dir) .context("Failed to add sncast_std dependency to Scarb.toml")?; add_starknet_dependency(script_root_dir) .context("Failed to add starknet dependency to Scarb.toml")?; Ok(()) } fn add_sncast_std_dependency(script_root_dir: &Utf8PathBuf) -> Result<()> { let cast_version = env!("CARGO_PKG_VERSION").to_string(); let dep_id = format!("sncast_std@{cast_version}"); ScarbCommand::new() .current_dir(script_root_dir) .args(["--offline", "add", &dep_id]) .run()?; Ok(()) } fn add_starknet_dependency(script_root_dir: &Utf8PathBuf) -> Result<()> { let scarb_manifest_path = script_root_dir.join("Scarb.toml"); let cairo_version = get_cairo_version(&scarb_manifest_path).context("Failed to get cairo version")?; let starknet_dependency = format!("starknet@>={cairo_version}"); ScarbCommand::new() .current_dir(script_root_dir) .args(["--offline", "add", &starknet_dependency]) .run()?; Ok(()) } fn modify_files_in_src_dir(script_name: &str, script_root_dir: &Utf8PathBuf) -> Result<()> { create_script_main_file(script_name, script_root_dir) .context(format!("Failed to create {script_name}.cairo file"))?; overwrite_lib_file(script_name, script_root_dir).context("Failed to overwrite lib.cairo file") } fn create_script_main_file(script_name: &str, script_root_dir: &Utf8PathBuf) -> Result<()> { let script_main_file_name = format!("{script_name}.cairo"); let script_main_file_path = script_root_dir.join("src").join(script_main_file_name); fs::write( script_main_file_path, indoc! {r#" use sncast_std::call; // The example below uses a contract deployed to the Sepolia testnet const CONTRACT_ADDRESS: felt252 = 0x07e867f1fa6da2108dd2b3d534f1fbec411c5ec9504eb3baa1e49c7a0bef5ab5; fn main() { let call_result = call( CONTRACT_ADDRESS.try_into().unwrap(), selector!("get_greeting"), array![], ) .expect('call failed'); assert(*call_result.data[1] == 'Hello, Starknet!', *call_result.data[1]); println!("{:?}", call_result); } "#}, )?; Ok(()) } fn overwrite_lib_file(script_name: &str, script_root_dir: &Utf8PathBuf) -> Result<()> { let lib_file_path = script_root_dir.join("src/lib.cairo"); fs::write( lib_file_path, formatdoc! {r" mod {script_name}; "}, )?; Ok(()) } fn clean_created_dir_and_files(script_root_dir: &Utf8PathBuf, ui: &UI) { if fs::remove_dir_all(script_root_dir).is_err() { ui.print_error( "script", format!("Failed to clean created files by init command at {script_root_dir}"), ); } } fn remove_cairo_test_dependency(script_root_dir: &Utf8PathBuf) -> Result<()> { let manifest_path = script_root_dir.join("Scarb.toml"); let mut document: DocumentMut = fs::read_to_string(&manifest_path)?.parse()?; document.remove("dev-dependencies"); fs::write(manifest_path, document.to_string())?; Ok(()) } ================================================ FILE: crates/sncast/src/starknet_commands/script/mod.rs ================================================ use crate::starknet_commands::script::run::Run; use crate::{Cli, starknet_commands::script::init::Init}; use crate::{get_cast_config, process_command_result, starknet_commands}; use anyhow::Context; use clap::{Args, Subcommand}; use sncast::helpers::scarb_utils::{ BuildConfig, assert_manifest_path_exists, build, build_and_load_artifacts, get_package_metadata, get_scarb_metadata_with_deps, }; use sncast::response::ui::UI; use sncast::{chain_id_to_network_name, get_chain_id, get_default_state_file_name}; use tokio::runtime::Runtime; pub mod init; pub mod run; #[derive(Args)] pub struct Script { #[command(subcommand)] pub command: Commands, } #[derive(Debug, Subcommand)] pub enum Commands { Init(Init), Run(Run), } pub fn run_script_command( cli: &Cli, runtime: Runtime, script: &Script, ui: &UI, ) -> anyhow::Result<()> { match &script.command { starknet_commands::script::Commands::Init(init) => { let result = starknet_commands::script::init::init(init, ui); process_command_result("script init", result, ui, None); } starknet_commands::script::Commands::Run(run) => { let manifest_path = assert_manifest_path_exists()?; let package_metadata = get_package_metadata(&manifest_path, &run.package)?; let config = get_cast_config(cli, ui)?; let provider = runtime.block_on(run.rpc.get_provider(&config, ui))?; let mut artifacts = build_and_load_artifacts( &package_metadata, &BuildConfig { scarb_toml_path: manifest_path.clone(), json: cli.json, profile: cli.profile.clone().unwrap_or("dev".to_string()), }, true, // TODO(#3959) Remove `base_ui` ui.base_ui(), ) .expect("Failed to build artifacts"); // TODO(#2042): remove duplicated compilation build( &package_metadata, &BuildConfig { scarb_toml_path: manifest_path.clone(), json: cli.json, profile: "dev".to_string(), }, "dev", ) .expect("Failed to build script"); let metadata_with_deps = get_scarb_metadata_with_deps(&manifest_path)?; let chain_id = runtime.block_on(get_chain_id(&provider))?; let state_file_path = if run.no_state_file { None } else { Some(package_metadata.root.join(get_default_state_file_name( &run.script_name, &chain_id_to_network_name(chain_id), ))) }; let url = runtime .block_on(run.rpc.get_url(&config)) .context("Failed to get url")?; let result = starknet_commands::script::run::run( &run.script_name, &metadata_with_deps, &package_metadata, &mut artifacts, &provider, &url, runtime, &config, state_file_path, ui, ); process_command_result("script run", result, ui, None); } } Ok(()) } ================================================ FILE: crates/sncast/src/starknet_commands/script/run/script_runtime.rs ================================================ use blockifier::execution::syscalls::vm_syscall_utils::SyscallSelector; use cairo_lang_casm::hints::{ExternalHint, Hint}; use cairo_lang_runner::Arg; use cairo_lang_runner::casm_run::{cell_ref_to_relocatable, extract_relocatable, get_val}; use cairo_vm::hint_processor::hint_processor_definition::{HintProcessorLogic, HintReference}; use cairo_vm::serde::deserialize_program::ApTracking; use cairo_vm::types::exec_scope::ExecutionScopes; use cairo_vm::types::relocatable::Relocatable; use cairo_vm::vm::errors::hint_errors::HintError; use cairo_vm::vm::errors::vm_errors::VirtualMachineError; use cairo_vm::vm::runners::cairo_runner::{ResourceTracker, RunResources}; use cairo_vm::vm::vm_core::VirtualMachine; use num_traits::ToPrimitive; use runtime::{SignalPropagator, StarknetRuntime, SyscallPtrAccess}; use starknet_types_core::felt::Felt; use std::any::Any; use std::collections::HashMap; use std::sync::Arc; pub struct CastScriptRuntime<'a> { pub starknet_runtime: StarknetRuntime<'a>, pub user_args: Vec>, } impl SyscallPtrAccess for CastScriptRuntime<'_> { fn get_mut_syscall_ptr(&mut self) -> &mut Relocatable { self.starknet_runtime.get_mut_syscall_ptr() } } impl ResourceTracker for CastScriptRuntime<'_> { fn consumed(&self) -> bool { self.starknet_runtime.consumed() } fn consume_step(&mut self) { self.starknet_runtime.consume_step(); } fn get_n_steps(&self) -> Option { self.starknet_runtime.get_n_steps() } fn run_resources(&self) -> &RunResources { self.starknet_runtime.run_resources() } } impl SignalPropagator for CastScriptRuntime<'_> { fn propagate_system_call_signal(&mut self, selector: SyscallSelector, vm: &mut VirtualMachine) { self.starknet_runtime .propagate_system_call_signal(selector, vm); } fn propagate_cheatcode_signal(&mut self, selector: &str, inputs: &[Felt]) { self.starknet_runtime .propagate_cheatcode_signal(selector, inputs); } } impl HintProcessorLogic for CastScriptRuntime<'_> { fn execute_hint( &mut self, vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, hint_data: &Box, ) -> Result<(), HintError> { let maybe_extended_hint = hint_data.downcast_ref::(); match maybe_extended_hint { // Copied from https://github.com/starkware-libs/cairo/blob/ada0450439a36d756223fba88dfd1f266f428f0c/crates/cairo-lang-runner/src/casm_run/mod.rs#L1321 Some(Hint::External(ExternalHint::AddRelocationRule { src, dst })) => { vm.add_relocation_rule( extract_relocatable(vm, src)?, extract_relocatable(vm, dst)?, )?; Ok(()) } // Copied from https://github.com/starkware-libs/cairo/blob/ada0450439a36d756223fba88dfd1f266f428f0c/crates/cairo-lang-runner/src/casm_run/mod.rs#L1330 Some(Hint::External(ExternalHint::WriteRunParam { index, dst })) => { let index = get_val(vm, index)?.to_usize().expect("Got a bad index."); let mut stack = vec![(cell_ref_to_relocatable(dst, vm), &self.user_args[index])]; while let Some((mut buffer, values)) = stack.pop() { for value in values { match value { Arg::Value(v) => { vm.insert_value(buffer, v)?; buffer += 1; } Arg::Array(arr) => { let arr_buffer = vm.add_memory_segment(); stack.push((arr_buffer, arr)); vm.insert_value(buffer, arr_buffer)?; buffer += 1; vm.insert_value(buffer, (arr_buffer + args_size(arr))?)?; buffer += 1; } } } } Ok(()) } _ => self .starknet_runtime .execute_hint(vm, exec_scopes, hint_data), } } fn compile_hint( &self, hint_code: &str, ap_tracking_data: &ApTracking, reference_ids: &HashMap, references: &[HintReference], accessible_scopes: &[String], constants: Arc>, ) -> Result, VirtualMachineError> { self.starknet_runtime.compile_hint( hint_code, ap_tracking_data, reference_ids, references, accessible_scopes, constants, ) } } fn args_size(args: &[Arg]) -> usize { args.iter().map(Arg::size).sum() } ================================================ FILE: crates/sncast/src/starknet_commands/script/run.rs ================================================ use crate::starknet_commands::{call, declare, deploy, get::tx_status, invoke}; use crate::{WaitForTx, get_account}; use anyhow::{Context, Result, anyhow, bail}; use blockifier::execution::contract_class::TrackedResource; use blockifier::execution::entry_point::ExecutableCallEntryPoint; use blockifier::execution::execution_utils::ReadOnlySegments; use blockifier::execution::syscalls::hint_processor::SyscallHintProcessor; use blockifier::execution::syscalls::vm_syscall_utils::SyscallSelector; use blockifier::state::cached_state::CachedState; use cairo_lang_casm::hints::Hint; use cairo_lang_runnable_utils::builder::{EntryCodeConfig, RunnableBuilder, create_code_footer}; use cairo_lang_runner::casm_run::hint_to_hint_params; use cairo_lang_runner::short_string::as_cairo_short_string; use cairo_lang_runner::{Arg, RunResultValue, SierraCasmRunner}; use cairo_lang_sierra::program::VersionedProgram; use cairo_lang_utils::ordered_hash_map::OrderedHashMap; use cairo_vm::serde::deserialize_program::HintParams; use cairo_vm::types::relocatable::Relocatable; use cairo_vm::vm::errors::hint_errors::HintError; use cairo_vm::vm::vm_core::VirtualMachine; use camino::Utf8PathBuf; use clap::Args; use conversions::byte_array::ByteArray; use conversions::serde::deserialize::BufferReader; use forge_runner::running::{has_segment_arena, syscall_handler_offset}; use foundry_ui::components::warning::WarningMessage; use runtime::starknet::context::{SerializableBlockInfo, build_context}; use runtime::starknet::state::DictStateReader; use runtime::{ CheatcodeHandlingResult, EnhancedHintError, ExtendedRuntime, ExtensionLogic, StarknetRuntime, SyscallHandlingResult, }; use scarb_api::package_matches_version_requirement; use scarb_metadata::{Metadata, PackageMetadata}; use script_runtime::CastScriptRuntime; use semver::{Comparator, Op, Version, VersionReq}; use shared::utils::build_readable_text; use sncast::AccountVariant; use sncast::get_nonce; use sncast::helpers::artifacts::CastStarknetContractArtifacts; use sncast::helpers::configuration::CastConfig; use sncast::helpers::constants::SCRIPT_LIB_ARTIFACT_NAME; use sncast::helpers::fee::{FeeArgs, ScriptFeeSettings}; use sncast::helpers::proof::ProofArgs; use sncast::helpers::rpc::RpcArgs; use sncast::response::script::run::ScriptRunResponse; use sncast::response::ui::UI; use sncast::state::hashing::{ generate_declare_tx_id, generate_deploy_tx_id, generate_invoke_tx_id, }; use sncast::state::state_file::StateManager; use starknet_rust::accounts::{Account, SingleOwnerAccount}; use starknet_rust::core::types::{BlockId, BlockTag::PreConfirmed}; use starknet_rust::providers::JsonRpcClient; use starknet_rust::providers::jsonrpc::HttpTransport; use starknet_rust::signers::LocalWallet; use starknet_types_core::felt::Felt; use std::collections::HashMap; use std::fs; use tokio::runtime::Runtime; use url::Url; mod script_runtime; #[derive(Args, Debug)] #[command(about = "Execute a deployment script")] pub struct Run { /// Module name that contains the `main` function, which will be executed pub script_name: String, /// Specifies scarb package to be used #[arg(long)] pub package: Option, /// Do not use the state file #[arg(long)] pub no_state_file: bool, #[command(flatten)] pub rpc: RpcArgs, } pub struct CastScriptExtension<'a> { pub provider: &'a JsonRpcClient, pub account: Option<&'a SingleOwnerAccount<&'a JsonRpcClient, LocalWallet>>, pub tokio_runtime: Runtime, pub config: &'a CastConfig, pub artifacts: &'a HashMap, pub state: StateManager, pub ui: &'a UI, } impl CastScriptExtension<'_> { pub fn account( &self, ) -> Result<&SingleOwnerAccount<&JsonRpcClient, LocalWallet>> { self.account.ok_or_else(|| anyhow!("Account not defined. Please ensure the correct account is passed to `script run` command")) } } impl<'a> ExtensionLogic for CastScriptExtension<'a> { type Runtime = CastScriptRuntime<'a>; #[expect(clippy::too_many_lines)] fn handle_cheatcode( &mut self, selector: &str, mut input_reader: BufferReader, _extended_runtime: &mut Self::Runtime, _vm: &VirtualMachine, ) -> Result { match selector { "call" => { let contract_address = input_reader.read()?; let function_selector = input_reader.read()?; let calldata_felts = input_reader.read()?; let call_result = self.tokio_runtime.block_on(call::call( contract_address, function_selector, calldata_felts, self.provider, &BlockId::Tag(PreConfirmed), )); Ok(CheatcodeHandlingResult::from_serializable(call_result)) } "declare" => { let contract: String = input_reader.read::()?.to_string(); let fee_args: FeeArgs = input_reader.read::()?.into(); let nonce = input_reader.read()?; let declare_tx_id = generate_declare_tx_id(contract.as_str()); if let Some(success_output) = self.state.get_output_if_success(declare_tx_id.as_str()) { return Ok(CheatcodeHandlingResult::from_serializable(success_output)); } let declare_result = self.tokio_runtime.block_on(declare::declare( contract.clone(), fee_args, nonce, self.account()?, self.artifacts, WaitForTx { wait: true, wait_params: self.config.wait_params, show_ui_outputs: true, }, true, self.ui, )); self.state.maybe_insert_tx_entry( declare_tx_id.as_str(), selector, &declare_result, )?; Ok(CheatcodeHandlingResult::from_serializable(declare_result)) } "deploy" => { let class_hash = input_reader.read()?; let constructor_calldata = input_reader.read::>()?; let salt = input_reader.read()?; let unique = input_reader.read()?; let fee_args: FeeArgs = input_reader.read::()?.into(); let nonce = input_reader.read()?; let deploy_tx_id = generate_deploy_tx_id(class_hash, &constructor_calldata, salt, unique); if let Some(success_output) = self.state.get_output_if_success(deploy_tx_id.as_str()) { return Ok(CheatcodeHandlingResult::from_serializable(success_output)); } let deploy_result = self.tokio_runtime.block_on(deploy::deploy( class_hash, &constructor_calldata, salt, unique, fee_args, nonce, self.account()?, WaitForTx { wait: true, wait_params: self.config.wait_params, show_ui_outputs: true, }, self.ui, )); self.state.maybe_insert_tx_entry( deploy_tx_id.as_str(), selector, &deploy_result, )?; Ok(CheatcodeHandlingResult::from_serializable(deploy_result)) } "invoke" => { let contract_address = input_reader.read()?; let function_selector = input_reader.read()?; let calldata: Vec<_> = input_reader.read()?; let fee_args = input_reader.read::()?.into(); let nonce = input_reader.read()?; let invoke_tx_id = generate_invoke_tx_id(contract_address, function_selector, &calldata); if let Some(success_output) = self.state.get_output_if_success(invoke_tx_id.as_str()) { return Ok(CheatcodeHandlingResult::from_serializable(success_output)); } let invoke_result = self.tokio_runtime.block_on(invoke::invoke( contract_address, calldata, nonce, fee_args, ProofArgs::none(), function_selector, self.account()?, WaitForTx { wait: true, wait_params: self.config.wait_params, show_ui_outputs: true, }, self.ui, )); self.state.maybe_insert_tx_entry( invoke_tx_id.as_str(), selector, &invoke_result, )?; Ok(CheatcodeHandlingResult::from_serializable(invoke_result)) } "get_nonce" => { let block_id = as_cairo_short_string(&input_reader.read()?) .expect("Failed to convert entry point name to short string"); let nonce = self.tokio_runtime.block_on(get_nonce( self.provider, &block_id, self.account()?.address(), ))?; Ok(CheatcodeHandlingResult::from_serializable(nonce)) } "tx_status" => { let transaction_hash = input_reader.read()?; let tx_status_result = self .tokio_runtime .block_on(tx_status::get_tx_status(self.provider, transaction_hash)); Ok(CheatcodeHandlingResult::from_serializable(tx_status_result)) } _ => Ok(CheatcodeHandlingResult::Forwarded), } } fn override_system_call( &mut self, _selector: SyscallSelector, _vm: &mut VirtualMachine, _extended_runtime: &mut Self::Runtime, ) -> Result { Err(HintError::CustomHint(Box::from( "Starknet syscalls are not supported", ))) } } #[expect(clippy::too_many_arguments, clippy::too_many_lines)] pub fn run( module_name: &str, metadata: &Metadata, package_metadata: &PackageMetadata, artifacts: &mut HashMap, provider: &JsonRpcClient, url: &Url, tokio_runtime: Runtime, config: &CastConfig, state_file_path: Option, ui: &UI, ) -> Result { warn_if_sncast_std_not_compatible(metadata, ui)?; let artifacts = inject_lib_artifact(metadata, package_metadata, artifacts)?; let artifact = artifacts .get(SCRIPT_LIB_ARTIFACT_NAME) .ok_or(anyhow!("Failed to find script artifact"))?; let sierra_program = serde_json::from_str::(&artifact.sierra) .with_context(|| "Failed to deserialize Sierra program")? .into_v1() .with_context(|| "Failed to load Sierra program")? .program; let runner = SierraCasmRunner::new( sierra_program.clone(), None, OrderedHashMap::default(), None, ) .with_context(|| "Failed to set up runner")?; // `builder` field in `SierraCasmRunner` is private, hence the need to create a new `RunnableBuilder` // https://github.com/starkware-libs/cairo/blob/66f5c7223f7a6c27c5f800816dba05df9b60674e/crates/cairo-lang-runner/src/lib.rs#L184 let builder = RunnableBuilder::new(sierra_program, None).with_context(|| "Failed to create builder")?; let name_suffix = module_name.to_string() + "::main"; let func = runner.find_function(name_suffix.as_str()) .context("Failed to find main function in script - please make sure `sierra-replace-ids` is not set to `false` for `dev` profile in script's Scarb.toml")?; let entry_code_config = EntryCodeConfig::testing(); let casm_program_wrapper_info = builder.create_wrapper_info(func, entry_code_config)?; let entry_code = casm_program_wrapper_info.header; let builtins = casm_program_wrapper_info.builtins; let footer = create_code_footer(); // import from cairo-lang-runner let assembled_program = builder .casm_program() .clone() .assemble_ex(&entry_code, &footer); let (hints_dict, string_to_hint) = hints_to_params(assembled_program.hints); // hint processor let mut context = build_context( &SerializableBlockInfo::default().into(), None, &TrackedResource::CairoSteps, ); let mut blockifier_state = CachedState::new(DictStateReader::default()); // TODO(#2954) let param_types = builder.generic_id_and_size_from_concrete(&func.signature.param_types); let segment_index = syscall_handler_offset(builtins.len(), has_segment_arena(¶m_types)); let syscall_handler = SyscallHintProcessor::new( &mut blockifier_state, &mut context, // This segment is created by SierraCasmRunner Relocatable { segment_index: segment_index .try_into() .expect("Failed to convert index to isize"), offset: 0, }, ExecutableCallEntryPoint::default(), &string_to_hint, ReadOnlySegments::default(), ); let account = if config.account.is_empty() { None } else { let rpc_args = RpcArgs { url: Some(url.clone()), network: None, }; let account = tokio_runtime.block_on(get_account(config, provider, &rpc_args, ui))?; match account { AccountVariant::LocalWallet(acc) => Some(acc), AccountVariant::Ledger(_) => bail!("Ledger is not supported for scripts"), } }; let state = StateManager::from(state_file_path)?; let cast_extension = CastScriptExtension { provider, tokio_runtime, config, artifacts: &artifacts, account: account.as_ref(), state, ui, }; let mut cast_runtime = ExtendedRuntime { extension: cast_extension, extended_runtime: CastScriptRuntime { starknet_runtime: StarknetRuntime { hint_handler: syscall_handler, panic_traceback: None, }, user_args: vec![vec![Arg::Value(Felt::from(i64::MAX))]], }, }; match runner.run_function( func, &mut cast_runtime, hints_dict, assembled_program.bytecode.iter(), builtins, ) { Ok(result) => match result.value { RunResultValue::Success(data) => Ok(ScriptRunResponse { status: "success".to_string(), message: build_readable_text(&data), }), RunResultValue::Panic(panic_data) => Ok(ScriptRunResponse { status: "script panicked".to_string(), message: build_readable_text(&panic_data), }), }, Err(err) => Err(err.into()), } } fn sncast_std_version_requirement() -> VersionReq { let version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap(); let comparator = Comparator { op: Op::Exact, major: version.major, minor: Some(version.minor), patch: Some(version.patch), pre: version.pre, }; VersionReq { comparators: vec![comparator], } } fn warn_if_sncast_std_not_compatible(scarb_metadata: &Metadata, ui: &UI) -> Result<()> { let sncast_std_version_requirement = sncast_std_version_requirement(); if !package_matches_version_requirement( scarb_metadata, "sncast_std", &sncast_std_version_requirement, )? { ui.print_warning(WarningMessage::new(&format!( "Package sncast_std version does not meet the recommended version requirement {sncast_std_version_requirement}, it might result in unexpected behaviour" ))); } Ok(()) } fn inject_lib_artifact( metadata: &Metadata, package_metadata: &PackageMetadata, artifacts: &mut HashMap, ) -> Result> { let sierra_filename = format!("{}.sierra.json", package_metadata.name); let target_dir = &metadata .target_dir .clone() .unwrap_or_else(|| metadata.workspace.root.join("target")); // TODO(#2042) let sierra_path = &target_dir.join("dev").join(sierra_filename); let lib_artifacts = CastStarknetContractArtifacts { sierra: fs::read_to_string(sierra_path)?, casm: String::new(), }; artifacts.insert(SCRIPT_LIB_ARTIFACT_NAME.to_string(), lib_artifacts); Ok(artifacts.clone()) } fn hints_to_params( hints: Vec<(usize, Vec)>, ) -> (HashMap>, HashMap) { let mut hints_dict: HashMap> = HashMap::new(); let mut string_to_hint: HashMap = HashMap::new(); for (offset, offset_hints) in hints { for hint in offset_hints.clone() { string_to_hint.insert(hint.representing_string(), hint.clone()); } hints_dict.insert( offset, offset_hints .clone() .iter() .map(hint_to_hint_params) .collect(), ); } (hints_dict, string_to_hint) } ================================================ FILE: crates/sncast/src/starknet_commands/show_config.rs ================================================ use anyhow::Result; use camino::Utf8PathBuf; use clap::Args; use sncast::helpers::configuration::CastConfig; use sncast::helpers::rpc::RpcArgs; use sncast::response::show_config::ShowConfigResponse; use sncast::{chain_id_to_network_name, get_chain_id}; use starknet_rust::providers::JsonRpcClient; use starknet_rust::providers::jsonrpc::HttpTransport; #[derive(Args)] #[command(about = "Show current configuration being used", long_about = None)] pub struct ShowConfig { #[command(flatten)] pub rpc: RpcArgs, } pub async fn show_config( show: &ShowConfig, provider: Option<&JsonRpcClient>, cast_config: CastConfig, profile: Option, ) -> Result { let chain_id = if let Some(provider) = provider { let chain_id_field = get_chain_id(provider).await?; Some(chain_id_to_network_name(chain_id_field)) } else { None }; let rpc_url = show.rpc.url.clone().or(cast_config.url); let network = show.rpc.network.or(cast_config.network); let account = Some(cast_config.account).filter(|p| !p.is_empty()); let mut accounts_file_path = Some(cast_config.accounts_file).filter(|p| p != &Utf8PathBuf::default()); let keystore = cast_config.keystore; if keystore.is_some() { accounts_file_path = None; } let wait_timeout = Some(cast_config.wait_params.get_timeout()); let wait_retry_interval = Some(cast_config.wait_params.get_retry_interval()); let block_explorer = cast_config.block_explorer; Ok(ShowConfigResponse { profile, chain_id, rpc_url, network, account, accounts_file_path, keystore, wait_timeout: wait_timeout.map(u64::from), wait_retry_interval: wait_retry_interval.map(u64::from), show_explorer_links: cast_config.show_explorer_links, block_explorer, }) } ================================================ FILE: crates/sncast/src/starknet_commands/utils/class_hash.rs ================================================ use anyhow::Context; use clap::Args; use conversions::{IntoConv, byte_array::ByteArray}; use sncast::helpers::artifacts::CastStarknetContractArtifacts; use sncast::{ ErrorData, response::{errors::StarknetCommandError, utils::class_hash::ClassHashResponse}, }; use starknet_rust::core::types::contract::SierraClass; use std::collections::HashMap; #[derive(Args, Debug)] #[command(about = "Generate the class hash of a contract", long_about = None)] pub struct ClassHash { /// Contract name #[arg(short = 'c', long = "contract-name")] pub contract: String, /// Specifies scarb package to be used #[arg(long)] pub package: Option, } #[expect(clippy::result_large_err)] pub fn get_class_hash( class_hash: &ClassHash, artifacts: &HashMap, ) -> Result { let contract_artifacts = artifacts.get(&class_hash.contract).ok_or( StarknetCommandError::ContractArtifactsNotFound(ErrorData { data: ByteArray::from(class_hash.contract.as_str()), }), )?; let contract_definition: SierraClass = serde_json::from_str(&contract_artifacts.sierra) .context("Failed to parse sierra artifact")?; let class_hash = contract_definition .class_hash() .map_err(anyhow::Error::from)?; Ok(ClassHashResponse { class_hash: class_hash.into_(), }) } ================================================ FILE: crates/sncast/src/starknet_commands/utils/felt_or_id.rs ================================================ use anyhow::{Context, Result}; use serde::Deserialize; use starknet_types_core::felt::Felt; use std::str::FromStr; const ID_PREFIX: char = '@'; #[derive(Deserialize, Debug, Clone)] pub struct FeltOrId(String); impl FeltOrId { pub fn new(s: String) -> Self { FeltOrId(s) } pub fn try_into_felt(&self) -> Result { Felt::from_str(&self.0) .context("Failed to parse contract address: expected a hex or decimal string") } pub fn as_id(&self) -> Option<&str> { self.0.strip_prefix(ID_PREFIX) } } impl std::str::FromStr for FeltOrId { type Err = anyhow::Error; fn from_str(s: &str) -> Result { Ok(FeltOrId(s.to_owned())) } } ================================================ FILE: crates/sncast/src/starknet_commands/utils/mod.rs ================================================ use clap::{Args, Subcommand}; use sncast::response::ui::UI; use sncast::{ helpers::{ configuration::CastConfig, scarb_utils::{ BuildConfig, assert_manifest_path_exists, build_and_load_artifacts, get_package_metadata, }, }, response::errors::handle_starknet_command_error, }; use crate::{ process_command_result, starknet_commands::{ self, utils::{class_hash::ClassHash, selector::Selector, serialize::Serialize}, }, }; pub mod class_hash; pub mod felt_or_id; pub mod selector; pub mod serialize; #[derive(Args)] #[command(about = "Utility commands for Starknet")] pub struct Utils { #[command(subcommand)] pub command: Commands, } #[derive(Debug, Subcommand)] pub enum Commands { Serialize(Serialize), /// Get contract class hash ClassHash(ClassHash), /// Calculate selector from name Selector(Selector), } pub async fn utils( utils: Utils, config: CastConfig, ui: &UI, json: bool, profile: String, ) -> anyhow::Result<()> { match utils.command { Commands::Serialize(serialize) => { let result = starknet_commands::utils::serialize::serialize(serialize, config, ui) .await .map_err(handle_starknet_command_error)?; process_command_result("serialize", Ok(result), ui, None); } Commands::ClassHash(class_hash) => { let manifest_path = assert_manifest_path_exists()?; let package_metadata = get_package_metadata(&manifest_path, &class_hash.package)?; let artifacts = build_and_load_artifacts( &package_metadata, &BuildConfig { scarb_toml_path: manifest_path, json, profile, }, false, // TODO(#3959) Remove `base_ui` ui.base_ui(), ) .expect("Failed to build contract"); let result = class_hash::get_class_hash(&class_hash, &artifacts) .map_err(handle_starknet_command_error)?; process_command_result("class-hash", Ok(result), ui, None); } Commands::Selector(sel) => { let result = selector::get_selector(&sel).map_err(handle_starknet_command_error)?; process_command_result("selector", Ok(result), ui, None); } } Ok(()) } ================================================ FILE: crates/sncast/src/starknet_commands/utils/selector.rs ================================================ use anyhow::anyhow; use clap::Args; use conversions::IntoConv; use sncast::response::{errors::StarknetCommandError, utils::selector::SelectorResponse}; use starknet_rust::core::utils::get_selector_from_name; #[derive(Args, Debug)] #[command(about = "Calculate entrypoint selector from function name", long_about = None)] pub struct Selector { /// Function name pub name: String, } #[expect(clippy::result_large_err)] pub fn get_selector(selector: &Selector) -> Result { let trimmed = selector.name.trim(); if trimmed.contains('(') || trimmed.contains(')') { return Err(StarknetCommandError::UnknownError(anyhow!( "Parentheses and the content within should not be supplied" ))); } let felt = get_selector_from_name(trimmed) .map_err(|e| StarknetCommandError::UnknownError(anyhow::Error::from(e)))?; Ok(SelectorResponse { selector: felt.into_(), }) } ================================================ FILE: crates/sncast/src/starknet_commands/utils/serialize.rs ================================================ use anyhow::{Context, Result, bail}; use camino::Utf8PathBuf; use clap::Args; use data_transformer::transform; use sncast::response::ui::UI; use sncast::{ get_class_hash_by_address, get_contract_class, helpers::{configuration::CastConfig, rpc::RpcArgs}, response::{errors::StarknetCommandError, utils::serialize::SerializeResponse}, }; use starknet_rust::core::{ types::{ContractClass, contract::AbiEntry}, utils::get_selector_from_name, }; use starknet_types_core::felt::Felt; #[derive(Args, Clone, Debug)] #[group(required = true, multiple = false)] pub struct LocationArgs { /// Class hash of contract which contains the function #[arg(short = 'c', long)] pub class_hash: Option, /// Address of contract which contains the function #[arg(short = 'd', long)] pub contract_address: Option, /// Path to the file containing ABI of the contract class #[arg(long)] pub abi_file: Option, } #[derive(Debug)] pub enum Location { AbiFile(Utf8PathBuf), ClassHash(Felt), ContractAddress(Felt), } impl TryFrom for Location { type Error = anyhow::Error; fn try_from(args: LocationArgs) -> Result { match (args.class_hash, args.contract_address, args.abi_file) { (Some(class_hash), None, None) => Ok(Location::ClassHash(class_hash)), (None, Some(address), None) => Ok(Location::ContractAddress(address)), (None, None, Some(path)) => Ok(Location::AbiFile(path)), _ => bail!( "Exactly one of --class-hash, --contract-address, or --abi-file must be provided" ), } } } #[derive(Args, Clone, Debug)] #[command(about = "Serialize Cairo expressions into calldata")] pub struct Serialize { /// Comma-separated string of Cairo expressions #[arg(long, allow_hyphen_values = true)] pub arguments: String, /// Name of the function whose calldata should be serialized #[arg(short, long)] pub function: String, #[command(flatten)] pub location_args: LocationArgs, #[command(flatten)] pub rpc_args: Option, } pub async fn serialize( Serialize { function, arguments, rpc_args, location_args, }: Serialize, config: CastConfig, ui: &UI, ) -> Result { let selector = get_selector_from_name(&function) .context("Failed to convert entry point selector to FieldElement")?; let location = Location::try_from(location_args)?; let abi = resolve_abi(location, rpc_args, &config, ui).await?; let calldata = transform(&arguments, &abi, &selector)?; Ok(SerializeResponse { calldata }) } pub async fn resolve_abi( location: Location, rpc_args: Option, config: &CastConfig, ui: &UI, ) -> Result> { match location { Location::AbiFile(path) => { let abi_str = tokio::fs::read_to_string(path) .await .context("Failed to read ABI file")?; serde_json::from_str(&abi_str).context("Failed to deserialize ABI from file") } Location::ClassHash(class_hash) => { let provider = rpc_args .context( "Either `--network` or `--url` must be provided when using `--class-hash`", )? .get_provider(config, ui) .await?; let contract_class = get_contract_class(class_hash, &provider).await?; parse_abi_from_contract_class(contract_class) } Location::ContractAddress(address) => { let provider = rpc_args.context("Either `--network` or `--url` must be provided when using `--contract-address`")?.get_provider(config, ui).await?; let class_hash = get_class_hash_by_address(&provider, address).await?; let contract_class = get_contract_class(class_hash, &provider).await?; parse_abi_from_contract_class(contract_class) } } } fn parse_abi_from_contract_class(contract_class: ContractClass) -> Result> { let ContractClass::Sierra(sierra) = contract_class else { bail!("ABI transformation not supported for Cairo 0 (legacy) contracts"); }; serde_json::from_str(&sierra.abi).context("Couldn't deserialize ABI from network") } ================================================ FILE: crates/sncast/src/starknet_commands/verify/explorer.rs ================================================ use anyhow::Result; use camino::Utf8PathBuf; use serde::Serialize; use sncast::response::ui::UI; use sncast::{Network, response::verify::VerifyResponse}; use starknet_rust::providers::{JsonRpcClient, jsonrpc::HttpTransport}; #[derive(Serialize, Debug)] #[serde(untagged)] pub enum ContractIdentifier { ClassHash { class_hash: String }, Address { contract_address: String }, } #[derive(Serialize, Debug)] pub struct VerificationPayload { pub contract_name: String, #[serde(flatten)] pub identifier: ContractIdentifier, pub source_code: serde_json::Value, } #[async_trait::async_trait] pub trait VerificationInterface<'a>: Sized { fn new( network: Network, workspace_dir: Utf8PathBuf, provider: &'a JsonRpcClient, ui: &'a UI, ) -> Result; async fn verify( &self, identifier: ContractIdentifier, contract_name: String, package: Option, test_files: bool, ui: &UI, ) -> Result; fn gen_explorer_url(&self) -> Result; } ================================================ FILE: crates/sncast/src/starknet_commands/verify/mod.rs ================================================ use anyhow::{Result, anyhow, bail}; use camino::Utf8PathBuf; use clap::{ArgGroup, Args, ValueEnum}; use promptly::prompt; use sncast::helpers::configuration::CastConfig; use sncast::helpers::rpc::FreeProvider; use sncast::response::ui::UI; use sncast::{Network, response::verify::VerifyResponse}; use sncast::{get_chain_id, get_provider}; use starknet_rust::providers::jsonrpc::{HttpTransport, JsonRpcClient}; use starknet_types_core::felt::Felt; use std::{collections::HashMap, fmt}; use url::Url; pub mod explorer; pub mod voyager; pub mod walnut; use explorer::ContractIdentifier; use explorer::VerificationInterface; use foundry_ui::components::warning::WarningMessage; use sncast::helpers::artifacts::CastStarknetContractArtifacts; use voyager::Voyager; use walnut::WalnutVerificationInterface; #[derive(Args)] #[command(about = "Verify a contract through a block explorer")] #[command(group( ArgGroup::new("contract_identifier") .required(true) .args(&["class_hash", "contract_address"]) ))] pub struct Verify { /// Class hash of a contract to be verified #[arg(short = 'g', long)] pub class_hash: Option, /// Address of a contract to be verified #[arg(short = 'd', long)] pub contract_address: Option, /// Name of the contract that is being verified #[arg(short, long)] pub contract_name: String, /// Block explorer to use for the verification #[arg(short, long, value_enum)] pub verifier: Verifier, /// The network on which block explorer will do the verification #[arg(short, long, value_enum)] pub network: Option, /// Assume "yes" as answer to confirmation prompt and run non-interactively #[arg(long, default_value = "false")] pub confirm_verification: bool, /// Specifies scarb package to be used #[arg(long)] pub package: Option, /// RPC provider url address; overrides url from snfoundry.toml. Will use public provider if not set. #[arg(long)] pub url: Option, /// Include test files under src/ for verification (only applies to voyager) #[arg(long, default_value = "false")] pub test_files: bool, } #[derive(ValueEnum, Clone, Debug)] pub enum Verifier { Walnut, Voyager, } impl fmt::Display for Verifier { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Verifier::Walnut => write!(f, "walnut"), Verifier::Voyager => write!(f, "voyager"), } } } async fn resolve_verification_network( cli_network: Option, config_network: Option, provider: &JsonRpcClient, ) -> Result { if let Some(network) = cli_network.or(config_network) { return Ok(network); } let chain_id = get_chain_id(provider).await?; Network::try_from(chain_id).map_err(|_| { anyhow!( "Failed to infer verification network from the RPC chain ID {chain_id:#x}; pass `--network mainnet` or `--network sepolia` explicitly" ) }) } fn display_files_and_confirm( verifier: &Verifier, files_to_display: Vec, confirm_verification: bool, ui: &UI, artifacts: &HashMap, contract_name: &str, ) -> Result<()> { // Display files that will be uploaded // TODO(#3960) JSON output support ui.print_notification("The following files will be uploaded to verifier:".to_string()); for file_path in files_to_display { ui.print_notification(file_path); } // Ask for confirmation after showing files if !confirm_verification { let prompt_text = format!( "\n\tYou are about to submit the above files to the third-party verifier at {verifier}.\n\n\tImportant: Make sure your project's Scarb.toml does not include sensitive information like private keys.\n\n\tAre you sure you want to proceed? (Y/n)" ); let input: String = prompt(prompt_text)?; if !input.to_lowercase().starts_with('y') { bail!("Verification aborted"); } } // Check contract exists after confirmation if !artifacts.contains_key(contract_name) { return Err(anyhow!("Contract named '{contract_name}' was not found")); } Ok(()) } pub async fn verify( args: Verify, manifest_path: &Utf8PathBuf, artifacts: &HashMap, config: &CastConfig, ui: &UI, ) -> Result { let Verify { contract_address, class_hash, contract_name, verifier, network, confirm_verification, package, url, test_files, } = args; let rpc_url = match url { Some(url) => url, None => { if let Some(config_url) = &config.url { config_url.clone() } else if let Some(network) = &config.network { network.url(&FreeProvider::semi_random()).await? } else { let network = network.ok_or_else(|| anyhow!("Either --network or --url must be provided"))?; network.url(&FreeProvider::semi_random()).await? } } }; let provider = get_provider(&rpc_url)?; // Build JSON Payload for the verification request // get the parent dir of the manifest path let workspace_dir = manifest_path .parent() .ok_or(anyhow!("Failed to obtain workspace dir"))?; let contract_identifier = match (class_hash, contract_address) { (Some(class_hash), None) => ContractIdentifier::ClassHash { class_hash: class_hash.to_fixed_hex_string(), }, (None, Some(contract_address)) => ContractIdentifier::Address { contract_address: contract_address.to_fixed_hex_string(), }, _ => { unreachable!("Exactly one of class_hash or contract_address must be provided."); } }; let network = resolve_verification_network(network, config.network, &provider).await?; // Handle test_files warning for Walnut if matches!(verifier, Verifier::Walnut) && test_files { ui.print_warning(WarningMessage::new( "The `--test-files` option is ignored for Walnut verifier", )); } // Create verifier instance, gather files, and perform verification match verifier { Verifier::Walnut => { let walnut = WalnutVerificationInterface::new( network, workspace_dir.to_path_buf(), &provider, ui, )?; // Gather and format files for display let files = walnut.gather_files()?; let files_to_display: Vec = files.iter().map(|(path, _)| format!(" {path}")).collect(); // Display files and confirm display_files_and_confirm( &verifier, files_to_display, confirm_verification, ui, artifacts, &contract_name, )?; // Perform verification walnut .verify(contract_identifier, contract_name, package, false, ui) .await } Verifier::Voyager => { let voyager = Voyager::new(network, workspace_dir.to_path_buf(), &provider, ui)?; // Gather and format files for display let (_, files) = voyager.gather_files(test_files)?; let files_to_display: Vec = files.keys().map(|name| format!(" {name}")).collect(); // Display files and confirm display_files_and_confirm( &verifier, files_to_display, confirm_verification, ui, artifacts, &contract_name, )?; // Perform verification voyager .verify(contract_identifier, contract_name, package, test_files, ui) .await } } } #[cfg(test)] mod tests { use super::resolve_verification_network; use serde_json::json; use sncast::Network; use sncast::get_provider; use starknet_types_core::felt::Felt; use url::Url; use wiremock::matchers::{body_partial_json, method}; use wiremock::{Mock, MockServer, ResponseTemplate}; fn unused_provider() -> starknet_rust::providers::jsonrpc::JsonRpcClient< starknet_rust::providers::jsonrpc::HttpTransport, > { get_provider(&Url::parse("http://127.0.0.1:1").unwrap()).unwrap() } async fn mock_provider_for_chain_id( chain_id: Felt, ) -> ( starknet_rust::providers::jsonrpc::JsonRpcClient< starknet_rust::providers::jsonrpc::HttpTransport, >, MockServer, ) { let mock_rpc = MockServer::start().await; Mock::given(method("POST")) .and(body_partial_json(json!({"method": "starknet_chainId"}))) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "id": 1, "jsonrpc": "2.0", "result": format!("{chain_id:#x}") }))) .expect(1) .mount(&mock_rpc) .await; ( get_provider(&Url::parse(&mock_rpc.uri()).unwrap()).unwrap(), mock_rpc, ) } #[tokio::test] async fn uses_cli_network_when_provided() { let provider = unused_provider(); let network = resolve_verification_network(Some(Network::Mainnet), Some(Network::Sepolia), &provider) .await .unwrap(); assert_eq!(network, Network::Mainnet); } #[tokio::test] async fn uses_config_network_when_cli_network_is_missing() { let provider = unused_provider(); let network = resolve_verification_network(None, Some(Network::Mainnet), &provider) .await .unwrap(); assert_eq!(network, Network::Mainnet); } #[tokio::test] async fn infers_mainnet_from_chain_id_when_no_network_is_configured() { let (provider, _mock_rpc) = mock_provider_for_chain_id(sncast::MAINNET).await; let network = resolve_verification_network(None, None, &provider) .await .unwrap(); assert_eq!(network, Network::Mainnet); } #[tokio::test] async fn infers_sepolia_from_chain_id_when_no_network_is_configured() { let (provider, _mock_rpc) = mock_provider_for_chain_id(sncast::SEPOLIA).await; let network = resolve_verification_network(None, None, &provider) .await .unwrap(); assert_eq!(network, Network::Sepolia); } #[tokio::test] async fn errors_when_network_cannot_be_resolved() { let (provider, _mock_rpc) = mock_provider_for_chain_id(Felt::from_hex_unchecked("0x1234")).await; let error = resolve_verification_network(None, None, &provider) .await .unwrap_err(); assert!( error .to_string() .contains("Failed to infer verification network from the RPC chain ID 0x1234") ); } } ================================================ FILE: crates/sncast/src/starknet_commands/verify/voyager.rs ================================================ use super::explorer::{ContractIdentifier, VerificationInterface}; use anyhow::{Context, Result, anyhow}; use camino::{Utf8Path, Utf8PathBuf}; use foundry_ui::components::warning::WarningMessage; use itertools::Itertools; use reqwest::{self, StatusCode}; use scarb_api::metadata::metadata_for_dir; use scarb_metadata::{Metadata, PackageMetadata}; use serde::Serialize; use sncast::Network; use sncast::response::explorer_link::ExplorerError; use sncast::response::ui::UI; use sncast::{helpers::scarb_utils, response::verify::VerifyResponse}; use starknet_rust::{ core::types::{BlockId, BlockTag}, providers::{ Provider, jsonrpc::{HttpTransport, JsonRpcClient}, }, }; use starknet_types_core::felt::Felt; use std::{collections::HashMap, env, ffi::OsStr, fs, path::PathBuf}; use url::Url; use walkdir::WalkDir; const CAIRO_EXT: &str = "cairo"; const VERIFY_ENDPOINT: &str = "/class-verify"; const STATUS_ENDPOINT: &str = "/class-verify/job"; pub struct Voyager<'a> { network: Network, metadata: Metadata, provider: &'a JsonRpcClient, } #[derive(Debug, Clone, Serialize)] pub struct Body { pub compiler_version: semver::Version, pub scarb_version: semver::Version, pub project_dir_path: Utf8PathBuf, #[serde(rename = "name")] pub contract_name: String, pub package_name: String, pub build_tool: String, pub license: Option, pub files: HashMap, } #[derive(Debug, serde::Deserialize)] pub struct ApiError { error: String, } #[derive(Debug, serde::Deserialize)] pub struct VerificationJobDispatch { job_id: String, } #[derive(Debug, thiserror::Error)] pub enum VoyagerApiError { #[error("Failed to parse {name} path: {path}")] DependencyPathError { name: String, path: String }, #[error("Scarb metadata failed for {name}: {path}")] MetadataError { name: String, path: String }, } fn gather_packages(metadata: &Metadata, packages: &mut Vec) -> Result<()> { let mut workspace_packages: Vec = metadata .packages .clone() .into_iter() .filter(|package_meta| metadata.workspace.members.contains(&package_meta.id)) .filter(|package_meta| !packages.contains(package_meta)) .collect(); let workspace_packages_names = workspace_packages .iter() .map(|package| package.name.clone()) .collect_vec(); // find all dependencies listed by path let mut dependencies: HashMap = HashMap::new(); for package in &workspace_packages { for dependency in &package.dependencies { let name = &dependency.name; let url = Url::parse(&dependency.source.repr).map_err(|_| { VoyagerApiError::DependencyPathError { name: name.clone(), path: dependency.source.repr.clone(), } })?; if url.scheme().starts_with("path") { let path = url.to_file_path() .map_err(|()| VoyagerApiError::DependencyPathError { name: name.clone(), path: dependency.source.repr.clone(), })?; dependencies.insert(name.clone(), path); } } } packages.append(&mut workspace_packages); // filter out dependencies already covered by workspace let out_of_workspace_dependencies: HashMap<&String, &PathBuf> = dependencies .iter() .filter(|&(k, _)| !workspace_packages_names.contains(k)) .collect(); for (name, manifest) in out_of_workspace_dependencies { let new_meta = metadata_for_dir(manifest.parent().expect("manifest should have a parent")) .map_err(|_| VoyagerApiError::MetadataError { name: name.clone(), path: manifest.to_string_lossy().to_string(), })?; gather_packages(&new_meta, packages)?; } Ok(()) } fn package_source_files( package_metadata: &PackageMetadata, include_test_files: bool, ) -> Result> { let mut sources: Vec = WalkDir::new(package_metadata.root.clone()) .into_iter() .filter_map(std::result::Result::ok) .filter(|f| f.file_type().is_file()) .filter(|f| { if let Some(ext) = f.path().extension() { if ext != OsStr::new(CAIRO_EXT) { return false; } let parts: Vec<_> = f .path() .components() .map(|c| c.as_os_str().to_string_lossy().to_lowercase()) .collect(); if parts.contains(&"src".to_string()) { // If include_test_files is true, include all files under src/ if include_test_files { return true; } // Otherwise, skip files with "test" in their path under src/ let path_str = f.path().to_string_lossy().to_lowercase(); if path_str.contains("/test") || path_str.contains("\\test") { return false; } // Also skip files ending with "_test.cairo" or "_tests.cairo" if path_str.ends_with("_test.cairo") || path_str.ends_with("_tests.cairo") { return false; } return true; } if parts.contains(&"test".to_string()) || parts.contains(&"tests".to_string()) { return false; } // We'll include files with #[test] attributes since they might be source files // that happen to include unit tests return true; } false }) .map(walkdir::DirEntry::into_path) .map(Utf8PathBuf::try_from) .try_collect()?; sources.push(package_metadata.manifest_path.clone()); let package_root = &package_metadata.root; if let Some(lic) = package_metadata .manifest_metadata .license_file .as_ref() .map(Utf8Path::new) .map(Utf8Path::to_path_buf) { sources.push(package_root.join(lic)); } if let Some(readme) = package_metadata .manifest_metadata .readme .as_deref() .map(Utf8Path::new) .map(Utf8Path::to_path_buf) { sources.push(package_root.join(readme)); } Ok(sources) } fn longest_common_prefix + Clone>( paths: &[Utf8PathBuf], first_guess: P, ) -> Utf8PathBuf { let ancestors = Utf8Path::ancestors(first_guess.as_ref()); let mut longest_prefix = first_guess.as_ref(); for prefix in ancestors { if paths.iter().all(|src| src.starts_with(prefix)) { longest_prefix = prefix; break; } } longest_prefix.to_path_buf() } impl Voyager<'_> { pub fn gather_files( &self, include_test_files: bool, ) -> Result<(Utf8PathBuf, HashMap)> { let mut packages = vec![]; gather_packages(&self.metadata, &mut packages)?; let mut sources: Vec = vec![]; for package in &packages { let mut package_sources = package_source_files(package, include_test_files)?; sources.append(&mut package_sources); } let prefix = longest_common_prefix(&sources, &self.metadata.workspace.root); let manifest_path = &self.metadata.workspace.manifest_path; let manifest = manifest_path .strip_prefix(&prefix) .map_err(|_| anyhow!("Couldn't strip {prefix} from {manifest_path}"))?; let mut files: HashMap = sources .iter() .map(|p| -> Result<(String, Utf8PathBuf)> { let name = p .strip_prefix(&prefix) .map_err(|_| anyhow!("Couldn't strip {prefix} from {p}"))?; Ok((name.to_string(), p.clone())) }) .try_collect()?; files.insert( manifest.to_string(), self.metadata.workspace.manifest_path.clone(), ); Ok((prefix, files)) } } #[async_trait::async_trait] impl<'a> VerificationInterface<'a> for Voyager<'a> { fn new( network: Network, workspace_dir: Utf8PathBuf, provider: &'a JsonRpcClient, _ui: &'a UI, ) -> Result { let manifest_path = scarb_utils::get_scarb_manifest_for(workspace_dir.as_ref())?; let metadata = scarb_utils::get_scarb_metadata_with_deps(&manifest_path)?; Ok(Voyager { network, metadata, provider, }) } async fn verify( &self, contract_identifier: ContractIdentifier, contract_name: String, package: Option, test_files: bool, ui: &UI, ) -> Result { let hash = match contract_identifier { ContractIdentifier::ClassHash { class_hash } => Felt::from_hex(class_hash.as_ref())?, ContractIdentifier::Address { contract_address } => { self.provider .get_class_hash_at( BlockId::Tag(BlockTag::Latest), Felt::from_hex(contract_address.as_ref())?, ) .await? } }; let cairo_version = self.metadata.app_version_info.cairo.version.clone(); let scarb_version = self.metadata.app_version_info.version.clone(); let mut workspace_packages: Vec = self .metadata .packages .iter() .filter(|&package_meta| self.metadata.workspace.members.contains(&package_meta.id)) .cloned() .collect(); let selected = (if workspace_packages.len() > 1 { match package { Some(ref package_name) => workspace_packages .into_iter() .find(|p| p.name == *package_name) .ok_or(anyhow!( "Package {package_name} not found in scarb metadata" )), None => Err(anyhow!( "More than one package found in scarb metadata - specify package using --package flag" )), } } else { workspace_packages .pop() .ok_or(anyhow!("No packages found in scarb metadata")) })?; let (prefix, files) = self.gather_files(test_files)?; let project_dir_path = self .metadata .workspace .root .strip_prefix(prefix) // backend expects this: "." for cwd .map(|path| { if path.as_str().is_empty() { Utf8Path::new(".") } else { path } })?; if selected.manifest_metadata.license.is_none() { ui.print_warning(WarningMessage::new("License not specified in Scarb.toml")); } let client = reqwest::Client::new(); let body = Body { compiler_version: cairo_version, scarb_version, project_dir_path: project_dir_path.to_path_buf(), contract_name: contract_name.clone(), license: selected.manifest_metadata.license.clone(), package_name: selected.name, build_tool: "scarb".to_string(), files: files .iter() .map(|(name, path)| -> Result<(String, String)> { let contents = fs::read_to_string(path.as_path())?; Ok((name.clone(), contents)) }) .try_collect()?, }; let url = format!( "{}{}/{:#066x}", self.gen_explorer_url()?, VERIFY_ENDPOINT, hash ); let response = client .post(url) .json(&body) .send() .await .context("Failed to submit contract for verification")?; match response.status() { StatusCode::OK => { let message = format!( "{} submitted for verification, you can query the status at: {}{}/{}", contract_name.clone(), self.gen_explorer_url()?, STATUS_ENDPOINT, response.json::().await?.job_id, ); Ok(VerifyResponse { message }) } StatusCode::BAD_REQUEST => Err(anyhow!(response.json::().await?.error)), _ => Err(anyhow!(response.text().await?)), } } fn gen_explorer_url(&self) -> Result { match env::var("VERIFIER_API_URL") { Ok(addr) => Ok(addr), Err(_) => match self.network { Network::Mainnet => Ok("https://api.voyager.online/beta".to_string()), Network::Sepolia => Ok("https://sepolia-api.voyager.online/beta".to_string()), Network::Devnet => Err(ExplorerError::DevnetNotSupported.into()), }, } } } ================================================ FILE: crates/sncast/src/starknet_commands/verify/walnut.rs ================================================ use anyhow::{Context, Result}; use camino::Utf8PathBuf; use reqwest::StatusCode; use sncast::response::ui::UI; use sncast::response::verify::VerifyResponse; use sncast::{Network, response::explorer_link::ExplorerError}; use starknet_rust::providers::{JsonRpcClient, jsonrpc::HttpTransport}; use std::env; use std::ffi::OsStr; use walkdir::WalkDir; use super::explorer::{ContractIdentifier, VerificationInterface, VerificationPayload}; pub struct WalnutVerificationInterface { network: Network, workspace_dir: Utf8PathBuf, } impl WalnutVerificationInterface { pub fn gather_files(&self) -> Result> { let mut files = Vec::new(); for entry in WalkDir::new(self.workspace_dir.clone()).follow_links(true) { let entry = entry?; let path = entry.path(); if path.is_file() && let Some(extension) = path.extension() && (extension == OsStr::new("cairo") || extension == OsStr::new("toml")) { let relative_path = path.strip_prefix(self.workspace_dir.clone())?; files.push(( relative_path.to_string_lossy().into_owned(), path.to_path_buf(), )); } } Ok(files) } } #[async_trait::async_trait] impl VerificationInterface<'_> for WalnutVerificationInterface { fn new( network: Network, workspace_dir: Utf8PathBuf, _provider: &JsonRpcClient, _ui: &UI, ) -> Result { Ok(WalnutVerificationInterface { network, workspace_dir, }) } async fn verify( &self, identifier: ContractIdentifier, contract_name: String, _package: Option, _test_files: bool, _ui: &UI, ) -> Result { // Read all files name along with their contents in a JSON format // in the workspace dir recursively // key is the file name and value is the file content let mut file_data = serde_json::Map::new(); // Use the gather_files method to get the list of files let files = self.gather_files()?; for (relative_path, full_path) in files { let file_content = std::fs::read_to_string(full_path)?; file_data.insert(relative_path, serde_json::Value::String(file_content)); } // Serialize the JSON object to a JSON string let source_code = serde_json::Value::Object(file_data); // Create the JSON payload with "contract name," "address," and "source_code" fields let payload = VerificationPayload { contract_name, identifier, source_code, }; // Serialize the payload to a JSON string for the POST request let json_payload = serde_json::to_string(&payload)?; // Send the POST request to the explorer let client = reqwest::Client::new(); let api_res = client .post(self.gen_explorer_url()?) .header("Content-Type", "application/json") .body(json_payload) .send() .await .context("Failed to send request to verifier API")?; if api_res.status() == StatusCode::OK { let message = api_res .text() .await .context("Failed to read verifier API response")?; Ok(VerifyResponse { message }) } else { let message = api_res.text().await.context("Failed to verify contract")?; Err(anyhow::anyhow!("{message}")) } } fn gen_explorer_url(&self) -> Result { let api_base_url = env::var("VERIFIER_API_URL").unwrap_or_else(|_| "https://api.walnut.dev".to_string()); let path = match self.network { Network::Mainnet => "/v1/sn_main/verify", Network::Sepolia => "/v1/sn_sepolia/verify", Network::Devnet => return Err(ExplorerError::DevnetNotSupported.into()), }; Ok(format!("{api_base_url}{path}")) } } ================================================ FILE: crates/sncast/src/state/hashing.rs ================================================ use sha3::Digest; use sha3::Sha3_256; use starknet_types_core::felt::Felt; use std::vec; trait SerialiseAsBytes { fn serialise_as_bytes(&self) -> Vec; } impl SerialiseAsBytes for Option { fn serialise_as_bytes(&self) -> Vec { match self { None => { vec![0] } Some(val) => { let mut res = vec![1u8]; res.extend(val.serialise_as_bytes()); res } } } } impl SerialiseAsBytes for &[T] { fn serialise_as_bytes(&self) -> Vec { self.iter() .flat_map(SerialiseAsBytes::serialise_as_bytes) .collect() } } impl SerialiseAsBytes for str { fn serialise_as_bytes(&self) -> Vec { self.as_bytes().to_vec() } } impl SerialiseAsBytes for Felt { fn serialise_as_bytes(&self) -> Vec { self.to_bytes_be().to_vec() } } impl SerialiseAsBytes for bool { fn serialise_as_bytes(&self) -> Vec { vec![u8::from(*self)] } } // if we change API this might have collisions with old API hashes pub(super) fn generate_id(selector: &str, inputs_bytes: Vec) -> String { let hash = Sha3_256::new() .chain_update(selector) .chain_update(inputs_bytes) .finalize(); base16ct::lower::encode_string(&hash) } #[must_use] pub fn generate_declare_tx_id(contract_name: &str) -> String { generate_id("declare", contract_name.serialise_as_bytes()) } #[must_use] pub fn generate_deploy_tx_id( class_hash: Felt, constructor_calldata: &[Felt], salt: Option, unique: bool, ) -> String { let bytes = [ class_hash.serialise_as_bytes(), constructor_calldata.serialise_as_bytes(), salt.serialise_as_bytes(), unique.serialise_as_bytes(), ] .concat(); generate_id("deploy", bytes) } #[must_use] pub fn generate_invoke_tx_id( contract_address: Felt, function_selector: Felt, calldata: &[Felt], ) -> String { let bytes = [ contract_address.serialise_as_bytes(), function_selector.serialise_as_bytes(), calldata.serialise_as_bytes(), ] .concat(); generate_id("invoke", bytes) } #[cfg(test)] mod tests { use super::*; use crate::state::hashing::{ generate_declare_tx_id, generate_deploy_tx_id, generate_id, generate_invoke_tx_id, }; use conversions::IntoConv; #[test] fn basic_case() { let hash = generate_id("aaa", vec![b'a']); assert_eq!( hash, "28913c89fa628136fffce7ded99d65a4e3f5c211f82639fed4adca30d53b8dff" ); } #[test] fn declare() { let contract_name = "testcontract"; let hash = generate_declare_tx_id(contract_name); assert_eq!( hash, "058d80fb318b7a9aefce7c3725d062f1e449197909a654920b773d3f2c8bb7ce" ); } #[test] fn deploy() { let class_hash: Felt = Felt::from_dec_str( "3372465304726137760522924034754430320558984443503992760655017624209518336998", ) .unwrap() .into_(); let constructor_calldata = vec![Felt::from(12u32), Felt::from(4u32)]; let salt = Some(Felt::from(89u32)); let unique = true; let hash = generate_deploy_tx_id(class_hash, &constructor_calldata, salt, unique); assert_eq!( hash, "c4146aa83f3d3c4e700db0bb8a2781d5b33914d899559d98918d73eb97985480" ); } #[test] fn invoke() { let contract_address = Felt::from_dec_str( "379396891768624119314138643760266110764950106055405813326441497989022918556", ) .unwrap() .into_(); let function_selector = Felt::from(890u32); let calldata = vec![Felt::from(1809u32), Felt::from(14u32)]; let hash = generate_invoke_tx_id(contract_address, function_selector, &calldata); assert_eq!( hash, "9b7d3fa2d93d1360a343bfd1d3d76aedef74aace5a5ad47ddbda136d9ce9b244" ); } } ================================================ FILE: crates/sncast/src/state/mod.rs ================================================ pub mod hashing; pub mod state_file; ================================================ FILE: crates/sncast/src/state/state_file.rs ================================================ use crate::WaitForTransactionError; use crate::helpers::constants::STATE_FILE_VERSION; use crate::response::declare::DeclareResponse; use crate::response::deploy::StandardDeployResponse; use crate::response::errors::StarknetCommandError; use crate::response::invoke::InvokeResponse; use crate::state::hashing::generate_id; use anyhow::{Context, Result, anyhow}; use camino::Utf8PathBuf; use conversions::serde::serialize::{BufferWriter, CairoSerialize}; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::HashMap; use std::fs; use std::time::{SystemTime, UNIX_EPOCH}; struct InnerStateManager { state_file: Utf8PathBuf, executed_transactions_prev_run: ScriptTransactionEntries, executed_transactions_current_run: ScriptTransactionEntries, } #[derive(Default)] pub struct StateManager { inner: Option, } impl StateManager { pub fn from(state_file_path: Option) -> Result { let res = if let Some(state_file_path) = state_file_path { let executed_transactions = load_or_create_state_file(&state_file_path)? .transactions .unwrap_or_default(); Self { inner: Some(InnerStateManager { state_file: state_file_path, executed_transactions_prev_run: executed_transactions, executed_transactions_current_run: ScriptTransactionEntries::default(), }), } } else { Self::default() }; Ok(res) } #[must_use] pub fn get_output_if_success(&self, tx_id: &str) -> Option { if let Some(state) = &self.inner { return state .executed_transactions_prev_run .get_success_output(tx_id); } None } pub fn maybe_insert_tx_entry( &mut self, tx_id: &str, selector: &str, result: &Result + Clone, StarknetCommandError>, ) -> Result<()> { if let Some(state) = &mut self.inner { state.executed_transactions_current_run.insert( tx_id, ScriptTransactionEntry::from(selector.to_string(), result), ); write_txs_to_state_file( &state.state_file, state.executed_transactions_current_run.clone(), )?; } Ok(()) } } #[derive(Deserialize, Serialize, Debug)] pub struct ScriptTransactionsSchema { pub version: u8, pub transactions: Option, } impl ScriptTransactionsSchema { pub fn append_transaction_entries(&mut self, tx_entries: ScriptTransactionEntries) { match self.transactions { Some(ref mut existing_entries) => { existing_entries .transactions .extend(tx_entries.transactions); } None => { self.transactions = Some(tx_entries); } } } } #[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Default)] pub struct ScriptTransactionEntries { pub transactions: HashMap, } impl ScriptTransactionEntries { #[must_use] pub fn get(&self, tx_id: &str) -> Option<&ScriptTransactionEntry> { self.transactions.get(tx_id) } pub fn insert(&mut self, tx_id: &str, entry: ScriptTransactionEntry) { self.transactions.insert(tx_id.to_string(), entry); } #[must_use] pub fn get_success_output(&self, tx_id: &str) -> Option { if let Some(entry) = self.get(tx_id) && entry.status == ScriptTransactionStatus::Success { return Some(entry.output.clone()); } None } } #[derive(Clone, Deserialize, Serialize, Debug, PartialEq)] pub struct ScriptTransactionEntry { pub name: String, pub output: ScriptTransactionOutput, pub status: ScriptTransactionStatus, pub timestamp: u64, pub misc: Option>, } impl ScriptTransactionEntry { pub fn from( name: String, result: &Result + Clone, StarknetCommandError>, ) -> ScriptTransactionEntry { let (response, status) = match result { Ok(response) => { let response: ScriptTransactionOutput = (*response).clone().into(); (response, ScriptTransactionStatus::Success) } Err(error) => { let transaction_status = match error { StarknetCommandError::WaitForTransactionError( WaitForTransactionError::TransactionError(_), ) => ScriptTransactionStatus::Fail, _ => ScriptTransactionStatus::Error, }; let response = ErrorResponse { message: error.to_string(), }; let response = ScriptTransactionOutput::ErrorResponse(response); (response, transaction_status) } }; let timestamp = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("System time is smaller than Unix epoch") .as_secs(); Self { name, output: response, status, timestamp, misc: None, } } } #[derive(Clone, Deserialize, Serialize, Debug, PartialEq)] #[serde(tag = "type")] pub enum ScriptTransactionOutput { InvokeResponse(InvokeResponse), DeclareResponse(DeclareResponse), DeployResponse(StandardDeployResponse), ErrorResponse(ErrorResponse), } impl From for ScriptTransactionOutput { fn from(value: InvokeResponse) -> Self { Self::InvokeResponse(value) } } impl From for ScriptTransactionOutput { fn from(value: DeclareResponse) -> Self { Self::DeclareResponse(value) } } impl From for ScriptTransactionOutput { fn from(value: StandardDeployResponse) -> Self { Self::DeployResponse(value) } } impl CairoSerialize for ScriptTransactionOutput { fn serialize(&self, output: &mut BufferWriter) { match self { ScriptTransactionOutput::InvokeResponse(val) => { Ok::<_, StarknetCommandError>(val).serialize(output); } ScriptTransactionOutput::DeclareResponse(val) => { Ok::<_, StarknetCommandError>(val).serialize(output); } ScriptTransactionOutput::DeployResponse(val) => { Ok::<_, StarknetCommandError>(val).serialize(output); } ScriptTransactionOutput::ErrorResponse(_) => { panic!("Cannot return ErrorResponse as script function response") } } } } #[derive(Clone, Deserialize, Serialize, Debug, PartialEq)] pub struct ErrorResponse { pub message: String, } #[derive(Clone, Copy, Deserialize, Serialize, Debug, PartialEq)] pub enum ScriptTransactionStatus { // script executed successfully, transaction accepted/succeeded Success, // script executed successfully, transaction rejected/reverted Fail, // script error Error, } pub fn load_state_file(path: &Utf8PathBuf) -> Result { let content = fs::read_to_string(path).context("Failed to load state file")?; match serde_json::from_str::(&content) { Ok(state_file) => { verify_version(state_file.version)?; Ok(state_file) } Err(_) => Err(anyhow!("Failed to parse state file - it may be corrupt")), } } pub fn load_or_create_state_file(path: &Utf8PathBuf) -> Result { if path.exists() { load_state_file(path) } else { let default_state = ScriptTransactionsSchema { version: STATE_FILE_VERSION, transactions: None, }; fs::write( path, serde_json::to_string_pretty(&default_state) .context("Failed to convert ScriptTransactionsSchema to json")?, ) .context("Failed to write initial state to state file")?; Ok(default_state) } } // TODO(#1233): remove must_use attribute when it's no longer needed #[must_use] pub fn generate_transaction_entry_with_id( tx_entry: ScriptTransactionEntry, input_bytes: Vec, ) -> ScriptTransactionEntries { let id = generate_id(tx_entry.name.as_str(), input_bytes); let transaction = HashMap::from([(id, tx_entry)]); ScriptTransactionEntries { transactions: transaction, } } pub fn read_txs_from_state_file( state_file_path: &Utf8PathBuf, ) -> Result> { let state_file = load_state_file(state_file_path)?; Ok(state_file.transactions) } pub fn write_txs_to_state_file( state_file_path: &Utf8PathBuf, tx_entries: ScriptTransactionEntries, ) -> Result<()> { let mut state_file = load_or_create_state_file(state_file_path) .with_context(|| anyhow!(format!("Failed to write to state file {state_file_path}")))?; state_file.append_transaction_entries(tx_entries); fs::write( state_file_path, serde_json::to_string_pretty(&state_file) .expect("Failed to convert ScriptTransactionsSchema to json"), ) .with_context(|| anyhow!("Failed to write new transactions to state file {state_file_path}"))?; Ok(()) } fn verify_version(version: u8) -> Result<()> { match version { STATE_FILE_VERSION => Ok(()), _ => Err(anyhow!(format!("Unsupported state file version {version}"))), } } #[cfg(test)] mod tests { use super::*; use crate::response::declare::DeclareTransactionResponse; use crate::state::state_file::ScriptTransactionOutput::ErrorResponse; use camino::Utf8PathBuf; use conversions::IntoConv; use conversions::string::TryFromHexStr; use starknet_types_core::felt::Felt; use tempfile::TempDir; #[test] fn test_load_or_create_state_file_new_happy() { let tempdir = TempDir::new().unwrap(); let state_file = Utf8PathBuf::from_path_buf( tempdir .path() .join("test_load_or_create_state_file_new_happy.json"), ) .unwrap(); let result = load_or_create_state_file(&state_file).unwrap(); assert_eq!(result.version, 1); assert_eq!(result.transactions, None); } #[test] fn test_load_or_create_state_file_exists_no_txs() { let state_file = Utf8PathBuf::from("tests/data/files/state_no_txs.json"); let result = load_or_create_state_file(&state_file).unwrap(); assert_eq!(result.version, 1); assert_eq!(result.transactions, None); } #[test] fn test_load_or_create_state_file_exists_with_tx() { let state_file = Utf8PathBuf::from("tests/data/files/state_with_tx.json"); let result = load_or_create_state_file(&state_file).unwrap(); assert_eq!( result.transactions.unwrap().get("123abc789").unwrap().name, "declare" ); } #[test] fn test_load_or_create_state_file_exists_with_tx_pre_0_34_0() { let state_file = Utf8PathBuf::from("tests/data/files/pre_0.34.0_state_with_tx.json"); let result = load_or_create_state_file(&state_file).unwrap(); assert_eq!( result.transactions.unwrap().get("123abc789").unwrap().name, "declare" ); } #[test] fn test_load_or_create_state_file_exists_with_txs() { let state_file = Utf8PathBuf::from("tests/data/files/state_with_txs.json"); let result = load_or_create_state_file(&state_file).unwrap(); let transaction_entry = result .transactions .as_ref() .and_then(|tx| tx.get("789def420")) .unwrap(); match &transaction_entry.output { ErrorResponse(error) => assert_eq!( error.message, "Max fee is smaller than the minimal transaction cost" ), _ => unreachable!(), } } #[test] fn test_load_or_create_state_file_exists_corrupt() { let state_file = Utf8PathBuf::from("tests/data/files/state_corrupt_missing_field.json"); let result = load_or_create_state_file(&state_file).unwrap_err(); assert_eq!( result.to_string(), "Failed to parse state file - it may be corrupt" ); } #[test] #[should_panic(expected = "Failed to load state file")] fn test_load_state_file_invalid_path() { let state_file = Utf8PathBuf::from("bla/bla/crypto.json"); load_state_file(&state_file).unwrap(); } #[test] fn test_version_mismatch() { let state_file = Utf8PathBuf::from("tests/data/files/state_wrong_version.json"); let result = load_or_create_state_file(&state_file).unwrap_err(); assert_eq!(result.to_string(), "Unsupported state file version 0"); } #[test] fn test_write_to_file() { let tempdir = TempDir::new().unwrap(); let state_file_path = Utf8PathBuf::from_path_buf(tempdir.path().join("write_state_nofile.json")).unwrap(); let inputs = vec![123u8, 46u8]; let transaction = ScriptTransactionEntry { name: "declare".to_string(), output: ScriptTransactionOutput::DeclareResponse(DeclareResponse::Success( DeclareTransactionResponse { class_hash: Felt::try_from_hex_str("0x123").unwrap().into_(), transaction_hash: Felt::try_from_hex_str("0x321").unwrap().into_(), }, )), status: ScriptTransactionStatus::Success, timestamp: 0, misc: None, }; let tx_entry = generate_transaction_entry_with_id(transaction.clone(), inputs); write_txs_to_state_file(&state_file_path, tx_entry).unwrap(); let content = fs::read_to_string(state_file_path).unwrap(); let parsed_content = serde_json::from_str::(&content).unwrap(); assert_eq!(parsed_content.version, 1); assert_eq!( parsed_content .transactions .unwrap() .transactions .iter() .next() .unwrap() .1, &transaction ); } #[test] fn test_write_to_file_append() { let tempdir = TempDir::new().unwrap(); let state_file_path = Utf8PathBuf::from_path_buf(tempdir.path().join("write_state_append.json")).unwrap(); let inputs = vec![123u8, 45u8]; let transaction1 = ScriptTransactionEntry { name: "declare".to_string(), output: ScriptTransactionOutput::DeclareResponse(DeclareResponse::Success( DeclareTransactionResponse { class_hash: Felt::try_from_hex_str("0x1").unwrap().into_(), transaction_hash: Felt::try_from_hex_str("0x2").unwrap().into_(), }, )), status: ScriptTransactionStatus::Success, timestamp: 0, misc: None, }; let tx1_entry = generate_transaction_entry_with_id(transaction1.clone(), inputs); write_txs_to_state_file(&state_file_path, tx1_entry).unwrap(); let inputs = vec![101u8, 22u8]; let transaction2 = ScriptTransactionEntry { name: "invoke".to_string(), output: ScriptTransactionOutput::InvokeResponse(InvokeResponse { transaction_hash: Felt::try_from_hex_str("0x3").unwrap().into_(), }), status: ScriptTransactionStatus::Success, timestamp: 1, misc: None, }; let tx2_entry = generate_transaction_entry_with_id(transaction2.clone(), inputs); write_txs_to_state_file(&state_file_path, tx2_entry).unwrap(); let content = fs::read_to_string(state_file_path).unwrap(); let parsed_content = serde_json::from_str::(&content).unwrap(); assert_eq!( parsed_content .transactions .clone() .unwrap() .transactions .len(), 2 ); assert_eq!( parsed_content .transactions .clone() .unwrap() .get("73a528dc325194630de256f187a49c8c3984cdeda6eacc0bad31053cb23715e2") .unwrap(), &transaction1 ); assert_eq!( parsed_content .transactions .clone() .unwrap() .get("d9b7c5fd12456cbad0a707f3a7800b17f0ad329c9795b4d392a053ef29caa947") .unwrap(), &transaction2 ); } #[test] fn test_write_to_file_multiple_at_once() { let tempdir = TempDir::new().unwrap(); let state_file_path = Utf8PathBuf::from_path_buf(tempdir.path().join("write_state_multiple.json")).unwrap(); let mut state = ScriptTransactionsSchema { version: STATE_FILE_VERSION, transactions: None, }; let inputs = vec![123u8, 45u8]; let transaction1 = ScriptTransactionEntry { name: "declare".to_string(), output: ScriptTransactionOutput::DeclareResponse(DeclareResponse::Success( DeclareTransactionResponse { class_hash: Felt::try_from_hex_str("0x1").unwrap().into_(), transaction_hash: Felt::try_from_hex_str("0x2").unwrap().into_(), }, )), status: ScriptTransactionStatus::Success, timestamp: 2, misc: None, }; let tx1_entry = generate_transaction_entry_with_id(transaction1.clone(), inputs); state.append_transaction_entries(tx1_entry); let inputs = vec![13u8, 15u8]; let transaction2 = ScriptTransactionEntry { name: "invoke".to_string(), output: ScriptTransactionOutput::InvokeResponse(InvokeResponse { transaction_hash: Felt::try_from_hex_str("0x3").unwrap().into_(), }), status: ScriptTransactionStatus::Success, timestamp: 3, misc: None, }; let tx2_entry = generate_transaction_entry_with_id(transaction2.clone(), inputs); state.append_transaction_entries(tx2_entry); write_txs_to_state_file(&state_file_path, state.transactions.unwrap()).unwrap(); let content = fs::read_to_string(state_file_path).unwrap(); let parsed_content = serde_json::from_str::(&content).unwrap(); assert_eq!( parsed_content .transactions .clone() .unwrap() .transactions .len(), 2 ); assert_eq!( parsed_content .transactions .clone() .unwrap() .get("73a528dc325194630de256f187a49c8c3984cdeda6eacc0bad31053cb23715e2") .unwrap(), &transaction1 ); assert_eq!( parsed_content .transactions .clone() .unwrap() .get("7b99f490728861b6701d95268e612dd4d0b5bb1f3a9e2dbbe9cc27f3eccda234") .unwrap(), &transaction2 ); } #[test] fn test_read_and_write_state_file_exists_with_txs() { let from_state_file = Utf8PathBuf::from("tests/data/files/state_with_txs.json"); let tempdir = TempDir::new().unwrap(); let temp_state_file = Utf8PathBuf::from_path_buf(tempdir.path().join("state_with_txs.json")).unwrap(); fs::copy(from_state_file, &temp_state_file).unwrap(); let tx_id = "789def420".to_string(); let result = read_txs_from_state_file(&temp_state_file).expect("Failed to read state file"); let mut entries = result.unwrap(); let transaction_entry = entries.transactions.get(&tx_id).unwrap(); assert_eq!(entries.transactions.len(), 3); assert_eq!(transaction_entry.status, ScriptTransactionStatus::Fail); let new_transaction = ScriptTransactionEntry { name: "deploy".to_string(), output: ScriptTransactionOutput::DeployResponse(StandardDeployResponse { transaction_hash: Felt::try_from_hex_str("0x3").unwrap().into_(), contract_address: Felt::try_from_hex_str("0x333").unwrap().into_(), }), status: ScriptTransactionStatus::Success, timestamp: 1, misc: None, }; entries .transactions .insert(tx_id.clone(), new_transaction) .unwrap(); write_txs_to_state_file(&temp_state_file, entries).unwrap(); let result = read_txs_from_state_file(&temp_state_file).expect("Failed to read state file"); let entries = result.unwrap(); let transaction_entry = entries.transactions.get(&tx_id).unwrap(); assert_eq!(entries.transactions.len(), 3); assert_eq!(transaction_entry.status, ScriptTransactionStatus::Success); } } ================================================ FILE: crates/sncast/tests/code_quality.rs ================================================ use camino::Utf8PathBuf; use packages_validation::check_and_lint; #[test] fn validate_sncast_std() { let package_path = Utf8PathBuf::from("../../sncast_std") .canonicalize() .unwrap() .try_into() .unwrap(); check_and_lint(&package_path); } ================================================ FILE: crates/sncast/tests/data/accounts/accounts.json ================================================ { "alpha-sepolia": { "user0": { "private_key": "0x56c12e097e49ea382ca8eadec0839401", "public_key": "0x48234b9bc6c1e749f4b908d310d8c53dae6564110b05ccf79016dca8ce7dfac", "address": "0x6f4621e7ad43707b3f69f9df49425c3d94fdc5ab2e444bfa0e7e4edeff7992d" }, "user1": { "private_key": "0xffd33878eed7767e7c546ce3fc026295", "public_key": "0x17b62d16ee2b9b5ccd3320e2c0b234dfbdd1d01d09d0aa29ce164827cddf46a", "salt": "0x14b6b215424909f34f417ddd7cbaca48de2d505d03c92467367d275e847d252", "address": "0xf6ecd22832b7c3713cfa7826ee309ce96a2769833f093795fafa1b8f20c48b", "deployed": true, "type": "open_zeppelin" }, "user2": { "private_key": "0xd55976edf8fadf692436af68f7476817", "public_key": "0x4db538fb2e14aaa37a635d17464e15b5b20e1ab92485c841f0c90ff2061119d", "address": "0x3e40c4c2770812f69166a12b0462e887ecf58a2eba5b7be1fba78450fd07dbd" }, "user3": { "private_key": "0x7e7f595383c98deeb705a6a06e1bc8ff", "public_key": "0x126ae3ef8f5c843112eed75876ce5bc650f01d33d1917c24f0cd0cee57869ef", "address": "0x4f740e090e9930518a3a7efc76c6a61b9dffa09dfb20aae645645af739cfac8" }, "user4": { "private_key": "0x7d4ad2e3c3e6f34ccaae5cf34bfcecb5", "public_key": "0x2598e6ec76379ce596506499ddf1c339e46dbc5d80569ceccee3880421ad237", "address": "0x3ffc270312cbefaf2fb4a88e97cc186797bada41a291331186ec5ca316e32fa" }, "user5": { "private_key": "0xa93f89e75aaf77eaa26fa0292becb8f0", "public_key": "0x3de0121583da0bbc0836e8be45fb93391057fcd0082cb6aae6b941c4a680969", "address": "0x51b3c1e02298485322763acbe269b53f46217adc1dc66f07e3e6f6d0fe885b7" }, "user6": { "private_key": "0x9255d1e74778df39cb6514fd143a431", "public_key": "0x6ec12575eb96e09affeeb84551c7e0e21f5c33ba2b379794285f224aabe2140", "address": "0x6ccfd328e3512986cde1de944f3f6598e5381aae635839cbf7d4db01b8bee0a" }, "user7": { "private_key": "0xb302d1b22f135036dfbd79198777c7e9", "public_key": "0x43916246826b183daf4f9235572ae46b2eabd1a5f3bea14f52d123a88e1dcf2", "address": "0x65b2c72a2fe093c135ab191c2e7ce181205bb10d59a366942a1cbf284b98662" }, "user8": { "private_key": "0xe087cfde9474b2e716b1aeda95a02081", "public_key": "0x4fdaeb60e75399877bd5d14d3dac2e805f3c2a6732851a19db63fee0cfae00d", "address": "0x68d6e92b8e2310c550178b2fe484d5cb89dd6aa93ae804afd4d124e9d6c3cd3" }, "oz_cairo_1": { "private_key": "0x88ecc06581d81c76cef06d6f4f0c1b28", "public_key": "0x8cdbe26bc82084b04eccb3c6f8a76f12ad6c4015b3dc8ab90dc840d42cac29", "address": "0x691a61b12a7105b1372cc377f135213c11e8400a546f6b0e7ea0296046690ce", "legacy": false }, "user9": { "address": "0x56d5ce4cfa419b1ed7b4a36a02673781ee7f8ce14f14dd138f3680d2373ce79", "private_key": "0x783384d0b78b078ee3aecbeefa3b53d", "public_key": "0x404bcccb310f044137fa1790d1b059dbe65a2f2183a47faa63c0d36b2672fe" }, "user10": { "address": "0x374271e0344358bc75965ba584e1f48586d88e1be735447b7b5816644526ad6", "private_key": "0x5c9e5e944bbb2b812b4ed945cb65fde5", "public_key": "0x1c7178f1c48d08463baaaddc077a53c15b88cde319401771db2cc60c554923e" }, "user11": { "address": "0x4ce45bd95d2ec35e3dd4490e28f896367958b966673789fef253626133ed37d", "private_key": "0x5e643953ed2cfb2da8ec571ddd4f0c6e", "public_key": "0xdfc904e94dc2ca03ce25be703959c2a209f9519ef97a6c6697caf283dd569e" }, "user12": { "address": "0x5a21a825f1c5ef6d39b6db8f5c04709dbc003ca77eac4bf76aa4e2ffb3acddb", "private_key": "0x11c734e0f6bc6d7af2eecc3ab4cafecd", "public_key": "0x548aa366cfff6986cf35e10a063d848112961cbb21cd0bb90feded2029fb228" }, "user13": { "address": "0x52e667bcfe4edf245f885841da86f4cc636450473bca1ed10b9c89e92d06989", "private_key": "0x1c2d90055709d02a7ed3e2c7a8747cd3", "public_key": "0x361827f172e65d9f49df3fad6c85e1a54668cb61ef2d1ad53fde4ccc660e577" }, "user14": { "address": "0x67e26e7507906e8b16d237adf405d8c9251d183b884990be4106b2cf2468d4b", "private_key": "0xc0a3ca11e949e7a68018291cbc68c217", "public_key": "0x7cac3417039e39f8b43f597cf2bfbe09b09d7d384ad3e776b40327d671f2cd6" }, "user15": { "address": "0x6b3f4e866f26b547f0b3c44ecda337ccb5fa3bb1039cc133e1fcc263ae71e46", "private_key": "0xa2cc1d4a77df993ffdbbc334f2a9bd75", "public_key": "0x6f966dd05f85a9ad3fcfd4ad2de8fd767b0512bf2905c1154fb033a5c43e334" }, "user16": { "address": "0x225321e7a7a8c5cd2efd8a8d7730b405adbc8f20b9cd80dd522f1d5e3ababfb", "private_key": "0x6f05692b1faf2c28bc4203fcafac001a", "public_key": "0x2bde1887739707bea5204c04d54cb5435c746b1dca7fd5b368ce6466919c0e5" }, "user17": { "address": "0x1774b8406f03426b2754aed8bf4934ea750182b6195d9033dfc90a67902cdbf", "private_key": "0x80e37e863d20b77f53bd347c3d888f2", "public_key": "0x72ebaa063bc33531088ce4bd3e88b7599548a1d86f249056573861b9736158f" }, "user18": { "address": "0x2c7645c4ec2c162e91d43938d145097f33eb00aa3c7aa44264a84315c62c1a7", "private_key": "0x98459e6c48a54ea0839a578c6814fc86", "public_key": "0x4825d8b86a41f7c66c40229445afb139cec19397dd2b81037aa60bf2140c4b9" }, "oz_cairo_0": { "private_key": "0x45136b667118bab7f2a9d656d08981ecd7065feb420c3f81c3ed8f461072b0a", "public_key": "0x6b35e242f65996a44b6cf808b954785926eb89e8f4b6441b65780c4eacb9668", "address": "0x5c8fb90cc7249383cbc83c682565720a6d3df92840c02424a6c230e26464c4c", "class_hah": "0x4d07e40e93398ed3c76981e72dd1fd22557a78ce36c0515f679e27f0bb5bc5f", "salt": "0xe35eea9d1b0fa729", "legacy": true }, "ready": { "address": "0x0321dd5ed1ff8ca08792d5c7c66d9daad1f0249c4defbe045b28ab0fec1beaed", "class_hash": "0x36078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f", "legacy": false, "private_key": "0x00bb17245c662eb6ada1ae85c0a6afc77b0df2aa92ee9046e577f4427cdf6d3a", "public_key": "0x04d2c00e488819b43a09d720694fe97aa44054803f411bdc93d6b22d548f69a2", "salt": "0x50d1de0aa965925d611a0d52142f22f6fd0a664838b213da4621f8d70230b40", "type": "ready" }, "braavos": { "address": "0x2235bf96ef3f88915e3537f9a692977f5caf10b728dd95b0a3ccca2282fbb9b", "class_hash": "0x3957f9f5a1cbfe918cedc2015c85200ca51a5f7506ecb6de98a5207b759bf8a", "deployed": false, "legacy": false, "private_key": "0x18201c12f4742c34655c5e41dba442afbe95fdeb018f8581d1b39b06824affe", "public_key": "0x222c9fa27a71f007fc04da4dad684fa6a2b7328c002788089920b53c16ea374", "salt": "0x76210f283afb4f88", "type": "braavos" }, "oz": { "address": "0x1173129651d1853fe9a15f8e8ef29e4806c5a9469b0a6b9622ee90f392bc26d", "class_hash": "0x5b4b537eaa2399e3aa99c4e2e0208ebd6c71bc1467938cd52c798c601e43564", "deployed": false, "legacy": false, "private_key": "0x4a31141908e08ec6d82d324303d97405ad2196def8a13ea320e2117166809d2", "public_key": "0x36a64f9d432cce317c3e50bc467d48f2797463babe8f29c83c2e6125ddd1947", "salt": "0x25e7144fce03d200", "type": "open_zeppelin" }, "devnet-1": { "private_key": "0x0000000000000000000000000000000056c12e097e49ea382ca8eadec0839401", "public_key": "0x048234b9bc6c1e749f4b908d310d8c53dae6564110b05ccf79016dca8ce7dfac", "address": "0x06f4621e7ad43707b3f69f9df49425c3d94fdc5ab2e444bfa0e7e4edeff7992d" }, "balance-test": { "private_key": "0x1", "public_key": "0x1", "address": "0x0585Dd8cAb667CA8415FaC8bEad99c78947079AA72d9120140549a6f2EDc4128" } } } ================================================ FILE: crates/sncast/tests/data/accounts/faulty_accounts.json ================================================ { "alpha-sepolia": { "with_wrong_private_key": { "private_key": "0x1", "public_key": "0x14b491156e96ecf11dace8999b1e5e30888548a581c067b1956b7468bb279b9", "salt": "0x14b6b215424909f34f417ddd7cbaca48de2d505d03c92467367d275e847d252", "address": "0x76e7ce6466e353a00b614ee763f1bc93dba01a57925784a7682efa6b1879c3d", "deployed": true }, "with_wrong_class_hash": { "private_key": "0x88ecc06581d81c76cef06d6f4f0c1b28", "public_key": "0x8cdbe26bc82084b04eccb3c6f8a76f12ad6c4015b3dc8ab90dc840d42cac29", "address": "0x691a61b12a7105b1372cc377f135213c11e8400a546f6b0e7ea0296046690ce", "class_hash": "0x1" }, "with_wrong_address": { "private_key": "0xe3e70682c2094cac629f6fbed82c07cd", "public_key": "0x7e52885445756b313ea16849145363ccb73fb4ab0440dbac333cf9d13de82b9", "address": "0x2" }, "with_nonexistent_address": { "private_key": "0xe3e70682c2094cac629f6fbed82c07cd", "public_key": "0x7e52885445756b313ea16849145363ccb73fb4ab0440dbac333cf9d13de82b9", "address": "0x1010101010011aaabbcc" } } } ================================================ FILE: crates/sncast/tests/data/accounts/faulty_accounts_invalid_felt.json ================================================ { "alpha-sepolia": { "with_invalid_private_key": { "private_key": "private_key", "public_key": "0x14b491156e96ecf11dace8999b1e5e30888548a581c067b1956b7468bb279b9", "salt": "0x14b6b215424909f34f417ddd7cbaca48de2d505d03c92467367d275e847d252", "address": "0x76e7ce6466e353a00b614ee763f1bc93dba01a57925784a7682efa6b1879c3d", "deployed": true } } } ================================================ FILE: crates/sncast/tests/data/accounts/invalid_format.json ================================================ { "alpha-sepolia": { "__default__": { "private_key": "0x421b62d2cba1fd39798d719a6cee5f599afc79b0bacbea50f76215057c068dd", "public_key": "0x12d3ad59161fd2a72d5bc8501bb2f2ca1acd34706d2dfa31a90aadb4b41e050", "address": "0x20f8c63faff27a0c5fe8a25dc1635c40c971bf67b8c35c6089a998649dfdfcb" } "user1": { "private_key": "0x74d866eabee023f563684e955401326eeec6a4d3ce609d23bfe8b6b391de269", "public_key": "0x14b491156e96ecf11dace8999b1e5e30888548a581c067b1956b7468bb279b9", "salt": "0x14b6b215424909f34f417ddd7cbaca48de2d505d03c92467367d275e847d252", "address": "0x76e7ce6466e353a00b614ee763f1bc93dba01a57925784a7682efa6b1879c3d", "deployed": true } } } ================================================ FILE: crates/sncast/tests/data/contracts/build_fails/Scarb.toml ================================================ [package] name = "build_fails" version = "0.1.0" edition = "2024_07" [dependencies] starknet = ">=2.0.2" [[target.starknet-contract]] [lib] sierra = false ================================================ FILE: crates/sncast/tests/data/contracts/build_fails/src/lib.cairo ================================================ #[starknet::contract] mod BuildFails { #[storage] struct Storage { storage: felt2, } } ================================================ FILE: crates/sncast/tests/data/contracts/constructor_with_params/Scarb.toml ================================================ [package] name = "constructor_with_params" version = "0.1.0" edition = "2024_07" [dependencies] starknet = ">=2.0.2" [[target.starknet-contract]] [lib] sierra = false ================================================ FILE: crates/sncast/tests/data/contracts/constructor_with_params/src/lib.cairo ================================================ #[starknet::contract] mod ConstructorWithParams { #[storage] struct Storage { value1: felt252, value2: u256, } #[constructor] fn constructor(ref self: ContractState, first: felt252, second: u256) { self.value1.write(first); self.value2.write(second); } } ================================================ FILE: crates/sncast/tests/data/contracts/contract_with_constructor_params/Scarb.toml ================================================ [package] name = "contract_with_constructor_params" version = "0.1.0" edition = "2024_07" [dependencies] starknet = ">=2.10.1" [[target.starknet-contract]] [lib] sierra = false ================================================ FILE: crates/sncast/tests/data/contracts/contract_with_constructor_params/src/lib.cairo ================================================ #[starknet::contract] mod ContractWithConstructorParams { #[storage] struct Storage {} #[constructor] fn constructor(ref self: ContractState, foo: felt252, bar: felt252) { let _x = foo + bar; } } ================================================ FILE: crates/sncast/tests/data/contracts/map/Scarb.toml ================================================ [package] name = "map" version = "0.1.0" edition = "2024_07" [dependencies] starknet = ">=2.0.2" [[target.starknet-contract]] [lib] sierra = false ================================================ FILE: crates/sncast/tests/data/contracts/map/src/lib.cairo ================================================ #[starknet::interface] trait IMap { fn put(ref self: TMapState, key: felt252, value: felt252); fn get(self: @TMapState, key: felt252) -> felt252; } #[starknet::contract] mod Map { use starknet::storage::{Map, StorageMapReadAccess, StoragePathEntry, StoragePointerWriteAccess}; #[storage] struct Storage { storage: Map, } #[abi(embed_v0)] impl MapImpl of super::IMap { fn put(ref self: ContractState, key: felt252, value: felt252) { self.storage.entry(key).write(value); } fn get(self: @ContractState, key: felt252) -> felt252 { self.storage.read(key) } } } ================================================ FILE: crates/sncast/tests/data/contracts/map/src/test_helpers.cairo ================================================ fn create_test_pair(key: felt252, value: felt252) -> (felt252, felt252) { (key, value) } ================================================ FILE: crates/sncast/tests/data/contracts/map/src/tests.cairo ================================================ /// Simple test file for testing --test-files flag functionality /// This file contains basic test helper functions /// Test function that validates a felt252 value #[test] fn test_validate_felt(value: felt252) -> bool { value != 0 } ================================================ FILE: crates/sncast/tests/data/contracts/multiple_packages/Scarb.toml ================================================ [workspace] members = [ "crates/*", ] [workspace.dependencies] starknet = "2.4.0" [workspace.package] version = "0.1.0" [package] name = "main_workspace" version.workspace = true edition = "2024_07" [dependencies] starknet.workspace = true package1 = { path = "crates/package1" } package2 = { path = "crates/package2" } [[target.starknet-contract]] ================================================ FILE: crates/sncast/tests/data/contracts/multiple_packages/crates/package1/Scarb.toml ================================================ [package] name = "package1" version.workspace = true edition = "2024_07" [dependencies] starknet.workspace = true [[target.starknet-contract]] [lib] ================================================ FILE: crates/sncast/tests/data/contracts/multiple_packages/crates/package1/src/lib.cairo ================================================ #[starknet::contract] pub mod supercomplexcode1 { #[storage] struct Storage {} #[abi(embed_v0)] fn whatever(ref self: ContractState) -> felt252 { 1 } } ================================================ FILE: crates/sncast/tests/data/contracts/multiple_packages/crates/package2/Scarb.toml ================================================ [package] name = "package2" version.workspace = true edition = "2024_07" [dependencies] starknet.workspace = true [[target.starknet-contract]] [lib] ================================================ FILE: crates/sncast/tests/data/contracts/multiple_packages/crates/package2/src/lib.cairo ================================================ #[starknet::contract] pub mod supercomplexcode2 { #[storage] struct Storage {} #[abi(embed_v0)] fn whatever(ref self: ContractState) -> felt252 { 2 } } ================================================ FILE: crates/sncast/tests/data/contracts/multiple_packages/src/lib.cairo ================================================ #[starknet::contract] mod supercomplexcode { use package1::supercomplexcode1; #[storage] struct Storage {} #[abi(embed_v0)] fn whatever(ref self: ContractState) -> felt252 { 3 } } ================================================ FILE: crates/sncast/tests/data/contracts/no_casm/Scarb.toml ================================================ [package] name = "build_fails_no_casm" version = "0.1.0" edition = "2024_07" [dependencies] starknet = ">=2.0.2" [[target.starknet-contract]] sierra = true casm = false [lib] sierra = true ================================================ FILE: crates/sncast/tests/data/contracts/no_casm/src/lib.cairo ================================================ #[starknet::interface] trait Iminimal_contract { fn empty(ref self: TContractState); } #[starknet::contract] mod minimal_contract { #[storage] struct Storage {} #[abi(embed_v0)] impl minimal_contractImpl of super::Iminimal_contract { fn empty(ref self: ContractState) {} } } ================================================ FILE: crates/sncast/tests/data/contracts/no_sierra/Scarb.toml ================================================ [package] name = "build_fails_no_sierra" version = "0.1.0" edition = "2024_07" [dependencies] starknet = ">=2.0.2" [[target.starknet-contract]] sierra = false [lib] sierra = false ================================================ FILE: crates/sncast/tests/data/contracts/no_sierra/src/lib.cairo ================================================ #[starknet::interface] trait Iminimal_contract { fn empty(ref self: TContractState); } #[starknet::contract] mod minimal_contract { #[storage] struct Storage {} #[abi(embed_v0)] impl minimal_contractImpl of super::Iminimal_contract { fn empty(ref self: ContractState) {} } } ================================================ FILE: crates/sncast/tests/data/contracts/virtual_workspace/Scarb.toml ================================================ [workspace] members = [ "crates/*", ] [workspace.package] version = "0.1.0" [workspace.dependencies] starknet = "2.4.0" ================================================ FILE: crates/sncast/tests/data/contracts/virtual_workspace/crates/cast_addition/Scarb.toml ================================================ [package] name = "cast_addition" version = "0.1.0" edition = "2024_07" [dependencies] starknet.workspace = true [[target.starknet-contract]] [lib] ================================================ FILE: crates/sncast/tests/data/contracts/virtual_workspace/crates/cast_addition/src/lib.cairo ================================================ pub fn add(a: felt252, b: felt252) -> felt252 { a + b } #[starknet::contract] mod AdditionContract { use cast_addition::add; #[storage] struct Storage {} #[external(v0)] fn answer(ref self: ContractState) -> felt252 { add(10, 20) } } ================================================ FILE: crates/sncast/tests/data/contracts/virtual_workspace/crates/cast_fibonacci/Scarb.toml ================================================ [package] name = "cast_fibonacci" version = "0.1.0" edition = "2024_07" [dependencies] cast_addition = { path = "../cast_addition" } starknet.workspace = true [[target.starknet-contract]] build-external-contracts = ["cast_addition::AdditionContract"] [lib] ================================================ FILE: crates/sncast/tests/data/contracts/virtual_workspace/crates/cast_fibonacci/src/lib.cairo ================================================ use cast_addition::add; fn fib(a: felt252, b: felt252, n: felt252) -> felt252 { match n { 0 => a, _ => fib(b, add(a, b), n - 1), } } #[starknet::contract] mod FibonacciContract { use cast_addition::add; use cast_fibonacci::fib; #[storage] struct Storage {} #[external(v0)] fn answer(ref self: ContractState) -> felt252 { add(fib(0, 1, 16), fib(0, 1, 8)) } } ================================================ FILE: crates/sncast/tests/data/files/correct_snfoundry.toml ================================================ [sncast.default] url = "http://127.0.0.1:5055/rpc" accounts-file = "../account-file" account = "user1" [sncast.profile1] url = "http://127.0.0.1:5050/rpc" account = "user3" [sncast.profile2] url = "http://127.0.0.1:5055/rpc" accounts-file = "../account-file" account = "user100" block-explorer = "ViewBlock" [sncast.profile3] url = "http://127.0.0.1:5055/rpc" account = "/path/to/account.json" keystore = "../keystore" [sncast.profile4] url = "http://127.0.0.1:5055/rpc" accounts-file = "../account-file" account = "user3" [sncast.profile5] url = "http://127.0.0.1:5055/rpc" account = "user8" [sncast.profile6] accounts-file = "/path/to/account.json" account = "user1" wait-params = { timeout = 500, retry-interval = 10 } show-explorer-links = false [sncast.profile7] network = "sepolia" account = "user1" accounts-file = "/path/to/account.json" [sncast.no_url] accounts-file = "../account-file" account = "user1" ================================================ FILE: crates/sncast/tests/data/files/data_transformer_contract_abi.json ================================================ [{"name":"DataTransformerImpl","type":"impl","interface_name":"data_transformer_contract::IDataTransformer"},{"name":"core::integer::u256","type":"struct","members":[{"name":"low","type":"core::integer::u128"},{"name":"high","type":"core::integer::u128"}]},{"name":"data_transformer_contract::SimpleStruct","type":"struct","members":[{"name":"a","type":"core::felt252"}]},{"name":"data_transformer_contract::NestedStructWithField","type":"struct","members":[{"name":"a","type":"data_transformer_contract::SimpleStruct"},{"name":"b","type":"core::felt252"}]},{"name":"data_transformer_contract::Enum","type":"enum","variants":[{"name":"One","type":"()"},{"name":"Two","type":"core::integer::u128"},{"name":"Three","type":"data_transformer_contract::NestedStructWithField"}]},{"name":"core::byte_array::ByteArray","type":"struct","members":[{"name":"data","type":"core::array::Array::"},{"name":"pending_word","type":"core::felt252"},{"name":"pending_word_len","type":"core::integer::u32"}]},{"name":"core::bool","type":"enum","variants":[{"name":"False","type":"()"},{"name":"True","type":"()"}]},{"name":"data_transformer_contract::ComplexStruct","type":"struct","members":[{"name":"a","type":"data_transformer_contract::NestedStructWithField"},{"name":"b","type":"core::felt252"},{"name":"c","type":"core::integer::u8"},{"name":"d","type":"core::integer::i32"},{"name":"e","type":"data_transformer_contract::Enum"},{"name":"f","type":"core::byte_array::ByteArray"},{"name":"g","type":"core::array::Array::"},{"name":"h","type":"core::integer::u256"},{"name":"i","type":"(core::integer::i128, core::integer::u128)"}]},{"name":"data_transformer_contract::BitArray","type":"struct","members":[{"name":"bit","type":"core::felt252"}]},{"name":"alexandria_data_structures::bit_array::BitArray","type":"struct","members":[{"name":"data","type":"core::array::Array::"},{"name":"current","type":"core::felt252"},{"name":"read_pos","type":"core::integer::u32"},{"name":"write_pos","type":"core::integer::u32"}]},{"name":"core::array::Span::","type":"struct","members":[{"name":"snapshot","type":"@core::array::Array::"}]},{"name":"data_transformer_contract::IDataTransformer","type":"interface","items":[{"name":"simple_fn","type":"function","inputs":[{"name":"a","type":"core::felt252"}],"outputs":[],"state_mutability":"external"},{"name":"u256_fn","type":"function","inputs":[{"name":"a","type":"core::integer::u256"}],"outputs":[],"state_mutability":"external"},{"name":"signed_fn","type":"function","inputs":[{"name":"a","type":"core::integer::i32"}],"outputs":[],"state_mutability":"external"},{"name":"unsigned_fn","type":"function","inputs":[{"name":"a","type":"core::integer::u32"}],"outputs":[],"state_mutability":"external"},{"name":"tuple_fn","type":"function","inputs":[{"name":"a","type":"(core::felt252, core::integer::u8, data_transformer_contract::Enum)"}],"outputs":[],"state_mutability":"external"},{"name":"complex_fn","type":"function","inputs":[{"name":"arr","type":"core::array::Array::>"},{"name":"one","type":"core::integer::u8"},{"name":"two","type":"core::integer::i16"},{"name":"three","type":"core::byte_array::ByteArray"},{"name":"four","type":"(core::felt252, core::integer::u32)"},{"name":"five","type":"core::bool"},{"name":"six","type":"core::integer::u256"}],"outputs":[],"state_mutability":"external"},{"name":"simple_struct_fn","type":"function","inputs":[{"name":"a","type":"data_transformer_contract::SimpleStruct"}],"outputs":[],"state_mutability":"external"},{"name":"nested_struct_fn","type":"function","inputs":[{"name":"a","type":"data_transformer_contract::NestedStructWithField"}],"outputs":[],"state_mutability":"external"},{"name":"enum_fn","type":"function","inputs":[{"name":"a","type":"data_transformer_contract::Enum"}],"outputs":[],"state_mutability":"external"},{"name":"complex_struct_fn","type":"function","inputs":[{"name":"a","type":"data_transformer_contract::ComplexStruct"}],"outputs":[],"state_mutability":"external"},{"name":"external_struct_fn","type":"function","inputs":[{"name":"a","type":"data_transformer_contract::BitArray"},{"name":"b","type":"alexandria_data_structures::bit_array::BitArray"}],"outputs":[],"state_mutability":"external"},{"name":"span_fn","type":"function","inputs":[{"name":"a","type":"core::array::Span::"}],"outputs":[],"state_mutability":"external"},{"name":"multiple_signed_fn","type":"function","inputs":[{"name":"a","type":"core::integer::i32"},{"name":"b","type":"core::integer::i8"}],"outputs":[],"state_mutability":"external"}]},{"name":"constructor","type":"constructor","inputs":[{"name":"init_owner","type":"core::starknet::contract_address::ContractAddress"}]},{"kind":"enum","name":"data_transformer_contract::DataTransformer::Event","type":"event","variants":[]}] ================================================ FILE: crates/sncast/tests/data/files/data_transformer_contract_abi_missing_function.json ================================================ [ { "name": "DataTransformerImpl", "type": "impl", "interface_name": "data_transformer_contract::IDataTransformer" }, { "name": "data_transformer_contract::IDataTransformer", "type": "interface", "items": [] } ] ================================================ FILE: crates/sncast/tests/data/files/data_transformer_contract_abi_missing_type.json ================================================ [ { "name": "DataTransformerImpl", "type": "impl", "interface_name": "data_transformer_contract::IDataTransformer" }, { "name": "data_transformer_contract::IDataTransformer", "type": "interface", "items": [ { "name": "nested_struct_fn", "type": "function", "inputs": [ { "name": "a", "type": "data_transformer_contract::NestedStructWithField" } ], "outputs": [], "state_mutability": "external" } ] } ] ================================================ FILE: crates/sncast/tests/data/files/invalid_snfoundry.toml ================================================ [sncast.url_and_network] account = "user1" url = "http://some.url" network = "sepolia" accounts-file = "/path/to/account.json" [sncast.profile_with_stark_scan] account = "user1" url = "http://some.url" network = "sepolia" accounts-file = "/path/to/account.json" block-explorer = "StarkScan" ================================================ FILE: crates/sncast/tests/data/files/pre_0.34.0_state_with_tx.json ================================================ { "version": 1, "transactions": [{ "123abc789": { "name": "declare", "output": { "type": "DeclareResponse", "class_hash": "0x123", "transaction_hash": "0x321" }, "status": "Success", "timestamp": 1706093159, "misc": null } }] } ================================================ FILE: crates/sncast/tests/data/files/state_corrupt_missing_field.json ================================================ { "version": 1, "transactions": { "123abc789": { "name": "declare", "output": { "class_hash": "0x123", "transaction_hash": "0x321" }, "status": "success", "misc": null } } } ================================================ FILE: crates/sncast/tests/data/files/state_no_txs.json ================================================ { "version": 1, "transactions": null } ================================================ FILE: crates/sncast/tests/data/files/state_with_tx.json ================================================ { "version": 1, "transactions": [{ "123abc789": { "name": "declare", "output": { "type": "DeclareResponse", "status": "Success", "class_hash": "0x0000000000000000000000000000000000000000000000000000000000000123", "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000321" }, "status": "Success", "timestamp": 1706093159, "misc": null } }] } ================================================ FILE: crates/sncast/tests/data/files/state_with_txs.json ================================================ { "version": 1, "transactions": [{ "123abc456": { "name": "declare", "output": { "type": "DeclareResponse", "status": "Success", "class_hash": "0x0000000000000000000000000000000000000000000000000000000000000123", "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000321" }, "status": "Success", "timestamp": 1706093159, "misc": null }, "111aaa111": { "name": "declare", "output": { "type": "DeclareResponse", "status": "AlreadyDeclared", "class_hash": "0x0000000000000000000000000000000000000000000000000000000000000123" }, "status": "Success", "timestamp": 1706093159, "misc": null }, "789def420": { "name": "deploy", "output": { "type": "ErrorResponse", "message": "Max fee is smaller than the minimal transaction cost" }, "status": "Fail", "timestamp": 1706093160, "misc": null } }] } ================================================ FILE: crates/sncast/tests/data/files/state_wrong_version.json ================================================ { "version": 0, "transactions": null } ================================================ FILE: crates/sncast/tests/data/keystore/my_account.json ================================================ { "version": 1, "variant": { "type": "open_zeppelin", "version": 1, "public_key": "0xe2d3d7080bfc665e0060a06e8e95c3db3ff78a1fec4cc81ddc87e49a12e0a", "legacy": true }, "deployment": { "status": "deployed", "class_hash": "0x4d07e40e93398ed3c76981e72dd1fd22557a78ce36c0515f679e27f0bb5bc5f", "address": "0xcce3217e4aea0ab738b55446b1b378750edfca617db549fda1ede28435206c" } } ================================================ FILE: crates/sncast/tests/data/keystore/my_account_braavos_invalid_multisig.json ================================================ { "version": 1, "variant": { "type": "braavos", "version": 1, "multisig": { "status": "on" }, "signers": [ { "type": "stark", "public_key": "0xe2d3d7080bfc665e0060a06e8e95c3db3ff78a1fec4cc81ddc87e49a12e0a" } ] }, "deployment": { "status": "undeployed", "class_hash": "0x816dd0297efc55dc1e7559020a3a825e81ef734b558f03c83325d4da7e6253", "salt": "0x1fe42e5aa816ee55d72471ad50e74a0d2974c9497b86adef561d27c12b4e045", "context": { "variant": "braavos", "base_account_class_hash": "0x13bfe114fb1cf405bfc3a7f8dbe2d91db146c17521d40dcf57e16d6b59fa8e6" } } } ================================================ FILE: crates/sncast/tests/data/keystore/my_account_braavos_multiple_signers.json ================================================ { "version": 1, "variant": { "type": "braavos", "version": 1, "multisig": { "status": "off" }, "signers": [ { "type": "stark", "public_key": "0xe2d3d7080bfc665e0060a06e8e95c3db3ff78a1fec4cc81ddc87e49a12e0a" }, { "type": "stark", "public_key": "0xe2d3d7080bfc665e0060a06e8e95c3db3ff78a1fec4cc81ddc87e49a12e0a" } ] }, "deployment": { "status": "undeployed", "class_hash": "0x816dd0297efc55dc1e7559020a3a825e81ef734b558f03c83325d4da7e6253", "salt": "0x1fe42e5aa816ee55d72471ad50e74a0d2974c9497b86adef561d27c12b4e045", "context": { "variant": "braavos", "base_account_class_hash": "0x13bfe114fb1cf405bfc3a7f8dbe2d91db146c17521d40dcf57e16d6b59fa8e6" } } } ================================================ FILE: crates/sncast/tests/data/keystore/my_account_braavos_undeployed_happy_case.json ================================================ { "version": 1, "variant": { "type": "braavos", "version": 1, "multisig": { "status": "off" }, "signers": [ { "type": "stark", "public_key": "0xe2d3d7080bfc665e0060a06e8e95c3db3ff78a1fec4cc81ddc87e49a12e0a" } ] }, "deployment": { "status": "undeployed", "class_hash": "0x816dd0297efc55dc1e7559020a3a825e81ef734b558f03c83325d4da7e6253", "salt": "0x1fe42e5aa816ee55d72471ad50e74a0d2974c9497b86adef561d27c12b4e045", "context": { "variant": "braavos", "base_account_class_hash": "0x13bfe114fb1cf405bfc3a7f8dbe2d91db146c17521d40dcf57e16d6b59fa8e6" } } } ================================================ FILE: crates/sncast/tests/data/keystore/my_account_invalid.json ================================================ { "version": 1, "variant": { "type": "open_zeppelin", "version": 1, "public_key": "0xe2d3d7080bfc665e0060a06e8e95c3db3ff78a1fec4cc81ddc87e49a12e0a" }, "deployment": { "class_hash": "0x4d07e40e93398ed3c76981e72dd1fd22557a78ce36c0515f679e27f0bb5bc5f", "salt": "0x14df438ac6825165c7a0af29decd5892528b763a333f93a5f6b12980dbddd9f" } } ================================================ FILE: crates/sncast/tests/data/keystore/my_account_oz_undeployed_happy_case.json ================================================ { "version": 1, "variant": { "type": "open_zeppelin", "version": 1, "public_key": "0xe2d3d7080bfc665e0060a06e8e95c3db3ff78a1fec4cc81ddc87e49a12e0a" }, "deployment": { "status": "undeployed", "class_hash": "0x4d07e40e93398ed3c76981e72dd1fd22557a78ce36c0515f679e27f0bb5bc5f", "salt": "0x14df438ac6825165c7a0af29decd5892528b763a333f93a5f6b12980dbddd9b" } } ================================================ FILE: crates/sncast/tests/data/keystore/my_account_ready_undeployed_happy_case.json ================================================ { "version": 1, "variant": { "type": "ready", "version": 1, "owner": "0xe2d3d7080bfc665e0060a06e8e95c3db3ff78a1fec4cc81ddc87e49a12e0a", "guardian": "0x0" }, "deployment": { "status": "undeployed", "class_hash": "0x36078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f", "salt": "0x1924d8e7415bd440195fefa23a1ce71b106252109bdbc59e51afb9229f136f5" } } ================================================ FILE: crates/sncast/tests/data/keystore/my_account_undeployed.json ================================================ { "version": 1, "variant": { "type": "open_zeppelin", "version": 1, "public_key": "0xe2d3d7080bfc665e0060a06e8e95c3db3ff78a1fec4cc81ddc87e49a12e0a" }, "deployment": { "status": "undeployed", "class_hash": "0x4d07e40e93398ed3c76981e72dd1fd22557a78ce36c0515f679e27f0bb5bc5f", "salt": "0x14df438ac6825165c7a0af29decd5892528b763a333f93a5f6b12980dbddd9f" } } ================================================ FILE: crates/sncast/tests/data/keystore/my_account_undeployed_happy_case_other_args.json ================================================ { "version": 1, "variant": { "type": "open_zeppelin", "version": 1, "public_key": "0xe2d3d7080bfc665e0060a06e8e95c3db3ff78a1fec4cc81ddc87e49a12e0a" }, "deployment": { "status": "undeployed", "class_hash": "0x4d07e40e93398ed3c76981e72dd1fd22557a78ce36c0515f679e27f0bb5bc5f", "salt": "0x14df438ac6825165c7a0af29decd5892528b763a333f93a5f6b12980dbddd9a" } } ================================================ FILE: crates/sncast/tests/data/keystore/my_key.json ================================================ { "crypto": { "cipher": "aes-128-ctr", "cipherparams": { "iv": "6778ab3640234a4446fbd8e5ef0ed4fd" }, "ciphertext": "6a2b513c8113368f8edb260f1cb6a18963be1a557c43e4a5cb8addca61a9f960", "kdf": "scrypt", "kdfparams": { "dklen": 32, "n": 8192, "p": 1, "r": 8, "salt": "3cabf6912a1af118d279259d0a73a08d2ee2955dc8b13daa144ff367a58581a6" }, "mac": "86de09eddb631f7984b6e327ef486e5bd9e9a5c9e8738feb89350fcecfcfc7cd" }, "id": "093ecb4e-794e-416c-81ac-03a18b098f63", "version": 3 } ================================================ FILE: crates/sncast/tests/data/keystore/my_key_invalid.json ================================================ { "crypto": { "cipher": "aes-128-ctr", "cipherparams": { "iv": "0b32081dd36ebf77706376034b7d43da" }, "ciphertext": "e98a9457cb1db843a5e70efda609d558b108e82e9af17c378a0c4c72564ac9dd", "kdf": "scrypt", "kdfparams": { "dklen": 32, "n": 8192, "p": 1, "r": 8, "salt": "219eb431e32d0514432d666dbc1e6d4861798477480d6ba26f215967726a11f4" }, "mac": "832bc7db3656fde7f5ed69d3d8a68a27f12ef1e85ee2b76896d6f241c6d72ff0" }, "id": "de5a454e-a55a-48e9-a1b7-3da6ddc0c20f", "version": 3 } ================================================ FILE: crates/sncast/tests/data/keystore/predeployed_account.json ================================================ { "deployment": { "address": "0x4ee94bdf625820bc562c49c4d1ca4b2ef82bcfc5ed0cf67464770bea333b19a", "class_hash": "0x4d07e40e93398ed3c76981e72dd1fd22557a78ce36c0515f679e27f0bb5bc5f", "status": "deployed" }, "variant": { "public_key": "0xd39cc3278f855cb025b28409d16137792175638a8acec3b5b3d2487d2472a6", "type": "open_zeppelin", "version": 1 }, "version": 1 } ================================================ FILE: crates/sncast/tests/data/keystore/predeployed_key.json ================================================ { "crypto": { "cipher": "aes-128-ctr", "cipherparams": { "iv": "969dc1d196eccd96518b4945820a7ac0" }, "ciphertext": "fd20c0dfa110976bffa70b54321487c4546d0c26816c8383ffcfa1ba099f73b1", "kdf": "scrypt", "kdfparams": { "dklen": 32, "n": 8192, "p": 1, "r": 8, "salt": "177919634a458573cf15ee537f658cca630d32aaae1a3b7cecf86a427ef01ea9" }, "mac": "adada9c6d157999ba340dc99c6b6596698904026e3f619d025962d1cbcd2a50e" }, "id": "caf9c371-4e46-45c7-b682-a44667a83725", "version": 3 } ================================================ FILE: crates/sncast/tests/data/multicall_configs/deploy_invalid.toml ================================================ [[call]] call_type = "deploy" class_hash = "0x1" inputs = [] id = "Map" unique = false ================================================ FILE: crates/sncast/tests/data/multicall_configs/deploy_invoke.toml ================================================ [[call]] call_type = "deploy" class_hash = "0x02a09379665a749e609b4a8459c86fe954566a6beeaddd0950e43f6c700ed321" inputs = [] id = "map_contract" unique = false [[call]] call_type = "invoke" contract_address = "0xcd8f9ab31324bb93251837e4efb4223ee195454f6304fcfcb277e277653008" function = "put" inputs = ["0x123", "234"] [[call]] call_type = "invoke" contract_address = "@map_contract" function = "put" inputs = ["0x123", "234"] ================================================ FILE: crates/sncast/tests/data/multicall_configs/deploy_invoke_calldata_ids.toml ================================================ [[call]] call_type = "deploy" class_hash = "0x02a09379665a749e609b4a8459c86fe954566a6beeaddd0950e43f6c700ed321" inputs = [] id = "map_contract" unique = false [[call]] call_type = "invoke" contract_address = "0xcd8f9ab31324bb93251837e4efb4223ee195454f6304fcfcb277e277653008" function = "put" inputs = ["0x123", "map_contract"] [[call]] call_type = "deploy" class_hash = "0x059426c817fb8103edebdbf1712fa084c6744b2829db9c62d1ea4dce14ee6ded" inputs = ["map_contract", "0x1", "0x1"] id = "constructor-params" unique = false ================================================ FILE: crates/sncast/tests/data/multicall_configs/deploy_invoke_numeric_inputs.toml ================================================ [[call]] call_type = "deploy" class_hash = "0x02a09379665a749e609b4a8459c86fe954566a6beeaddd0950e43f6c700ed321" inputs = [] id = "map_contract" unique = false [[call]] call_type = "invoke" contract_address = "0xcd8f9ab31324bb93251837e4efb4223ee195454f6304fcfcb277e277653008" function = "put" inputs = [0x123, 234] [[call]] call_type = "invoke" contract_address = "@map_contract" function = "put" inputs = [0x123, 9223372036854775807] ================================================ FILE: crates/sncast/tests/data/multicall_configs/deploy_invoke_numeric_overflow.toml ================================================ [[call]] call_type = "deploy" class_hash = "0x02a09379665a749e609b4a8459c86fe954566a6beeaddd0950e43f6c700ed321" inputs = [] id = "map_contract" unique = false [[call]] call_type = "invoke" contract_address = "0xcd8f9ab31324bb93251837e4efb4223ee195454f6304fcfcb277e277653008" function = "put" inputs = [0x123, 9223372036854775808] ================================================ FILE: crates/sncast/tests/data/multicall_configs/deploy_succ_invoke_fail.toml ================================================ [[call]] call_type = "deploy" class_hash = "0x7644be7f58307726aa836e945edede13e9e08c38eaf2186d4d48eca7dd435ac" inputs = [] id = "Map" unique = false [[call]] call_type = "invoke" contract_address = "0x1" function = "put" inputs = ["0x123", "234"] ================================================ FILE: crates/sncast/tests/data/multicall_configs/invoke_invalid.toml ================================================ [[call]] call_type = "invoke" contract_address = "0x1" function = "put" inputs = ["123", "234"] ================================================ FILE: crates/sncast/tests/data/multicall_configs/invoke_ledger.toml ================================================ [[call]] call_type = "invoke" contract_address = "0xcd8f9ab31324bb93251837e4efb4223ee195454f6304fcfcb277e277653008" function = "put" inputs = ["0x1", "0x2"] [[call]] call_type = "invoke" contract_address = "0xcd8f9ab31324bb93251837e4efb4223ee195454f6304fcfcb277e277653008" function = "put" inputs = ["0x3", "0x4"] ================================================ FILE: crates/sncast/tests/data/scripts/call/Scarb.toml ================================================ [package] name = "call_test_scripts" version = "0.1.0" edition = "2024_07" [dependencies] starknet = ">=2.3.0" sncast_std = { path = "../../../../../../sncast_std" } ================================================ FILE: crates/sncast/tests/data/scripts/call/src/invalid_address.cairo ================================================ use sncast_std::{ProviderError, ScriptCommandError, StarknetError, call}; fn main() { let eth = 0x049; let call_err: ScriptCommandError = call( eth.try_into().expect('bad address'), selector!("decimals"), array![], ) .unwrap_err(); println!("{:?}", call_err); assert( ScriptCommandError::ProviderError( ProviderError::StarknetError(StarknetError::ContractNotFound), ) == call_err, 'ohno', ) } ================================================ FILE: crates/sncast/tests/data/scripts/call/src/invalid_calldata.cairo ================================================ use sncast_std::{ScriptCommandError, call}; fn main() { let eth = 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7; let call_err: ScriptCommandError = call( eth.try_into().expect('bad address'), selector!("allowance"), array![0x12, 0x12, 0x12, 0x12, 0x12], ) .unwrap_err(); println!("{:?}", call_err); let call_err: ScriptCommandError = call( eth.try_into().expect('bad address'), selector!("allowance"), array![0x12], ) .unwrap_err(); println!("{:?}", call_err); } ================================================ FILE: crates/sncast/tests/data/scripts/call/src/invalid_entry_point.cairo ================================================ use sncast_std::{ScriptCommandError, call}; fn main() { let eth = 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7; let call_err: ScriptCommandError = call( eth.try_into().expect('bad address'), selector!("gimme_money"), array![], ) .unwrap_err(); println!("{:?}", call_err); } ================================================ FILE: crates/sncast/tests/data/scripts/call/src/lib.cairo ================================================ mod invalid_address; mod invalid_calldata; mod invalid_entry_point; ================================================ FILE: crates/sncast/tests/data/scripts/declare/Scarb.toml ================================================ [package] name = "declare_test_scripts" version = "0.1.0" edition = "2024_07" [dependencies] starknet = ">=2.3.0" sncast_std = { path = "../../../../../../sncast_std" } map1 = { path = "../map_script/contracts" } [lib] sierra = true [[target.starknet-contract]] build-external-contracts = ["map1::Mapa"] ================================================ FILE: crates/sncast/tests/data/scripts/declare/src/fee_settings.cairo ================================================ use sncast_std::{FeeSettingsTrait, declare, get_nonce}; fn main() { let fee_settings = FeeSettingsTrait::resource_bounds( 100000, 10000000000000, 1000000000, 100000000000000000000, 100000, 10000000000000, ); let declare_nonce = get_nonce('latest'); declare("Mapa", fee_settings, Option::Some(declare_nonce)).expect('declare failed'); println!("success"); } ================================================ FILE: crates/sncast/tests/data/scripts/declare/src/insufficient_account_balance.cairo ================================================ use sncast_std::{ FeeSettingsTrait, ProviderError, ScriptCommandError, StarknetError, declare, get_nonce, }; fn main() { let fee_settings = FeeSettingsTrait::resource_bounds( 9999999999999999999, 99999999999999999999999999999999999999, 9999999999999999999, 99999999999999999999999999999999999999, 9999999999999999999, 99999999999999999999999999999999999999, ); let declare_nonce = get_nonce('latest'); let declare_result = declare("Mapa", fee_settings, Option::Some(declare_nonce)).unwrap_err(); println!("{:?}", declare_result); assert( ScriptCommandError::ProviderError( ProviderError::StarknetError(StarknetError::InsufficientAccountBalance), ) == declare_result, 'ohno', ) } ================================================ FILE: crates/sncast/tests/data/scripts/declare/src/lib.cairo ================================================ mod fee_settings; mod insufficient_account_balance; mod no_contract; mod same_contract_twice; mod time_out; mod with_invalid_max_fee; mod with_invalid_nonce; ================================================ FILE: crates/sncast/tests/data/scripts/declare/src/no_contract.cairo ================================================ use sncast_std::{FeeSettingsTrait, declare}; fn main() { let fee_settings = FeeSettingsTrait::estimate(); let declare_result = declare("Mapaaaa", fee_settings, Option::None).unwrap_err(); println!("{:?}", declare_result); } ================================================ FILE: crates/sncast/tests/data/scripts/declare/src/same_contract_twice.cairo ================================================ use sncast_std::{DeclareResult, DeclareResultTrait, FeeSettingsTrait, declare, get_nonce}; fn main() { let fee_settings = FeeSettingsTrait::resource_bounds( 100000, 10000000000000, 1000000000, 100000000000000000000, 100000, 10000000000000, ); let declare_nonce = get_nonce('latest'); let first_declare_result = declare("Mapa", fee_settings, Option::Some(declare_nonce)) .expect('declare failed'); println!("success"); // Check if contract was declared successfully let class_hash = match first_declare_result { DeclareResult::Success(declare_transaction_result) => declare_transaction_result.class_hash, DeclareResult::AlreadyDeclared(_) => panic!("Should not be already declared"), }; // Check declare result trait is implemented correctly for Success assert(*first_declare_result.class_hash() == class_hash, 'Class hashes must be equal'); let declare_nonce = get_nonce('latest'); let second_declare_result = declare("Mapa", fee_settings, Option::Some(declare_nonce)) .expect('second declare failed'); // Check if already declared contract was handled correctly match second_declare_result { DeclareResult::Success(_) => panic!("Should be already declared"), DeclareResult::AlreadyDeclared(already_declared_result) => assert!( already_declared_result.class_hash == class_hash, ), } // Check declare result trait is implemented correctly for AlreadyDeclared assert(*second_declare_result.class_hash() == class_hash, 'Class hashes must be equal'); println!("{:?}", first_declare_result); println!("{:?}", second_declare_result); } ================================================ FILE: crates/sncast/tests/data/scripts/declare/src/time_out.cairo ================================================ use sncast_std::{FeeSettingsTrait, declare, get_nonce}; fn main() { let fee_settings = FeeSettingsTrait::resource_bounds( 100000, 10000000000000, 1000000000, 100000000000000000000, 100000, 10000000000000, ); let declare_nonce = get_nonce('latest'); let declare_result = declare("Mapa", fee_settings, Option::Some(declare_nonce)).unwrap_err(); println!("{:?}", declare_result); } ================================================ FILE: crates/sncast/tests/data/scripts/declare/src/with_invalid_max_fee.cairo ================================================ use sncast_std::{ FeeSettingsTrait, ProviderError, ScriptCommandError, StarknetError, declare, get_nonce, }; fn main() { let fee_settings = FeeSettingsTrait::resource_bounds(1, 1, 1, 1, 1, 1); let declare_nonce = get_nonce('latest'); let declare_result = declare("Mapa", fee_settings, Option::Some(declare_nonce)).unwrap_err(); println!("{:?}", declare_result); assert( ScriptCommandError::ProviderError( ProviderError::StarknetError(StarknetError::InsufficientResourcesForValidate), ) == declare_result, 'ohno', ) } ================================================ FILE: crates/sncast/tests/data/scripts/declare/src/with_invalid_nonce.cairo ================================================ use sncast_std::{ FeeSettingsTrait, ProviderError, ScriptCommandError, StarknetError, declare, get_nonce, }; fn main() { let fee_settings = FeeSettingsTrait::resource_bounds( 100000, 10000000000000, 1000000000, 100000000000000000000, 100000, 10000000000000, ); let declare_nonce = get_nonce('pre_confirmed') + 100; let declare_result = declare("Mapa", fee_settings, Option::Some(declare_nonce)).unwrap_err(); println!("{:?}", declare_result); assert( ScriptCommandError::ProviderError( ProviderError::StarknetError(StarknetError::InvalidTransactionNonce), ) == declare_result, 'ohno', ) } ================================================ FILE: crates/sncast/tests/data/scripts/deploy/Scarb.toml ================================================ [package] name = "deploy_test_scripts" version = "0.1.0" edition = "2024_07" [dependencies] starknet = ">=2.4.0" sncast_std = { path = "../../../../../../sncast_std" } ================================================ FILE: crates/sncast/tests/data/scripts/deploy/src/fee_settings.cairo ================================================ use sncast_std::{FeeSettingsTrait, deploy}; use starknet::ClassHash; fn main() { let fee_settings = FeeSettingsTrait::resource_bounds( 100000, 10000000000000, 1000000000, 100000000000000000000, 100000, 10000000000000, ); let salt = 0x3; let class_hash: ClassHash = 0x059426c817fb8103edebdbf1712fa084c6744b2829db9c62d1ea4dce14ee6ded .try_into() .expect('Invalid class hash value'); let deploy_result = deploy( class_hash, array![0x2, 0x2, 0x0], Option::Some(salt), true, fee_settings, Option::None, ) .expect('deploy failed'); assert(deploy_result.transaction_hash != 0, deploy_result.transaction_hash); } ================================================ FILE: crates/sncast/tests/data/scripts/deploy/src/invalid_calldata.cairo ================================================ use sncast_std::{FeeSettingsTrait, deploy, get_nonce}; use starknet::ClassHash; fn main() { let fee_settings = FeeSettingsTrait::resource_bounds( 100000, 10000000000000, 1000000000, 100000000000000000000, 100000, 10000000000000, ); let salt = 0x3; let class_hash: ClassHash = 0x059426c817fb8103edebdbf1712fa084c6744b2829db9c62d1ea4dce14ee6ded .try_into() .expect('Invalid class hash value'); let deploy_nonce = get_nonce('pre_confirmed'); let deploy_result = deploy( class_hash, array![0x2], Option::Some(salt), true, fee_settings, Option::Some(deploy_nonce), ) .unwrap_err(); println!("{:?}", deploy_result); } ================================================ FILE: crates/sncast/tests/data/scripts/deploy/src/invalid_class_hash.cairo ================================================ use sncast_std::{FeeSettingsTrait, deploy, get_nonce}; use starknet::ClassHash; fn main() { let fee_settings = FeeSettingsTrait::resource_bounds( 100000, 10000000000000, 1000000000, 100000000000000000000, 100000, 10000000000000, ); let salt = 0x3; let class_hash: ClassHash = 0xdddd.try_into().expect('Invalid class hash value'); let deploy_nonce = get_nonce('pre_confirmed'); let deploy_result = deploy( class_hash, array![0x2, 0x2, 0x0], Option::Some(salt), true, fee_settings, Option::Some(deploy_nonce), ) .unwrap_err(); println!("{:?}", deploy_result); } ================================================ FILE: crates/sncast/tests/data/scripts/deploy/src/invalid_nonce.cairo ================================================ use sncast_std::{ FeeSettingsTrait, ProviderError, ScriptCommandError, StarknetError, deploy, get_nonce, }; use starknet::ClassHash; fn main() { let fee_settings = FeeSettingsTrait::resource_bounds( 100000, 10000000000000, 1000000000, 100000000000000000000, 100000, 10000000000000, ); let salt = 0x3; let class_hash: ClassHash = 0x059426c817fb8103edebdbf1712fa084c6744b2829db9c62d1ea4dce14ee6ded .try_into() .expect('Invalid class hash value'); let deploy_nonce = get_nonce('pre_confirmed') + 100; let deploy_result = deploy( class_hash, array![0x2, 0x2, 0x0], Option::Some(salt), true, fee_settings, Option::Some(deploy_nonce), ) .unwrap_err(); println!("{:?}", deploy_result); assert( ScriptCommandError::ProviderError( ProviderError::StarknetError(StarknetError::InvalidTransactionNonce), ) == deploy_result, 'ohno', ) } ================================================ FILE: crates/sncast/tests/data/scripts/deploy/src/lib.cairo ================================================ mod fee_settings; mod invalid_calldata; mod invalid_class_hash; mod invalid_nonce; mod same_class_hash_and_salt; mod with_calldata; ================================================ FILE: crates/sncast/tests/data/scripts/deploy/src/same_class_hash_and_salt.cairo ================================================ use sncast_std::{FeeSettingsTrait, deploy, get_nonce}; use starknet::ClassHash; fn main() { let fee_settings = FeeSettingsTrait::resource_bounds( 100000, 10000000000000, 1000000000, 100000000000000000000, 100000, 10000000000000, ); let salt = 0x34542; let class_hash: ClassHash = 0x059426c817fb8103edebdbf1712fa084c6744b2829db9c62d1ea4dce14ee6ded .try_into() .expect('Invalid class hash value'); let deploy_nonce = get_nonce('pre_confirmed'); deploy( class_hash, array![0x2, 0x2, 0x0], Option::Some(salt), true, fee_settings, Option::Some(deploy_nonce), ) .expect('1st deploy failed'); let class_hash: ClassHash = 0x059426c817fb8103edebdbf1712fa084c6744b2829db9c62d1ea4dce14ee6ded .try_into() .expect('Invalid class hash value'); let deploy_nonce = get_nonce('pre_confirmed'); let deploy_result = deploy( class_hash, array![0x2, 0x2, 0x0], Option::Some(salt), true, fee_settings, Option::Some(deploy_nonce), ) .unwrap_err(); println!("{:?}", deploy_result); } ================================================ FILE: crates/sncast/tests/data/scripts/deploy/src/with_calldata.cairo ================================================ use sncast_std::{FeeSettingsTrait, deploy}; use starknet::ClassHash; fn main() { let fee_settings = FeeSettingsTrait::resource_bounds( 100000, 10000000000000, 1000000000, 100000000000000000000, 100000, 10000000000000, ); let salt = 0x3; let class_hash: ClassHash = 0x059426c817fb8103edebdbf1712fa084c6744b2829db9c62d1ea4dce14ee6ded .try_into() .expect('Invalid class hash value'); let deploy_result = deploy( class_hash, array![0x2, 0x2, 0x0], Option::Some(salt), true, fee_settings, Option::None, ) .expect('deploy failed'); assert(deploy_result.transaction_hash != 0, deploy_result.transaction_hash); } ================================================ FILE: crates/sncast/tests/data/scripts/invoke/Scarb.toml ================================================ [package] name = "invoke_script" version = "0.1.0" edition = "2024_07" [dependencies] starknet = ">=2.3.0" sncast_std = { path = "../../../../../../sncast_std" } ================================================ FILE: crates/sncast/tests/data/scripts/invoke/src/contract_does_not_exist.cairo ================================================ use sncast_std::{ FeeSettingsTrait, InvokeResult, ProviderError, ScriptCommandError, StarknetError, invoke, }; fn main() { let map_contract_address = 0x123.try_into().expect('Invalid contract address value'); let fee_settings = FeeSettingsTrait::resource_bounds( 100000, 10000000000000, 1000000000, 100000000000000000000, 100000, 10000000000000, ); let invoke_result = invoke( map_contract_address, selector!("put"), array![0x10, 0x1], fee_settings, Option::None, ) .unwrap_err(); println!("{:?}", invoke_result); } ================================================ FILE: crates/sncast/tests/data/scripts/invoke/src/lib.cairo ================================================ mod contract_does_not_exist; mod max_fee_too_low; mod wrong_calldata; mod wrong_function_name; ================================================ FILE: crates/sncast/tests/data/scripts/invoke/src/max_fee_too_low.cairo ================================================ use sncast_std::{ FeeSettingsTrait, InvokeResult, ProviderError, ScriptCommandError, StarknetError, invoke, }; fn main() { let map_contract_address = 0x07537a17e169c96cf2b0392508b3a66cbc50c9a811a8a7896529004c5e93fdf6 .try_into() .expect('Invalid contract address value'); let fee_settings = FeeSettingsTrait::resource_bounds(1, 1, 1, 1, 1, 1); let invoke_result = invoke( map_contract_address, selector!("put"), array![0x10, 0x1], fee_settings, Option::None, ) .unwrap_err(); println!("{:?}", invoke_result); } ================================================ FILE: crates/sncast/tests/data/scripts/invoke/src/wrong_calldata.cairo ================================================ use sncast_std::{ FeeSettingsTrait, InvokeResult, ProviderError, ScriptCommandError, StarknetError, invoke, }; fn main() { let map_contract_address = 0xcd8f9ab31324bb93251837e4efb4223ee195454f6304fcfcb277e277653008 .try_into() .expect('Invalid contract address value'); let fee_settings = FeeSettingsTrait::estimate(); let invoke_result = invoke( map_contract_address, selector!("put"), array![0x10], fee_settings, Option::None, ) .unwrap_err(); println!("{:?}", invoke_result); } ================================================ FILE: crates/sncast/tests/data/scripts/invoke/src/wrong_function_name.cairo ================================================ use sncast_std::{ FeeSettingsTrait, InvokeResult, ProviderError, ScriptCommandError, StarknetError, invoke, }; fn main() { let map_contract_address = 0xcd8f9ab31324bb93251837e4efb4223ee195454f6304fcfcb277e277653008 .try_into() .expect('Invalid contract address value'); let fee_settings = FeeSettingsTrait::estimate(); let invoke_result = invoke( map_contract_address, selector!("mariusz"), array![0x10, 0x1], fee_settings, Option::None, ) .unwrap_err(); println!("{:?}", invoke_result); } ================================================ FILE: crates/sncast/tests/data/scripts/map_script/contracts/Scarb.toml ================================================ [package] name = "map1" version = "0.2.0" edition = "2023_11" [dependencies] starknet = ">=2.4.0" [[target.starknet-contract]] [lib] sierra = false ================================================ FILE: crates/sncast/tests/data/scripts/map_script/contracts/src/lib.cairo ================================================ #[starknet::interface] trait IMap { fn put(ref self: TMapState, key: felt252, value: felt252); fn get(self: @TMapState, key: felt252) -> felt252; fn dummy(self: @TMapState) -> felt252; } #[starknet::contract] mod Mapa { use starknet::storage::{Map, StorageMapReadAccess, StoragePathEntry, StoragePointerWriteAccess}; #[storage] struct Storage { storage: Map, } #[abi(embed_v0)] impl MapaImpl of super::IMap { fn put(ref self: ContractState, key: felt252, value: felt252) { self.storage.entry(key).write(value); } fn get(self: @ContractState, key: felt252) -> felt252 { self.storage.read(key) } fn dummy(self: @ContractState) -> felt252 { 1 } } } #[starknet::contract] mod Mapa2 { use starknet::storage::{Map, StorageMapReadAccess, StoragePathEntry, StoragePointerWriteAccess}; #[storage] struct Storage { storage: Map, } #[abi(embed_v0)] impl Mapa2Impl of super::IMap { fn put(ref self: ContractState, key: felt252, value: felt252) { self.storage.entry(key).write(value); } fn get(self: @ContractState, key: felt252) -> felt252 { self.storage.read(key) } fn dummy(self: @ContractState) -> felt252 { 1 } } } ================================================ FILE: crates/sncast/tests/data/scripts/map_script/scripts/Scarb.toml ================================================ [package] name = "map_script" version = "0.1.0" edition = "2023_11" [dependencies] starknet = ">=2.3.0" sncast_std = { path = "../../../../../../../sncast_std" } map1 = { path = "../contracts" } [lib] sierra = true [[target.starknet-contract]] build-external-contracts = ["map1::Mapa", "map1::Mapa2"] ================================================ FILE: crates/sncast/tests/data/scripts/map_script/scripts/src/display_debug_traits_for_subcommand_responses.cairo ================================================ use sncast_std::{DeclareResultTrait, FeeSettingsTrait, call, declare, deploy, get_nonce, invoke}; fn main() { println!("test"); let salt = 0x3; let declare_nonce = get_nonce('latest'); println!("declare_nonce: {}", declare_nonce); println!("debug declare_nonce: {:?}", declare_nonce); let fee_settings = FeeSettingsTrait::resource_bounds( 100000, 10000000000000, 1000000000, 100000000000000000000, 100000, 10000000000000, ); let declare_result = declare("Mapa", fee_settings, Option::Some(declare_nonce)) .expect('declare failed'); println!("declare_result: {}", declare_result); println!("debug declare_result: {:?}", declare_result); let class_hash = declare_result.class_hash(); let deploy_nonce = get_nonce('pre_confirmed'); let deploy_result = deploy( *class_hash, ArrayTrait::new(), Option::Some(salt), true, fee_settings, Option::Some(deploy_nonce), ) .expect('deploy failed'); println!("deploy_result: {}", deploy_result); println!("debug deploy_result: {:?}", deploy_result); assert(deploy_result.transaction_hash != 0, deploy_result.transaction_hash); let invoke_nonce = get_nonce('pre_confirmed'); let invoke_result = invoke( deploy_result.contract_address, selector!("put"), array![0x1, 0x2], fee_settings, Option::Some(invoke_nonce), ) .expect('invoke failed'); println!("invoke_result: {}", invoke_result); println!("debug invoke_result: {:?}", invoke_result); assert(invoke_result.transaction_hash != 0, invoke_result.transaction_hash); let call_result = call(deploy_result.contract_address, selector!("get"), array![0x1]) .expect('call failed'); println!("call_result: {}", call_result); println!("debug call_result: {:?}", call_result); assert(call_result.data == array![0x2], *call_result.data.at(0)); } ================================================ FILE: crates/sncast/tests/data/scripts/map_script/scripts/src/lib.cairo ================================================ mod display_debug_traits_for_subcommand_responses; mod map_script; ================================================ FILE: crates/sncast/tests/data/scripts/map_script/scripts/src/map_script.cairo ================================================ use sncast_std::{DeclareResultTrait, FeeSettingsTrait, call, declare, deploy, get_nonce, invoke}; fn second_contract() { let fee_settings = FeeSettingsTrait::resource_bounds( 100000, 10000000000000, 1000000000, 100000000000000000000, 100000, 10000000000000, ); let declare_result = declare("Mapa2", fee_settings, Option::None) .expect('mapa2 declare failed'); let deploy_result = deploy( *declare_result.class_hash(), ArrayTrait::new(), Option::None, false, fee_settings, Option::None, ) .expect('mapa deploy failed'); assert(deploy_result.transaction_hash != 0, deploy_result.transaction_hash); let invoke_result = invoke( deploy_result.contract_address, selector!("put"), array![0x1, 0x3], fee_settings, Option::None, ) .expect('mapa2 invoke failed'); assert(invoke_result.transaction_hash != 0, invoke_result.transaction_hash); let call_result = call(deploy_result.contract_address, selector!("get"), array![0x1]) .expect('mapa2 call failed'); assert(call_result.data == array![0x3], *call_result.data.at(0)); } fn main() { let fee_settings = FeeSettingsTrait::resource_bounds( 100000, 10000000000000, 1000000000, 100000000000000000000, 100000, 10000000000000, ); let salt = 0x3; let declare_nonce = get_nonce('latest'); let declare_result = declare("Mapa", fee_settings, Option::Some(declare_nonce)) .expect('mapa declare failed'); let class_hash = declare_result.class_hash(); let deploy_nonce = get_nonce('pre_confirmed'); let deploy_result = deploy( *class_hash, ArrayTrait::new(), Option::Some(salt), true, fee_settings, Option::Some(deploy_nonce), ) .expect('mapa deploy failed'); assert(deploy_result.transaction_hash != 0, deploy_result.transaction_hash); let invoke_nonce = get_nonce('pre_confirmed'); let invoke_result = invoke( deploy_result.contract_address, selector!("put"), array![0x1, 0x2], fee_settings, Option::Some(invoke_nonce), ) .expect('mapa invoke failed'); assert(invoke_result.transaction_hash != 0, invoke_result.transaction_hash); let call_result = call(deploy_result.contract_address, selector!("get"), array![0x1]) .expect('mapa call failed'); assert(call_result.data == array![0x2], *call_result.data.at(0)); second_contract(); } ================================================ FILE: crates/sncast/tests/data/scripts/misc/Scarb.toml ================================================ [package] name = "misc_script" version = "0.1.0" edition = "2024_07" [dependencies] starknet = ">=2.3.0" sncast_std = { path = "../../../../../../sncast_std" } ================================================ FILE: crates/sncast/tests/data/scripts/misc/src/call_fail.cairo ================================================ use sncast_std::call; fn main() { let strk = 0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d; let call_result = call(strk.try_into().unwrap(), selector!("gimme_money"), array![]); call_result.expect('call failed'); } ================================================ FILE: crates/sncast/tests/data/scripts/misc/src/call_happy.cairo ================================================ use sncast_std::call; fn main() { let eth = 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7; let addr = 0x0089496091c660345BaA480dF76c1A900e57cf34759A899eFd1EADb362b20DB5; let call_result = call(eth.try_into().unwrap(), selector!("allowance"), array![addr, addr]) .unwrap(); let call_result = *call_result.data[0]; assert(call_result == 0, call_result); let call_result = call(eth.try_into().unwrap(), selector!("decimals"), array![]).unwrap(); let call_result = *call_result.data[0]; assert(call_result == 18, call_result); } ================================================ FILE: crates/sncast/tests/data/scripts/misc/src/lib.cairo ================================================ mod call_fail; mod call_happy; mod using_starknet_syscall; ================================================ FILE: crates/sncast/tests/data/scripts/misc/src/using_starknet_syscall.cairo ================================================ use core::box::BoxTrait; use starknet::get_execution_info; fn main() { let exec_info = get_execution_info().unbox(); assert(1 == 2, 'unreachable'); } ================================================ FILE: crates/sncast/tests/data/scripts/missing_field/Scarb.toml ================================================ [package] name = "missing_field_script" version = "0.1.0" edition = "2023_11" [dependencies] starknet = ">=2.3.0" sncast_std = { path = "../../../../../../sncast_std" } ================================================ FILE: crates/sncast/tests/data/scripts/missing_field/src/lib.cairo ================================================ mod missing_field; ================================================ FILE: crates/sncast/tests/data/scripts/missing_field/src/missing_field.cairo ================================================ use sncast_std::declare; fn main() { let declare_result = declare("Mapa", max_fee: Option::None); } ================================================ FILE: crates/sncast/tests/data/scripts/old_sncast_std/scripts/Scarb.toml ================================================ [package] name = "old_sncast_std" version = "0.1.0" edition = "2023_11" [dependencies] starknet = ">=2.3.0" sncast_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.13.1" } [lib] sierra = true [[target.starknet-contract]] ================================================ FILE: crates/sncast/tests/data/scripts/old_sncast_std/scripts/src/lib.cairo ================================================ mod map_script; ================================================ FILE: crates/sncast/tests/data/scripts/old_sncast_std/scripts/src/map_script.cairo ================================================ fn main() {} ================================================ FILE: crates/sncast/tests/data/scripts/packages/Scarb.toml ================================================ [workspace] members = [ "crates/scripts/*", ] [workspace.package] version = "0.1.0" [workspace.dependencies] starknet = "2.4.0" ================================================ FILE: crates/sncast/tests/data/scripts/packages/crates/scripts/script1/Scarb.toml ================================================ [package] name = "script1" version = "0.1.0" edition = "2024_07" [dependencies] starknet = ">=2.3.0" sncast_std = { path = "../../../../../../../../../sncast_std" } ================================================ FILE: crates/sncast/tests/data/scripts/packages/crates/scripts/script1/src/lib.cairo ================================================ use sncast_std::get_nonce; fn main() { get_nonce('latest'); } ================================================ FILE: crates/sncast/tests/data/scripts/packages/crates/scripts/script2/Scarb.toml ================================================ [package] name = "script2" version = "0.1.0" edition = "2024_07" [dependencies] starknet = ">=2.3.0" sncast_std = { path = "../../../../../../../../../sncast_std" } ================================================ FILE: crates/sncast/tests/data/scripts/packages/crates/scripts/script2/src/lib.cairo ================================================ use sncast_std::declare; fn main() { declare("whatever", Option::None, Option::None); } ================================================ FILE: crates/sncast/tests/data/scripts/state_file/Scarb.toml ================================================ [package] name = "state_file_test_script" version = "0.1.0" edition = "2023_11" [dependencies] starknet = ">=2.3.0" sncast_std = { path = "../../../../../../sncast_std" } ================================================ FILE: crates/sncast/tests/data/scripts/state_file/rerun_failed_tx_alpha-sepolia_state.json ================================================ { "version": 1, "transactions": { "transactions": { "31829eae07da513c7e6f457b9ac48af0004512db23efeae38734af97834bb273": { "name": "invoke", "output": { "type": "ErrorResponse", "message": "An error occurred in the called contract = ContractErrorData { revert_error: \"Error in the called contract (0x00f6ecd22832b7c3713cfa7826ee309ce96a2769833f093795fafa1b8f20c48b):\\nError at pc=0:4835:\\nGot an exception while executing a hint: Requested contract address ContractAddress(PatriciaKey(StarkFelt(\\\"0x00cd8f9ab31324bb93251837e4efb4223ee195454f6304fcfcb277e277653008\\\"))) is not deployed.\\nCairo traceback (most recent call last):\\nUnknown location (pc=0:67)\\nUnknown location (pc=0:1835)\\nUnknown location (pc=0:2554)\\nUnknown location (pc=0:3436)\\nUnknown location (pc=0:4040)\\n\" }" }, "status": "Error", "timestamp": 1712758668, "misc": null } } } } ================================================ FILE: crates/sncast/tests/data/scripts/state_file/src/all_tx_fail.cairo ================================================ use sncast_std::{FeeSettingsTrait, declare, deploy, get_nonce, invoke}; use starknet::{ClassHash, ContractAddress}; fn main() { let fee_settings = FeeSettingsTrait::resource_bounds( 100000, 10000000000000, 1000000000, 100000000000000000000, 100000, 10000000000000, ); let salt = 0x3; let nonexistent_class_hash: ClassHash = 0x10101.try_into().expect('Invalid class hash value'); let map_contract_address: ContractAddress = 0x2020202 .try_into() .expect('Invalid contract address value'); let declare_nonce = get_nonce('latest'); declare("Not_this_time", fee_settings, Option::Some(declare_nonce)) .expect_err('error expected declare'); let deploy_nonce = get_nonce('pre_confirmed'); deploy( nonexistent_class_hash, ArrayTrait::new(), Option::Some(salt), true, fee_settings, Option::Some(deploy_nonce), ) .expect_err('error expected deploy'); let invoke_nonce = get_nonce('pre_confirmed'); invoke( map_contract_address, selector!("put"), array![0x1, 0x2], fee_settings, Option::Some(invoke_nonce), ) .expect_err('error expected invoke'); } ================================================ FILE: crates/sncast/tests/data/scripts/state_file/src/lib.cairo ================================================ mod all_tx_fail; mod rerun_failed_tx; ================================================ FILE: crates/sncast/tests/data/scripts/state_file/src/rerun_failed_tx.cairo ================================================ use sncast_std::{FeeSettingsTrait, invoke}; fn main() { let fee_settings = FeeSettingsTrait::resource_bounds( 100000, 10000000000000, 1000000000, 100000000000000000000, 100000, 10000000000000, ); let map_contract_address = 0xcd8f9ab31324bb93251837e4efb4223ee195454f6304fcfcb277e277653008 .try_into() .expect('Invalid contract address value'); invoke(map_contract_address, selector!("put"), array![0x10, 0x1], fee_settings, Option::None) .unwrap(); } ================================================ FILE: crates/sncast/tests/data/scripts/state_script/contracts/Scarb.toml ================================================ [package] name = "state" version = "0.2.0" edition = "2023_11" [dependencies] starknet = ">=2.4.0" [[target.starknet-contract]] [lib] sierra = false ================================================ FILE: crates/sncast/tests/data/scripts/state_script/contracts/src/lib.cairo ================================================ #[starknet::interface] trait IState { fn put(ref self: TState, key: felt252, value: felt252); fn get(self: @TState, key: felt252) -> felt252; fn dummy(self: @TState) -> felt252; } #[starknet::contract] mod State { use starknet::storage::{Map, StorageMapReadAccess, StoragePathEntry, StoragePointerWriteAccess}; #[storage] struct Storage { storage: Map, } #[abi(embed_v0)] impl State of super::IState { fn put(ref self: ContractState, key: felt252, value: felt252) { self.storage.entry(key).write(value); } fn get(self: @ContractState, key: felt252) -> felt252 { self.storage.read(key) } fn dummy(self: @ContractState) -> felt252 { 1 } } } ================================================ FILE: crates/sncast/tests/data/scripts/state_script/scripts/Scarb.toml ================================================ [package] name = "state_script" version = "0.1.0" edition = "2024_07" [dependencies] starknet = ">=2.4.0" sncast_std = { path = "../../../../../../../sncast_std" } state = { path = "../contracts" } [lib] sierra = true [[target.starknet-contract]] build-external-contracts = [ "state::State" ] ================================================ FILE: crates/sncast/tests/data/scripts/state_script/scripts/src/lib.cairo ================================================ mod state_script; ================================================ FILE: crates/sncast/tests/data/scripts/state_script/scripts/src/state_script.cairo ================================================ use sncast_std::{DeclareResultTrait, FeeSettingsTrait, call, declare, deploy, get_nonce, invoke}; fn main() { let fee_settings = FeeSettingsTrait::resource_bounds( 100000, 10000000000000, 1000000000, 100000000000000000000, 100000, 10000000000000, ); let salt = 0x5; let declare_nonce = get_nonce('latest'); let declare_result = declare("State", fee_settings, Option::Some(declare_nonce)) .expect('state declare failed'); let class_hash = declare_result.class_hash(); let deploy_nonce = get_nonce('pre_confirmed'); let deploy_result = deploy( *class_hash, ArrayTrait::new(), Option::Some(salt), true, fee_settings, Option::Some(deploy_nonce), ) .expect('state deploy failed'); assert(deploy_result.transaction_hash != 0, deploy_result.transaction_hash); let invoke_nonce = get_nonce('pre_confirmed'); let invoke_result = invoke( deploy_result.contract_address, selector!("put"), array![0x1, 0x2], fee_settings, Option::Some(invoke_nonce), ) .expect('state invoke failed'); assert(invoke_result.transaction_hash != 0, invoke_result.transaction_hash); let call_result = call(deploy_result.contract_address, selector!("get"), array![0x1]) .expect('state call failed'); assert(call_result.data == array![0x2], *call_result.data.at(0)); } ================================================ FILE: crates/sncast/tests/data/scripts/tx_status/Scarb.toml ================================================ [package] name = "tx_status_test_scripts" version = "0.1.0" edition = "2024_07" [dependencies] starknet = ">=2.4.0" sncast_std = { path = "../../../../../../sncast_std" } ================================================ FILE: crates/sncast/tests/data/scripts/tx_status/src/incorrect_transaction_hash.cairo ================================================ use sncast_std::{ProviderError, ScriptCommandError, StarknetError, tx_status}; fn main() { let incorrect_tx_hash = 0x1; let status = tx_status(incorrect_tx_hash).unwrap_err(); println!("{:?}", status); assert!( ScriptCommandError::ProviderError( ProviderError::StarknetError(StarknetError::TransactionHashNotFound(())), ) == status, ) } ================================================ FILE: crates/sncast/tests/data/scripts/tx_status/src/lib.cairo ================================================ mod incorrect_transaction_hash; mod status_reverted; mod status_succeeded; ================================================ FILE: crates/sncast/tests/data/scripts/tx_status/src/status_reverted.cairo ================================================ use sncast_std::{ExecutionStatus, FinalityStatus, TxStatusResult, tx_status}; fn main() { let reverted_tx_hash = 0x00ae35dacba17cde62b8ceb12e3b18f4ab6e103fa2d5e3d9821cb9dc59d59a3c; let status = tx_status(reverted_tx_hash).unwrap(); println!("{}", status); println!("{:?}", status); assert!( TxStatusResult { finality_status: FinalityStatus::AcceptedOnL1, execution_status: Option::Some(ExecutionStatus::Reverted), } == status, ) } ================================================ FILE: crates/sncast/tests/data/scripts/tx_status/src/status_succeeded.cairo ================================================ use sncast_std::{ExecutionStatus, FinalityStatus, TxStatusResult, tx_status}; fn main() { let succeeded_tx_hash = 0x07d2067cd7675f88493a9d773b456c8d941457ecc2f6201d2fe6b0607daadfd1; let status = tx_status(succeeded_tx_hash).unwrap(); println!("{}", status); println!("{:?}", status); assert!( TxStatusResult { finality_status: FinalityStatus::AcceptedOnL1, execution_status: Option::Some(ExecutionStatus::Succeeded), } == status, ) } ================================================ FILE: crates/sncast/tests/docs_snippets/ledger.rs ================================================ use crate::e2e::ledger::{automation, setup_speculos}; use crate::helpers::constants::URL; use crate::helpers::runner::runner; use docs::snippet::SnippetType; use docs::utils::{ get_nth_ancestor, print_ignored_snippet_message, print_snippets_validation_summary, }; use docs::validation::{extract_snippets_from_directory, extract_snippets_from_file}; use shared::test_utils::output_assert::assert_stdout_contains; use std::sync::Arc; use tempfile::TempDir; const DOCS_SNIPPETS_PORT_BASE: u16 = 4006; async fn setup_speculos_automation(client: &Arc, args: &[&str]) { if args.contains(&"get-public-key") && !args.contains(&"--no-display") { client .automation(&[automation::APPROVE_PUBLIC_KEY]) .await .unwrap(); } else if args.contains(&"sign-hash") { client .automation(&[ automation::ENABLE_BLIND_SIGN, automation::APPROVE_BLIND_SIGN_HASH, ]) .await .unwrap(); } } #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_ledger_docs_snippets() { let root_dir_path = get_nth_ancestor(2); let ledger_appendix_dir = root_dir_path.join("docs/src/appendix/sncast/ledger"); let ledger_guide_path = root_dir_path.join("docs/src/starknet/ledger.md"); let snippet_type = SnippetType::sncast(); let appendix_snippets = extract_snippets_from_directory(&ledger_appendix_dir, &snippet_type) .expect("Failed to extract ledger appendix snippets"); let guide_snippets = extract_snippets_from_file(&ledger_guide_path, &snippet_type) .expect("Failed to extract ledger guide snippets"); let snippets: Vec<_> = appendix_snippets .into_iter() .chain(guide_snippets) .collect(); let tempdir = TempDir::new().expect("Unable to create a temporary directory"); std::fs::write(tempdir.path().join("accounts.json"), "{}").unwrap(); let target_accounts_json_path = tempdir.path().join("accounts.json"); // sign-hash snippets need a fresh Speculos instance each time: ENABLE_BLIND_SIGN fires // All other snippets share one instance to keep the total startup count low. let (shared_client, shared_url) = setup_speculos(DOCS_SNIPPETS_PORT_BASE); let mut sign_hash_port_offset = 1; for snippet in &snippets { if snippet.config.ignored { print_ignored_snippet_message(snippet); continue; } if !snippet.config.requires_ledger { continue; } let args = snippet.to_command_args(); let mut args: Vec<&str> = args.iter().map(String::as_str).collect(); // remove "sncast" from the args args.remove(0); args.insert(0, "--accounts-file"); args.insert(1, target_accounts_json_path.to_str().unwrap()); if snippet.config.replace_network { let network_pos = args.iter().position(|arg| *arg == "--network"); if let Some(network_pos) = network_pos { args[network_pos] = "--url"; args[network_pos + 1] = URL; } } let (snippet_client, snippet_url) = if args.contains(&"sign-hash") { let port = DOCS_SNIPPETS_PORT_BASE + sign_hash_port_offset; sign_hash_port_offset += 1; setup_speculos(port) } else { (shared_client.clone(), shared_url.clone()) }; setup_speculos_automation(&snippet_client, &args).await; let snapbox = runner(&args) .env("LEDGER_EMULATOR_URL", &snippet_url) .current_dir(tempdir.path()); let output = snapbox.assert().success(); snippet_client.automation(&[]).await.unwrap(); if let Some(expected_stdout) = &snippet.output && !snippet.config.ignored_output { assert_stdout_contains(output, expected_stdout); } } let ledger_snippets: Vec<_> = snippets .into_iter() .filter(|s| s.config.requires_ledger) .collect(); print_snippets_validation_summary(&ledger_snippets, snippet_type.as_str()); } ================================================ FILE: crates/sncast/tests/docs_snippets/mod.rs ================================================ pub mod ledger; pub mod validation; ================================================ FILE: crates/sncast/tests/docs_snippets/validation.rs ================================================ use std::fs; use crate::helpers::constants::URL; use crate::helpers::runner::runner; use camino::Utf8PathBuf; use docs::snippet::{Snippet, SnippetType}; use docs::utils::{ get_nth_ancestor, print_ignored_snippet_message, print_snippets_validation_summary, update_scarb_toml_dependencies, }; use docs::validation::{extract_snippets_from_directory, extract_snippets_from_file}; use shared::test_utils::output_assert::assert_stdout_contains; use tempfile::TempDir; #[test] fn test_docs_snippets() { let root_dir_path = get_nth_ancestor(2); let docs_dir_path = root_dir_path.join("docs/src"); let sncast_readme_path = root_dir_path.join("crates/sncast/README.md"); let snippet_type = SnippetType::sncast(); let docs_snippets = extract_snippets_from_directory(&docs_dir_path, &snippet_type) .expect("Failed to extract command snippets"); let readme_snippets = extract_snippets_from_file(&sncast_readme_path, &snippet_type) .expect("Failed to extract command snippets"); let snippets = docs_snippets .into_iter() .chain(readme_snippets) .collect::>(); let hello_sncast_dir = Utf8PathBuf::from_path_buf(root_dir_path.join("docs/listings/hello_sncast")) .expect("Invalid UTF-8 path"); let dirs_to_copy = [ "crates/sncast/tests/data/files", "docs/listings/hello_sncast", ]; let tempdir = TempDir::new().expect("Unable to create a temporary directory"); let target_path = tempdir.path(); for dir in &dirs_to_copy { let source_path = root_dir_path.join(dir); fs_extra::dir::copy( source_path.as_path(), target_path, &fs_extra::dir::CopyOptions::new() .overwrite(true) .content_only(true), ) .expect("Failed to copy the directory"); } let source_accounts_json_path = hello_sncast_dir.join("accounts.json"); let target_accounts_json_path = tempdir.path().join("accounts.json"); fs::copy(&source_accounts_json_path, &target_accounts_json_path) .expect("Failed to copy accounts.json"); update_scarb_toml_dependencies(&tempdir).unwrap(); for snippet in &snippets { if snippet.config.ignored || snippet.config.requires_ledger { print_ignored_snippet_message(snippet); continue; } let args = snippet.to_command_args(); let mut args: Vec<&str> = args.iter().map(String::as_str).collect(); // remove "sncast" from the args args.remove(0); args.insert(0, "--accounts-file"); args.insert(1, target_accounts_json_path.to_str().unwrap()); if snippet.config.replace_network { let network_pos = args.iter().position(|arg| *arg == "--network"); if let Some(network_pos) = network_pos { args[network_pos] = "--url"; args[network_pos + 1] = URL; } } let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); let output = snapbox.assert().success(); if let Some(expected_stdout) = &snippet.output && !snippet.config.ignored_output { assert_stdout_contains(output, expected_stdout); } } print_snippets_validation_summary(&snippets, snippet_type.as_str()); } ================================================ FILE: crates/sncast/tests/e2e/account/create.rs ================================================ use crate::helpers::constants::{ACCOUNT_FILE_PATH, DEVNET_OZ_CLASS_HASH_CAIRO_0, URL}; use crate::helpers::fixtures::copy_file; use crate::helpers::runner::runner; use configuration::test_utils::copy_config_to_tempdir; use indoc::{formatdoc, indoc}; use crate::helpers::env::set_create_keystore_password_env; use camino::Utf8PathBuf; use conversions::string::IntoHexStr; use serde_json::{json, to_string_pretty}; use shared::test_utils::output_assert::{ AsOutput, assert_stderr_contains, assert_stdout, assert_stdout_contains, }; use snapbox::assert_data_eq; use sncast::AccountType; use sncast::helpers::constants::{ BRAAVOS_BASE_ACCOUNT_CLASS_HASH, BRAAVOS_CLASS_HASH, OZ_CLASS_HASH, READY_CLASS_HASH, }; use std::fs; use tempfile::tempdir; use test_case::test_case; #[test_case("oz"; "oz_account_type")] #[test_case("ready"; "ready_account_type")] #[test_case("braavos"; "braavos_account_type")] #[tokio::test] pub async fn test_happy_case(account_type: &str) { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "create", "--url", URL, "--name", "my_account", "--salt", "0x1", "--type", account_type, ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r" Success: Account created Address: 0x0[..] Account successfully created but it needs to be deployed. The estimated deployment fee is [..] STRK. Prefund the account to cover deployment transaction fee After prefunding the account, run: sncast --accounts-file accounts.json account deploy --url http://127.0.0.1:5055/rpc --name my_account To see account creation details, visit: account: [..] "}, ); let contents = fs::read_to_string(temp_dir.path().join(accounts_file)) .expect("Unable to read created file"); let expected = json!( { "alpha-sepolia": { "my_account": { "address": "0x[..]", "class_hash": "0x[..]", "deployed": false, "legacy": false, "private_key": "0x[..]", "public_key": "0x[..]", "salt": "0x1", "type": get_formatted_account_type(account_type) } } } ); assert_data_eq!(contents, to_string_pretty(&expected).unwrap()); } // TODO(#3556): Remove this test once we drop Argent account type #[tokio::test] pub async fn test_happy_case_argent_with_deprecation_warning() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "create", "--url", URL, "--name", "my_account", "--salt", "0x1", "--type", "argent", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert_stdout( output, indoc! {r" [WARNING] Argent has rebranded as Ready. The `argent` option for the `--type` flag in `account create` is deprecated, please use `ready` instead. Success: Account created Address: 0x0[..] Account successfully created but it needs to be deployed. The estimated deployment fee is [..] STRK. Prefund the account to cover deployment transaction fee After prefunding the account, run: sncast --accounts-file accounts.json account deploy --url http://127.0.0.1:5055/rpc --name my_account To see account creation details, visit: account: [..] "}, ); let contents = fs::read_to_string(temp_dir.path().join(accounts_file)) .expect("Unable to read created file"); let expected = json!( { "alpha-sepolia": { "my_account": { "address": "0x[..]", "class_hash": "0x[..]", "deployed": false, "legacy": false, "private_key": "0x[..]", "public_key": "0x[..]", "salt": "0x1", "type": "ready" } } } ); assert_data_eq!(contents, to_string_pretty(&expected).unwrap()); } #[tokio::test] pub async fn test_invalid_class_hash() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "create", "--url", URL, "--type", "oz", "--class-hash", "0x10101", "--name", "my_account_create_happy", "--salt", "0x1", ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: account create Error: Class with hash 0x10101 is not declared, try using --class-hash with a hash of the declared class "}, ); } #[tokio::test] pub async fn test_happy_case_generate_salt() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "create", "--url", URL, "--name", "my_account", "--class-hash", DEVNET_OZ_CLASS_HASH_CAIRO_0, "--type", "oz", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(temp_dir.path()); snapbox.assert().success().stdout_eq(indoc! {r" Success: Account created Address: 0x0[..] Account successfully created but it needs to be deployed. The estimated deployment fee is [..] STRK. Prefund the account to cover deployment transaction fee After prefunding the account, run: sncast --accounts-file accounts.json account deploy --url http://127.0.0.1:5055/rpc --name my_account To see account creation details, visit: account: [..] "}); let contents = fs::read_to_string(temp_dir.path().join(accounts_file)) .expect("Unable to read created file"); assert!(contents.contains("my_account")); assert!(contents.contains("alpha-sepolia")); assert!(contents.contains("private_key")); assert!(contents.contains("public_key")); assert!(contents.contains("address")); assert!(contents.contains("salt")); assert!(contents.contains("class_hash")); assert!(contents.contains("legacy")); assert!(contents.contains("type")); } #[test_case("--url", URL; "with_url")] #[test_case("--network", "devnet"; "with_network")] #[tokio::test] pub async fn test_happy_case_add_profile(rpc_flag: &str, rpc_value: &str) { let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "create", rpc_flag, rpc_value, "--name", "my_account", "--add-profile", "my_account", ]; let output = runner(&args).current_dir(tempdir.path()).assert(); let config_path = Utf8PathBuf::from_path_buf(tempdir.path().join("snfoundry.toml")) .unwrap() .canonicalize_utf8() .unwrap(); assert_stdout_contains( output, format!("Add Profile: Profile my_account successfully added to {config_path}"), ); let contents = fs::read_to_string(tempdir.path().join("snfoundry.toml")) .expect("Unable to read snfoundry.toml"); let expected_lines = [ "[sncast.my_account]", "account = \"my_account\"", "accounts-file = \"accounts.json\"", &format!("{} = \"{}\"", rpc_flag.trim_start_matches("--"), rpc_value), ]; let expected_block = expected_lines.join("\n"); assert!(contents.contains(&expected_block)); } #[tokio::test] pub async fn test_happy_case_accounts_file_already_exists() { let accounts_file = "accounts.json"; let temp_dir = tempdir().expect("Unable to create a temporary directory"); copy_file( "tests/data/accounts/accounts.json", temp_dir.path().join(accounts_file), ); let args = vec![ "--accounts-file", accounts_file, "account", "create", "--url", URL, "--name", "my_account", "--salt", "0x1", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(temp_dir.path()); snapbox.assert().success().stdout_eq(indoc! {r" Success: Account created Address: 0x0[..] Account successfully created but it needs to be deployed. The estimated deployment fee is [..] STRK. Prefund the account to cover deployment transaction fee After prefunding the account, run: sncast --accounts-file accounts.json account deploy --url http://127.0.0.1:5055/rpc --name my_account To see account creation details, visit: account: [..] "}); let contents = fs::read_to_string(temp_dir.path().join(accounts_file)) .expect("Unable to read created file"); assert!(contents.contains("my_account")); assert!(contents.contains("deployed")); assert!(contents.contains("legacy")); } #[tokio::test] pub async fn test_profile_already_exists() { let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "create", "--url", URL, "--name", "myprofile", "--add-profile", "default", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert(); assert_stderr_contains( output, indoc! {r" Command: account create Error: Failed to add profile = default to the snfoundry.toml. Profile already exists "}, ); } #[tokio::test] pub async fn test_account_already_exists() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "account", "create", "--url", URL, "--name", "user1", "--salt", "0x1", ]; let snapbox = runner(&args); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: account create Error: Account with name = user1 already exists in network with chain_id = SN_SEPOLIA "}, ); } #[test_case("oz"; "oz_account_type")] #[test_case("ready"; "ready_account_type")] #[test_case("braavos"; "braavos_account_type")] #[tokio::test] pub async fn test_happy_case_keystore(account_type: &str) { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let keystore_file = "my_key.json"; let account_file = "my_account.json"; set_create_keystore_password_env(); let args = vec![ "--keystore", keystore_file, "--account", account_file, "account", "create", "--url", URL, "--type", account_type, ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(temp_dir.path()); snapbox.assert().stdout_eq(formatdoc! {r" Success: Account created Address: 0x0[..] Account successfully created but it needs to be deployed. The estimated deployment fee is [..] STRK. Prefund the account to cover deployment transaction fee After prefunding the account, run: sncast --account {} --keystore {} account deploy --url {} To see account creation details, visit: account: [..] ", account_file, keystore_file, URL}); assert!(temp_dir.path().join(keystore_file).exists()); let contents = fs::read_to_string(temp_dir.path().join(account_file)) .expect("Unable to read created file"); assert_data_eq!( contents, get_keystore_account_pattern(account_type.parse().unwrap(), None), ); } // TODO(#3556): Remove this test once we drop Argent account type #[tokio::test] pub async fn test_happy_case_keystore_argent_with_deprecation_warning() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let keystore_file = "my_key.json"; let account_file = "my_account.json"; set_create_keystore_password_env(); let args = vec![ "--keystore", keystore_file, "--account", account_file, "account", "create", "--url", URL, "--type", "argent", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(temp_dir.path()); snapbox.assert().stdout_eq(formatdoc! {r" [WARNING] Argent has rebranded as Ready. The `argent` option for the `--type` flag in `account create` is deprecated, please use `ready` instead. Success: Account created Address: 0x0[..] Account successfully created but it needs to be deployed. The estimated deployment fee is [..] STRK. Prefund the account to cover deployment transaction fee After prefunding the account, run: sncast --account {} --keystore {} account deploy --url {} To see account creation details, visit: account: [..] ", account_file, keystore_file, URL}); assert!(temp_dir.path().join(keystore_file).exists()); let contents = fs::read_to_string(temp_dir.path().join(account_file)) .expect("Unable to read created file"); assert_data_eq!( contents, get_keystore_account_pattern("argent".parse().unwrap(), None), ); } #[tokio::test] pub async fn test_happy_case_keystore_add_profile() { let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); let keystore_file = "my_key.json"; let account_file = "my_account.json"; let accounts_json_file = "accounts.json"; set_create_keystore_password_env(); let args = vec![ "--accounts-file", accounts_json_file, "--keystore", keystore_file, "--account", account_file, "account", "create", "--url", URL, "--add-profile", "with_keystore", ]; let output = runner(&args).current_dir(tempdir.path()).assert(); let config_path = Utf8PathBuf::from_path_buf(tempdir.path().join("snfoundry.toml")) .unwrap() .canonicalize_utf8() .unwrap(); assert_stdout_contains( output, format!("Add Profile: Profile with_keystore successfully added to {config_path}"), ); let contents = fs::read_to_string(tempdir.path().join(account_file)).expect("Unable to read created file"); assert!(contents.contains("\"deployment\": {")); assert!(contents.contains("\"variant\": {")); assert!(contents.contains("\"version\": 1")); assert!(contents.contains("\"legacy\": false")); let contents = fs::read_to_string(tempdir.path().join("snfoundry.toml")) .expect("Unable to read snfoundry.toml"); assert!(contents.contains(r"[sncast.with_keystore]")); assert!(contents.contains(r#"account = "my_account.json""#)); assert!(!contents.contains(r#"accounts-file = "accounts.json""#)); assert!(contents.contains(r#"keystore = "my_key.json""#)); assert!(contents.contains(r#"url = "http://127.0.0.1:5055/rpc""#)); } #[tokio::test] pub async fn test_keystore_without_account() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let keystore_file = "my_key.json"; set_create_keystore_password_env(); let args = vec![ "--keystore", keystore_file, "account", "create", "--url", URL, ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: account create Error: Argument `--account` must be passed and be a path when using `--keystore` "}, ); } #[tokio::test] pub async fn test_keystore_file_already_exists() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let keystore_file = "my_key.json"; let account_file = "my_account_new.json"; copy_file( "tests/data/keystore/my_key.json", temp_dir.path().join(keystore_file), ); set_create_keystore_password_env(); let args = vec![ "--keystore", keystore_file, "--account", account_file, "account", "create", "--url", URL, ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: account create Error: Keystore file my_key.json already exists "}, ); } #[tokio::test] pub async fn test_keystore_account_file_already_exists() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let keystore_file = "my_key_new.json"; let account_file = "my_account.json"; copy_file( "tests/data/keystore/my_account.json", temp_dir.path().join(account_file), ); set_create_keystore_password_env(); let args = vec![ "--keystore", keystore_file, "--account", account_file, "account", "create", "--url", URL, ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: account create Error: Account file my_account.json already exists "}, ); } #[tokio::test] pub async fn test_happy_case_keystore_int_format() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let keystore_file = "my_key_int.json"; let account_file = "my_account_int.json"; set_create_keystore_password_env(); let args = vec![ "--keystore", keystore_file, "--account", account_file, "account", "create", "--url", URL, "--class-hash", DEVNET_OZ_CLASS_HASH_CAIRO_0, "--type", "oz", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(temp_dir.path()); snapbox.assert().stdout_eq(formatdoc! {r" Success: Account created Address: [..] Account successfully created but it needs to be deployed. The estimated deployment fee is [..] STRK. Prefund the account to cover deployment transaction fee After prefunding the account, run: sncast --account {} --keystore {} account deploy --url {} To see account creation details, visit: account: [..] ", account_file, keystore_file, URL}); let contents = fs::read_to_string(temp_dir.path().join(account_file)) .expect("Unable to read created file"); assert!(contents.contains("\"deployment\": {")); assert!(contents.contains("\"variant\": {")); assert!(contents.contains("\"version\": 1")); assert!(contents.contains("\"legacy\": true")); } #[tokio::test] pub async fn test_happy_case_default_name_generation() { let tempdir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let create_args = vec![ "--accounts-file", accounts_file, "account", "create", "--url", URL, "--salt", "0x1", ]; let delete_args = vec![ "--accounts-file", &accounts_file, "account", "delete", "--name", "account-2", "--network", "sepolia", ]; let assert_account_created = |id: usize| { runner(&create_args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()) .assert() .stdout_eq(formatdoc! {r" Success: Account created Address: 0x0[..] Account successfully created but it needs to be deployed. The estimated deployment fee is [..] STRK. Prefund the account to cover deployment transaction fee After prefunding the account, run: sncast --accounts-file accounts.json account deploy --url http://127.0.0.1:5055/rpc --name account-{id} To see account creation details, visit: account: [..] ", id = id}); }; for i in 0..3 { assert_account_created(i + 1); } let contents = fs::read_to_string(tempdir.path().join(accounts_file)) .expect("Unable to read created file"); assert!( ["account-1", "account-2", "account-3"] .iter() .all(|a| contents.contains(a)) ); runner(&delete_args) .current_dir(tempdir.path()) .stdin("Y") .assert() .success() .stdout_eq(indoc! {r" Success: Account deleted Account successfully removed "}); let contents_after_delete = fs::read_to_string(tempdir.path().join(accounts_file)) .expect("Unable to read created file"); assert!(!contents_after_delete.contains("account-2")); assert_account_created(2); let contents = fs::read_to_string(tempdir.path().join(accounts_file)) .expect("Unable to read created file"); assert!(contents.contains("account-2")); let expected = json!( { "alpha-sepolia": { "account-1": { "address": "0x[..]", "class_hash": "0x[..]", "deployed": false, "legacy": false, "private_key": "0x[..]", "public_key": "0x[..]", "salt": "0x1", "type": "open_zeppelin" }, "account-2": { "address": "0x[..]", "class_hash": "0x[..]", "deployed": false, "legacy": false, "private_key": "0x[..]", "public_key": "0x[..]", "salt": "0x1", "type": "open_zeppelin" }, "account-3": { "address": "0x[..]", "class_hash": "0x[..]", "deployed": false, "legacy": false, "private_key": "0x[..]", "public_key": "0x[..]", "salt": "0x1", "type": "open_zeppelin" }, } } ); assert_data_eq!(contents, to_string_pretty(&expected).unwrap()); } fn get_formatted_account_type(account_type: &str) -> &str { match account_type { "oz" => "open_zeppelin", _ => account_type, } } fn get_keystore_account_pattern(account_type: AccountType, class_hash: Option<&str>) -> String { let account_json = match account_type { AccountType::OpenZeppelin => { json!( { "version": 1, "variant": { "type": "open_zeppelin", "version": 1, "public_key": "0x[..]", "legacy": false, }, "deployment": { "status": "undeployed", "class_hash": class_hash.unwrap_or(&OZ_CLASS_HASH.into_hex_string()), "salt": "0x[..]", } } ) } AccountType::Argent | AccountType::Ready => { json!( { "version": 1, "variant": { // TODO(#3556): Remove hardcoded "argent" and use format! with `AccountType::Ready` "type": "argent", "version": 1, "owner": "0x[..]", "guardian": "0x0" }, "deployment": { "status": "undeployed", "class_hash": class_hash.unwrap_or(&READY_CLASS_HASH.into_hex_string()), "salt": "0x[..]", } } ) } AccountType::Braavos => { json!( { "version": 1, "variant": { "type": "braavos", "version": 1, "multisig": { "status": "off" }, "signers": [ { "type": "stark", "public_key": "0x[..]" } ] }, "deployment": { "status": "undeployed", "class_hash": class_hash.unwrap_or(&BRAAVOS_CLASS_HASH.into_hex_string()), "salt": "0x[..]", "context": { "variant": "braavos", "base_account_class_hash": BRAAVOS_BASE_ACCOUNT_CLASS_HASH } } } ) } }; to_string_pretty(&account_json).unwrap() } #[tokio::test] pub async fn test_happy_case_deployment_fee_message() { let tempdir = tempdir().expect("Failed to create a temporary directory"); let args = vec!["account", "create", "--url", URL]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, "Account successfully created but it needs to be deployed. The estimated deployment fee is [..] STRK. Prefund the account to cover deployment transaction fee", ); } #[tokio::test] pub async fn test_happy_case_default_name_generation_when_accounts_file_empty() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "test_accounts.json"; let accounts_path = temp_dir.path().join(accounts_file); std::fs::File::create(&accounts_path).expect("Failed to create empty accounts file"); let args = vec![ "--accounts-file", accounts_file, "account", "create", "--url", URL, "--class-hash", DEVNET_OZ_CLASS_HASH_CAIRO_0, "--type", "oz", ]; let snapbox = runner(&args).current_dir(temp_dir.path()); snapbox.assert().success(); } #[tokio::test] pub async fn test_happy_case_accounts_file_empty() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "test_accounts.json"; let accounts_path = temp_dir.path().join(accounts_file); std::fs::File::create(&accounts_path).expect("Failed to create empty accounts file"); let args = vec![ "--accounts-file", accounts_file, "account", "create", "--url", URL, "--name", "my_account", "--class-hash", DEVNET_OZ_CLASS_HASH_CAIRO_0, "--type", "oz", ]; let snapbox = runner(&args).current_dir(temp_dir.path()); snapbox.assert().success(); } #[tokio::test] pub async fn test_json_output_format() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "--json", "account", "create", "--url", URL, "--name", "my_account", "--salt", "0x1", "--type", "oz", "--add-profile", "my_account", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(temp_dir.path()); snapbox.assert().stdout_eq(indoc! {r#" {"add_profile":"Profile my_account successfully added to [..]/snfoundry.toml","address":"0x[..]","command":"account create","estimated_fee":"[..]","message":"Account successfully created but it needs to be deployed. The estimated deployment fee is [..] STRK. Prefund the account to cover deployment transaction fee/n/nAfter prefunding the account, run:/nsncast --accounts-file accounts.json account deploy --url [..] --name my_account","type":"response"} {"links":"account: https://sepolia.voyager.online/contract/0x[..]","title":"account creation","type":"notification"} "#}); } #[tokio::test] pub async fn test_no_explorer_links_on_localhost() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "create", "--url", "http://127.0.0.1:5055/rpc", "--name", "my_account", "--salt", "0x1", "--type", "oz", ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert!( !output .as_stdout() .contains("To see account creation details, visit:") ); } #[tokio::test] pub async fn test_use_url_from_config() { let accounts_file = "accounts.json"; let temp_dir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); copy_file( "tests/data/accounts/accounts.json", temp_dir.path().join(accounts_file), ); let args = vec!["--accounts-file", accounts_file, "account", "create"]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(temp_dir.path()); snapbox.assert().success(); } ================================================ FILE: crates/sncast/tests/e2e/account/delete.rs ================================================ use crate::helpers::constants::ACCOUNT_FILE_PATH; use crate::helpers::runner::runner; use crate::{e2e::account::helpers::create_tempdir_with_accounts_file, helpers::constants::URL}; use indoc::indoc; use shared::test_utils::output_assert::{AsOutput, assert_stderr_contains}; #[test] pub fn test_no_accounts_in_network() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "account", "delete", "--name", "user99", "--network-name", "my-custom-network", ]; let snapbox = runner(&args); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: account delete Error: No accounts defined for network = my-custom-network "}, ); } #[test] pub fn test_account_does_not_exist() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "account", "delete", "--url", URL, "--name", "user99", ]; let snapbox = runner(&args); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: account delete Error: Account with name user99 does not exist "}, ); } #[test] pub fn test_delete_abort() { // Creating dummy accounts test file let accounts_file_name = "temp_accounts.json"; let temp_dir = create_tempdir_with_accounts_file(accounts_file_name, true); // Now delete dummy account let args = vec![ "--accounts-file", &accounts_file_name, "account", "delete", "--name", "user3", "--network-name", "custom-network", ]; // Run test with a negative user input let snapbox = runner(&args).current_dir(temp_dir.path()).stdin("n"); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: account delete Error: Delete aborted "}, ); } #[test] pub fn test_happy_case() { // Creating dummy accounts test file let accounts_file_name = "temp_accounts.json"; let temp_dir = create_tempdir_with_accounts_file(accounts_file_name, true); // Now delete dummy account let args = vec![ "--accounts-file", &accounts_file_name, "account", "delete", "--name", "user3", "--network-name", "custom-network", ]; // Run test with an affirmative user input let snapbox = runner(&args).current_dir(temp_dir.path()).stdin("Y"); snapbox.assert().success().stdout_eq(indoc! {r" Success: Account deleted Account successfully removed "}); } #[test] pub fn test_happy_case_url() { let accounts_file_name = "temp_accounts.json"; let temp_dir = create_tempdir_with_accounts_file(accounts_file_name, true); let args = vec![ "--accounts-file", &accounts_file_name, "account", "delete", "--url", URL, "--name", "user0", ]; let snapbox = runner(&args).current_dir(temp_dir.path()).stdin("Y"); snapbox.assert().success().stdout_eq(indoc! {r" Success: Account deleted Account successfully removed "}); } #[test] pub fn test_happy_case_with_yes_flag() { // Creating dummy accounts test file let accounts_file_name = "temp_accounts.json"; let temp_dir = create_tempdir_with_accounts_file(accounts_file_name, true); // Now delete dummy account let args = vec![ "--accounts-file", &accounts_file_name, "account", "delete", "--name", "user3", "--network-name", "custom-network", "--yes", ]; // Run test with no additional user input let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert!(output.as_stderr().is_empty()); output.stdout_eq(indoc! {r" Success: Account deleted Account successfully removed "}); } #[test] pub fn test_accept_only_one_network_type_argument() { let accounts_file_name = "temp_accounts.json"; let temp_dir = create_tempdir_with_accounts_file(accounts_file_name, true); let args = vec![ "--accounts-file", &accounts_file_name, "account", "delete", "--url", URL, "--name", "user3", "--network-name", "custom-network", ]; let snapbox = runner(&args).current_dir(temp_dir.path()).stdin("Y"); let output = snapbox.assert().failure(); assert_stderr_contains( output, indoc! {r" error: the argument '--url ' cannot be used with '--network-name ' "}, ); } ================================================ FILE: crates/sncast/tests/e2e/account/deploy.rs ================================================ use crate::helpers::constants::{DEVNET_OZ_CLASS_HASH_CAIRO_0, URL}; use crate::helpers::env::set_keystore_password_env; use crate::helpers::fixtures::copy_file; use crate::helpers::fixtures::{ get_address_from_keystore, get_transaction_hash, get_transaction_receipt, mint_token, }; use crate::helpers::runner::runner; use camino::Utf8PathBuf; use configuration::test_utils::copy_config_to_tempdir; use conversions::string::IntoHexStr; use indoc::indoc; use shared::test_utils::output_assert::{AsOutput, assert_stderr_contains}; use sncast::AccountType; use sncast::helpers::account::load_accounts; use sncast::helpers::constants::{ BRAAVOS_CLASS_HASH, KEYSTORE_PASSWORD_ENV_VAR, OZ_CLASS_HASH, READY_CLASS_HASH, }; use starknet_rust::core::types::TransactionReceipt::DeployAccount; use std::fs; use tempfile::{TempDir, tempdir}; use test_case::test_case; #[test_case(DEVNET_OZ_CLASS_HASH_CAIRO_0, "oz"; "cairo_0_class_hash")] #[test_case(&OZ_CLASS_HASH.into_hex_string(), "oz"; "cairo_1_class_hash")] #[test_case(&READY_CLASS_HASH.into_hex_string(), "ready"; "ready_class_hash")] #[test_case(&BRAAVOS_CLASS_HASH.into_hex_string(), "braavos"; "braavos_class_hash")] #[tokio::test] pub async fn test_happy_case(class_hash: &str, account_type: &str) { let tempdir = create_account(false, class_hash, account_type).await; let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "--json", "account", "deploy", "--url", URL, "--name", "my_account", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); let bdg = snapbox.assert(); let hash = get_transaction_hash(&bdg.get_output().stdout); let receipt = get_transaction_receipt(hash).await; assert!(matches!(receipt, DeployAccount(_))); let stdout_str = bdg.as_stdout(); assert!(stdout_str.contains("account deploy")); assert!(stdout_str.contains("transaction_hash")); let path = Utf8PathBuf::from_path_buf(tempdir.path().join(accounts_file)) .expect("Path is not valid UTF-8"); let items = load_accounts(&path).expect("Failed to load accounts"); assert_eq!(items["alpha-sepolia"]["my_account"]["deployed"], true); } #[tokio::test] pub async fn test_happy_case_max_fee() { let tempdir = create_account(false, &OZ_CLASS_HASH.into_hex_string(), "oz").await; let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "--json", "account", "deploy", "--url", URL, "--name", "my_account", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); let bdg = snapbox.assert(); let hash = get_transaction_hash(&bdg.get_output().stdout); let receipt = get_transaction_receipt(hash).await; assert!(matches!(receipt, DeployAccount(_))); let stdout_str = bdg.as_stdout(); assert!(stdout_str.contains("account deploy")); assert!(stdout_str.contains("transaction_hash")); let path = Utf8PathBuf::from_path_buf(tempdir.path().join(accounts_file)) .expect("Path is not valid UTF-8"); let items = load_accounts(&path).expect("Failed to load accounts"); assert_eq!(items["alpha-sepolia"]["my_account"]["deployed"], true); } #[tokio::test] pub async fn test_happy_case_add_profile() { let tempdir = create_account(true, &OZ_CLASS_HASH.into_hex_string(), "oz").await; let accounts_file = "accounts.json"; let args = vec![ "--profile", "deploy_profile", "--accounts-file", accounts_file, "--json", "account", "deploy", "--name", "my_account", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); let output = snapbox.assert(); let hash = get_transaction_hash(&output.get_output().stdout); let receipt = get_transaction_receipt(hash).await; assert!(matches!(receipt, DeployAccount(_))); let stdout_str = output.as_stdout(); assert!(stdout_str.contains("account deploy")); assert!(stdout_str.contains("transaction_hash")); } #[test_case("{\"alpha-sepolia\": {}}", "Error: Account = my_account not found under network = alpha-sepolia" ; "when account name not present")] #[test_case("{\"alpha-sepolia\": {\"my_account\" : {}}}", "Error: Failed to parse field `alpha-sepolia.my_account` in file 'accounts.json': missing field `public_key`[..]" ; "when public key not present")] fn test_account_deploy_error(accounts_content: &str, error: &str) { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; fs::write(temp_dir.path().join(accounts_file), accounts_content).unwrap(); let args = vec![ "--accounts-file", accounts_file, "account", "deploy", "--url", URL, "--name", "my_account", ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert(); assert_stderr_contains(output, error); } #[tokio::test] pub async fn test_valid_class_hash() { let tempdir = create_account(true, &OZ_CLASS_HASH.into_hex_string(), "oz").await; let accounts_file = "accounts.json"; let args = vec![ "--profile", "deploy_profile", "--accounts-file", accounts_file, "account", "deploy", "--name", "my_account", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); snapbox.assert().success().stdout_eq(indoc! {r" Success: Account deployed Transaction Hash: 0x[..] To see account deployment details, visit: transaction: https://sepolia.voyager.online/tx/0x[..] "}); } #[tokio::test] pub async fn test_valid_no_max_fee() { let tempdir = create_account(true, &OZ_CLASS_HASH.into_hex_string(), "oz").await; let accounts_file = "accounts.json"; let args = vec![ "--profile", "deploy_profile", "--accounts-file", accounts_file, "account", "deploy", "--url", URL, "--name", "my_account", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); snapbox.assert().success().stdout_eq(indoc! {r" Success: Account deployed Transaction Hash: 0x[..] To see account deployment details, visit: transaction: https://sepolia.voyager.online/tx/0x[..] "}); } pub async fn create_account(add_profile: bool, class_hash: &str, account_type: &str) -> TempDir { let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); let accounts_file = "accounts.json"; let mut args = vec![ "--accounts-file", accounts_file, "account", "create", "--url", URL, "--name", "my_account", "--class-hash", class_hash, "--type", account_type, ]; if add_profile { args.push("--add-profile"); args.push("deploy_profile"); } runner(&args).current_dir(tempdir.path()).assert().success(); let path = Utf8PathBuf::from_path_buf(tempdir.path().join(accounts_file)) .expect("Path is not valid UTF-8"); let items = load_accounts(&path).expect("Failed to load accounts"); mint_token( items["alpha-sepolia"]["my_account"]["address"] .as_str() .unwrap(), 9_999_999_999_999_999_999_999_999_999_999, ) .await; tempdir } #[test_case("oz"; "open_zeppelin_account")] #[test_case("ready"; "ready_account")] #[test_case("braavos"; "braavos_account")] #[tokio::test] pub async fn test_happy_case_keystore(account_type: &str) { let tempdir = tempdir().expect("Unable to create a temporary directory"); let keystore_file = "my_key.json"; let account_file = format!("my_account_{account_type}_undeployed_happy_case.json"); copy_file( "tests/data/keystore/my_key.json", tempdir.path().join(keystore_file), ); copy_file( format!("tests/data/keystore/{account_file}"), tempdir.path().join(&account_file), ); set_keystore_password_env(); let address = get_address_from_keystore( tempdir.path().join(keystore_file).to_str().unwrap(), tempdir.path().join(&account_file).to_str().unwrap(), KEYSTORE_PASSWORD_ENV_VAR, &account_type.parse().unwrap(), ); mint_token( &address.into_hex_string(), 9_999_999_999_999_999_999_999_999_999_999, ) .await; let args = vec![ "--keystore", keystore_file, "--account", &account_file, "account", "deploy", "--url", URL, ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); snapbox.assert().stdout_eq(indoc! {r" Success: Account deployed Transaction Hash: 0x[..] To see account deployment details, visit: transaction: https://sepolia.voyager.online/tx/0x[..] "}); let path = Utf8PathBuf::from_path_buf(tempdir.path().join(account_file)) .expect("Path is not valid UTF-8"); let items = load_accounts(&path).expect("Failed to load accounts"); assert_eq!(items["deployment"]["status"], "deployed"); assert!(!items["deployment"]["address"].is_null()); assert!(items["deployment"]["salt"].is_null()); assert!(items["deployment"]["context"].is_null()); } #[tokio::test] pub async fn test_keystore_already_deployed() { let tempdir = tempdir().expect("Unable to create a temporary directory"); let keystore_file = "my_key.json"; let account_file = "account.json"; copy_file( "tests/data/keystore/my_key.json", tempdir.path().join(keystore_file), ); copy_file( "tests/data/keystore/my_account.json", tempdir.path().join(account_file), ); set_keystore_password_env(); let args = vec![ "--keystore", keystore_file, "--account", account_file, "account", "deploy", "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: account deploy Error: Account already deployed "}, ); } #[tokio::test] pub async fn test_keystore_key_mismatch() { let tempdir = tempdir().expect("Unable to create a temporary directory"); let keystore_file = "my_key_invalid.json"; let account_file = "my_account_undeployed.json"; copy_file( "tests/data/keystore/my_key_invalid.json", tempdir.path().join(keystore_file), ); copy_file( "tests/data/keystore/my_account_undeployed.json", tempdir.path().join(account_file), ); set_keystore_password_env(); let args = vec![ "--keystore", keystore_file, "--account", account_file, "account", "deploy", "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: account deploy Error: Public key and private key from keystore do not match "}, ); } #[tokio::test] pub async fn test_deploy_keystore_inexistent_keystore_file() { let tempdir = tempdir().expect("Unable to create a temporary directory"); let keystore_file = "my_key_inexistent.json"; let account_file = "my_account_undeployed.json"; copy_file( "tests/data/keystore/my_account_undeployed.json", tempdir.path().join(account_file), ); set_keystore_password_env(); let args = vec![ "--keystore", keystore_file, "--account", account_file, "account", "deploy", "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: account deploy Error: Failed to find keystore file "}, ); } #[tokio::test] pub async fn test_deploy_keystore_inexistent_account_file() { let tempdir = tempdir().expect("Unable to create a temporary directory"); let keystore_file = "my_key.json"; let account_file = "my_account_inexistent.json"; copy_file( "tests/data/keystore/my_key.json", tempdir.path().join(keystore_file), ); set_keystore_password_env(); let args = vec![ "--keystore", keystore_file, "--account", account_file, "account", "deploy", "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: account deploy Error: File containing the account does not exist: When using `--keystore` argument, the `--account` argument should be a path to the starkli JSON account file "}, ); } #[tokio::test] pub async fn test_deploy_keystore_no_status() { let tempdir = tempdir().expect("Unable to create a temporary directory"); let keystore_file = "my_key.json"; let account_file = "my_account_invalid.json"; copy_file( "tests/data/keystore/my_key.json", tempdir.path().join(keystore_file), ); copy_file( "tests/data/keystore/my_account_invalid.json", tempdir.path().join(account_file), ); set_keystore_password_env(); let args = vec![ "--keystore", keystore_file, "--account", account_file, "account", "deploy", "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: account deploy Error: Failed to get status key from account JSON file "}, ); } #[tokio::test] pub async fn test_deploy_keystore_other_args() { let tempdir = tempdir().expect("Unable to create a temporary directory"); let keystore_file = "my_key.json"; let account_file = "my_account_undeployed_happy_case_other_args.json"; copy_file( "tests/data/keystore/my_key.json", tempdir.path().join(keystore_file), ); copy_file( "tests/data/keystore/my_account_undeployed_happy_case_other_args.json", tempdir.path().join(account_file), ); set_keystore_password_env(); let address = get_address_from_keystore( tempdir.path().join(keystore_file), tempdir.path().join(account_file), KEYSTORE_PASSWORD_ENV_VAR, &AccountType::OpenZeppelin, ); mint_token( &address.into_hex_string(), 9_999_999_999_999_999_999_999_999_999_999, ) .await; let args = vec![ "--accounts-file", "accounts.json", "--keystore", keystore_file, "--account", account_file, "account", "deploy", "--url", URL, "--name", "some-name", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); snapbox.assert().stdout_eq(indoc! {r" Success: Account deployed Transaction Hash: 0x[..] To see account deployment details, visit: transaction: https://sepolia.voyager.online/tx/0x[..] "}); } #[tokio::test] pub async fn test_json_output_format() { let tempdir = create_account(false, &OZ_CLASS_HASH.into_hex_string(), "oz").await; let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "--json", "account", "deploy", "--url", URL, "--name", "my_account", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); snapbox.assert().stdout_eq(indoc! {r#" {"command":"account deploy","transaction_hash":"0x0[..]","type":"response"} {"links":"transaction: https://sepolia.voyager.online/tx/0x0[..]","title":"account deployment","type":"notification"} "#}); } ================================================ FILE: crates/sncast/tests/e2e/account/helpers.rs ================================================ use indoc::indoc; use std::{fs::File, io::Write}; use tempfile::{TempDir, tempdir}; #[must_use] pub fn create_tempdir_with_accounts_file(file_name: &str, with_sample_data: bool) -> TempDir { let dir = tempdir().expect("Unable to create a temporary directory"); let location = dir.path().join(file_name); let mut file = File::create(location).expect("Unable to create a temporary accounts file"); let data = if with_sample_data { indoc! {r#" { "alpha-sepolia": { "user0": { "private_key": "0x1e9038bdc68ce1d27d54205256988e85", "public_key": "0x2f91ed13f8f0f7d39b942c80bfcd3d0967809d99e0cc083606cbe59033d2b39", "address": "0x4f5f24ceaae64434fa2bc2befd08976b51cf8f6a5d8257f7ec3616f61de263a", "type": "open_zeppelin" }, "user1": { "address": "0x9613a934141dd6625748a7e066a380b3f9787f079f35ecc2f3ba934d507d4e", "class_hash": "0x36078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f", "private_key": "0x1c3495fce931c0b3ed244f55c54226441a8254deafbc7fab2e46926b4d2fdae", "public_key": "0x63b3a3ac141e4c007b167b27450f110c729cc0d0238541ca705b0de5144edbd", "salt": "0xe2b200bbdf76c31b", "type": "ready" }, "user2": { "address": "0x9613a934141dd6625748a7e066a380b3f9787f079f35ecc2f3ba934d507d4e", "class_hash": "0x36078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f", "private_key": "0x1c3495fce931c0b3ed244f55c54226441a8254deafbc7fab2e46926b4d2fdae", "public_key": "0x63b3a3ac141e4c007b167b27450f110c729cc0d0238541ca705b0de5144edbd", "salt": "0xe2b200bbdf76c31b", "type": "argent" } }, "custom-network": { "user3": { "private_key": "0xe3e70682c2094cac629f6fbed82c07cd", "public_key": "0x7e52885445756b313ea16849145363ccb73fb4ab0440dbac333cf9d13de82b9", "address": "0x7e00d496e324876bbc8531f2d9a82bf154d1a04a50218ee74cdd372f75a551a" }, "user4": { "private_key": "0x73fbb3c1eff11167598455d0408f3932e42c678bd8f7fbc6028c716867cc01f", "public_key": "0x43a74f86b7e204f1ba081636c9d4015e1f54f5bb03a4ae8741602a15ffbb182", "salt": "0x54aa715a5cff30ccf7845ad4659eb1dac5b730c2541263c358c7e3a4c4a8064", "address": "0x7ccdf182d27c7aaa2e733b94db4a3f7b28ff56336b34abf43c15e3a9edfbe91", "deployed": true } } } "#} } else { r"{}" }; file.write_all(data.as_bytes()) .expect("Unable to write test data to a temporary file"); file.flush().expect("Unable to flush a temporary file"); dir } ================================================ FILE: crates/sncast/tests/e2e/account/import.rs ================================================ use crate::helpers::constants::{ DEVNET_OZ_CLASS_HASH_CAIRO_0, DEVNET_OZ_CLASS_HASH_CAIRO_1, DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS, URL, }; use crate::helpers::runner::runner; use camino::Utf8PathBuf; use configuration::CONFIG_FILENAME; use configuration::test_utils::copy_config_to_tempdir; use conversions::string::IntoHexStr; use indoc::{formatdoc, indoc}; use serde_json::json; use shared::test_utils::output_assert::{assert_stderr_contains, assert_stdout_contains}; use std::fs::{self, File}; use tempfile::tempdir; use test_case::test_case; #[test_case("oz", "open_zeppelin"; "oz_account_type")] #[test_case("ready", "ready"; "ready_account_type")] #[test_case("braavos", "braavos"; "braavos_account_type")] #[tokio::test] pub async fn test_happy_case(input_account_type: &str, saved_type: &str) { let tempdir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "import", "--url", URL, "--name", "my_account_import", "--address", "0x123", "--private-key", "0x456", "--class-hash", DEVNET_OZ_CLASS_HASH_CAIRO_0, "--type", input_account_type, ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().stdout_eq(indoc! {r" Success: Account imported successfully Account Name: my_account_import "}); let contents = fs::read_to_string(tempdir.path().join(accounts_file)) .expect("Unable to read created file"); let contents_json: serde_json::Value = serde_json::from_str(&contents).unwrap(); assert_eq!( contents_json, json!( { "alpha-sepolia": { "my_account_import": { "address": "0x123", "class_hash": DEVNET_OZ_CLASS_HASH_CAIRO_0, "deployed": false, "legacy": true, "private_key": "0x456", "public_key": "0x5f679dacd8278105bd3b84a15548fe84079068276b0e84d6cc093eb5430f063", "type": saved_type } } } ) ); } // TODO(#3556): Remove this test once we drop Argent account type #[tokio::test] pub async fn test_happy_case_argent_with_deprecation_warning() { let tempdir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "import", "--url", URL, "--name", "my_account_import", "--address", "0x123", "--private-key", "0x456", "--class-hash", DEVNET_OZ_CLASS_HASH_CAIRO_0, "--type", "argent", ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().stdout_eq(indoc! {r" [WARNING] Argent has rebranded as Ready. The `argent` option for the `--type` flag in `account import` is deprecated, please use `ready` instead. Success: Account imported successfully Account Name: my_account_import "}); let contents = fs::read_to_string(tempdir.path().join(accounts_file)) .expect("Unable to read created file"); let contents_json: serde_json::Value = serde_json::from_str(&contents).unwrap(); assert_eq!( contents_json, json!( { "alpha-sepolia": { "my_account_import": { "address": "0x123", "class_hash": DEVNET_OZ_CLASS_HASH_CAIRO_0, "deployed": false, "legacy": true, "private_key": "0x456", "public_key": "0x5f679dacd8278105bd3b84a15548fe84079068276b0e84d6cc093eb5430f063", "type": "ready" } } } ) ); } #[tokio::test] pub async fn test_existent_account_address() { let tempdir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "import", "--url", URL, "--name", "my_account_import", "--address", DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS, "--private-key", "0x456", "--type", "oz", ]; let _ = runner(&args).current_dir(tempdir.path()).assert(); let contents = fs::read_to_string(tempdir.path().join(accounts_file)) .expect("Unable to read created file"); let contents_json: serde_json::Value = serde_json::from_str(&contents).unwrap(); assert_eq!( contents_json, json!( { "alpha-sepolia": { "my_account_import": { "address": DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS, "class_hash": &DEVNET_OZ_CLASS_HASH_CAIRO_1.into_hex_string(), "deployed": true, "legacy": false, "private_key": "0x456", "public_key": "0x5f679dacd8278105bd3b84a15548fe84079068276b0e84d6cc093eb5430f063", "type": "open_zeppelin" } } } ) ); } #[tokio::test] pub async fn test_existent_account_address_and_incorrect_class_hash() { let tempdir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "import", "--url", URL, "--name", "my_account_import", "--address", DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS, "--private-key", "0x456", "--class-hash", DEVNET_OZ_CLASS_HASH_CAIRO_0, "--type", "oz", ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().stderr_eq(formatdoc! {r" Command: account import Error: Incorrect class hash {} for account address {} was provided ", DEVNET_OZ_CLASS_HASH_CAIRO_0, DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS}); } #[tokio::test] pub async fn test_nonexistent_account_address_and_nonexistent_class_hash() { let tempdir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "import", "--url", URL, "--name", "my_account_import", "--address", "0x202", "--private-key", "0x456", "--class-hash", "0x101", "--type", "oz", ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().stderr_eq(indoc! {r" Command: account import Error: Class with hash 0x101 is not declared, try using --class-hash with a hash of the declared class "}); } #[tokio::test] pub async fn test_nonexistent_account_address() { let tempdir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "import", "--url", URL, "--name", "my_account_import", "--address", "0x123", "--private-key", "0x456", "--type", "oz", ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().stderr_eq(indoc! {r" Command: account import Error: Class hash for the account address 0x123 could not be found. Please provide the class hash "}); } #[test_case("--url", URL; "with_url")] #[test_case("--network", "devnet"; "with_network")] #[tokio::test] pub async fn test_happy_case_add_profile(rpc_flag: &str, rpc_value: &str) { let tempdir = tempdir().expect("Failed to create a temporary directory"); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "import", rpc_flag, rpc_value, "--name", "my_account_import", "--address", "0x1", "--private-key", "0x2", "--class-hash", DEVNET_OZ_CLASS_HASH_CAIRO_0, "--type", "oz", "--add-profile", "my_account_import", ]; let output = runner(&args).current_dir(tempdir.path()).assert(); let config_path = Utf8PathBuf::from_path_buf(tempdir.path().join("snfoundry.toml")) .unwrap() .canonicalize_utf8() .unwrap(); assert_stdout_contains( output, format!("Add Profile: Profile my_account_import successfully added to {config_path}"), ); let current_dir_utf8 = Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap(); let contents = fs::read_to_string(current_dir_utf8.join(accounts_file)) .expect("Unable to read created file"); let contents_json: serde_json::Value = serde_json::from_str(&contents).unwrap(); assert_eq!( contents_json, json!( { "alpha-sepolia": { "my_account_import": { "address": "0x1", "class_hash": DEVNET_OZ_CLASS_HASH_CAIRO_0, "deployed": false, "private_key": "0x2", "public_key": "0x759ca09377679ecd535a81e83039658bf40959283187c654c5416f439403cf5", "legacy": true, "type": "open_zeppelin" } } } ) ); let contents = fs::read_to_string(current_dir_utf8.join("snfoundry.toml")) .expect("Unable to read snfoundry.toml"); let expected_lines = [ "[sncast.my_account_import]", "account = \"my_account_import\"", "accounts-file = \"accounts.json\"", &format!("{} = \"{}\"", rpc_flag.trim_start_matches("--"), rpc_value), ]; let expected_block = expected_lines.join("\n"); assert!(contents.contains(&expected_block)); } #[tokio::test] pub async fn test_detect_deployed() { let tempdir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "import", "--url", URL, "--name", "my_account_import", "--address", DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS, "--private-key", "0x5", "--type", "oz", ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().stdout_eq(indoc! {r" Success: Account imported successfully Account Name: my_account_import "}); let contents = fs::read_to_string(tempdir.path().join(accounts_file)) .expect("Unable to read created file"); let contents_json: serde_json::Value = serde_json::from_str(&contents).unwrap(); assert_eq!( contents_json, json!( { "alpha-sepolia": { "my_account_import": { "address": DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS, "class_hash": &DEVNET_OZ_CLASS_HASH_CAIRO_1.into_hex_string(), "deployed": true, "private_key": "0x5", "public_key": "0x788435d61046d3eec54d77d25bd194525f4fa26ebe6575536bc6f656656b74c", "legacy": false, "type": "open_zeppelin" } } } ) ); } #[tokio::test] pub async fn test_missing_arguments() { let args = vec![ "account", "import", "--url", URL, "--name", "my_account_import", ]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains( output, indoc! {r" error: the following required arguments were not provided: --address
--type "}, ); } #[tokio::test] pub async fn test_private_key_from_file() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let private_key_file = "my_private_key"; fs::write(temp_dir.path().join(private_key_file), "0x456").unwrap(); let args = vec![ "--accounts-file", accounts_file, "account", "import", "--url", URL, "--name", "my_account_import", "--address", "0x123", "--private-key-file", private_key_file, "--class-hash", DEVNET_OZ_CLASS_HASH_CAIRO_0, "--type", "oz", ]; let snapbox = runner(&args).current_dir(temp_dir.path()); snapbox.assert().stdout_eq(indoc! {r" Success: Account imported successfully Account Name: my_account_import "}); let contents = fs::read_to_string(temp_dir.path().join(accounts_file)) .expect("Unable to read created file"); let contents_json: serde_json::Value = serde_json::from_str(&contents).unwrap(); assert_eq!( contents_json, json!( { "alpha-sepolia": { "my_account_import": { "address": "0x123", "deployed": false, "legacy": true, "private_key": "0x456", "public_key": "0x5f679dacd8278105bd3b84a15548fe84079068276b0e84d6cc093eb5430f063", "class_hash": DEVNET_OZ_CLASS_HASH_CAIRO_0, "type": "open_zeppelin" } } } ) ); } #[tokio::test] pub async fn test_accept_only_one_private_key() { let args = vec![ "account", "import", "--name", "my_account_import", "--address", "0x123", "--private-key", "0x456", "--private-key-file", "my_private_key", ]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains( output, "error: the argument '--private-key ' cannot be used with '--private-key-file '", ); } #[tokio::test] pub async fn test_invalid_private_key_file_path() { let args = vec![ "account", "import", "--url", URL, "--name", "my_account_import", "--address", "0x123", "--private-key-file", "my_private_key", "--type", "oz", ]; let snapbox = runner(&args); let output = snapbox.assert().success(); let expected_file_error = "No such file or directory [..]"; assert_stderr_contains( output, formatdoc! {r" Command: account import Error: Failed to obtain private key from the file my_private_key: {} ", expected_file_error}, ); } #[tokio::test] pub async fn test_invalid_private_key_in_file() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let private_key_file = "my_private_key"; fs::write( temp_dir.path().join(private_key_file), "invalid private key", ) .unwrap(); let args = vec![ "--accounts-file", "accounts.json", "account", "import", "--url", URL, "--name", "my_account_import", "--address", "0x123", "--private-key-file", private_key_file, "--type", "oz", ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: account import Error: Failed to obtain private key from the file my_private_key: failed to create Felt from string: invalid dec string "}, ); } #[tokio::test] pub async fn test_private_key_as_int_in_file() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let private_key_file = "my_private_key"; fs::write(temp_dir.path().join(private_key_file), "1110").unwrap(); let args = vec![ "--accounts-file", accounts_file, "account", "import", "--url", URL, "--name", "my_account_import", "--address", DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS, "--private-key-file", private_key_file, "--type", "oz", ]; runner(&args) .current_dir(temp_dir.path()) .assert() .success(); let contents = fs::read_to_string(temp_dir.path().join(accounts_file)) .expect("Unable to read created file"); let contents_json: serde_json::Value = serde_json::from_str(&contents).unwrap(); assert_eq!( contents_json, json!( { "alpha-sepolia": { "my_account_import": { "address": DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS, "deployed": true, "legacy": false, "private_key": "0x456", "public_key": "0x5f679dacd8278105bd3b84a15548fe84079068276b0e84d6cc093eb5430f063", "class_hash": &DEVNET_OZ_CLASS_HASH_CAIRO_1.into_hex_string(), "type": "open_zeppelin" } } } ) ); } #[tokio::test] pub async fn test_empty_config_add_profile() { let tempdir = tempdir().expect("Unable to create a temporary directory"); File::create(tempdir.path().join(CONFIG_FILENAME)).unwrap(); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "import", "--url", URL, "--name", "my_account_import", "--address", DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS, "--private-key", "0x456", "--type", "oz", "--add-profile", "random", ]; let output = runner(&args).current_dir(tempdir.path()).assert(); let config_path = Utf8PathBuf::from_path_buf(tempdir.path().join("snfoundry.toml")) .unwrap() .canonicalize_utf8() .unwrap(); assert_stdout_contains( output, format!("Add Profile: Profile random successfully added to {config_path}"), ); let current_dir_utf8 = Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap(); let contents = fs::read_to_string(current_dir_utf8.join("snfoundry.toml")) .expect("Unable to read snfoundry.toml"); assert!(contents.contains("[sncast.random]")); assert!(contents.contains("account = \"my_account_import\"")); assert!(contents.contains(&format!("url = \"{URL}\""))); } #[tokio::test] pub async fn test_happy_case_valid_address_computation() { let tempdir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "import", "--url", URL, "--name", "my_account_import", "--address", "0x721c21e0cc9d37aec8e176797effd1be222aff6db25f068040adefabb7cfb6d", "--private-key", "0x2", "--salt", "0x3", "--class-hash", DEVNET_OZ_CLASS_HASH_CAIRO_0, "--type", "oz", ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().stdout_eq(indoc! {r" Success: Account imported successfully Account Name: my_account_import "}); let contents = fs::read_to_string(tempdir.path().join(accounts_file)) .expect("Unable to read created file"); let contents_json: serde_json::Value = serde_json::from_str(&contents).unwrap(); assert_eq!( contents_json, json!( { "alpha-sepolia": { "my_account_import": { "address": "0x721c21e0cc9d37aec8e176797effd1be222aff6db25f068040adefabb7cfb6d", "class_hash": DEVNET_OZ_CLASS_HASH_CAIRO_0, "deployed": false, "salt": "0x3", "legacy": true, "private_key": "0x2", "public_key": "0x759ca09377679ecd535a81e83039658bf40959283187c654c5416f439403cf5", "type": "open_zeppelin" } } } ) ); } #[tokio::test] pub async fn test_invalid_address_computation() { let tempdir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "import", "--url", URL, "--name", "my_account_import", "--address", "0x123", "--private-key", "0x456", "--salt", "0x789", "--class-hash", DEVNET_OZ_CLASS_HASH_CAIRO_0, "--type", "oz", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let computed_address = "0xaf550326d32c8106ef08d25cbc0dba06e5cd10a2201c2e9bc5ad4cbbce45e6"; snapbox.assert().stderr_eq(formatdoc! {r" Command: account import Error: Computed address {computed_address} does not match the provided address 0x123. Please ensure that the provided salt, class hash, and account type are correct. "}); } #[tokio::test] pub async fn test_happy_case_default_name_generation() { let tempdir = tempdir().expect("Unable to create a temporary directory"); let accounts_file = "accounts.json"; let import_args = vec![ "--accounts-file", accounts_file, "account", "import", "--url", URL, "--address", "0x123", "--private-key", "0x456", "--class-hash", DEVNET_OZ_CLASS_HASH_CAIRO_0, "--type", "oz", ]; let delete_args = vec![ "--accounts-file", &accounts_file, "account", "delete", "--name", "account-2", "--network-name", "alpha-sepolia", ]; let account_info = json!({ "address": "0x123", "class_hash": DEVNET_OZ_CLASS_HASH_CAIRO_0, "deployed": false, "legacy": true, "private_key": "0x456", "public_key": "0x5f679dacd8278105bd3b84a15548fe84079068276b0e84d6cc093eb5430f063", "type": "open_zeppelin" }); let mut all_accounts_content = serde_json::Value::Object(serde_json::Map::new()); all_accounts_content["alpha-sepolia"]["account-1"] = account_info.clone(); all_accounts_content["alpha-sepolia"]["account-2"] = account_info.clone(); all_accounts_content["alpha-sepolia"]["account-3"] = account_info.clone(); let mut accounts_content_after_delete = serde_json::Value::Object(serde_json::Map::new()); accounts_content_after_delete["alpha-sepolia"]["account-1"] = account_info.clone(); accounts_content_after_delete["alpha-sepolia"]["account-3"] = account_info.clone(); for i in 0..3 { let snapbox = runner(&import_args).current_dir(tempdir.path()); snapbox.assert().stdout_eq(formatdoc! {r" Success: Account imported successfully Account Name: account-{id} ", id = i + 1}); } let contents = fs::read_to_string(tempdir.path().join(accounts_file)) .expect("Unable to read created file"); let contents_json: serde_json::Value = serde_json::from_str(&contents).unwrap(); assert_eq!(contents_json, all_accounts_content); let snapbox = runner(&delete_args).current_dir(tempdir.path()).stdin("Y"); snapbox.assert().success().stdout_eq(indoc! {r" Success: Account deleted Account successfully removed "}); let contents = fs::read_to_string(tempdir.path().join(accounts_file)) .expect("Unable to read created file"); let contents_json: serde_json::Value = serde_json::from_str(&contents).unwrap(); assert_eq!(contents_json, accounts_content_after_delete); let snapbox = runner(&import_args).current_dir(tempdir.path()); snapbox.assert().stdout_eq(indoc! {r" Success: Account imported successfully Account Name: account-2 "}); let contents = fs::read_to_string(tempdir.path().join(accounts_file)) .expect("Unable to read created file"); let contents_json: serde_json::Value = serde_json::from_str(&contents).unwrap(); assert_eq!(contents_json, all_accounts_content); } #[tokio::test] pub async fn test_use_url_from_config() { let temp_dir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "import", "--address", "0x123", "--private-key", "0x456", "--type", "oz", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(temp_dir.path()); snapbox.assert().success(); } ================================================ FILE: crates/sncast/tests/e2e/account/list.rs ================================================ use anyhow::Context; use indoc::formatdoc; use serde_json::{Value, json}; use shared::test_utils::output_assert::{AsOutput, assert_stderr_contains, assert_stdout_contains}; use tempfile::tempdir; use crate::{e2e::account::helpers::create_tempdir_with_accounts_file, helpers::runner::runner}; #[test] fn test_happy_case() { let accounts_file_name = "temp_accounts.json"; let temp_dir = create_tempdir_with_accounts_file(accounts_file_name, true); let accounts_file_path = temp_dir .path() .canonicalize() .expect("Unable to resolve a temporary directory path") .join(accounts_file_name); let args = vec!["--accounts-file", &accounts_file_name, "account", "list"]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert!(output.as_stderr().is_empty()); let expected = formatdoc!( " Available accounts (at {}): - user0: network: alpha-sepolia public key: 0x2f91ed13f8f0f7d39b942c80bfcd3d0967809d99e0cc083606cbe59033d2b39 address: 0x4f5f24ceaae64434fa2bc2befd08976b51cf8f6a5d8257f7ec3616f61de263a type: OpenZeppelin - user1: network: alpha-sepolia public key: 0x63b3a3ac141e4c007b167b27450f110c729cc0d0238541ca705b0de5144edbd address: 0x9613a934141dd6625748a7e066a380b3f9787f079f35ecc2f3ba934d507d4e salt: 0xe2b200bbdf76c31b type: Ready class hash: 0x36078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f - user2: network: alpha-sepolia public key: 0x63b3a3ac141e4c007b167b27450f110c729cc0d0238541ca705b0de5144edbd address: 0x9613a934141dd6625748a7e066a380b3f9787f079f35ecc2f3ba934d507d4e salt: 0xe2b200bbdf76c31b type: Ready class hash: 0x36078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f - user3: network: custom-network public key: 0x7e52885445756b313ea16849145363ccb73fb4ab0440dbac333cf9d13de82b9 address: 0x7e00d496e324876bbc8531f2d9a82bf154d1a04a50218ee74cdd372f75a551a - user4: network: custom-network public key: 0x43a74f86b7e204f1ba081636c9d4015e1f54f5bb03a4ae8741602a15ffbb182 address: 0x7ccdf182d27c7aaa2e733b94db4a3f7b28ff56336b34abf43c15e3a9edfbe91 salt: 0x54aa715a5cff30ccf7845ad4659eb1dac5b730c2541263c358c7e3a4c4a8064 deployed: true To show private keys too, run with --display-private-keys or -p ", accounts_file_path.to_str().unwrap() ); assert_stdout_contains(output, expected); } #[test] fn test_happy_case_with_private_keys() { let accounts_file_name = "temp_accounts.json"; let temp_dir = create_tempdir_with_accounts_file(accounts_file_name, true); let accounts_file_path = temp_dir .path() .canonicalize() .expect("Unable to resolve a temporary directory path") .join(accounts_file_name); let args = vec![ "--accounts-file", &accounts_file_name, "account", "list", "--display-private-keys", ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert!(output.as_stderr().is_empty()); let expected = formatdoc!( " Available accounts (at {}): - user0: network: alpha-sepolia private key: 0x1e9038bdc68ce1d27d54205256988e85 public key: 0x2f91ed13f8f0f7d39b942c80bfcd3d0967809d99e0cc083606cbe59033d2b39 address: 0x4f5f24ceaae64434fa2bc2befd08976b51cf8f6a5d8257f7ec3616f61de263a type: OpenZeppelin - user1: network: alpha-sepolia private key: 0x1c3495fce931c0b3ed244f55c54226441a8254deafbc7fab2e46926b4d2fdae public key: 0x63b3a3ac141e4c007b167b27450f110c729cc0d0238541ca705b0de5144edbd address: 0x9613a934141dd6625748a7e066a380b3f9787f079f35ecc2f3ba934d507d4e salt: 0xe2b200bbdf76c31b type: Ready - user2: network: alpha-sepolia private key: 0x1c3495fce931c0b3ed244f55c54226441a8254deafbc7fab2e46926b4d2fdae public key: 0x63b3a3ac141e4c007b167b27450f110c729cc0d0238541ca705b0de5144edbd address: 0x9613a934141dd6625748a7e066a380b3f9787f079f35ecc2f3ba934d507d4e salt: 0xe2b200bbdf76c31b type: Ready - user3: network: custom-network private key: 0xe3e70682c2094cac629f6fbed82c07cd public key: 0x7e52885445756b313ea16849145363ccb73fb4ab0440dbac333cf9d13de82b9 address: 0x7e00d496e324876bbc8531f2d9a82bf154d1a04a50218ee74cdd372f75a551a - user4: network: custom-network private key: 0x73fbb3c1eff11167598455d0408f3932e42c678bd8f7fbc6028c716867cc01f public key: 0x43a74f86b7e204f1ba081636c9d4015e1f54f5bb03a4ae8741602a15ffbb182 address: 0x7ccdf182d27c7aaa2e733b94db4a3f7b28ff56336b34abf43c15e3a9edfbe91 salt: 0x54aa715a5cff30ccf7845ad4659eb1dac5b730c2541263c358c7e3a4c4a8064 deployed: true ", accounts_file_path.to_str().unwrap() ); assert_stdout_contains(output, expected); } #[test] fn test_happy_case_json() { let accounts_file_name = "temp_accounts.json"; let temp_dir = create_tempdir_with_accounts_file(accounts_file_name, true); let args = vec![ "--json", "--accounts-file", &accounts_file_name, "account", "list", ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert!(output.as_stderr().is_empty()); let output_plain = output.as_stdout().to_string(); let output_parsed: Value = serde_json::from_str(&output_plain) .context("Failed to parse command's output to JSON") .unwrap(); let expected = json!( { "command": "account delete", "type": "response", "user3": { "address": "0x7e00d496e324876bbc8531f2d9a82bf154d1a04a50218ee74cdd372f75a551a", "public_key": "0x7e52885445756b313ea16849145363ccb73fb4ab0440dbac333cf9d13de82b9", "network": "custom-network" }, "user4": { "public_key": "0x43a74f86b7e204f1ba081636c9d4015e1f54f5bb03a4ae8741602a15ffbb182", "address": "0x7ccdf182d27c7aaa2e733b94db4a3f7b28ff56336b34abf43c15e3a9edfbe91", "salt": "0x54aa715a5cff30ccf7845ad4659eb1dac5b730c2541263c358c7e3a4c4a8064", "deployed": true, "network": "custom-network" }, "user0": { "public_key": "0x2f91ed13f8f0f7d39b942c80bfcd3d0967809d99e0cc083606cbe59033d2b39", "address": "0x4f5f24ceaae64434fa2bc2befd08976b51cf8f6a5d8257f7ec3616f61de263a", "type": "open_zeppelin", "network": "alpha-sepolia" }, "user1": { "address": "0x9613a934141dd6625748a7e066a380b3f9787f079f35ecc2f3ba934d507d4e", "class_hash": "0x36078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f", "public_key": "0x63b3a3ac141e4c007b167b27450f110c729cc0d0238541ca705b0de5144edbd", "salt": "0xe2b200bbdf76c31b", "type": "ready", "network": "alpha-sepolia" }, "user2": { "address": "0x9613a934141dd6625748a7e066a380b3f9787f079f35ecc2f3ba934d507d4e", "class_hash": "0x36078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f", "public_key": "0x63b3a3ac141e4c007b167b27450f110c729cc0d0238541ca705b0de5144edbd", "salt": "0xe2b200bbdf76c31b", "type": "argent", "network": "alpha-sepolia" }, } ); assert_eq!(output_parsed, expected); } #[test] fn test_happy_case_with_private_keys_json() { let accounts_file_name = "temp_accounts.json"; let temp_dir = create_tempdir_with_accounts_file(accounts_file_name, true); let args = vec![ "--json", "--accounts-file", &accounts_file_name, "account", "list", "--display-private-keys", ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert!(output.as_stderr().is_empty()); let output_plain = output.as_stdout().to_string(); let output_parsed: Value = serde_json::from_str(&output_plain) .context("Failed to parse command's output to JSON") .unwrap(); let expected = json!( { "command": "account delete", "type": "response", "user3": { "address": "0x7e00d496e324876bbc8531f2d9a82bf154d1a04a50218ee74cdd372f75a551a", "private_key": "0xe3e70682c2094cac629f6fbed82c07cd", "public_key": "0x7e52885445756b313ea16849145363ccb73fb4ab0440dbac333cf9d13de82b9", "network": "custom-network" }, "user4": { "public_key": "0x43a74f86b7e204f1ba081636c9d4015e1f54f5bb03a4ae8741602a15ffbb182", "address": "0x7ccdf182d27c7aaa2e733b94db4a3f7b28ff56336b34abf43c15e3a9edfbe91", "salt": "0x54aa715a5cff30ccf7845ad4659eb1dac5b730c2541263c358c7e3a4c4a8064", "private_key": "0x73fbb3c1eff11167598455d0408f3932e42c678bd8f7fbc6028c716867cc01f", "deployed": true, "network": "custom-network" }, "user0": { "public_key": "0x2f91ed13f8f0f7d39b942c80bfcd3d0967809d99e0cc083606cbe59033d2b39", "address": "0x4f5f24ceaae64434fa2bc2befd08976b51cf8f6a5d8257f7ec3616f61de263a", "type": "open_zeppelin", "network": "alpha-sepolia", "private_key": "0x1e9038bdc68ce1d27d54205256988e85", }, "user1": { "address": "0x9613a934141dd6625748a7e066a380b3f9787f079f35ecc2f3ba934d507d4e", "class_hash": "0x36078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f", "private_key": "0x1c3495fce931c0b3ed244f55c54226441a8254deafbc7fab2e46926b4d2fdae", "public_key": "0x63b3a3ac141e4c007b167b27450f110c729cc0d0238541ca705b0de5144edbd", "salt": "0xe2b200bbdf76c31b", "type": "ready", "network": "alpha-sepolia", }, "user2": { "address": "0x9613a934141dd6625748a7e066a380b3f9787f079f35ecc2f3ba934d507d4e", "class_hash": "0x36078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f", "private_key": "0x1c3495fce931c0b3ed244f55c54226441a8254deafbc7fab2e46926b4d2fdae", "public_key": "0x63b3a3ac141e4c007b167b27450f110c729cc0d0238541ca705b0de5144edbd", "salt": "0xe2b200bbdf76c31b", "type": "argent", "network": "alpha-sepolia", }, } ); assert_eq!(output_parsed, expected); } #[test] fn test_accounts_file_does_not_exist() { let accounts_file_name = "some_inexistent_file.json"; let temp_dir = tempdir().expect("Unable to create a temporary directory"); let args = vec!["--accounts-file", &accounts_file_name, "account", "list"]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().failure(); assert!(output.as_stdout().is_empty()); let expected = "Error: Accounts file = some_inexistent_file.json does not exist! \ If you do not have an account create one with `account create` command \ or if you're using a custom accounts file, make sure \ to supply correct path to it with `--accounts-file` argument."; assert_stderr_contains(output, expected); } #[test] fn test_no_accounts_available() { let accounts_file_name = "temp_accounts.json"; let temp_dir = create_tempdir_with_accounts_file(accounts_file_name, false); let accounts_file_path = temp_dir .path() .canonicalize() .expect("Unable to resolve a temporary directory path") .join(accounts_file_name); let args = vec!["--accounts-file", &accounts_file_name, "account", "list"]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert!(output.as_stderr().is_empty()); assert_stdout_contains( output, format!( "No accounts available at {}", accounts_file_path.to_str().unwrap() ), ); } ================================================ FILE: crates/sncast/tests/e2e/account/mod.rs ================================================ mod create; mod delete; mod deploy; mod helpers; mod import; mod list; pub use deploy::create_account; ================================================ FILE: crates/sncast/tests/e2e/balance.rs ================================================ use crate::helpers::constants::URL; use crate::helpers::fixtures::get_accounts_path; use crate::helpers::runner::runner; use indoc::{formatdoc, indoc}; use shared::test_utils::output_assert::AsOutput; use sncast::helpers::token::Token; use tempfile::tempdir; use test_case::test_case; #[tokio::test] pub async fn happy_case() { let tempdir = tempdir().unwrap(); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "balance-test", "get", "balance", "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().stdout_eq(indoc! {r" Balance: 109394843313476728397 fri "}); } #[tokio::test] pub async fn happy_case_old_command() { let tempdir = tempdir().unwrap(); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "balance-test", "balance", "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().stdout_eq(indoc! {r" [WARNING] `sncast balance` has moved to `sncast get balance`. `sncast balance` will be removed in the next version. Balance: 109394843313476728397 fri "}); } #[tokio::test] pub async fn happy_case_json() { let tempdir = tempdir().unwrap(); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--json", "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "get", "balance", "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().stdout_eq(indoc! {r#" {"balance":"[..]","command":"get balance","token_unit":"fri","type":"response"} "#}); } #[test_case(&Token::Strk)] #[test_case(&Token::Eth)] #[tokio::test] pub async fn happy_case_with_token(token: &Token) { let tempdir = tempdir().unwrap(); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let token_unit = token.as_token_unit(); let token = token.to_string(); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "get", "balance", "--token", &token, "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().stdout_eq(formatdoc! {r" Balance: [..] {token_unit} "}); } #[tokio::test] pub async fn happy_case_with_block_id() { let tempdir = tempdir().unwrap(); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "get", "balance", "--block-id", "latest", "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().stdout_eq(indoc! {r" Balance: [..] fri "}); } #[tokio::test] pub async fn invalid_token() { let tempdir = tempdir().unwrap(); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "get", "balance", "--token", "xyz", "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().stderr_eq(indoc! {r" error: invalid value 'xyz' for '--token ' [possible values: strk, eth] For more information, try '--help'. "}); } #[tokio::test] pub async fn happy_case_with_token_address() { let tempdir = tempdir().unwrap(); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let strk_address = "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "get", "balance", "--token-address", strk_address, "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().stdout_eq(indoc! {r" Balance: [..] "}); } #[tokio::test] pub async fn happy_case_json_with_token_address() { let tempdir = tempdir().unwrap(); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--json", "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "get", "balance", "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); let balance_str = std::str::from_utf8(&output.get_output().stdout).unwrap(); assert!(!balance_str.contains("0x")); output.stdout_eq(indoc! {r#" {"balance":"[..]"} "#}); } #[tokio::test] pub async fn nonexistent_token_address() { let tempdir = tempdir().unwrap(); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "get", "balance", "--token-address", "0x123", "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let snapbox = snapbox.assert().failure(); let err = snapbox.as_stderr(); assert!(err.contains("Error: There is no contract at the specified address")); } ================================================ FILE: crates/sncast/tests/e2e/call.rs ================================================ use crate::helpers::constants::{ ACCOUNT_FILE_PATH, DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA, MAP_CONTRACT_ADDRESS_SEPOLIA, URL, }; use crate::helpers::fixtures::invoke_contract; use crate::helpers::runner::runner; use crate::helpers::shell::os_specific_shell; use camino::Utf8PathBuf; use indoc::indoc; use shared::test_utils::output_assert::assert_stderr_contains; use snapbox::cargo_bin; use sncast::helpers::fee::FeeSettings; #[test] fn test_happy_case() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "call", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "get", "--calldata", "0x0", "--block-id", "latest", ]; let snapbox = runner(&args); snapbox.assert().success().stdout_eq(indoc! {r" Success: Call completed Response: 0x0 Response Raw: [0x0] "}); } #[test] fn test_happy_case_cairo_expression_calldata() { let args = vec![ "call", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "put", "--arguments", "0x0_felt252, 0x2137", "--block-id", "latest", ]; let snapbox = runner(&args); snapbox.assert().success().stdout_eq(indoc! {r" Success: Call completed Response: [] "}); } #[tokio::test] async fn test_call_after_storage_changed() { let fee_settings = FeeSettings { l1_gas: Some(100_000), l1_gas_price: Some(10_000_000_000_000), l2_gas: Some(1_000_000_000), l2_gas_price: Some(100_000_000_000_000_000), l1_data_gas: Some(100_000), l1_data_gas_price: Some(10_000_000_000_000), tip: Some(100_000), }; invoke_contract( "user2", MAP_CONTRACT_ADDRESS_SEPOLIA, "put", fee_settings, &["0x2", "0x3"], ) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "call", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "get", "--calldata", "0x2", ]; let snapbox = runner(&args); snapbox.assert().success().stdout_eq(indoc! {r" Success: Call completed Response: 0x3 Response Raw: [0x3] "}); } #[tokio::test] async fn test_contract_does_not_exist() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "call", "--url", URL, "--contract-address", "0x1", "--function", "get", ]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains( output, "Error: An error occurred in the called contract[..]Requested contract address[..]is not deployed[..]", ); } #[test] fn test_wrong_function_name() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "call", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "nonexistent_get", ]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains( output, r#"Error: Function with selector "0x2924aec1f107eca35a5dc447cee68cc6985fe404841c9aad477adfcbe596d0a" not found in ABI of the contract"#, ); } #[test] fn test_wrong_calldata() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "call", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--calldata", "0x1", "0x2", "--function", "get", ]; let snapbox = runner(&args); let output = snapbox.assert().success(); // TODO(#3107) // 0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473 is "Input too long for arguments" assert_stderr_contains( output, indoc! {r#" Command: call Error: An error occurred in the called contract = [..] error: Message("[\"0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473\"]") }) } "#}, ); } #[tokio::test] async fn test_invalid_selector() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "call", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "ą", "--calldata", "0x1 0x2", ]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains( output, indoc! {r" Error: Failed to convert entry point selector to FieldElement Caused by: the provided name contains non-ASCII characters "}, ); } #[test] fn test_wrong_block_id() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "call", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "get", "--calldata", "0x0", "--block-id", "0x10101", ]; let snapbox = runner(&args); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: call Error: Block was not found "}, ); } #[test] fn test_happy_case_shell() { let binary_path = cargo_bin!("sncast"); let command = os_specific_shell(&Utf8PathBuf::from("tests/shell/call")); let snapbox = command .arg(binary_path) .arg(URL) .arg(DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA); snapbox.assert().success(); } #[test] fn test_leading_negative_values() { let binary_path = cargo_bin!("sncast"); let command = os_specific_shell(&Utf8PathBuf::from("tests/shell/call_unsigned")); let snapbox = command .arg(binary_path) .arg(URL) .arg(DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA); snapbox.assert().success(); } #[test] fn test_json_output_format() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--json", "call", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "get", "--calldata", "0x0", "--block-id", "latest", ]; let snapbox = runner(&args); snapbox.assert().success().stdout_eq(indoc! {r#" {"command":"call","response":"0x0","response_raw":["0x0"],"type":"response"} "#}); } ================================================ FILE: crates/sncast/tests/e2e/class_hash.rs ================================================ use crate::helpers::{ constants::CONTRACTS_DIR, fixtures::duplicate_contract_directory_with_salt, runner::runner, }; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; #[test] fn test_happy_case_get_class_hash() { let contract_path = duplicate_contract_directory_with_salt( CONTRACTS_DIR.to_string() + "/map", "put", "human_readable", ); let args = vec!["utils", "class-hash", "--contract-name", "Map"]; let snapbox = runner(&args).current_dir(contract_path.path()); let output = snapbox.assert().success(); assert_stdout_contains(output, indoc! {r"Class Hash: 0x0[..]"}); } ================================================ FILE: crates/sncast/tests/e2e/class_hash_at.rs ================================================ use crate::helpers::constants::{ DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS, MAP_CONTRACT_ADDRESS_SEPOLIA, URL, }; use crate::helpers::runner::runner; use indoc::indoc; use shared::test_utils::output_assert::assert_stderr_contains; #[tokio::test] async fn test_happy_case() { let args = vec![ "get", "class-hash-at", MAP_CONTRACT_ADDRESS_SEPOLIA, "--url", URL, ]; let snapbox = runner(&args).env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1"); snapbox.assert().success().stdout_eq(indoc! {r" Success: Class hash retrieved Class Hash: 0x02a09379665a749e609b4a8459c86fe954566a6beeaddd0950e43f6c700ed321 To see class details, visit: class: https://sepolia.voyager.online/class/0x02a09379665a749e609b4a8459c86fe954566a6beeaddd0950e43f6c700ed321 "}); } #[tokio::test] async fn test_with_block_id() { let args = vec![ "get", "class-hash-at", MAP_CONTRACT_ADDRESS_SEPOLIA, "--block-id", "latest", "--url", URL, ]; let snapbox = runner(&args); snapbox.assert().success().stdout_eq(indoc! {r" Success: Class hash retrieved Class Hash: 0x02a09379665a749e609b4a8459c86fe954566a6beeaddd0950e43f6c700ed321 "}); } #[tokio::test] async fn test_json_output() { let args = vec![ "--json", "get", "class-hash-at", MAP_CONTRACT_ADDRESS_SEPOLIA, "--url", URL, ]; let snapbox = runner(&args); let output = snapbox.assert().success(); let stdout = output.get_output().stdout.clone(); let json: serde_json::Value = serde_json::from_slice(&stdout).unwrap(); assert_eq!(json["command"], "get class-hash-at"); assert_eq!(json["type"], "response"); assert_eq!( json["class_hash"], "0x02a09379665a749e609b4a8459c86fe954566a6beeaddd0950e43f6c700ed321" ); } #[tokio::test] async fn test_nonexistent_contract_address() { let args = vec!["get", "class-hash-at", "0x0", "--url", URL]; let snapbox = runner(&args); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: get class-hash-at Error: There is no contract at the specified address "}, ); } #[tokio::test] async fn test_invalid_block_id() { let args = vec![ "get", "class-hash-at", DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS, "--block-id", "invalid_block", "--url", URL, ]; let snapbox = runner(&args); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: get class-hash-at Error: Incorrect value passed for block_id = invalid_block. Possible values are `pre_confirmed`, `latest`, block hash (hex) and block number (u64) "}, ); } ================================================ FILE: crates/sncast/tests/e2e/completions.rs ================================================ use crate::helpers::runner::runner; use clap::ValueEnum; use clap_complete::Shell; use indoc::formatdoc; use shared::test_utils::output_assert::assert_stderr_contains; #[test] fn test_happy_case() { for variant in Shell::value_variants() { let shell = variant.to_string(); let args = vec!["completions", shell.as_str()]; let snapbox = runner(&args); snapbox.assert().success(); } } #[test] fn test_generate_completions_unsupported_shell() { // SAFETY: Tests run in parallel and share the same environment variables. // However, this modification is applies only to this one test. unsafe { std::env::set_var("SHELL", "/bin/unsupported"); } let args = vec!["completions"]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains( output, formatdoc!( r" Error: Unsupported shell " ), ); } ================================================ FILE: crates/sncast/tests/e2e/declare.rs ================================================ use crate::helpers::constants::{CONTRACTS_DIR, DEVNET_OZ_CLASS_HASH_CAIRO_0, URL}; use crate::helpers::fixtures::{ copy_directory_to_tempdir, create_and_deploy_account, create_and_deploy_oz_account, duplicate_contract_directory_with_salt, get_accounts_path, get_transaction_by_hash, get_transaction_hash, get_transaction_receipt, join_tempdirs, }; use crate::helpers::runner::runner; use configuration::CONFIG_FILENAME; use indoc::indoc; use shared::test_utils::output_assert::{AsOutput, assert_stderr_contains, assert_stdout_contains}; use sncast::AccountType; use sncast::helpers::constants::{BRAAVOS_CLASS_HASH, OZ_CLASS_HASH, READY_CLASS_HASH}; use sncast::helpers::fee::FeeArgs; use starknet_rust::core::types::TransactionReceipt::Declare; use starknet_rust::core::types::{DeclareTransaction, Transaction, TransactionExecutionStatus}; use starknet_types_core::felt::{Felt, NonZeroFelt}; use std::fs; use test_case::test_case; #[tokio::test] async fn test_happy_case_human_readable() { let contract_path = duplicate_contract_directory_with_salt( CONTRACTS_DIR.to_string() + "/map", "put", "human_readable", ); let tempdir = create_and_deploy_oz_account().await; join_tempdirs(&contract_path, &tempdir); let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "declare", "--url", URL, "--contract-name", "Map", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r" Success: Declaration completed Class Hash: 0x[..] Transaction Hash: 0x[..] To see declaration details, visit: class: https://[..] transaction: https://[..] To deploy a contract of this class, run: sncast --accounts-file accounts.json --account my_account deploy --class-hash 0x[..] --url http://127.0.0.1:5055/rpc " }, ); } #[tokio::test] async fn test_happy_case_json_output() { let contract_path = duplicate_contract_directory_with_salt( CONTRACTS_DIR.to_string() + "/map", "put", "test_happy_case_json_output", ); let tempdir = create_and_deploy_oz_account().await; join_tempdirs(&contract_path, &tempdir); let args = vec![ "--json", "--accounts-file", "accounts.json", "--account", "my_account", "declare", "--url", URL, "--contract-name", "Map", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); snapbox.assert().success().stdout_eq(indoc! { r#" {"status":"compiling","message":"[..]"} {"type":"warn","message":"[..]"} {"type":"warn","message":"[..]"} {"status":"compiling","message":"[..]"} {"status":"finished","message":"[..]"} {"class_hash":"0x0[..]","command":"declare","transaction_hash":"0x0[..]","type":"response"} {"links":"class: https://sepolia.voyager.online/class/0x0[..]/ntransaction: https://sepolia.voyager.online/tx/0x0[..]/n","title":"declaration","type":"notification"} "#, }); } #[test_case(DEVNET_OZ_CLASS_HASH_CAIRO_0.parse().unwrap(), AccountType::OpenZeppelin; "cairo_0_class_hash" )] #[test_case(OZ_CLASS_HASH, AccountType::OpenZeppelin; "cairo_1_class_hash")] #[test_case(READY_CLASS_HASH, AccountType::Ready; "READY_CLASS_HASH")] #[test_case(BRAAVOS_CLASS_HASH, AccountType::Braavos; "braavos_class_hash")] #[tokio::test] async fn test_happy_case(class_hash: Felt, account_type: AccountType) { let contract_path = duplicate_contract_directory_with_salt( CONTRACTS_DIR.to_string() + "/map", "put", &class_hash.to_string(), ); let tempdir = create_and_deploy_account(class_hash, account_type).await; join_tempdirs(&contract_path, &tempdir); let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "--json", "declare", "--url", URL, "--contract-name", "Map", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success().get_output().stdout.clone(); let hash = get_transaction_hash(&output); let receipt = get_transaction_receipt(hash).await; assert!(matches!(receipt, Declare(_))); } #[tokio::test] async fn test_contract_with_constructor_params() { let contract_path = duplicate_contract_directory_with_salt( CONTRACTS_DIR.to_string() + "/contract_with_constructor_params", "put", "human_readable", ); let tempdir = create_and_deploy_oz_account().await; join_tempdirs(&contract_path, &tempdir); let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "declare", "--url", URL, "--contract-name", "ContractWithConstructorParams", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r" Success: Declaration completed Class Hash: 0x[..] Transaction Hash: 0x[..] To see declaration details, visit: class: https://[..] transaction: https://[..] To deploy a contract of this class, replace the placeholders in `--arguments` with your actual values, then run: sncast --accounts-file accounts.json --account my_account deploy --class-hash 0x[..] --arguments ', ' --url http://127.0.0.1:5055/rpc " }, ); } #[test_case(FeeArgs{ max_fee: Some(NonZeroFelt::try_from(Felt::from(1_000_000_000_000_000_000_000_000_u128)).unwrap()), l1_data_gas: None, l1_data_gas_price: None, l1_gas: None, l1_gas_price: None, l2_gas: None, l2_gas_price: None, tip: Some(100_000), estimate_tip: false, }; "max_fee")] #[test_case(FeeArgs{ max_fee: None, l1_data_gas: Some(100_000), l1_data_gas_price: Some(10_000_000_000_000), l1_gas: Some(100_000), l1_gas_price: Some(10_000_000_000_000), l2_gas: Some(1_000_000_000), l2_gas_price: Some(100_000_000_000_000_000_000), tip: None, estimate_tip: false, }; "resource_bounds")] #[tokio::test] async fn test_happy_case_different_fees(fee_args: FeeArgs) { let contract_path = duplicate_contract_directory_with_salt( CONTRACTS_DIR.to_string() + "/map", "put", &format!( "{}{}{}{}{}{}{}", fee_args.max_fee.map_or(Felt::from(0), Felt::from), fee_args.l1_data_gas.unwrap_or(1), fee_args.l1_data_gas_price.unwrap_or(2), fee_args.l1_gas.unwrap_or(3), fee_args.l1_gas_price.unwrap_or(4), fee_args.l2_gas.unwrap_or(5), fee_args.l2_gas_price.unwrap_or(6) ), ); let tempdir = create_and_deploy_oz_account().await; join_tempdirs(&contract_path, &tempdir); let mut args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "--json", "declare", "--url", URL, "--contract-name", "Map", ]; let options = [ ( "--max-fee", fee_args.max_fee.map(Felt::from).map(|x| x.to_string()), ), ("--l1-data-gas", fee_args.l1_data_gas.map(|x| x.to_string())), ( "--l1-data-gas-price", fee_args.l1_data_gas_price.map(|x| x.to_string()), ), ("--l1-gas", fee_args.l1_gas.map(|x| x.to_string())), ( "--l1-gas-price", fee_args.l1_gas_price.map(|x| x.to_string()), ), ("--l2-gas", fee_args.l2_gas.map(|x| x.to_string())), ( "--l2-gas-price", fee_args.l2_gas_price.map(|x| x.to_string()), ), ("--tip", fee_args.tip.map(|x| x.to_string())), ]; for &(key, ref value) in &options { if let Some(val) = value.as_ref() { args.push(key); args.push(val); } } let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success().get_output().stdout.clone(); let hash = get_transaction_hash(&output); let Declare(receipt) = get_transaction_receipt(hash).await else { panic!("Should be Declare receipt"); }; assert_eq!( receipt.execution_result.status(), TransactionExecutionStatus::Succeeded ); let Transaction::Declare(DeclareTransaction::V3(tx)) = get_transaction_by_hash(hash).await else { panic!("Expected Declare V3 transaction") }; assert_eq!(tx.tip, fee_args.tip.unwrap_or(0)); } #[tokio::test] async fn test_happy_case_specify_package() { let tempdir = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/multiple_packages"); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user8", "--json", "declare", "--url", URL, "--contract-name", "supercomplexcode", "--package", "main_workspace", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success().get_output().stdout.clone(); let hash = get_transaction_hash(&output); let receipt = get_transaction_receipt(hash).await; assert!(matches!(receipt, Declare(_))); } #[tokio::test] async fn test_contract_already_declared() { let tempdir = duplicate_contract_directory_with_salt( CONTRACTS_DIR.to_string() + "/map", "put", "8512851", ); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "declare", "--url", URL, "--contract-name", "Map", ]; runner(&args).current_dir(tempdir.path()).assert().success(); let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: declare Error: Contract with class hash 0x0[..] is already declared "}, ); } #[tokio::test] async fn test_contract_already_declared_estimate_fee() { let contract_path = duplicate_contract_directory_with_salt( CONTRACTS_DIR.to_string() + "/map", "put", "74362345", ); let tempdir = create_and_deploy_oz_account().await; join_tempdirs(&contract_path, &tempdir); let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "declare", "--url", URL, "--contract-name", "Map", ]; // Commented out because we explicitly do not want to set any resource bounds. // Transaction has to go through estimate fee endpoint first because it throws different errors // than the normal declare endpoint and we want to test them. runner(&args).current_dir(tempdir.path()).assert().success(); let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: declare Error: Contract with class hash 0x0[..] is already declared "}, ); } #[tokio::test] async fn test_invalid_nonce() { let contract_path = duplicate_contract_directory_with_salt(CONTRACTS_DIR.to_string() + "/map", "put", "1123"); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user8", "declare", "--url", URL, "--contract-name", "Map", "--nonce", "12345", ]; let snapbox = runner(&args).current_dir(contract_path.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r#" Command: declare Error: Transaction execution error = TransactionExecutionErrorData { transaction_index: 0, execution_error: Message("Account transaction nonce is invalid.") } "#}, ); } #[tokio::test] async fn test_wrong_contract_name_passed() { let tempdir = duplicate_contract_directory_with_salt( CONTRACTS_DIR.to_string() + "/map", "put", "521754725", ); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "declare", "--url", URL, "--contract-name", "nonexistent", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: declare Error: Failed to find nonexistent artifact in starknet_artifacts.json file[..] "}, ); } #[test] fn test_scarb_build_fails_when_wrong_cairo_path() { let tempdir = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/build_fails"); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "declare", "--url", URL, "--contract-name", "BuildFails", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().failure(); assert_stderr_contains( output, "Failed to build contract: Failed to build using scarb; `scarb` exited with error", ); } #[should_panic(expected = "Path to Scarb.toml manifest does not exist")] #[test] fn test_scarb_build_fails_scarb_toml_does_not_exist() { let tempdir = copy_directory_to_tempdir(CONTRACTS_DIR); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "declare", "--url", URL, "--contract-name", "BuildFails", ]; runner(&args).current_dir(tempdir.path()).assert().success(); } #[test] fn test_scarb_build_fails_manifest_does_not_exist() { let tempdir = copy_directory_to_tempdir(CONTRACTS_DIR); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "declare", "--url", URL, "--contract-name", "BuildFails", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().failure(); assert_stderr_contains( output, indoc! {r" Error: Path to Scarb.toml manifest does not exist =[..] "}, ); } #[test] fn test_too_low_gas() { let contract_path = duplicate_contract_directory_with_salt( CONTRACTS_DIR.to_string() + "/map", "put", "2451825", ); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user6", "--wait", "declare", "--url", URL, "--contract-name", "Map", "--l1-gas", "1", "--l2-gas", "1", "--l1-data-gas", "1", ]; let snapbox = runner(&args).current_dir(contract_path.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: declare Error: The transaction's resources don't cover validation or the minimal transaction fee "}, ); } #[should_panic(expected = "Make sure you have enabled sierra code generation in Scarb.toml")] #[test] fn test_scarb_no_sierra_artifact() { let tempdir = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/no_sierra"); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "declare", "--url", URL, "--contract-name", "minimal_contract", ]; runner(&args).current_dir(tempdir.path()).assert().success(); } #[test] fn test_scarb_no_casm_artifact() { let tempdir = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/no_casm"); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "declare", "--url", URL, "--contract-name", "minimal_contract", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r" Success: Declaration completed Class Hash: [..] Transaction Hash: [..] To deploy a contract of this class, run: sncast --accounts-file [..]accounts.json --account user1 deploy --class-hash 0x[..] --url http://127.0.0.1:5055/rpc "}, ); } #[tokio::test] async fn test_many_packages_default() { let tempdir = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/multiple_packages"); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user8", "--json", "declare", "--url", URL, "--contract-name", "supercomplexcode2", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().failure(); assert_stderr_contains( output, "Error: More than one package found in scarb metadata - specify package using --package flag", ); } #[tokio::test] async fn test_workspaces_package_specified_virtual_fibonacci() { let tempdir = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/virtual_workspace"); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user8", "--json", "declare", "--url", URL, "--package", "cast_fibonacci", "--contract-name", "FibonacciContract", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); let output = snapbox.assert().success().get_output().clone(); let output = output.stdout.clone(); let hash = get_transaction_hash(&output); let receipt = get_transaction_receipt(hash).await; assert!(matches!(receipt, Declare(_))); } #[tokio::test] async fn test_workspaces_package_no_contract() { let tempdir = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/virtual_workspace"); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user8", "declare", "--url", URL, "--package", "cast_addition", "--contract-name", "whatever", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: declare Error: Failed to find whatever artifact in starknet_artifacts.json file[..] "}, ); } #[tokio::test] async fn test_no_scarb_profile() { let contract_path = duplicate_contract_directory_with_salt(CONTRACTS_DIR.to_string() + "/map", "put", "694215"); fs::copy( "tests/data/files/correct_snfoundry.toml", contract_path.path().join(CONFIG_FILENAME), ) .expect("Failed to copy config file to temp dir"); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--profile", "profile5", "declare", "--url", URL, "--contract-name", "Map", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(contract_path.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {" [..] [WARNING] Profile profile5 does not exist in scarb, using 'release' profile. Success: Declaration completed Class Hash: [..] Transaction Hash: [..] To see declaration details, visit: class: [..] transaction: [..] To deploy a contract of this class, run: sncast --accounts-file [..]accounts.json --account user8 deploy --class-hash 0x[..] --url http://127.0.0.1:5055/rpc "}, ); } #[tokio::test] async fn test_no_explorer_links_on_localhost() { let contract_path = duplicate_contract_directory_with_salt( CONTRACTS_DIR.to_string() + "/map", "put", "localhost_test", ); let tempdir = create_and_deploy_oz_account().await; join_tempdirs(&contract_path, &tempdir); let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "declare", "--url", "http://localhost:5055/rpc", "--contract-name", "Map", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert!( !output .as_stdout() .contains("To see declaration details, visit:") ); } ================================================ FILE: crates/sncast/tests/e2e/declare_from.rs ================================================ use crate::helpers::constants::{ CONTRACTS_DIR, MAP_CONTRACT_CLASS_HASH_SEPOLIA, SEPOLIA_RPC_URL, URL, }; use crate::helpers::fixtures::{ create_and_deploy_oz_account, duplicate_contract_directory_with_salt, get_accounts_path, join_tempdirs, }; use crate::helpers::runner::runner; use indoc::indoc; use scarb_api::ScarbCommand; use shared::test_utils::output_assert::{assert_stderr_contains, assert_stdout_contains}; use std::fs; use std::process::Stdio; use tempfile::tempdir; #[tokio::test] async fn test_happy_case() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let example_contract_class_hash_sepolia = "0x66802613e2cd02ea21430a56181d9ee83c54d4ccdc45efa497d41fe1dc55a0e"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "declare-from", "--class-hash", example_contract_class_hash_sepolia, "--source-url", SEPOLIA_RPC_URL, "--url", URL, ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r" Success: Declaration completed Class Hash: 0x66802613e2cd02ea21430a56181d9ee83c54d4ccdc45efa497d41fe1dc55a0e Transaction Hash: 0x[..] To see declaration details, visit: class: https://[..] transaction: https://[..] " }, ); } #[tokio::test] async fn test_happy_case_with_block_id() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let example_b_contract_class_hash_sepolia = "0x3de1a95e27b385c882c79355ca415915989e71f67c0f6f8ce146d4bcee7163c"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user2", "declare-from", "--class-hash", example_b_contract_class_hash_sepolia, "--source-url", SEPOLIA_RPC_URL, "--url", URL, "--block-id", "latest", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r" Success: Declaration completed Class Hash: 0x3de1a95e27b385c882c79355ca415915989e71f67c0f6f8ce146d4bcee7163c Transaction Hash: 0x[..] To see declaration details, visit: class: https://[..] transaction: https://[..] " }, ); } #[tokio::test] async fn test_contract_already_declared() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user3", "declare-from", "--class-hash", MAP_CONTRACT_CLASS_HASH_SEPOLIA, "--source-url", SEPOLIA_RPC_URL, "--url", URL, ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: declare-from Error: Contract with class hash 0x0[..] is already declared "}, ); } #[tokio::test] async fn test_class_hash_does_not_exist_on_source_network() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "declare-from", "--class-hash", "0x1", "--source-url", SEPOLIA_RPC_URL, "--url", URL, ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: declare-from Error: Provided class hash does not exist "}, ); } #[tokio::test] async fn test_source_rpc_args_not_passed() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "declare-from", "--class-hash", "0x1", "--url", URL, ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().failure(); assert_stderr_contains( output, indoc! {r" Error: Either `--source-network` or `--source-url` must be provided "}, ); } #[tokio::test] async fn test_invalid_block_id() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "declare-from", "--class-hash", "0x1", "--url", URL, "--block-id", "0x10101", ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().failure(); assert_stderr_contains( output, indoc! {r" Error: Either `--source-network` or `--source-url` must be provided "}, ); } #[tokio::test] async fn test_declare_from_sierra_happy_case() { let contract_path = duplicate_contract_directory_with_salt( CONTRACTS_DIR.to_string() + "/map", "put", "declare_from_file_happy", ); let tempdir = create_and_deploy_oz_account().await; join_tempdirs(&contract_path, &tempdir); let build_output = ScarbCommand::new() .arg("build") .current_dir(tempdir.path()) .command() .stderr(Stdio::inherit()) .stdout(Stdio::inherit()) .output() .expect("Failed to run `scarb build`"); assert!(build_output.status.success(), "`scarb build` failed"); let sierra_path = tempdir .path() .join("target/dev/map_Map.contract_class.json"); assert!( sierra_path.exists(), "sierra artifact not found at {sierra_path:?}" ); let sierra_path = sierra_path.to_str().unwrap(); let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "declare-from", "--sierra-file", sierra_path, "--url", URL, ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r" Success: Declaration completed Class Hash: 0x[..] Transaction Hash: 0x[..] To see declaration details, visit: class: https://[..] transaction: https://[..] "}, ); } #[tokio::test] async fn test_declare_from_sierra_does_not_exist() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "declare-from", "--sierra-file", "/nonexistent/path/contract.contract_class.json", "--url", URL, ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: declare-from Error: Failed to read sierra file at [..]contract_class.json: No such file or directory [..] "}, ); } #[tokio::test] async fn test_declare_from_sierra_invalid_json() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let invalid_sierra_path = temp_dir.path().join("invalid_contract_class.json"); fs::write(&invalid_sierra_path, r#"{"not": "a valid sierra"}"#).unwrap(); let invalid_sierra_path = invalid_sierra_path.to_str().unwrap().to_string(); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "declare-from", "--sierra-file", invalid_sierra_path.as_str(), "--url", URL, ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: declare-from Error: Failed to parse sierra file: missing field `sierra_program` at line 1 column 25 "}, ); } #[tokio::test] async fn test_declare_from_sierra_already_declared() { let contract_path = duplicate_contract_directory_with_salt( CONTRACTS_DIR.to_string() + "/map", "put", "declare_from_file_already_declared", ); let tempdir = create_and_deploy_oz_account().await; join_tempdirs(&contract_path, &tempdir); let build_output = ScarbCommand::new() .arg("build") .current_dir(tempdir.path()) .command() .output() .expect("Failed to run `scarb build`"); assert!(build_output.status.success(), "`scarb build` failed"); let sierra_path = tempdir .path() .join("target/dev/map_Map.contract_class.json"); let sierra_path = sierra_path.to_str().unwrap(); let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "declare-from", "--sierra-file", sierra_path, "--url", URL, ]; runner(&args).current_dir(tempdir.path()).assert().success(); let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "declare-from", "--sierra-file", sierra_path, "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: declare-from Error: Contract with class hash 0x0[..] is already declared "}, ); } #[test] fn test_declare_from_requires_sierra_file_or_class_hash() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let args = vec!["declare-from", "--url", URL]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().failure(); assert_stderr_contains( output, indoc! {r" error: the following required arguments were not provided: <--sierra-file |--class-hash > Usage: sncast declare-from --url <--sierra-file |--class-hash > For more information, try '--help'. "}, ); } #[test] fn test_declare_from_conflicting_contract_source() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let args = vec![ "declare-from", "--class-hash", "0x1", "--sierra-file", "path/to/sierra.json", "--url", URL, ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().failure(); assert_stderr_contains( output, indoc! {r" error: the argument '--class-hash ' cannot be used with '--sierra-file ' Usage: sncast declare-from --url <--sierra-file |--class-hash > For more information, try '--help'. "}, ); } ================================================ FILE: crates/sncast/tests/e2e/deploy.rs ================================================ use crate::helpers::constants::{ ACCOUNT, ACCOUNT_FILE_PATH, CONSTRUCTOR_WITH_PARAMS_CONTRACT_CLASS_HASH_SEPOLIA, CONTRACTS_DIR, DEVNET_OZ_CLASS_HASH_CAIRO_0, MAP_CONTRACT_CLASS_HASH_SEPOLIA, URL, }; use crate::helpers::fixtures::{ create_and_deploy_account, create_and_deploy_oz_account, create_test_provider, duplicate_contract_directory_with_salt, get_transaction_by_hash, get_transaction_hash, get_transaction_receipt, join_tempdirs, }; use crate::helpers::runner::runner; use crate::helpers::shell::os_specific_shell; use camino::Utf8PathBuf; use indoc::indoc; use shared::test_utils::output_assert::{AsOutput, assert_stderr_contains, assert_stdout_contains}; use snapbox::cargo_bin; use sncast::AccountType; use sncast::helpers::constants::OZ_CLASS_HASH; use sncast::helpers::fee::FeeArgs; use starknet_rust::core::types::TransactionReceipt::Invoke; use starknet_rust::core::types::{ BlockId, BlockTag, InvokeTransaction, Transaction, TransactionExecutionStatus, }; use starknet_rust::providers::Provider; use starknet_types_core::felt::{Felt, NonZeroFelt}; use test_case::test_case; use toml::Value; #[tokio::test] async fn test_happy_case_human_readable() { let tempdir = create_and_deploy_account(OZ_CLASS_HASH, AccountType::OpenZeppelin).await; let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "deploy", "--url", URL, "--class-hash", MAP_CONTRACT_CLASS_HASH_SEPOLIA, "--salt", "0x2", "--unique", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! { " Success: Deployment completed Contract Address: 0x0[..] Transaction Hash: 0x0[..] To see deployment details, visit: contract: [..] transaction: [..] " }, ); } #[test_case(DEVNET_OZ_CLASS_HASH_CAIRO_0.parse().unwrap(), AccountType::OpenZeppelin; "cairo_0_class_hash")] #[test_case(OZ_CLASS_HASH, AccountType::OpenZeppelin; "cairo_1_class_hash")] #[test_case(sncast::helpers::constants::READY_CLASS_HASH, AccountType::Ready; "READY_CLASS_HASH")] #[test_case(sncast::helpers::constants::BRAAVOS_CLASS_HASH, AccountType::Braavos; "braavos_class_hash")] #[tokio::test] async fn test_happy_case(class_hash: Felt, account_type: AccountType) { let tempdir = create_and_deploy_account(class_hash, account_type).await; let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "--json", "deploy", "--url", URL, "--class-hash", MAP_CONTRACT_CLASS_HASH_SEPOLIA, "--salt", "0x2", "--unique", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let hash = get_transaction_hash(&snapbox.assert().success().get_output().stdout.clone()); let receipt = get_transaction_receipt(hash).await; assert!(matches!(receipt, Invoke(_))); } #[test_case(FeeArgs{ max_fee: Some(NonZeroFelt::try_from(Felt::from(1_000_000_000_000_000_000_000_000_u128)).unwrap()), l1_data_gas: None, l1_data_gas_price: None, l1_gas: None, l1_gas_price: None, l2_gas: None, l2_gas_price: None, tip: None, estimate_tip: false, }; "max_fee")] #[test_case(FeeArgs{ max_fee: None, l1_data_gas: Some(100_000), l1_data_gas_price: Some(10_000_000_000_000), l1_gas: Some(100_000), l1_gas_price: Some(10_000_000_000_000), l2_gas: Some(1_000_000_000), l2_gas_price: Some(100_000_000_000_000_000_000), tip: Some(100_000), estimate_tip: false, }; "resource_bounds")] #[tokio::test] async fn test_happy_case_different_fees(fee_args: FeeArgs) { let tempdir = create_and_deploy_oz_account().await; let mut args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "--json", "deploy", "--url", URL, "--class-hash", MAP_CONTRACT_CLASS_HASH_SEPOLIA, "--salt", "0x2", "--unique", ]; let options = [ ( "--max-fee", fee_args.max_fee.map(Felt::from).map(|x| x.to_string()), ), ("--l1-data-gas", fee_args.l1_data_gas.map(|x| x.to_string())), ( "--l1-data-gas-price", fee_args.l1_data_gas_price.map(|x| x.to_string()), ), ("--l1-gas", fee_args.l1_gas.map(|x| x.to_string())), ( "--l1-gas-price", fee_args.l1_gas_price.map(|x| x.to_string()), ), ("--l2-gas", fee_args.l2_gas.map(|x| x.to_string())), ( "--l2-gas-price", fee_args.l2_gas_price.map(|x| x.to_string()), ), ("--tip", fee_args.tip.map(|x| x.to_string())), ]; for &(key, ref value) in &options { if let Some(val) = value.as_ref() { args.push(key); args.push(val); } } let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success().get_output().stdout.clone(); let hash = get_transaction_hash(&output); let Invoke(receipt) = get_transaction_receipt(hash).await else { panic!("Should be Invoke receipt"); }; assert_eq!( receipt.execution_result.status(), TransactionExecutionStatus::Succeeded ); let Transaction::Invoke(InvokeTransaction::V3(tx)) = get_transaction_by_hash(hash).await else { panic!("Expected Invoke V3 transaction") }; assert_eq!(tx.tip, fee_args.tip.unwrap_or(0)); } #[tokio::test] async fn test_happy_case_with_constructor() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--account", "user4", "--json", "deploy", "--url", URL, "--constructor-calldata", "0x1", "0x1", "0x0", "--class-hash", CONSTRUCTOR_WITH_PARAMS_CONTRACT_CLASS_HASH_SEPOLIA, ]; let snapbox = runner(&args); let output = snapbox.assert().success().get_output().stdout.clone(); let hash = get_transaction_hash(&output); let receipt = get_transaction_receipt(hash).await; assert!(matches!(receipt, Invoke(_))); } #[tokio::test] async fn test_happy_case_with_constructor_cairo_expression_calldata() { let tempdir = create_and_deploy_oz_account().await; let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "--json", "deploy", "--url", URL, "--arguments", "0x420, 0x2137_u256", "--class-hash", CONSTRUCTOR_WITH_PARAMS_CONTRACT_CLASS_HASH_SEPOLIA, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success().get_output().stdout.clone(); let hash = get_transaction_hash(&output); let receipt = get_transaction_receipt(hash).await; assert!(matches!(receipt, Invoke(_))); } #[test] fn test_wrong_calldata() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--account", "user9", "deploy", "--url", URL, "--class-hash", CONSTRUCTOR_WITH_PARAMS_CONTRACT_CLASS_HASH_SEPOLIA, "--constructor-calldata", "0x1 0x2 0x3 0x4", ]; let snapbox = runner(&args); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: deploy Error: Transaction execution error [..]Input too long for arguments[..] "}, ); } #[test] fn test_class_hash_with_package() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--account", "user9", "deploy", "--url", URL, "--class-hash", CONSTRUCTOR_WITH_PARAMS_CONTRACT_CLASS_HASH_SEPOLIA, "--package", "my_package", ]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains( output, indoc! {r" error: the argument '--class-hash ' cannot be used with '--package ' "}, ); } #[tokio::test] async fn test_contract_not_declared() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--account", ACCOUNT, "deploy", "--url", URL, "--class-hash", "0x1", ]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains( output, "Error: An error occurred in the called contract[..]Class with hash[..]is not declared[..]", ); } #[test] fn test_contract_already_deployed() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--account", "user1", "deploy", "--url", URL, "--class-hash", MAP_CONTRACT_CLASS_HASH_SEPOLIA, "--salt", "0x1", ]; let snapbox = runner(&args); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: deploy Error: Transaction execution error [..] contract already deployed at address [..] "}, ); } #[test] fn test_too_low_gas() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--account", "user7", "--wait", "deploy", "--url", URL, "--class-hash", MAP_CONTRACT_CLASS_HASH_SEPOLIA, "--salt", "0x2", "--unique", "--l1-gas", "1", "--l2-gas", "1", "--l1-data-gas", "1", ]; let snapbox = runner(&args); let output = snapbox.assert().success(); // TODO Check extra blank line println!("====\n{}\n====", output.as_stderr()); assert_stderr_contains( output, indoc! {r" Command: deploy Error: The transaction's resources don't cover validation or the minimal transaction fee "}, ); } #[tokio::test] async fn test_happy_case_shell() { let tempdir = create_and_deploy_oz_account().await; let binary_path = cargo_bin!("sncast"); let command = os_specific_shell(&Utf8PathBuf::from("tests/shell/deploy")); let snapbox = command .current_dir(tempdir.path()) .arg(binary_path) .arg(URL) .arg(CONSTRUCTOR_WITH_PARAMS_CONTRACT_CLASS_HASH_SEPOLIA); snapbox.assert().success(); } #[tokio::test] async fn test_json_output_format() { let tempdir = create_and_deploy_account(OZ_CLASS_HASH, AccountType::OpenZeppelin).await; let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "--json", "deploy", "--url", URL, "--class-hash", MAP_CONTRACT_CLASS_HASH_SEPOLIA, "--salt", "0x2", "--unique", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r#" {"command":"deploy","contract_address":"0x[..]","transaction_hash":"0x[..]","type":"response"} {"links":"contract: https://sepolia.voyager.online/contract/0x[..]\ntransaction: https://sepolia.voyager.online/tx/0x[..]\n","title":"deployment","type":"notification"} "#}, ); } #[tokio::test] async fn test_happy_case_with_declare() { let contract_path = duplicate_contract_directory_with_salt( CONTRACTS_DIR.to_string() + "/map", "put", "with_declare", ); let tempdir = create_and_deploy_oz_account().await; join_tempdirs(&contract_path, &tempdir); let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "deploy", "--url", URL, "--contract-name", "Map", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! { " Success: Deployment completed Contract Address: 0x0[..] Class Hash: 0x0[..] Declare Transaction Hash: 0x0[..] Deploy Transaction Hash: 0x0[..] To see deployment details, visit: contract: [..] class: [..] deploy transaction: [..] declare transaction: [..] " }, ); } #[tokio::test] async fn test_happy_case_with_already_declared() { let contract_path = duplicate_contract_directory_with_salt( CONTRACTS_DIR.to_string() + "/map", "put", "with_redeclare", ); let tempdir = create_and_deploy_oz_account().await; join_tempdirs(&contract_path, &tempdir); { // Declare the contract first let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "declare", "--url", URL, "--contract-name", "Map", ]; runner(&args).current_dir(tempdir.path()).assert().success(); } // Deploy the contract with declaring let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "deploy", "--url", URL, "--contract-name", "Map", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! { " Success: Deployment completed Contract Address: 0x0[..] Transaction Hash: 0x0[..] To see deployment details, visit: contract: [..] transaction: [..] " }, ); } #[tokio::test] async fn test_happy_case_with_declare_nonce() { let contract_path = duplicate_contract_directory_with_salt( CONTRACTS_DIR.to_string() + "/map", "put", "with_declare_nonce", ); let tempdir = create_and_deploy_oz_account().await; join_tempdirs(&contract_path, &tempdir); let nonce = { // Get nonce let provider = create_test_provider(); let args = vec![ "--accounts-file", "accounts.json", "--json", "account", "list", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); let value: Value = serde_json::from_str(output.as_stdout()).unwrap(); let account_address = value["my_account"]["address"].as_str().unwrap(); provider .get_nonce( BlockId::Tag(BlockTag::Latest), Felt::from_hex(account_address).unwrap(), ) .await .unwrap() .to_string() }; let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "deploy", "--url", URL, "--contract-name", "Map", "--nonce", nonce.as_str(), ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! { " Success: Deployment completed Contract Address: 0x0[..] Class Hash: 0x0[..] Declare Transaction Hash: 0x0[..] Deploy Transaction Hash: 0x0[..] To see deployment details, visit: contract: [..] class: [..] deploy transaction: [..] declare transaction: [..] " }, ); } #[tokio::test] async fn test_deploy_with_declare_invalid_nonce() { let contract_path = duplicate_contract_directory_with_salt( CONTRACTS_DIR.to_string() + "/map", "put", "with_redeclare", ); let tempdir = create_and_deploy_oz_account().await; join_tempdirs(&contract_path, &tempdir); let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "deploy", "--url", URL, "--constructor-calldata", "0x1", "0x1", "0x0", "--contract-name", "Map", "--nonce", "0x123456", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r#" Command: deploy Error: Transaction execution error = TransactionExecutionErrorData { transaction_index: 0, execution_error: Message("Account transaction nonce is invalid.") } "#}, ); } ================================================ FILE: crates/sncast/tests/e2e/devnet_accounts.rs ================================================ use crate::helpers::constants::{MAP_CONTRACT_ADDRESS_SEPOLIA, SEPOLIA_RPC_URL, URL}; use crate::helpers::fixtures::copy_file; use crate::helpers::runner::runner; use indoc::indoc; use shared::test_utils::output_assert::{assert_stderr_contains, assert_stdout_contains}; use tempfile::tempdir; use test_case::test_case; #[test_case(1)] #[test_case(20)] #[tokio::test] pub async fn happy_case(account_number: u8) { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let account = format!("devnet-{account_number}"); let args = vec![ "--account", &account, "invoke", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "put", "--calldata", "0x1 0x2", ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! { " Success: Invoke completed Transaction Hash: 0x0[..] " }, ); } #[test_case(0)] #[test_case(21)] #[tokio::test] pub async fn account_number_out_of_range(account_number: u8) { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let account = format!("devnet-{account_number}"); let args = vec![ "--account", &account, "invoke", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "put", "--calldata", "0x1 0x2", ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().failure(); assert_stderr_contains( output, indoc! { " Error: Devnet account number must be between 1 and 20 " }, ); } #[tokio::test] pub async fn account_name_already_exists() { let accounts_file = "accounts.json"; let temp_dir = tempdir().expect("Unable to create a temporary directory"); copy_file( "tests/data/accounts/accounts.json", temp_dir.path().join(accounts_file), ); let args = vec![ "--accounts-file", accounts_file, "--account", "devnet-1", "invoke", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "put", "--calldata", "0x1 0x2", ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! { " [WARNING] Using account devnet-1 from accounts file accounts.json. To use an inbuilt devnet account, please rename your existing account or use an account with a different number. Success: Invoke completed Transaction Hash: 0x0[..] " }, ); } #[tokio::test] pub async fn use_devnet_account_with_network_not_being_devnet() { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let args = vec![ "--account", "devnet-1", "invoke", "--url", SEPOLIA_RPC_URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "put", "--calldata", "0x1 0x2", ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().failure(); assert_stderr_contains( output, format! {"Error: Node at {SEPOLIA_RPC_URL} is not responding to the Devnet health check (GET `/is_alive`). It may not be a Devnet instance or it may be down." }, ); } #[test_case("mainnet")] #[test_case("sepolia")] #[tokio::test] pub async fn use_devnet_account_with_network_flags(network: &str) { let temp_dir = tempdir().expect("Unable to create a temporary directory"); let args = vec![ "--account", "devnet-1", "invoke", "--network", network, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "put", "--calldata", "0x1 0x2", ]; let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().failure(); assert_stderr_contains( output, format! {"Error: Devnet accounts cannot be used with `--network {network}`" }, ); } ================================================ FILE: crates/sncast/tests/e2e/fee.rs ================================================ use crate::helpers::constants::{ACCOUNT_FILE_PATH, MAP_CONTRACT_ADDRESS_SEPOLIA, URL}; use crate::helpers::runner::runner; use indoc::indoc; use shared::test_utils::output_assert::assert_stderr_contains; #[test] fn test_max_fee_used_with_other_args() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--account", "user11", "--wait", "invoke", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "put", "--calldata", "0x1", "0x2", "--max-fee", "1", "--l1-gas", "1", "--l1-gas-price", "1", "--l2-gas", "1", "--l2-gas-price", "1", "--l1-data-gas", "1", "--l1-data-gas-price", "1", ]; let snapbox = runner(&args); let output = snapbox.assert(); assert_stderr_contains( output, indoc! {r" error: the argument '--max-fee ' cannot be used with: --l1-gas --l1-gas-price --l2-gas --l2-gas-price --l1-data-gas --l1-data-gas-price "}, ); } ================================================ FILE: crates/sncast/tests/e2e/invoke.rs ================================================ use crate::helpers::constants::{ ACCOUNT, ACCOUNT_FILE_PATH, DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA, DEVNET_OZ_CLASS_HASH_CAIRO_0, MAP_CONTRACT_ADDRESS_SEPOLIA, URL, }; use crate::helpers::fixtures::{ create_and_deploy_account, create_and_deploy_oz_account, get_transaction_by_hash, get_transaction_hash, get_transaction_receipt, }; use crate::helpers::runner::runner; use crate::helpers::shell::os_specific_shell; use camino::Utf8PathBuf; use indoc::indoc; use shared::test_utils::output_assert::{assert_stderr_contains, assert_stdout_contains}; use snapbox::cargo_bin; use sncast::AccountType; use sncast::helpers::constants::{BRAAVOS_CLASS_HASH, OZ_CLASS_HASH, READY_CLASS_HASH}; use sncast::helpers::fee::FeeArgs; use starknet_rust::core::types::TransactionReceipt::Invoke; use starknet_rust::core::types::{InvokeTransaction, Transaction, TransactionExecutionStatus}; use starknet_types_core::felt::{Felt, NonZeroFelt}; use test_case::test_case; #[tokio::test] async fn test_happy_case_human_readable() { let tempdir = create_and_deploy_account(OZ_CLASS_HASH, AccountType::OpenZeppelin).await; let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "invoke", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "put", "--calldata", "0x1 0x2", ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! { " Success: Invoke completed Transaction Hash: 0x0[..] To see invocation details, visit: transaction: [..] " }, ); } #[test_case(DEVNET_OZ_CLASS_HASH_CAIRO_0.parse().unwrap(), AccountType::OpenZeppelin; "cairo_0_class_hash")] #[test_case(OZ_CLASS_HASH, AccountType::OpenZeppelin; "cairo_1_class_hash")] #[test_case(READY_CLASS_HASH, AccountType::Ready; "READY_CLASS_HASH")] #[test_case(BRAAVOS_CLASS_HASH, AccountType::Braavos; "braavos_class_hash")] #[tokio::test] async fn test_happy_case(class_hash: Felt, account_type: AccountType) { let tempdir = create_and_deploy_account(class_hash, account_type).await; let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "--json", "invoke", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "put", "--calldata", "0x1 0x2", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); let stdout = output.get_output().stdout.clone(); let hash = get_transaction_hash(&stdout); let receipt = get_transaction_receipt(hash).await; assert!(matches!(receipt, Invoke(_))); } #[test_case(FeeArgs{ max_fee: Some(NonZeroFelt::try_from(Felt::from(1_000_000_000_000_000_000_000_000_u128)).unwrap()), l1_data_gas: None, l1_data_gas_price: None, l1_gas: None, l1_gas_price: None, l2_gas: None, l2_gas_price: None, tip: None, estimate_tip: false, }; "max_fee")] #[test_case(FeeArgs{ max_fee: None, l1_data_gas: Some(100_000), l1_data_gas_price: Some(10_000_000_000_000), l1_gas: Some(100_000), l1_gas_price: Some(10_000_000_000_000), l2_gas: Some(1_000_000_000), l2_gas_price: Some(100_000_000_000_000_000_000), tip: Some(100_000_000), estimate_tip: false, }; "resource_bounds")] #[tokio::test] async fn test_happy_case_different_fees(fee_args: FeeArgs) { let tempdir = create_and_deploy_oz_account().await; let mut args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "--json", "invoke", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "put", "--calldata", "0x1 0x2", ]; let options = [ ( "--max-fee", fee_args.max_fee.map(Felt::from).map(|x| x.to_string()), ), ("--l1-data-gas", fee_args.l1_data_gas.map(|x| x.to_string())), ( "--l1-data-gas-price", fee_args.l1_data_gas_price.map(|x| x.to_string()), ), ("--l1-gas", fee_args.l1_gas.map(|x| x.to_string())), ( "--l1-gas-price", fee_args.l1_gas_price.map(|x| x.to_string()), ), ("--l2-gas", fee_args.l2_gas.map(|x| x.to_string())), ( "--l2-gas-price", fee_args.l2_gas_price.map(|x| x.to_string()), ), ("--tip", fee_args.tip.map(|x| x.to_string())), ]; for &(key, ref value) in &options { if let Some(val) = value.as_ref() { args.push(key); args.push(val); } } let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success().get_output().stdout.clone(); let hash = get_transaction_hash(&output); let Invoke(receipt) = get_transaction_receipt(hash).await else { panic!("Should be Invoke receipt"); }; assert_eq!( receipt.execution_result.status(), TransactionExecutionStatus::Succeeded ); let Transaction::Invoke(InvokeTransaction::V3(tx)) = get_transaction_by_hash(hash).await else { panic!("Expected Invoke V3 transaction") }; assert_eq!(tx.tip, fee_args.tip.unwrap_or(0)); } #[tokio::test] async fn test_contract_does_not_exist() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--account", ACCOUNT, "invoke", "--url", URL, "--contract-address", "0x1", "--function", "put", ]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains( output, "Error: An error occurred in the called contract[..]Requested contract address[..]is not deployed[..]", ); } #[test] fn test_wrong_function_name() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--account", "user2", "invoke", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "nonexistent_put", ]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains( output, r#"Error: Function with selector "0x2e0f845a8d0319c5c37d558023299beec2a0155d415f41cca140a09e6877c67" not found in ABI of the contract"#, ); } #[test] fn test_wrong_calldata() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--account", "user5", "invoke", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "put", "--calldata", "0x1", ]; let snapbox = runner(&args); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: invoke Error: Transaction execution error [..]Failed to deserialize param #2[..] "}, ); } #[test] fn test_too_low_gas() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--account", "user11", "--wait", "invoke", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "put", "--calldata", "0x1", "0x2", "--l1-gas", "1", "--l1-gas-price", "1", "--l2-gas", "1", "--l2-gas-price", "1", "--l1-data-gas", "1", "--l1-data-gas-price", "1", ]; let snapbox = runner(&args); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: invoke Error: The transaction's resources don't cover validation or the minimal transaction fee "}, ); } #[tokio::test] async fn test_happy_case_cairo_expression_calldata() { let tempdir = create_and_deploy_oz_account().await; let calldata = r"NestedStructWithField { a: SimpleStruct { a: 0x24 }, b: 96 }"; let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "--json", "invoke", "--url", URL, "--contract-address", DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA, "--function", "nested_struct_fn", "--arguments", calldata, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success().get_output().stdout.clone(); let hash = get_transaction_hash(&output); let receipt = get_transaction_receipt(hash).await; assert!(matches!(receipt, Invoke(_))); } #[tokio::test] async fn test_happy_case_shell() { let tempdir = create_and_deploy_oz_account().await; let binary_path = cargo_bin!("sncast"); let command = os_specific_shell(&Utf8PathBuf::from("tests/shell/invoke")); let snapbox = command .current_dir(tempdir.path()) .arg(binary_path) .arg(URL) .arg(DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA); snapbox.assert().success(); } ================================================ FILE: crates/sncast/tests/e2e/ledger/account.rs ================================================ use std::fs; use crate::e2e::ledger::{ BRAAVOS_LEDGER_PATH, LEDGER_ACCOUNT_NAME, LEDGER_PUBLIC_KEY, OZ_LEDGER_PATH, READY_LEDGER_PATH, TEST_LEDGER_PATH, TEST_LEDGER_PATH_STORED, automation, setup_speculos, }; use crate::helpers::constants::URL; use crate::helpers::fixtures::mint_token; use crate::helpers::runner::runner; use camino::Utf8PathBuf; use configuration::test_utils::copy_config_to_tempdir; use conversions::string::IntoHexStr; use indoc::indoc; use serde_json::{json, to_string_pretty}; use shared::test_utils::output_assert::{assert_stderr_contains, assert_stdout_contains}; use snapbox::assert_data_eq; use sncast::helpers::account::load_accounts; use sncast::helpers::constants::{BRAAVOS_CLASS_HASH, OZ_CLASS_HASH, READY_CLASS_HASH}; use speculos_client::AutomationRule; use tempfile::tempdir; use test_case::test_case; #[test_case("oz", "open_zeppelin", OZ_CLASS_HASH.into_hex_string(), 6001, &[automation::APPROVE_PUBLIC_KEY]; "oz_account_type")] #[test_case("ready", "ready", READY_CLASS_HASH.into_hex_string(), 6002, &[automation::APPROVE_PUBLIC_KEY]; "ready_account_type")] // Braavos calls sign_hash twice during fee estimation (tx_hash + aux_hash) because // is_signer_interactive() always returns false — see BraavosAccountFactory::is_signer_interactive. // That means we need ENABLE_BLIND_SIGN + two APPROVE_BLIND_SIGN_HASH after the public key approval. #[test_case( "braavos", "braavos", BRAAVOS_CLASS_HASH.into_hex_string(), 6003, &[ automation::APPROVE_PUBLIC_KEY, automation::ENABLE_BLIND_SIGN, automation::APPROVE_BLIND_SIGN_HASH, // tx_hash automation::APPROVE_BLIND_SIGN_HASH, // aux_hash ]; "braavos_account_type" )] #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_create_ledger_account( account_type: &str, saved_type: &str, class_hash: String, port: u16, automations: &[speculos_client::AutomationRule<'static>], ) { let (client, url) = setup_speculos(port); let tempdir = tempdir().unwrap(); client.automation(automations).await.unwrap(); let output = runner(&[ "--accounts-file", "accounts.json", "account", "create", "--ledger-path", TEST_LEDGER_PATH, "--url", URL, "--name", LEDGER_ACCOUNT_NAME, "--type", account_type, ]) .env("LEDGER_EMULATOR_URL", &url) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()) .assert() .success(); assert_stdout_contains( output, indoc::formatdoc! {" Success: Account created Address: 0x0[..] Account successfully created but it needs to be deployed. The estimated deployment fee is [..] STRK. Prefund the account to cover deployment transaction fee After prefunding the account, run: sncast --accounts-file accounts.json account deploy --url {URL} --name {LEDGER_ACCOUNT_NAME} To see account creation details, visit: account: [..] ", URL = URL, LEDGER_ACCOUNT_NAME = LEDGER_ACCOUNT_NAME, }, ); let contents = fs::read_to_string(tempdir.path().join("accounts.json")) .expect("Unable to read created file"); let expected = json!( { "alpha-sepolia": { LEDGER_ACCOUNT_NAME: { "address": "0x[..]", "class_hash": class_hash, "deployed": false, "legacy": false, "public_key": "0x[..]", "salt": "0x[..]", "type": saved_type, "ledger_path": TEST_LEDGER_PATH_STORED, } } } ); assert_data_eq!(contents, to_string_pretty(&expected).unwrap()); } #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_create_ledger_account_add_profile() { let (client, url) = setup_speculos(6004); let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); client .automation(&[automation::APPROVE_PUBLIC_KEY]) .await .unwrap(); let output = runner(&[ "--accounts-file", "accounts.json", "account", "create", "--ledger-path", TEST_LEDGER_PATH, "--url", URL, "--name", LEDGER_ACCOUNT_NAME, "--add-profile", LEDGER_ACCOUNT_NAME, ]) .env("LEDGER_EMULATOR_URL", &url) .current_dir(tempdir.path()) .assert() .success(); let config_path = Utf8PathBuf::from_path_buf(tempdir.path().join("snfoundry.toml")) .unwrap() .canonicalize_utf8() .unwrap(); assert_stdout_contains( output, format!("Add Profile: Profile {LEDGER_ACCOUNT_NAME} successfully added to {config_path}"), ); let contents = std::fs::read_to_string(tempdir.path().join("snfoundry.toml")).unwrap(); assert!(contents.contains(&format!("[sncast.{LEDGER_ACCOUNT_NAME}]"))); assert!(contents.contains(&format!("account = \"{LEDGER_ACCOUNT_NAME}\""))); } #[test_case( "oz", OZ_LEDGER_PATH, 6005, // create: public key only (OZ skips signing during fee estimation) // deploy: enable blind sign + 1 sign_hash &[ automation::APPROVE_PUBLIC_KEY, automation::ENABLE_BLIND_SIGN, automation::APPROVE_BLIND_SIGN_HASH, ]; "oz_account_type" )] #[test_case( "ready", READY_LEDGER_PATH, 6006, // create: public key only (Ready skips signing during fee estimation) // deploy: enable blind sign + 1 sign_hash &[ automation::APPROVE_PUBLIC_KEY, automation::ENABLE_BLIND_SIGN, automation::APPROVE_BLIND_SIGN_HASH, ]; "ready_account_type" )] #[test_case( "braavos", BRAAVOS_LEDGER_PATH, 6007, // create: public key + enable blind sign + 2x sign_hash (tx_hash + aux_hash) // deploy: 2x sign_hash again (tx_hash + aux_hash), blind sign already enabled &[ automation::APPROVE_PUBLIC_KEY, automation::ENABLE_BLIND_SIGN, automation::APPROVE_BLIND_SIGN_HASH, // create: tx_hash automation::APPROVE_BLIND_SIGN_HASH, // create: aux_hash automation::APPROVE_BLIND_SIGN_HASH, // deploy: tx_hash automation::APPROVE_BLIND_SIGN_HASH, // deploy: aux_hash ]; "braavos_account_type" )] #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_deploy_ledger_account( account_type_str: &str, ledger_path: &str, port: u16, automations: &[AutomationRule<'static>], ) { let (client, url) = setup_speculos(port); client.automation(automations).await.unwrap(); let tempdir = tempdir().unwrap(); let accounts_file = tempdir.path().join("accounts.json"); let accounts_file_str = accounts_file.to_str().unwrap(); // First create the accounts runner(&[ "--accounts-file", accounts_file_str, "account", "create", "--ledger-path", ledger_path, "--url", URL, "--name", LEDGER_ACCOUNT_NAME, "--type", account_type_str, ]) .env("LEDGER_EMULATOR_URL", &url) .assert() .success(); let accounts_content = std::fs::read_to_string(&accounts_file).unwrap(); let contents_json: serde_json::Value = serde_json::from_str(&accounts_content).unwrap(); let address = contents_json["alpha-sepolia"][LEDGER_ACCOUNT_NAME]["address"] .as_str() .unwrap() .to_string(); mint_token(&address, u128::MAX).await; let args = vec![ "--accounts-file", accounts_file_str, "account", "deploy", "--name", LEDGER_ACCOUNT_NAME, "--url", URL, ]; let output = runner(&args) .env("LEDGER_EMULATOR_URL", &url) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .assert() .success(); assert_stdout_contains( output, indoc! {" Ledger device will display a confirmation screen. Please approve it to continue... Success: Account deployed Transaction Hash: 0x[..] "}, ); let path = Utf8PathBuf::from_path_buf(accounts_file.clone()).expect("Path is not valid UTF-8"); let items = load_accounts(&path).expect("Failed to load accounts"); assert_eq!( items["alpha-sepolia"][LEDGER_ACCOUNT_NAME]["deployed"], true ); } #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_invalid_derivation_path() { let (_client, url) = setup_speculos(6008); let output = runner(&[ "ledger", "get-public-key", "--path", "invalid/path", "--no-display", ]) .env("LEDGER_EMULATOR_URL", &url) .assert() .failure(); assert_stderr_contains( output, "error: invalid Ledger derivation path: EIP-2645 paths must have 6 levels", ); } #[test_case("oz", "open_zeppelin", OZ_CLASS_HASH.into_hex_string(), 6009; "oz_account_type")] #[test_case("ready", "ready", READY_CLASS_HASH.into_hex_string(), 6010; "ready_account_type")] #[test_case("braavos", "braavos", BRAAVOS_CLASS_HASH.into_hex_string(), 6011; "braavos_account_type")] #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_import_ledger_account( account_type: &str, saved_type: &str, class_hash: String, port: u16, ) { let (client, url) = setup_speculos(port); let tempdir = tempdir().unwrap(); client .automation(&[automation::APPROVE_PUBLIC_KEY]) .await .unwrap(); let output = runner(&[ "--accounts-file", "accounts.json", "account", "import", "--ledger-path", TEST_LEDGER_PATH, "--url", URL, "--name", LEDGER_ACCOUNT_NAME, "--address", "0x1", "--class-hash", &class_hash, "--type", account_type, ]) .env("LEDGER_EMULATOR_URL", &url) .current_dir(tempdir.path()) .assert() .success(); assert_stdout_contains( output, indoc! {r" Success: Account imported successfully Account Name: my_ledger "}, ); let accounts_content = std::fs::read_to_string(tempdir.path().join("accounts.json")).unwrap(); let contents_json: serde_json::Value = serde_json::from_str(&accounts_content).unwrap(); assert_eq!( contents_json, json!({ "alpha-sepolia": { LEDGER_ACCOUNT_NAME: { "address": "0x1", "class_hash": class_hash, "deployed": false, "legacy": false, "public_key": LEDGER_PUBLIC_KEY, "type": saved_type, "ledger_path": TEST_LEDGER_PATH_STORED, } } }) ); } #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_import_ledger_account_add_profile() { let (client, url) = setup_speculos(6012); let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); client .automation(&[automation::APPROVE_PUBLIC_KEY]) .await .unwrap(); let oz_class_hash = OZ_CLASS_HASH.into_hex_string(); let output = runner(&[ "--accounts-file", "accounts.json", "account", "import", "--ledger-path", TEST_LEDGER_PATH, "--url", URL, "--name", LEDGER_ACCOUNT_NAME, "--address", "0x1", "--class-hash", &oz_class_hash, "--type", "oz", "--add-profile", LEDGER_ACCOUNT_NAME, ]) .env("LEDGER_EMULATOR_URL", &url) .current_dir(tempdir.path()) .assert() .success(); let config_path = Utf8PathBuf::from_path_buf(tempdir.path().join("snfoundry.toml")) .unwrap() .canonicalize_utf8() .unwrap(); assert_stdout_contains( output, format!("Add Profile: Profile {LEDGER_ACCOUNT_NAME} successfully added to {config_path}"), ); let contents = std::fs::read_to_string(tempdir.path().join("snfoundry.toml")).unwrap(); assert!(contents.contains(&format!("[sncast.{LEDGER_ACCOUNT_NAME}]"))); assert!(contents.contains(&format!("account = \"{LEDGER_ACCOUNT_NAME}\""))); } ================================================ FILE: crates/sncast/tests/e2e/ledger/basic.rs ================================================ use crate::e2e::ledger::{TEST_LEDGER_PATH, automation, setup_speculos}; use crate::helpers::runner::runner; use shared::test_utils::output_assert::{assert_stderr_contains, assert_stdout_contains}; #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_get_app_version() { let (_client, url) = setup_speculos(4001); let output = runner(&["ledger", "app-version"]) .env("LEDGER_EMULATOR_URL", &url) .assert() .success(); assert_stdout_contains(output, "App Version: 2.3.4"); } #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_get_public_key_headless() { let (_client, url) = setup_speculos(4002); let output = runner(&[ "ledger", "get-public-key", "--account-id", "0", "--no-display", ]) .env("LEDGER_EMULATOR_URL", &url) .assert() .success(); assert_stdout_contains( output, "Public Key: 0x51f3e99d539868d8f45ca705ad6f75e68229a6037a919b15216b4e92a4d6d8", ); } #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_get_public_key_with_confirmation() { let (client, url) = setup_speculos(4003); client .automation(&[automation::APPROVE_PUBLIC_KEY]) .await .unwrap(); let output = runner(&["ledger", "get-public-key", "--path", TEST_LEDGER_PATH]) .env("LEDGER_EMULATOR_URL", &url) .assert() .success(); assert_stdout_contains( output, "Public Key: 0x51f3e99d539868d8f45ca705ad6f75e68229a6037a919b15216b4e92a4d6d8", ); } #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_sign_hash() { let (client, url) = setup_speculos(4004); client .automation(&[ automation::ENABLE_BLIND_SIGN, automation::APPROVE_BLIND_SIGN_HASH, ]) .await .unwrap(); let output = runner(&[ "ledger", "sign-hash", "--path", TEST_LEDGER_PATH, "0x01234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd", ]) .env("LEDGER_EMULATOR_URL", &url) .assert() .success(); assert_stdout_contains(output, "Hash signature:\nr: 0x[..]\ns: 0x[..]"); } #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_sign_hash_invalid_format() { let (_client, url) = setup_speculos(4005); let output = runner(&[ "ledger", "sign-hash", "--path", TEST_LEDGER_PATH, "not-a-hex-hash", ]) .env("LEDGER_EMULATOR_URL", &url) .assert() .failure(); assert_stderr_contains( output, "error: invalid value 'not-a-hex-hash' for ''[..]", ); } ================================================ FILE: crates/sncast/tests/e2e/ledger/mod.rs ================================================ #![cfg(not(target_arch = "wasm32"))] use std::env; use std::{borrow::Cow, sync::Arc}; use crate::helpers::constants::URL; use crate::helpers::fixtures::mint_token; use clap::Command; use clap::builder::TypedValueParser; use serde_json::json; use sncast::AccountType; use sncast::helpers::braavos::BraavosAccountFactory; use sncast::helpers::constants::{ BRAAVOS_BASE_ACCOUNT_CLASS_HASH, BRAAVOS_CLASS_HASH, OZ_CLASS_HASH, READY_CLASS_HASH, }; use sncast::helpers::ledger::{DerivationPathParser, create_ledger_app}; use sncast::response::ui::UI; use speculos_client::{ AutomationAction, AutomationCondition, AutomationRule, Button, DeviceModel, SpeculosClient, }; use starknet_rust::accounts::{AccountFactory, ArgentAccountFactory, OpenZeppelinAccountFactory}; use starknet_rust::core::types::{BlockId, BlockTag}; use starknet_rust::providers::Provider; use starknet_rust::providers::jsonrpc::{HttpTransport, JsonRpcClient}; use starknet_rust::signers::LedgerSigner; use starknet_types_core::felt::Felt; use tempfile::TempDir; use url::Url; mod account; mod basic; mod network; pub(crate) const OZ_LEDGER_PATH: &str = "m//starknet'/sncast'/0'/0'/0"; pub(crate) const READY_LEDGER_PATH: &str = "m//starknet'/sncast'/0'/1'/0"; pub(crate) const BRAAVOS_LEDGER_PATH: &str = "m//starknet'/sncast'/0'/2'/0"; pub(crate) const TEST_LEDGER_PATH: &str = OZ_LEDGER_PATH; pub(crate) const TEST_LEDGER_PATH_STORED: &str = "m/2645'/1195502025'/355113700'/0'/0'/0"; // TODO(#4221): Update to latest version and build in workflow const APP_PATH: &str = "tests/data/ledger-app/nanox#strk#0.25.13.elf"; pub(crate) const LEDGER_PUBLIC_KEY: &str = "0x51f3e99d539868d8f45ca705ad6f75e68229a6037a919b15216b4e92a4d6d8"; pub(crate) const LEDGER_ACCOUNT_NAME: &str = "my_ledger"; pub(crate) fn setup_speculos(port: u16) -> (Arc, String) { let client = Arc::new(SpeculosClient::new(DeviceModel::Nanox, port, APP_PATH).unwrap()); let url = format!("http://127.0.0.1:{port}"); (client, url) } fn create_jsonrpc_client() -> JsonRpcClient { JsonRpcClient::new(HttpTransport::new(Url::parse(URL).unwrap())) } pub(crate) fn create_temp_accounts_json(address: Felt) -> TempDir { let tempdir = TempDir::new().unwrap(); let accounts_json = json!({ "alpha-sepolia": { LEDGER_ACCOUNT_NAME: { "public_key": LEDGER_PUBLIC_KEY, "address": format!("{address:#066x}"), "deployed": true, "type": "open_zeppelin", "ledger_path": TEST_LEDGER_PATH_STORED, } } }); let accounts_path = tempdir.path().join("accounts.json"); std::fs::write(&accounts_path, accounts_json.to_string()).unwrap(); tempdir } pub(crate) async fn deploy_ledger_account(speculos_url: &str, path: &str, salt: Felt) -> Felt { deploy_ledger_account_of_type(speculos_url, path, salt, AccountType::OpenZeppelin).await } async fn deploy_if_needed( factory: F, salt: Felt, provider: &JsonRpcClient, ) -> Felt where F: AccountFactory + Sync, F::SignError: Send, { let deployment = factory.deploy_v3(salt); let address = deployment.address(); let is_deployed = provider .get_class_hash_at(BlockId::Tag(BlockTag::Latest), address) .await .is_ok(); mint_token(&format!("{address:#066x}"), u128::MAX).await; if !is_deployed { deployment.send().await.expect("Failed to deploy account"); } address } pub(crate) async fn deploy_ledger_account_of_type( speculos_url: &str, path: &str, salt: Felt, account_type: AccountType, ) -> Felt { let provider = create_jsonrpc_client(); let ui = UI::default(); let parsed = DerivationPathParser .parse_ref(&Command::new("test"), None, std::ffi::OsStr::new(path)) .unwrap(); parsed.print_warnings(&ui); let parsed_path = parsed.path; // SAFETY: All tests share the same devnet instance, so even if a race condition causes // `set_var` to race with another test using a different ledger emulator URL, the account // deployment will still reach the correct devnet and remain accessible to the original test. // The ledger emulator URL has no effect on account deployment when using the emulator. unsafe { env::set_var("LEDGER_EMULATOR_URL", speculos_url); }; let app = create_ledger_app().await.unwrap(); let ledger_signer = LedgerSigner::new_with_app(parsed_path, app).unwrap(); let chain_id = starknet_rust::core::chain_id::SEPOLIA; match account_type { AccountType::OpenZeppelin => { let factory = OpenZeppelinAccountFactory::new( OZ_CLASS_HASH, chain_id, ledger_signer, provider.clone(), ) .await .unwrap(); deploy_if_needed(factory, salt, &provider).await } AccountType::Ready | AccountType::Argent => { let factory = ArgentAccountFactory::new( READY_CLASS_HASH, chain_id, None, ledger_signer, provider.clone(), ) .await .unwrap(); deploy_if_needed(factory, salt, &provider).await } AccountType::Braavos => { let factory = BraavosAccountFactory::new( BRAAVOS_CLASS_HASH, BRAAVOS_BASE_ACCOUNT_CLASS_HASH, chain_id, ledger_signer, provider.clone(), ) .await .unwrap(); deploy_if_needed(factory, salt, &provider).await } } } pub(crate) mod automation { use super::*; pub(crate) const APPROVE_PUBLIC_KEY: AutomationRule<'static> = AutomationRule { text: Some(Cow::Borrowed("Confirm Public Key")), regexp: None, x: None, y: None, conditions: &[], actions: &[ // Press right AutomationAction::Button { button: Button::Right, pressed: true, }, AutomationAction::Button { button: Button::Right, pressed: false, }, // Press right AutomationAction::Button { button: Button::Right, pressed: true, }, AutomationAction::Button { button: Button::Right, pressed: false, }, // Press right AutomationAction::Button { button: Button::Right, pressed: true, }, AutomationAction::Button { button: Button::Right, pressed: false, }, // Press both AutomationAction::Button { button: Button::Left, pressed: true, }, AutomationAction::Button { button: Button::Right, pressed: true, }, AutomationAction::Button { button: Button::Left, pressed: false, }, AutomationAction::Button { button: Button::Right, pressed: false, }, ], }; pub(crate) const ENABLE_BLIND_SIGN: AutomationRule<'static> = AutomationRule { text: None, regexp: Some(Cow::Borrowed("^(S)?tarknet$")), x: None, y: None, conditions: &[AutomationCondition { varname: Cow::Borrowed("blind_enabled"), value: false, }], actions: &[ // Right AutomationAction::Button { button: Button::Right, pressed: true, }, AutomationAction::Button { button: Button::Right, pressed: false, }, // Right AutomationAction::Button { button: Button::Right, pressed: true, }, AutomationAction::Button { button: Button::Right, pressed: false, }, // Both AutomationAction::Button { button: Button::Left, pressed: true, }, AutomationAction::Button { button: Button::Right, pressed: true, }, AutomationAction::Button { button: Button::Left, pressed: false, }, AutomationAction::Button { button: Button::Right, pressed: false, }, // Both AutomationAction::Button { button: Button::Left, pressed: true, }, AutomationAction::Button { button: Button::Right, pressed: true, }, AutomationAction::Button { button: Button::Left, pressed: false, }, AutomationAction::Button { button: Button::Right, pressed: false, }, // Left AutomationAction::Button { button: Button::Left, pressed: true, }, AutomationAction::Button { button: Button::Left, pressed: false, }, // Mark as done AutomationAction::Setbool { varname: Cow::Borrowed("blind_enabled"), value: true, }, ], }; /// Must be used with [`ENABLE_BLIND_SIGN`]. pub(crate) const APPROVE_BLIND_SIGN_HASH: AutomationRule<'static> = AutomationRule { text: None, regexp: Some(Cow::Borrowed("^Cancel$")), x: None, y: None, conditions: &[AutomationCondition { varname: Cow::Borrowed("blind_enabled"), value: true, }], actions: &[ // Right AutomationAction::Button { button: Button::Right, pressed: true, }, AutomationAction::Button { button: Button::Right, pressed: false, }, // Both AutomationAction::Button { button: Button::Left, pressed: true, }, AutomationAction::Button { button: Button::Right, pressed: true, }, AutomationAction::Button { button: Button::Left, pressed: false, }, AutomationAction::Button { button: Button::Right, pressed: false, }, // Right AutomationAction::Button { button: Button::Right, pressed: true, }, AutomationAction::Button { button: Button::Right, pressed: false, }, // Right AutomationAction::Button { button: Button::Right, pressed: true, }, AutomationAction::Button { button: Button::Right, pressed: false, }, // Right AutomationAction::Button { button: Button::Right, pressed: true, }, AutomationAction::Button { button: Button::Right, pressed: false, }, // Both AutomationAction::Button { button: Button::Left, pressed: true, }, AutomationAction::Button { button: Button::Right, pressed: true, }, AutomationAction::Button { button: Button::Left, pressed: false, }, AutomationAction::Button { button: Button::Right, pressed: false, }, ], }; } ================================================ FILE: crates/sncast/tests/e2e/ledger/network.rs ================================================ use crate::e2e::ledger::{ BRAAVOS_LEDGER_PATH, LEDGER_ACCOUNT_NAME, OZ_LEDGER_PATH, TEST_LEDGER_PATH, automation, create_temp_accounts_json, deploy_ledger_account, deploy_ledger_account_of_type, setup_speculos, }; use crate::helpers::constants::{ CONSTRUCTOR_WITH_PARAMS_CONTRACT_CLASS_HASH_SEPOLIA, CONTRACTS_DIR, MAP_CONTRACT_ADDRESS_SEPOLIA, MAP_CONTRACT_CLASS_HASH_SEPOLIA, MULTICALL_CONFIGS_DIR, URL, }; use crate::helpers::fixtures::{ duplicate_contract_directory_with_salt, get_transaction_hash, get_transaction_receipt, join_tempdirs, }; use crate::helpers::runner::runner; use sncast::AccountType; use starknet_rust::core::types::TransactionReceipt::{Declare, Invoke}; use starknet_types_core::felt::Felt; use std::path::Path; use tempfile::tempdir; use test_case::test_case; #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_ledger_invoke_happy_case() { let (client, url) = setup_speculos(5001); client .automation(&[ automation::ENABLE_BLIND_SIGN, automation::APPROVE_BLIND_SIGN_HASH, ]) .await .unwrap(); let account_address = deploy_ledger_account(&url, TEST_LEDGER_PATH, Felt::from(5001_u32)).await; let tempdir = create_temp_accounts_json(account_address); let accounts_file = tempdir.path().join("accounts.json"); let args = vec![ "--accounts-file", accounts_file.to_str().unwrap(), "--account", LEDGER_ACCOUNT_NAME, "--json", "invoke", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "put", "--calldata", "0x1 0x2", ]; let output = runner(&args) .env("LEDGER_EMULATOR_URL", &url) .assert() .success() .get_output() .stdout .clone(); let tx_hash = get_transaction_hash(&output); let receipt = get_transaction_receipt(tx_hash).await; assert!(matches!(receipt, Invoke(_))); } #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_ledger_invoke_with_wait() { let (client, url) = setup_speculos(5002); client .automation(&[ automation::ENABLE_BLIND_SIGN, automation::APPROVE_BLIND_SIGN_HASH, ]) .await .unwrap(); let account_address = deploy_ledger_account(&url, TEST_LEDGER_PATH, Felt::from(5002_u32)).await; let tempdir = create_temp_accounts_json(account_address); let accounts_file = tempdir.path().join("accounts.json"); // Without `--ledger-path`, it should be taken from the accounts file let args = vec![ "--accounts-file", accounts_file.to_str().unwrap(), "--account", LEDGER_ACCOUNT_NAME, "--json", "--wait", "invoke", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "put", "--calldata", "0x3 0x4", ]; let output = runner(&args) .env("LEDGER_EMULATOR_URL", &url) .assert() .success() .get_output() .stdout .clone(); let tx_hash = get_transaction_hash(&output); let receipt = get_transaction_receipt(tx_hash).await; assert!(matches!(receipt, Invoke(_))); } #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_ledger_deploy_happy_case() { let (client, url) = setup_speculos(5004); client .automation(&[ automation::ENABLE_BLIND_SIGN, automation::APPROVE_BLIND_SIGN_HASH, ]) .await .unwrap(); let account_address = deploy_ledger_account(&url, TEST_LEDGER_PATH, Felt::from(5004_u32)).await; let tempdir = create_temp_accounts_json(account_address); let accounts_file = tempdir.path().join("accounts.json"); let args = vec![ "--accounts-file", accounts_file.to_str().unwrap(), "--account", LEDGER_ACCOUNT_NAME, "--json", "deploy", "--url", URL, "--class-hash", MAP_CONTRACT_CLASS_HASH_SEPOLIA, "--salt", "0x123", "--unique", ]; let output = runner(&args) .env("LEDGER_EMULATOR_URL", &url) .assert() .success() .get_output() .stdout .clone(); let tx_hash = get_transaction_hash(&output); let receipt = get_transaction_receipt(tx_hash).await; assert!(matches!(receipt, Invoke(_))); } #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_ledger_deploy_with_constructor() { let (client, url) = setup_speculos(5005); client .automation(&[ automation::ENABLE_BLIND_SIGN, automation::APPROVE_BLIND_SIGN_HASH, ]) .await .unwrap(); let account_address = deploy_ledger_account(&url, TEST_LEDGER_PATH, Felt::from(6001_u32)).await; let tempdir = create_temp_accounts_json(account_address); let accounts_file = tempdir.path().join("accounts.json"); let args = vec![ "--accounts-file", accounts_file.to_str().unwrap(), "--account", LEDGER_ACCOUNT_NAME, "--json", "deploy", "--url", URL, "--class-hash", CONSTRUCTOR_WITH_PARAMS_CONTRACT_CLASS_HASH_SEPOLIA, "--salt", "0x456", "--unique", "--constructor-calldata", "0x1 0x1 0x0", ]; let output = runner(&args) .env("LEDGER_EMULATOR_URL", &url) .assert() .success() .get_output() .stdout .clone(); let tx_hash = get_transaction_hash(&output); let receipt = get_transaction_receipt(tx_hash).await; assert!(matches!(receipt, Invoke(_))); } #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_ledger_declare() { let (client, url) = setup_speculos(5006); client .automation(&[ automation::ENABLE_BLIND_SIGN, automation::APPROVE_BLIND_SIGN_HASH, ]) .await .unwrap(); let account_address = deploy_ledger_account(&url, TEST_LEDGER_PATH, Felt::from(5006_u32)).await; let contract_dir = duplicate_contract_directory_with_salt( CONTRACTS_DIR.to_string() + "/map", "put", "ledger_declare", ); let accounts_tempdir = create_temp_accounts_json(account_address); join_tempdirs(&accounts_tempdir, &contract_dir); let args = vec![ "--accounts-file", "accounts.json", "--account", LEDGER_ACCOUNT_NAME, "--json", "declare", "--url", URL, "--contract-name", "Map", ]; let output = runner(&args) .env("LEDGER_EMULATOR_URL", &url) .current_dir(contract_dir.path()) .assert() .success() .get_output() .stdout .clone(); let tx_hash = get_transaction_hash(&output); let receipt = get_transaction_receipt(tx_hash).await; assert!(matches!(receipt, Declare(_))); } #[test_case(AccountType::OpenZeppelin, "oz", Some(OZ_LEDGER_PATH), None, 5008; "oz_account_type")] #[test_case(AccountType::Ready, "ready", None, Some(1), 5009; "ready_account_type")] #[test_case(AccountType::Braavos, "braavos", Some(BRAAVOS_LEDGER_PATH), None, 5010; "braavos_account_type")] #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_ledger_import_and_invoke( account_type: AccountType, account_type_str: &str, ledger_path: Option<&str>, ledger_account_id: Option, port: u16, ) { let (client, url) = setup_speculos(port); client .automation(&[ automation::ENABLE_BLIND_SIGN, automation::APPROVE_BLIND_SIGN_HASH, automation::APPROVE_PUBLIC_KEY, ]) .await .unwrap(); let account_id_path_buf; let deploy_path = match (ledger_path, ledger_account_id) { (Some(path), None) => path, (None, Some(id)) => { account_id_path_buf = format!("m//starknet'/sncast'/0'/{id}'/0"); &account_id_path_buf } _ => unreachable!(), }; let account_address = deploy_ledger_account_of_type(&url, deploy_path, Felt::from(u32::from(port)), account_type) .await; let tempdir = tempdir().unwrap(); let accounts_file = tempdir.path().join("accounts.json"); let accounts_file_str = accounts_file.to_str().unwrap(); let account_address_str = format!("{account_address:#x}"); let ledger_account_id_str; let mut import_args = vec![ "--accounts-file", accounts_file_str, "account", "import", "--url", URL, "--name", LEDGER_ACCOUNT_NAME, "--address", &account_address_str, "--type", account_type_str, ]; if let Some(path) = ledger_path { import_args.push("--ledger-path"); import_args.push(path); } else if let Some(id) = ledger_account_id { ledger_account_id_str = id.to_string(); import_args.push("--ledger-account-id"); import_args.push(&ledger_account_id_str); } runner(&import_args) .env("LEDGER_EMULATOR_URL", &url) .assert() .success(); let args = vec![ "--accounts-file", accounts_file_str, "--account", LEDGER_ACCOUNT_NAME, "--json", "invoke", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "put", "--calldata", "0x1 0x2", ]; let output = runner(&args) .env("LEDGER_EMULATOR_URL", &url) .assert() .success() .get_output() .stdout .clone(); let tx_hash = get_transaction_hash(&output); let receipt = get_transaction_receipt(tx_hash).await; assert!(matches!(receipt, Invoke(_))); } #[tokio::test] #[ignore = "requires Speculos installation"] async fn test_ledger_multicall() { let (client, url) = setup_speculos(5007); client .automation(&[ automation::ENABLE_BLIND_SIGN, automation::APPROVE_BLIND_SIGN_HASH, ]) .await .unwrap(); let account_address = deploy_ledger_account(&url, TEST_LEDGER_PATH, Felt::from(5007_u32)).await; let tempdir = create_temp_accounts_json(account_address); let accounts_file = tempdir.path().join("accounts.json"); let multicall_path = project_root::get_project_root().expect("failed to get project root path"); let multicall_path = Path::new(&multicall_path) .join(MULTICALL_CONFIGS_DIR) .join("invoke_ledger.toml"); let multicall_path = multicall_path .to_str() .expect("failed converting path to str"); let args = vec![ "--accounts-file", accounts_file.to_str().unwrap(), "--account", LEDGER_ACCOUNT_NAME, "--json", "multicall", "run", "--url", URL, "--path", multicall_path, ]; let output = runner(&args) .env("LEDGER_EMULATOR_URL", &url) .assert() .success() .get_output() .stdout .clone(); let tx_hash = get_transaction_hash(&output); let receipt = get_transaction_receipt(tx_hash).await; assert!(matches!(receipt, Invoke(_))); } ================================================ FILE: crates/sncast/tests/e2e/main_tests.rs ================================================ use crate::helpers::constants::{ ACCOUNT, ACCOUNT_FILE_PATH, CONTRACTS_DIR, MAP_CONTRACT_ADDRESS_SEPOLIA, URL, }; use crate::helpers::env::set_keystore_password_env; use crate::helpers::fixtures::{ duplicate_contract_directory_with_salt, get_accounts_path, get_keystores_path, }; use crate::helpers::runner::runner; use configuration::test_utils::copy_config_to_tempdir; use indoc::indoc; use shared::test_utils::output_assert::assert_stderr_contains; #[tokio::test] async fn test_happy_case_from_sncast_config() { let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "call", "--url", URL, "--contract-address", "0x0", "--function", "doesnotmatter", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().failure(); assert_stderr_contains( output, "Error: An error occurred in the called contract[..]Requested contract address [..] is not deployed[..]", ); } #[tokio::test] async fn test_happy_case_predefined_network() { let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--profile", "no_url", "call", "--network", "sepolia", "--contract-address", "0x0", "--function", "doesnotmatter", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().failure(); assert_stderr_contains( output, "Error: An error occurred in the called contract[..]Requested contract address [..] is not deployed[..]", ); } #[tokio::test] async fn test_url_with_network_args() { let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--profile", "no_url", "call", "--network", "sepolia", "--url", URL, "--contract-address", "0x0", "--function", "doesnotmatter", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().failure(); assert_stderr_contains( output, "error: the argument '--network ' cannot be used with '--url '", ); } #[tokio::test] async fn test_happy_case_from_cli_no_scarb() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--account", ACCOUNT, "call", "--url", URL, "--contract-address", "0x0", "--function", "doesnotmatter", ]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains( output, "Error: An error occurred in the called contract[..]Requested contract address [..] is not deployed[..]", ); } #[tokio::test] async fn test_happy_case_from_cli_with_sncast_config() { let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--profile", "default", "--account", ACCOUNT, "call", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "get", "--calldata", "0x0", "--block-id", "latest", ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().success().stdout_eq(indoc! {r" Success: Call completed Response: 0x0 Response Raw: [0x0] "}); } #[tokio::test] async fn test_happy_case_mixed() { let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--account", ACCOUNT, "call", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "get", "--calldata", "0x0", "--block-id", "latest", ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().success().stdout_eq(indoc! {r" Success: Call completed Response: 0x0 Response Raw: [0x0] "}); } #[tokio::test] async fn test_nonexistent_account_address() { let contract_path = duplicate_contract_directory_with_salt(CONTRACTS_DIR.to_string() + "/map", "dummy", "101"); let accounts_json_path = get_accounts_path("tests/data/accounts/faulty_accounts.json"); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "with_nonexistent_address", "declare", "--url", URL, "--contract-name", "Map", ]; let snapbox = runner(&args).current_dir(contract_path.path()); let output = snapbox.assert().failure(); assert_stderr_contains( output, "Error: Account with address 0x1010101010011aaabbcc not found on network SN_SEPOLIA", ); } #[tokio::test] async fn test_missing_account_flag() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "declare", "--url", URL, "--contract-name", "whatever", ]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains( output, "Error: Account name not passed nor found in snfoundry.toml", ); } #[tokio::test] async fn test_inexistent_keystore() { let args = vec![ "--keystore", "inexistent_key.json", "declare", "--url", URL, "--contract-name", "my_contract", ]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains(output, "Error: Failed to find keystore file"); } #[tokio::test] async fn test_keystore_account_required() { let args = vec![ "--keystore", "tests/data/keystore/my_key.json", "declare", "--url", URL, "--contract-name", "my_contract", ]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains( output, "Error: Argument `--account` must be passed and be a path when using `--keystore`", ); } #[tokio::test] async fn test_keystore_inexistent_account() { let args = vec![ "--keystore", "tests/data/keystore/my_key.json", "--account", "inexistent_account", "declare", "--url", URL, "--contract-name", "my_contract", ]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains( output, "Error: File containing the account does not exist[..]", ); } #[tokio::test] async fn test_keystore_undeployed_account() { let contract_path = duplicate_contract_directory_with_salt(CONTRACTS_DIR.to_string() + "/map", "put", "8"); let my_key_path = get_keystores_path("tests/data/keystore/my_key.json"); let my_account_undeployed_path = get_keystores_path("tests/data/keystore/my_account_undeployed.json"); let args = vec![ "--keystore", my_key_path.as_str(), "--account", my_account_undeployed_path.as_str(), "declare", "--url", URL, "--contract-name", "Map", ]; set_keystore_password_env(); let snapbox = runner(&args).current_dir(contract_path.path()); let output = snapbox.assert().failure(); assert_stderr_contains(output, "Error: Failed to get account address"); } #[tokio::test] async fn test_keystore_declare() { let contract_path = duplicate_contract_directory_with_salt(CONTRACTS_DIR.to_string() + "/map", "put", "999"); let my_key_path = get_keystores_path("tests/data/keystore/predeployed_key.json"); let my_account_path = get_keystores_path("tests/data/keystore/predeployed_account.json"); let args = vec![ "--keystore", my_key_path.as_str(), "--account", my_account_path.as_str(), "declare", "--url", URL, "--contract-name", "Map", ]; set_keystore_password_env(); let snapbox = runner(&args).current_dir(contract_path.path()); assert!(snapbox.assert().success().get_output().stderr.is_empty()); } #[tokio::test] async fn test_keystore_and_ledger_conflict() { let args = vec![ "--keystore", "some_keystore", "account", "create", "--url", URL, "--ledger-path", "m//starknet'/sncast'/0'/0'/0", ]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains(output, "Error: keystore and ledger cannot be used together"); } ================================================ FILE: crates/sncast/tests/e2e/mod.rs ================================================ mod account; pub mod balance; mod call; mod class_hash; mod class_hash_at; mod completions; mod declare; mod declare_from; mod deploy; mod devnet_accounts; mod fee; mod invoke; pub(crate) mod ledger; mod main_tests; mod multicall; mod nonce; mod proof; mod script; mod selector; mod serialize; mod show_config; mod transaction; mod tx_status; mod verify; ================================================ FILE: crates/sncast/tests/e2e/multicall/execute.rs ================================================ use crate::helpers::constants::{ MAP_CONTRACT_ADDRESS_SEPOLIA, MAP_CONTRACT_CLASS_HASH_SEPOLIA, URL, }; use crate::helpers::fixtures::create_and_deploy_account; use crate::helpers::runner::runner; use indoc::indoc; use shared::test_utils::output_assert::{assert_stderr_contains, assert_stdout_contains}; use sncast::AccountType; use sncast::helpers::constants::OZ_CLASS_HASH; #[tokio::test] async fn test_one_invoke() { let tempdir = create_and_deploy_account(OZ_CLASS_HASH, AccountType::OpenZeppelin).await; let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "multicall", "execute", "--url", URL, "invoke", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--calldata", "0x1 0x2", "--function", "put", ]; let snapbox = runner(&args) .current_dir(tempdir.path()) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1"); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! { " Success: Multicall completed Transaction Hash: 0x[..] To see invocation details, visit: transaction: [..] " }, ); } #[tokio::test] async fn test_two_invokes() { let tempdir = create_and_deploy_account(OZ_CLASS_HASH, AccountType::OpenZeppelin).await; let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "multicall", "execute", "--url", URL, "invoke", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--calldata", "0x1 0x2", "--function", "put", "/", "invoke", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "put", "--calldata", "0x3 0x4", ]; let snapbox = runner(&args) .current_dir(tempdir.path()) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1"); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! { " Success: Multicall completed Transaction Hash: 0x[..] To see invocation details, visit: transaction: [..] " }, ); } #[tokio::test] async fn test_deploy_and_invoke() { let tempdir = create_and_deploy_account(OZ_CLASS_HASH, AccountType::OpenZeppelin).await; let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "multicall", "execute", "--url", URL, "deploy", "--class-hash", MAP_CONTRACT_CLASS_HASH_SEPOLIA, "/", "invoke", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "get", "--calldata", "0x1", ]; let snapbox = runner(&args) .current_dir(tempdir.path()) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1"); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! { " Success: Multicall completed Transaction Hash: 0x[..] To see invocation details, visit: transaction: [..] " }, ); } #[tokio::test] async fn test_use_id() { let tempdir = create_and_deploy_account(OZ_CLASS_HASH, AccountType::OpenZeppelin).await; let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "multicall", "execute", "--url", URL, "deploy", "--class-hash", MAP_CONTRACT_CLASS_HASH_SEPOLIA, "--id", "dpl", "/", "invoke", "--contract-address", "@dpl", "--function", "get", "--calldata", "@dpl", ]; let snapbox = runner(&args) .current_dir(tempdir.path()) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1"); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! { " Success: Multicall completed Transaction Hash: 0x[..] To see invocation details, visit: transaction: [..] " }, ); } #[tokio::test] async fn test_non_existent_id() { let tempdir = create_and_deploy_account(OZ_CLASS_HASH, AccountType::OpenZeppelin).await; let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "multicall", "execute", "--url", URL, "deploy", "--class-hash", MAP_CONTRACT_CLASS_HASH_SEPOLIA, "--id", "dpl", "/", "invoke", "--contract-address", "@non_existent_id", "--function", "get", "--calldata", "0x1", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! { " Error: Failed to find contract address for id: non_existent_id " }, ); } #[tokio::test] async fn test_duplicated_id() { let tempdir = create_and_deploy_account(OZ_CLASS_HASH, AccountType::OpenZeppelin).await; let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "multicall", "execute", "--url", URL, "deploy", "--class-hash", MAP_CONTRACT_CLASS_HASH_SEPOLIA, "--id", "dpl", "/", "deploy", "--class-hash", MAP_CONTRACT_CLASS_HASH_SEPOLIA, "--id", "dpl", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! { " Error: Duplicate id found: dpl " }, ); } #[tokio::test] async fn test_unrecognized_command() { let tempdir = create_and_deploy_account(OZ_CLASS_HASH, AccountType::OpenZeppelin).await; let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "multicall", "execute", "--url", URL, "declare", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! { " Command: multicall execute Error: Unknown multicall command: 'declare'. Allowed commands: deploy, invoke " }, ); } #[tokio::test] async fn test_empty_calls() { let tempdir = create_and_deploy_account(OZ_CLASS_HASH, AccountType::OpenZeppelin).await; let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "multicall", "execute", "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! { " Command: multicall execute Error: No valid multicall commands found to execute. Please check the provided commands. " }, ); } ================================================ FILE: crates/sncast/tests/e2e/multicall/mod.rs ================================================ mod execute; mod new; mod run; ================================================ FILE: crates/sncast/tests/e2e/multicall/new.rs ================================================ use crate::helpers::constants::ACCOUNT_FILE_PATH; use crate::helpers::runner::runner; use indoc::{formatdoc, indoc}; use shared::test_utils::output_assert::{AsOutput, assert_stderr_contains, assert_stdout_contains}; use sncast::helpers::constants::DEFAULT_MULTICALL_CONTENTS; use tempfile::tempdir; #[tokio::test] async fn test_happy_case_file() { let tmp_dir = tempdir().expect("Failed to create temporary directory"); let multicall_toml_file = "multicall.toml"; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "multicall", "new", multicall_toml_file, ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tmp_dir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r" Success: Multicall template created successfully Path: multicall.toml Content: [..] "}, ); let contents = std::fs::read_to_string(tmp_dir.path().join(multicall_toml_file)) .expect("Should have been able to read the file"); assert!(contents.contains(DEFAULT_MULTICALL_CONTENTS)); } #[tokio::test] async fn test_no_output_path_specified() { let args = vec!["--accounts-file", ACCOUNT_FILE_PATH, "multicall", "new"]; let snapbox = runner(&args); let output = snapbox.assert().failure(); let expected = indoc! {r" error: the following required arguments were not provided: Usage: sncast[..] multicall new For more information, try '--help'. "}; assert!(output.as_stdout().is_empty()); assert_stderr_contains(output, expected); } #[tokio::test] async fn test_directory_non_existent() { let tmp_dir = tempdir().expect("failed to create temporary directory"); let multicall_toml_path = "non_existent_directory/multicall.toml"; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "multicall", "new", multicall_toml_path, ]; let snapbox = runner(&args).current_dir(tmp_dir.path()); let output = snapbox.assert().success(); assert!(output.as_stdout().is_empty()); let expected_file_error = "No such file or directory [..]"; assert_stderr_contains( output, formatdoc! {r" Command: multicall new Error: {} ", expected_file_error}, ); } #[tokio::test] async fn test_file_invalid_path() { let tmp_dir = tempdir().expect("failed to create temporary directory"); let tmp_path = tmp_dir .path() .to_str() .expect("failed to convert path to string"); let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "multicall", "new", tmp_path, ]; let snapbox = runner(&args).current_dir(tmp_dir.path()); let output = snapbox.assert().success(); assert!(output.as_stdout().is_empty()); assert_stderr_contains( output, indoc! {r" Command: multicall new Error: Output file cannot be a directory[..] "}, ); } ================================================ FILE: crates/sncast/tests/e2e/multicall/run.rs ================================================ use crate::helpers::constants::{ACCOUNT_FILE_PATH, MULTICALL_CONFIGS_DIR, URL}; use crate::helpers::fixtures::create_and_deploy_oz_account; use crate::helpers::runner::runner; use indoc::{formatdoc, indoc}; use shared::test_utils::output_assert::{AsOutput, assert_stderr_contains}; use std::path::Path; use test_case::test_case; #[test_case("oz_cairo_0"; "cairo_0_account")] #[test_case("oz_cairo_1"; "cairo_1_account")] #[test_case("oz"; "oz_account")] #[test_case("ready"; "ready_account")] #[test_case("braavos"; "braavos_account")] #[tokio::test] async fn test_happy_case(account: &str) { let path = project_root::get_project_root().expect("failed to get project root path"); let path = Path::new(&path) .join(MULTICALL_CONFIGS_DIR) .join("deploy_invoke.toml"); let path = path.to_str().expect("failed converting path to str"); let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--account", account, "multicall", "run", "--url", URL, "--path", path, ]; let snapbox = runner(&args).env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1"); let output = snapbox.assert(); let stderr_str = output.as_stderr(); assert!( stderr_str.is_empty(), "Multicall error, stderr: \n{stderr_str}", ); output.stdout_eq(indoc! {r" Success: Multicall completed Transaction Hash: 0x[..] To see invocation details, visit: transaction: [..] "}); } #[tokio::test] async fn test_calldata_ids() { let tempdir = create_and_deploy_oz_account().await; let path = project_root::get_project_root().expect("failed to get project root path"); let path = Path::new(&path) .join(MULTICALL_CONFIGS_DIR) .join("deploy_invoke_calldata_ids.toml"); let path = path.to_str().expect("failed converting path to str"); let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "multicall", "run", "--url", URL, "--path", path, ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); let output = snapbox.assert(); let stderr_str = output.as_stderr(); assert!( stderr_str.is_empty(), "Multicall error, stderr: \n{stderr_str}", ); output.stdout_eq(indoc! {r" Success: Multicall completed Transaction Hash: 0x[..] To see invocation details, visit: transaction: [..] "}); } #[tokio::test] async fn test_invalid_path() { let tempdir = create_and_deploy_oz_account().await; let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "multicall", "run", "--url", URL, "--path", "non-existent", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert!(output.as_stdout().is_empty()); let expected_file_error = "No such file or directory [..]"; assert_stderr_contains( output, formatdoc! {r" Command: multicall run Error: {} ", expected_file_error}, ); } #[tokio::test] async fn test_deploy_fail() { let tempdir = create_and_deploy_oz_account().await; let path = project_root::get_project_root().expect("failed to get project root path"); let path = Path::new(&path) .join(MULTICALL_CONFIGS_DIR) .join("deploy_invalid.toml"); let path = path.to_str().expect("failed converting path to str"); let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "multicall", "run", "--url", URL, "--path", path, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: multicall run Error: Transaction execution error [..] "}, ); } #[tokio::test] async fn test_invoke_fail() { let tempdir = create_and_deploy_oz_account().await; let path = project_root::get_project_root().expect("failed to get project root path"); let path = Path::new(&path) .join(MULTICALL_CONFIGS_DIR) .join("invoke_invalid.toml"); let path = path.to_str().expect("failed converting path to str"); let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "multicall", "run", "--url", URL, "--path", path, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: multicall run Error: Transaction execution error [..] "}, ); } #[tokio::test] async fn test_deploy_success_invoke_fails() { let tempdir = create_and_deploy_oz_account().await; let path = project_root::get_project_root().expect("failed to get project root path"); let path = Path::new(&path) .join(MULTICALL_CONFIGS_DIR) .join("deploy_succ_invoke_fail.toml"); let path = path.to_str().expect("failed converting path to str"); let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "multicall", "run", "--url", URL, "--path", path, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: multicall run Error: Transaction execution error [..] "}, ); } #[tokio::test] async fn test_numeric_inputs() { let tempdir = create_and_deploy_oz_account().await; let path = project_root::get_project_root().expect("failed to get project root path"); let path = Path::new(&path) .join(MULTICALL_CONFIGS_DIR) .join("deploy_invoke_numeric_inputs.toml"); let path = path.to_str().expect("failed converting path to str"); let args = vec![ "--accounts-file", "accounts.json", "--account", "my_account", "multicall", "run", "--url", URL, "--path", path, ]; let snapbox = runner(&args) .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") .current_dir(tempdir.path()); let output = snapbox.assert(); let stderr_str = output.as_stderr(); assert!( stderr_str.is_empty(), "Multicall error, stderr: \n{stderr_str}", ); output.stdout_eq(indoc! {r" Success: Multicall completed Transaction Hash: 0x[..] To see invocation details, visit: transaction: [..] "}); } ================================================ FILE: crates/sncast/tests/e2e/nonce.rs ================================================ use crate::helpers::constants::{DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS, URL}; use crate::helpers::runner::runner; use indoc::indoc; use shared::test_utils::output_assert::assert_stderr_contains; #[tokio::test] async fn test_happy_case() { let args = vec![ "get", "nonce", DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS, "--url", URL, ]; let snapbox = runner(&args); snapbox.assert().success().stdout_eq(indoc! {r" Success: Nonce retrieved Nonce: 0x[..] "}); } #[tokio::test] async fn test_happy_case_with_block_id() { let args = vec![ "get", "nonce", DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS, "--block-id", "latest", "--url", URL, ]; let snapbox = runner(&args); snapbox.assert().success().stdout_eq(indoc! {r" Success: Nonce retrieved Nonce: 0x[..] "}); } #[tokio::test] async fn test_happy_case_json() { let args = vec![ "--json", "get", "nonce", DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS, "--url", URL, ]; let snapbox = runner(&args); snapbox.assert().success().stdout_eq(indoc! {r#" {"command":"get nonce","nonce":"0x[..]","type":"response"} "#}); } #[tokio::test] async fn test_nonexistent_contract_address() { let args = vec!["get", "nonce", "0x0", "--url", URL]; let snapbox = runner(&args); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: get nonce Error: There is no contract at the specified address "}, ); } #[tokio::test] async fn test_invalid_block_id() { let args = vec![ "get", "nonce", DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS, "--block-id", "invalid_block", "--url", URL, ]; let snapbox = runner(&args); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: get nonce Error: Incorrect value passed for block_id = invalid_block. Possible values are `pre_confirmed`, `latest`, block hash (hex) and block number (u64) "}, ); } ================================================ FILE: crates/sncast/tests/e2e/proof.rs ================================================ use crate::helpers::constants::{ACCOUNT_FILE_PATH, MAP_CONTRACT_ADDRESS_SEPOLIA, URL}; use crate::helpers::runner::runner; use indoc::indoc; use shared::test_utils::output_assert::assert_stderr_contains; // TODO (#4258): Add proper tests for sending invoke with proof. #[test] fn test_proof_file_requires_proof_facts_file() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--account", "user11", "invoke", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "put", "--calldata", "0x1", "0x2", "--proof-file", "proof.txt", ]; let snapbox = runner(&args); let output = snapbox.assert(); assert_stderr_contains( output, indoc! {r" error: the following required arguments were not provided: --proof-facts-file "}, ); } #[test] fn test_proof_facts_file_requires_proof_file() { let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "--account", "user11", "invoke", "--url", URL, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "put", "--calldata", "0x1", "0x2", "--proof-facts-file", "proof-facts.txt", ]; let snapbox = runner(&args); let output = snapbox.assert(); assert_stderr_contains( output, indoc! {r" error: the following required arguments were not provided: --proof-file "}, ); } ================================================ FILE: crates/sncast/tests/e2e/script/call.rs ================================================ use crate::helpers::constants::{SCRIPTS_DIR, URL}; use crate::helpers::fixtures::copy_script_directory_to_tempdir; use crate::helpers::runner::runner; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; #[tokio::test] async fn test_happy_case() { let tempdir = copy_script_directory_to_tempdir(SCRIPTS_DIR.to_owned() + "/misc", Vec::::new()); let script_name = "call_happy"; let args = vec!["script", "run", &script_name, "--url", URL]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().success().stdout_eq(indoc! {r" ... Success: Script execution completed Status: success "}); } #[tokio::test] async fn test_failing() { let tempdir = copy_script_directory_to_tempdir(SCRIPTS_DIR.to_owned() + "/misc", Vec::::new()); let script_name = "call_fail"; let args = vec!["script", "run", &script_name, "--url", URL]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r" Success: Script execution completed Status: script panicked 0x63616c6c206661696c6564 ('call failed') "}, ); } #[tokio::test] async fn test_call_invalid_entry_point() { let tempdir = copy_script_directory_to_tempdir(SCRIPTS_DIR.to_owned() + "/call", Vec::::new()); let script_name = "invalid_entry_point"; let args = vec!["script", "run", &script_name, "--url", URL]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r" ScriptCommandError::ProviderError(ProviderError::StarknetError(StarknetError::EntryPointNotFound(()))) Success: Script execution completed Status: success "}, ); } #[tokio::test] async fn test_call_invalid_address() { let tempdir = copy_script_directory_to_tempdir(SCRIPTS_DIR.to_owned() + "/call", Vec::::new()); let script_name = "invalid_address"; let args = vec!["script", "run", &script_name, "--url", URL]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r" ScriptCommandError::ProviderError(ProviderError::StarknetError(StarknetError::ContractNotFound(()))) Success: Script execution completed Status: success "}, ); } #[tokio::test] async fn test_call_invalid_calldata() { let tempdir = copy_script_directory_to_tempdir(SCRIPTS_DIR.to_owned() + "/call", Vec::::new()); let script_name = "invalid_calldata"; let args = vec!["script", "run", &script_name, "--url", URL]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r#" ScriptCommandError::ProviderError(ProviderError::StarknetError(StarknetError::ContractError(ContractErrorData { revert_error: ContractExecutionError::Nested(&ContractExecutionErrorInner { [..] error: ContractExecutionError::Message("["0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473"]") }) }))) ScriptCommandError::ProviderError(ProviderError::StarknetError(StarknetError::ContractError(ContractErrorData { revert_error: ContractExecutionError::Nested(&ContractExecutionErrorInner { [..] error: ContractExecutionError::Message("["0x4661696c656420746f20646573657269616c697a6520706172616d202332"]") }) }))) Success: Script execution completed Status: success "#}, ); } ================================================ FILE: crates/sncast/tests/e2e/script/declare.rs ================================================ use crate::helpers::constants::{ACCOUNT_FILE_PATH, SCRIPTS_DIR, URL}; use crate::helpers::fixtures::duplicate_contract_directory_with_salt; use crate::helpers::fixtures::{copy_script_directory_to_tempdir, get_accounts_path}; use crate::helpers::runner::runner; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; use test_case::test_case; #[test_case("oz_cairo_0"; "cairo_0_account")] #[test_case("oz_cairo_1"; "cairo_1_account")] #[test_case("oz"; "oz_account")] #[test_case("ready"; "ready_account")] #[test_case("braavos"; "braavos_account")] #[tokio::test] async fn test_wrong_contract_name(account: &str) { let contract_dir = duplicate_contract_directory_with_salt( SCRIPTS_DIR.to_owned() + "/map_script/contracts/", "dummy", "609", ); let tempdir = copy_script_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/declare/", vec![contract_dir.as_ref()], ); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "no_contract"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", account, "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r#" ScriptCommandError::ContractArtifactsNotFound(ErrorData { msg: "Mapaaaa" }) Success: Script execution completed Status: success "#}, ); } #[tokio::test] #[ignore = "TODO(#2912)"] async fn test_same_contract_twice() { let contract_dir = duplicate_contract_directory_with_salt( SCRIPTS_DIR.to_owned() + "/map_script/contracts/", "dummy", "69", ); let script_dir = copy_script_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/declare/", vec![contract_dir.as_ref()], ); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let script_name = "same_contract_twice"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user4", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(script_dir.path()); snapbox.assert().success().stdout_eq(indoc! {" ... success DeclareResult::Success(DeclareTransactionResult { class_hash: [..], transaction_hash: [..] }) DeclareResult::AlreadyDeclared(AlreadyDeclaredResult { class_hash: [..] }) Success: Script execution completed Status: success "}); } #[tokio::test] async fn test_with_invalid_max_fee() { let contract_dir = duplicate_contract_directory_with_salt( SCRIPTS_DIR.to_owned() + "/map_script/contracts/", "dummy", "19", ); let script_dir = copy_script_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/declare/", vec![contract_dir.as_ref()], ); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "with_invalid_max_fee"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user2", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(script_dir.path()); snapbox.assert().success().stdout_eq(indoc! {r" ... ScriptCommandError::ProviderError(ProviderError::StarknetError(StarknetError::InsufficientResourcesForValidate(()))) Success: Script execution completed Status: success "}); } #[tokio::test] async fn test_with_invalid_nonce() { let contract_dir = duplicate_contract_directory_with_salt( SCRIPTS_DIR.to_owned() + "/map_script/contracts/", "dummy", "21", ); let script_dir = copy_script_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/declare/", vec![contract_dir.as_ref()], ); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "with_invalid_nonce"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user4", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(script_dir.path()); snapbox.assert().success().stdout_eq(indoc! {r" ... ScriptCommandError::ProviderError(ProviderError::StarknetError(StarknetError::InvalidTransactionNonce(()))) Success: Script execution completed Status: success "}); } #[tokio::test] #[ignore = "TODO(#3091) Devnet response does not match te spec"] async fn test_insufficient_account_balance() { let contract_dir = duplicate_contract_directory_with_salt( SCRIPTS_DIR.to_owned() + "/map_script/contracts/", "dummy", "21", ); let script_dir = copy_script_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/declare/", vec![contract_dir.as_ref()], ); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "insufficient_account_balance"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user6", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(script_dir.path()); snapbox.assert().success().stdout_eq(indoc! {r" ... ScriptCommandError::ProviderError(ProviderError::StarknetError(StarknetError::InsufficientAccountBalance(()))) Success: Script execution completed Status: success "}); } #[tokio::test] async fn test_sncast_timed_out() { let contract_dir = duplicate_contract_directory_with_salt( SCRIPTS_DIR.to_owned() + "/map_script/contracts/", "dummy", "78", ); let script_dir = copy_script_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/declare/", vec![contract_dir.as_ref()], ); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let script_name = "time_out"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user8", "--wait-timeout", "1", "--wait-retry-interval", "1", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(script_dir.path()); snapbox.assert().success().stdout_eq(indoc! {r" ... ScriptCommandError::WaitForTransactionError(WaitForTransactionError::TimedOut(())) Success: Script execution completed Status: success "}); } #[tokio::test] async fn test_fee_settings() { let contract_dir = duplicate_contract_directory_with_salt( SCRIPTS_DIR.to_owned() + "/map_script/contracts/", "dummy", "100", ); let script_dir = copy_script_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/declare/", vec![contract_dir.as_ref()], ); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let script_name = "fee_settings"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user4", "script", "run", "--url", URL, &script_name, ]; let snapbox = runner(&args).current_dir(script_dir.path()); snapbox.assert().success().stdout_eq(indoc! {r" ... success Success: Script execution completed Status: success "}); } ================================================ FILE: crates/sncast/tests/e2e/script/deploy.rs ================================================ use crate::helpers::constants::{ACCOUNT_FILE_PATH, SCRIPTS_DIR, URL}; use crate::helpers::fixtures::{copy_script_directory_to_tempdir, get_accounts_path}; use crate::helpers::runner::runner; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; use test_case::test_case; #[test_case("oz_cairo_0"; "cairo_0_account")] #[test_case("oz_cairo_1"; "cairo_1_account")] #[test_case("oz"; "oz_account")] #[test_case("ready"; "ready_account")] #[test_case("braavos"; "braavos_account")] #[tokio::test] async fn test_with_calldata(account: &str) { let tempdir = copy_script_directory_to_tempdir(SCRIPTS_DIR.to_owned() + "/deploy", Vec::::new()); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "with_calldata"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", account, "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r" Success: Script execution completed Status: success "}, ); } #[tokio::test] async fn test_with_fee_settings() { let tempdir = copy_script_directory_to_tempdir(SCRIPTS_DIR.to_owned() + "/deploy", Vec::::new()); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "fee_settings"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user7", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r" Success: Script execution completed Status: success "}, ); } #[tokio::test] async fn test_same_salt_and_class_hash_deployed_twice() { let tempdir = copy_script_directory_to_tempdir(SCRIPTS_DIR.to_owned() + "/deploy", Vec::::new()); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "same_class_hash_and_salt"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user3", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r#" [..] ScriptCommandError::WaitForTransactionError(WaitForTransactionError::TransactionError(TransactionError::Reverted(ErrorData { msg: "Transaction execution has failed: [..] [..]: Error in the contract class constructor ([..]): Deployment failed: contract already deployed at address [..] " }))) Success: Script execution completed Status: success "#}, ); } #[tokio::test] async fn test_invalid_class_hash() { let tempdir = copy_script_directory_to_tempdir(SCRIPTS_DIR.to_owned() + "/deploy", Vec::::new()); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "invalid_class_hash"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user2", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r#" [..] ScriptCommandError::WaitForTransactionError(WaitForTransactionError::TransactionError(TransactionError::Reverted(ErrorData { msg: "Transaction execution has failed: [..] [..]: Error in the contract class constructor ([..]): Class with hash [..] is not declared. " }))) Success: Script execution completed Status: success "#}, ); } #[tokio::test] async fn test_invalid_call_data() { let tempdir = copy_script_directory_to_tempdir(SCRIPTS_DIR.to_owned() + "/deploy", Vec::::new()); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "invalid_calldata"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user5", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r#" [..] ScriptCommandError::WaitForTransactionError(WaitForTransactionError::TransactionError(TransactionError::Reverted(ErrorData { msg: "Transaction execution has failed: [..] [..]: Error in the contract class constructor ([..]): Execution failed. Failure reason: Error in contract [..]: 0x4661696c656420746f20646573657269616c697a6520706172616d202332 ('Failed to deserialize param #2'). " }))) Success: Script execution completed Status: success "#}, ); } #[tokio::test] async fn test_invalid_nonce() { let tempdir = copy_script_directory_to_tempdir(SCRIPTS_DIR.to_owned() + "/deploy", Vec::::new()); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "invalid_nonce"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user5", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {" ScriptCommandError::ProviderError(ProviderError::StarknetError(StarknetError::InvalidTransactionNonce(()))) Success: Script execution completed Status: success "}, ); } ================================================ FILE: crates/sncast/tests/e2e/script/general.rs ================================================ use crate::helpers::constants::{ACCOUNT_FILE_PATH, SCRIPTS_DIR, URL}; use crate::helpers::fixtures::{ assert_tx_entry_failed, assert_tx_entry_success, copy_directory_to_tempdir, copy_script_directory_to_tempdir, copy_workspace_directory_to_tempdir, duplicate_contract_directory_with_salt, get_accounts_path, }; use crate::helpers::runner::runner; use camino::Utf8PathBuf; use indoc::{formatdoc, indoc}; use shared::test_utils::output_assert::assert_stderr_contains; use sncast::get_default_state_file_name; use sncast::state::state_file::{ScriptTransactionStatus, read_txs_from_state_file}; use tempfile::tempdir; use test_case::test_case; #[test_case("oz_cairo_0"; "cairo_0_account")] #[test_case("oz_cairo_1"; "cairo_1_account")] #[test_case("oz"; "oz_account")] #[test_case("ready"; "ready_account")] #[test_case("braavos"; "braavos_account")] #[tokio::test] async fn test_happy_case(account: &str) { let contract_dir = duplicate_contract_directory_with_salt( SCRIPTS_DIR.to_owned() + "/map_script/contracts/", "dummy", account, ); let script_dir = copy_script_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/map_script/scripts/", vec![contract_dir.as_ref()], ); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "map_script"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", account, "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(script_dir.path()); snapbox.assert().success().stdout_eq(indoc! {r" ... Success: Script execution completed Status: success "}); } #[tokio::test] async fn test_run_script_from_different_directory_no_path_to_scarb_toml() { let tempdir = tempdir().expect("Unable to create temporary directory"); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "call_happy"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().failure(); assert_stderr_contains( output, "Error: Path to Scarb.toml manifest does not exist =[..]", ); } #[tokio::test] #[ignore = "TODO: Fix this tests in https://github.com/foundry-rs/starknet-foundry/issues/2351"] async fn test_fail_when_using_starknet_syscall() { let script_dir = copy_script_directory_to_tempdir(SCRIPTS_DIR.to_owned() + "/misc", Vec::::new()); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "using_starknet_syscall"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user1", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(script_dir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: script run Error: Got an exception while executing a hint: Hint Error: Starknet syscalls are not supported "}, ); } #[tokio::test] async fn test_incompatible_sncast_std_version() { let script_dir = copy_directory_to_tempdir(SCRIPTS_DIR.to_owned() + "/old_sncast_std/scripts"); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "map_script"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user4", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(script_dir.path()); let version = env!("CARGO_PKG_VERSION"); snapbox.assert().success().stdout_eq(formatdoc! {r" ... [WARNING] Package sncast_std version does not meet the recommended version requirement ={version}, it might result in unexpected behaviour ... "}); } #[tokio::test] async fn test_multiple_packages_not_picked() { let workspace_dir = copy_workspace_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/packages", vec!["crates/scripts/script1", "crates/scripts/script2"], Vec::::new().as_ref(), ); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "script1"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user4", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(workspace_dir.path()); let output = snapbox.assert().failure(); assert_stderr_contains( output, "Error: More than one package found in scarb metadata - specify package using --package flag", ); } #[tokio::test] async fn test_multiple_packages_happy_case() { let workspace_dir = copy_workspace_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/packages", vec!["crates/scripts/script1", "crates/scripts/script2"], Vec::::new().as_ref(), ); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "script1"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user4", "script", "run", "--package", &script_name, &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(workspace_dir.path()); snapbox.assert().success().stdout_eq(indoc! {r" ... Success: Script execution completed Status: success "}); } #[tokio::test] async fn test_run_script_display_debug_traits() { let contract_dir = duplicate_contract_directory_with_salt( SCRIPTS_DIR.to_owned() + "/map_script/contracts/", "dummy", "45", ); let script_dir = copy_script_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/map_script/scripts/", vec![contract_dir.as_ref()], ); let accounts_json_path = get_accounts_path("tests/data/accounts/accounts.json"); let script_name = "display_debug_traits_for_subcommand_responses"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user6", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(script_dir.path()); snapbox.assert().success().stdout_eq(indoc! {r" ... test declare_nonce: [..] debug declare_nonce: [..] Transaction hash: 0x[..] declare_result: class_hash: [..], transaction_hash: [..] debug declare_result: DeclareResult::Success(DeclareTransactionResult { class_hash: [..], transaction_hash: [..] }) Transaction hash: 0x[..] deploy_result: contract_address: [..], transaction_hash: [..] debug deploy_result: DeployResult { contract_address: [..], transaction_hash: [..] } Transaction hash: 0x[..] invoke_result: [..] debug invoke_result: InvokeResult { transaction_hash: [..] } call_result: [2] debug call_result: CallResult { data: [2] } Success: Script execution completed Status: success "}); } #[tokio::test] async fn test_nonexistent_account_address() { let script_name = "map_script"; let args = vec![ "--accounts-file", "../../../accounts/faulty_accounts.json", "--account", "with_nonexistent_address", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(SCRIPTS_DIR.to_owned() + "/map_script/scripts"); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: script run Error: Account with address 0x1010101010011aaabbcc not found on network SN_SEPOLIA "}, ); } #[tokio::test] async fn test_no_account_passed() { let script_name = "map_script"; let args = vec!["script", "run", &script_name, "--url", URL]; let snapbox = runner(&args).current_dir(SCRIPTS_DIR.to_owned() + "/map_script/scripts"); snapbox.assert().success().stdout_eq(indoc! {r#" ... Success: Script execution completed Status: script panicked "Account not defined. Please ensure the correct account is passed to `script run` command" ... "#}); } #[tokio::test] async fn test_missing_field() { let tempdir = copy_script_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/missing_field", Vec::::new(), ); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "missing_field"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user4", "script", "run", &script_name, "--url", URL, ]; // TODO(#4170): Replace [..] with actual error message when scarb 2.16.0 will be minimal test version. let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().failure().stdout_eq(indoc! {r" ... error[..]: Wrong number of arguments. Expected 3, found: 2 ... "}); } #[tokio::test] async fn test_run_script_twice_with_state_file_enabled() { let contract_dir = duplicate_contract_directory_with_salt( SCRIPTS_DIR.to_owned() + "/state_script/contracts/", "dummy", "34547", ); let script_dir = copy_script_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/state_script/scripts/", vec![contract_dir.as_ref()], ); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "state_script"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user7", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(script_dir.path()); snapbox.assert().success().stdout_eq(indoc! {r" ... Success: Script execution completed Status: success "}); let state_file_path = Utf8PathBuf::from_path_buf( script_dir .path() .join(get_default_state_file_name(script_name, "alpha-sepolia")), ) .unwrap(); let tx_entries_after_first_run = read_txs_from_state_file(&state_file_path).unwrap().unwrap(); assert!( tx_entries_after_first_run .transactions .iter() .all(|(_, value)| value.status == ScriptTransactionStatus::Success) ); assert_eq!(tx_entries_after_first_run.transactions.len(), 3); let snapbox = runner(&args).current_dir(script_dir.path()); snapbox.assert().success().stdout_eq(indoc! {r" ... Success: Script execution completed Status: success "}); let tx_entries_after_second_run = read_txs_from_state_file(&state_file_path).unwrap().unwrap(); assert_eq!(tx_entries_after_first_run, tx_entries_after_second_run); } #[tokio::test] async fn test_state_file_contains_all_failed_txs() { let script_dir = copy_script_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/state_file/", Vec::::new(), ); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "all_tx_fail"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user10", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(script_dir.path()); snapbox.assert().success().stdout_eq(indoc! {r" ... Success: Script execution completed Status: success "}); let state_file_path = Utf8PathBuf::from_path_buf( script_dir .path() .join(get_default_state_file_name(script_name, "alpha-sepolia")), ) .unwrap(); let tx_entries_after_first_run = read_txs_from_state_file(&state_file_path).unwrap().unwrap(); assert_eq!(tx_entries_after_first_run.transactions.len(), 3); let declare_tx_entry = tx_entries_after_first_run .get("2341f038132e07bd9fa3cabf5fa0c3fde26b0fc03e7b09198dbd230e1b1e071c") .unwrap(); assert_tx_entry_failed( declare_tx_entry, "declare", ScriptTransactionStatus::Error, vec![ "Failed to find Not_this_time artifact in starknet_artifacts.json file. Please make sure you have specified correct package using `--package` flag.", ], ); let deploy_tx_entry = tx_entries_after_first_run .get("2402e1bcaf641961a4e97b76cad1e91f9522e4a34e57b5f740f3ea529b853c8f") .unwrap(); assert_tx_entry_failed( deploy_tx_entry, "deploy", ScriptTransactionStatus::Fail, vec!["Class with hash 0x", "is not declared"], ); let invoke_tx_entry = tx_entries_after_first_run .get("9e0f8008202594e57674569610b5cd22079802b0929f570dfe118b107cb24221") .unwrap(); assert_tx_entry_failed( invoke_tx_entry, "invoke", ScriptTransactionStatus::Fail, vec!["Requested contract address", "is not deployed"], ); } #[tokio::test] async fn test_state_file_rerun_failed_tx() { let script_dir = copy_script_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/state_file/", Vec::::new(), ); let script_name = "rerun_failed_tx"; let map_invoke_tx_id = "31829eae07da513c7e6f457b9ac48af0004512db23efeae38734af97834bb273"; let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let state_file_path = Utf8PathBuf::from_path_buf( script_dir .path() .join(get_default_state_file_name(script_name, "alpha-sepolia")), ) .unwrap(); let tx_entries_before = read_txs_from_state_file(&state_file_path).unwrap().unwrap(); assert_eq!(tx_entries_before.transactions.len(), 1); let invoke_tx_entry_before = tx_entries_before.get(map_invoke_tx_id).unwrap(); assert_tx_entry_failed( invoke_tx_entry_before, "invoke", ScriptTransactionStatus::Error, vec!["Requested contract address", "is not deployed"], ); let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user4", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(script_dir.path()); snapbox.assert().success().stdout_eq(indoc! {r" ... Success: Script execution completed Status: success "}); let tx_entries_after_first_run = read_txs_from_state_file(&state_file_path).unwrap().unwrap(); assert_eq!(tx_entries_after_first_run.transactions.len(), 1); let invoke_tx_entry = tx_entries_after_first_run.get(map_invoke_tx_id).unwrap(); assert_tx_entry_success(invoke_tx_entry, "invoke"); } #[tokio::test] async fn test_using_release_profile() { let contract_dir = duplicate_contract_directory_with_salt( SCRIPTS_DIR.to_owned() + "/map_script/contracts/", "dummy", "69420", ); let script_dir = copy_script_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/map_script/scripts/", vec![contract_dir.as_ref()], ); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "map_script"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user5", "--profile", "release", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(script_dir.path()); snapbox.assert().success().stdout_eq(indoc! {r" ... Success: Script execution completed Status: success "}); } ================================================ FILE: crates/sncast/tests/e2e/script/init.rs ================================================ use crate::helpers::runner::runner; use camino::Utf8PathBuf; use indoc::{formatdoc, indoc}; use scarb_api::ScarbCommand; use scarb_api::version::scarb_version; use semver::Version; use shared::test_utils::output_assert::{assert_stderr_contains, assert_stdout_contains}; use sncast::helpers::constants::INIT_SCRIPTS_DIR; use sncast::helpers::scarb_utils::get_cairo_version; use tempfile::TempDir; const SCARB_2_14_0: Version = Version::new(2, 14, 0); #[test] fn test_script_init_happy_case() { let script_name = "my_script"; let temp_dir = TempDir::new().expect("Unable to create a temporary directory"); let snapbox = runner(&["script", "init", script_name]).current_dir(temp_dir.path()); snapbox.assert().stdout_eq(formatdoc! {r" [WARNING] [..] Success: Script initialization completed Initialized `{script_name}` at [..]/scripts/{script_name} "}); let script_dir_path = temp_dir.path().join(INIT_SCRIPTS_DIR).join(script_name); let scarb_toml_path = script_dir_path.join("Scarb.toml"); let scarb_toml_content = std::fs::read_to_string(&scarb_toml_path).unwrap(); let lib_cairo_content = std::fs::read_to_string(script_dir_path.join("src/lib.cairo")).unwrap(); let main_file_content = std::fs::read_to_string(script_dir_path.join(format!("src/{script_name}.cairo"))).unwrap(); let cast_version = env!("CARGO_PKG_VERSION"); let scarb_toml_path = Utf8PathBuf::from_path_buf(scarb_toml_path).unwrap(); let cairo_version = get_cairo_version(&scarb_toml_path).unwrap(); let scarb_version = scarb_version().unwrap().scarb; let expected_scarb_toml = if scarb_version >= SCARB_2_14_0 { formatdoc!( r#" [package] name = "{script_name}" version = "0.1.0" edition = [..] # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [executable] [cairo] enable-gas = false [dependencies] cairo_execute = "{cairo_version}" sncast_std = "{cast_version}" starknet = ">={cairo_version}" "# ) } else { formatdoc!( r#" [package] name = "{script_name}" version = "0.1.0" edition = [..] # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] sncast_std = "{cast_version}" starknet = ">={cairo_version}" "# ) }; snapbox::assert_data_eq!(scarb_toml_content, expected_scarb_toml); assert_eq!( lib_cairo_content, formatdoc! {r" mod {script_name}; "} ); assert_eq!( main_file_content, indoc! {r#" use sncast_std::call; // The example below uses a contract deployed to the Sepolia testnet const CONTRACT_ADDRESS: felt252 = 0x07e867f1fa6da2108dd2b3d534f1fbec411c5ec9504eb3baa1e49c7a0bef5ab5; fn main() { let call_result = call( CONTRACT_ADDRESS.try_into().unwrap(), selector!("get_greeting"), array![], ) .expect('call failed'); assert(*call_result.data[1] == 'Hello, Starknet!', *call_result.data[1]); println!("{:?}", call_result); } "#} ); } #[test] fn test_init_fails_when_scripts_dir_exists_in_cwd() { let script_name = "my_script"; let temp_dir = TempDir::new().expect("Unable to create a temporary directory"); std::fs::create_dir_all(temp_dir.path().join(INIT_SCRIPTS_DIR)) .expect("Failed to create scripts directory in the current temp directory"); let snapbox = runner(&["script", "init", script_name]).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: script init Error: Scripts directory already exists at [..] "}, ); } #[test] fn test_init_twice_fails() { let script_name = "my_script"; let temp_dir = TempDir::new().expect("Unable to create a temporary directory"); let args = vec!["script", "init", script_name]; runner(&args) .current_dir(temp_dir.path()) .assert() .success(); assert!(temp_dir.path().join(INIT_SCRIPTS_DIR).exists()); let snapbox = runner(&args).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: script init Error: Scripts directory already exists at [..] "}, ); } #[ignore = "Fails if plugin is unreleased, fixed and restore after release"] #[test] fn test_initialized_script_compiles() { let script_name = "my_script"; let temp_dir = TempDir::new().expect("Unable to create a temporary directory"); let snapbox = runner(&["script", "init", script_name]).current_dir(temp_dir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, formatdoc! {r" [WARNING] The newly created script isn't auto-added to the workspace. [..] Success: Script initialization completed Initialized `{script_name}` at [..]{script_name} "}, ); let script_dir_path = temp_dir.path().join(INIT_SCRIPTS_DIR).join(script_name); // Using a tag during the release process will cause the test to fail as the new tag won't exist in the repository yet // This command will overwrite sncast_std dependency to use the master branch instead of a tag ScarbCommand::new_with_stdio() .current_dir(&script_dir_path) .args([ "--offline", "add", "sncast_std", "--git", "https://github.com/foundry-rs/starknet-foundry.git", "--branch", "master", ]) .run() .expect("Failed to overwrite sncast_std dependency in Scarb.toml"); ScarbCommand::new_with_stdio() .current_dir(&script_dir_path) .arg("build") .run() .expect("Failed to compile the initialized script"); } ================================================ FILE: crates/sncast/tests/e2e/script/invoke.rs ================================================ use crate::helpers::constants::{ACCOUNT_FILE_PATH, SCRIPTS_DIR, URL}; use crate::helpers::fixtures::{copy_script_directory_to_tempdir, get_accounts_path}; use crate::helpers::runner::runner; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; use test_case::test_case; #[test_case("oz_cairo_0"; "cairo_0_account")] #[test_case("oz_cairo_1"; "cairo_1_account")] #[test_case("oz"; "oz_account")] #[test_case("ready"; "ready_account")] #[test_case("braavos"; "braavos_account")] #[tokio::test] async fn test_insufficient_resource_for_validate(account: &str) { let script_dir = copy_script_directory_to_tempdir(SCRIPTS_DIR.to_owned() + "/invoke", Vec::::new()); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "max_fee_too_low"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", account, "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(script_dir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r" ScriptCommandError::ProviderError(ProviderError::StarknetError(StarknetError::InsufficientResourcesForValidate(()))) Success: Script execution completed Status: success "}, ); } #[tokio::test] async fn test_contract_does_not_exist() { let script_dir = copy_script_directory_to_tempdir(SCRIPTS_DIR.to_owned() + "/invoke", Vec::::new()); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "contract_does_not_exist"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user4", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(script_dir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r#" [..] ScriptCommandError::WaitForTransactionError(WaitForTransactionError::TransactionError(TransactionError::Reverted(ErrorData { msg: "Transaction execution has failed: [..] [..]: Error in the called contract ([..]): Requested contract address [..] is not deployed. " }))) Success: Script execution completed Status: success "#}, ); } #[test] fn test_wrong_function_name() { let script_dir = copy_script_directory_to_tempdir(SCRIPTS_DIR.to_owned() + "/invoke", Vec::::new()); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "wrong_function_name"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user4", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(script_dir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r#" [..] ScriptCommandError::ProviderError(ProviderError::StarknetError(StarknetError::TransactionExecutionError(TransactionExecutionErrorData { transaction_index: 0, execution_error: ContractExecutionError::Nested(&ContractExecutionErrorInner { contract_address: [..], class_hash: [..], selector: [..], error: ContractExecutionError::Nested(&ContractExecutionErrorInner { contract_address: [..], class_hash: [..], selector: [..], error: ContractExecutionError::Nested(&ContractExecutionErrorInner { contract_address: [..], class_hash: [..], selector: [..], error: ContractExecutionError::Message("Transaction execution has failed: 0: Error in the called contract (contract address: 0x03ffc270312cbefaf2fb4a88e97cc186797bada41a291331186ec5ca316e32fa, class hash: 0x05b4b537eaa2399e3aa99c4e2e0208ebd6c71bc1467938cd52c798c601e43564, selector: [..]): Execution failed. Failure reason: Error in contract (contract address: [..], class hash: [..], selector: [..]): Error in contract (contract address: [..], class hash: [..], selector: [..]): [..] ('ENTRYPOINT_NOT_FOUND'). ["0x454e545259504f494e545f4e4f545f464f554e44"]") }) }) }) }))) Success: Script execution completed Status: success "#}, ); } #[test] fn test_wrong_calldata() { let script_dir = copy_script_directory_to_tempdir(SCRIPTS_DIR.to_owned() + "/invoke", Vec::::new()); let accounts_json_path = get_accounts_path(ACCOUNT_FILE_PATH); let script_name = "wrong_calldata"; let args = vec![ "--accounts-file", accounts_json_path.as_str(), "--account", "user4", "script", "run", &script_name, "--url", URL, ]; let snapbox = runner(&args).current_dir(script_dir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r#" ScriptCommandError::ProviderError(ProviderError::StarknetError(StarknetError::TransactionExecutionError(TransactionExecutionErrorData { transaction_index: 0, execution_error: ContractExecutionError::Nested(&ContractExecutionErrorInner { contract_address: [..], class_hash: [..], selector: [..], error: ContractExecutionError::Nested(&ContractExecutionErrorInner { contract_address: [..], class_hash: [..], selector: [..], error: ContractExecutionError::Nested(&ContractExecutionErrorInner { contract_address: [..], class_hash: [..], selector: [..], error: ContractExecutionError::Message("Transaction execution has failed: 0: Error in the called contract (contract address: [..], class hash: [..], selector: [..]): Execution failed. Failure reason: Error in contract (contract address: [..], class hash: [..], selector: [..]): Error in contract (contract address: [..], class hash: [..], selector: [..]): [..] ('Failed to deserialize param #2'). ["0x4661696c656420746f20646573657269616c697a6520706172616d202332"]") }) }) }) }))) Success: Script execution completed Status: success "#}, ); } ================================================ FILE: crates/sncast/tests/e2e/script/mod.rs ================================================ mod call; mod declare; mod deploy; mod general; mod init; mod invoke; mod tx_status; ================================================ FILE: crates/sncast/tests/e2e/script/tx_status.rs ================================================ use crate::helpers::constants::{SCRIPTS_DIR, URL}; use crate::helpers::fixtures::copy_script_directory_to_tempdir; use crate::helpers::runner::runner; use indoc::indoc; use shared::test_utils::output_assert::assert_stdout_contains; #[tokio::test] async fn test_tx_status_status_reverted() { let tempdir = copy_script_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/tx_status", Vec::::new(), ); let script_name = "status_reverted"; let args = vec!["script", "run", &script_name, "--url", URL]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r" finality_status: AcceptedOnL1, execution_status: Reverted TxStatusResult { finality_status: FinalityStatus::AcceptedOnL1(()), execution_status: Option::Some(ExecutionStatus::Reverted(())) } Success: Script execution completed Status: success "}, ); } #[tokio::test] async fn test_tx_status_status_succeeded() { let tempdir = copy_script_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/tx_status", Vec::::new(), ); let script_name = "status_succeeded"; let args = vec!["script", "run", &script_name, "--url", URL]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r" finality_status: AcceptedOnL1, execution_status: Succeeded TxStatusResult { finality_status: FinalityStatus::AcceptedOnL1(()), execution_status: Option::Some(ExecutionStatus::Succeeded(())) } Success: Script execution completed Status: success "}, ); } #[tokio::test] async fn test_tx_status_incorrect_transaction_hash() { let tempdir = copy_script_directory_to_tempdir( SCRIPTS_DIR.to_owned() + "/tx_status", Vec::::new(), ); let script_name = "incorrect_transaction_hash"; let args = vec!["script", "run", &script_name, "--url", URL]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r" ScriptCommandError::ProviderError(ProviderError::StarknetError(StarknetError::TransactionHashNotFound(()))) Success: Script execution completed Status: success "}, ); } ================================================ FILE: crates/sncast/tests/e2e/selector.rs ================================================ use crate::helpers::runner::runner; use indoc::indoc; use shared::test_utils::output_assert::{assert_stderr_contains, assert_stdout_contains}; #[test] fn test_selector_happy_case() { let args = vec!["utils", "selector", "transfer"]; let snapbox = runner(&args); let output = snapbox.assert().success(); assert_stdout_contains( output, indoc! {r" Selector: 0x0083afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e "}, ); } #[test] fn test_selector_json_output() { let args = vec!["--json", "utils", "selector", "transfer"]; let snapbox = runner(&args); let output = snapbox.assert().success(); let stdout = output.get_output().stdout.clone(); let json: serde_json::Value = serde_json::from_slice(&stdout).unwrap(); assert_eq!(json["command"], "selector"); assert_eq!(json["type"], "response"); assert_eq!( json["selector"], "0x0083afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e" ); } #[test] fn test_selector_with_parentheses() { let args = vec!["utils", "selector", "transfer(u256)"]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains( output, indoc! {r" Error: Parentheses and the content within should not be supplied "}, ); } ================================================ FILE: crates/sncast/tests/e2e/serialize.rs ================================================ use crate::helpers::constants::{ DATA_TRANSFORMER_CONTRACT_ABI_PATH, DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA, DATA_TRANSFORMER_CONTRACT_CLASS_HASH_SEPOLIA, MAP_CONTRACT_ADDRESS_SEPOLIA, URL, }; use crate::helpers::runner::runner; use crate::helpers::shell::os_specific_shell; use camino::Utf8PathBuf; use indoc::indoc; use shared::test_utils::output_assert::assert_stderr_contains; use snapbox::cargo_bin; use tempfile::tempdir; #[tokio::test] async fn test_happy_case() { let tempdir = tempdir().unwrap(); let calldata = r"NestedStructWithField { a: SimpleStruct { a: 0x24 }, b: 96 }"; let args = vec![ "utils", "serialize", "--arguments", calldata, "--contract-address", DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA, "--function", "nested_struct_fn", "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().success().stdout_eq(indoc! {r" Calldata: [0x24, 0x60] "}); } #[tokio::test] async fn test_happy_case_class_hash() { let tempdir = tempdir().unwrap(); let calldata = r"NestedStructWithField { a: SimpleStruct { a: 0x24 }, b: 96 }"; let args = vec![ "utils", "serialize", "--arguments", calldata, "--class-hash", DATA_TRANSFORMER_CONTRACT_CLASS_HASH_SEPOLIA, "--function", "nested_struct_fn", "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().success().stdout_eq(indoc! {r" Calldata: [0x24, 0x60] "}); } #[tokio::test] async fn test_happy_case_abi_file() { let tempdir = tempdir().unwrap(); let abi_file_path = Utf8PathBuf::from(DATA_TRANSFORMER_CONTRACT_ABI_PATH); let temp_abi_file_path = tempdir.path().join(abi_file_path.file_name().unwrap()); std::fs::copy(abi_file_path, &temp_abi_file_path) .expect("Failed to copy ABI file to temp directory"); let calldata = r"NestedStructWithField { a: SimpleStruct { a: 0x24 }, b: 96 }"; let args = vec![ "utils", "serialize", "--arguments", calldata, "--abi-file", temp_abi_file_path.to_str().unwrap(), "--function", "nested_struct_fn", ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().success().stdout_eq(indoc! {r" Calldata: [0x24, 0x60] "}); } #[tokio::test] async fn test_abi_file_missing_function() { let tempdir = tempdir().unwrap(); let abi_file_path = Utf8PathBuf::from("tests/data/files/data_transformer_contract_abi_missing_function.json"); let temp_abi_file_path = tempdir.path().join(abi_file_path.file_name().unwrap()); std::fs::copy(abi_file_path, &temp_abi_file_path) .expect("Failed to copy ABI file to temp directory"); let calldata = r"NestedStructWithField { a: SimpleStruct { a: 0x24 }, b: 96 }"; let args = vec![ "utils", "serialize", "--arguments", calldata, "--abi-file", temp_abi_file_path.to_str().unwrap(), "--function", "nested_struct_fn", ]; let output = runner(&args).current_dir(tempdir.path()).assert().failure(); assert_stderr_contains( output, indoc! {r#" Error: Function with selector "0x2cf7c96d7437a80a891adac280b9089dbe00c5413e7d253bbc87845271ae772" not found in ABI of the contract "#}, ); } #[tokio::test] async fn test_abi_file_missing_type() { let tempdir = tempdir().unwrap(); let abi_file_path = Utf8PathBuf::from("tests/data/files/data_transformer_contract_abi_missing_type.json"); let temp_abi_file_path = tempdir.path().join(abi_file_path.file_name().unwrap()); std::fs::copy(abi_file_path, &temp_abi_file_path) .expect("Failed to copy ABI file to temp directory"); let calldata = r"NestedStructWithField { a: SimpleStruct { a: 0x24 }, b: 96 }"; let args = vec![ "utils", "serialize", "--arguments", calldata, "--abi-file", temp_abi_file_path.to_str().unwrap(), "--function", "nested_struct_fn", ]; let output = runner(&args).current_dir(tempdir.path()).assert().failure(); assert_stderr_contains( output, indoc! {r#" Error: Error while processing Cairo-like calldata Struct "NestedStructWithField" not found in ABI "#}, ); } #[tokio::test] async fn test_happy_case_json() { let tempdir = tempdir().unwrap(); let calldata = r"NestedStructWithField { a: SimpleStruct { a: 0x24 }, b: 96 }"; let args = vec![ "--json", "utils", "serialize", "--arguments", calldata, "--contract-address", DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA, "--function", "nested_struct_fn", "--url", URL, ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().success().stdout_eq(indoc! {r#" {"calldata":["0x24","0x60"],"command":"serialize","type":"response"} "#}); } #[tokio::test] async fn test_contract_does_not_exist() { let args = vec![ "utils", "serialize", "--arguments", "some_calldata", "--contract-address", "0x1", "--function", "nested_struct_fn", "--url", URL, ]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains( output, "Error: An error occurred in the called contract[..]Requested contract address[..]is not deployed[..]", ); } #[tokio::test] async fn test_wrong_function_name() { let calldata = r"NestedStructWithField { a: SimpleStruct { a: 0x24 }, b: 96 }"; let args = vec![ "utils", "serialize", "--arguments", calldata, "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--function", "nonexistent_function", "--url", URL, ]; let snapbox = runner(&args); let output = snapbox.assert().failure(); assert_stderr_contains( output, r#"Error: Function with selector "0x38a013a14030cb08ae86482a9e0f3bad42daeb0222bdfe0634d77388deab9b9" not found in ABI of the contract"#, ); } #[tokio::test] async fn test_rpc_args_not_passed_when_using_class_hash() { let tempdir = tempdir().unwrap(); let calldata = r"NestedStructWithField { a: SimpleStruct { a: 0x24 }, b: 96 }"; let args = vec![ "utils", "serialize", "--arguments", calldata, "--class-hash", DATA_TRANSFORMER_CONTRACT_CLASS_HASH_SEPOLIA, "--function", "nested_struct_fn", ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().failure().stderr_eq(indoc! {r" Error: Either `--network` or `--url` must be provided when using `--class-hash` "}); } #[tokio::test] async fn test_rpc_args_not_passed_when_using_contract_address() { let tempdir = tempdir().unwrap(); let calldata = r"NestedStructWithField { a: SimpleStruct { a: 0x24 }, b: 96 }"; let args = vec![ "utils", "serialize", "--arguments", calldata, "--contract-address", DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA, "--function", "nested_struct_fn", ]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().failure().stderr_eq(indoc! {r" Error: Either `--network` or `--url` must be provided when using `--contract-address` "}); } #[tokio::test] async fn test_happy_case_shell() { let binary_path = cargo_bin!("sncast"); let command = os_specific_shell(&Utf8PathBuf::from("tests/shell/serialize")); let snapbox = command .arg(binary_path) .arg(URL) .arg(DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA); snapbox.assert().success(); } ================================================ FILE: crates/sncast/tests/e2e/show_config.rs ================================================ use crate::helpers::{constants::URL, runner::runner}; use configuration::test_utils::copy_config_to_tempdir; use indoc::formatdoc; use shared::test_utils::output_assert::assert_stderr_contains; #[tokio::test] async fn test_show_config_from_snfoundry_toml() { let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); let args = vec!["show-config"]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().success().stdout_eq(formatdoc! {r" Chain ID: alpha-sepolia RPC URL: {} Account: user1 Accounts File Path: ../account-file Wait Timeout: 300s Wait Retry Interval: 5s Show Explorer Links: true ", URL}); } #[tokio::test] async fn test_show_config_from_cli() { let args = vec![ "--account", "/path/to/account.json", "--keystore", "../keystore", "--wait-timeout", "2", "--wait-retry-interval", "1", "show-config", "--url", URL, ]; let snapbox = runner(&args); snapbox.assert().success().stdout_eq(formatdoc! {r" Chain ID: alpha-sepolia RPC URL: {} Account: /path/to/account.json Keystore: ../keystore Wait Timeout: 2s Wait Retry Interval: 1s Show Explorer Links: true ", URL}); } #[tokio::test] async fn test_show_config_from_cli_and_snfoundry_toml() { let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); let args = vec!["--account", "user2", "--profile", "profile2", "show-config"]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().success().stdout_eq(formatdoc! {r" Profile: profile2 Chain ID: alpha-sepolia RPC URL: {} Account: user2 Accounts File Path: ../account-file Wait Timeout: 300s Wait Retry Interval: 5s Show Explorer Links: true Block Explorer: ViewBlock ", URL}); } #[tokio::test] async fn test_show_config_when_no_keystore() { let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); let args = vec!["--profile", "profile4", "show-config"]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().success().stdout_eq(formatdoc! {r" Profile: profile4 Chain ID: alpha-sepolia RPC URL: {} Account: user3 Accounts File Path: ../account-file Wait Timeout: 300s Wait Retry Interval: 5s Show Explorer Links: true ", URL}); } #[tokio::test] async fn test_show_config_when_keystore() { let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); let args = vec!["--profile", "profile3", "show-config"]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().success().stdout_eq(formatdoc! {r" Profile: profile3 Chain ID: alpha-sepolia RPC URL: {} Account: /path/to/account.json Keystore: ../keystore Wait Timeout: 300s Wait Retry Interval: 5s Show Explorer Links: true ", URL}); } #[tokio::test] async fn test_show_config_no_url() { let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); let args = vec!["--profile", "profile6", "show-config"]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().success().stdout_eq(formatdoc! {r" Profile: profile6 Account: user1 Accounts File Path: /path/to/account.json Wait Timeout: 500s Wait Retry Interval: 10s Show Explorer Links: false "}); } #[tokio::test] async fn test_show_config_with_network() { let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None); let args = vec!["--profile", "profile7", "show-config"]; let snapbox = runner(&args).current_dir(tempdir.path()); snapbox.assert().success().stdout_eq(formatdoc! {r" Profile: profile7 Chain ID: alpha-sepolia Network: sepolia Account: user1 Accounts File Path: /path/to/account.json Wait Timeout: 300s Wait Retry Interval: 5s Show Explorer Links: true "}); } #[tokio::test] async fn test_only_one_from_url_and_network_allowed() { let tempdir = copy_config_to_tempdir("tests/data/files/invalid_snfoundry.toml", None); let args = vec!["--profile", "url_and_network", "show-config"]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().failure(); assert_stderr_contains( output, "Error: Failed to load config: Only one of `url` or `network` may be specified", ); } #[tokio::test] async fn test_stark_scan_as_block_explorer() { let tempdir = copy_config_to_tempdir("tests/data/files/invalid_snfoundry.toml", None); let args = vec!["--profile", "profile_with_stark_scan", "show-config"]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().failure(); assert_stderr_contains( output, "Error: Failed to load config: starkscan.co was terminated and `'StarkScan'` is no longer available. Please set `block-explorer` to `'Voyager'` or other explorer of your choice.", ); } ================================================ FILE: crates/sncast/tests/e2e/transaction.rs ================================================ use crate::e2e::account::create_account; use crate::helpers::constants::{MAP_CONTRACT_DECLARE_TX_HASH_SEPOLIA, URL}; use crate::helpers::fixtures::get_transaction_hash; use crate::helpers::runner::runner; use conversions::string::IntoHexStr; use indoc::indoc; use shared::test_utils::output_assert::{assert_stderr_contains, assert_stdout_contains}; use sncast::helpers::constants::OZ_CLASS_HASH; const INVOKE_TX_HASH: &str = "0x07d2067cd7675f88493a9d773b456c8d941457ecc2f6201d2fe6b0607daadfd1"; #[tokio::test] async fn test_get_invoke_transaction() { let args = vec!["get", "tx", INVOKE_TX_HASH, "--url", URL]; let snapbox = runner(&args).env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1"); let output = snapbox.assert().success(); output.stdout_eq(indoc! {r" Success: Transaction found Type: INVOKE Version: 3 Transaction Hash: 0x07d2067cd7675f88493a9d773b456c8d941457ecc2f6201d2fe6b0607daadfd1 Sender Address: 0x01d091b30a2d20ca2509579f8beae26934bfdc3725c0b497f50b353b7a3c636f Nonce: 98206 Calldata: [0x1, 0x424ce41bea300e095e763d9fb4316af76c9da9c0fa926009f25b42b6f4ad04a, 0xc844fd57777b0cd7e75c8ea68deec0adf964a6308da7a58de32364b7131cc8, 0x13, 0x441b0ab7fcd3923bd830e146e99ed90c4aebd19951eb6ed7b3713241aa8af, 0x29e701, 0xf29c0193adc354752489f1a7af2f507d72a5e5b76cce705094d05d72e21ab5, 0x6655cb7c, 0x304020100000000000000000000000000000000000000000000000000000000, 0x4, 0x27693e402, 0x276a2b3d2, 0x276a3f070, 0x276aeecf0, 0xb9eab07caffbd5538, 0x1, 0x2, 0x6771e459d1e5563ec13af0ca40f04406ff4b70e6cc9a534dce12957f46c0f24, 0x36383aebe2151145a66dd7a87d9c885a862339e35d2ee0bd9df4075d17a8979, 0x2cb74dff29a13dd5d855159349ec92f943bacf0547ff3734e7d84a15d08cbc5, 0xb1a29e2cfed2f0a9d5f137845280bb6ce746f2f4b6a2dd05ec794171f4012, 0x1f85c957582717816bd2c910ac678caf007f6f84d71bc5a95f38de0b6435163, 0x4225d1c8ee8e451a25e30c10689ef898e11ccf5c0f68d0fc7876c47b318e946] Account Deployment Data: [] Resource Bounds L1 Gas: max_amount=39865, max_price_per_unit=226571933234745 Resource Bounds L1 Data Gas: max_amount=0, max_price_per_unit=0 Resource Bounds L2 Gas: max_amount=0, max_price_per_unit=0 Tip: 0 Paymaster Data: [] Nonce DA Mode: L1 Fee DA Mode: L1 Signature: [0x1e45e75e772a8f21592cac2cb7d662ee53af236155621ae580bd02bbfa13bbc, 0x3528f5508bd323fa3301ca59823c0b35f17bc29a9a70d919be2ecd6f941d3db] To see transaction details, visit: transaction: https://sepolia.voyager.online/tx/0x07d2067cd7675f88493a9d773b456c8d941457ecc2f6201d2fe6b0607daadfd1 "}); } #[tokio::test] async fn test_json_output() { let args = vec!["--json", "get", "tx", INVOKE_TX_HASH, "--url", URL]; let snapbox = runner(&args); let output = snapbox.assert().success(); let stdout = output.get_output().stdout.clone(); let json: serde_json::Value = serde_json::from_slice(&stdout).unwrap(); assert_eq!(json["command"], "get tx"); assert_eq!(json["type"], "response"); assert_eq!(json["transaction_type"], "INVOKE_V3"); let tx = &json["transaction"]; assert!(tx["transaction_hash"].as_str().unwrap().starts_with("0x")); assert!(tx["sender_address"].as_str().unwrap().starts_with("0x")); assert!(tx["nonce"].as_str().unwrap().starts_with("0x")); assert!(tx["calldata"].is_array()); assert!(tx["signature"].is_array()); assert!(tx["tip"].as_str().unwrap().starts_with("0x")); assert!(tx["paymaster_data"].is_array()); assert_eq!(tx["nonce_data_availability_mode"], "L1"); assert_eq!(tx["fee_data_availability_mode"], "L1"); assert!(tx["account_deployment_data"].is_array()); } #[tokio::test] async fn test_deploy_account_transaction() { let tempdir = create_account(false, &OZ_CLASS_HASH.into_hex_string(), "oz").await; let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "--json", "account", "deploy", "--url", URL, "--name", "my_account", ]; let snapbox = runner(&args).current_dir(tempdir.path()); let output = snapbox.assert().success(); let hash = get_transaction_hash(&output.get_output().stdout); let hash_hex = format!("{hash:#x}"); let get_tx_args = vec!["get", "tx", hash_hex.as_str(), "--url", URL]; let get_tx_output = runner(&get_tx_args) .current_dir(tempdir.path()) .assert() .success(); assert_stdout_contains( get_tx_output, indoc! {r" Success: Transaction found Type: DEPLOY ACCOUNT Version: 3 Transaction Hash: 0x[..] Nonce: 0 Class Hash: 0x05b4b537eaa2399e3aa99c4e2e0208ebd6c71bc1467938cd52c798c601e43564 Contract Address Salt: 0x[..] Constructor Calldata: [0x[..]] Resource Bounds L1 Gas: max_amount=0, max_price_per_unit=1500000000 Resource Bounds L1 Data Gas: max_amount=672, max_price_per_unit=1500000000 Resource Bounds L2 Gas: max_amount=3302760, max_price_per_unit=1500000000 Tip: 0 Paymaster Data: [] Nonce DA Mode: L1 Fee DA Mode: L1 Signature: [0x[..], 0x[..]] "}, ); } #[tokio::test] async fn test_declare_transaction() { let args = vec![ "get", "tx", MAP_CONTRACT_DECLARE_TX_HASH_SEPOLIA, "--url", URL, ]; let snapbox = runner(&args); let output = snapbox.assert().success(); output.stdout_eq(indoc! {r" Success: Transaction found Type: DECLARE Version: 2 Transaction Hash: 0x04f644d3ea723b9c28781f2bea76e9c2cd8cc667b2861faf66b4e45402ea221c Sender Address: 0x0709cebece48663c3f0ece4b4553c9b1aaf325a3de5eb93792d5edfc3fdc42a8 Nonce: 6 Class Hash: 0x02a09379665a749e609b4a8459c86fe954566a6beeaddd0950e43f6c700ed321 Compiled Class Hash: 0x023ea170b0fc421a0ba919e32310cab42c16b3c9ded46add315a94ae63f5dde4 Max Fee: 0x1559951f089bf Signature: [0xb587f3ac9d32ea2ef741409681d8f255e300cbeb633e28a8557bcd1464f623, 0x600ddf65382e33485a9ed0cdb632233cf83a5e971c75e9dc9c31d585cec3655] "}); } #[tokio::test] async fn test_nonexistent_transaction() { let args = vec!["get", "tx", "0x1", "--url", URL]; let snapbox = runner(&args); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: get tx Error: Failed to get transaction: Transaction with provided hash was not found (does not exist) "}, ); } // TODO (#4258): Add test for invoke tx with `proof_facts` ================================================ FILE: crates/sncast/tests/e2e/tx_status.rs ================================================ use crate::helpers::constants::URL; use crate::helpers::runner::runner; use indoc::indoc; use shared::test_utils::output_assert::assert_stderr_contains; const SUCCEEDED_TX_HASH: &str = "0x07d2067cd7675f88493a9d773b456c8d941457ecc2f6201d2fe6b0607daadfd1"; const REVERTED_TX_HASH: &str = "0x00ae35dacba17cde62b8ceb12e3b18f4ab6e103fa2d5e3d9821cb9dc59d59a3c"; #[tokio::test] async fn test_incorrect_transaction_hash() { let args = vec!["get", "tx-status", "0x1", "--url", URL]; let snapbox = runner(&args); let output = snapbox.assert().success(); assert_stderr_contains( output, indoc! {r" Command: get tx-status Error: Failed to get transaction status: Transaction with provided hash was not found (does not exist) "}, ); } #[tokio::test] async fn test_succeeded_old_command() { let args = vec!["tx-status", SUCCEEDED_TX_HASH, "--url", URL]; let snapbox = runner(&args); snapbox.assert().success().stdout_eq(indoc! {r" [WARNING] `sncast tx-status` has moved to `sncast get tx-status`. `sncast tx-status` will be removed in the next version. Success: Transaction status retrieved Finality Status: Accepted on L1 Execution Status: Succeeded "}); } #[tokio::test] async fn test_succeeded() { let args = vec!["get", "tx-status", SUCCEEDED_TX_HASH, "--url", URL]; let snapbox = runner(&args); snapbox.assert().success().stdout_eq(indoc! {r" Success: Transaction status retrieved Finality Status: Accepted on L1 Execution Status: Succeeded "}); } #[tokio::test] async fn test_reverted() { let args = vec!["get", "tx-status", REVERTED_TX_HASH, "--url", URL]; let snapbox = runner(&args); snapbox.assert().success().stdout_eq(indoc! {r" Success: Transaction status retrieved Finality Status: Accepted on L1 Execution Status: Reverted "}); } ================================================ FILE: crates/sncast/tests/e2e/verify/mod.rs ================================================ mod voyager; mod walnut; ================================================ FILE: crates/sncast/tests/e2e/verify/voyager.rs ================================================ use crate::helpers::constants::{ ACCOUNT_FILE_PATH, CONTRACTS_DIR, MAP_CONTRACT_ADDRESS_SEPOLIA, MAP_CONTRACT_CLASS_HASH_SEPOLIA, }; use crate::helpers::fixtures::copy_directory_to_tempdir; use crate::helpers::runner::runner; use indoc::formatdoc; use serde_json::json; use shared::test_utils::output_assert::assert_stderr_contains; use sncast::SEPOLIA; use starknet_types_core::felt::Felt; use std::fs; use wiremock::matchers::{body_partial_json, method, path}; use wiremock::{Mock, MockServer, Request, ResponseTemplate}; async fn mock_chain_id(mock_rpc: &MockServer, chain_id: Felt) { Mock::given(method("POST")) .and(body_partial_json(json!({"method": "starknet_chainId"}))) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "id": 1, "jsonrpc": "2.0", "result": format!("{chain_id:#x}") }))) .expect(1) .mount(mock_rpc) .await; } async fn mock_sepolia_chain_id(mock_rpc: &MockServer) { mock_chain_id(mock_rpc, SEPOLIA).await; } #[tokio::test] async fn test_happy_case_contract_address() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let mock_server = MockServer::start().await; let rpc_response = json!({ "id": 1, "jsonrpc": "2.0", "result": MAP_CONTRACT_CLASS_HASH_SEPOLIA }); let mock_rpc = MockServer::start().await; let mock_rpc_uri = mock_rpc.uri().clone(); // Only mock the getClassHashAt call that voyager actually makes Mock::given(method("POST")) .and(body_partial_json( json!({"method": "starknet_getClassHashAt"}), )) .respond_with(ResponseTemplate::new(200).set_body_json(rpc_response)) .expect(1) .mount(&mock_rpc) .await; let job_id = "2b206064-ffee-4955-8a86-1ff3b854416a"; let class_hash = Felt::from_hex(MAP_CONTRACT_CLASS_HASH_SEPOLIA).expect("Invalid class hash"); Mock::given(method("POST")) .and(path(format!("class-verify/{class_hash:#066x}"))) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "job_id": job_id }))) .expect(1) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "Map", "--verifier", "voyager", "--network", "sepolia", "--url", &mock_rpc_uri, ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()) .stdin("Y"); snapbox.assert().success(); } #[tokio::test] async fn test_happy_case_class_hash() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let mock_server = MockServer::start().await; let mock_rpc = MockServer::start().await; let mock_rpc_uri = mock_rpc.uri().clone(); // For class hash tests, no RPC calls are made since we already have the class hash // No need to mock any RPC calls let job_id = "2b206064-ffee-4955-8a86-1ff3b854416a"; let class_hash = Felt::from_hex(MAP_CONTRACT_CLASS_HASH_SEPOLIA).expect("Invalid class hash"); Mock::given(method("POST")) .and(path(format!("class-verify/{class_hash:#066x}"))) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "job_id": job_id }))) .expect(1) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--class-hash", MAP_CONTRACT_CLASS_HASH_SEPOLIA, "--contract-name", "Map", "--verifier", "voyager", "--network", "sepolia", "--url", &mock_rpc_uri, ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()) .stdin("Y"); snapbox.assert().success(); } #[tokio::test] async fn test_happy_case_with_confirm_verification_flag() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let mock_server = MockServer::start().await; let rpc_response = json!({ "id": 1, "jsonrpc": "2.0", "result": MAP_CONTRACT_CLASS_HASH_SEPOLIA }); let mock_rpc = MockServer::start().await; let mock_rpc_uri = mock_rpc.uri().clone(); // Only mock the getClassHashAt call that voyager actually makes Mock::given(method("POST")) .and(body_partial_json( json!({"method": "starknet_getClassHashAt"}), )) .respond_with(ResponseTemplate::new(200).set_body_json(rpc_response)) .expect(1) .mount(&mock_rpc) .await; mock_sepolia_chain_id(&mock_rpc).await; let job_id = "2b206064-ffee-4955-8a86-1ff3b854416a"; let class_hash = Felt::from_hex(MAP_CONTRACT_CLASS_HASH_SEPOLIA).expect("Invalid class hash"); Mock::given(method("POST")) .and(path(format!("class-verify/{class_hash:#066x}"))) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "job_id": job_id }))) .expect(1) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "Map", "--verifier", "voyager", "--confirm-verification", "--url", &mock_rpc_uri, ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()); snapbox.assert().success(); } #[tokio::test] async fn test_happy_case_uses_network_from_config() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); fs::write( contract_path.path().join("snfoundry.toml"), "[sncast.default]\nnetwork = \"sepolia\"\n", ) .unwrap(); let mock_server = MockServer::start().await; let rpc_response = json!({ "id": 1, "jsonrpc": "2.0", "result": MAP_CONTRACT_CLASS_HASH_SEPOLIA }); let mock_rpc = MockServer::start().await; let mock_rpc_uri = mock_rpc.uri().clone(); Mock::given(method("POST")) .and(body_partial_json( json!({"method": "starknet_getClassHashAt"}), )) .respond_with(ResponseTemplate::new(200).set_body_json(rpc_response)) .expect(1) .mount(&mock_rpc) .await; Mock::given(method("POST")) .and(body_partial_json(json!({"method": "starknet_chainId"}))) .respond_with(ResponseTemplate::new(200)) .expect(0) .mount(&mock_rpc) .await; let job_id = "config-network-job-id"; let class_hash = Felt::from_hex(MAP_CONTRACT_CLASS_HASH_SEPOLIA).expect("Invalid class hash"); Mock::given(method("POST")) .and(path(format!("class-verify/{class_hash:#066x}"))) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "job_id": job_id }))) .expect(1) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "Map", "--verifier", "voyager", "--confirm-verification", "--url", &mock_rpc_uri, ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()); snapbox.assert().success(); } #[tokio::test] async fn test_failed_verification_contract_address() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let mock_server = MockServer::start().await; let rpc_response = json!({ "id": 1, "jsonrpc": "2.0", "result": MAP_CONTRACT_CLASS_HASH_SEPOLIA }); let mock_rpc = MockServer::start().await; let mock_rpc_uri = mock_rpc.uri().clone(); // Only mock the getClassHashAt call that voyager actually makes Mock::given(method("POST")) .and(body_partial_json( json!({"method": "starknet_getClassHashAt"}), )) .respond_with(ResponseTemplate::new(200).set_body_json(rpc_response)) .expect(1) .mount(&mock_rpc) .await; let error = "some error message"; let class_hash = Felt::from_hex(MAP_CONTRACT_CLASS_HASH_SEPOLIA).expect("Invalid class hash"); Mock::given(method("POST")) .and(path(format!("class-verify/{class_hash:#066x}"))) .respond_with(ResponseTemplate::new(400).set_body_json(json!({ "error": error }))) .expect(1) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "Map", "--verifier", "voyager", "--network", "sepolia", "--url", &mock_rpc_uri, ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()) .stdin("Y"); let output = snapbox.assert().success(); assert_stderr_contains( output, formatdoc! {" Command: verify Error: {} ", error, }, ); } #[tokio::test] async fn test_failed_verification_class_hash() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let mock_server = MockServer::start().await; let mock_rpc = MockServer::start().await; let mock_rpc_uri = mock_rpc.uri().clone(); // For class hash tests, no RPC calls are made since we already have the class hash // No need to mock any RPC calls let error = "some error message"; let class_hash = Felt::from_hex(MAP_CONTRACT_CLASS_HASH_SEPOLIA).expect("Invalid class hash"); Mock::given(method("POST")) .and(path(format!("class-verify/{class_hash:#066x}"))) .respond_with(ResponseTemplate::new(400).set_body_json(json!({ "error": error }))) .expect(1) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--class-hash", MAP_CONTRACT_CLASS_HASH_SEPOLIA, "--contract-name", "Map", "--verifier", "voyager", "--network", "sepolia", "--url", &mock_rpc_uri, ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()) .stdin("Y"); let output = snapbox.assert().success(); assert_stderr_contains( output, formatdoc! {" Command: verify Error: {} ", error, }, ); } #[tokio::test] async fn test_failed_class_hash_lookup() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let mock_server = MockServer::start().await; let contract_not_found = json!({ "error": { "code": 20, "message": "Contract not found" }, "id": 1, "jsonrpc": "2.0" }); let mock_rpc = MockServer::start().await; let mock_rpc_uri = mock_rpc.uri().clone(); // Mock the getClassHashAt call to return contract not found Mock::given(method("POST")) .and(body_partial_json( json!({"method": "starknet_getClassHashAt"}), )) .respond_with(ResponseTemplate::new(400).set_body_json(contract_not_found)) .expect(1) .mount(&mock_rpc) .await; // Voyager API should not be called since RPC call fails let class_hash = Felt::from_hex(MAP_CONTRACT_CLASS_HASH_SEPOLIA).expect("Invalid class hash"); Mock::given(method("POST")) .and(path(format!("class-verify/{class_hash:#066x}"))) .respond_with(ResponseTemplate::new(400)) .expect(0) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "Map", "--verifier", "voyager", "--network", "sepolia", "--url", &mock_rpc_uri, ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()) .stdin("Y"); let output = snapbox.assert().success(); assert_stderr_contains( output, formatdoc! {" Command: verify Error: ContractNotFound ", }, ); } #[tokio::test] async fn test_virtual_workspaces() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/virtual_workspace"); let mock_server = MockServer::start().await; let rpc_response = json!({ "id": 1, "jsonrpc": "2.0", "result": MAP_CONTRACT_CLASS_HASH_SEPOLIA }); let mock_rpc = MockServer::start().await; let mock_rpc_uri = mock_rpc.uri().clone(); // Only mock the getClassHashAt call that voyager actually makes Mock::given(method("POST")) .and(body_partial_json( json!({"method": "starknet_getClassHashAt"}), )) .respond_with(ResponseTemplate::new(200).set_body_json(rpc_response)) .expect(1) .mount(&mock_rpc) .await; let job_id = "2b206064-ffee-4955-8a86-1ff3b854416a"; let class_hash = Felt::from_hex(MAP_CONTRACT_CLASS_HASH_SEPOLIA).expect("Invalid class hash"); Mock::given(method("POST")) .and(path(format!("class-verify/{class_hash:#066x}"))) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "job_id": job_id }))) .expect(1) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "FibonacciContract", "--package", "cast_fibonacci", "--verifier", "voyager", "--network", "sepolia", "--url", &mock_rpc_uri, ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()) .stdin("Y"); snapbox.assert().success(); } #[tokio::test] async fn test_contract_name_not_found() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/virtual_workspace"); let mock_server = MockServer::start().await; let mock_rpc = MockServer::start().await; let mock_rpc_uri = mock_rpc.uri().clone(); // For this test, the error happens before any RPC calls are made (contract name not found) // So no RPC mocks needed let job_id = "2b206064-ffee-4955-8a86-1ff3b854416a"; let class_hash = Felt::from_hex(MAP_CONTRACT_CLASS_HASH_SEPOLIA).expect("Invalid class hash"); let expected_body = json!({ "project_dir_path": ".", "name": "FibonacciContract", "package_name": "cast_fibonacci", "license": null }); Mock::given(method("POST")) .and(path(format!("class-verify/{class_hash:#066x}"))) .and(body_partial_json(&expected_body)) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "job_id": job_id }))) .expect(0) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "non_existent", "--package", "cast_fibonacci", "--verifier", "voyager", "--network", "sepolia", "--url", &mock_rpc_uri, ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()) .stdin("Y"); let output = snapbox.assert().success(); assert_stderr_contains( output, formatdoc! {" Command: verify Error: Contract named 'non_existent' was not found ", }, ); } #[tokio::test] async fn test_error_when_neither_network_nor_url_provided() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "Map", "--verifier", "voyager", "--confirm-verification", ]; let snapbox = runner(&args).current_dir(contract_path.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, formatdoc! {" Command: verify Error: Either --network or --url must be provided ", }, ); } #[tokio::test] async fn test_error_when_chain_id_is_unrecognized_and_network_is_missing() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let mock_server = MockServer::start().await; let mock_rpc = MockServer::start().await; let mock_rpc_uri = mock_rpc.uri().clone(); mock_chain_id(&mock_rpc, Felt::from_hex_unchecked("0x1234")).await; let class_hash = Felt::from_hex(MAP_CONTRACT_CLASS_HASH_SEPOLIA).expect("Invalid class hash"); Mock::given(method("POST")) .and(path(format!("class-verify/{class_hash:#066x}"))) .respond_with(ResponseTemplate::new(200)) .expect(0) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--class-hash", MAP_CONTRACT_CLASS_HASH_SEPOLIA, "--contract-name", "Map", "--verifier", "voyager", "--confirm-verification", "--url", &mock_rpc_uri, ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()); let output = snapbox.assert().success(); assert_stderr_contains( output, formatdoc! {" Command: verify Error: Failed to infer verification network from the RPC chain ID 0x1234; pass `--network mainnet` or `--network sepolia` explicitly ", }, ); } #[tokio::test] async fn test_test_files_flag_includes_test_files() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let mock_server = MockServer::start().await; let rpc_response = json!({ "id": 1, "jsonrpc": "2.0", "result": MAP_CONTRACT_CLASS_HASH_SEPOLIA }); let mock_rpc = MockServer::start().await; let mock_rpc_uri = mock_rpc.uri().clone(); // Mock the getClassHashAt call Mock::given(method("POST")) .and(body_partial_json( json!({"method": "starknet_getClassHashAt"}), )) .respond_with(ResponseTemplate::new(200).set_body_json(rpc_response)) .expect(1) .mount(&mock_rpc) .await; let job_id = "test-job-id-with-test-files"; let class_hash = Felt::from_hex(MAP_CONTRACT_CLASS_HASH_SEPOLIA).expect("Invalid class hash"); // Mock the verification request and verify that test files are included Mock::given(method("POST")) .and(path(format!("class-verify/{class_hash:#066x}"))) .and(body_partial_json(json!({ "name": "Map", "package_name": "map" }))) .and(|req: &Request| { if let Ok(body_str) = std::str::from_utf8(&req.body) && let Ok(body_json) = serde_json::from_str::(body_str) && let Some(files) = body_json.get("files") && let Some(files_obj) = files.as_object() { // Verify that test files ARE present return files_obj.contains_key("src/test_helpers.cairo") && files_obj.contains_key("src/tests.cairo"); } false }) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "job_id": job_id }))) .expect(1) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "Map", "--verifier", "voyager", "--network", "sepolia", "--url", &mock_rpc_uri, "--test-files", ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()) .stdin("Y"); snapbox.assert().success(); } #[tokio::test] async fn test_without_test_files_flag_excludes_test_files() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let mock_server = MockServer::start().await; let rpc_response = json!({ "id": 1, "jsonrpc": "2.0", "result": MAP_CONTRACT_CLASS_HASH_SEPOLIA }); let mock_rpc = MockServer::start().await; let mock_rpc_uri = mock_rpc.uri().clone(); // Mock the getClassHashAt call Mock::given(method("POST")) .and(body_partial_json( json!({"method": "starknet_getClassHashAt"}), )) .respond_with(ResponseTemplate::new(200).set_body_json(rpc_response)) .expect(1) .mount(&mock_rpc) .await; let job_id = "test-job-id-without-test-files"; let class_hash = Felt::from_hex(MAP_CONTRACT_CLASS_HASH_SEPOLIA).expect("Invalid class hash"); // Mock the verification request - without --test-files flag, test files should be excluded Mock::given(method("POST")) .and(path(format!("class-verify/{class_hash:#066x}"))) .and(body_partial_json(json!({ "name": "Map", "package_name": "map" }))) .and(|req: &Request| { if let Ok(body_str) = std::str::from_utf8(&req.body) && let Ok(body_json) = serde_json::from_str::(body_str) && let Some(files) = body_json.get("files") && let Some(files_obj) = files.as_object() { // Verify that test files are NOT present return !files_obj.contains_key("src/test_helpers.cairo") && !files_obj.contains_key("src/tests.cairo"); } false }) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "job_id": job_id }))) .expect(1) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "Map", "--verifier", "voyager", "--network", "sepolia", "--url", &mock_rpc_uri, // Note: --test-files flag is NOT included ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()) .stdin("Y"); snapbox.assert().success(); } ================================================ FILE: crates/sncast/tests/e2e/verify/walnut.rs ================================================ use crate::helpers::constants::{ ACCOUNT_FILE_PATH, CONTRACTS_DIR, MAP_CONTRACT_ADDRESS_SEPOLIA, MAP_CONTRACT_CLASS_HASH_SEPOLIA, }; use crate::helpers::fixtures::copy_directory_to_tempdir; use crate::helpers::runner::runner; use indoc::formatdoc; use shared::test_utils::output_assert::{assert_stderr_contains, assert_stdout_contains}; use wiremock::matchers::{method, path}; use wiremock::{Mock, MockServer, ResponseTemplate}; #[tokio::test] async fn test_happy_case_contract_address() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let mock_server = MockServer::start().await; let verifier_response = "Contract successfully verified"; Mock::given(method("POST")) .and(path("/v1/sn_sepolia/verify")) .respond_with( ResponseTemplate::new(200) .append_header("content-type", "text/plain") .set_body_string(verifier_response), ) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "Map", "--verifier", "walnut", "--network", "sepolia", ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()) .stdin("Y"); let output = snapbox.assert().success(); assert_stdout_contains( output, formatdoc!( r" Success: Verification completed {} ", verifier_response ), ); } #[tokio::test] async fn test_happy_case_class_hash() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let mock_server = MockServer::start().await; let verifier_response = "Contract successfully verified"; Mock::given(method("POST")) .and(path("/v1/sn_sepolia/verify")) .respond_with( ResponseTemplate::new(200) .append_header("content-type", "text/plain") .set_body_string(verifier_response), ) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--class-hash", MAP_CONTRACT_CLASS_HASH_SEPOLIA, "--contract-name", "Map", "--verifier", "walnut", "--network", "sepolia", ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()) .stdin("Y"); let output = snapbox.assert().success(); assert_stdout_contains( output, formatdoc!( r" Success: Verification completed {} ", verifier_response ), ); } #[tokio::test] async fn test_failed_verification_contract_address() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let mock_server = MockServer::start().await; let verifier_response = "An error occurred during verification: contract class isn't declared"; Mock::given(method("POST")) .and(path("/v1/sn_sepolia/verify")) .respond_with( ResponseTemplate::new(400) .append_header("content-type", "text/plain") .set_body_string(verifier_response), ) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "Map", "--verifier", "walnut", "--network", "sepolia", ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()) .stdin("Y"); let output = snapbox.assert().success(); assert_stderr_contains( output, formatdoc!( r" Command: verify Error: {} ", verifier_response ), ); } #[tokio::test] async fn test_failed_verification_class_hash() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let mock_server = MockServer::start().await; let verifier_response = "An error occurred during verification: contract class isn't declared"; Mock::given(method("POST")) .and(path("/v1/sn_sepolia/verify")) .respond_with( ResponseTemplate::new(400) .append_header("content-type", "text/plain") .set_body_string(verifier_response), ) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--class-hash", MAP_CONTRACT_CLASS_HASH_SEPOLIA, "--contract-name", "Map", "--verifier", "walnut", "--network", "sepolia", ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()) .stdin("Y"); let output = snapbox.assert().success(); assert_stderr_contains( output, formatdoc!( r" Command: verify Error: {} ", verifier_response ), ); } #[tokio::test] async fn test_verification_abort() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "nonexistent", "--verifier", "walnut", "--network", "sepolia", ]; let snapbox = runner(&args).current_dir(contract_path.path()).stdin("n"); let output = snapbox.assert().success(); assert_stderr_contains( output, formatdoc!( r" Command: verify Error: Verification aborted " ), ); } #[tokio::test] async fn test_happy_case_lowercase_y() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let mock_server = MockServer::start().await; let verifier_response = "Contract successfully verified"; Mock::given(method("POST")) .and(path("/v1/sn_sepolia/verify")) .respond_with( ResponseTemplate::new(200) .append_header("content-type", "text/plain") .set_body_string(verifier_response), ) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "Map", "--verifier", "walnut", "--network", "sepolia", ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()) .stdin("y"); let output = snapbox.assert().success(); assert_stdout_contains( output, formatdoc!( r" Success: Verification completed {} ", verifier_response ), ); } #[tokio::test] async fn test_wrong_contract_name_passed() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "nonexistent", "--verifier", "walnut", "--network", "sepolia", ]; let snapbox = runner(&args).current_dir(contract_path.path()).stdin("Y"); let output = snapbox.assert().success(); assert_stderr_contains( output, formatdoc!( r" Command: verify Error: Contract named 'nonexistent' was not found " ), ); } #[tokio::test] async fn test_happy_case_with_confirm_verification_flag() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let mock_server = MockServer::start().await; let verifier_response = "Contract successfully verified"; Mock::given(method("POST")) .and(path("/v1/sn_sepolia/verify")) .respond_with( ResponseTemplate::new(200) .append_header("content-type", "text/plain") .set_body_string(verifier_response), ) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "Map", "--verifier", "walnut", "--network", "sepolia", "--confirm-verification", ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, formatdoc!( r" Success: Verification completed {} ", verifier_response ), ); } #[tokio::test] async fn test_happy_case_specify_package() { let tempdir = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/multiple_packages"); let mock_server = MockServer::start().await; let verifier_response = "Contract successfully verified"; Mock::given(method("POST")) .and(path("/v1/sn_sepolia/verify")) .respond_with( ResponseTemplate::new(200) .append_header("content-type", "text/plain") .set_body_string(verifier_response), ) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "supercomplexcode", "--verifier", "walnut", "--network", "sepolia", "--package", "main_workspace", ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(tempdir.path()) .stdin("Y"); let output = snapbox.assert().success(); assert_stdout_contains( output, formatdoc!( r" Success: Verification completed {} ", verifier_response ), ); } #[tokio::test] async fn test_worskpaces_package_specified_virtual_fibonacci() { let tempdir = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/virtual_workspace"); let mock_server = MockServer::start().await; let verifier_response = "Contract successfully verified"; Mock::given(method("POST")) .and(path("/v1/sn_sepolia/verify")) .respond_with( ResponseTemplate::new(200) .append_header("content-type", "text/plain") .set_body_string(verifier_response), ) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "FibonacciContract", "--verifier", "walnut", "--network", "sepolia", "--package", "cast_fibonacci", ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(tempdir.path()) .stdin("Y"); let output = snapbox.assert().success(); assert_stdout_contains( output, formatdoc!( r" Success: Verification completed {} ", verifier_response ), ); } #[tokio::test] async fn test_worskpaces_package_no_contract() { let tempdir = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/virtual_workspace"); let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "nonexistent", "--verifier", "walnut", "--network", "sepolia", "--package", "cast_addition", ]; let snapbox = runner(&args).current_dir(tempdir.path()).stdin("Y"); let output = snapbox.assert().success(); assert_stderr_contains( output, formatdoc!( r" Command: verify Error: Contract named 'nonexistent' was not found " ), ); } #[tokio::test] async fn test_test_files_flag_ignored_with_warning() { let contract_path = copy_directory_to_tempdir(CONTRACTS_DIR.to_string() + "/map"); let mock_server = MockServer::start().await; let verifier_response = "Contract successfully verified"; Mock::given(method("POST")) .and(path("/v1/sn_sepolia/verify")) .respond_with( ResponseTemplate::new(200) .append_header("content-type", "text/plain") .set_body_string(verifier_response), ) .mount(&mock_server) .await; let args = vec![ "--accounts-file", ACCOUNT_FILE_PATH, "verify", "--contract-address", MAP_CONTRACT_ADDRESS_SEPOLIA, "--contract-name", "Map", "--verifier", "walnut", "--network", "sepolia", "--test-files", "--confirm-verification", ]; let snapbox = runner(&args) .env("VERIFIER_API_URL", mock_server.uri()) .current_dir(contract_path.path()); let output = snapbox.assert().success(); assert_stdout_contains( output, formatdoc!( r" [WARNING] The `--test-files` option is ignored for Walnut verifier Success: Verification completed {} ", verifier_response ), ); } ================================================ FILE: crates/sncast/tests/helpers/constants.rs ================================================ use starknet_rust::macros::felt; use starknet_types_core::felt::Felt; pub const ACCOUNT: &str = "user1"; pub const ACCOUNT_FILE_PATH: &str = "tests/data/accounts/accounts.json"; pub const SEPOLIA_RPC_URL: &str = "http://188.34.188.184:7070/rpc/v0_10"; pub const URL: &str = "http://127.0.0.1:5055/rpc"; pub const NETWORK: &str = "testnet"; pub const DEVNET_SEED: u32 = 1_053_545_548; pub const DEVNET_ACCOUNTS_NUMBER: u8 = 20; // Block number used by devnet to fork the Sepolia testnet network in the tests pub const DEVNET_FORK_BLOCK_NUMBER: u32 = 721_720; pub const CONTRACTS_DIR: &str = "tests/data/contracts"; pub const SCRIPTS_DIR: &str = "tests/data/scripts"; pub const MULTICALL_CONFIGS_DIR: &str = "crates/sncast/tests/data/multicall_configs"; pub const DEVNET_OZ_CLASS_HASH_CAIRO_0: &str = "0x4d07e40e93398ed3c76981e72dd1fd22557a78ce36c0515f679e27f0bb5bc5f"; pub const DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS: &str = "0x691a61b12a7105b1372cc377f135213c11e8400a546f6b0e7ea0296046690ce"; pub const DEVNET_OZ_CLASS_HASH_CAIRO_1: Felt = felt!("0x05b4b537eaa2399e3aa99c4e2e0208ebd6c71bc1467938cd52c798c601e43564"); pub const MAP_CONTRACT_ADDRESS_SEPOLIA: &str = "0xcd8f9ab31324bb93251837e4efb4223ee195454f6304fcfcb277e277653008"; pub const MAP_CONTRACT_CLASS_HASH_SEPOLIA: &str = "0x2a09379665a749e609b4a8459c86fe954566a6beeaddd0950e43f6c700ed321"; pub const MAP_CONTRACT_DECLARE_TX_HASH_SEPOLIA: &str = "0x4f644d3ea723b9c28781f2bea76e9c2cd8cc667b2861faf66b4e45402ea221c"; pub const CONSTRUCTOR_WITH_PARAMS_CONTRACT_CLASS_HASH_SEPOLIA: &str = "0x59426c817fb8103edebdbf1712fa084c6744b2829db9c62d1ea4dce14ee6ded"; pub const DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA: &str = "0x00351c816183324878714973f3da1a43c1a40d661b8dac5cb69294cc333342ed"; pub const DATA_TRANSFORMER_CONTRACT_CLASS_HASH_SEPOLIA: &str = "0x0786d1f010d66f838837290e472415186ba6a789fb446e7f92e444bed7b5d9c0"; pub const DATA_TRANSFORMER_CONTRACT_ABI_PATH: &str = "tests/data/files/data_transformer_contract_abi.json"; ================================================ FILE: crates/sncast/tests/helpers/devnet.rs ================================================ use crate::helpers::constants::{ DEVNET_ACCOUNTS_NUMBER, DEVNET_FORK_BLOCK_NUMBER, DEVNET_SEED, SEPOLIA_RPC_URL, URL, }; use crate::helpers::fixtures::{ deploy_braavos_account, deploy_cairo_0_account, deploy_keystore_account, deploy_latest_oz_account, deploy_ready_account, }; use ctor::{ctor, dtor}; use std::net::TcpStream; use std::process::{Command, Stdio}; use std::string::ToString; use std::time::{Duration, Instant}; use tokio::runtime::Runtime; use url::Url; #[expect(clippy::zombie_processes)] #[cfg(test)] #[ctor] fn start_devnet() { fn verify_devnet_availability(address: &str) -> bool { TcpStream::connect(address).is_ok() } let port = Url::parse(URL).unwrap().port().unwrap_or(80).to_string(); let host = Url::parse(URL) .unwrap() .host() .expect("Can't parse devnet URL!") .to_string(); loop { if verify_devnet_availability(&format!("{host}:{port}")) { stop_devnet(); } else { break; } } Command::new("starknet-devnet") .args([ "--port", &port, "--seed", &DEVNET_SEED.to_string(), "--state-archive-capacity", "full", "--fork-network", SEPOLIA_RPC_URL, "--fork-block", &DEVNET_FORK_BLOCK_NUMBER.to_string(), "--initial-balance", "9999999999999999999999999999999", "--accounts", &DEVNET_ACCOUNTS_NUMBER.to_string(), ]) .stdout(Stdio::null()) .spawn() .expect("Failed to start devnet!"); let now = Instant::now(); let timeout = Duration::from_secs(30); loop { if verify_devnet_availability(&format!("{host}:{port}")) { break; } else if now.elapsed() >= timeout { eprintln!("Timed out while waiting for devnet!"); std::process::exit(1); } } let rt = Runtime::new().expect("Could not instantiate Runtime"); rt.block_on(deploy_keystore_account()); rt.block_on(deploy_cairo_0_account()); rt.block_on(deploy_latest_oz_account()); rt.block_on(deploy_ready_account()); rt.block_on(deploy_braavos_account()); } #[cfg(test)] #[dtor] fn stop_devnet() { let port = Url::parse(URL).unwrap().port().unwrap_or(80).to_string(); let pattern = format!("starknet-devnet.*{port}.*{DEVNET_SEED}"); Command::new("pkill") .args(["-f", &pattern]) .output() .expect("Failed to kill devnet processes"); } ================================================ FILE: crates/sncast/tests/helpers/devnet_detection.rs ================================================ use sncast::helpers::devnet::detection::{DevnetDetectionError, detect_devnet_url}; use std::net::TcpStream; use std::process::{Child, Command, Stdio}; use std::time::{Duration, Instant}; use url::Url; use crate::helpers::constants::URL; // These tests are marked to run serially to avoid interference from second devnet instance #[tokio::test] async fn test_devnet_detection() { test_detect_devnet_url().await; test_multiple_devnet_instances_error().await; } async fn test_detect_devnet_url() { let result = detect_devnet_url() .await .expect("Failed to detect devnet URL"); assert_eq!(result, Url::parse(&URL.replace("/rpc", "")).unwrap()); } async fn test_multiple_devnet_instances_error() { let mut devnet1 = start_devnet_instance(5051, 1234); wait_for_devnet("127.0.0.1:5051", Duration::from_secs(10)); let result = detect_devnet_url().await; let _ = devnet1.kill(); let _ = devnet1.wait(); assert!(matches!( result, Err(DevnetDetectionError::MultipleInstances) )); } fn start_devnet_instance(port: u16, seed: u32) -> Child { Command::new("starknet-devnet") .args([ "--port", &port.to_string(), "--seed", &seed.to_string(), "--accounts", "1", ]) .stdout(Stdio::null()) .stderr(Stdio::null()) .spawn() .expect("Failed to start devnet instance") } fn wait_for_devnet(address: &str, timeout: Duration) { let now = Instant::now(); loop { if TcpStream::connect(address).is_ok() { break; } else if now.elapsed() >= timeout { panic!("Timed out waiting for devnet at {address}"); } std::thread::sleep(Duration::from_millis(100)); } } ================================================ FILE: crates/sncast/tests/helpers/devnet_provider.rs ================================================ use crate::helpers::constants::{DEVNET_ACCOUNTS_NUMBER, DEVNET_SEED, SEPOLIA_RPC_URL, URL}; use num_traits::ToPrimitive; use sncast::helpers::{constants::OZ_CLASS_HASH, devnet::provider::DevnetProvider}; #[tokio::test] async fn test_get_config() { let devnet_provider = DevnetProvider::new(URL); let config = devnet_provider .get_config() .await .expect("Failed to get config"); assert!(config.account_contract_class_hash == OZ_CLASS_HASH); assert!(config.seed == DEVNET_SEED); assert!(config.total_accounts == DEVNET_ACCOUNTS_NUMBER); } #[tokio::test] async fn test_get_predeployed_accounts() { let devnet_provider = DevnetProvider::new(URL); let predeployed_accounts = devnet_provider .get_predeployed_accounts() .await .expect("Failed to get predeployed accounts"); assert!(predeployed_accounts.len().to_u8().unwrap() == DEVNET_ACCOUNTS_NUMBER); } #[tokio::test] async fn test_is_alive_happy_case() { let devnet_provider = DevnetProvider::new(URL); devnet_provider .ensure_alive() .await .expect("Failed to ensure the devnet is alive"); } #[tokio::test] async fn test_is_alive_fails_on_sepolia_node() { let devnet_provider = DevnetProvider::new(SEPOLIA_RPC_URL); let res = devnet_provider.ensure_alive().await; assert!(res.is_err(), "Expected an error"); let err = res.unwrap_err().to_string(); assert!( err == format!( "Node at {SEPOLIA_RPC_URL} is not responding to the Devnet health check (GET `/is_alive`). It may not be a Devnet instance or it may be down." ), "Unexpected error message: {err}" ); } ================================================ FILE: crates/sncast/tests/helpers/env.rs ================================================ use sncast::helpers::constants::{CREATE_KEYSTORE_PASSWORD_ENV_VAR, KEYSTORE_PASSWORD_ENV_VAR}; use std::env; pub fn set_keystore_password_env() { // SAFETY: Tests run in parallel and share the same environment variables. // However, we only set this variable once to a fixed value and never modify or unset it. // The only potential issue would be if a test explicitly required this variable to be unset, // but to the best of our knowledge, no such test exists. unsafe { env::set_var(KEYSTORE_PASSWORD_ENV_VAR, "123"); }; } pub fn set_create_keystore_password_env() { // SAFETY: Tests run in parallel and share the same environment variables. // However, we only set this variable once to a fixed value and never modify or unset it. // The only potential issue would be if a test explicitly required this variable to be unset, // but to the best of our knowledge, no such test exists. unsafe { env::set_var(CREATE_KEYSTORE_PASSWORD_ENV_VAR, "123"); }; } ================================================ FILE: crates/sncast/tests/helpers/fixtures.rs ================================================ use crate::helpers::constants::{ACCOUNT_FILE_PATH, DEVNET_OZ_CLASS_HASH_CAIRO_0, URL}; use crate::helpers::runner::runner; use anyhow::Context; use camino::{Utf8Path, Utf8PathBuf}; use conversions::string::IntoHexStr; use core::str; use fs_extra::dir::{CopyOptions, copy}; use serde::Deserialize; use serde::de::DeserializeOwned; use serde_json::{Map, Value, json}; use sncast::helpers::account::load_accounts; use sncast::helpers::braavos::BraavosAccountFactory; use sncast::helpers::configuration::CastConfig; use sncast::helpers::constants::{ BRAAVOS_BASE_ACCOUNT_CLASS_HASH, BRAAVOS_CLASS_HASH, OZ_CLASS_HASH, READY_CLASS_HASH, }; use sncast::helpers::fee::FeeSettings; use sncast::helpers::rpc::RpcArgs; use sncast::helpers::scarb_utils::get_package_metadata; use sncast::response::ui::UI; use sncast::state::state_file::{ ScriptTransactionEntry, ScriptTransactionOutput, ScriptTransactionStatus, }; use sncast::{AccountType, apply_optional_fields, get_chain_id, get_keystore_password}; use sncast::{get_account, get_provider}; use starknet_rust::accounts::{ Account, AccountFactory, ArgentAccountFactory, ExecutionV3, OpenZeppelinAccountFactory, }; use starknet_rust::core::types::{Call, InvokeTransactionResult, Transaction, TransactionReceipt}; use starknet_rust::core::utils::get_contract_address; use starknet_rust::core::utils::get_selector_from_name; use starknet_rust::providers::JsonRpcClient; use starknet_rust::providers::jsonrpc::HttpTransport; use starknet_rust::signers::{LocalWallet, SigningKey}; use starknet_types_core::felt::Felt; use std::collections::HashMap; use std::env; use std::fs; use std::fs::File; use std::io::{BufRead, Write}; use tempfile::{TempDir, tempdir}; use toml::Table; use url::Url; const SCRIPT_ORIGIN_TIMESTAMP: u64 = 1_709_853_748; pub async fn deploy_keystore_account() { let keystore_path = "tests/data/keystore/predeployed_key.json"; let account_path = "tests/data/keystore/predeployed_account.json"; let private_key = SigningKey::from_keystore(keystore_path, "123").expect("Failed to get private_key"); let contents = fs::read_to_string(account_path).expect("Failed to read keystore account file"); let items: Value = serde_json::from_str(&contents) .unwrap_or_else(|_| panic!("Failed to parse keystore account file at = {account_path}")); let deployment_info = items .get("deployment") .expect("Failed to get deployment key"); let address = get_from_json_as_str(deployment_info, "address"); deploy_oz_account( address, DEVNET_OZ_CLASS_HASH_CAIRO_0, "0xa5d90c65b1b1339", private_key, ) .await; } pub async fn deploy_cairo_0_account() { let (address, salt, private_key) = get_account_deployment_data("oz_cairo_0"); deploy_oz_account( address.as_str(), DEVNET_OZ_CLASS_HASH_CAIRO_0, salt.as_str(), private_key, ) .await; } pub async fn deploy_latest_oz_account() { let (address, salt, private_key) = get_account_deployment_data("oz"); deploy_oz_account( address.as_str(), OZ_CLASS_HASH.into_hex_string().as_str(), salt.as_str(), private_key, ) .await; } pub async fn deploy_ready_account() { let provider = get_provider(&Url::parse(URL).unwrap()).expect("Failed to get the provider"); let chain_id = get_chain_id(&provider) .await .expect("Failed to get chain id"); let (address, salt, private_key) = get_account_deployment_data("ready"); let factory = ArgentAccountFactory::new( READY_CLASS_HASH, chain_id, None, LocalWallet::from_signing_key(private_key), provider, ) .await .expect("Failed to create Account Factory"); deploy_account_to_devnet(factory, address.as_str(), salt.as_str()).await; } pub async fn deploy_braavos_account() { let provider = get_provider(&Url::parse(URL).unwrap()).expect("Failed to get the provider"); let chain_id = get_chain_id(&provider) .await .expect("Failed to get chain id"); let (address, salt, private_key) = get_account_deployment_data("braavos"); let factory = BraavosAccountFactory::new( BRAAVOS_CLASS_HASH, BRAAVOS_BASE_ACCOUNT_CLASS_HASH, chain_id, LocalWallet::from_signing_key(private_key), provider, ) .await .expect("Failed to create Account Factory"); deploy_account_to_devnet(factory, address.as_str(), salt.as_str()).await; } async fn deploy_oz_account(address: &str, class_hash: &str, salt: &str, private_key: SigningKey) { let provider = get_provider(&Url::parse(URL).unwrap()).expect("Failed to get the provider"); let chain_id = get_chain_id(&provider) .await .expect("Failed to get chain id"); let factory = OpenZeppelinAccountFactory::new( class_hash.parse().expect("Failed to parse class hash"), chain_id, LocalWallet::from_signing_key(private_key), provider, ) .await .expect("Failed to create Account Factory"); deploy_account_to_devnet(factory, address, salt).await; } async fn deploy_account_to_devnet(factory: T, address: &str, salt: &str) { mint_token(address, u128::MAX).await; factory .deploy_v3(salt.parse().expect("Failed to parse salt")) .l1_gas(100_000) .l1_gas_price(10_000_000_000_000) .l2_gas(1_000_000_000) .l2_gas_price(10_000_000_000_000) .l1_data_gas(100_000) .l1_data_gas_price(10_000_000_000_000) .send() .await .expect("Failed to deploy account"); } fn get_account_deployment_data(account: &str) -> (String, String, SigningKey) { let items = load_accounts(&Utf8PathBuf::from(ACCOUNT_FILE_PATH)).expect("Failed to load accounts"); let account_data = items .get("alpha-sepolia") .and_then(|accounts| accounts.get(account)) .unwrap_or_else(|| panic!("Failed to get {account} account")); let address = get_from_json_as_str(account_data, "address"); let salt = get_from_json_as_str(account_data, "salt"); let private_key = get_from_json_as_str(account_data, "private_key"); let private_key = SigningKey::from_secret_scalar( private_key .parse() .expect("Failed to convert private key to Felt"), ); (address.to_string(), salt.to_string(), private_key) } fn get_from_json_as_str<'a>(entry: &'a Value, key: &str) -> &'a str { entry .get(key) .and_then(Value::as_str) .unwrap_or_else(|| panic!("Failed to get {key} key")) } pub async fn invoke_contract( account: &str, contract_address: &str, entry_point_name: &str, fee_settings: FeeSettings, constructor_calldata: &[&str], ) -> InvokeTransactionResult { let provider = get_provider(&Url::parse(URL).unwrap()).expect("Could not get the provider"); let config = CastConfig { account: account.to_string(), accounts_file: Utf8PathBuf::from(ACCOUNT_FILE_PATH), ..Default::default() }; let rpc_args = RpcArgs { url: Some(Url::parse(URL).expect("Failed to parse URL")), network: None, }; let account = get_account(&config, &provider, &rpc_args, &UI::default()) .await .expect("Could not get the account"); let mut calldata: Vec = vec![]; for value in constructor_calldata { let value: Felt = value.parse().expect("Could not parse the calldata"); calldata.push(value); } let call = Call { to: contract_address .parse() .expect("Could not parse the contract address"), selector: get_selector_from_name(entry_point_name) .unwrap_or_else(|_| panic!("Could not get selector from {entry_point_name}")), calldata, }; let account = match account { sncast::AccountVariant::LocalWallet(acc) => acc, sncast::AccountVariant::Ledger(_) => panic!("Ledger account not supported in test"), }; let execution = account.execute_v3(vec![call]); let execution = apply_optional_fields!( execution, fee_settings.l1_gas => ExecutionV3::l1_gas, fee_settings.l1_gas_price => ExecutionV3::l1_gas_price, fee_settings.l2_gas => ExecutionV3::l2_gas, fee_settings.l2_gas_price => ExecutionV3::l2_gas_price, fee_settings.l1_data_gas => ExecutionV3::l1_data_gas, fee_settings.l1_data_gas_price => ExecutionV3::l1_data_gas_price ); execution .send() .await .expect("Transaction execution failed") } pub async fn mint_token(recipient: &str, amount: u128) { let client = reqwest::Client::new(); let json = json!({ "jsonrpc": "2.0", "method": "devnet_mint", "params": { "address": recipient, "amount": amount, "unit": "FRI", }, "id": 0, }); let resp = client .post("http://127.0.0.1:5055/rpc") .header("Content-Type", "application/json") .body(json.to_string()) .send() .await .expect("Error occurred while minting tokens"); let resp_body: serde_json::Value = resp.json().await.expect("No JSON in response"); assert!(resp_body["result"].is_object()); } #[must_use] pub fn default_cli_args() -> Vec<&'static str> { vec!["--url", URL, "--accounts-file", ACCOUNT_FILE_PATH] } fn parse_output(output: &[u8]) -> T { for line in BufRead::split(output, b'\n') { let line = line.expect("Failed to read line from stdout"); if let Ok(t) = serde_json::de::from_slice::(&line) { return t; } } panic!("Failed to deserialize stdout JSON to struct"); } #[derive(Deserialize)] #[expect(dead_code)] struct TransactionHashOutput { pub transaction_hash: String, contract_address: Option, class_hash: Option, command: Option, } #[must_use] pub fn get_transaction_hash(output: &[u8]) -> Felt { let output = parse_output::(output); output .transaction_hash .parse() .expect("Could not parse a number") } pub async fn get_transaction_receipt(tx_hash: Felt) -> TransactionReceipt { let client = reqwest::Client::new(); let json = json!( { "jsonrpc": "2.0", "method": "starknet_getTransactionReceipt", "params": { "transaction_hash": format!("{tx_hash:#x}"), }, "id": 0, } ); let resp: Value = serde_json::from_str( &client .post(URL) .header("Content-Type", "application/json") .body(json.to_string()) .send() .await .expect("Error occurred while getting transaction receipt") .text() .await .expect("Could not get response from getTransactionReceipt"), ) .expect("Could not serialize getTransactionReceipt response"); let result = resp .get("result") .expect("There is no `result` field in getTransactionReceipt response"); serde_json::from_str(&result.to_string()) .expect("Could not serialize result to `TransactionReceipt`") } pub async fn get_transaction_by_hash(tx_hash: Felt) -> Transaction { let client = reqwest::Client::new(); let json = json!( { "jsonrpc": "2.0", "method": "starknet_getTransactionByHash", "params": { "transaction_hash": format!("{tx_hash:#x}"), }, "id": 0, } ); let resp: Value = serde_json::from_str( &client .post(URL) .header("Content-Type", "application/json") .body(json.to_string()) .send() .await .expect("Error occurred while getting transaction") .text() .await .expect("Could not get response from getTransactionByHash"), ) .expect("Could not serialize getTransactionByHash response"); let result = resp .get("result") .expect("There is no `result` field in getTransactionByHash response"); serde_json::from_str(&result.to_string()).expect("Could not serialize result to `Transaction`") } #[must_use] pub fn create_test_provider() -> JsonRpcClient { let parsed_url = Url::parse(URL).unwrap(); JsonRpcClient::new(HttpTransport::new(parsed_url)) } pub fn copy_file(src_path: impl AsRef, dest_path: impl AsRef) { fs_extra::file::copy( src_path.as_ref(), dest_path.as_ref(), &fs_extra::file::CopyOptions::new().overwrite(true), ) .expect("Failed to copy the file"); } #[must_use] pub fn duplicate_contract_directory_with_salt( src_dir: impl AsRef, code_to_be_salted: &str, salt: &str, ) -> TempDir { let src_dir = Utf8PathBuf::from(src_dir.as_ref()); let temp_dir = copy_directory_to_tempdir(&src_dir); let dest_dir = Utf8PathBuf::from_path_buf(temp_dir.path().to_path_buf()) .expect("Failed to create Utf8PathBuf from PathBuf"); let file_to_be_salted = "src/lib.cairo"; let contract_code = fs::read_to_string(src_dir.join(file_to_be_salted)).expect("Unable to get contract code"); let updated_code = contract_code.replace(code_to_be_salted, &(code_to_be_salted.to_string() + salt)); fs::write(dest_dir.join(file_to_be_salted), updated_code) .expect("Unable to change contract code"); temp_dir } #[must_use] pub fn copy_directory_to_tempdir(src_dir: impl AsRef) -> TempDir { let temp_dir = TempDir::new().expect("Unable to create a temporary directory"); fs_extra::dir::copy( src_dir.as_ref(), temp_dir.as_ref(), &fs_extra::dir::CopyOptions::new() .overwrite(true) .content_only(true), ) .expect("Failed to copy the directory"); temp_dir } fn copy_script_directory( src_dir: impl AsRef, dest_dir: impl AsRef, deps: Vec>, ) { let src_dir = Utf8PathBuf::from(src_dir.as_ref()); let dest_dir = Utf8PathBuf::from(dest_dir.as_ref()); let mut deps = get_deps_map_from_paths(deps); let manifest_path = dest_dir.join("Scarb.toml"); let contents = fs::read_to_string(&manifest_path).unwrap(); let mut parsed_toml: Table = toml::from_str(&contents) .with_context(|| format!("Failed to parse {manifest_path}")) .unwrap(); let deps_toml = parsed_toml .get_mut("dependencies") .unwrap() .as_table_mut() .unwrap(); let sncast_std = deps_toml .get_mut("sncast_std") .expect("sncast_std not found"); let sncast_std_path = sncast_std.get_mut("path").expect("No path to sncast_std"); let sncast_std_path = Utf8PathBuf::from(sncast_std_path.as_str().expect("Failed to extract string")); let sncast_std_path = src_dir.join(sncast_std_path); let sncast_std_path_absolute = sncast_std_path .canonicalize_utf8() .expect("Failed to canonicalize sncast_std path"); deps.insert(String::from("sncast_std"), sncast_std_path_absolute); for (key, value) in deps { let pkg = deps_toml.get_mut(&key).unwrap().as_table_mut().unwrap(); pkg.insert("path".to_string(), toml::Value::String(value.to_string())); } let modified_toml = toml::to_string(&parsed_toml).expect("Failed to serialize TOML"); let mut file = File::create(manifest_path).expect("Failed to create file"); file.write_all(modified_toml.as_bytes()) .expect("Failed to write to file"); } pub fn copy_script_directory_to_tempdir( src_dir: impl AsRef, deps: Vec>, ) -> TempDir { let temp_dir = copy_directory_to_tempdir(&src_dir); let dest_dir = Utf8PathBuf::from_path_buf(temp_dir.path().to_path_buf()) .expect("Failed to create Utf8PathBuf from PathBuf"); copy_script_directory(&src_dir, dest_dir, deps); temp_dir } pub fn copy_workspace_directory_to_tempdir( src_dir: impl AsRef, relative_member_paths: Vec>, deps: &[impl AsRef + Clone], ) -> TempDir { let src_dir = Utf8PathBuf::from(src_dir.as_ref()); let temp_dir = copy_directory_to_tempdir(&src_dir); let dest_dir = Utf8PathBuf::from_path_buf(temp_dir.path().to_path_buf()) .expect("Failed to create Utf8PathBuf from PathBuf"); for member in relative_member_paths { let member = member.as_ref().to_str().unwrap(); let src_member_path = src_dir.join(member); let dest_member_path = dest_dir.join(member); fs::create_dir_all(&dest_member_path).expect("Failed to create directories in temp dir"); copy_script_directory(&src_member_path, dest_member_path, deps.to_vec()); } temp_dir } #[must_use] pub fn get_deps_map_from_paths( paths: Vec>, ) -> HashMap { let mut deps = HashMap::::new(); for path in paths { let path = Utf8PathBuf::from_path_buf(path.as_ref().to_path_buf()) .expect("Failed to create Utf8PathBuf from PathBuf"); let manifest_path = path.join("Scarb.toml"); let package = get_package_metadata(&manifest_path, &None).expect("Failed to get package metadata"); deps.insert(package.name.clone(), path); } deps } pub fn get_address_from_keystore( keystore_path: impl AsRef, account_path: impl AsRef, password: &str, account_type: &AccountType, ) -> Felt { let contents = std::fs::read_to_string(account_path).unwrap(); let items: Map = serde_json::from_str(&contents).unwrap(); let deployment = items.get("deployment").unwrap(); let private_key = SigningKey::from_keystore( keystore_path, get_keystore_password(password).unwrap().as_str(), ) .unwrap(); let salt = Felt::from_hex( deployment .get("salt") .and_then(serde_json::Value::as_str) .unwrap(), ) .unwrap(); let class_hash = match account_type { AccountType::Braavos => BRAAVOS_BASE_ACCOUNT_CLASS_HASH, AccountType::OpenZeppelin | AccountType::Argent | AccountType::Ready => Felt::from_hex( deployment .get("class_hash") .and_then(serde_json::Value::as_str) .unwrap(), ) .unwrap(), }; let calldata = match account_type { AccountType::OpenZeppelin | AccountType::Braavos => { vec![private_key.verifying_key().scalar()] } // This is a serialization of `Signer` enum for the variant `StarknetSigner` from the Ready account code // One stands for `None` for the guardian argument AccountType::Argent | AccountType::Ready => { vec![Felt::ZERO, private_key.verifying_key().scalar(), Felt::ONE] } }; get_contract_address(salt, class_hash, &calldata, Felt::ZERO) } #[must_use] pub fn get_accounts_path(relative_path_from_cargo_toml: &str) -> String { use std::path::PathBuf; let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"); let binding = PathBuf::from(manifest_dir).join(relative_path_from_cargo_toml); binding .to_str() .expect("Failed to convert path to string") .to_string() } #[must_use] pub fn get_keystores_path(relative_path_from_cargo_toml: &str) -> String { use std::path::PathBuf; let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"); let binding = PathBuf::from(manifest_dir).join(relative_path_from_cargo_toml); binding .to_str() .expect("Failed to convert path to string") .to_string() } pub fn assert_tx_entry_failed( tx_entry: &ScriptTransactionEntry, name: &str, status: ScriptTransactionStatus, msg_contains: Vec<&str>, ) { assert_eq!(tx_entry.name, name); assert_eq!(tx_entry.status, status); let ScriptTransactionOutput::ErrorResponse(response) = &tx_entry.output else { panic!("Wrong response") }; for msg in msg_contains { assert!(response.message.contains(msg)); } assert!(tx_entry.timestamp > SCRIPT_ORIGIN_TIMESTAMP); } pub fn assert_tx_entry_success(tx_entry: &ScriptTransactionEntry, name: &str) { assert_eq!(tx_entry.name, name); assert_eq!(tx_entry.status, ScriptTransactionStatus::Success); let expected_selector = match tx_entry.output { ScriptTransactionOutput::DeployResponse(_) => "deploy", ScriptTransactionOutput::DeclareResponse(_) => "declare", ScriptTransactionOutput::InvokeResponse(_) => "invoke", ScriptTransactionOutput::ErrorResponse(_) => panic!("Error response received"), }; assert_eq!(expected_selector, name); assert!(tx_entry.timestamp > SCRIPT_ORIGIN_TIMESTAMP); } pub async fn create_and_deploy_oz_account() -> TempDir { create_and_deploy_account(OZ_CLASS_HASH, AccountType::OpenZeppelin).await } pub async fn create_and_deploy_account(class_hash: Felt, account_type: AccountType) -> TempDir { let class_hash = &class_hash.into_hex_string(); let account_type = match account_type { AccountType::OpenZeppelin => "oz", AccountType::Argent => "argent", AccountType::Ready => "ready", AccountType::Braavos => "braavos", }; let tempdir = tempdir().unwrap(); let accounts_file = "accounts.json"; let args = vec![ "--accounts-file", accounts_file, "account", "create", "--url", URL, "--name", "my_account", "--class-hash", class_hash, "--type", account_type, ]; runner(&args).current_dir(tempdir.path()).assert().success(); let contents = fs::read_to_string(tempdir.path().join(accounts_file)).unwrap(); let items: Value = serde_json::from_str(&contents).unwrap(); mint_token( items["alpha-sepolia"]["my_account"]["address"] .as_str() .unwrap(), u128::MAX, ) .await; let args = vec![ "--accounts-file", accounts_file, "--json", "account", "deploy", "--url", URL, "--name", "my_account", ]; runner(&args).current_dir(tempdir.path()).assert().success(); tempdir } pub fn join_tempdirs(from: &TempDir, to: &TempDir) { copy( from.path(), to.path(), &CopyOptions::new().overwrite(true).content_only(true), ) .expect("Failed to copy the directory"); } ================================================ FILE: crates/sncast/tests/helpers/mod.rs ================================================ pub mod constants; pub mod devnet; pub mod devnet_detection; pub mod devnet_provider; pub mod env; pub mod fixtures; pub mod runner; pub mod shell; ================================================ FILE: crates/sncast/tests/helpers/runner.rs ================================================ use snapbox::cargo_bin; use snapbox::cmd::Command; #[must_use] pub fn runner(args: &[&str]) -> Command { Command::new(cargo_bin!("sncast")).args(args) } ================================================ FILE: crates/sncast/tests/helpers/shell.rs ================================================ use camino::Utf8PathBuf; use snapbox::cmd::Command; #[must_use] pub fn os_specific_shell(script_path: &Utf8PathBuf) -> Command { let test_path = script_path.with_extension("sh"); let absolute_test_path = test_path.canonicalize_utf8().unwrap(); Command::new(absolute_test_path) } ================================================ FILE: crates/sncast/tests/integration/fee.rs ================================================ use sncast::helpers::fee::{FeeArgs, FeeSettings}; use starknet_rust::core::types::FeeEstimate; use starknet_types_core::felt::{Felt, NonZeroFelt}; #[tokio::test] async fn test_happy_case() { let args = FeeArgs { max_fee: None, l1_gas: Some(100), l1_gas_price: Some(200), l2_gas: Some(100), l2_gas_price: Some(200), l1_data_gas: Some(100), l1_data_gas_price: Some(200), tip: None, estimate_tip: false, }; let settings = args.try_into_fee_settings(None).unwrap(); assert_eq!( settings, FeeSettings { l1_gas: Some(100), l1_gas_price: Some(200), l2_gas: Some(100), l2_gas_price: Some(200), l1_data_gas: Some(100), l1_data_gas_price: Some(200), tip: Some(0), } ); } #[tokio::test] async fn test_max_fee_none() { let args = FeeArgs { max_fee: None, l1_gas: Some(100), l1_gas_price: Some(100), l2_gas: Some(100), l2_gas_price: Some(100), l1_data_gas: Some(100), l1_data_gas_price: Some(100), tip: Some(100), estimate_tip: false, }; let settings = args.try_into_fee_settings(None).unwrap(); assert_eq!( settings, FeeSettings { l1_gas: Some(100), l1_gas_price: Some(100), l2_gas: Some(100), l2_gas_price: Some(100), l1_data_gas: Some(100), l1_data_gas_price: Some(100), tip: Some(100), } ); } #[tokio::test] async fn test_max_fee_set() { let mock_fee_estimate = FeeEstimate { l1_gas_consumed: 1, l1_gas_price: 2, l2_gas_consumed: 3, l2_gas_price: 4, l1_data_gas_consumed: 5, l1_data_gas_price: 6, overall_fee: 44, }; let args = FeeArgs { max_fee: Some(NonZeroFelt::try_from(Felt::from(100)).unwrap()), l1_gas: None, l1_gas_price: None, l2_gas: None, l2_gas_price: None, l1_data_gas: None, l1_data_gas_price: None, tip: None, estimate_tip: false, }; let settings = args .try_into_fee_settings(Some(&mock_fee_estimate)) .unwrap(); assert_eq!( settings, FeeSettings { l1_gas: Some(1), l1_gas_price: Some(2), l2_gas: Some(3), l2_gas_price: Some(4), l1_data_gas: Some(5), l1_data_gas_price: Some(6), tip: Some(0), } ); } #[tokio::test] async fn test_max_fee_set_and_fee_estimate_higher() { let mock_fee_estimate = FeeEstimate { l1_gas_consumed: 10, l1_data_gas_price: 20, l2_gas_consumed: 30, l2_gas_price: 40, l1_data_gas_consumed: 50, l1_gas_price: 60, overall_fee: 4400, }; let args = FeeArgs { max_fee: Some(NonZeroFelt::try_from(Felt::from(100)).unwrap()), l1_gas: None, l1_gas_price: None, l2_gas: None, l2_gas_price: None, l1_data_gas: None, l1_data_gas_price: None, tip: None, estimate_tip: false, }; let err = args .try_into_fee_settings(Some(&mock_fee_estimate)) .unwrap_err(); assert_eq!( err.to_string(), format!( "Estimated fee ({}) is higher than provided max fee ({})", mock_fee_estimate.overall_fee, Felt::from(args.max_fee.unwrap()) ) ); } #[tokio::test] #[should_panic(expected = "Fee estimate must be passed when max_fee is provided")] async fn test_max_fee_set_and_fee_estimate_none() { let args = FeeArgs { max_fee: Some(NonZeroFelt::try_from(Felt::from(100)).unwrap()), l1_gas: None, l1_gas_price: None, l2_gas: None, l2_gas_price: None, l1_data_gas: None, l1_data_gas_price: None, tip: None, estimate_tip: false, }; args.try_into_fee_settings(None).unwrap(); } #[tokio::test] async fn test_all_args_none() { let args = FeeArgs { max_fee: None, l1_gas: None, l1_gas_price: None, l2_gas: None, l2_gas_price: None, l1_data_gas: None, l1_data_gas_price: None, tip: None, estimate_tip: false, }; let settings = args.try_into_fee_settings(None).unwrap(); assert_eq!( settings, FeeSettings { l1_gas: None, l1_gas_price: None, l2_gas: None, l2_gas_price: None, l1_data_gas: None, l1_data_gas_price: None, tip: Some(0), } ); } #[tokio::test] async fn test_estimate_tip() { let args = FeeArgs { max_fee: None, l1_gas: None, l1_gas_price: None, l2_gas: None, l2_gas_price: None, l1_data_gas: None, l1_data_gas_price: None, tip: None, estimate_tip: true, }; let settings = args.try_into_fee_settings(None).unwrap(); assert_eq!( settings, FeeSettings { l1_gas: None, l1_gas_price: None, l2_gas: None, l2_gas_price: None, l1_data_gas: None, l1_data_gas_price: None, tip: None, } ); } ================================================ FILE: crates/sncast/tests/integration/lib_tests.rs ================================================ use crate::helpers::constants::{ DEVNET_OZ_CLASS_HASH_CAIRO_0, DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS, URL, }; use crate::helpers::fixtures::create_test_provider; use camino::Utf8PathBuf; use shared::rpc::{get_rpc_version, is_expected_version}; use sncast::helpers::configuration::CastConfig; use sncast::helpers::rpc::RpcArgs; use sncast::response::ui::UI; use sncast::{check_if_legacy_contract, get_account, get_provider}; use starknet_rust::macros::felt; use url::Url; #[tokio::test] async fn test_get_provider() { let provider = get_provider(&Url::parse(URL).unwrap()); assert!(provider.is_ok()); } #[tokio::test] async fn test_get_account() { let provider = create_test_provider(); let config = CastConfig { account: "user1".to_string(), accounts_file: Utf8PathBuf::from("tests/data/accounts/accounts.json"), ..Default::default() }; let rpc_args = RpcArgs { url: Some(Url::parse(URL).expect("Failed to parse URL")), network: None, }; let account = get_account(&config, &provider, &rpc_args, &UI::default()) .await .unwrap(); assert_eq!(account.chain_id(), felt!("0x534e5f5345504f4c4941")); assert_eq!( account.address(), felt!("0xf6ecd22832b7c3713cfa7826ee309ce96a2769833f093795fafa1b8f20c48b") ); } #[tokio::test] async fn test_get_account_no_file() { let provider = create_test_provider(); let config = CastConfig { account: "user1".to_string(), accounts_file: Utf8PathBuf::from("tests/data/accounts/nonexistentfile.json"), ..Default::default() }; let rpc_args = RpcArgs { url: Some(Url::parse(URL).expect("Failed to parse URL")), network: None, }; let account = get_account(&config, &provider, &rpc_args, &UI::default()).await; let err = account.unwrap_err(); assert!( err.to_string() .contains("Accounts file = tests/data/accounts/nonexistentfile.json does not exist!") ); } #[tokio::test] async fn test_get_account_invalid_file() { let provider = create_test_provider(); let config = CastConfig { account: "user1".to_string(), accounts_file: Utf8PathBuf::from("tests/data/accounts/invalid_format.json"), ..Default::default() }; let rpc_args = RpcArgs { url: Some(Url::parse(URL).expect("Failed to parse URL")), network: None, }; let account = get_account(&config, &provider, &rpc_args, &UI::default()).await; let err = account.unwrap_err(); assert!(err .to_string() .contains("Failed to parse field `alpha-sepolia.?` in file 'tests/data/accounts/invalid_format.json': expected `,` or `}` at line 8 column 9") ); } #[tokio::test] async fn test_get_account_no_account() { let provider = create_test_provider(); let config = CastConfig { account: String::new(), accounts_file: Utf8PathBuf::from("tests/data/accounts/accounts.json"), ..Default::default() }; let rpc_args = RpcArgs { url: Some(Url::parse(URL).expect("Failed to parse URL")), network: None, }; let account = get_account(&config, &provider, &rpc_args, &UI::default()).await; let err = account.unwrap_err(); assert!( err.to_string() .contains("Account name not passed nor found in snfoundry.toml") ); } #[tokio::test] async fn test_get_account_no_user_for_network() { let provider = create_test_provider(); let config = CastConfig { account: "user100".to_string(), accounts_file: Utf8PathBuf::from("tests/data/accounts/accounts.json"), ..Default::default() }; let rpc_args = RpcArgs { url: Some(Url::parse(URL).expect("Failed to parse URL")), network: None, }; let account = get_account(&config, &provider, &rpc_args, &UI::default()).await; let err = account.unwrap_err(); assert!( err.to_string() .contains("Account = user100 not found under network = alpha-sepolia") ); } #[tokio::test] async fn test_get_account_failed_to_convert_field_elements() { let provider = create_test_provider(); let config = CastConfig { account: "with_invalid_private_key".to_string(), accounts_file: Utf8PathBuf::from("tests/data/accounts/faulty_accounts_invalid_felt.json"), ..Default::default() }; let rpc_args = RpcArgs { url: Some(Url::parse(URL).expect("Failed to parse URL")), network: None, }; let account1 = get_account(&config, &provider, &rpc_args, &UI::default()).await; let err = account1.unwrap_err(); assert!(err.to_string().contains( "Failed to parse field `alpha-sepolia.with_invalid_private_key` in file 'tests/data/accounts/faulty_accounts_invalid_felt.json': data did not match any variant of untagged enum SignerType at line 9 column 9" )); } // TODO (#1690): Move this test to the shared crate and execute it for a real node #[tokio::test] async fn test_supported_rpc_version_matches_devnet_version() { let provider = create_test_provider(); let devnet_spec_version = get_rpc_version(&provider).await.unwrap(); assert!(is_expected_version(&devnet_spec_version)); } #[tokio::test] async fn test_check_if_legacy_contract_by_class_hash() { let provider = create_test_provider(); let class_hash = DEVNET_OZ_CLASS_HASH_CAIRO_0 .parse() .expect("Failed to parse DEVNET_OZ_CLASS_HASH_CAIRO_0"); let mock_address = "0x1".parse().unwrap(); let is_legacy = check_if_legacy_contract(Some(class_hash), mock_address, &provider) .await .unwrap(); assert!(is_legacy); } #[tokio::test] async fn test_check_if_legacy_contract_by_address() { let provider = create_test_provider(); let address = DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS .parse() .expect("Failed to parse DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS"); let is_legacy = check_if_legacy_contract(None, address, &provider) .await .unwrap(); assert!(!is_legacy); } ================================================ FILE: crates/sncast/tests/integration/mod.rs ================================================ mod fee; mod lib_tests; mod wait_for_tx; ================================================ FILE: crates/sncast/tests/integration/wait_for_tx.rs ================================================ use crate::helpers::{ constants::{ACCOUNT, ACCOUNT_FILE_PATH, URL}, fixtures::{create_test_provider, invoke_contract}, }; use camino::Utf8PathBuf; use sncast::helpers::{ configuration::CastConfig, constants::UDC_ADDRESS, fee::FeeSettings, rpc::RpcArgs, }; use sncast::response::ui::UI; use url::Url; use crate::helpers::constants::{ CONSTRUCTOR_WITH_PARAMS_CONTRACT_CLASS_HASH_SEPOLIA, MAP_CONTRACT_CLASS_HASH_SEPOLIA, MAP_CONTRACT_DECLARE_TX_HASH_SEPOLIA, }; use conversions::string::IntoHexStr; use sncast::{ValidatedWaitParams, get_account}; use sncast::{WaitForTx, handle_wait_for_tx, wait_for_tx}; use starknet_rust::contract::{ContractFactory, UdcSelector}; use starknet_types_core::felt::Felt; #[tokio::test] async fn test_happy_path() { let provider = create_test_provider(); let ui = UI::default(); let res = wait_for_tx( &provider, MAP_CONTRACT_DECLARE_TX_HASH_SEPOLIA.parse().unwrap(), ValidatedWaitParams::default(), Some(&ui), ) .await; assert!(res.is_ok()); assert!(matches!(res.unwrap().as_str(), "Transaction accepted")); } #[tokio::test] async fn test_rejected_transaction() { let provider = create_test_provider(); let config = CastConfig { account: ACCOUNT.to_string(), accounts_file: Utf8PathBuf::from(ACCOUNT_FILE_PATH), ..Default::default() }; let rpc_args = RpcArgs { url: Some(Url::parse(URL).unwrap()), network: None, }; let account = get_account(&config, &provider, &rpc_args, &UI::default()) .await .expect("Could not get the account"); let account = match account { sncast::AccountVariant::LocalWallet(acc) => acc, sncast::AccountVariant::Ledger(_) => panic!("Ledger account not supported in tests"), }; let factory = ContractFactory::new_with_udc( MAP_CONTRACT_CLASS_HASH_SEPOLIA.parse().unwrap(), account, UdcSelector::New, ); let deployment = factory .deploy_v3(Vec::new(), Felt::ONE, false) .l1_gas(1) .l2_gas(1) .l1_data_gas(1) .send() .await .map_err(|e| anyhow::anyhow!(e)); let resp = deployment.unwrap_err(); assert!( resp.to_string() .contains("InsufficientResourcesForValidate") ); } #[tokio::test] #[should_panic( expected = "Transaction execution failed: Provider(StarknetError(InsufficientResourcesForValidate))" )] async fn test_wait_for_reverted_transaction() { let provider = create_test_provider(); let salt = "0x029c81e6487b5f9278faa6f454cda3c8eca259f99877faab823b3704327cd695"; let fee_settings = FeeSettings { l1_gas: Some(1), l1_gas_price: Some(1), l2_gas: Some(1), l2_gas_price: Some(1), l1_data_gas: Some(1), l1_data_gas_price: Some(1), tip: None, }; let transaction_hash = invoke_contract( ACCOUNT, UDC_ADDRESS.into_hex_string().as_str(), "deployContract", fee_settings, &[ CONSTRUCTOR_WITH_PARAMS_CONTRACT_CLASS_HASH_SEPOLIA, salt, "0x1", "0x3", "0x43", "0x41", "0x1", ], ) .await .transaction_hash; let ui = UI::default(); wait_for_tx( &provider, transaction_hash, ValidatedWaitParams::new(1, 3), Some(&ui), ) .await .map_err(anyhow::Error::from) .unwrap(); } #[tokio::test] #[should_panic(expected = "sncast timed out while waiting for transaction to succeed")] async fn test_wait_for_nonexistent_tx() { let provider = create_test_provider(); let ui = UI::default(); wait_for_tx( &provider, "0x123456789".parse().expect("Could not parse a number"), ValidatedWaitParams::new(1, 3), Some(&ui), ) .await .map_err(anyhow::Error::from) .unwrap(); } #[tokio::test] async fn test_happy_path_handle_wait_for_tx() { let provider = create_test_provider(); let ui = UI::default(); let res = handle_wait_for_tx( &provider, MAP_CONTRACT_DECLARE_TX_HASH_SEPOLIA.parse().unwrap(), 1, WaitForTx { wait: true, wait_params: ValidatedWaitParams::new(5, 63), show_ui_outputs: true, }, &ui, ) .await; assert!(matches!(res, Ok(1))); } #[tokio::test] #[should_panic(expected = "Invalid values for retry_interval and/or timeout!")] async fn test_wait_for_wrong_retry_values() { let provider = create_test_provider(); let ui = UI::default(); wait_for_tx( &provider, MAP_CONTRACT_DECLARE_TX_HASH_SEPOLIA.parse().unwrap(), ValidatedWaitParams::new(2, 1), Some(&ui), ) .await .unwrap(); } #[tokio::test] #[should_panic(expected = "Invalid values for retry_interval and/or timeout!")] async fn test_wait_for_wrong_retry_values_timeout_zero() { let provider = create_test_provider(); let ui = UI::default(); wait_for_tx( &provider, MAP_CONTRACT_DECLARE_TX_HASH_SEPOLIA.parse().unwrap(), ValidatedWaitParams::new(2, 0), Some(&ui), ) .await .unwrap(); } #[tokio::test] #[should_panic(expected = "Invalid values for retry_interval and/or timeout!")] async fn test_wait_for_wrong_retry_values_interval_zero() { let provider = create_test_provider(); let ui = UI::default(); wait_for_tx( &provider, MAP_CONTRACT_DECLARE_TX_HASH_SEPOLIA.parse().unwrap(), ValidatedWaitParams::new(0, 1), Some(&ui), ) .await .unwrap(); } ================================================ FILE: crates/sncast/tests/main.rs ================================================ mod docs_snippets; mod e2e; pub mod helpers; mod integration; ================================================ FILE: crates/sncast/tests/shell/call.sh ================================================ #!/bin/bash CAST_BINARY="$1" URL="$2" DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA="$3" $CAST_BINARY \ --json \ call \ --url \ "$URL" \ --contract-address \ "$DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA" \ --function \ complex_fn \ --arguments 'array![array![1, 2], array![3, 4, 5], array![6]],'\ '12,'\ '-128_i8,'\ '"Some string (a ByteArray)",'\ "('a shortstring', 32_u32),"\ 'true,'\ '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' ================================================ FILE: crates/sncast/tests/shell/call_unsigned.sh ================================================ #!/bin/bash CAST_BINARY="$1" URL="$2" DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA="$3" $CAST_BINARY \ --json \ call \ --url \ "$URL" \ --contract-address \ "$DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA" \ --function \ multiple_signed_fn \ --arguments '-3_i32, -4' ================================================ FILE: crates/sncast/tests/shell/deploy.sh ================================================ #!/bin/bash CAST_BINARY="$1" URL="$2" CONSTRUCTOR_WITH_PARAMS_CONTRACT_CLASS_HASH_SEPOLIA="$3" $CAST_BINARY \ --accounts-file \ accounts.json \ --account \ my_account \ --json \ deploy \ --url \ "$URL" \ --class-hash \ "$CONSTRUCTOR_WITH_PARAMS_CONTRACT_CLASS_HASH_SEPOLIA" \ --arguments \ '0x420, 0x2137_u256' \ --l1-gas \ 100000 \ --l1-gas-price \ 10000000000000 \ --l2-gas \ 1000000000 \ --l2-gas-price \ 100000000000000000000 \ --l1-data-gas \ 100000 \ --l1-data-gas-price \ 10000000000000 ================================================ FILE: crates/sncast/tests/shell/invoke.sh ================================================ #!/bin/bash CAST_BINARY="$1" URL="$2" DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA="$3" $CAST_BINARY \ --accounts-file \ accounts.json \ --account \ my_account \ --json \ invoke \ --url \ "$URL" \ --contract-address \ "$DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA" \ --function \ complex_fn \ --arguments 'array![array![1, 2], array![3, 4, 5], array![6]],'\ '12,'\ '-128_i8,'\ '"Some string (a ByteArray)",'\ "('a shortstring', 32_u32),"\ 'true,'\ '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' \ --max-fee \ 99999999999999999 \ ================================================ FILE: crates/sncast/tests/shell/serialize.sh ================================================ #!/bin/bash CAST_BINARY="$1" URL="$2" DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA="$3" $CAST_BINARY \ utils \ serialize \ --url \ "$URL" \ --contract-address \ "$DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA" \ --function \ complex_fn \ --arguments 'array![array![1, 2], array![3, 4, 5], array![6]],'\ '12,'\ '-128_i8,'\ '"Some string (a ByteArray)",'\ "('a shortstring', 32_u32),"\ 'true,'\ '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' \ ================================================ FILE: crates/snforge-scarb-plugin/Cargo.toml ================================================ [package] name = "snforge_scarb_plugin" version = "0.59.0" edition = "2024" publish = false rust-version = "1.87.0" [lib] crate-type = ["rlib", "cdylib"] [profile.ci] inherits = "release" debug-assertions = true [dependencies] cairo-lang-macro = "0.2.1" cairo-lang-parser = "=2.12.3" cairo-lang-utils = "=2.12.3" cairo-lang-syntax = "=2.12.3" cairo-lang-primitive-token = "1" url = "=2.5.4" indoc = "=2.0.5" smol_str = "=0.3.2" num-bigint = "=0.4.6" regex = "1.11.1" xxhash-rust = { version = "0.8", features = ["xxh3"] } [dev-dependencies] cairo-lang-formatter = "=2.12.3" ================================================ FILE: crates/snforge-scarb-plugin/Scarb.toml ================================================ [package] name = "snforge_scarb_plugin" version = "0.59.0" edition = "2024_07" include = ["target/scarb/cairo-plugin/"] [cairo-plugin] ================================================ FILE: crates/snforge-scarb-plugin/clippy.toml ================================================ disallowed-methods = [ { path = "std::env::args", reason = "all external inputs should be handled inside ExternalInputs struct" }, { path = "std::env::args_os", reason = "all external inputs should be handled inside ExternalInputs struct" }, { path = "std::env::current_dir", reason = "all external inputs should be handled inside ExternalInputs struct" }, { path = "std::env::current_exe", reason = "all external inputs should be handled inside ExternalInputs struct" }, { path = "std::env::home_dir", reason = "all external inputs should be handled inside ExternalInputs struct" }, { path = "std::env::var", reason = "all external inputs should be handled inside ExternalInputs struct" }, { path = "std::env::var_os", reason = "all external inputs should be handled inside ExternalInputs struct" }, { path = "std::env::vars", reason = "all external inputs should be handled inside ExternalInputs struct" }, { path = "std::env::vars_os", reason = "all external inputs should be handled inside ExternalInputs struct" }, { path = "std::fs::exists", reason = "all external inputs should be handled inside ExternalInputs struct" }, { path = "std::fs::hard_link", reason = "all external inputs should be handled inside ExternalInputs struct" }, { path = "std::fs::metadata", reason = "all external inputs should be handled inside ExternalInputs struct" }, { path = "std::fs::read", reason = "all external inputs should be handled inside ExternalInputs struct" }, { path = "std::fs::read_dir", reason = "all external inputs should be handled inside ExternalInputs struct" }, { path = "std::fs::read_link", reason = "all external inputs should be handled inside ExternalInputs struct" }, { path = "std::fs::read_to_string", reason = "all external inputs should be handled inside ExternalInputs struct" }, { path = "std::fs::symlink_metadata", reason = "all external inputs should be handled inside ExternalInputs struct" }, ] ================================================ FILE: crates/snforge-scarb-plugin/src/args/named.rs ================================================ use crate::attributes::{AttributeInfo, ErrorExt}; use cairo_lang_macro::Diagnostic; use cairo_lang_syntax::node::ast::Expr; use indoc::formatdoc; use smol_str::SmolStr; use std::{ collections::HashMap, ops::{Deref, DerefMut}, }; #[derive(Debug, Default, Clone)] pub struct NamedArgs(HashMap>); impl Deref for NamedArgs { type Target = HashMap>; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for NamedArgs { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl NamedArgs { pub fn allow_only(&self, expected: &[&str]) -> Result<(), Diagnostic> { let mut unexpected = self .0 .keys() .filter(|k| !expected.contains(&k.as_str())) .collect::>(); if unexpected.is_empty() { return Ok(()); } // Sort for deterministic output unexpected.sort_by_key(|k| k.as_str()); Err(T::error(formatdoc!( "unexpected argument(s): {}", unexpected .iter() .map(|k| format!("<{}>", k.as_str())) .collect::>() .join(", ") ))) } pub fn as_once(&self, arg: &str) -> Result<&Expr, Diagnostic> { let exprs = self .0 .get(arg) .ok_or_else(|| Diagnostic::error(format!("<{arg}> argument is missing")))?; Self::once(exprs, arg) } pub fn as_once_optional(&self, arg: &str) -> Result, Diagnostic> { let exprs = self.0.get(arg); match exprs { None => Ok(None), Some(exprs) => Self::once(exprs, arg).map(Some), } } fn once<'a>(exprs: &'a [Expr], arg: &str) -> Result<&'a Expr, Diagnostic> { if exprs.len() == 1 { Ok(exprs.last().unwrap()) } else { Err(Diagnostic::error(format!( "<{arg}> argument was specified {} times, expected to be used only once", exprs.len() ))) } } pub fn one_of_once + Copy>(&self, args: &[T]) -> Result<(T, &Expr), Diagnostic> { let (field, values) = self.one_of(args)?; let value = Self::once(values, field.as_ref())?; Ok((field, value)) } pub fn one_of + Copy>(&self, args: &[T]) -> Result<(T, &Vec), Diagnostic> { let occurred_args: Vec<_> = args .iter() .filter(|arg| self.0.contains_key(arg.as_ref())) .collect(); match occurred_args.as_slice() { [field] => Ok((**field, self.0.get(field.as_ref()).unwrap())), _ => Err(format!( "exactly one of {} should be specified, got {}", args.iter() .map(|field| format!("<{}>", field.as_ref())) .collect::>() .join(" | "), occurred_args.len() )), } .map_err(Diagnostic::error) } } ================================================ FILE: crates/snforge-scarb-plugin/src/args/unnamed.rs ================================================ use crate::attributes::{AttributeInfo, ErrorExt}; use cairo_lang_macro::Diagnostic; use cairo_lang_syntax::node::ast::Expr; use std::{collections::HashMap, ops::Deref}; pub struct UnnamedArgs<'a>(Vec<(usize, &'a Expr)>); impl<'a> Deref for UnnamedArgs<'a> { type Target = Vec<(usize, &'a Expr)>; fn deref(&self) -> &Self::Target { &self.0 } } impl UnnamedArgs<'_> { pub fn new(unnamed: &HashMap) -> UnnamedArgs<'_> { let mut args: Vec<_> = unnamed.iter().collect(); args.sort_by_key(|(a, _)| *a); let args = args.into_iter().map(|(&pos, expr)| (pos, expr)).collect(); UnnamedArgs(args) } } impl<'a> UnnamedArgs<'a> { pub fn of_length( &self, ) -> Result<&[(usize, &'a Expr); N], Diagnostic> { self.as_slice() .try_into() .map_err(|_| T::error(format!("expected arguments: {}, got: {}", N, self.len()))) } } ================================================ FILE: crates/snforge-scarb-plugin/src/args.rs ================================================ use self::{named::NamedArgs, unnamed::UnnamedArgs}; use crate::attributes::{AttributeInfo, ErrorExt}; use cairo_lang_macro::Diagnostic; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::{ Terminal, ast::{ArgClause, Expr, OptionArgListParenthesized}, }; use smol_str::SmolStr; use std::collections::HashMap; pub mod named; pub mod unnamed; #[derive(Debug, Default)] pub struct Arguments { named: NamedArgs, unnamed: HashMap, shorthand: HashMap, } impl Arguments { pub fn new( db: &SimpleParserDatabase, args: OptionArgListParenthesized, warns: &mut Vec, ) -> Self { let args = match args { OptionArgListParenthesized::Empty(_) => vec![], OptionArgListParenthesized::ArgListParenthesized(args) => { let args = args.arguments(db).elements(db); if args.len() == 0 { warns.push(T::warn( "used with empty argument list. Either remove () or specify some arguments", )); } args.collect::>() } }; let mut this = Self::default(); for (i, arg) in args.into_iter().enumerate() { match arg.arg_clause(db) { ArgClause::Unnamed(value) => { this.unnamed.insert(i, value.value(db)); } ArgClause::FieldInitShorthand(value) => { this.shorthand.insert(i, value.name(db).name(db).text(db)); } ArgClause::Named(value) => { this.named .entry(value.name(db).text(db)) .or_default() .push(value.value(db)); } } } this } pub fn is_empty(&self) -> bool { self.named.is_empty() && self.unnamed.is_empty() && self.shorthand.is_empty() } #[inline] pub fn named_only(&self) -> Result<&NamedArgs, Diagnostic> { if self.shorthand.is_empty() && self.unnamed.is_empty() { Ok(&self.named) } else { Err(T::error("can be used with named arguments only")) } } #[inline] pub fn unnamed_only(&self) -> Result, Diagnostic> { if self.shorthand.is_empty() && self.named.is_empty() { Ok(UnnamedArgs::new(&self.unnamed)) } else { Err(T::error("can be used with unnamed arguments only")) } } #[inline] pub fn unnamed(&self) -> UnnamedArgs<'_> { UnnamedArgs::new(&self.unnamed) } #[inline] pub fn named(&self) -> NamedArgs { self.named.clone() } #[inline] pub fn assert_is_empty(&self) -> Result<(), Diagnostic> { if self.is_empty() { Ok(()) } else { Err(T::error("does not accept any arguments"))? } } } ================================================ FILE: crates/snforge-scarb-plugin/src/asserts.rs ================================================ use crate::attributes::{AttributeInfo, ErrorExt}; use cairo_lang_macro::Diagnostic; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::{ast::FunctionWithBody, helpers::QueryAttrs}; pub fn assert_is_used_once( db: &SimpleParserDatabase, func: &FunctionWithBody, ) -> Result<(), Diagnostic> { if func.attributes(db).has_attr(db, T::ATTR_NAME) { Err(T::error("can only be used once per item")) } else { Ok(()) } } ================================================ FILE: crates/snforge-scarb-plugin/src/attributes/available_gas.rs ================================================ use crate::{ args::Arguments, attributes::{AttributeCollector, AttributeInfo, AttributeTypeData}, cairo_expression::CairoExpression, config_statement::extend_with_config_cheatcodes, types::{Number, ParseFromExpr}, }; use cairo_lang_macro::{Diagnostic, Diagnostics, ProcMacroResult, TokenStream, quote}; use cairo_lang_parser::utils::SimpleParserDatabase; pub struct AvailableGasCollector; impl AttributeInfo for AvailableGasCollector { const ATTR_NAME: &'static str = "available_gas"; } impl AttributeTypeData for AvailableGasCollector { const CHEATCODE_NAME: &'static str = "set_config_available_gas"; } impl AttributeCollector for AvailableGasCollector { fn args_into_config_expression( db: &SimpleParserDatabase, args: Arguments, _warns: &mut Vec, ) -> Result { Ok(from_resource_bounds(db, &args)?) } } fn from_resource_bounds( db: &SimpleParserDatabase, args: &Arguments, ) -> Result { let named_args = args.named_only::()?; named_args.allow_only::(&["l1_gas", "l1_data_gas", "l2_gas"])?; let max = u64::MAX; let l1_gas = named_args .as_once_optional("l1_gas")? .map(|arg| Number::parse_from_expr::(db, arg, "l1_gas")) .transpose()? .unwrap_or(Number(max.into())); let l1_data_gas = named_args .as_once_optional("l1_data_gas")? .map(|arg| Number::parse_from_expr::(db, arg, "l1_data_gas")) .transpose()? .unwrap_or(Number(max.into())); let l2_gas = named_args .as_once_optional("l2_gas")? .map(|arg| Number::parse_from_expr::(db, arg, "l2_gas")) .transpose()? .unwrap_or(Number(max.into())); l1_gas.validate_in_gas_range::("l1_gas")?; l1_data_gas.validate_in_gas_range::("l1_data_gas")?; l2_gas.validate_in_gas_range::("l2_gas")?; let l1_gas_expr = l1_gas.as_cairo_expression(); let l1_data_gas_expr = l1_data_gas.as_cairo_expression(); let l2_gas_expr = l2_gas.as_cairo_expression(); Ok(quote!( snforge_std::_internals::config_types::AvailableResourceBoundsConfig { l1_gas: #l1_gas_expr, l1_data_gas: #l1_data_gas_expr, l2_gas: #l2_gas_expr, } )) } #[must_use] pub fn available_gas(args: TokenStream, item: TokenStream) -> ProcMacroResult { extend_with_config_cheatcodes::(args, item) } ================================================ FILE: crates/snforge-scarb-plugin/src/attributes/disable_predeployed_contracts.rs ================================================ use super::{AttributeInfo, AttributeTypeData}; use crate::{ args::Arguments, attributes::AttributeCollector, config_statement::extend_with_config_cheatcodes, }; use cairo_lang_macro::{Diagnostic, Diagnostics, ProcMacroResult, TokenStream, quote}; use cairo_lang_parser::utils::SimpleParserDatabase; pub struct PredeployedContractsCollector; impl AttributeInfo for PredeployedContractsCollector { const ATTR_NAME: &'static str = "disable_predeployed_contracts"; } impl AttributeTypeData for PredeployedContractsCollector { const CHEATCODE_NAME: &'static str = "set_config_disable_contracts"; } impl AttributeCollector for PredeployedContractsCollector { fn args_into_config_expression( _db: &SimpleParserDatabase, args: Arguments, _warns: &mut Vec, ) -> Result { args.assert_is_empty::()?; Ok(quote! { snforge_std::_internals::config_types::PredeployedContractsConfig { is_disabled: true } }) } } #[must_use] pub fn disable_predeployed_contracts(args: TokenStream, item: TokenStream) -> ProcMacroResult { extend_with_config_cheatcodes::(args, item) } ================================================ FILE: crates/snforge-scarb-plugin/src/attributes/fork/block_id.rs ================================================ use super::ForkCollector; use crate::{ attributes::ErrorExt, cairo_expression::CairoExpression, types::{Number, ParseFromExpr}, }; use cairo_lang_macro::{Diagnostic, TokenStream, quote}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::{ast::Expr, helpers::GetIdentifier}; #[derive(Debug, Clone, Copy)] pub enum BlockIdVariants { Hash, Number, Tag, } impl AsRef for BlockIdVariants { fn as_ref(&self) -> &str { match self { Self::Hash => "block_hash", Self::Number => "block_number", Self::Tag => "block_tag", } } } #[derive(Debug, Clone)] pub enum BlockId { Hash(Number), Number(Number), Tag, } impl CairoExpression for BlockId { fn as_cairo_expression(&self) -> TokenStream { match self { Self::Hash(hash) => { let block_hash = hash.as_cairo_expression(); quote!(snforge_std::_internals::config_types::BlockId::BlockHash(#block_hash)) } Self::Number(number) => { let block_number = number.as_cairo_expression(); quote!(snforge_std::_internals::config_types::BlockId::BlockNumber(#block_number)) } Self::Tag => quote!(snforge_std::_internals::config_types::BlockId::BlockTag), } } } impl ParseFromExpr<(BlockIdVariants, &Expr)> for BlockId { fn parse_from_expr( db: &SimpleParserDatabase, (variant, block_args): &(BlockIdVariants, &Expr), arg_name: &str, ) -> Result { match variant { BlockIdVariants::Tag => { if let Expr::Path(path) = block_args { let segments = path.segments(db).elements(db); if segments.len() == 1 { let segment = segments.last().unwrap(); // currently no other tags if segment.identifier(db).as_str() == "latest" { return Ok(Self::Tag); } } } Err(ForkCollector::error(format!( "<{arg_name}> value incorrect, expected: latest", ))) } BlockIdVariants::Hash => { let hash = Number::parse_from_expr::( db, block_args, BlockIdVariants::Hash.as_ref(), )?; Ok(Self::Hash(hash)) } BlockIdVariants::Number => { let number = Number::parse_from_expr::( db, block_args, BlockIdVariants::Number.as_ref(), )?; Ok(Self::Number(number)) } } } } ================================================ FILE: crates/snforge-scarb-plugin/src/attributes/fork.rs ================================================ use self::block_id::{BlockId, BlockIdVariants}; use crate::{ args::Arguments, attributes::{AttributeCollector, AttributeInfo, AttributeTypeData}, branch, cairo_expression::CairoExpression, config_statement::extend_with_config_cheatcodes, types::ParseFromExpr, }; use cairo_lang_macro::{Diagnostic, Diagnostics, ProcMacroResult, Severity, TokenStream, quote}; use cairo_lang_parser::utils::SimpleParserDatabase; use url::Url; mod block_id; pub struct ForkCollector; impl AttributeInfo for ForkCollector { const ATTR_NAME: &'static str = "fork"; } impl AttributeTypeData for ForkCollector { const CHEATCODE_NAME: &'static str = "set_config_fork"; } impl AttributeCollector for ForkCollector { fn args_into_config_expression( db: &SimpleParserDatabase, args: Arguments, _warns: &mut Vec, ) -> Result { let expr = branch!( inline_args(db, &args), overridden_args(db, &args), from_file_args(db, &args) )?; Ok(expr) } } fn inline_args(db: &SimpleParserDatabase, args: &Arguments) -> Result { let named_args = args.named_only::()?; named_args.allow_only::(&["url", "block_hash", "block_number", "block_tag"])?; let block_id = named_args.one_of_once(&[ BlockIdVariants::Hash, BlockIdVariants::Number, BlockIdVariants::Tag, ])?; let url = named_args.as_once("url")?; let block_id = BlockId::parse_from_expr::(db, &block_id, block_id.0.as_ref())?; let url = Url::parse_from_expr::(db, url, "url")?; let block_id = block_id.as_cairo_expression(); let url = url.as_cairo_expression(); Ok(quote!( snforge_std::_internals::config_types::ForkConfig::Inline( snforge_std::_internals::config_types::InlineForkConfig { url: #url, block: #block_id } ) )) } fn from_file_args(db: &SimpleParserDatabase, args: &Arguments) -> Result { let &[arg] = args .unnamed_only::()? .of_length::<1, ForkCollector>()?; let name = String::parse_from_expr::(db, arg.1, arg.0.to_string().as_str())?; let name = name.as_cairo_expression(); Ok(quote!(snforge_std::_internals::config_types::ForkConfig::Named(#name))) } fn overridden_args(db: &SimpleParserDatabase, args: &Arguments) -> Result { let &[arg] = args.unnamed().of_length::<1, ForkCollector>()?; let named_args = args.named(); named_args.allow_only::(&["block_hash", "block_number", "block_tag"])?; let block_id = named_args.one_of_once(&[ BlockIdVariants::Hash, BlockIdVariants::Number, BlockIdVariants::Tag, ])?; let block_id = BlockId::parse_from_expr::(db, &block_id, block_id.0.as_ref())?; let name = String::parse_from_expr::(db, arg.1, arg.0.to_string().as_str())?; let block_id = block_id.as_cairo_expression(); let name = name.as_cairo_expression(); Ok(quote!( snforge_std::_internals::config_types::ForkConfig::Overridden( snforge_std::_internals::config_types::OverriddenForkConfig { block: #block_id, name: #name } ) )) } #[must_use] pub fn fork(args: TokenStream, item: TokenStream) -> ProcMacroResult { extend_with_config_cheatcodes::(args, item) } ================================================ FILE: crates/snforge-scarb-plugin/src/attributes/fuzzer/wrapper.rs ================================================ use crate::args::Arguments; use crate::attributes::AttributeInfo; use crate::attributes::internal_config_statement::InternalConfigStatementCollector; use crate::attributes::test::TestCollector; use crate::common::{into_proc_macro_result, with_parsed_values}; use crate::format_ident; use crate::utils::{SyntaxNodeUtils, create_single_token, get_statements}; use cairo_lang_macro::{Diagnostic, Diagnostics, ProcMacroResult, TokenStream, quote}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::ast::OptionTypeClause::{Empty, TypeClause}; use cairo_lang_syntax::node::ast::{FunctionWithBody, Param}; use cairo_lang_syntax::node::helpers::QueryAttrs; use cairo_lang_syntax::node::with_db::SyntaxNodeWithDb; use cairo_lang_syntax::node::{Terminal, TypedSyntaxNode}; pub struct FuzzerWrapperCollector; impl AttributeInfo for FuzzerWrapperCollector { const ATTR_NAME: &'static str = "__fuzzer_wrapper"; } #[must_use] pub fn fuzzer_wrapper(args: TokenStream, item: TokenStream) -> ProcMacroResult { into_proc_macro_result(args, item, |args, item, warns| { with_parsed_values::(args, item, warns, fuzzer_wrapper_internal) }) } #[expect(clippy::needless_pass_by_value)] fn fuzzer_wrapper_internal( db: &SimpleParserDatabase, func: &FunctionWithBody, _args_db: &SimpleParserDatabase, args: Arguments, _warns: &mut Vec, ) -> Result { args.assert_is_empty::()?; let attr_list = func.attributes(db); let test_or_executable_attrs = if let Some(test_attr) = attr_list.find_attr(db, TestCollector::ATTR_NAME) { vec![test_attr] } else { attr_list .query_attr(db, InternalConfigStatementCollector::ATTR_NAME) .collect::>() }; let actual_body_fn_attrs = attr_list .elements(db) .filter(|attr| !test_or_executable_attrs.contains(attr)) .map(|stmt| stmt.to_token_stream(db)) .fold(TokenStream::empty(), |mut acc, token| { acc.extend(token); acc }); let test_or_executable_attrs = test_or_executable_attrs .iter() .map(|stmt| stmt.to_token_stream(db)) .fold(TokenStream::empty(), |mut acc, token| { acc.extend(token); acc }); let vis = func.visibility(db).as_syntax_node(); let vis = SyntaxNodeWithDb::new(&vis, db); let name = format_ident!( "{}__snforge_internal_fuzzer_generated", func.declaration(db).name(db).text(db) ); let signature = func.declaration(db).signature(db).as_syntax_node(); let signature = SyntaxNodeWithDb::new(&signature, db); let fuzzer_assignments = extract_and_transform_params(db, func, |param| { let name = param.name(db).as_syntax_node(); let name = SyntaxNodeWithDb::new(&name, db); let name_type = match param.type_clause(db) { TypeClause(type_clause) => type_clause.ty(db).as_syntax_node(), Empty(option_type_clause) => option_type_clause.as_syntax_node(), }; let name_type = SyntaxNodeWithDb::new(&name_type, db); quote! { let #name = snforge_std::fuzzable::Fuzzable::<#name_type>::generate(); snforge_std::_internals::save_fuzzer_arg(@#name); } }); let blank_values_for_config_run = extract_and_transform_params(db, func, |_param| { quote! { snforge_std::fuzzable::Fuzzable::blank(), } }); let arguments_list = extract_and_transform_params(db, func, |param| { let name = param.name(db).as_syntax_node(); let name = SyntaxNodeWithDb::new(&name, db); quote! { #name, } }); let func_name = func.declaration(db).name(db).as_syntax_node(); let func_name = SyntaxNodeWithDb::new(&func_name, db); let (statements, if_content) = get_statements(db, func); let internal_config_attr = create_single_token(InternalConfigStatementCollector::ATTR_NAME); Ok(quote!( #test_or_executable_attrs #vis fn #name() { if snforge_std::_internals::is_config_run() { #if_content #func_name(#blank_values_for_config_run); return; } #fuzzer_assignments #func_name(#arguments_list); } #actual_body_fn_attrs #[#internal_config_attr] fn #func_name #signature { #statements } )) } fn extract_and_transform_params( db: &SimpleParserDatabase, func: &FunctionWithBody, transformer: F, ) -> TokenStream where F: Fn(&Param) -> TokenStream, { func.declaration(db) .signature(db) .parameters(db) .elements(db) .map(|e| transformer(&e)) .fold(TokenStream::empty(), |mut acc, token| { acc.extend(token); acc }) } ================================================ FILE: crates/snforge-scarb-plugin/src/attributes/fuzzer.rs ================================================ use super::{AttributeCollector, AttributeInfo, AttributeTypeData, ErrorExt}; use crate::args::Arguments; use crate::asserts::assert_is_used_once; use crate::attributes::fuzzer::wrapper::FuzzerWrapperCollector; use crate::cairo_expression::CairoExpression; use crate::common::into_proc_macro_result; use crate::config_statement::extend_with_config_cheatcodes; use crate::parse::parse; use crate::types::{Number, ParseFromExpr}; use crate::utils::create_single_token; use cairo_lang_macro::{Diagnostic, Diagnostics, ProcMacroResult, TokenStream, quote}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::TypedSyntaxNode; use cairo_lang_syntax::node::with_db::SyntaxNodeWithDb; use cairo_lang_utils::Upcast; use num_bigint::BigInt; pub mod wrapper; pub struct FuzzerConfigCollector; impl AttributeInfo for FuzzerConfigCollector { const ATTR_NAME: &'static str = "__fuzzer_config"; } pub struct FuzzerCollector; impl AttributeInfo for FuzzerCollector { const ATTR_NAME: &'static str = "fuzzer"; } impl AttributeTypeData for FuzzerCollector { const CHEATCODE_NAME: &'static str = "set_config_fuzzer"; } impl AttributeCollector for FuzzerCollector { fn args_into_config_expression( db: &SimpleParserDatabase, args: Arguments, _warns: &mut Vec, ) -> Result { let named_args = args.named_only::()?; named_args.allow_only::(&["seed", "runs"])?; let seed = named_args .as_once_optional("seed")? .map(|arg| Number::parse_from_expr::(db, arg, "seed")) .transpose()?; let runs = named_args .as_once_optional("runs")? .map(|arg| Number::parse_from_expr::(db, arg, "runs")) .transpose()?; if let Some(Number(ref runs)) = runs { if runs <= &BigInt::from(0) { Err(Self::error("runs must be greater than 0"))?; } } let seed = seed.as_cairo_expression(); let runs = runs.as_cairo_expression(); Ok(quote!( snforge_std::_internals::config_types::FuzzerConfig { seed: #seed, runs: #runs } )) } } #[must_use] pub fn fuzzer(args: TokenStream, item: TokenStream) -> ProcMacroResult { into_proc_macro_result(args, item, fuzzer_internal) } #[must_use] pub fn fuzzer_config(args: TokenStream, item: TokenStream) -> ProcMacroResult { extend_with_config_cheatcodes::(args, item) } fn fuzzer_internal( args: &TokenStream, item: &TokenStream, _warns: &mut Vec, ) -> Result { let (db, func) = parse::(item)?; let db = db.upcast(); assert_is_used_once::(db, &func)?; let attrs = func.attributes(db).as_syntax_node(); let attrs = SyntaxNodeWithDb::new(&attrs, db); let body = func.body(db).as_syntax_node(); let body = SyntaxNodeWithDb::new(&body, db); let declaration = func.declaration(db).as_syntax_node(); let declaration = SyntaxNodeWithDb::new(&declaration, db); let fuzzer_config = create_single_token(FuzzerConfigCollector::ATTR_NAME); let fuzzer_wrapper = create_single_token(FuzzerWrapperCollector::ATTR_NAME); let args = args.clone(); Ok(quote!( #[#fuzzer_config #args] #[#fuzzer_wrapper] #attrs #declaration #body )) } ================================================ FILE: crates/snforge-scarb-plugin/src/attributes/ignore.rs ================================================ use super::{AttributeInfo, AttributeTypeData}; use crate::{ args::Arguments, attributes::AttributeCollector, config_statement::extend_with_config_cheatcodes, }; use cairo_lang_macro::{Diagnostic, Diagnostics, ProcMacroResult, TokenStream, quote}; use cairo_lang_parser::utils::SimpleParserDatabase; pub struct IgnoreCollector; impl AttributeInfo for IgnoreCollector { const ATTR_NAME: &'static str = "ignore"; } impl AttributeTypeData for IgnoreCollector { const CHEATCODE_NAME: &'static str = "set_config_ignore"; } impl AttributeCollector for IgnoreCollector { fn args_into_config_expression( _db: &SimpleParserDatabase, args: Arguments, _warns: &mut Vec, ) -> Result { args.assert_is_empty::()?; Ok(quote!( snforge_std::_internals::config_types::IgnoreConfig { is_ignored: true } )) } } #[must_use] pub fn ignore(args: TokenStream, item: TokenStream) -> ProcMacroResult { extend_with_config_cheatcodes::(args, item) } ================================================ FILE: crates/snforge-scarb-plugin/src/attributes/internal_config_statement.rs ================================================ use super::AttributeInfo; use crate::{ args::Arguments, asserts::assert_is_used_once, common::{into_proc_macro_result, with_parsed_values}, config_statement::append_config_statements, }; use cairo_lang_macro::{Diagnostic, Diagnostics, ProcMacroResult, TokenStream}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::ast::FunctionWithBody; pub struct InternalConfigStatementCollector; impl AttributeInfo for InternalConfigStatementCollector { const ATTR_NAME: &'static str = "__internal_config_statement"; } #[must_use] pub fn internal_config_statement(args: TokenStream, item: TokenStream) -> ProcMacroResult { into_proc_macro_result(args, item, |args, item, warns| { with_parsed_values::( args, item, warns, internal_config_statement_internal, ) }) } // we need to insert empty config statement in case there was no config used // so function will be stopped in configuration mode run #[expect(clippy::needless_pass_by_value)] fn internal_config_statement_internal( db: &SimpleParserDatabase, func: &FunctionWithBody, _args_db: &SimpleParserDatabase, args: Arguments, _warns: &mut Vec, ) -> Result { assert_is_used_once::(db, func)?; args.assert_is_empty::()?; Ok(append_config_statements(db, func, TokenStream::empty())) } ================================================ FILE: crates/snforge-scarb-plugin/src/attributes/should_panic/expected.rs ================================================ use super::ShouldPanicCollector; use crate::{ attributes::{AttributeInfo, ErrorExt}, cairo_expression::CairoExpression, types::{Felt, ParseFromExpr}, }; use cairo_lang_macro::{Diagnostic, TokenStream, quote}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::{Terminal, ast::Expr}; #[derive(Debug, Clone, Default)] pub enum Expected { Felt(Felt), ByteArray(String), Array(Vec), #[default] Any, } impl CairoExpression for Expected { fn as_cairo_expression(&self) -> TokenStream { match self { Self::Felt(felt) => { let string = felt.as_cairo_expression(); quote!(snforge_std::_internals::config_types::Expected::ShortString(#string)) } Self::ByteArray(string) => { let string = string.as_cairo_expression(); quote!(snforge_std::_internals::config_types::Expected::ByteArray(#string)) } Self::Array(strings) => { let arr = strings.as_cairo_expression(); quote!(snforge_std::_internals::config_types::Expected::Array(#arr)) } Self::Any => quote!(snforge_std::_internals::config_types::Expected::Any), } } } impl ParseFromExpr for Expected { fn parse_from_expr( db: &SimpleParserDatabase, expr: &Expr, arg_name: &str, ) -> Result { let error_msg = format!( "<{arg_name}> argument must be string, short string, number or list of short strings or numbers in regular brackets ()" ); match expr { Expr::ShortString(_) | Expr::Literal(_) => { Ok(Self::Felt( Felt::parse_from_expr::(db, expr, arg_name) // this unwrap is safe because we checked if expression is valid short string or number .unwrap(), )) } Expr::String(string) => { let string = string.text(db).trim_matches('"').to_string(); Ok(Self::ByteArray(string)) } Expr::Tuple(expressions) => { let elements = expressions .expressions(db) .elements(db) .map(|expr| Felt::parse_from_expr::(db, &expr, arg_name)) .collect::, Diagnostic>>() .map_err(|_| ShouldPanicCollector::error(error_msg))?; Ok(Self::Array(elements)) } _ => Err(ShouldPanicCollector::error(error_msg)), } } } ================================================ FILE: crates/snforge-scarb-plugin/src/attributes/should_panic.rs ================================================ use self::expected::Expected; use crate::{ args::Arguments, attributes::{AttributeCollector, AttributeInfo, AttributeTypeData}, cairo_expression::CairoExpression, config_statement::extend_with_config_cheatcodes, types::ParseFromExpr, }; use cairo_lang_macro::{Diagnostic, Diagnostics, ProcMacroResult, TokenStream, quote}; use cairo_lang_parser::utils::SimpleParserDatabase; mod expected; pub struct ShouldPanicCollector; impl AttributeInfo for ShouldPanicCollector { const ATTR_NAME: &'static str = "should_panic"; } impl AttributeTypeData for ShouldPanicCollector { const CHEATCODE_NAME: &'static str = "set_config_should_panic"; } impl AttributeCollector for ShouldPanicCollector { fn args_into_config_expression( db: &SimpleParserDatabase, args: Arguments, _warns: &mut Vec, ) -> Result { let named_args = args.named_only::()?; named_args.allow_only::(&["expected"])?; let expected = named_args.as_once_optional("expected")?; let expected = expected .map(|expr| Expected::parse_from_expr::(db, expr, "expected")) .transpose()? .unwrap_or_default(); let expected = expected.as_cairo_expression(); Ok(quote!( snforge_std::_internals::config_types::ShouldPanicConfig { expected: #expected } )) } } #[must_use] pub fn should_panic(args: TokenStream, item: TokenStream) -> ProcMacroResult { extend_with_config_cheatcodes::(args, item) } ================================================ FILE: crates/snforge-scarb-plugin/src/attributes/test.rs ================================================ use super::{AttributeInfo, ErrorExt, internal_config_statement::InternalConfigStatementCollector}; use crate::asserts::assert_is_used_once; use crate::common::{has_fuzzer_attribute, has_test_case_attribute}; use crate::external_inputs::ExternalInput; use crate::utils::{create_single_token, get_statements}; use crate::{ args::Arguments, common::{into_proc_macro_result, with_parsed_values}, format_ident, }; use cairo_lang_macro::{Diagnostic, Diagnostics, ProcMacroResult, TokenStream, quote}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::with_db::SyntaxNodeWithDb; use cairo_lang_syntax::node::{Terminal, TypedSyntaxNode, ast::FunctionWithBody}; pub struct TestCollector; impl AttributeInfo for TestCollector { const ATTR_NAME: &'static str = "test"; } #[must_use] pub fn test(args: TokenStream, item: TokenStream) -> ProcMacroResult { into_proc_macro_result(args, item, |args, item, warns| { with_parsed_values::(args, item, warns, test_internal) }) } #[expect(clippy::needless_pass_by_value)] fn test_internal( db: &SimpleParserDatabase, func: &FunctionWithBody, _args_db: &SimpleParserDatabase, args: Arguments, _warns: &mut Vec, ) -> Result { assert_is_used_once::(db, func)?; args.assert_is_empty::()?; ensure_parameters_only_with_fuzzer_or_test_case_attribute(db, func)?; let has_test_case = has_test_case_attribute(db, func); let has_fuzzer = has_fuzzer_attribute(db, func); // If the function has `#[test_case]` attribute and does not have `#[fuzzer]`, we can // safely skip code generation from `#[test]`. It will be handled later by `#[test_case]`. if has_test_case && !has_fuzzer { let func_item = func.as_syntax_node(); let func_item = SyntaxNodeWithDb::new(&func_item, db); return Ok(quote!( #func_item )); } let internal_config = create_single_token(InternalConfigStatementCollector::ATTR_NAME); let func_item = func.as_syntax_node(); let func_item = SyntaxNodeWithDb::new(&func_item, db); let name = func.declaration(db).name(db).text(db).to_string(); let test_filter = ExternalInput::get().forge_test_filter; let should_run_test = match test_filter { Some(ref filter) => name.contains(filter), None => true, }; let has_fuzzer = has_fuzzer_attribute(db, func); // If there is `#[fuzzer]` attribute, called function is suffixed with `__snforge_internal_fuzzer_generated` // `#[__fuzzer_wrapper]` is responsible for adding this suffix. let called_func_ident = if has_fuzzer { format_ident!("{name}__snforge_internal_fuzzer_generated") } else { format_ident!("{name}") }; let called_func = TokenStream::new(vec![called_func_ident]); let signature = func.declaration(db).signature(db).as_syntax_node(); let signature = SyntaxNodeWithDb::new(&signature, db); let signature = quote! { #signature }; let attributes = func.attributes(db).as_syntax_node(); let attributes = SyntaxNodeWithDb::new(&attributes, db); let test_func = TokenStream::new(vec![format_ident!( "{}__snforge_internal_test_generated", name )]); let func_name_node = func.declaration(db).name(db).as_syntax_node(); let func_name_ident = SyntaxNodeWithDb::new(&func_name_node, db); if should_run_test { let call_args = TokenStream::empty(); let test_func_with_attrs = test_func_with_attrs(&test_func, &called_func, &call_args); let (statements, if_content) = get_statements(db, func); Ok(quote!( #test_func_with_attrs #attributes fn #func_name_ident #signature { if snforge_std::_internals::is_config_run() { #if_content return; } #statements } )) } else { Ok(quote!( #[#internal_config] #func_item )) } } fn ensure_parameters_only_with_fuzzer_or_test_case_attribute( db: &SimpleParserDatabase, func: &FunctionWithBody, ) -> Result<(), Diagnostic> { if has_parameters(db, func) && !has_fuzzer_attribute(db, func) && !has_test_case_attribute(db, func) { Err(TestCollector::error( "function with parameters must have #[fuzzer] or #[test_case] attribute", ))?; } Ok(()) } fn has_parameters(db: &SimpleParserDatabase, func: &FunctionWithBody) -> bool { func.declaration(db) .signature(db) .parameters(db) .elements(db) .len() != 0 } #[must_use] pub fn test_func_with_attrs( test_fn_name: &TokenStream, fn_name: &TokenStream, call_args: &TokenStream, ) -> TokenStream { let test_fn_name = test_fn_name.clone(); let fn_name = fn_name.clone(); let call_args = call_args.clone(); let out_of_gas = create_single_token("'Out of gas'"); quote!( #[implicit_precedence(core::pedersen::Pedersen, core::RangeCheck, core::integer::Bitwise, core::ec::EcOp, core::poseidon::Poseidon, core::SegmentArena, core::circuit::RangeCheck96, core::circuit::AddMod, core::circuit::MulMod, core::gas::GasBuiltin, System)] #[snforge_internal_test_executable] fn #test_fn_name(mut _data: Span) -> Span:: { core::internal::require_implicit::(); core::internal::revoke_ap_tracking(); core::option::OptionTraitImpl::expect(core::gas::withdraw_gas(), #out_of_gas); core::option::OptionTraitImpl::expect( core::gas::withdraw_gas_all(core::gas::get_builtin_costs()), #out_of_gas ); #fn_name (#call_args); let mut arr = ArrayTrait::new(); core::array::ArrayTrait::span(@arr) } ) } ================================================ FILE: crates/snforge-scarb-plugin/src/attributes/test_case/name.rs ================================================ use crate::args::unnamed::UnnamedArgs; use crate::attributes::ErrorExt; use crate::attributes::test_case::TestCaseCollector; use cairo_lang_macro::Diagnostics; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::TypedSyntaxNode; use cairo_lang_syntax::node::ast::Expr; use regex::Regex; use std::sync::LazyLock; static RE_SANITIZE: LazyLock = LazyLock::new(|| Regex::new(r"[^a-zA-Z0-9]+").expect("Failed to create regex")); fn sanitize_expr(expr: &Expr, db: &SimpleParserDatabase) -> String { let expr_text = &expr.as_syntax_node().get_text(db); let expr_sanitized = RE_SANITIZE .replace_all(expr_text, "_") .to_lowercase() .trim_matches('_') .to_string(); if expr_sanitized.is_empty() { "_empty".into() } else { expr_sanitized } } fn generate_case_suffix(unnamed_args: &UnnamedArgs, db: &SimpleParserDatabase) -> String { if unnamed_args.is_empty() { unreachable!("Arguments cannot be empty for case name generation"); } let exprs = unnamed_args .iter() .map(|(_, expr)| sanitize_expr(expr, db)) .collect::>(); exprs.join("_") } pub fn test_case_name( func_name: &str, name_arg: Option<&Expr>, unnamed_args: &UnnamedArgs, db: &SimpleParserDatabase, ) -> Result { let suffix = if let Some(expr) = name_arg { match expr { Expr::ShortString(_) | Expr::String(_) => {} _ => { return Err(Diagnostics::from(TestCaseCollector::error( "Only string literals are allowed for 'name' argument.", ))); } } sanitize_expr(expr, db) } else { generate_case_suffix(unnamed_args, db) }; Ok(format!("{func_name}_{suffix}")) } ================================================ FILE: crates/snforge-scarb-plugin/src/attributes/test_case.rs ================================================ use crate::args::Arguments; use crate::args::unnamed::UnnamedArgs; use crate::attributes::internal_config_statement::InternalConfigStatementCollector; use crate::attributes::test::{TestCollector, test_func_with_attrs}; use crate::attributes::test_case::name::test_case_name; use crate::attributes::{AttributeInfo, ErrorExt}; use crate::common::{ has_fuzzer_attribute, has_test_attribute, into_proc_macro_result, with_parsed_values, }; use crate::utils::SyntaxNodeUtils; use crate::{create_single_token, format_ident}; use cairo_lang_macro::{Diagnostic, Diagnostics, ProcMacroResult, TokenStream, quote}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::ast::FunctionWithBody; use cairo_lang_syntax::node::with_db::SyntaxNodeWithDb; use cairo_lang_syntax::node::{Terminal, TypedSyntaxNode}; mod name; pub struct TestCaseCollector; impl AttributeInfo for TestCaseCollector { const ATTR_NAME: &'static str = "test_case"; } #[must_use] pub fn test_case(args: TokenStream, item: TokenStream) -> ProcMacroResult { into_proc_macro_result(args, item, |args, item, warns| { with_parsed_values::(args, item, warns, test_case_internal) }) } #[expect(clippy::needless_pass_by_value)] fn test_case_internal( db: &SimpleParserDatabase, func: &FunctionWithBody, args_db: &SimpleParserDatabase, args: Arguments, _warns: &mut Vec, ) -> Result { let named_args = args.named(); named_args.allow_only::(&["name"])?; let unnamed_args = args.unnamed(); ensure_params_valid(func, &args.unnamed(), db)?; let func_name = func.declaration(db).name(db); let name_arg = named_args.as_once_optional("name")?; let case_fn_name = test_case_name(&func_name.text(db), name_arg, &unnamed_args, args_db)?; let filtered_fn_attrs = collect_preserved_attributes_for_test_case(func, db); let signature = func.declaration(db).signature(db).as_syntax_node(); let signature = SyntaxNodeWithDb::new(&signature, db); let func_body = func.body(db).as_syntax_node(); let func_body = SyntaxNodeWithDb::new(&func_body, db); let case_fn_name = format_ident!("{}", case_fn_name); let case_fn_name = TokenStream::new(vec![case_fn_name]); let func_name = func_name.to_token_stream(db); let call_args = args_to_token_stream(&unnamed_args, args_db); let test_func_with_attrs = test_func_with_attrs(&case_fn_name, &func_name, &call_args); // If the function has both `#[fuzzer]` and `#[test]` attributes, we do not need to add // `#[__internal_config_statement]` attribute, since it will be handled by `#[test]`. let skip_internal_config = has_fuzzer_attribute(db, func) && has_test_attribute(db, func); let internal_config_attr = if skip_internal_config { TokenStream::empty() } else { let internal_config = create_single_token(InternalConfigStatementCollector::ATTR_NAME); quote!(#[#internal_config]) }; let func_ident = quote!( #filtered_fn_attrs #internal_config_attr fn #func_name #signature #func_body ); Ok(quote!( #test_func_with_attrs #func_ident )) } fn args_to_token_stream(args: &UnnamedArgs, db: &SimpleParserDatabase) -> TokenStream { args.iter() .map(|(_, expr)| { let expr = expr.as_syntax_node(); let expr = SyntaxNodeWithDb::new(&expr, db); quote! { #expr, } }) .fold(TokenStream::empty(), |mut acc, token| { acc.extend(token); acc }) } fn ensure_params_valid( func: &FunctionWithBody, unnamed_args: &UnnamedArgs, db: &SimpleParserDatabase, ) -> Result<(), Diagnostics> { let param_count = func .declaration(db) .signature(db) .parameters(db) .elements(db) .len(); if param_count == 0 { return Err(Diagnostics::from(TestCaseCollector::error( "The function must have at least one parameter to use #[test_case] attribute", ))); } if param_count != unnamed_args.len() { return Err(Diagnostics::from(TestCaseCollector::error(format!( "Expected {} arguments, but got {}", param_count, unnamed_args.len() )))); } Ok(()) } fn collect_preserved_attributes_for_test_case( func: &FunctionWithBody, func_db: &SimpleParserDatabase, ) -> TokenStream { let attr_list = func.attributes(func_db); let has_fuzzer = has_fuzzer_attribute(func_db, func); // We do not want to copy the `#[test]` attribute unless the function has `#[fuzzer]`. // We also do not want to copy `#[__internal_config_statement]` because it will be added later. attr_list .elements(func_db) .filter(|attr| { let test_attr_text = format!("#[{}]", TestCollector::ATTR_NAME); let internal_config_attr_text = format!("#[{}]", InternalConfigStatementCollector::ATTR_NAME); let attr_text = attr.as_syntax_node().get_text(func_db); let attr_text = attr_text.trim(); let is_test_attr = attr_text == test_attr_text; let is_internal_config_attr = attr_text == internal_config_attr_text; (!is_test_attr || has_fuzzer) && !is_internal_config_attr }) .map(|attr| attr.to_token_stream(func_db)) .fold(TokenStream::empty(), |mut acc, token| { acc.extend(token); acc }) } ================================================ FILE: crates/snforge-scarb-plugin/src/attributes.rs ================================================ use crate::args::Arguments; use cairo_lang_macro::{Diagnostic, Diagnostics, TokenStream}; use cairo_lang_parser::utils::SimpleParserDatabase; pub mod available_gas; pub mod disable_predeployed_contracts; pub mod fork; pub mod fuzzer; pub mod ignore; pub mod internal_config_statement; pub mod should_panic; pub mod test; pub mod test_case; pub trait AttributeInfo { const ATTR_NAME: &'static str; } pub trait AttributeTypeData { const CHEATCODE_NAME: &'static str; } pub trait AttributeCollector: AttributeInfo + AttributeTypeData { fn args_into_config_expression( db: &SimpleParserDatabase, args: Arguments, warns: &mut Vec, ) -> Result; } pub trait ErrorExt { fn error(message: impl ToString) -> Diagnostic; fn warn(message: impl ToString) -> Diagnostic; } impl ErrorExt for T where T: AttributeInfo, { fn error(message: impl ToString) -> Diagnostic { let message = message.to_string(); let attr_name = Self::ATTR_NAME; Diagnostic::error(format!("#[{attr_name}] {message}")) } fn warn(message: impl ToString) -> Diagnostic { let message = message.to_string(); let attr_name = Self::ATTR_NAME; Diagnostic::warn(format!("#[{attr_name}] {message}")) } } ================================================ FILE: crates/snforge-scarb-plugin/src/cairo_expression.rs ================================================ use cairo_lang_macro::{TokenStream, quote}; pub trait CairoExpression { fn as_cairo_expression(&self) -> TokenStream; } impl CairoExpression for Option where T: CairoExpression, { fn as_cairo_expression(&self) -> TokenStream { if let Some(v) = self { let v = v.as_cairo_expression(); quote!(Option::Some( #v )) } else { quote!(Option::None) } } } impl CairoExpression for Vec where T: CairoExpression, { fn as_cairo_expression(&self) -> TokenStream { let items = self.iter().fold(TokenStream::empty(), |mut acc, val| { let val = val.as_cairo_expression(); acc.extend(quote! { #val, }); acc }); quote! { array![#items] } } } ================================================ FILE: crates/snforge-scarb-plugin/src/common.rs ================================================ use crate::{ args::Arguments, attributes::{ AttributeInfo, fuzzer::{FuzzerCollector, FuzzerConfigCollector, wrapper::FuzzerWrapperCollector}, test::TestCollector, test_case::TestCaseCollector, }, parse::{parse, parse_args}, }; use cairo_lang_macro::{Diagnostic, Diagnostics, ProcMacroResult, TokenStream}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::{TypedSyntaxNode, ast::FunctionWithBody}; use cairo_lang_utils::Upcast; #[expect(clippy::needless_pass_by_value)] pub fn into_proc_macro_result( args: TokenStream, item: TokenStream, handler: impl Fn( &TokenStream, &TokenStream, &mut Vec, ) -> Result, ) -> ProcMacroResult { let mut warns = vec![]; // `Vec` instead of `Diagnostics` because `Diagnostics` does not allow to push ready `Diagnostic` match handler(&args, &item, &mut warns) { Ok(item) => ProcMacroResult::new(item).with_diagnostics(warns.into()), Err(mut diagnostics) => { diagnostics.extend(warns); ProcMacroResult::new(item).with_diagnostics(diagnostics) } } } pub fn with_parsed_values( args: &TokenStream, item: &TokenStream, warns: &mut Vec, handler: impl Fn( //func item &SimpleParserDatabase, &FunctionWithBody, //args &SimpleParserDatabase, Arguments, //warns &mut Vec, ) -> Result, ) -> Result where Collector: AttributeInfo, { let (db, func) = parse::(item)?; let db = db.upcast(); let (args_db, args) = parse_args(args); let args_db = args_db.upcast(); let args = Arguments::new::(args_db, args, warns); handler(db, &func, args_db, args, warns) } fn has_any_attribute( db: &SimpleParserDatabase, func: &FunctionWithBody, attr_names: &[&str], ) -> bool { func.attributes(db).elements(db).any(|attr| { attr_names.contains( &attr .attr(db) .as_syntax_node() .get_text_without_trivia(db) .as_str(), ) }) } pub fn has_fuzzer_attribute(db: &SimpleParserDatabase, func: &FunctionWithBody) -> bool { const FUZZER_ATTRIBUTES: [&str; 3] = [ FuzzerCollector::ATTR_NAME, FuzzerWrapperCollector::ATTR_NAME, FuzzerConfigCollector::ATTR_NAME, ]; has_any_attribute(db, func, &FUZZER_ATTRIBUTES) } pub fn has_test_case_attribute(db: &SimpleParserDatabase, func: &FunctionWithBody) -> bool { const TEST_CASE_ATTRIBUTES: [&str; 1] = [TestCaseCollector::ATTR_NAME]; has_any_attribute(db, func, &TEST_CASE_ATTRIBUTES) } pub fn has_test_attribute(db: &SimpleParserDatabase, func: &FunctionWithBody) -> bool { const TEST_ATTRIBUTES: [&str; 1] = [TestCollector::ATTR_NAME]; has_any_attribute(db, func, &TEST_ATTRIBUTES) } ================================================ FILE: crates/snforge-scarb-plugin/src/config_statement.rs ================================================ use crate::asserts::assert_is_used_once; use crate::utils::{create_single_token, get_statements}; use crate::{ args::Arguments, attributes::AttributeCollector, common::{into_proc_macro_result, with_parsed_values}, }; use cairo_lang_macro::{Diagnostic, Diagnostics, ProcMacroResult, TokenStream, quote}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::TypedSyntaxNode; use cairo_lang_syntax::node::ast::FunctionWithBody; use cairo_lang_syntax::node::with_db::SyntaxNodeWithDb; pub fn extend_with_config_cheatcodes( args: TokenStream, item: TokenStream, ) -> ProcMacroResult where Collector: AttributeCollector, { into_proc_macro_result(args, item, |args, item, warns| { with_parsed_values::(args, item, warns, with_config_cheatcodes::) }) } fn with_config_cheatcodes( db: &SimpleParserDatabase, func: &FunctionWithBody, args_db: &SimpleParserDatabase, args: Arguments, warns: &mut Vec, ) -> Result where Collector: AttributeCollector, { assert_is_used_once::(db, func)?; let value = Collector::args_into_config_expression(args_db, args, warns)?; let cheatcode_name = Collector::CHEATCODE_NAME; let cheatcode = create_single_token(format!("'{cheatcode_name}'")); let cheatcode = quote! { starknet::testing::cheatcode::<#cheatcode>(data.span()); }; let config_cheatcode = quote!( let mut data = array![]; #value .serialize(ref data); #cheatcode ); Ok(append_config_statements(db, func, config_cheatcode)) } #[expect(clippy::needless_pass_by_value)] pub fn append_config_statements( db: &SimpleParserDatabase, func: &FunctionWithBody, config_statements: TokenStream, ) -> TokenStream { let vis = func.visibility(db).as_syntax_node(); let vis = SyntaxNodeWithDb::new(&vis, db); let attrs = func.attributes(db).as_syntax_node(); let attrs = SyntaxNodeWithDb::new(&attrs, db); let declaration = func.declaration(db).as_syntax_node(); let declaration = SyntaxNodeWithDb::new(&declaration, db); let (statements, if_content) = get_statements(db, func); quote!( #attrs #vis #declaration { if snforge_std::_internals::is_config_run() { #if_content #config_statements return; } #statements } ) } ================================================ FILE: crates/snforge-scarb-plugin/src/external_inputs.rs ================================================ use cairo_lang_macro::fingerprint; use std::env; use std::hash::{Hash, Hasher}; use xxhash_rust::xxh3::Xxh3; /// All external inputs that influence the compilation should be added here. #[derive(Hash)] pub struct ExternalInput { pub forge_test_filter: Option, } #[allow(clippy::disallowed_methods)] impl ExternalInput { pub fn get() -> Self { Self { forge_test_filter: env::var("SNFORGE_TEST_FILTER").ok(), } } } /// This function implements a callback that Scarb will use to determine /// whether Cairo code depending on this macro should be recompiled. /// The callback is concerned with informing Scarb about changes to inputs that don't come from Scarb directly, /// like the `SNFORGE_TEST_FILTER` environmental variable. /// /// Warning: Removing this callback can break incremental compilation with this macro! #[fingerprint] fn test_filter_fingerprint() -> u64 { // The hashes need to be consistent across different runs. // Thus, we cannot use the default hasher, which is rng-seeded. let mut hasher = Xxh3::default(); ExternalInput::get().hash(&mut hasher); hasher.finish() } ================================================ FILE: crates/snforge-scarb-plugin/src/lib.rs ================================================ // Disallows using methods that are not safed to be used. // See clippy.toml for the list of disallowed methods and reasoning behind them. #![deny(clippy::disallowed_methods)] use attributes::fuzzer; use attributes::{ available_gas::available_gas, disable_predeployed_contracts::disable_predeployed_contracts, fork::fork, fuzzer::fuzzer, ignore::ignore, internal_config_statement::internal_config_statement, should_panic::should_panic, test::test, test_case::test_case, }; use cairo_lang_macro::{ProcMacroResult, TokenStream, attribute_macro, executable_attribute}; mod args; mod asserts; pub mod attributes; mod cairo_expression; mod common; mod config_statement; mod external_inputs; mod parse; mod types; mod utils; pub use utils::create_single_token; executable_attribute!("snforge_internal_test_executable"); #[attribute_macro] fn __internal_config_statement(args: TokenStream, item: TokenStream) -> ProcMacroResult { internal_config_statement(args, item) } #[attribute_macro] fn __fuzzer_config(args: TokenStream, item: TokenStream) -> ProcMacroResult { fuzzer::fuzzer_config(args, item) } #[attribute_macro] fn __fuzzer_wrapper(args: TokenStream, item: TokenStream) -> ProcMacroResult { fuzzer::wrapper::fuzzer_wrapper(args, item) } #[attribute_macro] fn test_case(args: TokenStream, item: TokenStream) -> ProcMacroResult { test_case(args, item) } #[attribute_macro] fn test(args: TokenStream, item: TokenStream) -> ProcMacroResult { test(args, item) } #[attribute_macro] fn ignore(args: TokenStream, item: TokenStream) -> ProcMacroResult { ignore(args, item) } #[attribute_macro] fn fuzzer(args: TokenStream, item: TokenStream) -> ProcMacroResult { fuzzer(args, item) } #[attribute_macro] fn fork(args: TokenStream, item: TokenStream) -> ProcMacroResult { fork(args, item) } #[attribute_macro] fn available_gas(args: TokenStream, item: TokenStream) -> ProcMacroResult { available_gas(args, item) } #[attribute_macro] fn should_panic(args: TokenStream, item: TokenStream) -> ProcMacroResult { should_panic(args, item) } #[attribute_macro] fn disable_predeployed_contracts(args: TokenStream, item: TokenStream) -> ProcMacroResult { disable_predeployed_contracts(args, item) } ================================================ FILE: crates/snforge-scarb-plugin/src/parse.rs ================================================ use crate::attributes::{AttributeInfo, ErrorExt}; use crate::utils::create_single_token; use cairo_lang_macro::{Diagnostic, TokenStream, TokenTree, quote}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::ast::SyntaxFile; use cairo_lang_syntax::node::{ TypedSyntaxNode, ast::{FunctionWithBody, ModuleItem, OptionArgListParenthesized}, helpers::QueryAttrs, }; use cairo_lang_utils::Upcast; pub fn parse( code: &TokenStream, ) -> Result<(SimpleParserDatabase, FunctionWithBody), Diagnostic> { let simple_db = SimpleParserDatabase::default(); let (parsed_node, diagnostics) = simple_db.parse_token_stream(code); if !diagnostics.is_empty() { return Err(Diagnostic::error("Failed because of invalid syntax")); } let db = simple_db.upcast(); let function = SyntaxFile::from_syntax_node(db, parsed_node) .items(db) .elements(db) .find_map(|element| { if let ModuleItem::FreeFunction(func) = element { Some(func) } else { None } }); match function { Some(func) => Ok((simple_db, func)), None => Err(T::error("can be used only on a function")), } } struct InternalCollector; impl AttributeInfo for InternalCollector { const ATTR_NAME: &'static str = "__SNFORGE_INTERNAL_ATTR__"; } pub fn parse_args(args: &TokenStream) -> (SimpleParserDatabase, OptionArgListParenthesized) { let attr_name = create_single_token(InternalCollector::ATTR_NAME); let args = args.clone(); let mut token_stream = quote! { #[#attr_name #args] fn __SNFORGE_INTERNAL_FN__(){{}} }; if !token_stream.is_empty() { match &mut token_stream.tokens[0] { TokenTree::Ident(ident) => { ident.span.start = 0; } } } let (simple_db, func) = parse::(&token_stream) .expect("Parsing the arguments shouldn't fail at this stage"); // Arguments were parsed previously, so they should pass parsing here let db = simple_db.upcast(); let args = func .attributes(db) .find_attr(db, InternalCollector::ATTR_NAME) .unwrap() .arguments(db); (simple_db, args) } ================================================ FILE: crates/snforge-scarb-plugin/src/types.rs ================================================ use crate::utils::create_single_token; use crate::{ attributes::{AttributeInfo, ErrorExt}, cairo_expression::CairoExpression, }; use cairo_lang_macro::{Diagnostic, TokenStream}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::{Terminal, ast::Expr}; use num_bigint::BigInt; use url::Url; pub trait ParseFromExpr: Sized { fn parse_from_expr( db: &SimpleParserDatabase, expr: &E, arg_name: &str, ) -> Result; } #[derive(Debug, Clone)] pub enum Felt { Number(Number), ShortString(ShortString), } impl CairoExpression for Felt { fn as_cairo_expression(&self) -> TokenStream { match self { Self::Number(number) => number.as_cairo_expression(), Self::ShortString(string) => string.as_cairo_expression(), } } } impl ParseFromExpr for Felt { fn parse_from_expr( db: &SimpleParserDatabase, expr: &Expr, arg_name: &str, ) -> Result { match expr { Expr::ShortString(string) => { let string = string.text(db).trim_matches('\'').to_string(); Ok(Self::ShortString(ShortString(string))) } Expr::Literal(string) => { let num = string.numeric_value(db).unwrap(); Ok(Self::Number(Number(num))) } _ => Err(T::error(format!("<{arg_name}> argument must be felt")))?, } } } #[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct Number(pub(crate) BigInt); impl Number { pub fn validate_in_gas_range( &self, arg_name: &str, ) -> Result<(), Diagnostic> { let max = u64::MAX; if *self > Number(max.into()) { return Err(T::error(format!( "{arg_name} it too large (max permissible value is {max})" ))); } Ok(()) } } #[derive(Debug, Clone)] pub struct ShortString(pub(crate) String); impl CairoExpression for Number { fn as_cairo_expression(&self) -> TokenStream { TokenStream::new(vec![create_single_token(format!( "0x{}", self.0.to_str_radix(16) ))]) } } impl CairoExpression for ShortString { fn as_cairo_expression(&self) -> TokenStream { TokenStream::new(vec![create_single_token(format!("'{}'", self.0))]) } } impl ParseFromExpr for Number { fn parse_from_expr( db: &SimpleParserDatabase, expr: &Expr, arg_name: &str, ) -> Result { match expr { Expr::Literal(literal) => { let num = literal .numeric_value(db) .ok_or_else(|| T::error(format!("<{arg_name}> got invalid number literal")))?; Ok(Self(num)) } _ => Err(T::error(format!("<{arg_name}> should be number literal"))), } } } impl ParseFromExpr for Url { fn parse_from_expr( db: &SimpleParserDatabase, expr: &Expr, arg_name: &str, ) -> Result { let url = String::parse_from_expr::(db, expr, arg_name)?; Url::parse(&url).map_err(|_| T::error(format!("<{arg_name}> is not a valid url"))) } } impl ParseFromExpr for String { fn parse_from_expr( db: &SimpleParserDatabase, expr: &Expr, arg_name: &str, ) -> Result { match expr { Expr::String(string) => Ok(string.text(db).trim_matches('"').to_string()), _ => Err(T::error(format!( "<{arg_name}> invalid type, should be: double quoted string" ))), } } } impl ParseFromExpr for ShortString { fn parse_from_expr( db: &SimpleParserDatabase, expr: &Expr, arg_name: &str, ) -> Result { match expr { Expr::ShortString(string) => { let string = string.text(db).trim_matches('\'').to_string(); Ok(ShortString(string)) } _ => Err(T::error(format!( "<{arg_name}> invalid type, should be: double quoted string" ))), } } } impl CairoExpression for String { fn as_cairo_expression(&self) -> TokenStream { TokenStream::new(vec![create_single_token(format!(r#""{self}""#))]) } } impl CairoExpression for Url { fn as_cairo_expression(&self) -> TokenStream { TokenStream::new(vec![create_single_token(format!(r#""{self}""#))]) } } ================================================ FILE: crates/snforge-scarb-plugin/src/utils.rs ================================================ use cairo_lang_macro::{Diagnostic, Severity, TextSpan, Token, TokenStream, TokenTree, quote}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::TypedSyntaxNode; use cairo_lang_syntax::node::ast::{Condition, Expr, FunctionWithBody, Statement}; use cairo_lang_syntax::node::helpers::GetIdentifier; use cairo_lang_syntax::node::with_db::SyntaxNodeWithDb; use indoc::formatdoc; pub fn higher_severity(a: Severity, b: Severity) -> Severity { match (a, b) { (Severity::Warning, Severity::Warning) => Severity::Warning, _ => Severity::Error, } } pub fn format_error_message(variants: &[Diagnostic]) -> String { let formatted_variants: Vec = variants .iter() .map(|variant| format!("- variant: {}", variant.message())) .collect(); formatdoc! {" All options failed {} Resolve at least one of them ", formatted_variants.join("\n")} } /// The `branch` macro is used to evaluate multiple expressions and return the first successful result. /// If all expressions fail, it collects the error messages and returns a combined error. /// /// This macro is used instead of a function because it can perform lazy evaluation and has better readability. #[macro_export] macro_rules! branch { ($($result:expr_2021),+) => {{ let mut messages = Vec::new(); let mut result = None; $( if result.is_none() { match $result { Ok(val) => { result = Some(val); }, Err(err) => { messages.push(err); }, } } )+ if let Some(result) = result { Ok(result) } else { let severity = messages.clone().into_iter().fold(Severity::Warning, |acc, diagnostic| $crate::utils::higher_severity(acc, diagnostic.severity())); let message = $crate::utils::format_error_message(&messages); Err(Diagnostic::new(severity, message)) } }}; } pub fn create_single_token(content: impl AsRef) -> TokenTree { TokenTree::Ident(Token::new(content, TextSpan::call_site())) } pub trait SyntaxNodeUtils { fn to_token_stream(&self, db: &SimpleParserDatabase) -> TokenStream; } impl SyntaxNodeUtils for T { fn to_token_stream(&self, db: &SimpleParserDatabase) -> TokenStream { let syntax = self.as_syntax_node(); let syntax = SyntaxNodeWithDb::new(&syntax, db); quote!(#syntax) } } // Gets test statements and content of `if` statement that checks if function is run in config mode pub fn get_statements( db: &SimpleParserDatabase, func: &FunctionWithBody, ) -> (TokenStream, TokenStream) { let statements = func .body(db) .statements(db) .elements(db) .collect::>(); let if_content = statements.first().and_then(|stmt| { // first statement is `if` let Statement::Expr(expr) = stmt else { return None; }; let Expr::If(if_expr) = expr.expr(db) else { return None; }; // its condition is function call let Some(Condition::Expr(expr)) = if_expr.conditions(db).elements(db).next() else { return None; }; let Expr::FunctionCall(expr) = expr.expr(db) else { return None; }; // this function is named "snforge_std::_internals::is_config_run" let segments: Vec<_> = expr.path(db).segments(db).elements(db).collect(); let [snforge_std, cheatcode, is_config_run] = segments.as_slice() else { return None; }; if snforge_std.identifier(db) != "snforge_std" || cheatcode.identifier(db) != "_internals" || is_config_run.identifier(db) != "is_config_run" { return None; } let statements: Vec<_> = if_expr.if_block(db).statements(db).elements(db).collect(); // omit last one (`return;`) as it have to be inserted after all new statements Some( statements[..statements.len() - 1] .iter() .map(|stmt| stmt.to_token_stream(db)) .fold(TokenStream::empty(), |mut acc, token| { acc.extend(token); acc }), ) }); // there was already config check, omit it and collect remaining statements let statements = if if_content.is_some() { &statements[1..] } else { &statements[..] } .iter() .map(|stmt| stmt.to_token_stream(db)) .fold(TokenStream::empty(), |mut acc, token| { acc.extend(token); acc }); (statements, if_content.unwrap_or_else(TokenStream::empty)) } #[macro_export] macro_rules! format_ident { ($name:literal $(,$formats:expr_2021),*) => { { use cairo_lang_macro::{TextSpan, Token, TokenTree}; let content = format!($name,$($formats),*); TokenTree::Ident(Token::new(content, TextSpan::call_site())) } }; } ================================================ FILE: crates/snforge-scarb-plugin/tests/integration/main.rs ================================================ mod multiple_attributes; mod single_attributes; mod utils; ================================================ FILE: crates/snforge-scarb-plugin/tests/integration/multiple_attributes.rs ================================================ use crate::utils::{assert_diagnostics, assert_output, empty_function}; use cairo_lang_macro::{TokenStream, TokenTree, quote}; use cairo_lang_parser::utils::SimpleParserDatabase; use cairo_lang_syntax::node::ast::{ModuleItem, SyntaxFile}; use cairo_lang_syntax::node::with_db::SyntaxNodeWithDb; use cairo_lang_syntax::node::{Terminal, TypedSyntaxNode}; use snforge_scarb_plugin::attributes::fuzzer::wrapper::fuzzer_wrapper; use snforge_scarb_plugin::attributes::fuzzer::{fuzzer, fuzzer_config}; use snforge_scarb_plugin::attributes::{available_gas::available_gas, fork::fork, test::test}; use snforge_scarb_plugin::create_single_token; fn get_function(token_stream: &TokenStream, function_name: &str, skip_args: bool) -> TokenStream { let db = SimpleParserDatabase::default(); let (parsed_node, _diagnostics) = db.parse_token_stream(token_stream); let syntax_file = SyntaxFile::from_syntax_node(&db, parsed_node); let function = syntax_file .items(&db) .elements(&db) .find_map(|e| { if let ModuleItem::FreeFunction(free_function) = e { if free_function.declaration(&db).name(&db).text(&db) == function_name { Some(free_function.clone()) } else { None } } else { None } }) .unwrap(); let vis = function.visibility(&db).as_syntax_node(); let vis = SyntaxNodeWithDb::new(&vis, &db); let signature = function.declaration(&db).as_syntax_node(); let signature = SyntaxNodeWithDb::new(&signature, &db); let body = function.body(&db).as_syntax_node(); let body = SyntaxNodeWithDb::new(&body, &db); let attrs = function.attributes(&db).as_syntax_node(); let attrs = SyntaxNodeWithDb::new(&attrs, &db); let mut token_stream = if skip_args { quote! { #vis #signature #body } } else { quote! { #attrs #vis #signature #body } }; match &mut token_stream.tokens[0] { TokenTree::Ident(ident) => { ident.span.start = 0; } } token_stream } #[test] fn works_with_few_attributes() { let args = TokenStream::empty(); let result = test(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, " #[implicit_precedence(core::pedersen::Pedersen, core::RangeCheck, core::integer::Bitwise, core::ec::EcOp, core::poseidon::Poseidon, core::SegmentArena, core::circuit::RangeCheck96, core::circuit::AddMod, core::circuit::MulMod, core::gas::GasBuiltin, System)] #[snforge_internal_test_executable] fn empty_fn__snforge_internal_test_generated(mut _data: Span) -> Span:: { core::internal::require_implicit::(); core::internal::revoke_ap_tracking(); core::option::OptionTraitImpl::expect(core::gas::withdraw_gas(), 'Out of gas'); core::option::OptionTraitImpl::expect( core::gas::withdraw_gas_all(core::gas::get_builtin_costs()), 'Out of gas', ); empty_fn(); let mut arr = ArrayTrait::new(); core::array::ArrayTrait::span(@arr) } fn empty_fn() { if snforge_std::_internals::is_config_run() { return; } } ", ); let item = get_function(&result.token_stream, "empty_fn", false); let args = quote!((l1_gas: 1, l1_data_gas: 2, l2_gas: 3)); let result = available_gas(args, item); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::AvailableResourceBoundsConfig { l1_gas: 0x1, l1_data_gas: 0x2, l2_gas: 0x3 } .serialize(ref data); starknet::testing::cheatcode::<'set_config_available_gas'>(data.span()); return; } } ", ); let item = result.token_stream; let args = quote!(("test")); let result = fork(args, item); assert_diagnostics(&result, &[]); assert_output( &result, r#" fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::AvailableResourceBoundsConfig { l1_gas: 0x1, l1_data_gas: 0x2, l2_gas: 0x3 } .serialize(ref data); starknet::testing::cheatcode::<'set_config_available_gas'>(data.span()); let mut data = array![]; snforge_std::_internals::config_types::ForkConfig::Named("test") .serialize(ref data); starknet::testing::cheatcode::<'set_config_fork'>(data.span()); return; } } "#, ); } #[test] fn works_with_fuzzer() { let args = TokenStream::empty(); let result = test(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, " #[implicit_precedence(core::pedersen::Pedersen, core::RangeCheck, core::integer::Bitwise, core::ec::EcOp, core::poseidon::Poseidon, core::SegmentArena, core::circuit::RangeCheck96, core::circuit::AddMod, core::circuit::MulMod, core::gas::GasBuiltin, System)] #[snforge_internal_test_executable] fn empty_fn__snforge_internal_test_generated(mut _data: Span) -> Span:: { core::internal::require_implicit::(); core::internal::revoke_ap_tracking(); core::option::OptionTraitImpl::expect(core::gas::withdraw_gas(), 'Out of gas'); core::option::OptionTraitImpl::expect( core::gas::withdraw_gas_all(core::gas::get_builtin_costs()), 'Out of gas', ); empty_fn(); let mut arr = ArrayTrait::new(); core::array::ArrayTrait::span(@arr) } fn empty_fn() { if snforge_std::_internals::is_config_run() { return; } } ", ); let item = get_function(&result.token_stream, "empty_fn", false); let args = quote!((runs: 123, seed: 321)); let result = fuzzer(args, item); assert_diagnostics(&result, &[]); assert_output( &result, r" #[__fuzzer_config(runs: 123, seed: 321)] #[__fuzzer_wrapper] fn empty_fn() { if snforge_std::_internals::is_config_run() { return; } } ", ); } #[test] fn works_with_fuzzer_before_test() { let item = quote!( fn empty_fn(f: felt252) {} ); let fuzzer_args = quote!((runs: 123, seed: 321)); let fuzzer_res = fuzzer(fuzzer_args, item); assert_diagnostics(&fuzzer_res, &[]); assert_output( &fuzzer_res, r" #[__fuzzer_config(runs: 123, seed: 321)] #[__fuzzer_wrapper] fn empty_fn(f: felt252) {} ", ); let test_args = TokenStream::empty(); let item = get_function(&fuzzer_res.token_stream, "empty_fn", false); let result = test(test_args, item); assert_diagnostics(&result, &[]); assert_output( &result, r" #[implicit_precedence(core::pedersen::Pedersen, core::RangeCheck, core::integer::Bitwise, core::ec::EcOp, core::poseidon::Poseidon, core::SegmentArena, core::circuit::RangeCheck96, core::circuit::AddMod, core::circuit::MulMod, core::gas::GasBuiltin, System)] #[snforge_internal_test_executable] fn empty_fn__snforge_internal_test_generated(mut _data: Span) -> Span:: { core::internal::require_implicit::(); core::internal::revoke_ap_tracking(); core::option::OptionTraitImpl::expect(core::gas::withdraw_gas(), 'Out of gas'); core::option::OptionTraitImpl::expect( core::gas::withdraw_gas_all(core::gas::get_builtin_costs()), 'Out of gas', ); empty_fn__snforge_internal_fuzzer_generated(); let mut arr = ArrayTrait::new(); core::array::ArrayTrait::span(@arr) } #[__fuzzer_config(runs: 123, seed: 321)] #[__fuzzer_wrapper] fn empty_fn(f: felt252) { if snforge_std::_internals::is_config_run() { return; } } ", ); // We need to remove `#[__fuzzer_wrapper]` to be able to call `fuzzer_wrapper()` again let item = get_function(&result.token_stream, "empty_fn", true); let item = quote!( #[implicit_precedence(core::pedersen::Pedersen, core::RangeCheck, core::integer::Bitwise, core::ec::EcOp, core::poseidon::Poseidon, core::SegmentArena, core::circuit::RangeCheck96, core::circuit::AddMod, core::circuit::MulMod, core::gas::GasBuiltin, System)] #[snforge_internal_test_executable] #item #[__fuzzer_config(runs: 123, seed: 321)] #[__internal_config_statement] fn empty_fn() {} ); let result = fuzzer_wrapper(TokenStream::empty(), item); assert_diagnostics(&result, &[]); assert_output( &result, r" fn empty_fn__snforge_internal_fuzzer_generated() { if snforge_std::_internals::is_config_run() { empty_fn(snforge_std::fuzzable::Fuzzable::blank()); return; } let f = snforge_std::fuzzable::Fuzzable::::generate(); snforge_std::_internals::save_fuzzer_arg(@f); empty_fn(f); } #[implicit_precedence(core::pedersen::Pedersen, core::RangeCheck, core::integer::Bitwise, core::ec::EcOp, core::poseidon::Poseidon, core::SegmentArena, core::circuit::RangeCheck96, core::circuit::AddMod, core::circuit::MulMod, core::gas::GasBuiltin, System)] #[snforge_internal_test_executable] #[__internal_config_statement] fn empty_fn(f: felt252) {} ", ); } #[test] #[expect(clippy::too_many_lines)] fn works_with_fuzzer_config_wrapper() { let item = quote!( fn empty_fn(f: felt252) {} ); let args = quote!((l2_gas: 999)); let result = available_gas(args, item); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn(f: felt252) { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::AvailableResourceBoundsConfig { l1_gas: 0xffffffffffffffff, l1_data_gas: 0xffffffffffffffff, l2_gas: 0x3e7 } .serialize(ref data); starknet::testing::cheatcode::<'set_config_available_gas'>(data.span()); return; } } ", ); // Append `#[fuzzer]` so we can use `test()` let mut item = TokenStream::new(vec![create_single_token("#[fuzzer]")]); item.extend(result.token_stream); let result = test(TokenStream::empty(), item); assert_diagnostics(&result, &[]); assert_output( &result, r" #[implicit_precedence(core::pedersen::Pedersen, core::RangeCheck, core::integer::Bitwise, core::ec::EcOp, core::poseidon::Poseidon, core::SegmentArena, core::circuit::RangeCheck96, core::circuit::AddMod, core::circuit::MulMod, core::gas::GasBuiltin, System)] #[snforge_internal_test_executable] fn empty_fn__snforge_internal_test_generated(mut _data: Span) -> Span:: { core::internal::require_implicit::(); core::internal::revoke_ap_tracking(); core::option::OptionTraitImpl::expect(core::gas::withdraw_gas(), 'Out of gas'); core::option::OptionTraitImpl::expect( core::gas::withdraw_gas_all(core::gas::get_builtin_costs()), 'Out of gas', ); empty_fn__snforge_internal_fuzzer_generated(); let mut arr = ArrayTrait::new(); core::array::ArrayTrait::span(@arr) } #[fuzzer] fn empty_fn(f: felt252) { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::AvailableResourceBoundsConfig { l1_gas: 0xffffffffffffffff, l1_data_gas: 0xffffffffffffffff, l2_gas: 0x3e7 } .serialize(ref data); starknet::testing::cheatcode::<'set_config_available_gas'>(data.span()); return; } } ", ); // Skip all the lines including `#[fuzzer]` that was appended previously let item = get_function(&result.token_stream, "empty_fn", true); let internal_config_statement = TokenStream::new(vec![create_single_token("__internal_config_statement")]); let item = quote! { #[#internal_config_statement] #item }; let args = quote!((runs: 123, seed: 321)); let result = fuzzer_config(args, item); assert_diagnostics(&result, &[]); assert_output( &result, r" #[__internal_config_statement] fn empty_fn(f: felt252) { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::AvailableResourceBoundsConfig { l1_gas: 0xffffffffffffffff, l1_data_gas: 0xffffffffffffffff, l2_gas: 0x3e7 } .serialize(ref data); starknet::testing::cheatcode::<'set_config_available_gas'>(data.span()); let mut data = array![]; snforge_std::_internals::config_types::FuzzerConfig { seed: Option::Some(0x141), runs: Option::Some(0x7b) } .serialize(ref data); starknet::testing::cheatcode::<'set_config_fuzzer'>(data.span()); return; } } ", ); let item = result.token_stream; let args = TokenStream::empty(); let result = fuzzer_wrapper(args, item); assert_diagnostics(&result, &[]); assert_output( &result, r" #[__internal_config_statement] fn empty_fn__snforge_internal_fuzzer_generated() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::AvailableResourceBoundsConfig { l1_gas: 0xffffffffffffffff, l1_data_gas: 0xffffffffffffffff, l2_gas: 0x3e7 } .serialize(ref data); starknet::testing::cheatcode::<'set_config_available_gas'>(data.span()); let mut data = array![]; snforge_std::_internals::config_types::FuzzerConfig { seed: Option::Some(0x141), runs: Option::Some(0x7b) } .serialize(ref data); starknet::testing::cheatcode::<'set_config_fuzzer'>(data.span()); empty_fn(snforge_std::fuzzable::Fuzzable::blank()); return; } let f = snforge_std::fuzzable::Fuzzable::::generate(); snforge_std::_internals::save_fuzzer_arg(@f); empty_fn(f); } #[__internal_config_statement] fn empty_fn(f: felt252) { } ", ); } use snforge_scarb_plugin::attributes::test_case::test_case; #[test] #[expect(clippy::too_many_lines)] fn works_with_test_fuzzer_and_test_case() { // Ad 1. We must add `#[test_case]` first so `#[test]` will not throw // diagnostic error "function with parameters must have #[fuzzer] or #[test_case] attribute". // It will be later removed (Ad 2.). let item = quote!( #[test_case(name: "one_and_two", 1, 2, 3)] fn test_add(x: i128, y: i128, expected: i128) {} ); let result = test(TokenStream::empty(), item.clone()); assert_diagnostics(&result, &[]); assert_output( &result, r#" #[test_case(name: "one_and_two", 1, 2, 3)] fn test_add(x: i128, y: i128, expected: i128) {} "#, ); let item = get_function(&result.token_stream, "test_add", false); let result = fuzzer(TokenStream::empty(), item); assert_diagnostics(&result, &[]); assert_output( &result, r#" #[__fuzzer_config] #[__fuzzer_wrapper] #[test_case(name: "one_and_two", 1, 2, 3)] fn test_add(x: i128, y: i128, expected: i128) {} "#, ); // Ad 2. Now, we need to remove `#[test_case]` before calling `test_case()`. let item = get_function(&result.token_stream, "test_add", true); let item = quote! { #[__fuzzer_config] #[__fuzzer_wrapper] #item }; let args = quote!((name: "one_and_two", 1, 2, 3)); let result = test_case(args, item); assert_diagnostics(&result, &[]); assert_output( &result, " #[implicit_precedence(core::pedersen::Pedersen, core::RangeCheck, core::integer::Bitwise, core::ec::EcOp, core::poseidon::Poseidon, core::SegmentArena, core::circuit::RangeCheck96, core::circuit::AddMod, core::circuit::MulMod, core::gas::GasBuiltin, System)] #[snforge_internal_test_executable] fn test_add_one_and_two(mut _data: Span) -> Span:: { core::internal::require_implicit::(); core::internal::revoke_ap_tracking(); core::option::OptionTraitImpl::expect(core::gas::withdraw_gas(), 'Out of gas'); core::option::OptionTraitImpl::expect( core::gas::withdraw_gas_all(core::gas::get_builtin_costs()), 'Out of gas', ); test_add(1, 2, 3); let mut arr = ArrayTrait::new(); core::array::ArrayTrait::span(@arr) } #[__fuzzer_config] #[__fuzzer_wrapper] #[__internal_config_statement] fn test_add(x: i128, y: i128, expected: i128) {} ", ); let item = get_function(&result.token_stream, "test_add", true); let item = quote!( #[implicit_precedence(core::pedersen::Pedersen, core::RangeCheck, core::integer::Bitwise, core::ec::EcOp, core::poseidon::Poseidon, core::SegmentArena, core::circuit::RangeCheck96, core::circuit::AddMod, core::circuit::MulMod, core::gas::GasBuiltin, System)] #[snforge_internal_test_executable] fn test_add_one_and_two(mut _data: Span) -> Span:: { core::internal::require_implicit::(); core::internal::revoke_ap_tracking(); core::option::OptionTraitImpl::expect(core::gas::withdraw_gas(), "Out of gas"); core::option::OptionTraitImpl::expect( core::gas::withdraw_gas_all(core::gas::get_builtin_costs()), "Out of gas", ); test_add(1, 2, 3); let mut arr = ArrayTrait::new(); core::array::ArrayTrait::span(@arr) } #[__fuzzer_wrapper] #item ); let result = fuzzer_config(TokenStream::empty(), item); assert_diagnostics(&result, &[]); assert_output( &result, " #[implicit_precedence(core::pedersen::Pedersen, core::RangeCheck, core::integer::Bitwise, core::ec::EcOp, core::poseidon::Poseidon, core::SegmentArena, core::circuit::RangeCheck96, core::circuit::AddMod, core::circuit::MulMod, core::gas::GasBuiltin, System)] #[snforge_internal_test_executable] fn test_add_one_and_two(mut _data: Span) -> Span { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::FuzzerConfig { seed: Option::None, runs: Option::None, } .serialize(ref data); starknet::testing::cheatcode::<'set_config_fuzzer'>(data.span()); return; } core::internal::require_implicit::(); core::internal::revoke_ap_tracking(); core::option::OptionTraitImpl::expect(core::gas::withdraw_gas(), \"Out of gas\"); core::option::OptionTraitImpl::expect( core::gas::withdraw_gas_all(core::gas::get_builtin_costs()),\"Out of gas\", ); test_add(1, 2, 3); let mut arr = ArrayTrait::new(); core::array::ArrayTrait::span(@arr) } "); let item = get_function(&result.token_stream, "test_add_one_and_two", false); let result = fuzzer_wrapper(TokenStream::empty(), item); assert_diagnostics(&result, &[]); assert_output( &result, " fn test_add_one_and_two__snforge_internal_fuzzer_generated() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::FuzzerConfig { seed: Option::None, runs: Option::None, } .serialize(ref data); starknet::testing::cheatcode::<'set_config_fuzzer'>(data.span()); test_add_one_and_two(snforge_std::fuzzable::Fuzzable::blank()); return; } let _data = snforge_std::fuzzable::Fuzzable::>::generate(); snforge_std::_internals::save_fuzzer_arg(@_data); test_add_one_and_two(_data); } #[implicit_precedence(core::pedersen::Pedersen, core::RangeCheck, core::integer::Bitwise, core::ec::EcOp, core::poseidon::Poseidon, core::SegmentArena, core::circuit::RangeCheck96, core::circuit::AddMod, core::circuit::MulMod, core::gas::GasBuiltin, System)] #[snforge_internal_test_executable] #[__internal_config_statement] fn test_add_one_and_two(mut _data: Span) -> Span:: { core::internal::require_implicit::(); core::internal::revoke_ap_tracking(); core::option::OptionTraitImpl::expect(core::gas::withdraw_gas(), \"Out of gas\"); core::option::OptionTraitImpl::expect( core::gas::withdraw_gas_all(core::gas::get_builtin_costs()), \"Out of gas\", ); test_add(1, 2, 3); let mut arr = ArrayTrait::new(); core::array::ArrayTrait::span(@arr) } ", ); } ================================================ FILE: crates/snforge-scarb-plugin/tests/integration/single_attributes/available_gas.rs ================================================ use crate::utils::{assert_diagnostics, assert_output, empty_function}; use cairo_lang_macro::{Diagnostic, quote}; use indoc::formatdoc; use snforge_scarb_plugin::attributes::available_gas::available_gas; #[test] fn works_with_empty() { let args = quote!(()); let result = available_gas(args, empty_function()); assert_output( &result, " fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::AvailableResourceBoundsConfig { l1_gas: 0xffffffffffffffff, l1_data_gas: 0xffffffffffffffff, l2_gas: 0xffffffffffffffff } .serialize(ref data); starknet::testing::cheatcode::<'set_config_available_gas'>(data.span()); return; } } ", ); assert_diagnostics( &result, &[Diagnostic::warn( "#[available_gas] used with empty argument list. Either remove () or specify some arguments", )], ); } #[test] fn fails_with_non_number_literal() { let args = quote!((l2_gas: "123")); let result = available_gas(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error(formatdoc!( "#[available_gas] should be number literal" ))], ); } #[test] fn work_with_number_some_set() { let args = quote!((l1_gas: 123)); let result = available_gas(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::AvailableResourceBoundsConfig { l1_gas: 0x7b, l1_data_gas: 0xffffffffffffffff, l2_gas: 0xffffffffffffffff } .serialize(ref data); starknet::testing::cheatcode::<'set_config_available_gas'>(data.span()); return; } } ", ); } #[test] fn work_with_number_all_set() { let args = quote!((l1_gas: 1, l1_data_gas: 2, l2_gas: 3)); let result = available_gas(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::AvailableResourceBoundsConfig { l1_gas: 0x1, l1_data_gas: 0x2, l2_gas: 0x3 } .serialize(ref data); starknet::testing::cheatcode::<'set_config_available_gas'>(data.span()); return; } }", ); } #[test] fn is_used_once() { let args = quote!((l2_gas: 1, l2_gas: 3)); let result = available_gas(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error(formatdoc!( " argument was specified 2 times, expected to be used only once" ))], ); } #[test] fn does_not_work_with_unnamed_arg() { let args = quote!((3)); let result = available_gas(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error(formatdoc!( "#[available_gas] can be used with named arguments only" ))], ); } #[test] fn fails_with_unexpected_args() { let args = quote!((sth: 1000)); let result = available_gas(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error(formatdoc!( "#[available_gas] unexpected argument(s): " ))], ); } #[test] fn handles_number_overflow_l1() { let args = quote!((l1_gas: 18446744073709551616)); let result = available_gas(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error(formatdoc!( "#[available_gas] l1_gas it too large (max permissible value is 18446744073709551615)" ))], ); } #[test] fn handles_number_overflow_l1_data() { let args = quote!((l1_data_gas: 18446744073709551616)); let result = available_gas(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error(formatdoc!( "#[available_gas] l1_data_gas it too large (max permissible value is 18446744073709551615)" ))], ); } #[test] fn handles_number_overflow_l2() { let args = quote!((l2_gas: 18446744073709551616)); let result = available_gas(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error(formatdoc!( "#[available_gas] l2_gas it too large (max permissible value is 18446744073709551615)" ))], ); } #[test] fn max_permissible_value() { let args = quote!((l2_gas: 18446744073709551615)); let result = available_gas(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::AvailableResourceBoundsConfig { l1_gas: 0xffffffffffffffff, l1_data_gas: 0xffffffffffffffff, l2_gas: 0xffffffffffffffff } .serialize(ref data); starknet::testing::cheatcode::<'set_config_available_gas'>(data.span()); return; } } ", ); } ================================================ FILE: crates/snforge-scarb-plugin/tests/integration/single_attributes/disable_predeployed_contracts.rs ================================================ use crate::utils::{assert_diagnostics, assert_output, empty_function}; use cairo_lang_macro::{Diagnostic, TokenStream, quote}; use snforge_scarb_plugin::attributes::disable_predeployed_contracts::disable_predeployed_contracts; #[test] fn fails_with_args() { let args = quote!((123)); let result = disable_predeployed_contracts(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error( "#[disable_predeployed_contracts] does not accept any arguments", )], ); } #[test] fn works_without_args() { let args = TokenStream::empty(); let result = disable_predeployed_contracts(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::PredeployedContractsConfig { is_disabled: true } .serialize(ref data); starknet::testing::cheatcode::<'set_config_disable_contracts'>(data.span()); return; } } ", ); } #[test] fn is_used_once() { let item = quote! { #[disable_predeployed_contracts] fn empty_fn() {} }; let args = TokenStream::empty(); let result = disable_predeployed_contracts(args, item); assert_diagnostics( &result, &[Diagnostic::error( "#[disable_predeployed_contracts] can only be used once per item", )], ); } ================================================ FILE: crates/snforge-scarb-plugin/tests/integration/single_attributes/fork.rs ================================================ use crate::utils::{assert_diagnostics, assert_output, empty_function}; use cairo_lang_macro::{Diagnostic, quote}; use indoc::formatdoc; use snforge_scarb_plugin::attributes::fork::fork; #[test] fn fails_without_block() { let args = quote!((url: "invalid url")); let result = fork(args, empty_function()); assert_diagnostics( &result, &[ Diagnostic::error(formatdoc!( " All options failed - variant: exactly one of | | should be specified, got 0 - variant: #[fork] expected arguments: 1, got: 0 - variant: #[fork] can be used with unnamed arguments only Resolve at least one of them " )) ], ); } #[test] fn fails_without_url() { let args = quote!((block_number: 23)); let result = fork(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error(formatdoc!( " All options failed - variant: argument is missing - variant: #[fork] expected arguments: 1, got: 0 - variant: #[fork] can be used with unnamed arguments only Resolve at least one of them " ))], ); } #[test] fn fails_without_args() { let args = quote!(()); let result = fork(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::warn("#[fork] used with empty argument list. Either remove () or specify some arguments"), Diagnostic::error(formatdoc!( " All options failed - variant: exactly one of | | should be specified, got 0 - variant: #[fork] expected arguments: 1, got: 0 - variant: #[fork] expected arguments: 1, got: 0 Resolve at least one of them " ))], ); } #[test] fn fails_with_invalid_url() { let args = quote!((url: "invalid url", block_number: 23)); let result = fork(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error(formatdoc!( " All options failed - variant: #[fork] is not a valid url - variant: #[fork] expected arguments: 1, got: 0 - variant: #[fork] can be used with unnamed arguments only Resolve at least one of them " ))], ); } #[test] fn accepts_string() { let args = quote!(("test")); let result = fork(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, r#" fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::ForkConfig::Named("test") .serialize(ref data); starknet::testing::cheatcode::<'set_config_fork'>(data.span()); return; } } "#, ); } #[test] fn fails_with_unexpected_args() { let args = quote!((url: "http://example.com", block_number: 23, tomato: 123, hello: "world")); let result = fork(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error(formatdoc!( " All options failed - variant: #[fork] unexpected argument(s): , - variant: #[fork] expected arguments: 1, got: 0 - variant: #[fork] can be used with unnamed arguments only Resolve at least one of them " ))], ); } #[test] fn accepts_inline_config() { let args = quote!((url: "http://example.com", block_number: 23)); let result = fork(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, r#" fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::ForkConfig::Inline( snforge_std::_internals::config_types::InlineForkConfig { url: "http://example.com/", block: snforge_std::_internals::config_types::BlockId::BlockNumber(0x17) } ) .serialize(ref data); starknet::testing::cheatcode::<'set_config_fork'>(data.span()); return; } } "#, ); } #[test] fn overriding_config_name_first() { let args = quote!(("MAINNET", block_number: 23)); let result = fork(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, r#" fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::ForkConfig::Overridden( snforge_std::_internals::config_types::OverriddenForkConfig { block: snforge_std::_internals::config_types::BlockId::BlockNumber(0x17), name: "MAINNET" } ) .serialize(ref data); starknet::testing::cheatcode::<'set_config_fork'>(data.span()); return; } } "#, ); } #[test] fn overriding_config_name_second() { let args = quote!((block_number: 23, "MAINNET")); let result = fork(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, r#" fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::ForkConfig::Overridden( snforge_std::_internals::config_types::OverriddenForkConfig { block: snforge_std::_internals::config_types::BlockId::BlockNumber(0x17), name: "MAINNET" } ) .serialize(ref data); starknet::testing::cheatcode::<'set_config_fork'>(data.span()); return; } } "#, ); } #[test] fn is_used_once() { let item = quote!( #[fork] fn empty_fn() {} ); let args = quote!(("name")); let result = fork(args, item); assert_diagnostics( &result, &[Diagnostic::error("#[fork] can only be used once per item")], ); } ================================================ FILE: crates/snforge-scarb-plugin/tests/integration/single_attributes/fuzzer.rs ================================================ use crate::utils::{assert_diagnostics, assert_output, empty_function}; use cairo_lang_macro::{Diagnostic, TextSpan, Token, TokenStream, TokenTree, quote}; use snforge_scarb_plugin::attributes::fuzzer::wrapper::fuzzer_wrapper; use snforge_scarb_plugin::attributes::fuzzer::{fuzzer, fuzzer_config}; #[test] fn work_without_args() { let args = TokenStream::empty(); let result = fuzzer(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, " #[__fuzzer_config] #[__fuzzer_wrapper] fn empty_fn() {} ", ); } #[test] fn work_with_args() { let args = quote!((runs: 655, seed: 32872357)); let result = fuzzer(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, " #[__fuzzer_config(runs: 655, seed: 32872357)] #[__fuzzer_wrapper] fn empty_fn() {} ", ); } #[test] fn config_works_with_runs_only() { let args = quote!((runs: 655)); let result = fuzzer_config(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::FuzzerConfig { seed: Option::None, runs: Option::Some(0x28f) } .serialize(ref data); starknet::testing::cheatcode::<'set_config_fuzzer'>(data.span()); return; } } ", ); } #[test] fn config_works_with_seed_only() { let args = quote!((seed: 655)); let result = fuzzer_config(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::FuzzerConfig { seed: Option::Some(0x28f), runs: Option::None } .serialize(ref data); starknet::testing::cheatcode::<'set_config_fuzzer'>(data.span()); return; } } ", ); } #[test] fn config_works_with_both_args() { let args = quote!((runs: 655, seed: 32872357)); let result = fuzzer_config(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::FuzzerConfig { seed: Option::Some(0x1f597a5), runs: Option::Some(0x28f) } .serialize(ref data); starknet::testing::cheatcode::<'set_config_fuzzer'>(data.span()); return; } } ", ); } #[test] fn config_wrapper_work_without_args() { let args = TokenStream::empty(); let result = fuzzer_config(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::FuzzerConfig { seed: Option::None, runs: Option::None } .serialize(ref data); starknet::testing::cheatcode::<'set_config_fuzzer'>(data.span()); return; } } ", ); let item = result.token_stream; let args = TokenStream::empty(); let result = fuzzer_wrapper(args, item); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn__snforge_internal_fuzzer_generated() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::FuzzerConfig { seed: Option::None, runs: Option::None } .serialize(ref data); starknet::testing::cheatcode::<'set_config_fuzzer'>(data.span()); empty_fn(); return; } empty_fn(); } #[__internal_config_statement] fn empty_fn() { } ", ); } #[test] fn config_wrapper_work_with_both_args() { let args = quote!((runs: 655, seed: 32872357)); let result = fuzzer_config(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::FuzzerConfig { seed: Option::Some(0x1f597a5), runs: Option::Some(0x28f) } .serialize(ref data); starknet::testing::cheatcode::<'set_config_fuzzer'>(data.span()); return; } } ", ); let item = result.token_stream; let args = TokenStream::empty(); let result = fuzzer_wrapper(args, item); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn__snforge_internal_fuzzer_generated() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::FuzzerConfig { seed: Option::Some(0x1f597a5), runs: Option::Some(0x28f) } .serialize(ref data); starknet::testing::cheatcode::<'set_config_fuzzer'>(data.span()); empty_fn(); return; } empty_fn(); } #[__internal_config_statement] fn empty_fn() { } ", ); } #[test] fn config_wrapper_work_with_fn_with_single_param() { let item = quote!( fn empty_fn(f: felt252) {} ); let args = TokenStream::empty(); let result = fuzzer_config(args, item); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn(f: felt252) { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::FuzzerConfig { seed: Option::None, runs: Option::None } .serialize(ref data); starknet::testing::cheatcode::<'set_config_fuzzer'>(data.span()); return; } } ", ); let item = result.token_stream; let args = TokenStream::empty(); let result = fuzzer_wrapper(args, item); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn__snforge_internal_fuzzer_generated() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::FuzzerConfig { seed: Option::None, runs: Option::None } .serialize(ref data); starknet::testing::cheatcode::<'set_config_fuzzer'>(data.span()); empty_fn(snforge_std::fuzzable::Fuzzable::blank()); return; } let f = snforge_std::fuzzable::Fuzzable::::generate(); snforge_std::_internals::save_fuzzer_arg(@f); empty_fn(f); } #[__internal_config_statement] fn empty_fn(f: felt252) { } ", ); } #[test] fn config_wrapper_work_with_fn_with_params() { let item = quote!( fn empty_fn(f: felt252, u: u32) {} ); let args = TokenStream::empty(); let result = fuzzer_config(args, item); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn(f: felt252, u: u32) { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::FuzzerConfig { seed: Option::None, runs: Option::None } .serialize(ref data); starknet::testing::cheatcode::<'set_config_fuzzer'>(data.span()); return; } } ", ); let item = result.token_stream; let args = TokenStream::empty(); let result = fuzzer_wrapper(args, item); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn__snforge_internal_fuzzer_generated() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::FuzzerConfig { seed: Option::None, runs: Option::None } .serialize(ref data); starknet::testing::cheatcode::<'set_config_fuzzer'>(data.span()); empty_fn(snforge_std::fuzzable::Fuzzable::blank(), snforge_std::fuzzable::Fuzzable::blank()); return; } let f = snforge_std::fuzzable::Fuzzable::::generate(); snforge_std::_internals::save_fuzzer_arg(@f); let u = snforge_std::fuzzable::Fuzzable::::generate(); snforge_std::_internals::save_fuzzer_arg(@u); empty_fn(f, u); } #[__internal_config_statement] fn empty_fn(f: felt252, u: u32) { } ", ); } #[test] fn wrapper_handle_attributes() { let item = quote!( #[available_gas(l2_gas: 40000)] #[test] fn empty_fn() {} ); let args = TokenStream::empty(); let result = fuzzer_wrapper(args, item); assert_output( &result, " #[test] fn empty_fn__snforge_internal_fuzzer_generated() { if snforge_std::_internals::is_config_run() { empty_fn(); return; } empty_fn(); } #[available_gas(l2_gas: 40000)] #[__internal_config_statement] fn empty_fn() { } ", ); } #[test] fn fail_with_invalid_args() { let args = TokenStream::new(vec![TokenTree::Ident(Token::new( "(seed: '655')", TextSpan::call_site(), ))]); let result = fuzzer_config(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error( "#[fuzzer] should be number literal", )], ); } #[test] fn fail_with_unnamed_arg() { let args = quote!((123)); let result = fuzzer_config(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error( "#[fuzzer] can be used with named arguments only", )], ); } #[test] fn is_used_once() { let item = quote!( #[fuzzer] fn empty_fn() {} ); let args = TokenStream::empty(); let result = fuzzer(args, item); assert_diagnostics( &result, &[Diagnostic::error( "#[fuzzer] can only be used once per item", )], ); } #[test] fn fails_with_unexpected_args() { let args = quote!((runs: 100, tomato: 123)); let result = fuzzer_config(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error( "#[fuzzer] unexpected argument(s): ", )], ); } ================================================ FILE: crates/snforge-scarb-plugin/tests/integration/single_attributes/ignore.rs ================================================ use crate::utils::{assert_diagnostics, assert_output, empty_function}; use cairo_lang_macro::{Diagnostic, TokenStream, quote}; use snforge_scarb_plugin::attributes::ignore::ignore; #[test] fn fails_with_args() { let args = quote!((123)); let result = ignore(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error("#[ignore] does not accept any arguments")], ); } #[test] fn works_without_args() { let args = TokenStream::empty(); let result = ignore(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::IgnoreConfig { is_ignored: true } .serialize(ref data); starknet::testing::cheatcode::<'set_config_ignore'>(data.span()); return; } } ", ); } #[test] fn is_used_once() { let item = quote!( #[ignore] fn empty_fn() {} ); let args = TokenStream::empty(); let result = ignore(args, item); assert_diagnostics( &result, &[Diagnostic::error( "#[ignore] can only be used once per item", )], ); } ================================================ FILE: crates/snforge-scarb-plugin/tests/integration/single_attributes/internal_config_statement.rs ================================================ use crate::utils::{assert_diagnostics, assert_output, empty_function}; use cairo_lang_macro::{Diagnostic, TokenStream, quote}; use snforge_scarb_plugin::attributes::internal_config_statement::internal_config_statement; #[test] fn fails_with_non_empty_args() { let args = quote!((123)); let result = internal_config_statement(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error( "#[__internal_config_statement] does not accept any arguments", )], ); } #[test] fn appends_config_statement() { let args = TokenStream::empty(); let result = internal_config_statement(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn() { if snforge_std::_internals::is_config_run() { return; } } ", ); } #[test] fn is_used_once() { let item = quote!( #[__internal_config_statement] fn empty_fn() {} ); let args = TokenStream::empty(); let result = internal_config_statement(args, item); assert_diagnostics( &result, &[Diagnostic::error( "#[__internal_config_statement] can only be used once per item", )], ); } ================================================ FILE: crates/snforge-scarb-plugin/tests/integration/single_attributes/should_panic.rs ================================================ use crate::utils::{assert_diagnostics, assert_output, empty_function}; use cairo_lang_macro::{Diagnostic, TextSpan, Token, TokenStream, TokenTree, quote}; use snforge_scarb_plugin::attributes::should_panic::should_panic; #[test] fn work_with_empty() { let args = TokenStream::empty(); let result = should_panic(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, r" fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::ShouldPanicConfig { expected: snforge_std::_internals::config_types::Expected::Any } .serialize(ref data); starknet::testing::cheatcode::<'set_config_should_panic'>(data.span()); return; } } ", ); } #[test] fn work_with_expected_string() { let args = quote!((expected: "panic data")); let result = should_panic(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, r#" fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::ShouldPanicConfig { expected: snforge_std::_internals::config_types::Expected::ByteArray("panic data") } .serialize(ref data); starknet::testing::cheatcode::<'set_config_should_panic'>(data.span()); return; } } "#, ); } #[test] fn work_with_expected_string_escaped() { let args = quote!((expected: "can\"t \0 null byte")); let result = should_panic(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, r#" fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::ShouldPanicConfig { expected: snforge_std::_internals::config_types::Expected::ByteArray("can\"t \0 null byte") } .serialize(ref data); starknet::testing::cheatcode::<'set_config_should_panic'>(data.span()); return; } } "#, ); } #[test] fn work_with_expected_short_string() { let args = TokenStream::new(vec![TokenTree::Ident(Token::new( "(expected: 'panic data')", TextSpan::call_site(), ))]); let result = should_panic(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, r" fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::ShouldPanicConfig { expected: snforge_std::_internals::config_types::Expected::ShortString('panic data') } .serialize(ref data); starknet::testing::cheatcode::<'set_config_should_panic'>(data.span()); return; } } ", ); } #[test] fn work_with_expected_short_string_escaped() { let args = TokenStream::new(vec![TokenTree::Ident(Token::new( r"(expected: 'can\'t')", TextSpan::call_site(), ))]); let result = should_panic(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, r" fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::ShouldPanicConfig { expected: snforge_std::_internals::config_types::Expected::ShortString('can\'t') } .serialize(ref data); starknet::testing::cheatcode::<'set_config_should_panic'>(data.span()); return; } } ", ); } #[test] fn work_with_expected_tuple() { let args = TokenStream::new(vec![TokenTree::Ident(Token::new( r"(expected: ('panic data', ' or not'))", TextSpan::call_site(), ))]); let result = should_panic(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, " fn empty_fn() { if snforge_std::_internals::is_config_run() { let mut data = array![]; snforge_std::_internals::config_types::ShouldPanicConfig { expected: snforge_std::_internals::config_types::Expected::Array(array!['panic data',' or not',]) } .serialize(ref data); starknet::testing::cheatcode::<'set_config_should_panic'>(data.span()); return; } } ", ); } #[test] fn is_used_once() { let item = quote!( #[should_panic] fn empty_fn() {} ); let args = TokenStream::empty(); let result = should_panic(args, item); assert_diagnostics( &result, &[Diagnostic::error( "#[should_panic] can only be used once per item", )], ); } #[test] fn fails_with_unexpected_args() { let args = quote!((expected: "panic", tomato: 123)); let result = should_panic(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error( "#[should_panic] unexpected argument(s): ", )], ); } ================================================ FILE: crates/snforge-scarb-plugin/tests/integration/single_attributes/test.rs ================================================ use crate::utils::{assert_diagnostics, assert_output, empty_function}; use cairo_lang_macro::{Diagnostic, TokenStream, quote}; use snforge_scarb_plugin::attributes::test::test; #[test] fn appends_internal_config_and_executable() { let args = TokenStream::empty(); let result = test(args, empty_function()); assert_diagnostics(&result, &[]); assert_output( &result, " #[implicit_precedence(core::pedersen::Pedersen, core::RangeCheck, core::integer::Bitwise, core::ec::EcOp, core::poseidon::Poseidon, core::SegmentArena, core::circuit::RangeCheck96, core::circuit::AddMod, core::circuit::MulMod, core::gas::GasBuiltin, System)] #[snforge_internal_test_executable] fn empty_fn__snforge_internal_test_generated(mut _data: Span) -> Span:: { core::internal::require_implicit::(); core::internal::revoke_ap_tracking(); core::option::OptionTraitImpl::expect(core::gas::withdraw_gas(), 'Out of gas'); core::option::OptionTraitImpl::expect( core::gas::withdraw_gas_all(core::gas::get_builtin_costs()), 'Out of gas', ); empty_fn(); let mut arr = ArrayTrait::new(); core::array::ArrayTrait::span(@arr) } fn empty_fn() { if snforge_std::_internals::is_config_run() { return; } } ", ); } #[test] fn fails_with_non_empty_args() { let args = quote!((123)); let result = test(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error("#[test] does not accept any arguments")], ); } #[test] fn is_used_once() { let item = quote!( #[test] fn empty_fn() {} ); let args = TokenStream::empty(); let result = test(args, item); assert_diagnostics( &result, &[Diagnostic::error("#[test] can only be used once per item")], ); } #[test] fn fails_with_params() { let item = quote!( fn empty_fn(f: felt252) {} ); let args = TokenStream::empty(); let result = test(args, item); assert_diagnostics( &result, &[Diagnostic::error( "#[test] function with parameters must have #[fuzzer] or #[test_case] attribute", )], ); } ================================================ FILE: crates/snforge-scarb-plugin/tests/integration/single_attributes/test_case.rs ================================================ use crate::utils::{assert_diagnostics, assert_output, empty_function}; use cairo_lang_macro::{Diagnostic, TokenStream, quote}; use snforge_scarb_plugin::attributes::test_case::test_case; pub fn function_with_params() -> TokenStream { quote!( fn test_add(x: i128, y: i128, expected: i128) {} ) } #[test] fn works_with_args() { let args = quote!((1, 2, 3)); let result = test_case(args, function_with_params()); assert_diagnostics(&result, &[]); assert_output( &result, " #[implicit_precedence(core::pedersen::Pedersen, core::RangeCheck, core::integer::Bitwise, core::ec::EcOp, core::poseidon::Poseidon, core::SegmentArena, core::circuit::RangeCheck96, core::circuit::AddMod, core::circuit::MulMod, core::gas::GasBuiltin, System)] #[snforge_internal_test_executable] fn test_add_1_2_3(mut _data: Span) -> Span { core::internal::require_implicit::(); core::internal::revoke_ap_tracking(); core::option::OptionTraitImpl::expect(core::gas::withdraw_gas(), 'Out of gas'); core::option::OptionTraitImpl::expect( core::gas::withdraw_gas_all(core::gas::get_builtin_costs()), 'Out of gas', ); test_add(1, 2, 3); let mut arr = ArrayTrait::new(); core::array::ArrayTrait::span(@arr) } #[__internal_config_statement] fn test_add(x: i128, y: i128, expected: i128) {} ", ); } #[test] fn works_with_name_and_args() { let args = quote!((name: "one_and_two", 1, 2, 3)); let result = test_case(args, function_with_params()); assert_diagnostics(&result, &[]); assert_output( &result, " #[implicit_precedence(core::pedersen::Pedersen, core::RangeCheck, core::integer::Bitwise, core::ec::EcOp, core::poseidon::Poseidon, core::SegmentArena, core::circuit::RangeCheck96, core::circuit::AddMod, core::circuit::MulMod, core::gas::GasBuiltin, System)] #[snforge_internal_test_executable] fn test_add_one_and_two(mut _data: Span) -> Span { core::internal::require_implicit::(); core::internal::revoke_ap_tracking(); core::option::OptionTraitImpl::expect(core::gas::withdraw_gas(), 'Out of gas'); core::option::OptionTraitImpl::expect( core::gas::withdraw_gas_all(core::gas::get_builtin_costs()), 'Out of gas', ); test_add(1, 2, 3); let mut arr = ArrayTrait::new(); core::array::ArrayTrait::span(@arr) } #[__internal_config_statement] fn test_add(x: i128, y: i128, expected: i128) {} ", ); } #[test] fn invalid_args_number() { let args = quote!((1, 2)); let result = test_case(args, function_with_params()); assert_diagnostics( &result, &[Diagnostic::error( "#[test_case] Expected 3 arguments, but got 2", )], ); } #[test] fn name_passed_multiple_times() { let args = quote!((name: "a", name: "b", 1, 2, 3)); let result = test_case(args, function_with_params()); assert_diagnostics( &result, &[Diagnostic::error( " argument was specified 2 times, expected to be used only once", )], ); } #[test] fn function_without_params() { let args = quote!((1, 2, 3)); let result = test_case(args, empty_function()); assert_diagnostics( &result, &[Diagnostic::error( "#[test_case] The function must have at least one parameter to use #[test_case] attribute", )], ); } #[test] fn fails_with_unexpected_named_args() { let args = quote!((name: "test", tomato: 123, 1, 2, 3)); let result = test_case(args, function_with_params()); assert_diagnostics( &result, &[Diagnostic::error( "#[test_case] unexpected argument(s): ", )], ); } ================================================ FILE: crates/snforge-scarb-plugin/tests/integration/single_attributes.rs ================================================ mod available_gas; mod disable_predeployed_contracts; mod fork; mod fuzzer; mod ignore; mod internal_config_statement; mod should_panic; mod test; mod test_case; ================================================ FILE: crates/snforge-scarb-plugin/tests/integration/utils.rs ================================================ use cairo_lang_formatter::{CairoFormatter, FormatterConfig}; use cairo_lang_macro::{Diagnostic, ProcMacroResult, TokenStream, quote}; use std::collections::HashSet; pub fn empty_function() -> TokenStream { quote!( fn empty_fn() {} ) } pub fn assert_diagnostics(result: &ProcMacroResult, expected: &[Diagnostic]) { let diagnostics: HashSet<_> = result.diagnostics.iter().collect(); let expected: HashSet<_> = expected.iter().collect(); let remaining_diagnostics: Vec<_> = diagnostics .difference(&expected) .map(|diff| { format!( "Diagnostic where emitted and unexpected:\n {:?} => \"{}\"\n", diff.severity(), diff.message(), ) }) .collect(); let not_emitted_diagnostics: Vec<_> = expected .difference(&diagnostics) .map(|diff| { format!( "Diagnostic where expected but not emitted:\n {:?} => \"{}\"\n", diff.severity(), diff.message(), ) }) .collect(); assert!( remaining_diagnostics.is_empty() && not_emitted_diagnostics.is_empty(), "\n---------------------\n\n{}\n---------------------", remaining_diagnostics.join("\n") + "\n" + ¬_emitted_diagnostics.join("\n") ); } // Before asserting output token, format both strings with `CairoFormatter` and normalize by removing newlines pub fn assert_output(result: &ProcMacroResult, expected: &str) { let fmt = CairoFormatter::new(FormatterConfig::default()); let format_and_normalize_code = |code: String| -> String { fmt.format_to_string(&code) .unwrap_or_else(|_| panic!("Failed to format provided code: {code}")) .into_output_text() .replace('\n', "") .trim() .to_string() }; assert_eq!( format_and_normalize_code(result.token_stream.to_string()), format_and_normalize_code(expected.to_string()), "Invalid code generated" ); } ================================================ FILE: crates/testing/packages_validation/Cargo.toml ================================================ [package] name = "packages_validation" version = "1.0.0" edition.workspace = true [lib] [dependencies] camino.workspace = true scarb-api = { path = "../../scarb-api" } project-root.workspace = true [dev-dependencies] test-case.workspace = true ================================================ FILE: crates/testing/packages_validation/src/lib.rs ================================================ use camino::Utf8PathBuf; use scarb_api::ScarbCommand; use std::process::Stdio; pub fn check_and_lint(package_path: &Utf8PathBuf) { let check_output = ScarbCommand::new() .current_dir(package_path) .arg("check") .command() .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) .output() .expect("Failed to run `scarb check`"); assert!( check_output.status.success(), "`scarb check` failed in {package_path}", ); let lint_output = ScarbCommand::new() .current_dir(package_path) .arg("lint") .command() .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) .output() .expect("Failed to run `scarb lint`"); assert!( lint_output.status.success(), "`scarb lint` failed in {package_path}" ); } ================================================ FILE: crates/universal-sierra-compiler-api/Cargo.toml ================================================ [package] name = "universal-sierra-compiler-api" version = "1.0.0" edition.workspace = true [dependencies] shared.workspace = true serde.workspace = true serde_json.workspace = true which.workspace = true tempfile.workspace = true num-bigint.workspace = true cairo-lang-casm.workspace = true cairo-lang-starknet-classes.workspace = true tracing.workspace = true strum_macros.workspace = true thiserror.workspace = true ================================================ FILE: crates/universal-sierra-compiler-api/src/command.rs ================================================ use shared::command::{CommandError, CommandExt}; use std::process::Output; use std::{ env, ffi::OsStr, process::{Command, Stdio}, }; use thiserror::Error; /// Errors that can occur while working with `universal-sierra-compiler` command. #[derive(Debug, Error)] pub enum USCError { #[error( "`universal-sierra-compiler` binary not available. \ Make sure it is installed https://github.com/software-mansion/universal-sierra-compiler \ and available in PATH or set via UNIVERSAL_SIERRA_COMPILER." )] NotFound(#[source] which::Error), #[error( "Error while compiling Sierra. \ Make sure you have the latest universal-sierra-compiler binary installed. \ Contact Starknet Foundry team through Github or Telegram if it doesn't help." )] RunFailed(#[source] CommandError), } /// An internal builder for `universal-sierra-compiler` command invocation. #[derive(Debug)] pub struct USCInternalCommand { inner: Command, } impl USCInternalCommand { /// Creates a new `universal-sierra-compiler` command builder. pub fn new() -> Result { ensure_available()?; let mut cmd = Command::new(binary_path()); cmd.stderr(Stdio::inherit()); Ok(Self { inner: cmd }) } /// Adds an argument to pass to `universal-sierra-compiler`. pub fn arg(mut self, arg: impl AsRef) -> Self { self.inner.arg(arg); self } /// Returns the constructed [`Command`]. #[must_use] pub fn command(self) -> Command { self.inner } /// Runs the `universal-sierra-compiler` command and returns the [`Output`]. pub fn run(self) -> Result { self.command().output_checked().map_err(USCError::RunFailed) } } /// Ensures that `universal-sierra-compiler` binary is available in the system. pub fn ensure_available() -> Result<(), USCError> { which::which(binary_path()) .map(|_| ()) .map_err(USCError::NotFound) } /// Returns the binary path either from env or fallback to default name. fn binary_path() -> String { env::var("UNIVERSAL_SIERRA_COMPILER") .unwrap_or_else(|_| "universal-sierra-compiler".to_string()) } ================================================ FILE: crates/universal-sierra-compiler-api/src/compile.rs ================================================ use crate::command::{USCError, USCInternalCommand}; use serde_json::Value; use std::io; use std::io::Write; use std::path::Path; use strum_macros::Display; use tempfile::Builder; use thiserror::Error; /// Errors that can occur during Sierra compilation. #[derive(Debug, Error)] pub enum CompilationError { #[error("Failed to write Sierra JSON to temp file: {0}")] TempFileWrite(#[from] io::Error), #[error("Could not serialize Sierra JSON: {0}")] Serialization(serde_json::Error), #[error(transparent)] USCSetup(#[from] USCError), #[error("Failed to deserialize compilation output: {0}")] Deserialization(serde_json::Error), } #[derive(Debug, Display, Copy, Clone)] #[strum(serialize_all = "lowercase")] pub enum SierraType { Contract, Raw, } /// Compiles the given Sierra JSON into the specified type using the `universal-sierra-compiler`. pub fn compile_sierra( sierra_json: &Value, sierra_type: SierraType, ) -> Result { let mut temp_sierra_file = Builder::new().tempfile()?; let json_bytes = serde_json::to_vec(sierra_json).map_err(CompilationError::Serialization)?; temp_sierra_file.write_all(&json_bytes)?; compile_sierra_at_path(temp_sierra_file.path(), sierra_type) } /// Compiles the Sierra file at the given path into the specified type using the `universal-sierra-compiler`. #[tracing::instrument(skip_all, level = "debug")] pub fn compile_sierra_at_path( sierra_file_path: &Path, sierra_type: SierraType, ) -> Result { let usc_output = USCInternalCommand::new()? .arg(format!("compile-{sierra_type}")) .arg("--sierra-path") .arg(sierra_file_path) .run()?; Ok(String::from_utf8(usc_output.stdout).expect("valid UTF-8 from universal-sierra-compiler")) } ================================================ FILE: crates/universal-sierra-compiler-api/src/lib.rs ================================================ //! API for compiling Sierra programs using the `universal-sierra-compiler` (USC). //! //! This crate provides functions to compile Sierra JSON representations of contracts and raw programs into their respective Casm formats. //! //! # Note: //! To allow more flexibility when changing internals, please make public as few items as possible. use crate::command::{USCError, USCInternalCommand}; use crate::compile::{CompilationError, SierraType, compile_sierra, compile_sierra_at_path}; use crate::representation::RawCasmProgram; use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; use serde_json::Value; use std::path::Path; use std::process::Command; mod command; mod compile; pub mod representation; /// Compiles Sierra JSON of a contract into [`CasmContractClass`]. pub fn compile_contract_sierra(sierra_json: &Value) -> Result { let json = compile_sierra(sierra_json, SierraType::Contract)?; serde_json::from_str(&json).map_err(CompilationError::Deserialization) } /// Compiles Sierra JSON file at the given path of a contract into [`CasmContractClass`]. pub fn compile_contract_sierra_at_path( sierra_file_path: &Path, ) -> Result { let json = compile_sierra_at_path(sierra_file_path, SierraType::Contract)?; serde_json::from_str(&json).map_err(CompilationError::Deserialization) } /// Compiles Sierra JSON of a raw program into [`RawCasmProgram`]. pub fn compile_raw_sierra(sierra_json: &Value) -> Result { let json = compile_sierra(sierra_json, SierraType::Raw)?; serde_json::from_str(&json).map_err(CompilationError::Deserialization) } /// Compiles Sierra JSON file at the given path of a raw program into [`RawCasmProgram`]. pub fn compile_raw_sierra_at_path( sierra_file_path: &Path, ) -> Result { let json = compile_sierra_at_path(sierra_file_path, SierraType::Raw)?; serde_json::from_str(&json).map_err(CompilationError::Deserialization) } /// Creates a `universal-sierra-compiler --version` command. /// /// Only exists because of how requirements checker was implemented. pub fn version_command() -> Result { Ok(USCInternalCommand::new()?.arg("--version").command()) } ================================================ FILE: crates/universal-sierra-compiler-api/src/representation.rs ================================================ use cairo_lang_casm::hints::Hint; use num_bigint::BigInt; use serde::{Deserialize, Serialize}; pub type CasmCodeOffset = usize; pub type CasmInstructionIdx = usize; #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] pub struct RawCasmProgram { pub assembled_cairo_program: AssembledCairoProgram, /// `debug_info[i]` contains the following information about the first CASM instruction that /// was generated by the Sierra statement with /// `StatementIdx(i)` (i-th index in Sierra statements vector): /// - code offset in the CASM bytecode /// - index in CASM instructions vector /// /// Those 2 values are usually not equal since the instruction sizes in CASM may vary pub debug_info: Vec<(CasmCodeOffset, CasmInstructionIdx)>, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct AssembledCairoProgram { pub bytecode: Vec, pub hints: Vec<(usize, Vec)>, } ================================================ FILE: design_documents/accessing_emitted_events.md ================================================ # Accessing emitted events * [Accessing emitted events](#accessing-emitted-events) * [Context](#context) * [Goal](#goal) * [Considered Solutions](#considered-solutions) * [`expect_events` cheatcode](#expectevents-cheatcode) * [Usage example](#usage-example) * [`spy_events` cheatcode](#spyevents-cheatcode) * [Propositions to consider](#propositions-to-consider) * [Usage example](#usage-example-1) ## Context Some contract functions can emit events. It is important to test if they were emitted properly. ## Goal Propose a solution that will allow checking if events were emitted. ## Considered Solutions 1. `expect_events` cheatcode 2. `spy_events` cheatcode ## `expect_events` cheatcode Introduce a cheatcode with the signature: ```cario fn expect_events(events: Array) ``` where `snforge_std::Event` is defined as below ```cario struct Event { name: felt252, keys: Array, data: Array } ``` - `name` is a name of an event passed as a shortstring, - `keys` are values under `#[key]`-marked fields of an event, - `data` are all other values in emitted event. `expect_event` cheatcode will define events which should be emitted in the next call. Other calls will not be affected. If provided events will not be emitted it will panic with a detailed message. `events` are the subset of all events emitted by the function, but you can also require function to return exactly those (and not more) events with the `expect_exact_events` cheatcode. ```cario fn expect_exact_events(events: Array) ``` It will panic if: - not all defined events were emitted - some other events where emitted ### Usage example ```cario #[starknet::interface] trait IHelloEvent { fn emit_store_name(self: @TContractState, name: felt252); } mod HelloEvent { // ... #[event] #[derive(Drop, starknet::Event)] enum Event { StoredName: StoredName, } #[derive(Drop, starknet::Event)] struct StoredName { #[key] user: ContractAddress, name: felt252 } #[abi(embed_v0)] impl IHelloEventImpl of super::IHelloEvent { fn emit_store_name(self: @ContractState, name: felt252) { // ... self.emit(Event::StoredName(StoredName { user: get_caller_address(), name: name })); } } } use snforge_std::expect_events; use snforge_std::Event; #[test] fn check_emitted_event() { // ... expect_events(array![Event { name: 'StoredName', keys: array![123], data: array![456] }]); let res = contract.emit_store_name(...); // if the event is not emitted it will panic let res = contract.emit_store_name(...); // expect_events does not work here expect_exact_events(array![Event { name: 'StoredName', keys: array![123], data: array![456] }]); let res = contract.emit_store_name(...); // function has to emit exactly those events defined in the array otherwise it will panic // ... } ``` ## `spy_events` cheatcode Another idea is to give users the highest possible customization. There would be a handler for extracting events emitted after the line it was created. Users would have to assert events manually (which could be more complex than the previous solution but would add more flexibility). Introduce `spy_events` cheatcode with the signature: ```cario fn spy_events() -> snforge_std::EventSpy ``` where `snforge_std::EventSpy` would allow for accessing events emitted after its creation. `EventSpy` would be defined as follows. ```cario struct EventSpy { events: Array, } struct Event { from: ContractAddress, name: felt252, keys: Array, data: Array } trait EventFetcher { fn fetch_events(ref self: EventSpy); } ``` Users will be responsible for calling `fetch_events` to load emitted events to the `events` property. There will also be `assert_emitted` method available on the `EventSpy` struct. ```cairo trait EventAssertions { fn assert_emitted(ref self: EventSpy, events: Array); } ``` It is designed to enable more simplified flow: - `fetch_events` will be called internally, so there will always be the newest events, - checked events will be removed from the `events` property. ### Propositions to consider - `TargetedSpy` - regular spy with the target parameter for creation, which would be an enumeration of: ```cairo enum SpyOn { All, One(ContractAddress), Multiple(Array>) } ``` - Two different classes for Events - one for incoming events and the second one for events created by users. It would clarify the confusion when `name` field is hashed when it comes from `EventSpy`. ### Usage example ```cario // Contract is the same as in the previous example use snforge_std::spy_events; use snforge_std::EventSpy; use snforge_std::EventFetcher; use snforge_std::EventAssertions; use snforge_std::event_name_hash; #[test] fn check_emitted_event_simple() { // ... let mut spy = spy_events(); // all events emitted after this line will be saved under the `spy` variable let res = contract.emit_store_name(...); // after this line there will be no events under the `spy.events` spy.assert_emitted(array![Event {from: ..., name: 'StoredName', ...}]); let res = contract.emit_store_name(...); let res = contract.emit_store_name(...); spy.assert_emitted( array![ Event {from: ..., name: 'StoredName', ...}, Event {from: ..., name: 'StoredName', ...} ] ); assert(spy.events.len() == 0, 'All events should be consumed'); // ... } #[test] fn check_emitted_event_complex() { // ... let mut spy = spy_events(); // all events emitted after this line will be saved under the `spy` variable let res = contract.emit_store_name(...); spy.fetch_events(); // users can assert events on their own assert(spy.events.len() == 1, 'There should be one event'); assert(spy.events.at(0).name == event_name_hash('StoredName'), 'Wrong event name'); let data = array![...]; assert(spy.events.at(0).data == data, 'Wrong data'); let res = contract.emit_store_name(...); let res = contract.emit_store_name(...); // events will not be present before fetching assert!(spy.events.len() == 1, 'There should be one event'); spy.fetch_events(); assert(spy.events.len() == 3, 'There should be three events'); // ... ``` ================================================ FILE: design_documents/cairo_deployment_scripts.md ================================================ # Cairo deployment scripts ## Table of contents * [Context](#context) * [Goal](#goal) * [Considered Solutions](#considered-solution) * [sncast commands](#sncast-commands) * [sncast config](#sncast-config) * [interacting with contract](#interacting-with-contract) * [running the script](#running-the-script) * [error handling](#error-handling) * [idempotency](#idempotency) * [example script](#example-script) * [example state file](#example-state-file) * [miscellaneous](#miscellaneous) * [Proposed steps and tasks](#proposed-stepstasks) ## Context There should be a possibility to write a script in cairo, that would enable users to make transactions and send them to starknet chain. It should allow to declare and deploy contracts as well as apply state transitions on already deployed contracts. ## Goal Propose a solution with an example syntax, that would allow to write deployment scripts in cairo. ## Considered solution This section is split into smaller subsections describing things we will need to tackle while implementing the solution. ### sncast commands Specific sncast commands (declare, deploy, account) could be imported as regular functions to the scripts, and called as such. Our functions return specific types (structs defined in `cast/src/helpers/response_structs.rs`), that make retrieving essential information easier, so we should be good on that front. The commands would have to be implemented in the same manner as forge's cheatcodes. ### sncast config We should allow for all flags passed to sncast to be propagated to the script. CastConfig should also be importable and usable from script if someone wishes so, but we should not require it. That means, we might have to review our subcommands and if some of them requires it directly (`account create` is one), we should try to get rid of this dependency. ### interacting with contract We will use contracts dispatchers to be able to call/invoke its functions directly (e.g. if contract named `mycontract` has a function `myfunction`, we should be able to just do `mycontractDispatcher.myfunction();`). We should also support our subcommands (`invoke`, `call`) to call/invoke so dispatchers are able to use them, and for calling/invoking contracts without dispatchers. ### running the script The script would essentially run an entrypoint function - `main`. Inside our script subcommand, we will have to compile the cairo script file using a custom builder/extension, and then run it using some form of a custom runner - all similarly to how it is done in snforge. The main function would be required in the deployment script, and we should return an error if it is not found. The deployment script could then be run like so: ```bash $ sncast script /path/to/myscript.s.cairo ``` In later versions, if needed, we might want to also allow to mark an entrypoint function with: ```cairo #[script] fn some_script { ... } ``` Log info should be outputted to stdout and optionally written to some log file. We could have a flag to suppress outputting to stdout or to output with different formats (int, hex). By default, the scripts could run in some kind of "dry run mode", meaning all the transactions would be simulated with specific rpc calls (like `starknet_simulateTransactions`). An interesting idea would be to research an approach similar to cheatnet - fork a runner and test the scripts against it (to be assessed if it is feasible). We could also provide a way to show a trace of said transactions if required. In later iterations, we could also provide a helper function that sets up the devnet to be used for dry running for users. The behaviour would be changed with `--broadcast` flag, that would actually execute needed transactions. We should also include a warning message about potential incurring cost. ### error handling If the transaction fails, we should put all the relevant info about it to the state file, output log information and stop script execution. If the script fails (for any reason that is not connected to transactions - eg rpc node down), we should output relevant log information. In case of failed transaction, we should allow to: - let the users handle the errors in cairo with Result - let the script fail - in the "output" field in state file we should then include an error message. After fixing the issue we should allow users to just re-run the script - all the previous (succeeded) transactions should not be replayed, the erroneous transaction should be retried and its output should be put into [state file](#example-state-file). ### idempotency At the later stages we will want to have the script to be able to track the on chain state using a state file, which would in turn allow the script to be called multiple times without any modifications, to ensure desired state. To achieve that we must ensure idempotency of calls to the network. The proposed solution would be to use an external json file, that holds hashes of transactions that were already performed. While executing a command we could first check this file to see if given transaction exists/succeeded, and based on this either perform and an action (make a transaction) or skip it and proceed to the next thing in script. Each transaction request would have its id in the state file, in a form of `Hash(function_name, arguments, address)`. If the id changes the transaction from state file should be re-executed. At each invocation the script generates the list of transactions to make, and compares it with state file - if any transaction id has changed or is missing, this transaction (and each next one from the list) should be (re)executed. To achieve this there are a few possible solutions: - adjust our existing cast subcommand functions to handle checking the file - pros: - just import a subcommand function and use it without a fuss - easy to implement for cast subcommands functions - cons: - we will have to modify currently existing functions to enable state checking - this solution does not work for dispatcher functions - just try to execute functions, parse the output and compare it with the state file - pros: - no modifications needed for subcommands functions - just import a function and use it - cons: - kind of brute force solution - we should not have to talk to chain since we have all the necessary info in local file - executing dispatcher functions can cause us all kinds of trouble (eg - someone could invoke 'send_eth' function multiple times, where in fact they only wanted to do it once) - not every cast subcommand is idempotent - implement a higher order function (or function wrapper) to conditionally execute functions based on the information in state file - pros: - no modifications needed to cast subcommands functions - works with dispatcher functions just fine - added flexibility - user can decide which function should be replayed each time, and which shouldn't - cons: - boilerplate code - wrapping each function call will be ugly - no option to just annotate main function (procedural macros could achieve this, but they do some compile-time modifications) - potentially more complex An example pseudo-implementation of this could look like this: ```rust fn skip_if_done(func: F) where F: Fn() + 'static, { // Decorator logic here // Check the state file and decide whether to execute the function or not if should_execute { func(); // write func outputs to state file } else { // Do nothing or return an appropriate value } } fn main() { skip_if_done(|| { dispatcher.send_eth(...); }); } ``` - Use a procedural macro to automatically apply decorator-like behaviour to functions, which will decide on whether given function should be executetd or not (based on state file) - pros: - elegant solution - could be applied selectively (per function) or globally (to main) - cons: - more complex solution - probably not very suitable for runtime based behaviours (like reading json file) - requires more research An example pseudo-implementation of this could look like this: ```rust // skip_if_done.rs use proc_macro_hack::proc_macro_hack; #[proc_macro_hack] pub use skip_if_done_impl::skip_if_done; // skip_if_done_impl.rs use quote::quote; use std::env; fn should_execute() -> bool { // Your logic to check whether the function should be executed goes here true } #[macro_export] macro_rules! skip_if_done { ($($tokens:tt)*) => { { if should_execute() { $($tokens)* } } }; } // main.rs #[path = "skip_if_done.rs"] mod skip_if_done; fn main() { skip_if_done! { dispatcher.send_eth(...); } } ``` Given all this, the most obvious solutions seem to be: - for cast subcommands functions: - adjust them to handle checking the file - for dispatcher functions: - implement a higher order function Picking those would ensure us that: - syntax is relatively boiler-code free (only dispatcher functions are annotated) - idempotency is achieved - it is quite easy to write a script ### example script An example deployment script could look like this: ```cairo // we might need to rename account functions to avoid confusion use sncast::starknet_commands::account::create::create as create_account use sncast::starknet_commands::account::deploy::deploy as deploy_account use sncast::{get_provider, get_account}; use sncast::starknet_commands::{declare, deploy, invoke, call}; (...) fn make_account(provider: &JsonRpcClient) -> Result, LocalWallet>> { let mut prefunder = get_account("user", &provider)?; // the user that will prefund new account let user = create_account("user1", &provider); let prefund_account = invoke("0x123", "deposit", [1234, user.address], &prefunder); deploy_account("user1", &provider); get_account("user1", &provider)? } fn main() { ler provider = get_provider("http://127.0.0.1:5050/rpc")?; let user = make_account(provider); let declared_contract = declare("mycontract", &user, "/path/to/Scarb.toml"); let contract = deploy(&declared_contract.class_hash, [], &user); let dispatcher = DispatcherMyContract { contract.contract_address }; skip_if_done(|| { dispatcher.increase_balance(5); }); let get_balance = skip_if_done(|| { dispatcher.get_balance(); }); let called_balance = call(&contract.contract_address, "get_balance", [], "latest"); } ``` ### example state file The state file by default should be written to the current working directory, with a name ` ================================================ FILE: docs/theme/header.hbs ================================================ ================================================ FILE: docs/theme/index.hbs ================================================ {{ title }} {{#if is_print }} {{/if}} {{#if base_url}} {{/if}} {{> head}} {{#if favicon_svg}} {{/if}} {{#if favicon_png}} {{/if}} {{#if print_enable}} {{/if}} {{#if copy_fonts}} {{/if}} {{#each additional_css}} {{/each}} {{#if mathjax_support}} {{/if}}

Keyboard shortcuts

Press or to navigate between chapters

{{#if search_enabled}}

Press S or / to search in the book

{{/if}}

Press ? to show this help

Press Esc to hide this help

{{> header}} {{#if search_enabled}} {{/if}}
{{{ content }}}
{{#if live_reload_endpoint}} {{/if}} {{#if google_analytics}} {{/if}} {{#if playground_line_numbers}} {{/if}} {{#if playground_copyable}} {{/if}} {{#if playground_js}} {{/if}} {{#if search_js}} {{/if}} {{#each additional_js}} {{/each}} {{#if is_print}} {{#if mathjax_support}} {{else}} {{/if}} {{/if}} {{#if fragment_map}} {{/if}}
================================================ FILE: scripts/build_docs.sh ================================================ #!/bin/bash # TODO(#2781) pushd docs OUTPUT=$(mdbook build 2>&1) echo "$OUTPUT" if echo "$OUTPUT" | grep -q "\[ERROR\]"; then exit 1 fi ================================================ FILE: scripts/check_snapshots.sh ================================================ #!/bin/sh # This is internal script used to check correctness of snapshots used in snapshots tests. # Runs mentioned tests for all Scarb versions from ./get_scarb_versions.sh. # # NOTE: this requires ./get_scarb_versions.sh to be present in the CWD. set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" BOLD="" RED="" YELLOW="" GREEN="" RESET="" # Check whether colors are supported and should be enabled if [ -z "${NO_COLOR:-}" ] && echo "${TERM:-}" | grep -q "^xterm"; then BOLD="\033[1m" RED="\033[31m" YELLOW="\033[33m" GREEN="\033[32m" RESET="\033[0m" fi SEPARATOR="━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" usage() { cat </dev/null | awk '{print $2}') trap cleanup EXIT _failed="" _ok=0 _total=0 for _ver in $_versions; do _total=$((_total + 1)) info "$SEPARATOR" info "scarb $_ver" info "$SEPARATOR" install_version "scarb" "$_ver" set_tool_version "scarb" "$_ver" if run_tests "$UPDATE_SNAPSHOTS"; then _ok=$((_ok + 1)) else _failed="${_failed}${_failed:+, }$_ver" fi done if [ -n "$_failed" ]; then warn "failed: $_failed" if [ "$UPDATE_SNAPSHOTS" = "1" ]; then exit 0 fi err "run with --fix to update snapshots" fi info "${GREEN}${_ok}/${_total} passed${RESET}" } say() { printf 'check_snapshots: %b\n' "$1" } info() { say "${BOLD}info:${RESET} $1" } warn() { say "${BOLD}${YELLOW}warn:${RESET} ${YELLOW}$1${RESET}" } err() { say "${BOLD}${RED}error:${RESET} ${RED}$1${RESET}" >&2 exit 1 } need_cmd() { if ! check_cmd "$1"; then err "need '$1' (command not found)" fi } check_cmd() { command -v "$1" >/dev/null 2>&1 } ensure() { if ! "$@"; then err "command failed: $*"; fi } install_version() { _tool="$1" _installed_version="$2" if check_version_installed "$_tool" "$_installed_version"; then info "$_tool $_installed_version is already installed" else info "Installing $_tool $_version..." ensure asdf install "$_tool" "$_installed_version" fi } check_version_installed() { _tool="$1" _version="$2" asdf list "$_tool" | grep -q "^[^0-9]*${_version}$" } set_tool_version() { _tool="$1" _version="$2" info "Setting $_tool version to $_version..." ensure asdf set "$_tool" "$_version" } parse_scarb_versions() { echo "$1" | sed 's/"//g' | tr ',' '\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep -v '^$' } cleanup() { if [ -n "${_original_scarb:-}" ]; then asdf set scarb "$_original_scarb" 2>/dev/null || true fi } main "$@" || exit 1 ================================================ FILE: scripts/compareVersions.js ================================================ const semver = require('semver') if (process.argv.length !== 4) { console.error('Two arguments required'); process.exit(1); } const old_version = process.argv[2]; const new_version = process.argv[3]; console.log((semver.gt(new_version, old_version)).toString()) ================================================ FILE: scripts/get_scarb_versions.sh ================================================ #!/bin/bash set -e # This script is used to find the following Scarb versions: # The current major.minor version along with all its patch versions # The latest patch versions for the two versions preceding the current one # # Options: # --previous List only versions older than the current Scarb version (from repo root .tool-versions). function get_all_patch_versions() { asdf list all scarb "$1" | grep -v "rc" } function get_latest_patch_version() { get_all_patch_versions "$1" | sort -uV | tail -1 } function version_less_than() { _version1="$1" _version2="$2" printf '%s\n%s' "$_version1" "$_version2" | sort -V | head -n1 | grep -xqvF "$_version2" } PREVIOUS_ONLY=0 for arg in "$@"; do case "$arg" in --previous) PREVIOUS_ONLY=1 ;; esac done if [[ "$PREVIOUS_ONLY" -eq 1 ]]; then repo_root="$(git rev-parse --show-toplevel)" tool_versions="$repo_root/.tool-versions" if [[ ! -f "$tool_versions" ]]; then echo ".tool-versions not found at $tool_versions" >&2 exit 1 fi current=$(grep -E '^\s*scarb\s+' "$tool_versions" | awk '{ print $2 }') if [[ -z "$current" ]]; then echo "no scarb version in $tool_versions" >&2 exit 1 fi fi major_minor_versions=($(get_all_patch_versions | cut -d . -f 1,2 | sort -uV | tail -3)) declare -a scarb_versions ver=$(get_latest_patch_version "${major_minor_versions[0]}") if [[ "$PREVIOUS_ONLY" -eq 0 ]] || version_less_than "$ver" "$current"; then scarb_versions+=("$ver") fi ver=$(get_latest_patch_version "${major_minor_versions[1]}") if [[ "$PREVIOUS_ONLY" -eq 0 ]] || version_less_than "$ver" "$current"; then scarb_versions+=("$ver") fi for ver in $(get_all_patch_versions "${major_minor_versions[2]}" | sort -uV); do if [[ "$PREVIOUS_ONLY" -eq 0 ]] || version_less_than "$ver" "$current"; then scarb_versions+=("$ver") fi done printf '"%s", ' "${scarb_versions[@]}" | sed 's/, $/\n/' ================================================ FILE: scripts/handle_version.sh ================================================ #!/bin/bash get_version() { local overridden_version=$1 local plugin_path=$2 local default_version default_version=$(grep "^version =" "$plugin_path" | cut -d '"' -f 2) if [ -z "$overridden_version" ]; then echo "$default_version" else echo "$overridden_version" fi } update_version_in_file() { local file=$1 local version=$2 if [ -n "$version" ]; then sed -i.bak "/\[package\]/,/version =/ s/version = \".*/version = \"$version\"/" "$file" rm "$file.bak" 2> /dev/null fi } export -f get_version export -f update_version_in_file ================================================ FILE: scripts/install.sh ================================================ #!/usr/bin/env sh set -e echo Installing snfoundryup... LOCAL_BIN="${HOME}/.local/bin" SNFOUNDRYUP_URL="https://raw.githubusercontent.com/foundry-rs/starknet-foundry/master/scripts/snfoundryup" SNFOUNDRYUP_PATH="${LOCAL_BIN}/snfoundryup" # Check for curl if ! command -v curl > /dev/null 2>&1; then echo "curl could not be found, please install it first." exit 1 fi # Create the ${HOME}/.local/bin bin directory and snfoundryup binary if it doesn't exist. mkdir -p "${LOCAL_BIN}" curl -# -L "${SNFOUNDRYUP_URL}" -o "${SNFOUNDRYUP_PATH}" chmod +x "${SNFOUNDRYUP_PATH}" # Store the correct profile file (i.e. .profile for bash or .zshenv for ZSH). case $SHELL in */zsh) PROFILE=${ZDOTDIR-"$HOME"}/.zshenv PREF_SHELL=zsh ;; */bash) PROFILE=$HOME/.bashrc PREF_SHELL=bash ;; */fish) PROFILE=$HOME/.config/fish/config.fish PREF_SHELL=fish ;; */ash) PROFILE=$HOME/.profile PREF_SHELL=ash ;; *) echo "snfoundryup: could not detect shell, manually add ${LOCAL_BIN} to your PATH." exit 0 esac # Only add snfoundryup if it isn't already in PATH. case ":$PATH:" in *":${LOCAL_BIN}:"*) # The path is already in PATH, do nothing ;; *) # Add the snfoundryup directory to the path echo >> "$PROFILE" && echo "export PATH=\"\$PATH:$LOCAL_BIN\"" >> "$PROFILE" ;; esac printf "\nDetected your preferred shell is %s and added snfoundryup to PATH. Run 'source %s' or start a new terminal session to use snfoundryup.\n" "$PREF_SHELL" "$PROFILE" printf "Then, simply run 'snfoundryup' to install Starknet-Foundry.\n" ================================================ FILE: scripts/package.json ================================================ { "dependencies": { "semver": "^7.5.4" } } ================================================ FILE: scripts/package.sh ================================================ #!/usr/bin/env bash set -euxo pipefail TARGET="$1" PKG_FULL_NAME="$2" rm -rf "$PKG_FULL_NAME" mkdir -p "$PKG_FULL_NAME/bin" binary_crates=("snforge" "sncast") for crate in "${binary_crates[@]}"; do cp "./target/${TARGET}/release/${crate}" "$PKG_FULL_NAME/bin/" done cp -r README.md LICENSE "$PKG_FULL_NAME/" tar czvf "${PKG_FULL_NAME}.tar.gz" "$PKG_FULL_NAME" ================================================ FILE: scripts/release.sh ================================================ #!/bin/bash set -euxo pipefail VERSION=$1 sed -i.bak "s/## \[Unreleased\]/## \[Unreleased\]\n\n## \[${VERSION}\] - $(TZ=Europe/Krakow date '+%Y-%m-%d')/" CHANGELOG.md rm CHANGELOG.md.bak 2> /dev/null sed -i.bak "/\[workspace.package\]/,/version =/ s/version = \".*/version = \"${VERSION}\"/" Cargo.toml rm Cargo.toml.bak 2> /dev/null sed -i.bak "/\[package\]/,/version =/ s/version = \".*/version = \"${VERSION}\"/" sncast_std/Scarb.toml rm sncast_std/Scarb.toml.bak 2> /dev/null scarb --manifest-path sncast_std/Scarb.toml build sed -i.bak "/\[package\]/,/version =/ s/version = \".*/version = \"${VERSION}\"/" snforge_std/Scarb.toml rm snforge_std/Scarb.toml.bak 2> /dev/null sed -i.bak "/\[package\]/,/version =/ s/version = \".*/version = \"${VERSION}\"/" crates/snforge-scarb-plugin/Scarb.toml rm crates/snforge-scarb-plugin/Scarb.toml.bak 2> /dev/null sed -i.bak "/\[package\]/,/version =/ s/version = \".*/version = \"${VERSION}\"/" crates/snforge-scarb-plugin/Cargo.toml rm crates/snforge-scarb-plugin/Cargo.toml.bak 2> /dev/null sed -i.bak "/\[preprocessor.variables.variables\]/,/snforge_std_version =/ s/snforge_std_version = \".*/snforge_std_version = \"${VERSION}\"/" docs/book.toml rm docs/book.toml.bak 2> /dev/null # start: Update cache test data VERSION_UNDERSCORED=$(echo "$VERSION" | tr '.' '_') DIRECTORY="crates/forge/tests/data/forking/.snfoundry_cache" OLD_FILE_PATH=$(find "$DIRECTORY" -type f -regex '.*_v[0-9][a-z0-9_-]*\.json') # Strip the shortest match of "_v*" starting from the end of the string OLD_FILE_PATH_BASE="${OLD_FILE_PATH%_v*}" NEW_FILE_PATH="${OLD_FILE_PATH_BASE}_v${VERSION_UNDERSCORED}.json" mv "$OLD_FILE_PATH" "$NEW_FILE_PATH" sed -i.bak -E "s/\"cache_version\":\"[a-z0-9_-]+\"/\"cache_version\":\"${VERSION_UNDERSCORED}\"/" "$NEW_FILE_PATH" rm "$NEW_FILE_PATH.bak" 2> /dev/null # end scarb --manifest-path snforge_std/Scarb.toml build cargo update cargo update --manifest-path crates/snforge-scarb-plugin/Cargo.toml ================================================ FILE: scripts/scarbfmt.sh ================================================ #!/usr/bin/env bash SNFOUNDRY_ROOT="$(git rev-parse --show-toplevel)" pushd "$SNFOUNDRY_ROOT" || exit find . -type f -name "Scarb.toml" -execdir sh -c ' echo "Running \"scarb fmt\" in directory: $PWD" scarb fmt ' \; popd || exit ================================================ FILE: scripts/smoke_test.sh ================================================ #!/bin/bash export DEV_DISABLE_SNFORGE_STD_DEPENDENCY=true RPC_URL="$1" SNFORGE_PATH="$2" SNCAST_PATH="$3" REPO_URL="$4" REVISION="$5" VERSION="$6" # Check snforge_std from github repository $SNFORGE_PATH new my_project_0 pushd my_project_0 || exit scarb add --dev snforge_std --git "$REPO_URL" --rev "$REVISION" $SNFORGE_PATH test || exit popd || exit scarb cache clean # Check snforge_std from registry with prebuilt plugin $SNFORGE_PATH new my_project_1 pushd my_project_1 || exit sed -i.bak "/^\[dev-dependencies\]/a\\ snforge_std = { version = \"=${VERSION}\", registry = \"https://scarbs.dev/\" }\\ " Scarb.toml rm Scarb.toml.bak 2>/dev/null test_output=$($SNFORGE_PATH test) test_exit=$? echo $test_output if [[ $test_exit -ne 0 ]] || echo "$test_output" | grep -q 'Compiling snforge_scarb_plugin'; then exit 1 fi popd || exit scarb cache clean # Check snforge_std from registry without prebuilt plugin $SNFORGE_PATH new my_project_2 pushd my_project_2 || exit sed -i.bak "/^\[dev-dependencies\]/a\\ snforge_std = { version = \"=${VERSION}\", registry = \"https://scarbs.dev/\" }\\ " Scarb.toml sed -i.bak '/^allow-prebuilt-plugins = \["snforge_std"\]$/d' Scarb.toml rm Scarb.toml.bak 2>/dev/null $SNFORGE_PATH test || exit popd || exit scarb cache clean # Check cast if ! $SNCAST_PATH call \ --url "$RPC_URL" \ --contract-address 0x06b248bde9ce00d69099304a527640bc9515a08f0b49e5168e2096656f207e1d \ --function "get" --calldata 0x1 | grep -q $'Success: Call completed\n\nResponse: 0x0\nResponse Raw: [0x0]'; then exit 1 fi ================================================ FILE: scripts/snfoundryup ================================================ #!/bin/sh # shellcheck shell=dash # shellcheck disable=SC2039 # This is just a little script that can be downloaded from the internet to install Starknet Foundry. # It just does platform detection, downloads the release archive, extracts it and tries to make # the `snforge` and `sncast` binaries available in $PATH in least invasive way possible. # # It runs on Unix shells like {a,ba,da,k,z}sh. It uses the common `local` extension. # Note: Most shells limit `local` to 1 var per line, contra bash. # # Most of this code is based on/copy-pasted from rustup and protostar installers. set -u REPO="https://github.com/foundry-rs/starknet-foundry" XDG_DATA_HOME="${XDG_DATA_HOME:-"${HOME}/.local/share"}" INSTALL_ROOT="${XDG_DATA_HOME}/starknet-foundry-install" LOCAL_BIN="${HOME}/.local/bin" LOCAL_BIN_ESCAPED="\$HOME/.local/bin" BINARIES="snforge sncast" # Array syntax for shells. List can be expanded for new binaries usage() { cat <&2 exit 1 } need_cmd() { if ! check_cmd "$1"; then err "need '$1' (command not found)" fi } check_cmd() { command -v "$1" >/dev/null 2>&1 } assert_nz() { if [ -z "$1" ]; then err "assert_nz $2"; fi } # Run a command that should never fail. # If the command fails execution will immediately terminate with an error showing the failing command. ensure() { if ! "$@"; then err "command failed: $*"; fi } # This is just for indicating that commands' results are being intentionally ignored. # Usually, because it's being executed as part of error handling. ignore() { "$@" } download_universal_sierra_compiler() { curl -L https://raw.githubusercontent.com/software-mansion/universal-sierra-compiler/master/scripts/install.sh | sh } # This function has been copied verbatim from rustup install script. get_architecture() { local _ostype _cputype _bitness _arch _clibtype _ostype="$(uname -s)" _cputype="$(uname -m)" _clibtype="gnu" if [ "$_ostype" = Linux ]; then if [ "$(uname -o)" = Android ]; then _ostype=Android fi if ldd --_requested_version 2>&1 | grep -q 'musl'; then _clibtype="musl" fi fi if [ "$_ostype" = Darwin ] && [ "$_cputype" = i386 ]; then # Darwin `uname -m` lies if sysctl hw.optional.x86_64 | grep -q ': 1'; then _cputype=x86_64 fi fi if [ "$_ostype" = SunOS ]; then # Both Solaris and illumos presently announce as "SunOS" in "uname -s" # so use "uname -o" to disambiguate. We use the full path to the # system uname in case the user has coreutils uname first in PATH, # which has historically sometimes printed the wrong value here. if [ "$(/usr/bin/uname -o)" = illumos ]; then _ostype=illumos fi # illumos systems have multi-arch userlands, and "uname -m" reports the # machine hardware name; e.g., "i86pc" on both 32- and 64-bit x86 # systems. Check for the native (widest) instruction set on the # running kernel: if [ "$_cputype" = i86pc ]; then _cputype="$(isainfo -n)" fi fi case "$_ostype" in Android) _ostype=linux-android ;; Linux) check_proc _ostype=unknown-linux-$_clibtype _bitness=$(get_bitness) ;; FreeBSD) _ostype=unknown-freebsd ;; NetBSD) _ostype=unknown-netbsd ;; DragonFly) _ostype=unknown-dragonfly ;; Darwin) _ostype=apple-darwin ;; illumos) _ostype=unknown-illumos ;; MINGW* | MSYS* | CYGWIN* | Windows_NT) _ostype=pc-windows-gnu ;; *) err "unrecognized OS type: $_ostype" ;; esac case "$_cputype" in i386 | i486 | i686 | i786 | x86) _cputype=i686 ;; xscale | arm) _cputype=arm if [ "$_ostype" = "linux-android" ]; then _ostype=linux-androideabi fi ;; armv6l) _cputype=arm if [ "$_ostype" = "linux-android" ]; then _ostype=linux-androideabi else _ostype="${_ostype}eabihf" fi ;; armv7l | armv8l) _cputype=armv7 if [ "$_ostype" = "linux-android" ]; then _ostype=linux-androideabi else _ostype="${_ostype}eabihf" fi ;; aarch64 | arm64) _cputype=aarch64 ;; x86_64 | x86-64 | x64 | amd64) _cputype=x86_64 ;; mips) _cputype=$(get_endianness mips '' el) ;; mips64) if [ "$_bitness" -eq 64 ]; then # only n64 ABI is supported for now _ostype="${_ostype}abi64" _cputype=$(get_endianness mips64 '' el) fi ;; ppc) _cputype=powerpc ;; ppc64) _cputype=powerpc64 ;; ppc64le) _cputype=powerpc64le ;; s390x) _cputype=s390x ;; riscv64) _cputype=riscv64gc ;; loongarch64) _cputype=loongarch64 ;; *) err "unknown CPU type: $_cputype" ;; esac # Detect 64-bit linux with 32-bit userland if [ "${_ostype}" = unknown-linux-gnu ] && [ "${_bitness}" -eq 32 ]; then case $_cputype in x86_64) if [ -n "${RUSTUP_CPUTYPE:-}" ]; then _cputype="$RUSTUP_CPUTYPE" else { # 32-bit executable for amd64 = x32 if is_host_amd64_elf; then err "x86_64 linux with x86 userland unsupported" else _cputype=i686 fi }; fi ;; mips64) _cputype=$(get_endianness mips '' el) ;; powerpc64) _cputype=powerpc ;; aarch64) _cputype=armv7 if [ "$_ostype" = "linux-android" ]; then _ostype=linux-androideabi else _ostype="${_ostype}eabihf" fi ;; riscv64gc) err "riscv64 with 32-bit userland unsupported" ;; esac fi # Detect armv7 but without the CPU features Rust needs in that build, # and fall back to arm. # See https://github.com/rust-lang/rustup.rs/issues/587. if [ "$_ostype" = "unknown-linux-gnueabihf" ] && [ "$_cputype" = armv7 ]; then if ensure grep '^Features' /proc/cpuinfo | grep -q -v neon; then # At least one processor does not have NEON. _cputype=arm fi fi _arch="${_cputype}-${_ostype}" RETVAL="$_arch" } resolve_version() { local _requested_version=$1 local _requested_ref=$2 local _response say "retrieving $_requested_version version from ${REPO}..." _response=$(ensure curl -# -Ls -H 'Accept: application/json' "${REPO}/releases/${_requested_ref}") if [ "{\"error\":\"Not Found\"}" = "$_response" ]; then err "version $_requested_version not found" fi RETVAL=$(echo "$_response" | sed -e 's/.*"tag_name":"\([^"]*\)".*/\1/') } create_install_dir() { local _requested_version=$1 local _installdir="${INSTALL_ROOT}/${_requested_version}" if [ -d "$_installdir" ]; then ensure rm -rf "$_installdir" say "removed existing snforge and sncast installation at ${_installdir}" fi ensure mkdir -p "$_installdir" RETVAL=$_installdir } download() { local _resolved_version=$1 local _arch=$2 local _installdir=$3 local _tempdir=$4 local _tarball="starknet-foundry-${_resolved_version}-${_arch}.tar.gz" local _url="${REPO}/releases/download/${_resolved_version}/${_tarball}" local _dl="$_tempdir/starknet-foundry.tar.gz" say "downloading ${_tarball}..." ensure curl -# -fLS -o "$_dl" "$_url" ensure tar -xz -C "$_installdir" --strip-components=1 -f "$_dl" } create_symlinks() { local _installdir=$1 for binary in $BINARIES; do local _binary="${_installdir}/bin/${binary}" local _symlink="${LOCAL_BIN}/${binary}" ensure mkdir -p "$LOCAL_BIN" ensure ln -fs "$_binary" "$_symlink" ensure chmod u+x "$_symlink" say "created symlink ${_symlink} -> ${_binary}" done } add_local_bin_to_path() { local _profile local _pref_shell case ${SHELL:-""} in */zsh) _profile=$HOME/.zshrc _pref_shell=zsh ;; */ash) _profile=$HOME/.profile _pref_shell=ash ;; */bash) _profile=$HOME/.bashrc _pref_shell=bash ;; */fish) _profile=$HOME/.config/fish/config.fish _pref_shell=fish ;; *) err "could not detect shell, manually add '${LOCAL_BIN_ESCAPED}' to your PATH." ;; esac echo >>"$_profile" && echo "export PATH=\"\$PATH:${LOCAL_BIN_ESCAPED}\"" >>"$_profile" echo \ "Detected your preferred shell is ${_pref_shell} and added '${LOCAL_BIN_ESCAPED}' to PATH." \ "Run 'source ${_profile}' or start a new terminal session to use Starknet Foundry." } clone_and_build() { local _commit_hash=$1 local _tempdir if ! _tempdir="$(ensure mktemp -d)"; then exit 1 fi say "Cloning repository at commit $_commit_hash..." ensure git clone "$REPO" "$_tempdir" ensure cd "$_tempdir" ensure git fetch --all ensure git checkout "$_commit_hash" say "Building binaries with cargo..." ensure cargo build --release local _build_output_dir="$PWD/target/release" create_install_dir "${_commit_hash}/bin" local _installdir=${RETVAL%????} # create_symlinks expects a PATH with /bin stripped, rather than modifying the function, we just strip it here. assert_nz "$_installdir" "installdir" ensure cp "$_build_output_dir/snforge" "$_installdir/bin/snforge" ensure cp "$_build_output_dir/sncast" "$_installdir/bin/sncast" create_symlinks "$_installdir" ensure cd - > /dev/null # Go back to the previous directory and suppress output } main "$@" || exit 1 ================================================ FILE: scripts/verify_cairo_listings.sh ================================================ #!/bin/bash set -xe for d in ./docs/listings/*; do (cd "$d" && scarb check); done ================================================ FILE: sncast_std/README.md ================================================ # sncast_std For a library reference please visit https://foundry-rs.github.io/starknet-foundry/appendix/sncast-library.html Full documentation can be found at https://foundry-rs.github.io/starknet-foundry/sncast_std/ ================================================ FILE: sncast_std/Scarb.lock ================================================ # Code generated by scarb DO NOT EDIT. version = 1 [[package]] name = "sncast_std" version = "0.59.0" ================================================ FILE: sncast_std/Scarb.toml ================================================ [package] name = "sncast_std" version = "0.59.0" edition = "2023_11" description = "Library used for writing deployment scripts in Cairo" homepage = "https://foundry-rs.github.io/starknet-foundry/starknet/script.html" documentation = "https://foundry-rs.github.io/starknet-foundry/appendix/sncast-library.html" repository = "https://github.com/foundry-rs/starknet-foundry" license-file = "../LICENSE" ================================================ FILE: sncast_std/src/lib.cairo ================================================ use core::array::ArrayTrait; use core::fmt::{Debug, Display, Error, Formatter}; use core::serde::Serde; use starknet::testing::cheatcode; use starknet::{ClassHash, ContractAddress}; #[derive(Drop, PartialEq, Serde, Debug)] pub struct ErrorData { msg: ByteArray, } #[derive(Drop, PartialEq, Serde, Debug)] pub struct ContractErrorData { revert_error: ContractExecutionError, } #[derive(Drop, PartialEq, Debug)] pub struct TransactionExecutionErrorData { transaction_index: felt252, execution_error: ContractExecutionError, } impl TransactionExecutionErrorDataSerde of Serde { fn serialize(self: @TransactionExecutionErrorData, ref output: Array) { output.append(*self.transaction_index); self.execution_error.serialize(ref output); } fn deserialize(ref serialized: Span) -> Option { let transaction_index = (*serialized.pop_front()?); let execution_error = Serde::::deserialize(ref serialized) .expect('Failed to deserialize'); Option::Some(TransactionExecutionErrorData { transaction_index, execution_error }) } } #[derive(Drop, PartialEq, Debug)] pub enum ContractExecutionError { Nested: Box, Message: ByteArray, } #[derive(Drop, Serde, Debug)] pub struct ContractExecutionErrorInner { contract_address: ContractAddress, class_hash: felt252, selector: felt252, error: ContractExecutionError, } impl ContractExecutionErrorInnerPartialEq of PartialEq { fn eq(lhs: @ContractExecutionErrorInner, rhs: @ContractExecutionErrorInner) -> bool { lhs.contract_address == rhs.contract_address && lhs.class_hash == rhs.class_hash && lhs.selector == rhs.selector && lhs.error == rhs.error } } impl BoxContractExecutionErrorInnerPartialEq of PartialEq> { fn eq(lhs: @Box, rhs: @Box) -> bool { let lhs = (lhs).as_snapshot().unbox(); let rhs = (rhs).as_snapshot().unbox(); ContractExecutionErrorInnerPartialEq::eq(lhs, rhs) } } impl ContractExecutionErrorSerde of Serde { fn serialize(self: @ContractExecutionError, ref output: Array) { // We need to add 0 and 1 because of enum variants serialization match self { ContractExecutionError::Nested(inner) => { let inner = inner.as_snapshot().unbox(); output.append(0); inner.serialize(ref output); }, ContractExecutionError::Message(msg) => { output.append(1); msg.serialize(ref output); }, } } fn deserialize(ref serialized: Span) -> Option { let first = (*serialized.pop_front()?); if first == 0 { let inner = Serde::::deserialize(ref serialized) .expect('Failed to deserialize'); let inner = BoxTrait::new(inner); Option::Some(ContractExecutionError::Nested(inner)) } else { let message = Serde::::deserialize(ref serialized) .expect('Failed to deserialize'); Option::Some(ContractExecutionError::Message(message)) } } } impl BoxContractExecutionErrorSerde of Serde> { fn serialize(self: @Box, ref output: Array) { let unboxed = self.as_snapshot().unbox(); Serde::::serialize(unboxed, ref output) } fn deserialize(ref serialized: Span) -> Option> { Option::Some(BoxTrait::new(ContractExecutionErrorSerde::deserialize(ref serialized)?)) } } #[derive(Drop, Serde, PartialEq, Debug)] pub enum StarknetError { /// Failed to receive transaction FailedToReceiveTransaction, /// Contract not found ContractNotFound, /// Requested entrypoint does not exist in the contract EntryPointNotFound, /// Block not found BlockNotFound, /// Invalid transaction index in a block InvalidTransactionIndex, /// Class hash not found ClassHashNotFound, /// Transaction hash not found TransactionHashNotFound, /// Contract error ContractError: ContractErrorData, /// Transaction execution error TransactionExecutionError: TransactionExecutionErrorData, /// Class already declared ClassAlreadyDeclared, /// Invalid transaction nonce InvalidTransactionNonce, /// The transaction's resources don't cover validation or the minimal transaction fee InsufficientResourcesForValidate, /// Account balance is smaller than the transaction's max_fee InsufficientAccountBalance, /// Account validation failed ValidationFailure: ErrorData, /// Compilation failed CompilationFailed, /// Contract class size it too large ContractClassSizeIsTooLarge, /// Sender address in not an account contract NonAccount, /// A transaction with the same hash already exists in the mempool DuplicateTx, /// the compiled class hash did not match the one supplied in the transaction CompiledClassHashMismatch, /// the transaction version is not supported UnsupportedTxVersion, /// the contract class version is not supported UnsupportedContractClassVersion, /// An unexpected error occurred UnexpectedError: ErrorData, } #[derive(Drop, Serde, PartialEq, Debug)] pub enum ProviderError { StarknetError: StarknetError, RateLimited, UnknownError: ErrorData, } #[derive(Drop, Serde, PartialEq, Debug)] pub enum TransactionError { Reverted: ErrorData, } #[derive(Drop, Serde, PartialEq, Debug)] pub enum WaitForTransactionError { TransactionError: TransactionError, TimedOut, ProviderError: ProviderError, } #[derive(Drop, Serde, PartialEq, Debug)] pub enum ScriptCommandError { UnknownError: ErrorData, ContractArtifactsNotFound: ErrorData, WaitForTransactionError: WaitForTransactionError, ProviderError: ProviderError, } pub impl DisplayClassHash of Display { fn fmt(self: @ClassHash, ref f: Formatter) -> Result<(), Error> { let class_hash: felt252 = (*self).into(); Display::fmt(@class_hash, ref f) } } pub impl DisplayContractAddress of Display { fn fmt(self: @ContractAddress, ref f: Formatter) -> Result<(), Error> { let addr: felt252 = (*self).into(); Display::fmt(@addr, ref f) } } #[derive(Drop, Clone, Debug, Serde)] pub struct CallResult { pub data: Array, } impl DisplayCallResult of Display { fn fmt(self: @CallResult, ref f: Formatter) -> Result<(), Error> { Debug::fmt(self.data, ref f) } } pub fn call( contract_address: ContractAddress, function_selector: felt252, calldata: Array, ) -> Result { let contract_address_felt: felt252 = contract_address.into(); let mut inputs = array![contract_address_felt, function_selector]; let mut calldata_serialized = array![]; calldata.serialize(ref calldata_serialized); inputs.append_span(calldata_serialized.span()); let mut buf = handle_cheatcode(cheatcode::<'call'>(inputs.span())); let mut result_data: Result = match Serde::>::deserialize(ref buf) { Option::Some(result_data) => result_data, Option::None => panic!("call deserialize failed"), }; result_data } #[derive(Drop, Copy, Debug, Serde)] pub enum DeclareResult { AlreadyDeclared: AlreadyDeclaredResult, Success: DeclareTransactionResult, } #[derive(Drop, Copy, Debug, Serde)] pub struct DeclareTransactionResult { pub class_hash: ClassHash, pub transaction_hash: felt252, } #[derive(Drop, Copy, Debug, Serde)] pub struct AlreadyDeclaredResult { pub class_hash: ClassHash, } pub trait DeclareResultTrait { fn class_hash(self: @DeclareResult) -> @ClassHash; } impl DeclareResultImpl of DeclareResultTrait { fn class_hash(self: @DeclareResult) -> @ClassHash { match self { DeclareResult::Success(result) => result.class_hash, DeclareResult::AlreadyDeclared(result) => result.class_hash, } } } impl DisplayDeclareResult of Display { fn fmt(self: @DeclareResult, ref f: Formatter) -> Result<(), Error> { match self { DeclareResult::Success(result) => write!( f, "class_hash: {}, transaction_hash: {}", result.class_hash, result.transaction_hash, ), DeclareResult::AlreadyDeclared(result) => write!( f, "class_hash: {}", result.class_hash, ), } } } pub fn declare( contract_name: ByteArray, fee_settings: FeeSettings, nonce: Option, ) -> Result { let mut inputs = array![]; contract_name.serialize(ref inputs); let mut fee_settings_serialized = array![]; fee_settings.serialize(ref fee_settings_serialized); let mut nonce_serialized = array![]; nonce.serialize(ref nonce_serialized); inputs.append_span(fee_settings_serialized.span()); inputs.append_span(nonce_serialized.span()); let mut buf = handle_cheatcode(cheatcode::<'declare'>(inputs.span())); let mut result_data: Result = match Serde::>::deserialize(ref buf) { Option::Some(result_data) => result_data, Option::None => panic!("declare deserialize failed"), }; result_data } #[derive(Drop, Copy, Debug, Serde)] pub struct DeployResult { pub contract_address: ContractAddress, pub transaction_hash: felt252, } impl DisplayDeployResult of Display { fn fmt(self: @DeployResult, ref f: Formatter) -> Result<(), Error> { write!( f, "contract_address: {}, transaction_hash: {}", *self.contract_address, *self.transaction_hash, ) } } #[derive(Drop, Copy, Debug, Serde, PartialEq)] pub struct FeeSettings { max_fee: Option, l1_gas: Option, l1_gas_price: Option, l2_gas: Option, l2_gas_price: Option, l1_data_gas: Option, l1_data_gas_price: Option, } #[generate_trait] pub impl FeeSettingsImpl of FeeSettingsTrait { fn resource_bounds( l1_gas: u64, l1_gas_price: u128, l2_gas: u64, l2_gas_price: u128, l1_data_gas: u64, l1_data_gas_price: u128, ) -> FeeSettings { FeeSettings { max_fee: Option::None, l1_gas: Option::Some(l1_gas), l1_gas_price: Option::Some(l1_gas_price), l2_gas: Option::Some(l2_gas), l2_gas_price: Option::Some(l2_gas_price), l1_data_gas: Option::Some(l1_data_gas), l1_data_gas_price: Option::Some(l1_data_gas_price), } } fn max_fee(max_fee: felt252) -> FeeSettings { FeeSettings { max_fee: Option::Some(max_fee), l1_gas: Option::None, l1_gas_price: Option::None, l2_gas: Option::None, l2_gas_price: Option::None, l1_data_gas: Option::None, l1_data_gas_price: Option::None, } } fn estimate() -> FeeSettings { FeeSettings { max_fee: Option::None, l1_gas: Option::None, l1_gas_price: Option::None, l2_gas: Option::None, l2_gas_price: Option::None, l1_data_gas: Option::None, l1_data_gas_price: Option::None, } } } pub fn deploy( class_hash: ClassHash, constructor_calldata: Array, salt: Option, unique: bool, fee_settings: FeeSettings, nonce: Option, ) -> Result { let class_hash_felt: felt252 = class_hash.into(); let mut inputs = array![class_hash_felt]; let mut constructor_calldata_serialized = array![]; constructor_calldata.serialize(ref constructor_calldata_serialized); let mut salt_serialized = array![]; salt.serialize(ref salt_serialized); let mut fee_settings_serialized = array![]; fee_settings.serialize(ref fee_settings_serialized); let mut nonce_serialized = array![]; nonce.serialize(ref nonce_serialized); inputs.append_span(constructor_calldata_serialized.span()); inputs.append_span(salt_serialized.span()); inputs.append(unique.into()); inputs.append_span(fee_settings_serialized.span()); inputs.append_span(nonce_serialized.span()); let mut buf = handle_cheatcode(cheatcode::<'deploy'>(inputs.span())); let mut result_data: Result = match Serde::>::deserialize(ref buf) { Option::Some(result_data) => result_data, Option::None => panic!("deploy deserialize failed"), }; result_data } #[derive(Drop, Clone, Debug, Serde)] pub struct InvokeResult { pub transaction_hash: felt252, } impl DisplayInvokeResult of Display { fn fmt(self: @InvokeResult, ref f: Formatter) -> Result<(), Error> { write!(f, "{}", *self.transaction_hash) } } pub fn invoke( contract_address: ContractAddress, entry_point_selector: felt252, calldata: Array, fee_settings: FeeSettings, nonce: Option, ) -> Result { let contract_address_felt: felt252 = contract_address.into(); let mut inputs = array![contract_address_felt, entry_point_selector]; let mut calldata_serialized = array![]; calldata.serialize(ref calldata_serialized); let mut fee_settings_serialized = array![]; fee_settings.serialize(ref fee_settings_serialized); let mut nonce_serialized = array![]; nonce.serialize(ref nonce_serialized); inputs.append_span(calldata_serialized.span()); inputs.append_span(fee_settings_serialized.span()); inputs.append_span(nonce_serialized.span()); let mut buf = handle_cheatcode(cheatcode::<'invoke'>(inputs.span())); let mut result_data: Result = match Serde::>::deserialize(ref buf) { Option::Some(result_data) => result_data, Option::None => panic!("invoke deserialize failed"), }; result_data } pub fn get_nonce(block_tag: felt252) -> felt252 { let inputs = array![block_tag]; let buf = handle_cheatcode(cheatcode::<'get_nonce'>(inputs.span())); *buf[0] } #[derive(Drop, Copy, Debug, Serde, PartialEq)] pub enum FinalityStatus { Received, Candidate, PreConfirmed, AcceptedOnL2, AcceptedOnL1, } pub impl DisplayFinalityStatus of Display { fn fmt(self: @FinalityStatus, ref f: Formatter) -> Result<(), Error> { let finality_status: ByteArray = match self { FinalityStatus::Received => "Received", FinalityStatus::Candidate => "Candidate", FinalityStatus::PreConfirmed => "PreConfirmed", FinalityStatus::AcceptedOnL2 => "AcceptedOnL2", FinalityStatus::AcceptedOnL1 => "AcceptedOnL1", }; write!(f, "{finality_status}") } } #[derive(Drop, Copy, Debug, Serde, PartialEq)] pub enum ExecutionStatus { Succeeded, Reverted, } pub impl DisplayExecutionStatus of Display { fn fmt(self: @ExecutionStatus, ref f: Formatter) -> Result<(), Error> { let execution_status: ByteArray = match self { ExecutionStatus::Succeeded => "Succeeded", ExecutionStatus::Reverted => "Reverted", }; write!(f, "{execution_status}") } } #[derive(Drop, Copy, Debug, Serde, PartialEq)] pub struct TxStatusResult { pub finality_status: FinalityStatus, pub execution_status: Option, } pub impl DisplayTxStatusResult of Display { fn fmt(self: @TxStatusResult, ref f: Formatter) -> Result<(), Error> { match self.execution_status { Option::Some(status) => write!( f, "finality_status: {}, execution_status: {}", self.finality_status, status, ), Option::None => write!(f, "finality_status: {}", self.finality_status), } } } pub fn tx_status(transaction_hash: felt252) -> Result { let mut inputs = array![transaction_hash]; let mut buf = handle_cheatcode(cheatcode::<'tx_status'>(inputs.span())); let mut result_data: Result = match Serde::>::deserialize(ref buf) { Option::Some(result_data) => result_data, Option::None => panic!("tx_status deserialize failed"), }; result_data } fn handle_cheatcode(input: Span) -> Span { let first = *input.at(0); let input = input.slice(1, input.len() - 1); if first == 1 { // it's in fact core::byte_array::BYTE_ARRAY_MAGIC but it can't be imported here let mut arr = array![0x46a6158a16a947e5916b2a2ca68501a45e93d7110e81aa2d6438b1c57c879a3]; arr.append_span(input); panic(arr) } else { input } } ================================================ FILE: snforge_std/README.md ================================================ # snforge_std For a library reference please visit https://foundry-rs.github.io/starknet-foundry/appendix/snforge-library.html Full documentation can be found at https://foundry-rs.github.io/starknet-foundry/snforge_std/ ================================================ FILE: snforge_std/Scarb.lock ================================================ # Code generated by scarb DO NOT EDIT. version = 1 [[package]] name = "snforge_scarb_plugin" version = "0.59.0" [[package]] name = "snforge_std" version = "0.59.0" dependencies = [ "snforge_scarb_plugin", ] ================================================ FILE: snforge_std/Scarb.toml ================================================ [package] name = "snforge_std" version = "0.59.0" edition = "2024_07" description = "Cairo testing library" documentation = "https://foundry-rs.github.io/starknet-foundry/appendix/snforge-library.html" repository = "https://github.com/foundry-rs/starknet-foundry" license-file = "../LICENSE" re-export-cairo-plugins = ["snforge_scarb_plugin"] [dependencies] snforge_scarb_plugin = { path = "../crates/snforge-scarb-plugin" } ================================================ FILE: snforge_std/src/byte_array.cairo ================================================ use core::byte_array::BYTE_ARRAY_MAGIC; pub fn byte_array_as_felt_array(self: @ByteArray) -> Array { let mut serialized = array![]; self.serialize(ref serialized); serialized } /// This function is meant to transform a serialized output from a contract call into a `ByteArray`. /// `x` - Span of `felt252`s returned from a contract call (panic data) /// Returns the parsed `ByteArray`, or an `Err` if the parsing failed. pub fn try_deserialize_bytearray_error(x: Span) -> Result { if x.len() > 0 && *x.at(0) == BYTE_ARRAY_MAGIC { let mut x_span = x.slice(1, x.len() - 1); return Serde::::deserialize(ref x_span).ok_or("Malformed input provided"); } Result::Err("Input is not a ByteArray-formatted error") } ================================================ FILE: snforge_std/src/cheatcode.cairo ================================================ pub(crate) fn execute_cheatcode(input: Span) -> Span { let result = starknet::testing::cheatcode::(input); let enum_variant = *result.at(0); let result_content = result.slice(1, result.len() - 1); if enum_variant == 1 { // Check if the result is an `Err` let mut arr = array![core::byte_array::BYTE_ARRAY_MAGIC]; arr.append_span(result_content); panic(arr) } else { result_content } } pub(crate) fn execute_cheatcode_and_deserialize>( input: Span, ) -> T { let mut serialized_output = execute_cheatcode::(input); match Serde::deserialize(ref serialized_output) { Option::Some(output) => output, Option::None => panic!("snforge_std version mismatch: check the warning above"), } } // Do not use this function directly. // It is an internal part of the snforge architecture used by macros. pub fn is_config_run() -> bool { execute_cheatcode_and_deserialize::<'is_config_mode'>(array![].span()) } // Do not use this function directly. // It is an internal part of the snforge fuzzer logic used by macros. pub fn save_fuzzer_arg>(input: @T) { let input = format!("{input:?}"); let mut serialized = array![]; input.serialize(ref serialized); execute_cheatcode::<'save_fuzzer_arg'>(serialized.span()); } ================================================ FILE: snforge_std/src/cheatcodes/block_hash.cairo ================================================ use starknet::ContractAddress; use crate::cheatcode::execute_cheatcode_and_deserialize; use super::CheatSpan; use super::execution_info::{CheatArguments, Operation}; /// Changes the block hash for the given block number and contract address. /// - `contract_address` - The contract address to which the cheat applies. /// - `block_number` - Block number to be modified. /// - `block_hash` - `felt252` representing the new block hash. /// - `span` - instance of `CheatSpan` specifying the number of syscalls with the cheat pub fn cheat_block_hash( contract_address: ContractAddress, block_number: u64, block_hash: felt252, span: CheatSpan, ) { _cheat_block_hash( block_number, Operation::Start(CheatArguments { value: block_hash, span, target: contract_address }), ); } /// Starts a global block hash modification. /// - `block_number` - Block number to be modified. /// - `block_hash` - The block hash value to set globally. pub fn start_cheat_block_hash_global(block_number: u64, block_hash: felt252) { _cheat_block_hash(block_number, Operation::StartGlobal(block_hash)); } /// Cancels the `start_cheat_block_hash_global`. /// - `block_number` - Block number for which the cheat should be stopped. pub fn stop_cheat_block_hash_global(block_number: u64) { _cheat_block_hash(block_number, Operation::StopGlobal); } /// Starts a block hash modification for a specific contract. /// - `contract_address` - Contract address associated with the modification. /// - `block_number` - Block number to be modified. /// - `block_hash` - The block hash to set. pub fn start_cheat_block_hash( contract_address: ContractAddress, block_number: u64, block_hash: felt252, ) { _cheat_block_hash( block_number, Operation::Start( CheatArguments { value: block_hash, span: CheatSpan::Indefinite, target: contract_address, }, ), ); } /// Cancels the `cheat_block_hash`/`start_cheat_block_hash` for a specific contract. /// - `block_number` - Block number for which the cheat should be stopped. /// - `contract_address` - The contract affected by the previous cheat. pub fn stop_cheat_block_hash(contract_address: ContractAddress, block_number: u64) { _cheat_block_hash(block_number, Operation::Stop(contract_address)); } fn _cheat_block_hash(block_number: u64, operation: Operation) { let mut inputs = array![block_number.into()]; operation.serialize(ref inputs); execute_cheatcode_and_deserialize::<'set_block_hash', ()>(inputs.span()); } ================================================ FILE: snforge_std/src/cheatcodes/contract_class.cairo ================================================ use starknet::{ClassHash, ContractAddress, SyscallResult}; use crate::byte_array::byte_array_as_felt_array; use crate::cheatcode::execute_cheatcode_and_deserialize; #[derive(Drop, Serde, Copy)] pub struct ContractClass { pub class_hash: ClassHash, } #[derive(Drop, Serde, Clone)] pub enum DeclareResult { Success: ContractClass, AlreadyDeclared: ContractClass, } pub trait ContractClassTrait { /// Calculates an address of a contract in advance that would be returned when calling `deploy` /// The precalculated address is only correct for the very next deployment /// The `constructor_calldata` has a direct impact on the resulting contract address /// `self` - an instance of the struct `ContractClass` which is obtained by calling `declare` /// and unpacking `DeclareResult` /// `constructor_calldata` - serialized calldata for the deploy constructor /// Returns the precalculated `ContractAddress` fn precalculate_address( self: @ContractClass, constructor_calldata: @Array::, ) -> ContractAddress; /// Deploys a contract /// `self` - an instance of the struct `ContractClass` which is obtained by calling `declare` /// and unpacking `DeclareResult` /// `constructor_calldata` - calldata for the constructor, serialized with `Serde` /// Returns the address the contract was deployed at and serialized constructor return data, or /// panic data if it failed fn deploy( self: @ContractClass, constructor_calldata: @Array::, ) -> SyscallResult<(ContractAddress, Span)>; /// Deploys a contract at a given address /// `self` - an instance of the struct `ContractClass` which is obtained by calling `declare` /// and unpacking `DeclareResult` /// `constructor_calldata` - serialized calldata for the constructor /// `contract_address` - address the contract should be deployed at /// Returns the address the contract was deployed at and serialized constructor return data, or /// panic data if it failed fn deploy_at( self: @ContractClass, constructor_calldata: @Array::, contract_address: ContractAddress, ) -> SyscallResult<(ContractAddress, Span)>; /// Utility method for creating a new `ContractClass` instance /// `class_hash` - a numeric value that can be converted into the class hash of `ContractClass` /// Returns the created `ContractClass` fn new>(class_hash: T) -> ContractClass; } impl ContractClassImpl of ContractClassTrait { fn precalculate_address( self: @ContractClass, constructor_calldata: @Array::, ) -> ContractAddress { let mut inputs = _prepare_calldata(self.class_hash, constructor_calldata); execute_cheatcode_and_deserialize::<'precalculate_address'>(inputs.span()) } fn deploy( self: @ContractClass, constructor_calldata: @Array::, ) -> SyscallResult<(ContractAddress, Span)> { let salt: felt252 = execute_cheatcode_and_deserialize::<'get_salt'>(array![].span()); // Internal cheatcode to mark next `deploy_syscall` as coming from cheatcode. // This allows `deploy_syscall` to be handled differently when coming from cheatcode. execute_cheatcode_and_deserialize::<'set_next_syscall_from_cheatcode', ()>(array![].span()); starknet::syscalls::deploy_syscall( *self.class_hash, salt, constructor_calldata.span(), false, ) } fn deploy_at( self: @ContractClass, constructor_calldata: @Array::, contract_address: ContractAddress, ) -> SyscallResult<(ContractAddress, Span)> { execute_cheatcode_and_deserialize::< 'set_deploy_at_address', (), >(array![contract_address.into()].span()); self.deploy(constructor_calldata) } fn new>(class_hash: T) -> ContractClass { ContractClass { class_hash: class_hash.into() } } } pub trait DeclareResultTrait { /// Gets inner `ContractClass` /// `self` - an instance of the struct `DeclareResult` which is obtained by calling `declare` // Returns the `@ContractClass` fn contract_class(self: @DeclareResult) -> @ContractClass; } impl DeclareResultImpl of DeclareResultTrait { fn contract_class(self: @DeclareResult) -> @ContractClass { match self { DeclareResult::Success(contract_class) => contract_class, DeclareResult::AlreadyDeclared(contract_class) => contract_class, } } } /// Declares a contract /// `contract` - name of a contract as Cairo string. It is a name of the contract (part after mod /// keyword) e.g. "HelloStarknet" /// Returns the `DeclareResult` that encapsulated possible outcomes in the enum: /// - `Success`: Contains the successfully declared `ContractClass`. /// - `AlreadyDeclared`: Contains `ContractClass` and signals that the contract has already been /// declared. pub fn declare(contract: ByteArray) -> Result> { execute_cheatcode_and_deserialize::<'declare'>(byte_array_as_felt_array(@contract).span()) } /// Retrieves a class hash of a contract deployed under the given address /// `contract_address` - target contract address /// Returns the `ClassHash` under given address pub fn get_class_hash(contract_address: ContractAddress) -> ClassHash { execute_cheatcode_and_deserialize::<'get_class_hash'>(array![contract_address.into()].span()) } fn _prepare_calldata( class_hash: @ClassHash, constructor_calldata: @Array::, ) -> Array { let class_hash: felt252 = (*class_hash).into(); let mut inputs: Array = array![class_hash]; constructor_calldata.serialize(ref inputs); inputs } ================================================ FILE: snforge_std/src/cheatcodes/erc20.cairo ================================================ use snforge_std::cheatcodes::storage::{map_entry_address, store}; use starknet::ContractAddress; const STRK_CONTRACT_ADDRESS: felt252 = 0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d; const ETH_CONTRACT_ADDRESS: felt252 = 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7; const BALANCES_VARIABLE_SELECTOR: felt252 = 0x3a4e8ec16e258a799fe707996fd5d21d42b29adc1499a370edf7f809d8c458a; // selector!("ERC20_balances"); #[derive(Drop, Serde, Copy, Debug)] pub struct CustomToken { pub contract_address: ContractAddress, pub balances_variable_selector: felt252, } #[derive(Drop, Copy, Clone, Debug)] pub enum Token { STRK, ETH, Custom: CustomToken, } #[generate_trait] pub impl TokenImpl of TokenTrait { fn contract_address(self: Token) -> ContractAddress { match self { Token::STRK => STRK_CONTRACT_ADDRESS.try_into().unwrap(), Token::ETH => ETH_CONTRACT_ADDRESS.try_into().unwrap(), Token::Custom(CustomToken { contract_address, .. }) => contract_address, } } fn balances_variable_selector(self: Token) -> felt252 { match self { Token::STRK | Token::ETH => BALANCES_VARIABLE_SELECTOR, Token::Custom(CustomToken { balances_variable_selector, .., }) => balances_variable_selector, } } } /// Sets the balance of `token`` for `target`` contract to `new_balance` /// - `target` - address of the contract, which balance you want to modify /// - `new_balance` - new balance value /// - `token` - token for which the balance is being set pub fn set_balance(target: ContractAddress, new_balance: u256, token: Token) { let balance_low_address = map_entry_address( token.balances_variable_selector(), [target.into()].span(), ); let balance_high_address = balance_low_address + 1; store(token.contract_address(), balance_low_address, array![new_balance.low.into()].span()); store(token.contract_address(), balance_high_address, array![new_balance.high.into()].span()); } ================================================ FILE: snforge_std/src/cheatcodes/events.cairo ================================================ use starknet::ContractAddress; use crate::cheatcode::execute_cheatcode_and_deserialize; /// Creates `EventSpy` instance that spies on all events emitted after its creation. pub fn spy_events() -> EventSpy { execute_cheatcode_and_deserialize::<'spy_events'>(array![].span()) } /// Raw event format (as seen via the RPC-API), can be used for asserting the emitted events. #[derive(Drop, Clone, Serde, Debug, PartialEq)] pub struct Event { pub keys: Array, pub data: Array, } /// An event spy structure allowing to get events emitted only after its creation. #[derive(Drop, Serde)] pub struct EventSpy { event_offset: usize, } /// A wrapper structure on an array of events to handle filtering smoothly. #[derive(Drop, Serde, Clone, Debug, PartialEq)] pub struct Events { pub events: Array<(ContractAddress, Event)>, } pub trait EventSpyTrait { /// Gets all events given [`EventSpy`] spies for. fn get_events(ref self: EventSpy) -> Events; } impl EventSpyTraitImpl of EventSpyTrait { fn get_events(ref self: EventSpy) -> Events { execute_cheatcode_and_deserialize::<'get_events'>(array![self.event_offset.into()].span()) } } pub trait EventsFilterTrait { /// Filter events emitted by a given [`ContractAddress`]. fn emitted_by(self: @Events, contract_address: ContractAddress) -> Events; } impl EventsFilterTraitImpl of EventsFilterTrait { fn emitted_by(self: @Events, contract_address: ContractAddress) -> Events { let mut new_events = array![]; for (from, event) in self.events.span() { if *from == contract_address { new_events.append((*from, event.clone())); }; } Events { events: new_events } } } /// Allows to assert the expected events emission (or lack thereof), /// in the scope of [`EventSpy`] structure. pub trait EventSpyAssertionsTrait, +Drop> { fn assert_emitted(ref self: EventSpy, events: @Array<(ContractAddress, T)>); fn assert_not_emitted(ref self: EventSpy, events: @Array<(ContractAddress, T)>); } impl EventSpyAssertionsTraitImpl, +Drop> of EventSpyAssertionsTrait { fn assert_emitted(ref self: EventSpy, events: @Array<(ContractAddress, T)>) { let received_events = self.get_events(); for (from, event) in events.span() { if !received_events.is_emitted(*from, event) { let from: felt252 = (*from).into(); panic!("Event with matching data and keys was not emitted from {}", from); } }; } fn assert_not_emitted(ref self: EventSpy, events: @Array<(ContractAddress, T)>) { let received_events = self.get_events(); for (from, event) in events.span() { if received_events.is_emitted(*from, event) { let from: felt252 = (*from).into(); panic!("Event with matching data and keys was emitted from {}", from); } }; } } pub trait IsEmitted, +Drop> { fn is_emitted(self: @Events, expected_emitted_by: ContractAddress, expected_event: @T) -> bool; } pub impl IsEmittedImpl, +Drop> of IsEmitted { fn is_emitted(self: @Events, expected_emitted_by: ContractAddress, expected_event: @T) -> bool { let expected_event: Event = expected_event.into(); let mut is_emitted = false; for (from, event) in self.events.span() { if from == @expected_emitted_by && event == @expected_event { is_emitted = true; break; }; } return is_emitted; } } impl EventIntoImpl, +Drop> of Into { fn into(self: T) -> Event { let mut keys = array![]; let mut data = array![]; self.append_keys_and_data(ref keys, ref data); Event { keys, data } } } impl EventSnapIntoImpl, +Drop> of Into<@T, Event> { fn into(self: @T) -> Event { let mut keys = array![]; let mut data = array![]; self.append_keys_and_data(ref keys, ref data); Event { keys, data } } } impl EventTraitImpl of starknet::Event { fn append_keys_and_data(self: @Event, ref keys: Array, ref data: Array) { keys.append_span(self.keys.span()); data.append_span(self.data.span()); } fn deserialize(ref keys: Span, ref data: Span) -> Option { Option::None } } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/account_contract_address.cairo ================================================ use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Changes the address of an account which the transaction originates from, for the given contract /// address and span. /// - `contract_address` - instance of `ContractAddress` specifying which contracts to cheat /// - `account_contract_address` - transaction account deployment data to be set /// - `span` - instance of `CheatSpan` specifying the number of contract calls with the cheat /// applied pub fn cheat_account_contract_address( contract_address: ContractAddress, account_contract_address: ContractAddress, span: CheatSpan, ) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .tx_info .account_contract_address = Operation::Start( CheatArguments { value: account_contract_address, span, target: contract_address }, ); cheat_execution_info(execution_info); } /// Changes the address of an account which the transaction originates from. /// - `account_contract_address` - transaction account deployment data to be set pub fn start_cheat_account_contract_address_global(account_contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .tx_info .account_contract_address = Operation::StartGlobal(account_contract_address); cheat_execution_info(execution_info); } /// Cancels the `start_cheat_account_contract_address_global`. pub fn stop_cheat_account_contract_address_global() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.account_contract_address = Operation::StopGlobal; cheat_execution_info(execution_info); } /// Changes the address of an account which the transaction originates from, for the given /// contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `account_contract_address` - transaction account deployment data to be set pub fn start_cheat_account_contract_address( contract_address: ContractAddress, account_contract_address: ContractAddress, ) { cheat_account_contract_address( contract_address, account_contract_address, CheatSpan::Indefinite, ); } /// Cancels the `cheat_account_contract_address` / `start_cheat_account_contract_address` for the /// given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to stop cheating pub fn stop_cheat_account_contract_address(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.account_contract_address = Operation::Stop(contract_address); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/account_deployment_data.cairo ================================================ use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Changes the transaction account deployment data for the given contract address and span. /// - `contract_address` - instance of `ContractAddress` specifying which contracts to cheat /// - `account_deployment_data` - transaction account deployment data to be set /// - `span` - instance of `CheatSpan` specifying the number of contract calls with the cheat /// applied pub fn cheat_account_deployment_data( contract_address: ContractAddress, account_deployment_data: Span, span: CheatSpan, ) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .tx_info .account_deployment_data = Operation::Start( CheatArguments { value: account_deployment_data, span, target: contract_address }, ); cheat_execution_info(execution_info); } /// Changes the transaction account deployment data. /// - `account_deployment_data` - transaction account deployment data to be set pub fn start_cheat_account_deployment_data_global(account_deployment_data: Span) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .tx_info .account_deployment_data = Operation::StartGlobal(account_deployment_data); cheat_execution_info(execution_info); } /// Cancels the `cheat_account_deployment_data_global`. pub fn stop_cheat_account_deployment_data_global() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.account_deployment_data = Operation::StopGlobal; cheat_execution_info(execution_info); } /// Changes the transaction account deployment data for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `account_deployment_data` - transaction account deployment data to be set pub fn start_cheat_account_deployment_data( contract_address: ContractAddress, account_deployment_data: Span, ) { cheat_account_deployment_data(contract_address, account_deployment_data, CheatSpan::Indefinite); } /// Cancels the `cheat_account_deployment_data` / `start_cheat_account_deployment_data` for the /// given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to stop cheating pub fn stop_cheat_account_deployment_data(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.account_deployment_data = Operation::Stop(contract_address); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/block_number.cairo ================================================ use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Changes the block number for the given contract address and span. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `block_number` - block number to be set /// - `span` - instance of `CheatSpan` specifying the number of contract calls with the cheat /// applied pub fn cheat_block_number(contract_address: ContractAddress, block_number: u64, span: CheatSpan) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .block_info .block_number = Operation::Start( CheatArguments { value: block_number, span, target: contract_address }, ); cheat_execution_info(execution_info); } /// Changes the block number. /// - `block_number` - block number to be set pub fn start_cheat_block_number_global(block_number: u64) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.block_info.block_number = Operation::StartGlobal(block_number); cheat_execution_info(execution_info); } /// Cancels the `start_cheat_block_number_global`. pub fn stop_cheat_block_number_global() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.block_info.block_number = Operation::StopGlobal; cheat_execution_info(execution_info); } /// Changes the block number for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `block_number` - block number to be set pub fn start_cheat_block_number(contract_address: ContractAddress, block_number: u64) { cheat_block_number(contract_address, block_number, CheatSpan::Indefinite); } /// Cancels the `cheat_block_number` / `start_cheat_block_number` for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to stop cheating pub fn stop_cheat_block_number(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.block_info.block_number = Operation::Stop(contract_address); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/block_timestamp.cairo ================================================ use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Changes the block timestamp for the given contract address and span. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `block_timestamp` - block timestamp to be set /// - `span` - instance of `CheatSpan` specifying the number of contract calls with the cheat /// applied pub fn cheat_block_timestamp( contract_address: ContractAddress, block_timestamp: u64, span: CheatSpan, ) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .block_info .block_timestamp = Operation::Start( CheatArguments { value: block_timestamp, span, target: contract_address }, ); cheat_execution_info(execution_info); } /// Changes the block timestamp. /// - `block_timestamp` - block timestamp to be set pub fn start_cheat_block_timestamp_global(block_timestamp: u64) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.block_info.block_timestamp = Operation::StartGlobal(block_timestamp); cheat_execution_info(execution_info); } /// Cancels the `start_cheat_block_timestamp_global`. pub fn stop_cheat_block_timestamp_global() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.block_info.block_timestamp = Operation::StopGlobal; cheat_execution_info(execution_info); } /// Changes the block timestamp for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `block_timestamp` - block timestamp to be set pub fn start_cheat_block_timestamp(contract_address: ContractAddress, block_timestamp: u64) { cheat_block_timestamp(contract_address, block_timestamp, CheatSpan::Indefinite); } /// Cancels the `cheat_block_timestamp` / `start_cheat_block_timestamp` for the given /// contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to stop cheating pub fn stop_cheat_block_timestamp(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.block_info.block_timestamp = Operation::Stop(contract_address); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/caller_address.cairo ================================================ use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Changes the caller address for the given contract address and span. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `caller_address` - caller address to be set /// - `span` - instance of `CheatSpan` specifying the number of contract calls with the cheat /// applied pub fn cheat_caller_address( contract_address: ContractAddress, caller_address: ContractAddress, span: CheatSpan, ) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .caller_address = Operation::Start( CheatArguments { value: caller_address, span, target: contract_address }, ); cheat_execution_info(execution_info); } /// Changes the caller address. /// - `caller_address` - caller address to be set pub fn start_cheat_caller_address_global(caller_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.caller_address = Operation::StartGlobal(caller_address); cheat_execution_info(execution_info); } /// Cancels the `start_cheat_caller_address_global`. pub fn stop_cheat_caller_address_global() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.caller_address = Operation::StopGlobal; cheat_execution_info(execution_info); } /// Changes the caller address for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `caller_address` - caller address to be set pub fn start_cheat_caller_address( contract_address: ContractAddress, caller_address: ContractAddress, ) { cheat_caller_address(contract_address, caller_address, CheatSpan::Indefinite); } /// Cancels the `cheat_caller_address` / `start_cheat_caller_address` for the given /// contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to stop cheating pub fn stop_cheat_caller_address(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.caller_address = Operation::Stop(contract_address); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/chain_id.cairo ================================================ use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Changes the transaction chain_id for the given contract address and span. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `chain_id` - transaction chain_id to be set /// - `span` - instance of `CheatSpan` specifying the number of contract calls with the cheat /// applied pub fn cheat_chain_id(contract_address: ContractAddress, chain_id: felt252, span: CheatSpan) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .tx_info .chain_id = Operation::Start(CheatArguments { value: chain_id, span, target: contract_address }); cheat_execution_info(execution_info); } /// Changes the transaction chain_id. /// - `chain_id` - transaction chain_id to be set pub fn start_cheat_chain_id_global(chain_id: felt252) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.chain_id = Operation::StartGlobal(chain_id); cheat_execution_info(execution_info); } /// Cancels the `start_cheat_chain_id_global`. pub fn stop_cheat_chain_id_global() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.chain_id = Operation::StopGlobal; cheat_execution_info(execution_info); } /// Changes the transaction chain_id for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `chain_id` - transaction chain_id to be set pub fn start_cheat_chain_id(contract_address: ContractAddress, chain_id: felt252) { cheat_chain_id(contract_address, chain_id, CheatSpan::Indefinite); } /// Cancels the `cheat_chain_id` / `start_cheat_chain_id` for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to stop cheating pub fn stop_cheat_chain_id(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.chain_id = Operation::Stop(contract_address); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/contract_address.cairo ================================================ //! These cheatcodes are currently used only internally by the `interact_with_state` cheatcode, //! and are specifically intended to cheat on the `TEST_ADDRESS`. //! They are not exposed through the public API in a general form, as there are no known use cases //! that would require them. use crate::test_address; use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Overrides the contract address for the default test address. /// After this function is called, any call to `starknet::get_contract_address()` /// within the test context will return the provided `contract_address`. /// - `contract_address` - The contract address to use for the default test address. pub(crate) fn start_cheat_contract_address(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .contract_address = Operation::Start( CheatArguments { value: contract_address, span: CheatSpan::Indefinite, target: test_address(), }, ); cheat_execution_info(execution_info); } /// Cancels the `start_cheat_contract_address`. pub(crate) fn stop_cheat_contract_address() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.contract_address = Operation::Stop(test_address()); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/fee_data_availability_mode.cairo ================================================ use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Changes the transaction fee data availability mode for the given contract address and span. /// - `contract_address` - instance of `ContractAddress` specifying which contracts to cheat /// - `fee_data_availability_mode` - transaction fee data availability mode to be set /// - `span` - instance of `CheatSpan` specifying the number of contract calls with the cheat /// applied pub fn cheat_fee_data_availability_mode( contract_address: ContractAddress, fee_data_availability_mode: u32, span: CheatSpan, ) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .tx_info .fee_data_availability_mode = Operation::Start( CheatArguments { value: fee_data_availability_mode, span, target: contract_address, }, ); cheat_execution_info(execution_info); } /// Changes the transaction fee data availability mode. /// - `fee_data_availability_mode` - transaction fee data availability mode to be set pub fn start_cheat_fee_data_availability_mode_global(fee_data_availability_mode: u32) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .tx_info .fee_data_availability_mode = Operation::StartGlobal(fee_data_availability_mode); cheat_execution_info(execution_info); } /// Cancels the `start_cheat_fee_data_availability_mode_global`. pub fn stop_cheat_fee_data_availability_mode_global() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.fee_data_availability_mode = Operation::StopGlobal; cheat_execution_info(execution_info); } /// Changes the transaction fee data availability mode for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `fee_data_availability_mode` - transaction fee data availability mode to be set pub fn start_cheat_fee_data_availability_mode( contract_address: ContractAddress, fee_data_availability_mode: u32, ) { cheat_fee_data_availability_mode( contract_address, fee_data_availability_mode, CheatSpan::Indefinite, ); } /// Cancels the `cheat_fee_data_availability_mode` / `start_cheat_fee_data_availability_mode` for /// the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to stop cheating pub fn stop_cheat_fee_data_availability_mode(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.fee_data_availability_mode = Operation::Stop(contract_address); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/max_fee.cairo ================================================ use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Changes the transaction max fee for the given contract address and span. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `max_fee` - transaction max fee to be set /// - `span` - instance of `CheatSpan` specifying the number of contract calls with the cheat /// applied pub fn cheat_max_fee(contract_address: ContractAddress, max_fee: u128, span: CheatSpan) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .tx_info .max_fee = Operation::Start(CheatArguments { value: max_fee, span, target: contract_address }); cheat_execution_info(execution_info); } /// Changes the transaction max fee. /// - `max_fee` - transaction max fee to be set pub fn start_cheat_max_fee_global(max_fee: u128) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.max_fee = Operation::StartGlobal(max_fee); cheat_execution_info(execution_info); } /// Cancels the `start_cheat_max_fee_global`. pub fn stop_cheat_max_fee_global() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.max_fee = Operation::StopGlobal; cheat_execution_info(execution_info); } /// Changes the transaction max fee for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `max_fee` - transaction max fee to be set pub fn start_cheat_max_fee(contract_address: ContractAddress, max_fee: u128) { cheat_max_fee(contract_address, max_fee, CheatSpan::Indefinite); } /// Cancels the `cheat_max_fee` / `start_cheat_max_fee` for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to stop cheating pub fn stop_cheat_max_fee(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.max_fee = Operation::Stop(contract_address); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/nonce.cairo ================================================ use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Changes the transaction nonce for the given contract address and span. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `nonce` - transaction nonce to be set /// - `span` - instance of `CheatSpan` specifying the number of contract calls with the cheat /// applied pub fn cheat_nonce(contract_address: ContractAddress, nonce: felt252, span: CheatSpan) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .tx_info .nonce = Operation::Start(CheatArguments { value: nonce, span, target: contract_address }); cheat_execution_info(execution_info); } /// Changes the transaction nonce. /// - `nonce` - transaction nonce to be set pub fn start_cheat_nonce_global(nonce: felt252) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.nonce = Operation::StartGlobal(nonce); cheat_execution_info(execution_info); } /// Cancels the `start_cheat_nonce_global`. pub fn stop_cheat_nonce_global() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.nonce = Operation::StopGlobal; cheat_execution_info(execution_info); } /// Changes the transaction nonce for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `nonce` - transaction nonce to be set pub fn start_cheat_nonce(contract_address: ContractAddress, nonce: felt252) { cheat_nonce(contract_address, nonce, CheatSpan::Indefinite); } /// Cancels the `cheat_nonce` / `start_cheat_nonce` for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to stop cheating pub fn stop_cheat_nonce(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.nonce = Operation::Stop(contract_address); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/nonce_data_availability_mode.cairo ================================================ use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Changes the transaction nonce data availability mode for the given contract address and span. /// - `contract_address` - instance of `ContractAddress` specifying which contracts to cheat /// - `nonce_data_availability_mode` - transaction nonce data availability mode to be set /// - `span` - instance of `CheatSpan` specifying the number of contract calls with the cheat /// applied pub fn cheat_nonce_data_availability_mode( contract_address: ContractAddress, nonce_data_availability_mode: u32, span: CheatSpan, ) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .tx_info .nonce_data_availability_mode = Operation::Start( CheatArguments { value: nonce_data_availability_mode, span, target: contract_address, }, ); cheat_execution_info(execution_info); } /// Changes the transaction nonce data availability mode. /// - `nonce_data_availability_mode` - transaction nonce data availability mode to be set pub fn start_cheat_nonce_data_availability_mode_global(nonce_data_availability_mode: u32) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .tx_info .nonce_data_availability_mode = Operation::StartGlobal(nonce_data_availability_mode); cheat_execution_info(execution_info); } /// Cancels the `start_cheat_nonce_data_availability_mode_global`. pub fn stop_cheat_nonce_data_availability_mode_global() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.nonce_data_availability_mode = Operation::StopGlobal; cheat_execution_info(execution_info); } /// Changes the transaction nonce data availability mode for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `nonce_data_availability_mode` - transaction nonce data availability mode to be set pub fn start_cheat_nonce_data_availability_mode( contract_address: ContractAddress, nonce_data_availability_mode: u32, ) { cheat_nonce_data_availability_mode( contract_address, nonce_data_availability_mode, CheatSpan::Indefinite, ); } /// Cancels the `cheat_nonce_data_availability_mode` / `start_cheat_nonce_data_availability_mode` /// for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to stop cheating pub fn stop_cheat_nonce_data_availability_mode(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.nonce_data_availability_mode = Operation::Stop(contract_address); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/paymaster_data.cairo ================================================ use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Changes the transaction paymaster data for the given contract address and span. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `paymaster_data` - transaction paymaster data to be set /// - `span` - instance of `CheatSpan` specifying the number of contract calls with the cheat /// applied pub fn cheat_paymaster_data( contract_address: ContractAddress, paymaster_data: Span, span: CheatSpan, ) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .tx_info .paymaster_data = Operation::Start( CheatArguments { value: paymaster_data, span, target: contract_address }, ); cheat_execution_info(execution_info); } /// Changes the transaction paymaster data. /// - `paymaster_data` - transaction paymaster data to be set pub fn start_cheat_paymaster_data_global(paymaster_data: Span) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.paymaster_data = Operation::StartGlobal(paymaster_data); cheat_execution_info(execution_info); } /// Cancels the `start_cheat_paymaster_data_global`. pub fn stop_cheat_paymaster_data_global() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.paymaster_data = Operation::StopGlobal; cheat_execution_info(execution_info); } /// Changes the transaction paymaster data for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `paymaster_data` - transaction paymaster data to be set pub fn start_cheat_paymaster_data( contract_address: ContractAddress, paymaster_data: Span, ) { cheat_paymaster_data(contract_address, paymaster_data, CheatSpan::Indefinite); } /// Cancels the `cheat_paymaster_data` / `start_cheat_paymaster_data` for the given /// contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to stop cheating pub fn stop_cheat_paymaster_data(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.paymaster_data = Operation::Stop(contract_address); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/proof_facts.cairo ================================================ use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Changes the transaction proof facts for the given contract address and span. /// - `contract_address` - instance of `ContractAddress` specifying which contracts to cheat /// - `proof_facts` - transaction proof facts to be set /// - `span` - instance of `CheatSpan` specifying the number of contract calls with the cheat /// applied pub fn cheat_proof_facts( contract_address: ContractAddress, proof_facts: Span, span: CheatSpan, ) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .tx_info .proof_facts = Operation::Start(CheatArguments { value: proof_facts, span, target: contract_address }); cheat_execution_info(execution_info); } /// Changes the transaction proof facts. /// - `proof_facts` - transaction proof facts to be set pub fn start_cheat_proof_facts_global(proof_facts: Span) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.proof_facts = Operation::StartGlobal(proof_facts); cheat_execution_info(execution_info); } /// Cancels the `cheat_proof_facts_global`. pub fn stop_cheat_proof_facts_global() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.proof_facts = Operation::StopGlobal; cheat_execution_info(execution_info); } /// Changes the transaction proof facts for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `proof_facts` - transaction proof facts to be set pub fn start_cheat_proof_facts(contract_address: ContractAddress, proof_facts: Span) { cheat_proof_facts(contract_address, proof_facts, CheatSpan::Indefinite); } /// Cancels the `cheat_proof_facts` / `start_cheat_proof_facts` for the /// given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to stop cheating pub fn stop_cheat_proof_facts(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.proof_facts = Operation::Stop(contract_address); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/resource_bounds.cairo ================================================ use starknet::ResourcesBounds; use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Changes the transaction resource bounds for the given contract address and span. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `resource_bounds` - transaction resource bounds to be set /// - `span` - instance of `CheatSpan` specifying the number of contract calls with the cheat /// applied pub fn cheat_resource_bounds( contract_address: ContractAddress, resource_bounds: Span, span: CheatSpan, ) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .tx_info .resource_bounds = Operation::Start( CheatArguments { value: resource_bounds, span, target: contract_address }, ); cheat_execution_info(execution_info); } /// Changes the transaction resource bounds. /// - `resource_bounds` - transaction resource bounds to be set pub fn start_cheat_resource_bounds_global(resource_bounds: Span) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.resource_bounds = Operation::StartGlobal(resource_bounds); cheat_execution_info(execution_info); } /// Cancels the `start_cheat_resource_bounds_global`. pub fn stop_cheat_resource_bounds_global() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.resource_bounds = Operation::StopGlobal; cheat_execution_info(execution_info); } /// Changes the transaction resource bounds for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `resource_bounds` - transaction resource bounds to be set pub fn start_cheat_resource_bounds( contract_address: ContractAddress, resource_bounds: Span, ) { cheat_resource_bounds(contract_address, resource_bounds, CheatSpan::Indefinite); } /// Cancels the `cheat_resource_bounds` / `start_cheat_resource_bounds` for the given /// contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to stop cheating pub fn stop_cheat_resource_bounds(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.resource_bounds = Operation::Stop(contract_address); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/sequencer_address.cairo ================================================ use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Changes the sequencer address for the given contract address and span. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `sequencer_address` - sequencer address to be set /// - `span` - instance of `CheatSpan` specifying the number of contract calls with the cheat /// applied pub fn cheat_sequencer_address( contract_address: ContractAddress, sequencer_address: ContractAddress, span: CheatSpan, ) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .block_info .sequencer_address = Operation::Start( CheatArguments { value: sequencer_address, span, target: contract_address }, ); cheat_execution_info(execution_info); } /// Changes the sequencer address. /// - `sequencer_address` - sequencer address to be set pub fn start_cheat_sequencer_address_global(sequencer_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.block_info.sequencer_address = Operation::StartGlobal(sequencer_address); cheat_execution_info(execution_info); } /// Cancels the `start_cheat_sequencer_address_global`. pub fn stop_cheat_sequencer_address_global() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.block_info.sequencer_address = Operation::StopGlobal; cheat_execution_info(execution_info); } /// Changes the sequencer address for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `sequencer_address` - sequencer address to be set pub fn start_cheat_sequencer_address( contract_address: ContractAddress, sequencer_address: ContractAddress, ) { cheat_sequencer_address(contract_address, sequencer_address, CheatSpan::Indefinite); } /// Cancels the `cheat_sequencer_address` / `start_cheat_sequencer_address` for the given /// contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to stop cheating pub fn stop_cheat_sequencer_address(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.block_info.sequencer_address = Operation::Stop(contract_address); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/signature.cairo ================================================ use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Changes the transaction signature for the given contract address and span. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `signature` - transaction signature to be set /// - `span` - instance of `CheatSpan` specifying the number of contract calls with the cheat /// applied pub fn cheat_signature( contract_address: ContractAddress, signature: Span, span: CheatSpan, ) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .tx_info .signature = Operation::Start(CheatArguments { value: signature, span, target: contract_address }); cheat_execution_info(execution_info); } /// Changes the transaction signature. /// - `signature` - transaction signature to be set pub fn start_cheat_signature_global(signature: Span) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.signature = Operation::StartGlobal(signature); cheat_execution_info(execution_info); } /// Cancels the `start_cheat_signature_global`. pub fn stop_cheat_signature_global() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.signature = Operation::StopGlobal; cheat_execution_info(execution_info); } /// Changes the transaction signature for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `signature` - transaction signature to be set pub fn start_cheat_signature(contract_address: ContractAddress, signature: Span) { cheat_signature(contract_address, signature, CheatSpan::Indefinite); } /// Cancels the `cheat_signature` / `start_cheat_signature` for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to stop cheating pub fn stop_cheat_signature(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.signature = Operation::Stop(contract_address); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/tip.cairo ================================================ use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Changes the transaction tip for the given contract address and span. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `tip` - transaction tip to be set /// - `span` - instance of `CheatSpan` specifying the number of contract calls with the cheat /// applied pub fn cheat_tip(contract_address: ContractAddress, tip: u128, span: CheatSpan) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .tx_info .tip = Operation::Start(CheatArguments { value: tip, span, target: contract_address }); cheat_execution_info(execution_info); } /// Changes the transaction tip. /// - `tip` - transaction tip to be set pub fn start_cheat_tip_global(tip: u128) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.tip = Operation::StartGlobal(tip); cheat_execution_info(execution_info); } /// Cancels the `start_cheat_tip_global`. pub fn stop_cheat_tip_global() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.tip = Operation::StopGlobal; cheat_execution_info(execution_info); } /// Changes the transaction tip for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `tip` - transaction tip to be set pub fn start_cheat_tip(contract_address: ContractAddress, tip: u128) { cheat_tip(contract_address, tip, CheatSpan::Indefinite); } /// Cancels the `cheat_tip` / `start_cheat_tip` for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to stop cheating pub fn stop_cheat_tip(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.tip = Operation::Stop(contract_address); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/transaction_hash.cairo ================================================ use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Changes the transaction hash for the given contract address and span. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `transaction_hash` - transaction hash to be set /// - `span` - instance of `CheatSpan` specifying the number of contract calls with the cheat /// applied pub fn cheat_transaction_hash( contract_address: ContractAddress, transaction_hash: felt252, span: CheatSpan, ) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .tx_info .transaction_hash = Operation::Start( CheatArguments { value: transaction_hash, span, target: contract_address }, ); cheat_execution_info(execution_info); } /// Changes the transaction hash. /// - `transaction_hash` - transaction hash to be set pub fn start_cheat_transaction_hash_global(transaction_hash: felt252) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.transaction_hash = Operation::StartGlobal(transaction_hash); cheat_execution_info(execution_info); } /// Cancels the `start_cheat_transaction_hash_global`. pub fn stop_cheat_transaction_hash_global() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.transaction_hash = Operation::StopGlobal; cheat_execution_info(execution_info); } /// Changes the transaction hash for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `transaction_hash` - transaction hash to be set pub fn start_cheat_transaction_hash(contract_address: ContractAddress, transaction_hash: felt252) { cheat_transaction_hash(contract_address, transaction_hash, CheatSpan::Indefinite); } /// Cancels the `cheat_transaction_hash` / `start_cheat_transaction_hash` for the given /// contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to stop cheating pub fn stop_cheat_transaction_hash(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.transaction_hash = Operation::Stop(contract_address); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info/version.cairo ================================================ use super::{ CheatArguments, CheatSpan, ContractAddress, ExecutionInfoMock, Operation, cheat_execution_info, }; /// Changes the transaction version for the given contract address and span. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `version` - transaction version to be set /// - `span` - instance of `CheatSpan` specifying the number of contract calls with the cheat /// applied pub fn cheat_transaction_version( contract_address: ContractAddress, version: felt252, span: CheatSpan, ) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info .tx_info .version = Operation::Start(CheatArguments { value: version, span, target: contract_address }); cheat_execution_info(execution_info); } /// Changes the transaction version. /// - `version` - transaction version to be set pub fn start_cheat_transaction_version_global(version: felt252) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.version = Operation::StartGlobal(version); cheat_execution_info(execution_info); } /// Cancels the `start_cheat_transaction_version_global`. pub fn stop_cheat_transaction_version_global() { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.version = Operation::StopGlobal; cheat_execution_info(execution_info); } /// Changes the transaction version for the given contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to cheat /// - `version` - transaction version to be set pub fn start_cheat_transaction_version(contract_address: ContractAddress, version: felt252) { cheat_transaction_version(contract_address, version, CheatSpan::Indefinite); } /// Cancels the `cheat_transaction_version` / `start_cheat_transaction_version` for the given /// contract_address. /// - `contract_address` - instance of `ContractAddress` specifying which contract to stop cheating pub fn stop_cheat_transaction_version(contract_address: ContractAddress) { let mut execution_info: ExecutionInfoMock = Default::default(); execution_info.tx_info.version = Operation::Stop(contract_address); cheat_execution_info(execution_info); } ================================================ FILE: snforge_std/src/cheatcodes/execution_info.cairo ================================================ use snforge_std::cheatcodes::CheatSpan; use starknet::{ContractAddress, ResourcesBounds}; use crate::cheatcode::execute_cheatcode_and_deserialize; pub mod account_contract_address; pub mod account_deployment_data; pub mod block_number; pub mod block_timestamp; pub mod caller_address; pub mod chain_id; #[doc(hidden)] pub mod contract_address; pub mod fee_data_availability_mode; pub mod max_fee; pub mod nonce; pub mod nonce_data_availability_mode; pub mod paymaster_data; pub mod proof_facts; pub mod resource_bounds; pub mod sequencer_address; pub mod signature; pub mod tip; pub mod transaction_hash; pub mod version; #[derive(Serde, Drop, Copy)] pub(crate) struct CheatArguments { pub(crate) value: T, pub(crate) span: CheatSpan, pub(crate) target: ContractAddress, } #[derive(Serde, Drop, Copy)] pub(crate) enum Operation { StartGlobal: T, Start: CheatArguments, Stop: ContractAddress, StopGlobal, Retain, } /// A structure used for setting individual fields in `TxInfo` /// All fields are wrapped into `Operation`, meaning that the field will be: /// - `Retain` - unchanged /// - `Start` - changed for given contract and span /// - `Stop` - reset to the initial value for given contract and span /// - `StartGlobal` - changed for all contracts until overridden or stopped /// - `StopGlobal` - reset to the initial value for all contracts #[derive(Copy, Drop, Serde)] struct TxInfoMock { version: Operation, account_contract_address: Operation, max_fee: Operation, signature: Operation>, transaction_hash: Operation, chain_id: Operation, nonce: Operation, // starknet::info::v2::TxInfo fields resource_bounds: Operation>, tip: Operation, paymaster_data: Operation>, nonce_data_availability_mode: Operation, fee_data_availability_mode: Operation, account_deployment_data: Operation>, proof_facts: Operation>, } impl TxInfoMockImpl of Default { /// Returns a default object initialized with Operation::Retain for each field /// Useful for setting only a few of fields instead of all of them fn default() -> TxInfoMock { TxInfoMock { version: Operation::Retain, account_contract_address: Operation::Retain, max_fee: Operation::Retain, signature: Operation::Retain, transaction_hash: Operation::Retain, chain_id: Operation::Retain, nonce: Operation::Retain, resource_bounds: Operation::Retain, tip: Operation::Retain, paymaster_data: Operation::Retain, nonce_data_availability_mode: Operation::Retain, fee_data_availability_mode: Operation::Retain, account_deployment_data: Operation::Retain, proof_facts: Operation::Retain, } } } /// A structure used for setting individual fields in `BlockInfo` /// All fields are wrapped into `Operation`, meaning that the field will be: /// - `Retain` - unchanged /// - `Start` - changed for given contract and span /// - `Stop` - reset to the initial value for given contract and span /// - `StartGlobal` - changed for all contracts until overridden or stopped /// - `StopGlobal` - reset to the initial value for all contracts #[derive(Copy, Drop, Serde)] struct BlockInfoMock { block_number: Operation, block_timestamp: Operation, sequencer_address: Operation, } impl BlockInfoMockImpl of Default { /// Returns a default object initialized with Operation::Retain for each field /// Useful for setting only a few of fields instead of all of them fn default() -> BlockInfoMock { BlockInfoMock { block_number: Operation::Retain, block_timestamp: Operation::Retain, sequencer_address: Operation::Retain, } } } /// A structure used for setting individual fields in `ExecutionInfo` /// All fields are wrapped into `Operation`, meaning that the field will be: /// - `Retain` - unchanged /// - `Start` - changed for given contract and span /// - `Stop` - reset to the initial value for given contract and span /// - `StartGlobal` - changed for all contracts until overridden or stopped /// - `StopGlobal` - reset to the initial value for all contracts #[derive(Copy, Drop, Serde)] struct ExecutionInfoMock { block_info: BlockInfoMock, tx_info: TxInfoMock, caller_address: Operation, contract_address: Operation, } impl ExecutionInfoMockImpl of Default { /// Returns a default object initialized with Operation::Retain for each field /// Useful for setting only a few of fields instead of all of them fn default() -> ExecutionInfoMock { ExecutionInfoMock { block_info: Default::default(), tx_info: Default::default(), caller_address: Operation::Retain, contract_address: Operation::Retain, } } } /// Changes `ExecutionInfo` returned by `get_execution_info()` /// - `execution_info_mock` - a struct with same structure as `ExecutionInfo` (returned by /// `get_execution_info()`) fn cheat_execution_info(execution_info_mock: ExecutionInfoMock) { let mut inputs = array![]; execution_info_mock.serialize(ref inputs); execute_cheatcode_and_deserialize::<'cheat_execution_info', ()>(inputs.span()); } ================================================ FILE: snforge_std/src/cheatcodes/generate_arg.cairo ================================================ use crate::cheatcode::execute_cheatcode_and_deserialize; // Generates a random number that is used for creating data for fuzz tests pub fn generate_arg, +Drop, +Into>(min_value: T, max_value: T) -> T { execute_cheatcode_and_deserialize::< 'generate_arg', >(array![min_value.into(), max_value.into()].span()) } ================================================ FILE: snforge_std/src/cheatcodes/generate_random_felt.cairo ================================================ use crate::cheatcode::execute_cheatcode_and_deserialize; /// Generates a random felt value /// /// Returns a random felt within the range of 0 and 2^252 - 1 pub fn generate_random_felt() -> felt252 { execute_cheatcode_and_deserialize::<'generate_random_felt'>(array![].span()) } ================================================ FILE: snforge_std/src/cheatcodes/l1_handler.cairo ================================================ use starknet::{ContractAddress, SyscallResult}; use crate::cheatcode::execute_cheatcode_and_deserialize; #[derive(Drop, Clone)] pub struct L1Handler { target: ContractAddress, selector: felt252, } pub trait L1HandlerTrait { fn new(target: ContractAddress, selector: felt252) -> L1Handler; fn execute(self: L1Handler, from_address: felt252, payload: Span) -> SyscallResult<()>; } impl L1HandlerImpl of L1HandlerTrait { /// `target` - The target starknet contract address /// `selector` - Selector of a `#[l1_handler]` function. Can be acquired with /// `selector!("function_handler_name")` macro Returns a structure referring to a L1 handler /// function fn new(target: ContractAddress, selector: felt252) -> L1Handler { L1Handler { target, selector } } /// Mocks L1 -> L2 message from Ethereum handled by the given L1 handler function /// `self` - `L1Handler` structure referring to a L1 handler function /// `from_address` - Ethereum address of the contract that you want to be the message sender /// `payload` - The handlers' function arguments serialized with `Serde` /// Returns () or panic data if it failed fn execute( self: L1Handler, from_address: felt252, payload: Span, ) -> SyscallResult<()> { let mut inputs: Array = array![ self.target.into(), self.selector, from_address.into(), ]; payload.serialize(ref inputs); execute_cheatcode_and_deserialize::<'l1_handler_execute'>(inputs.span()) } } ================================================ FILE: snforge_std/src/cheatcodes/message_to_l1.cairo ================================================ use starknet::{ContractAddress, EthAddress}; use crate::cheatcode::execute_cheatcode_and_deserialize; /// Creates `MessageToL1Spy` instance that spies on all messages sent to L1 pub fn spy_messages_to_l1() -> MessageToL1Spy { execute_cheatcode_and_deserialize::<'spy_messages_to_l1'>(array![].span()) } /// Raw message to L1 format (as seen via the RPC-API), can be used for asserting the sent messages. #[derive(Drop, Clone, Serde)] pub struct MessageToL1 { /// An ethereum address where the message is destined to go pub to_address: EthAddress, /// Actual payload which will be delivered to L1 contract pub payload: Array, } /// A message spy structure allowing to get messages emitted only after its creation. #[derive(Drop, Serde)] pub struct MessageToL1Spy { message_offset: usize, } /// A wrapper structure on an array of messages to handle filtering smoothly. #[derive(Drop, Serde)] pub struct MessagesToL1 { pub messages: Array<(ContractAddress, MessageToL1)>, } pub trait MessageToL1SpyTrait { /// Gets all messages given [`MessageToL1Spy`] spies for. fn get_messages(ref self: MessageToL1Spy) -> MessagesToL1; } impl MessageToL1SpyTraitImpl of MessageToL1SpyTrait { fn get_messages(ref self: MessageToL1Spy) -> MessagesToL1 { execute_cheatcode_and_deserialize::< 'get_messages_to_l1', >(array![self.message_offset.into()].span()) } } pub trait MessageToL1FilterTrait { /// Filter messages emitted by a sender of a given [`ContractAddress`] fn sent_by(self: @MessagesToL1, contract_address: ContractAddress) -> MessagesToL1; /// Filter messages emitted by a receiver of a given ethereum address fn sent_to(self: @MessagesToL1, to_address: EthAddress) -> MessagesToL1; } impl MessageToL1FilterTraitImpl of MessageToL1FilterTrait { fn sent_by(self: @MessagesToL1, contract_address: ContractAddress) -> MessagesToL1 { let mut counter = 0; let mut new_messages = array![]; while counter < self.messages.len() { let (sent_by, msg) = self.messages.at(counter); if *sent_by == contract_address { new_messages.append((*sent_by, msg.clone())); } counter += 1; } MessagesToL1 { messages: new_messages } } fn sent_to(self: @MessagesToL1, to_address: EthAddress) -> MessagesToL1 { let mut counter = 0; let mut new_messages = array![]; while counter < self.messages.len() { let (sent_by, msg) = self.messages.at(counter); if *msg.to_address == to_address { new_messages.append((*sent_by, msg.clone())); } counter += 1; } MessagesToL1 { messages: new_messages } } } /// Allows to assert the expected sent messages (or lack thereof), /// in the scope of [`MessageToL1Spy`] structure. pub trait MessageToL1SpyAssertionsTrait { fn assert_sent(ref self: MessageToL1Spy, messages: @Array<(ContractAddress, MessageToL1)>); fn assert_not_sent(ref self: MessageToL1Spy, messages: @Array<(ContractAddress, MessageToL1)>); } impl MessageToL1SpyAssertionsTraitImpl of MessageToL1SpyAssertionsTrait { fn assert_sent(ref self: MessageToL1Spy, messages: @Array<(ContractAddress, MessageToL1)>) { let mut i = 0; let sent_messages = self.get_messages(); while i < messages.len() { let (from, message) = messages.at(i); let sent = is_sent(@sent_messages, from, message); if !sent { let from: felt252 = (*from).into(); panic!("Message with matching data and receiver was not emitted from {}", from); } i += 1; }; } fn assert_not_sent(ref self: MessageToL1Spy, messages: @Array<(ContractAddress, MessageToL1)>) { let mut i = 0; let sent_messages = self.get_messages(); while i < messages.len() { let (from, message) = messages.at(i); let emitted = is_sent(@sent_messages, from, message); if emitted { let from: felt252 = (*from).into(); panic!("Message with matching data and receiver was sent from {}", from); } i += 1; }; } } fn is_sent( messages: @MessagesToL1, expected_sent_by: @ContractAddress, expected_message: @MessageToL1, ) -> bool { let mut i = 0; let mut is_emitted = false; while i < messages.messages.len() { let (from, message) = messages.messages.at(i); if from == expected_sent_by && message.payload == expected_message.payload && message.to_address == expected_message.to_address { is_emitted = true; break; } i += 1; } return is_emitted; } ================================================ FILE: snforge_std/src/cheatcodes/storage.cairo ================================================ use starknet::{ContractAddress, StorageAddress}; use crate::cheatcode::execute_cheatcode_and_deserialize; use crate::cheatcodes::execution_info::contract_address::{ start_cheat_contract_address, stop_cheat_contract_address, }; fn validate_storage_address_felt(storage_address_felt: felt252) { let result: Option = storage_address_felt.try_into(); match result { Option::Some(_) => {}, // Panics in order not to leave inconsistencies in the state Option::None(()) => panic!("storage_address out of range {}", storage_address_felt), } } fn store_felt252(target: ContractAddress, storage_address: felt252, value: felt252) { validate_storage_address_felt(storage_address); let inputs = array![target.into(), storage_address.into(), value]; execute_cheatcode_and_deserialize::<'store', ()>(inputs.span()); } fn load_felt252(target: ContractAddress, storage_address: felt252) -> felt252 { validate_storage_address_felt(storage_address); let inputs = array![target.into(), storage_address]; execute_cheatcode_and_deserialize::<'load'>(inputs.span()) } /// Stores felts from `serialized_value` in `target` contract's storage, starting at /// `storage_address`. /// - `target` - address of the contract, which storage you want to modify /// - `storage_address` - offset of the data in the contract's storage /// - `serialized_value` - a sequence of felts that will be inserted starting at `storage_address` pub fn store(target: ContractAddress, storage_address: felt252, serialized_value: Span) { let mut offset: usize = 0; while offset != serialized_value.len() { store_felt252(target, storage_address + offset.into(), *serialized_value.at(offset)); offset += 1; } } /// Loads `size` felts from `target` contract's storage into an `Array`, starting at /// `storage_address`. /// - `target` - address of the contract, which storage you want to modify /// - `storage_address` - offset of the data in the contract's storage /// - `size` - how many felts will be loaded into the result `Array` pub fn load(target: ContractAddress, storage_address: felt252, size: felt252) -> Array { let mut output_array: Array = array![]; let mut offset: usize = 0; while offset.into() != size { let loaded = load_felt252(target, storage_address + offset.into()); output_array.append(loaded); offset += 1; } output_array } pub fn map_entry_address(map_selector: felt252, keys: Span) -> felt252 { let mut inputs = array![map_selector]; keys.serialize(ref inputs); execute_cheatcode_and_deserialize::<'map_entry_address'>(inputs.span()) } pub fn interact_with_state, impl func: core::ops::FnOnce, +Drop>( contract_address: ContractAddress, f: F, ) -> func::Output { start_cheat_contract_address(contract_address); let res = f(); stop_cheat_contract_address(); res } ================================================ FILE: snforge_std/src/cheatcodes.cairo ================================================ use starknet::{ClassHash, ContractAddress}; use super::cheatcode::execute_cheatcode_and_deserialize; pub mod block_hash; pub mod contract_class; pub mod erc20; pub mod events; pub mod execution_info; pub mod generate_arg; pub mod generate_random_felt; pub mod l1_handler; pub mod message_to_l1; pub mod storage; /// Enum used to specify how long the target should be cheated for. #[derive(Copy, Drop, Serde, PartialEq, Clone, Debug)] pub enum CheatSpan { /// Applies the cheatcode indefinitely, until the cheat is canceled manually (e.g. using /// `stop_cheat_block_timestamp`). Indefinite, /// Applies the cheatcode for a specified number of calls to the target, /// after which the cheat is canceled (or until the cheat is canceled manually). TargetCalls: NonZero, } pub fn test_selector() -> felt252 { // Result of selector!("TEST_CONTRACT_SELECTOR") since `selector!` macro requires dependency on // `starknet`. 655947323460646800722791151288222075903983590237721746322261907338444055163 } pub fn test_address() -> ContractAddress { 469394814521890341860918960550914.try_into().expect('Test address should be valid') } /// Mocks contract call to a `function_selector` of a contract at the given address, for `n_times` /// first calls that are made to the contract. /// A call to function `function_selector` will return data provided in `ret_data` argument. /// An address with no contract can be mocked as well. /// An entrypoint that is not present on the deployed contract is also possible to mock. /// Note that the function is not meant for mocking internal calls - it works only for contract /// entry points. /// - `contract_address` - target contract address /// - `function_selector` - hashed name of the target function (can be obtained with `selector!` /// macro) /// - `ret_data` - data to return by the function `function_selector` /// - `n_times` - number of calls to mock the function for pub fn mock_call, impl TDestruct: Destruct>( contract_address: ContractAddress, function_selector: felt252, ret_data: T, n_times: u32, ) { assert!(n_times > 0, "cannot `mock_call` 0 times, `n_times` argument must be greater than 0"); let contract_address_felt: felt252 = contract_address.into(); let mut inputs = array![contract_address_felt, function_selector]; CheatSpan::TargetCalls(n_times.try_into().expect('`n_times` must be > 0')) .serialize(ref inputs); let mut ret_data_arr = ArrayTrait::new(); ret_data.serialize(ref ret_data_arr); ret_data_arr.serialize(ref inputs); execute_cheatcode_and_deserialize::<'mock_call', ()>(inputs.span()); } /// Mocks contract call to a function of a contract at the given address, indefinitely. /// See `mock_call` for comprehensive definition of how it can be used. /// - `contract_address` - targeted contracts' address /// - `function_selector` - hashed name of the target function (can be obtained with `selector!` /// macro) /// - `ret_data` - data to be returned by the function pub fn start_mock_call, impl TDestruct: Destruct>( contract_address: ContractAddress, function_selector: felt252, ret_data: T, ) { let contract_address_felt: felt252 = contract_address.into(); let mut inputs = array![contract_address_felt, function_selector]; CheatSpan::Indefinite.serialize(ref inputs); let mut ret_data_arr = ArrayTrait::new(); ret_data.serialize(ref ret_data_arr); ret_data_arr.serialize(ref inputs); execute_cheatcode_and_deserialize::<'mock_call', ()>(inputs.span()); } /// Cancels the `mock_call` / `start_mock_call` for the function with given name and contract /// address. /// - `contract_address` - targeted contracts' address /// - `function_selector` - hashed name of the target function (can be obtained with `selector!` /// macro) pub fn stop_mock_call(contract_address: ContractAddress, function_selector: felt252) { let contract_address_felt: felt252 = contract_address.into(); execute_cheatcode_and_deserialize::< 'stop_mock_call', (), >(array![contract_address_felt, function_selector].span()); } #[derive(Drop, Serde, PartialEq, Debug)] pub enum ReplaceBytecodeError { /// Means that the contract does not exist, and thus bytecode cannot be replaced ContractNotDeployed, /// Means that the given class for replacement is not declared UndeclaredClassHash, } /// Replaces class for given contract address. /// The `new_class` hash has to be declared in order for the replacement class to execute the code, /// when interacting with the contract. /// - `contract` - address specifying which address will be replaced /// - `new_class` - class hash, that will be used now for given address /// Returns `Result::Ok` if the replacement succeeded, and a `ReplaceBytecodeError` with appropriate /// error type otherwise pub fn replace_bytecode( contract: ContractAddress, new_class: ClassHash, ) -> Result<(), ReplaceBytecodeError> { execute_cheatcode_and_deserialize::< 'replace_bytecode', >(array![contract.into(), new_class.into()].span()) } ================================================ FILE: snforge_std/src/config_types.cairo ================================================ #[derive(Drop, Serde)] pub struct AvailableResourceBoundsConfig { pub l1_gas: felt252, pub l1_data_gas: felt252, pub l2_gas: felt252, } #[derive(Drop, Serde)] pub enum AvailableGasConfig { MaxGas: felt252, MaxResourceBounds: AvailableResourceBoundsConfig, } #[derive(Drop, Serde)] pub enum BlockId { BlockTag, BlockHash: felt252, BlockNumber: felt252, } #[derive(Drop, Serde)] pub struct InlineForkConfig { pub url: ByteArray, pub block: BlockId, } #[derive(Drop, Serde)] pub struct OverriddenForkConfig { pub name: ByteArray, pub block: BlockId, } #[derive(Drop, Serde)] pub enum ForkConfig { Inline: InlineForkConfig, Named: ByteArray, Overridden: OverriddenForkConfig, } #[derive(Drop, Serde)] pub struct FuzzerConfig { pub runs: Option, pub seed: Option, } #[derive(Drop, Serde)] pub enum Expected { ShortString: felt252, ByteArray: ByteArray, Array: Array, Any, } #[derive(Drop, Serde)] pub struct ShouldPanicConfig { pub expected: Expected, } #[derive(Drop, Serde)] pub struct IgnoreConfig { pub is_ignored: bool, } #[derive(Drop, Serde)] pub struct PredeployedContractsConfig { pub is_disabled: bool, } ================================================ FILE: snforge_std/src/env/env_vars.cairo ================================================ use crate::byte_array::byte_array_as_felt_array; use crate::cheatcode::execute_cheatcode; /// Reads an environment variable, without parsing it /// `name` - name of an environment variable /// Returns the read array of felts pub fn var(name: ByteArray) -> Array { execute_cheatcode::<'var'>(byte_array_as_felt_array(@name).span()).into() } ================================================ FILE: snforge_std/src/env.cairo ================================================ mod env_vars; pub use env_vars::var; ================================================ FILE: snforge_std/src/fs/file_operations.cairo ================================================ use crate::byte_array::byte_array_as_felt_array; use crate::cheatcode::execute_cheatcode; #[derive(Drop, Clone)] pub struct File { path: ByteArray, } pub trait FileTrait { /// Creates a file struct used for reading json / text /// `path` - a path to file in ByteArray form, relative to the package root fn new(path: ByteArray) -> File; } impl FileTraitImpl of FileTrait { fn new(path: ByteArray) -> File { File { path } } } /// `file` - a `File` struct to read text data from /// Returns an array of felts read from the file, panics if read was not possible pub fn read_txt(file: @File) -> Array { execute_cheatcode::<'read_txt'>(byte_array_as_felt_array(file.path).span()).into() } /// `file` - a `File` struct to read json data from /// Returns an array of felts read from the file, panics if read was not possible, or json was /// incorrect pub fn read_json(file: @File) -> Array { execute_cheatcode::<'read_json'>(byte_array_as_felt_array(file.path).span()).into() } pub trait FileParser> { /// Reads from the text file and tries to deserialize the result into given type with `Serde` /// `file` - File instance /// Returns an instance of `T` if deserialization was possible fn parse_txt(file: @File) -> Option; /// Reads from the json file and tries to deserialize the result into given type with `Serde` /// `file` - File instance /// Returns an instance of `T` if deserialization was possible fn parse_json(file: @File) -> Option; } impl FileParserImpl> of FileParser { fn parse_txt(file: @File) -> Option { let mut content = execute_cheatcode::< 'read_txt', >(byte_array_as_felt_array(file.path).span()); Serde::::deserialize(ref content) } fn parse_json(file: @File) -> Option { let mut content = execute_cheatcode::< 'read_json', >(byte_array_as_felt_array(file.path).span()); Serde::::deserialize(ref content) } } ================================================ FILE: snforge_std/src/fs.cairo ================================================ mod file_operations; pub use file_operations::File; pub use file_operations::{FileParser, FileTrait, read_json, read_txt}; ================================================ FILE: snforge_std/src/fuzzable.cairo ================================================ use core::fmt::Debug; use starknet::ContractAddress; pub use super::cheatcodes::generate_arg::generate_arg; const MAX_FELT: felt252 = 0x800000000000011000000000000000000000000000000000000000000000000; pub trait Fuzzable> { fn blank() -> T; fn generate() -> T; } impl FuzzableFelt of Fuzzable { fn blank() -> felt252 { 0x0 } fn generate() -> felt252 { generate_arg(0x0, MAX_FELT) } } mod nums { use core::num::traits::{Bounded, Zero}; use super::{Debug, Fuzzable, generate_arg}; pub impl FuzzableNum< T, +Zero, +Bounded, +Drop, +Serde, +Into, +Debug, > of Fuzzable { fn blank() -> T { Zero::::zero() } fn generate() -> T { generate_arg(Bounded::::MIN, Bounded::::MAX) } } } pub impl FuzzableU8 = nums::FuzzableNum; pub impl FuzzableU16 = nums::FuzzableNum; pub impl FuzzableU32 = nums::FuzzableNum; pub impl FuzzableU64 = nums::FuzzableNum; pub impl FuzzableU128 = nums::FuzzableNum; pub impl FuzzableI8 = nums::FuzzableNum; pub impl FuzzableI16 = nums::FuzzableNum; pub impl FuzzableI32 = nums::FuzzableNum; pub impl FuzzableI64 = nums::FuzzableNum; pub impl FuzzableI128 = nums::FuzzableNum; pub impl FuzzableU256 of Fuzzable { fn blank() -> u256 { 0 } fn generate() -> u256 { let mut serialized: Span = array![ Fuzzable::::generate().into(), Fuzzable::::generate().into(), ] .span(); Serde::deserialize(ref serialized).unwrap() } } pub impl FuzzableByteArray1000ASCII of Fuzzable { fn blank() -> ByteArray { "" } // Generates a random string of length 0 to 1000 fn generate() -> ByteArray { let mut ba_len: u32 = generate_arg(0, 1000); let mut ba = ""; while ba_len > 0 { // Limit only to printable characters with ASCII codes 32-126 let letter = Fuzzable::::generate() % 95; ba.append_byte(letter + 32); ba_len = ba_len - 1; } ba } } pub impl FuzzableBool of Fuzzable { fn blank() -> bool { false } fn generate() -> bool { generate_arg(0, 1) == 1 } } pub impl FuzzableContractAddress of Fuzzable { fn blank() -> ContractAddress { 0x1.try_into().expect('0x1 should be a valid address') } fn generate() -> ContractAddress { // [0, 2 ** 251) let arg = generate_arg( 0x0, 3618502788666131106986593281521497120414687020801267626233049500247285301247, ); arg.try_into().expect('Should be a valid address') } } ================================================ FILE: snforge_std/src/lib.cairo ================================================ pub mod cheatcodes; pub use cheatcodes::CheatSpan; pub use cheatcodes::block_hash::cheat_block_hash; pub use cheatcodes::block_hash::{ start_cheat_block_hash, start_cheat_block_hash_global, stop_cheat_block_hash, stop_cheat_block_hash_global, }; pub use cheatcodes::contract_class::declare; pub use cheatcodes::contract_class::{ ContractClass, ContractClassTrait, DeclareResult, DeclareResultTrait, get_class_hash, }; pub use cheatcodes::erc20::set_balance; pub use cheatcodes::erc20::{CustomToken, Token, TokenImpl, TokenTrait}; pub use cheatcodes::events::Event; pub use cheatcodes::events::{ EventSpy, EventSpyAssertionsTrait, EventSpyTrait, EventsFilterTrait, IsEmitted, spy_events, }; pub use cheatcodes::execution_info::account_contract_address::{ cheat_account_contract_address, start_cheat_account_contract_address, start_cheat_account_contract_address_global, stop_cheat_account_contract_address, stop_cheat_account_contract_address_global, }; pub use cheatcodes::execution_info::account_deployment_data::{ cheat_account_deployment_data, start_cheat_account_deployment_data, start_cheat_account_deployment_data_global, stop_cheat_account_deployment_data, stop_cheat_account_deployment_data_global, }; pub use cheatcodes::execution_info::block_number::{ cheat_block_number, start_cheat_block_number, start_cheat_block_number_global, stop_cheat_block_number, stop_cheat_block_number_global, }; pub use cheatcodes::execution_info::block_timestamp::{ cheat_block_timestamp, start_cheat_block_timestamp, start_cheat_block_timestamp_global, stop_cheat_block_timestamp, stop_cheat_block_timestamp_global, }; pub use cheatcodes::execution_info::caller_address::cheat_caller_address; pub use cheatcodes::execution_info::caller_address::{ start_cheat_caller_address, start_cheat_caller_address_global, stop_cheat_caller_address, stop_cheat_caller_address_global, }; pub use cheatcodes::execution_info::chain_id::{ cheat_chain_id, start_cheat_chain_id, start_cheat_chain_id_global, stop_cheat_chain_id, stop_cheat_chain_id_global, }; pub use cheatcodes::execution_info::fee_data_availability_mode::{ cheat_fee_data_availability_mode, start_cheat_fee_data_availability_mode, start_cheat_fee_data_availability_mode_global, stop_cheat_fee_data_availability_mode, stop_cheat_fee_data_availability_mode_global, }; pub use cheatcodes::execution_info::max_fee::{ cheat_max_fee, start_cheat_max_fee, start_cheat_max_fee_global, stop_cheat_max_fee, stop_cheat_max_fee_global, }; pub use cheatcodes::execution_info::nonce::{ cheat_nonce, start_cheat_nonce, start_cheat_nonce_global, stop_cheat_nonce, stop_cheat_nonce_global, }; pub use cheatcodes::execution_info::nonce_data_availability_mode::{ cheat_nonce_data_availability_mode, start_cheat_nonce_data_availability_mode, start_cheat_nonce_data_availability_mode_global, stop_cheat_nonce_data_availability_mode, stop_cheat_nonce_data_availability_mode_global, }; pub use cheatcodes::execution_info::paymaster_data::{ cheat_paymaster_data, start_cheat_paymaster_data, start_cheat_paymaster_data_global, stop_cheat_paymaster_data, stop_cheat_paymaster_data_global, }; pub use cheatcodes::execution_info::proof_facts::{ cheat_proof_facts, start_cheat_proof_facts, start_cheat_proof_facts_global, stop_cheat_proof_facts, stop_cheat_proof_facts_global, }; pub use cheatcodes::execution_info::resource_bounds::{ cheat_resource_bounds, start_cheat_resource_bounds, start_cheat_resource_bounds_global, stop_cheat_resource_bounds, stop_cheat_resource_bounds_global, }; pub use cheatcodes::execution_info::sequencer_address::{ cheat_sequencer_address, start_cheat_sequencer_address, start_cheat_sequencer_address_global, stop_cheat_sequencer_address, stop_cheat_sequencer_address_global, }; pub use cheatcodes::execution_info::signature::{ cheat_signature, start_cheat_signature, start_cheat_signature_global, stop_cheat_signature, stop_cheat_signature_global, }; pub use cheatcodes::execution_info::tip::{ cheat_tip, start_cheat_tip, start_cheat_tip_global, stop_cheat_tip, stop_cheat_tip_global, }; pub use cheatcodes::execution_info::transaction_hash::{ cheat_transaction_hash, start_cheat_transaction_hash, start_cheat_transaction_hash_global, stop_cheat_transaction_hash, stop_cheat_transaction_hash_global, }; pub use cheatcodes::execution_info::version::{ cheat_transaction_version, start_cheat_transaction_version, start_cheat_transaction_version_global, stop_cheat_transaction_version, stop_cheat_transaction_version_global, }; pub use cheatcodes::generate_random_felt::generate_random_felt; pub use cheatcodes::l1_handler::L1Handler; pub use cheatcodes::l1_handler::L1HandlerTrait; pub use cheatcodes::message_to_l1::{ MessageToL1, MessageToL1FilterTrait, MessageToL1Spy, MessageToL1SpyAssertionsTrait, MessageToL1SpyTrait, spy_messages_to_l1, }; pub use cheatcodes::storage::store; pub use cheatcodes::storage::{interact_with_state, load, map_entry_address}; pub use cheatcodes::{ ReplaceBytecodeError, mock_call, replace_bytecode, start_mock_call, stop_mock_call, test_address, test_selector, }; pub mod byte_array; mod cheatcode; mod config_types; pub mod env; pub mod fs; pub mod fuzzable; pub mod signature; pub mod testing; pub mod trace; #[doc(hidden)] pub mod _internals { pub use cheatcode::{is_config_run, save_fuzzer_arg}; use super::cheatcode; pub use super::config_types; } ================================================ FILE: snforge_std/src/signature/secp256k1_curve.cairo ================================================ use core::option::OptionTrait; use core::serde::Serde; use snforge_std::signature::{KeyPair, KeyPairTrait, SignerTrait, VerifierTrait}; use starknet::SyscallResultTrait; use starknet::secp256_trait::{Secp256PointTrait, Secp256Trait, is_valid_signature}; use starknet::secp256k1::Secp256k1Point; use crate::cheatcode::execute_cheatcode_and_deserialize; use super::SignError; pub type Secp256k1CurveKeyPair = KeyPair; pub impl Secp256k1CurveKeyPairImpl of KeyPairTrait { fn generate() -> Secp256k1CurveKeyPair { let (secret_key, pk_x, pk_y) = execute_cheatcode_and_deserialize::< 'generate_ecdsa_keys', (u256, u256, u256), >(array!['Secp256k1'].span()); let public_key = Secp256Trait::secp256_ec_new_syscall(pk_x, pk_y).unwrap_syscall().unwrap(); KeyPair { secret_key, public_key } } fn from_secret_key(secret_key: u256) -> Secp256k1CurveKeyPair { if (secret_key == 0_u256 || secret_key >= Secp256Trait::::get_curve_size()) { core::panic_with_felt252('invalid secret_key'); } let generator = Secp256Trait::get_generator_point(); let public_key = Secp256PointTrait::mul(generator, secret_key).unwrap_syscall(); KeyPair { secret_key, public_key } } } pub impl Secp256k1CurveSignerImpl of SignerTrait { fn sign(self: Secp256k1CurveKeyPair, message_hash: u256) -> Result<(u256, u256), SignError> { let mut input = array!['Secp256k1']; self.secret_key.serialize(ref input); message_hash.serialize(ref input); execute_cheatcode_and_deserialize::<'ecdsa_sign_message'>(input.span()) } } pub impl Secp256k1CurveVerifierImpl of VerifierTrait { fn verify(self: Secp256k1CurveKeyPair, message_hash: u256, signature: (u256, u256)) -> bool { let (r, s) = signature; is_valid_signature::(message_hash, r, s, self.public_key) } } ================================================ FILE: snforge_std/src/signature/secp256r1_curve.cairo ================================================ use snforge_std::signature::{KeyPair, KeyPairTrait, SignerTrait, VerifierTrait}; use starknet::SyscallResultTrait; use starknet::secp256_trait::{Secp256PointTrait, Secp256Trait, is_valid_signature}; use starknet::secp256r1::Secp256r1Point; use crate::cheatcode::execute_cheatcode_and_deserialize; use super::SignError; pub type Secp256r1CurveKeyPair = KeyPair; pub impl Secp256r1CurveKeyPairImpl of KeyPairTrait { fn generate() -> Secp256r1CurveKeyPair { let (secret_key, pk_x, pk_y) = execute_cheatcode_and_deserialize::< 'generate_ecdsa_keys', (u256, u256, u256), >(array!['Secp256r1'].span()); let public_key = Secp256Trait::secp256_ec_new_syscall(pk_x, pk_y).unwrap_syscall().unwrap(); KeyPair { secret_key, public_key } } fn from_secret_key(secret_key: u256) -> Secp256r1CurveKeyPair { if (secret_key == 0_u256 || secret_key >= Secp256Trait::::get_curve_size()) { core::panic_with_felt252('invalid secret_key'); } let generator = Secp256Trait::get_generator_point(); let public_key = Secp256PointTrait::mul(generator, secret_key).unwrap_syscall(); KeyPair { secret_key, public_key } } } pub impl Secp256r1CurveSignerImpl of SignerTrait { fn sign(self: Secp256r1CurveKeyPair, message_hash: u256) -> Result<(u256, u256), SignError> { let mut input = array!['Secp256r1']; self.secret_key.serialize(ref input); message_hash.serialize(ref input); execute_cheatcode_and_deserialize::<'ecdsa_sign_message'>(input.span()) } } pub impl Secp256r1CurveVerifierImpl of VerifierTrait { fn verify(self: Secp256r1CurveKeyPair, message_hash: u256, signature: (u256, u256)) -> bool { let (r, s) = signature; is_valid_signature::(message_hash, r, s, self.public_key) } } ================================================ FILE: snforge_std/src/signature/stark_curve.cairo ================================================ use core::ec::{EcPoint, EcPointImpl, stark_curve}; use core::ecdsa::check_ecdsa_signature; use snforge_std::signature::{KeyPair, KeyPairTrait, SignerTrait, VerifierTrait}; use crate::cheatcode::execute_cheatcode_and_deserialize; use super::SignError; pub type StarkCurveKeyPair = KeyPair; pub impl StarkCurveKeyPairImpl of KeyPairTrait { fn generate() -> StarkCurveKeyPair { let (secret_key, public_key) = execute_cheatcode_and_deserialize::< 'generate_stark_keys', (felt252, felt252), >(array![].span()); KeyPair { secret_key, public_key } } fn from_secret_key(secret_key: felt252) -> StarkCurveKeyPair { if (secret_key == 0) { core::panic_with_felt252('invalid secret_key'); } let generator = EcPointImpl::new(stark_curve::GEN_X, stark_curve::GEN_Y).unwrap(); let public_key: EcPoint = EcPointImpl::mul(generator, secret_key); let (pk_x, _pk_y) = public_key.try_into().unwrap().coordinates(); KeyPair { secret_key, public_key: pk_x } } } pub impl StarkCurveSignerImpl of SignerTrait { fn sign( self: StarkCurveKeyPair, message_hash: felt252, ) -> Result<(felt252, felt252), SignError> { execute_cheatcode_and_deserialize::< 'stark_sign_message', >(array![self.secret_key, message_hash].span()) } } pub impl StarkCurveVerifierImpl of VerifierTrait { fn verify( self: StarkCurveKeyPair, message_hash: felt252, signature: (felt252, felt252), ) -> bool { let (r, s) = signature; check_ecdsa_signature(message_hash, self.public_key, r, s) } } ================================================ FILE: snforge_std/src/signature.cairo ================================================ pub mod secp256k1_curve; pub mod secp256r1_curve; pub mod stark_curve; #[derive(Copy, Drop)] pub struct KeyPair { /// A key that is used for signing the messages pub secret_key: SK, /// A (x, y) point on the elliptic curve used for verification of the signature pub public_key: PK, } pub trait KeyPairTrait { /// Generates the private and public keys using the built-in random generator fn generate() -> KeyPair; /// Derives the KeyPair (`secret_key` + `public_key`) using `secret_key` fn from_secret_key(secret_key: SK) -> KeyPair; } pub trait SignerTrait { /// Signs given message hash /// `self` - KeyPair used for signing /// `message_hash` - input to sign bounded by the curve type (u256 for 256bit curves, felt252 /// for StarkCurve) /// Returns the signature components (usually r,s tuple) or error fn sign(self: T, message_hash: H) -> Result; } pub trait VerifierTrait { /// `self` - KeyPair used for verifying /// `message_hash` - input to verify bounded by the curve type (u256 for 256bit curves, felt252 /// for StarkCurve) /// `signature` - the signature components (usually r,s tuple) /// Returns a boolean representing the validity of the signature fn verify(self: T, message_hash: H, signature: U) -> bool; } #[derive(Copy, Drop, Serde, PartialEq)] pub enum SignError { InvalidSecretKey, HashOutOfRange, } ================================================ FILE: snforge_std/src/testing.cairo ================================================ use crate::cheatcode::execute_cheatcode_and_deserialize; /// Gets the current step during test execution pub fn get_current_vm_step() -> u32 { execute_cheatcode_and_deserialize::<'get_current_vm_step', u32>(array![].span()) } ================================================ FILE: snforge_std/src/trace.cairo ================================================ use starknet::ContractAddress; use crate::cheatcode::execute_cheatcode_and_deserialize; /// Tree-like structure which contains all of the starknet calls and sub-calls along with the /// results #[derive(Drop, Serde, PartialEq, Clone, Debug)] pub struct CallTrace { pub entry_point: CallEntryPoint, /// All the calls that happened in the scope of `entry_point` pub nested_calls: Array, pub result: CallResult, } /// A single function entry point summary #[derive(Drop, Serde, PartialEq, Clone, Debug)] pub struct CallEntryPoint { pub entry_point_type: EntryPointType, /// Hashed selector of the invoked function pub entry_point_selector: felt252, /// Serialized arguments calldata pub calldata: Array, /// Contract address targeted by the call pub contract_address: ContractAddress, /// Address that the call originates from pub caller_address: ContractAddress, pub call_type: CallType, } /// Type of the function being invoked #[derive(Drop, Serde, PartialEq, Clone, Debug)] pub enum EntryPointType { /// Constructor of a contract Constructor, /// Contract interface entry point External, /// An entrypoint for handling messages from L1 L1Handler, } /// Denotes type of the call #[derive(Drop, Serde, PartialEq, Clone, Debug)] pub enum CallType { /// Regular call Call, /// Library call Delegate, } /// Result of a contract or a library call #[derive(Drop, Serde, PartialEq, Clone, Debug)] pub enum CallResult { /// A successful call with it's result Success: Array, /// A failed call along with it's panic data Failure: CallFailure, } /// Represents a pre-processed failure of a call #[derive(Drop, Serde, PartialEq, Clone, Debug)] pub enum CallFailure { /// Contains raw panic data Panic: Array, /// Contains panic data in parsed form, if parsing is applicable Error: ByteArray, } /// Returns current call trace of the test, up to the last call made to a contract pub fn get_call_trace() -> CallTrace { execute_cheatcode_and_deserialize::<'get_call_trace'>(array![].span()) } use core::fmt::{Debug, Display, Error, Formatter}; impl DisplayCallResult of Display { fn fmt(self: @CallResult, ref f: Formatter) -> Result<(), Error> { match self { CallResult::Success(val) => { write!(f, "Success: ")?; Debug::fmt(val, ref f)?; }, CallResult::Failure(call_failure) => { write!(f, "Failure: ")?; match call_failure { CallFailure::Panic(val) => { Debug::fmt(val, ref f)?; }, CallFailure::Error(msg) => { Display::fmt(msg, ref f)?; }, }; }, } Result::Ok(()) } } impl DisplayEntryPointType of Display { fn fmt(self: @EntryPointType, ref f: Formatter) -> Result<(), Error> { let str: ByteArray = match self { EntryPointType::Constructor => "Constructor", EntryPointType::External => "External", EntryPointType::L1Handler => "L1 Handler", }; f.buffer.append(@str); Result::Ok(()) } } impl DisplayCallType of Display { fn fmt(self: @CallType, ref f: Formatter) -> Result<(), Error> { let str: ByteArray = match self { CallType::Call => "Call", CallType::Delegate => "Delegate", }; f.buffer.append(@str); Result::Ok(()) } } impl DisplayCallTrace of Display { fn fmt(self: @CallTrace, ref f: Formatter) -> Result<(), Error> { Display::fmt(@IndentedCallTrace { struct_ref: self, base_indents: 0 }, ref f).unwrap(); Result::Ok(()) } } #[derive(Drop)] struct Indented { struct_ref: @T, base_indents: u8, } type IndentedEntryPoint = Indented; type IndentedCallTraceArray = Indented>; type IndentedCallTrace = Indented; type IndentedCallResult = Indented; impl DisplayIndentedCallTrace of Display> { fn fmt(self: @Indented, ref f: Formatter) -> Result<(), Error> { Display::fmt( @IndentedEntryPoint { base_indents: *self.base_indents, struct_ref: *self.struct_ref.entry_point, }, ref f, ) .unwrap(); write!(f, "\n").unwrap(); write_indents_to_formatter(*self.base_indents, ref f); write!(f, "Nested Calls: [").unwrap(); if (*self.struct_ref.nested_calls).len() > 0 { write!(f, "\n").unwrap(); Display::fmt( @IndentedCallTraceArray { base_indents: (*self.base_indents) + 1, struct_ref: *self.struct_ref.nested_calls, }, ref f, ) .unwrap(); write!(f, "\n").unwrap(); write_indents_to_formatter(*self.base_indents, ref f); } write!(f, "]").unwrap(); write!(f, "\n").unwrap(); Display::fmt( @IndentedCallResult { base_indents: *self.base_indents, struct_ref: *self.struct_ref.result, }, ref f, ) .unwrap(); Result::Ok(()) } } impl DisplayIndentedCallTraceArray of Display>> { fn fmt(self: @Indented>, ref f: Formatter) -> Result<(), Error> { let mut i: u32 = 0; let trace_len = (*self.struct_ref).len(); while i < trace_len { write_indents_to_formatter(*self.base_indents, ref f); write!(f, "(\n").unwrap(); Display::fmt( @IndentedCallTrace { base_indents: *self.base_indents + 1, struct_ref: (*self.struct_ref)[i], }, ref f, ) .unwrap(); write!(f, "\n").unwrap(); write_indents_to_formatter(*self.base_indents, ref f); write!(f, ")").unwrap(); i = i + 1; if i != trace_len { write!(f, ",\n").unwrap(); } } Result::Ok(()) } } impl DisplayIndentedEntryPoint of Display> { fn fmt(self: @Indented, ref f: Formatter) -> Result<(), Error> { write_indents_to_formatter(*self.base_indents, ref f); write!(f, "Entry point type: ")?; Display::fmt(*self.struct_ref.entry_point_type, ref f)?; write!(f, "\n")?; write_indents_to_formatter(*self.base_indents, ref f); write!(f, "Selector: ")?; Display::fmt(*self.struct_ref.entry_point_selector, ref f)?; write!(f, "\n")?; write_indents_to_formatter(*self.base_indents, ref f); write!(f, "Calldata: ")?; Debug::fmt(*self.struct_ref.calldata, ref f)?; write!(f, "\n")?; write_indents_to_formatter(*self.base_indents, ref f); write!(f, "Storage address: ")?; Debug::fmt(*self.struct_ref.contract_address, ref f)?; write!(f, "\n")?; write_indents_to_formatter(*self.base_indents, ref f); write!(f, "Caller address: ")?; Debug::fmt(*self.struct_ref.caller_address, ref f)?; write!(f, "\n")?; write_indents_to_formatter(*self.base_indents, ref f); write!(f, "Call type: ")?; Display::fmt(*self.struct_ref.call_type, ref f)?; Result::Ok(()) } } impl DisplayIndentedCallResult of Display> { fn fmt(self: @Indented, ref f: Formatter) -> Result<(), Error> { write_indents_to_formatter(*self.base_indents, ref f); write!(f, "Call Result: ")?; Display::fmt(*self.struct_ref, ref f)?; Result::Ok(()) } } fn write_indents_to_formatter(indents: u8, ref f: Formatter) { let mut i: u8 = 0; while i < indents { write!(f, " ").unwrap(); i = i + 1; } } ================================================ FILE: snforge_templates/balance_contract/src/lib.cairo ================================================ /// Interface representing `HelloContract`. /// This interface allows modification and retrieval of the contract balance. #[starknet::interface] pub trait IHelloStarknet { /// Increase contract balance. fn increase_balance(ref self: TContractState, amount: felt252); /// Retrieve contract balance. fn get_balance(self: @TContractState) -> felt252; } /// Simple contract for managing balance. #[starknet::contract] mod HelloStarknet { use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] struct Storage { balance: felt252, } #[abi(embed_v0)] impl HelloStarknetImpl of super::IHelloStarknet { fn increase_balance(ref self: ContractState, amount: felt252) { assert(amount != 0, 'Amount cannot be 0'); self.balance.write(self.balance.read() + amount); } fn get_balance(self: @ContractState) -> felt252 { self.balance.read() } } } ================================================ FILE: snforge_templates/balance_contract/tests/test_contract.cairo ================================================ use starknet::ContractAddress; use snforge_std::{declare, ContractClassTrait, DeclareResultTrait}; use {{ PROJECT_NAME }}::IHelloStarknetSafeDispatcher; use {{ PROJECT_NAME }}::IHelloStarknetSafeDispatcherTrait; use {{ PROJECT_NAME }}::IHelloStarknetDispatcher; use {{ PROJECT_NAME }}::IHelloStarknetDispatcherTrait; fn deploy_contract(name: ByteArray) -> ContractAddress { let contract = declare(name).unwrap().contract_class(); let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); contract_address } #[test] fn test_increase_balance() { let contract_address = deploy_contract("HelloStarknet"); let dispatcher = IHelloStarknetDispatcher { contract_address }; let balance_before = dispatcher.get_balance(); assert(balance_before == 0, 'Invalid balance'); dispatcher.increase_balance(42); let balance_after = dispatcher.get_balance(); assert(balance_after == 42, 'Invalid balance'); } #[test] #[feature("safe_dispatcher")] fn test_cannot_increase_balance_with_zero_value() { let contract_address = deploy_contract("HelloStarknet"); let safe_dispatcher = IHelloStarknetSafeDispatcher { contract_address }; let balance_before = safe_dispatcher.get_balance().unwrap(); assert(balance_before == 0, 'Invalid balance'); match safe_dispatcher.increase_balance(0) { Result::Ok(_) => core::panic_with_felt252('Should have panicked'), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'Amount cannot be 0', *panic_data.at(0)); } }; } ================================================ FILE: snforge_templates/cairo_program/src/lib.cairo ================================================ fn main() -> u32 { fib(16) } fn fib(mut n: u32) -> u32 { let mut a: u32 = 0; let mut b: u32 = 1; while n != 0 { n = n - 1; let temp = b; b = a + b; a = temp; }; a } #[cfg(test)] mod tests { use super::fib; #[test] fn it_works() { assert(fib(16) == 987, 'it works!'); } } ================================================ FILE: snforge_templates/erc20_contract/src/lib.cairo ================================================ pub mod mock_erc20; pub mod token_sender; ================================================ FILE: snforge_templates/erc20_contract/src/mock_erc20.cairo ================================================ /// Example ERC20 token contract created with openzeppelin dependency. /// Full guide and documentation can be found at: /// https://docs.openzeppelin.com/contracts-cairo/1.0.0/guides/erc20-supply #[starknet::contract] pub mod MockERC20 { use openzeppelin_token::erc20::{DefaultConfig, ERC20Component, ERC20HooksEmptyImpl}; use starknet::ContractAddress; /// Declare the ERC20 component for this contract. /// This allows the contract to inherit ERC20 functionalities. component!(path: ERC20Component, storage: erc20, event: ERC20Event); /// Define ERC20 public interface. #[abi(embed_v0)] impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; /// Define internal implementation, allowing internal modifications like minting. impl ERC20InternalImpl = ERC20Component::InternalImpl; #[storage] struct Storage { #[substorage(v0)] erc20: ERC20Component::Storage, } #[event] #[derive(Drop, starknet::Event)] enum Event { #[flat] ERC20Event: ERC20Component::Event, } #[constructor] fn constructor(ref self: ContractState, initial_supply: u256, recipient: ContractAddress) { let name = "MockToken"; let symbol = "MTK"; /// Initialize the contract by setting the token name and symbol. self.erc20.initializer(name, symbol); /// Create `initial_supply` amount of tokens and assigns them to `recipient`. self.erc20.mint(recipient, initial_supply); } } ================================================ FILE: snforge_templates/erc20_contract/src/token_sender.cairo ================================================ use starknet::ContractAddress; #[derive(Drop, Serde, Copy)] pub struct TransferRequest { pub recipient: ContractAddress, pub amount: u256, } /// Interface representing `TokenSender` contract functionality. #[starknet::interface] pub trait ITokenSender { /// Function to send tokens to multiple recipients in a single transaction. /// - `token_address` - The address of the token contract /// - `transfer_list` - The list of transfers to perform fn multisend( ref self: TContractState, token_address: ContractAddress, transfer_list: Array, ); } #[starknet::contract] pub mod TokenSender { use openzeppelin_interfaces::erc20::{IERC20Dispatcher, IERC20DispatcherTrait}; use starknet::{ContractAddress, get_caller_address, get_contract_address}; use super::TransferRequest; #[event] #[derive(Drop, starknet::Event)] pub enum Event { TransferSent: TransferSent, } #[derive(Drop, starknet::Event)] pub struct TransferSent { #[key] pub recipient: ContractAddress, pub token_address: ContractAddress, pub amount: u256, } #[constructor] fn constructor(ref self: ContractState) {} #[storage] struct Storage {} #[abi(embed_v0)] impl TokenSender of super::ITokenSender { fn multisend( ref self: ContractState, token_address: ContractAddress, transfer_list: Array, ) { // Create an ERC20 dispatcher to interact with the given token contract. let erc20 = IERC20Dispatcher { contract_address: token_address }; // Compute total amount to be transferred. let mut total_amount: u256 = 0; for t in transfer_list.span() { total_amount += *t.amount; }; // Transfer the total amount from the caller to this contract. erc20.transfer_from(get_caller_address(), get_contract_address(), total_amount); // Distribute tokens to each recipient in the transfer list. for t in transfer_list.span() { erc20.transfer(*t.recipient, *t.amount); self .emit( TransferSent { recipient: *t.recipient, token_address: token_address, amount: *t.amount, }, ); }; } } } ================================================ FILE: snforge_templates/erc20_contract/tests/test_erc20.cairo ================================================ use openzeppelin_interfaces::erc20::{IERC20Dispatcher, IERC20DispatcherTrait}; use openzeppelin_token::erc20::ERC20Component; use snforge_std::{ CheatSpan, ContractClassTrait, DeclareResultTrait, EventSpyAssertionsTrait, cheat_caller_address, declare, spy_events, }; use starknet::ContractAddress; const STRK_TOKEN_ADDRESS: ContractAddress = 0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d .try_into() .unwrap(); const sender_account: ContractAddress = 1.try_into().unwrap(); const target_account: ContractAddress = 2.try_into().unwrap(); const INITIAL_SUPPLY: u256 = 10_000_000_000; fn setup() -> ContractAddress { let erc20_class_hash = declare("MockERC20").unwrap().contract_class(); let mut calldata = ArrayTrait::new(); INITIAL_SUPPLY.serialize(ref calldata); sender_account.serialize(ref calldata); let (contract_address, _) = erc20_class_hash.deploy(@calldata).unwrap(); contract_address } #[test] fn test_get_balance() { let contract_address = setup(); let erc20 = IERC20Dispatcher { contract_address }; assert!(erc20.balance_of(sender_account) == INITIAL_SUPPLY, "Balance should be > 0"); } #[test] fn test_transfer() { let contract_address = setup(); let erc20 = IERC20Dispatcher { contract_address }; let balance_before = erc20.balance_of(target_account); assert!(balance_before == 0, "Invalid balance"); cheat_caller_address(contract_address, sender_account, CheatSpan::TargetCalls(1)); let transfer_value: u256 = 100; erc20.transfer(target_account, transfer_value); let balance_after = erc20.balance_of(target_account); assert!(balance_after == transfer_value, "No value transferred"); } #[test] #[fork("SEPOLIA_LATEST", block_number: 61804)] fn test_fork_transfer() { let erc20 = IERC20Dispatcher { contract_address: STRK_TOKEN_ADDRESS }; let owner_account: ContractAddress = 0x04337e199aa6a8959aeb2a6afcd2f82609211104191a041e7b9ba2f4039768f0 .try_into() .unwrap(); let balance_before = erc20.balance_of(target_account); assert!(balance_before == 0, "Invalid balance"); cheat_caller_address(STRK_TOKEN_ADDRESS, owner_account, CheatSpan::TargetCalls(1)); let transfer_value: u256 = 100; erc20.transfer(target_account, transfer_value); let balance_after = erc20.balance_of(target_account); assert!(balance_after == transfer_value, "No value transferred"); } #[test] fn test_transfer_event() { let contract_address = setup(); let erc20 = IERC20Dispatcher { contract_address }; cheat_caller_address(contract_address, sender_account, CheatSpan::TargetCalls(1)); let mut spy = spy_events(); let transfer_value: u256 = 100; erc20.transfer(target_account, transfer_value); spy .assert_emitted( @array![ ( contract_address, ERC20Component::Event::Transfer( ERC20Component::Transfer { from: sender_account, to: target_account, value: transfer_value, }, ), ), ], ); } #[test] #[should_panic(expected: ('ERC20: insufficient balance',))] fn should_panic_transfer() { let contract_address = setup(); let erc20 = IERC20Dispatcher { contract_address }; let balance_before = erc20.balance_of(target_account); assert!(balance_before == 0, "Invalid balance"); cheat_caller_address(contract_address, sender_account, CheatSpan::TargetCalls(1)); let transfer_value: u256 = INITIAL_SUPPLY + 1; erc20.transfer(target_account, transfer_value); } ================================================ FILE: snforge_templates/erc20_contract/tests/test_token_sender.cairo ================================================ use openzeppelin_interfaces::erc20::{IERC20Dispatcher, IERC20DispatcherTrait}; use snforge_std::{ CheatSpan, ContractClassTrait, DeclareResultTrait, EventSpyAssertionsTrait, cheat_caller_address, declare, spy_events, }; use starknet::ContractAddress; use {{ PROJECT_NAME }}::token_sender::{ ITokenSenderDispatcher, ITokenSenderDispatcherTrait, TokenSender, TransferRequest, }; const INITIAL_SUPPLY: u256 = 10_000_000_000; const sender_account: ContractAddress = 1.try_into().unwrap(); const target_account: ContractAddress = 2.try_into().unwrap(); fn setup() -> (ContractAddress, ContractAddress) { let erc20_class_hash = declare("MockERC20").unwrap().contract_class(); let mut calldata = ArrayTrait::new(); INITIAL_SUPPLY.serialize(ref calldata); sender_account.serialize(ref calldata); let (erc20_address, _) = erc20_class_hash.deploy(@calldata).unwrap(); let token_sender_class_hash = declare("TokenSender").unwrap().contract_class(); let mut calldata = ArrayTrait::new(); let (token_sender_address, _) = token_sender_class_hash.deploy(@calldata).unwrap(); (erc20_address, token_sender_address) } #[test] fn test_single_send() { let (erc20_address, token_sender_address) = setup(); let erc20 = IERC20Dispatcher { contract_address: erc20_address }; assert!(erc20.balance_of(sender_account) == INITIAL_SUPPLY, "Balance should be > 0"); cheat_caller_address(erc20_address, sender_account, CheatSpan::TargetCalls(1)); let transfer_value: u256 = 100; erc20.approve(token_sender_address, transfer_value * 2); assert!( erc20.allowance(sender_account, token_sender_address) == transfer_value * 2, "Allowance not set", ); let token_sender = ITokenSenderDispatcher { contract_address: token_sender_address }; let request = TransferRequest { recipient: target_account, amount: transfer_value }; let mut transfer_list = ArrayTrait::::new(); transfer_list.append(request); cheat_caller_address(token_sender_address, sender_account, CheatSpan::TargetCalls(1)); token_sender.multisend(erc20_address, transfer_list); let balance_after = erc20.balance_of(target_account); assert!(balance_after == transfer_value, "Balance should be > 0"); } #[test] #[fuzzer] fn test_single_send_fuzz(transfer_value: u256) { let (erc20_address, token_sender_address) = setup(); let erc20 = IERC20Dispatcher { contract_address: erc20_address }; assert!(erc20.balance_of(sender_account) == INITIAL_SUPPLY, "Balance should be > 0"); cheat_caller_address(erc20_address, sender_account, CheatSpan::TargetCalls(1)); let transfer_value: u256 = 100; erc20.approve(token_sender_address, transfer_value * 2); assert!( erc20.allowance(sender_account, token_sender_address) == transfer_value * 2, "Allowance not set", ); let token_sender = ITokenSenderDispatcher { contract_address: token_sender_address }; let request = TransferRequest { recipient: target_account, amount: transfer_value }; let mut transfer_list = ArrayTrait::::new(); transfer_list.append(request); let mut spy = spy_events(); cheat_caller_address(token_sender_address, sender_account, CheatSpan::TargetCalls(1)); token_sender.multisend(erc20_address, transfer_list); spy .assert_emitted( @array![ ( token_sender_address, TokenSender::Event::TransferSent( TokenSender::TransferSent { recipient: target_account, token_address: erc20_address, amount: transfer_value, }, ), ), ], ); let balance_after = erc20.balance_of(target_account); assert!(balance_after == transfer_value, "Balance should be > 0"); } #[test] fn test_multisend() { let (erc20_address, token_sender_address) = setup(); let erc20 = IERC20Dispatcher { contract_address: erc20_address }; let other_target_account = 3.try_into().unwrap(); assert!(erc20.balance_of(sender_account) == INITIAL_SUPPLY, "Balance should be > 0"); cheat_caller_address(erc20_address, sender_account, CheatSpan::TargetCalls(1)); let transfer_value: u256 = 100; erc20.approve(token_sender_address, transfer_value * 2); assert!( erc20.allowance(sender_account, token_sender_address) == transfer_value * 2, "Allowance not set", ); let token_sender = ITokenSenderDispatcher { contract_address: token_sender_address }; let request_1 = TransferRequest { recipient: target_account, amount: transfer_value }; let request_2 = TransferRequest { recipient: other_target_account, amount: transfer_value }; let mut transfer_list = ArrayTrait::::new(); transfer_list.append(request_1); transfer_list.append(request_2); cheat_caller_address(token_sender_address, sender_account, CheatSpan::TargetCalls(1)); token_sender.multisend(erc20_address, transfer_list); let balance_after = erc20.balance_of(target_account); assert!(balance_after == transfer_value, "Balance should be > 0"); let balance_after = erc20.balance_of(other_target_account); assert!(balance_after == transfer_value, "Balance should be > 0"); }