This is page 10 of 103. Use http://codebase.md/cyfrin/aderyn?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .cargo
│ └── config.toml
├── .git-blame-ignore-revs
├── .gitattributes
├── .github
│ ├── images
│ │ ├── aderyn_logo.png
│ │ ├── poweredbycyfrinblack.png
│ │ └── poweredbycyfrinblue.png
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.md
│ │ ├── false_positive_issue.md
│ │ └── feature_request.md
│ └── workflows
│ ├── cargo.yml
│ ├── dependencies.yml
│ ├── release.yml
│ ├── reports.yml
│ └── toml.yml
├── .gitignore
├── .gitmodules
├── .vscode
│ └── settings.json
├── aderyn
│ ├── Cargo.toml
│ ├── oranda.json
│ ├── README.md
│ ├── src
│ │ ├── birdsong.rs
│ │ ├── completions.rs
│ │ ├── lib.rs
│ │ ├── lsp.rs
│ │ ├── main.rs
│ │ ├── mcp.rs
│ │ └── panic.rs
│ └── templates
│ └── aderyn.toml
├── aderyn_core
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── README.md
│ ├── src
│ │ ├── ast
│ │ │ ├── ast_nodes.rs
│ │ │ ├── ast.rs
│ │ │ ├── impls
│ │ │ │ ├── ctx
│ │ │ │ │ ├── utils.rs
│ │ │ │ │ └── workspace.rs
│ │ │ │ ├── ctx.rs
│ │ │ │ ├── disp
│ │ │ │ │ ├── blocks.rs
│ │ │ │ │ ├── contracts.rs
│ │ │ │ │ ├── enumerations.rs
│ │ │ │ │ ├── errors.rs
│ │ │ │ │ ├── events.rs
│ │ │ │ │ ├── expressions.rs
│ │ │ │ │ ├── functions.rs
│ │ │ │ │ ├── identifiers.rs
│ │ │ │ │ ├── literals.rs
│ │ │ │ │ ├── modifiers.rs
│ │ │ │ │ ├── statements.rs
│ │ │ │ │ ├── structures.rs
│ │ │ │ │ ├── types.rs
│ │ │ │ │ ├── user_defined_value_types.rs
│ │ │ │ │ ├── using_for_directives.rs
│ │ │ │ │ └── variables.rs
│ │ │ │ ├── disp.rs
│ │ │ │ ├── node
│ │ │ │ │ ├── blocks.rs
│ │ │ │ │ ├── contracts.rs
│ │ │ │ │ ├── documentation.rs
│ │ │ │ │ ├── enumerations.rs
│ │ │ │ │ ├── errors.rs
│ │ │ │ │ ├── events.rs
│ │ │ │ │ ├── expressions.rs
│ │ │ │ │ ├── functions.rs
│ │ │ │ │ ├── identifiers.rs
│ │ │ │ │ ├── import_directives.rs
│ │ │ │ │ ├── literals.rs
│ │ │ │ │ ├── modifiers.rs
│ │ │ │ │ ├── pragma_directives.rs
│ │ │ │ │ ├── source_units.rs
│ │ │ │ │ ├── statements.rs
│ │ │ │ │ ├── structures.rs
│ │ │ │ │ ├── types.rs
│ │ │ │ │ ├── user_defined_value_types.rs
│ │ │ │ │ ├── using_for_directives.rs
│ │ │ │ │ └── variables.rs
│ │ │ │ ├── node.rs
│ │ │ │ ├── own
│ │ │ │ │ ├── hashing.rs
│ │ │ │ │ ├── node_id.rs
│ │ │ │ │ ├── source_units.rs
│ │ │ │ │ └── utils.rs
│ │ │ │ └── own.rs
│ │ │ ├── impls.rs
│ │ │ ├── macros.rs
│ │ │ ├── magic.rs
│ │ │ ├── node_type.rs
│ │ │ └── yul.rs
│ │ ├── ast.rs
│ │ ├── audit
│ │ │ ├── attack_surface.rs
│ │ │ ├── auditor.rs
│ │ │ ├── entrypoint.rs
│ │ │ └── public_functions_no_sender.rs
│ │ ├── audit.rs
│ │ ├── context
│ │ │ ├── browser
│ │ │ │ ├── ancestral_line.rs
│ │ │ │ ├── closest_ancestor.rs
│ │ │ │ ├── external_calls.rs
│ │ │ │ ├── extractor.rs
│ │ │ │ ├── immediate_children.rs
│ │ │ │ ├── location.rs
│ │ │ │ ├── macros.rs
│ │ │ │ ├── parent.rs
│ │ │ │ ├── peek_over.rs
│ │ │ │ ├── peek_under.rs
│ │ │ │ ├── peek.rs
│ │ │ │ ├── siblings.rs
│ │ │ │ ├── sort_nodes.rs
│ │ │ │ └── storage_vars.rs
│ │ │ ├── browser.rs
│ │ │ ├── capturable.rs
│ │ │ ├── flow
│ │ │ │ ├── display.rs
│ │ │ │ ├── error.rs
│ │ │ │ ├── kind.rs
│ │ │ │ ├── primitives.rs
│ │ │ │ ├── reducibles.rs
│ │ │ │ ├── tests.rs
│ │ │ │ ├── utils.rs
│ │ │ │ ├── visualizer.rs
│ │ │ │ └── voids.rs
│ │ │ ├── flow.rs
│ │ │ ├── graph
│ │ │ │ ├── callgraph
│ │ │ │ │ ├── legacy.rs
│ │ │ │ │ ├── new.rs
│ │ │ │ │ ├── tests.rs
│ │ │ │ │ ├── utils.rs
│ │ │ │ │ └── visit.rs
│ │ │ │ ├── callgraph.rs
│ │ │ │ ├── preprocess
│ │ │ │ │ ├── legacy.rs
│ │ │ │ │ └── new.rs
│ │ │ │ ├── preprocess.rs
│ │ │ │ ├── traits.rs
│ │ │ │ └── utils.rs
│ │ │ ├── graph.rs
│ │ │ ├── macros.rs
│ │ │ ├── mcp
│ │ │ │ ├── callgraph
│ │ │ │ │ ├── render.rs
│ │ │ │ │ ├── tool.rs
│ │ │ │ │ └── utils.rs
│ │ │ │ ├── callgraph.rs
│ │ │ │ ├── contract_surface
│ │ │ │ │ ├── render.rs
│ │ │ │ │ ├── tool.rs
│ │ │ │ │ └── util.rs
│ │ │ │ ├── contract_surface.rs
│ │ │ │ ├── list_contracts
│ │ │ │ │ ├── render.rs
│ │ │ │ │ └── tool.rs
│ │ │ │ ├── list_contracts.rs
│ │ │ │ ├── node_finder
│ │ │ │ │ ├── render.rs
│ │ │ │ │ ├── tool.rs
│ │ │ │ │ └── utils.rs
│ │ │ │ ├── node_finder.rs
│ │ │ │ ├── node_summarizer
│ │ │ │ │ ├── render.rs
│ │ │ │ │ ├── tool.rs
│ │ │ │ │ └── utils.rs
│ │ │ │ ├── node_summarizer.rs
│ │ │ │ ├── project_overview
│ │ │ │ │ ├── render.rs
│ │ │ │ │ └── tool.rs
│ │ │ │ ├── project_overview.rs
│ │ │ │ ├── tool_guide
│ │ │ │ │ └── tool.rs
│ │ │ │ └── tool_guide.rs
│ │ │ ├── mcp.rs
│ │ │ ├── router
│ │ │ │ ├── external_calls.rs
│ │ │ │ ├── internal_calls.rs
│ │ │ │ ├── modifier_calls.rs
│ │ │ │ └── tests.rs
│ │ │ ├── router.rs
│ │ │ └── workspace.rs
│ │ ├── context.rs
│ │ ├── detect
│ │ │ ├── detector.rs
│ │ │ ├── entrypoint.rs
│ │ │ ├── helpers.rs
│ │ │ ├── high
│ │ │ │ ├── _template.rs
│ │ │ │ ├── abi_encode_packed_hash_collision.rs
│ │ │ │ ├── arbitrary_transfer_from.rs
│ │ │ │ ├── const_func_changes_state.rs
│ │ │ │ ├── contract_locks_ether.rs
│ │ │ │ ├── dangerous_unary_operator.rs
│ │ │ │ ├── delegate_call_unchecked_address.rs
│ │ │ │ ├── delete_nested_mapping.rs
│ │ │ │ ├── dynamic_array_length_assignment.rs
│ │ │ │ ├── enumerable_loop_removal.rs
│ │ │ │ ├── eth_send_unchecked_address.rs
│ │ │ │ ├── experimental_encoder.rs
│ │ │ │ ├── function_selector_collision.rs
│ │ │ │ ├── incorrect_caret_operator.rs
│ │ │ │ ├── incorrect_erc20_interface.rs
│ │ │ │ ├── incorrect_erc721_interface.rs
│ │ │ │ ├── incorrect_shift_order.rs
│ │ │ │ ├── misused_boolean.rs
│ │ │ │ ├── msg_value_in_loops.rs
│ │ │ │ ├── multiple_constructors.rs
│ │ │ │ ├── nested_struct_in_mapping.rs
│ │ │ │ ├── out_of_order_retryable.rs
│ │ │ │ ├── pre_declared_variable_usage.rs
│ │ │ │ ├── reentrancy_state_change.rs
│ │ │ │ ├── reused_contract_name.rs
│ │ │ │ ├── rtlo.rs
│ │ │ │ ├── selfdestruct.rs
│ │ │ │ ├── signed_integer_storage_array.rs
│ │ │ │ ├── state_variable_shadowing.rs
│ │ │ │ ├── storage_array_memory_edit.rs
│ │ │ │ ├── strict_equality_contract_balance.rs
│ │ │ │ ├── tautological_compare.rs
│ │ │ │ ├── tautology_or_contradiction.rs
│ │ │ │ ├── tx_origin_used_for_auth.rs
│ │ │ │ ├── unchecked_low_level_call.rs
│ │ │ │ ├── unchecked_send.rs
│ │ │ │ ├── unprotected_initializer.rs
│ │ │ │ ├── unsafe_casting.rs
│ │ │ │ ├── weak_randomness.rs
│ │ │ │ └── yul_return.rs
│ │ │ ├── high.rs
│ │ │ ├── low
│ │ │ │ ├── _template.rs
│ │ │ │ ├── assert_state_change.rs
│ │ │ │ ├── block_timestamp_deadline.rs
│ │ │ │ ├── boolean_equality.rs
│ │ │ │ ├── builtin_symbol_shadowing.rs
│ │ │ │ ├── centralization_risk.rs
│ │ │ │ ├── constant_function_contains_assembly.rs
│ │ │ │ ├── costly_loop.rs
│ │ │ │ ├── dead_code.rs
│ │ │ │ ├── delegatecall_in_loop.rs
│ │ │ │ ├── deprecated_oz_function.rs
│ │ │ │ ├── division_before_multiplication.rs
│ │ │ │ ├── ecrecover.rs
│ │ │ │ ├── empty_block.rs
│ │ │ │ ├── empty_require_revert.rs
│ │ │ │ ├── function_initializing_state.rs
│ │ │ │ ├── function_pointer_in_constructor.rs
│ │ │ │ ├── inconsistent_type_names.rs
│ │ │ │ ├── incorrect_modifier.rs
│ │ │ │ ├── internal_function_used_once.rs
│ │ │ │ ├── large_numeric_literal.rs
│ │ │ │ ├── literal_instead_of_constant.rs
│ │ │ │ ├── local_variable_shadowing.rs
│ │ │ │ ├── missing_inheritance.rs
│ │ │ │ ├── modifier_used_only_once.rs
│ │ │ │ ├── multiple_placeholders.rs
│ │ │ │ ├── non_reentrant_not_first.rs
│ │ │ │ ├── push_0_opcode.rs
│ │ │ │ ├── redundant_statement.rs
│ │ │ │ ├── require_revert_in_loop.rs
│ │ │ │ ├── return_bomb.rs
│ │ │ │ ├── solmate_safe_transfer_lib.rs
│ │ │ │ ├── state_change_without_event.rs
│ │ │ │ ├── state_no_address_check.rs
│ │ │ │ ├── state_variable_could_be_constant.rs
│ │ │ │ ├── state_variable_could_be_immutable.rs
│ │ │ │ ├── state_variable_read_external.rs
│ │ │ │ ├── storage_array_length_not_cached.rs
│ │ │ │ ├── todo.rs
│ │ │ │ ├── unchecked_return.rs
│ │ │ │ ├── uninitialized_local_variable.rs
│ │ │ │ ├── unsafe_erc20_operation.rs
│ │ │ │ ├── unsafe_oz_erc721_mint.rs
│ │ │ │ ├── unspecific_solidity_pragma.rs
│ │ │ │ ├── unused_error.rs
│ │ │ │ ├── unused_import.rs
│ │ │ │ ├── unused_public_function.rs
│ │ │ │ ├── unused_state_variable.rs
│ │ │ │ └── void_constructor.rs
│ │ │ ├── low.rs
│ │ │ └── test_utils.rs
│ │ ├── detect.rs
│ │ ├── lib.rs
│ │ ├── stats
│ │ │ ├── cloc.rs
│ │ │ ├── dbg_tips.txt
│ │ │ ├── ignore.rs
│ │ │ ├── token.rs
│ │ │ └── util.rs
│ │ ├── stats.rs
│ │ ├── test_utils
│ │ │ └── load_source_unit.rs
│ │ ├── test_utils.rs
│ │ ├── visitor
│ │ │ ├── ast_visitor.rs
│ │ │ ├── macros.rs
│ │ │ └── workspace_visitor.rs
│ │ └── visitor.rs
│ ├── templates
│ │ └── mcp-tool-response
│ │ ├── callgraph.md
│ │ ├── contract_surface.md
│ │ ├── list_contracts.md
│ │ ├── node_finder_get_all.md
│ │ ├── node_finder_grep.md
│ │ ├── node_finder_search.md
│ │ ├── node_summarizer.md
│ │ ├── project_overview.md
│ │ └── tool_guide.md
│ └── tests
│ ├── common
│ │ ├── ancestral_line.rs
│ │ ├── closest_ancestor.rs
│ │ ├── immediate_children.rs
│ │ ├── immediate_parent.rs
│ │ ├── mod.rs
│ │ ├── new_ast_nodes.rs
│ │ ├── peek_over.rs
│ │ └── sibling.rs
│ └── traversal.rs
├── aderyn_driver
│ ├── .gitignore
│ ├── benches
│ │ └── detectors.rs
│ ├── Cargo.toml
│ ├── README.md
│ ├── src
│ │ ├── compile.rs
│ │ ├── config.rs
│ │ ├── display.rs
│ │ ├── driver.rs
│ │ ├── interface
│ │ │ ├── json.rs
│ │ │ ├── lsp.rs
│ │ │ ├── markdown.rs
│ │ │ ├── mod.rs
│ │ │ ├── sarif.rs
│ │ │ ├── tables.rs
│ │ │ └── util.rs
│ │ ├── lib.rs
│ │ ├── mcp.rs
│ │ ├── process.rs
│ │ └── runner.rs
│ └── tests
│ └── astgen.rs
├── bacon.toml
├── benchmarks
│ ├── aderyn
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── iteration_times.svg
│ │ │ └── pdf.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── iteration_times_small.svg
│ │ ├── iteration_times.svg
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── relative_iteration_times_small.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── SD.svg
│ │ └── typical.svg
│ ├── arbitrary-transfer-from
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── avoid-abi-encode-packed
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── block-timestamp-deadline
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── centralization-risk
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── constants-instead-of-literals
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── delegate-call-in-loop
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── deprecated-oz-functions
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── ecrecover
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── empty-block
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── hello_world
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── inconsistent-type-names
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── large-numeric-literal
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── non-reentrant-before-others
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── push-zero-opcode
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── report
│ │ └── index.html
│ ├── require-with-string
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── solmate-safe-transfer-lib
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── unindexed-events
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── unprotected-initializer
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── unsafe-erc20-functions
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── unsafe-oz-erc721-mint
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── unspecific-solidity-pragma
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── useless-internal-function
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── useless-modifier
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ ├── useless-public-function
│ │ ├── base
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ ├── change
│ │ │ └── estimates.json
│ │ ├── new
│ │ │ ├── benchmark.json
│ │ │ ├── estimates.json
│ │ │ ├── sample.json
│ │ │ └── tukey.json
│ │ └── report
│ │ ├── both
│ │ │ ├── pdf.svg
│ │ │ └── regression.svg
│ │ ├── change
│ │ │ ├── mean.svg
│ │ │ ├── median.svg
│ │ │ └── t-test.svg
│ │ ├── index.html
│ │ ├── MAD.svg
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ ├── pdf_small.svg
│ │ ├── pdf.svg
│ │ ├── regression_small.svg
│ │ ├── regression.svg
│ │ ├── relative_pdf_small.svg
│ │ ├── relative_regression_small.svg
│ │ ├── SD.svg
│ │ ├── slope.svg
│ │ └── typical.svg
│ └── zero-address-check
│ ├── base
│ │ ├── benchmark.json
│ │ ├── estimates.json
│ │ ├── sample.json
│ │ └── tukey.json
│ ├── change
│ │ └── estimates.json
│ ├── new
│ │ ├── benchmark.json
│ │ ├── estimates.json
│ │ ├── sample.json
│ │ └── tukey.json
│ └── report
│ ├── both
│ │ ├── pdf.svg
│ │ └── regression.svg
│ ├── change
│ │ ├── mean.svg
│ │ ├── median.svg
│ │ └── t-test.svg
│ ├── index.html
│ ├── MAD.svg
│ ├── mean.svg
│ ├── median.svg
│ ├── pdf_small.svg
│ ├── pdf.svg
│ ├── regression_small.svg
│ ├── regression.svg
│ ├── relative_pdf_small.svg
│ ├── relative_regression_small.svg
│ ├── SD.svg
│ ├── slope.svg
│ └── typical.svg
├── Cargo.lock
├── Cargo.toml
├── cli
│ ├── benchmarks.sh
│ └── reportgen.sh
├── CODEOWNERS
├── CONTRIBUTING.md
├── cyfrinup
│ ├── dynamic_script
│ └── why.md
├── deny.toml
├── dist-workspace.toml
├── funding.json
├── LICENSE
├── Makefile
├── package-lock.json
├── package.json
├── README.md
├── RELEASE_CHECKLIST.md
├── reports
│ ├── adhoc-sol-files-highs-only-report.json
│ ├── adhoc-sol-files-report.md
│ ├── ccip-functions-report.md
│ ├── empty_report.md
│ ├── hardhat-playground-report.md
│ ├── nft-report-icm.md
│ ├── nft-report.md
│ ├── prb-math-report.md
│ ├── report.json
│ ├── report.md
│ ├── report.sarif
│ ├── sablier-aderyn-toml-nested-root.md
│ ├── templegold-report.md
│ └── uniswap_profile.md
├── rust-toolchain.toml
├── rustfmt.toml
├── tests
│ ├── adhoc-sol-files
│ │ ├── aderyn.toml
│ │ ├── Counter.sol
│ │ ├── DemoASTNodes.sol
│ │ ├── Helper.sol
│ │ ├── InconsistentUints.sol
│ │ ├── inheritance
│ │ │ ├── ExtendedInheritance.sol
│ │ │ ├── IContractInheritance.sol
│ │ │ └── InheritanceBase.sol
│ │ ├── InternalFunctions.sol
│ │ ├── lib
│ │ │ └── ThisShouldBeExcluded.sol
│ │ ├── multiple-versions
│ │ │ ├── 0.4
│ │ │ │ ├── A.sol
│ │ │ │ └── B.sol
│ │ │ ├── 0.5
│ │ │ │ ├── A.sol
│ │ │ │ └── B.sol
│ │ │ ├── 0.6
│ │ │ │ ├── A.sol
│ │ │ │ └── B.sol
│ │ │ ├── 0.7
│ │ │ │ ├── A.sol
│ │ │ │ └── B.sol
│ │ │ └── 0.8
│ │ │ ├── A.sol
│ │ │ └── B.sol
│ │ ├── OnceModifierExample.sol
│ │ └── StateVariables.sol
│ ├── ast
│ │ ├── abstract_contract.json
│ │ ├── address_payable.json
│ │ ├── array_type_name.json
│ │ ├── ast-erc4626.json
│ │ ├── base_constructor_call.json
│ │ ├── bit_not.json
│ │ ├── call.json
│ │ ├── constructor.json
│ │ ├── contract_dep_order.json
│ │ ├── do_while.json
│ │ ├── documentation_1.json
│ │ ├── documentation_2.json
│ │ ├── documentation_3.json
│ │ ├── documentation_local_variable.json
│ │ ├── documentation_on_statements.json
│ │ ├── documentation_triple.json
│ │ ├── empty_block.json
│ │ ├── enum_value_declaration.json
│ │ ├── enum_value.json
│ │ ├── event_definition.json
│ │ ├── experimental_encoder_pragma.json
│ │ ├── fallback_and_reveice_ether.json
│ │ ├── fallback_payable.json
│ │ ├── fallback.json
│ │ ├── function_type.json
│ │ ├── function.json
│ │ ├── global_enum.json
│ │ ├── global_struct.json
│ │ ├── inheritance_specifier.json
│ │ ├── leave.json
│ │ ├── license.json
│ │ ├── long_type_name_binary_operation.json
│ │ ├── long_type_name_identifier.json
│ │ ├── loop.json
│ │ ├── mappings.json
│ │ ├── modifier_definition.json
│ │ ├── modifier_invocation.json
│ │ ├── mutability.json
│ │ ├── nested_functions.json
│ │ ├── non_utf8.json
│ │ ├── override.json
│ │ ├── placeholder_statement.json
│ │ ├── receive_ether.json
│ │ ├── short_type_name_ref.json
│ │ ├── short_type_name.json
│ │ ├── slot_offset.json
│ │ ├── smoke.json
│ │ ├── source_location.json
│ │ ├── string.json
│ │ ├── stringlit.json
│ │ ├── switch_default.json
│ │ ├── switch.json
│ │ ├── try_catch.json
│ │ ├── two_base_functions.json
│ │ ├── unicode.json
│ │ ├── used_errors.json
│ │ ├── userDefinedValueType.json
│ │ ├── using_for_directive.json
│ │ ├── var_access.json
│ │ └── yul_hex_literal.json
│ ├── contract-playground
│ │ ├── .github
│ │ │ └── workflows
│ │ │ └── test.yml
│ │ ├── .gitignore
│ │ ├── dot
│ │ │ └── .gitkeep
│ │ ├── foundry.toml
│ │ ├── README.md
│ │ ├── script
│ │ │ └── Counter.s.sol
│ │ ├── src
│ │ │ ├── AbstractContract.sol
│ │ │ ├── AderynIgnoreCustomDetectors.sol
│ │ │ ├── AdminContract.sol
│ │ │ ├── ArbitraryTransferFrom.sol
│ │ │ ├── AssemblyExample.sol
│ │ │ ├── AssertStateChange.sol
│ │ │ ├── auditor_mode
│ │ │ │ ├── ExternalCalls.sol
│ │ │ │ └── PublicFunctionsWithoutSenderCheck.sol
│ │ │ ├── BooleanEquality.sol
│ │ │ ├── BuiltinSymbolShadow.sol
│ │ │ ├── CacheArrayLength.sol
│ │ │ ├── CallGraphTests.sol
│ │ │ ├── Casting.sol
│ │ │ ├── cloc
│ │ │ │ ├── AnotherHeavilyCommentedContract.sol
│ │ │ │ ├── EmptyContractFile.sol
│ │ │ │ └── HeavilyCommentedContract.sol
│ │ │ ├── CompilerBugStorageSignedIntegerArray.sol
│ │ │ ├── ConstantFuncsAssembly.sol
│ │ │ ├── ConstantsLiterals.sol
│ │ │ ├── ConstFuncChangeState.sol
│ │ │ ├── ContractLocksEther.sol
│ │ │ ├── ContractWithTodo.sol
│ │ │ ├── control_flow
│ │ │ │ └── SimpleProgram.sol
│ │ │ ├── CostlyOperationsInsideLoops.sol
│ │ │ ├── Counter.sol
│ │ │ ├── CrazyPragma.sol
│ │ │ ├── DangerousStrictEquality1.sol
│ │ │ ├── DangerousStrictEquality2.sol
│ │ │ ├── DangerousUnaryOperator.sol
│ │ │ ├── DeadCode.sol
│ │ │ ├── DelegateCallWithoutAddressCheck.sol
│ │ │ ├── DeletionNestedMappingStructureContract.sol
│ │ │ ├── DeprecatedOZFunctions.sol
│ │ │ ├── DivisionBeforeMultiplication.sol
│ │ │ ├── DynamicArrayLengthAssignment.sol
│ │ │ ├── EmitAfterExternalCall.sol
│ │ │ ├── EmptyBlocks.sol
│ │ │ ├── EnumerableSetIteration.sol
│ │ │ ├── eth2
│ │ │ │ └── DepositContract.sol
│ │ │ ├── ExperimentalEncoder.sol
│ │ │ ├── ExternalCalls.sol
│ │ │ ├── FunctionInitializingState.sol
│ │ │ ├── FunctionPointers.sol
│ │ │ ├── FunctionSignatureCollision.sol
│ │ │ ├── HugeConstants.sol
│ │ │ ├── IgnoreEverything.sol
│ │ │ ├── InconsistentUints.sol
│ │ │ ├── IncorrectCaretOperator.sol
│ │ │ ├── IncorrectERC20.sol
│ │ │ ├── IncorrectERC721.sol
│ │ │ ├── IncorrectModifier.sol
│ │ │ ├── IncorrectShift.sol
│ │ │ ├── inheritance
│ │ │ │ ├── ExtendedInheritance.sol
│ │ │ │ ├── IContractInheritance.sol
│ │ │ │ └── InheritanceBase.sol
│ │ │ ├── InternalFunctions.sol
│ │ │ ├── KeccakContract.sol
│ │ │ ├── LocalVariableShadow.sol
│ │ │ ├── MissingInheritance.sol
│ │ │ ├── MisusedBoolean.sol
│ │ │ ├── MsgValueInLoop.sol
│ │ │ ├── MultipleConstructorSchemes.sol
│ │ │ ├── MultiplePlaceholders.sol
│ │ │ ├── nested
│ │ │ │ ├── 1
│ │ │ │ │ └── Nested.sol
│ │ │ │ └── 2
│ │ │ │ └── Nested.sol
│ │ │ ├── nested_mappings
│ │ │ │ ├── LaterVersion.sol
│ │ │ │ └── NestedMappings.sol
│ │ │ ├── OnceModifierExample.sol
│ │ │ ├── OnlyLibrary.sol
│ │ │ ├── OutOfOrderRetryable.sol
│ │ │ ├── parent_chain
│ │ │ │ └── ParentChainContract.sol
│ │ │ ├── PragmaRange.sol
│ │ │ ├── PreDeclaredVarUsage.sol
│ │ │ ├── PublicFunction.sol
│ │ │ ├── PublicVariableReadInExternalContext.sol
│ │ │ ├── RedundantStatements.sol
│ │ │ ├── ReturnBomb.sol
│ │ │ ├── reused_contract_name
│ │ │ │ ├── ContractA.sol
│ │ │ │ └── ContractB.sol
│ │ │ ├── RevertsAndRequriesInLoops.sol
│ │ │ ├── router
│ │ │ │ ├── ExternalCalls.sol
│ │ │ │ ├── FallbackAndReceiveOverrides.sol
│ │ │ │ ├── InternalCalls.sol
│ │ │ │ ├── ModifierCalls.sol
│ │ │ │ └── VarOverridesFunction.sol
│ │ │ ├── RTLO.sol
│ │ │ ├── SendEtherNoChecks.sol
│ │ │ ├── SendEtherNoChecksLibImport.sol
│ │ │ ├── StateChangeAfterExternalCall.sol
│ │ │ ├── StateShadowing.sol
│ │ │ ├── StateVariableCouldBeDeclaredConstant.sol
│ │ │ ├── StateVariableCouldBeDeclaredImmutable.sol
│ │ │ ├── StateVariables.sol
│ │ │ ├── StateVariablesChangesWithoutEvents.sol
│ │ │ ├── StateVariablesManipulation.sol
│ │ │ ├── StorageConditionals.sol
│ │ │ ├── StorageParameters.sol
│ │ │ ├── T11sTranferer.sol
│ │ │ ├── TautologicalCompare.sol
│ │ │ ├── TautologyOrContradiction.sol
│ │ │ ├── TestERC20.sol
│ │ │ ├── TransientKeyword.sol
│ │ │ ├── Trump.sol
│ │ │ ├── TxOriginUsedForAuth.sol
│ │ │ ├── U2.sol
│ │ │ ├── U3.sol
│ │ │ ├── U4.sol
│ │ │ ├── U5.sol
│ │ │ ├── UncheckedCalls.sol
│ │ │ ├── UncheckedReturn.sol
│ │ │ ├── UncheckedSend.sol
│ │ │ ├── UninitializedLocalVariables.sol
│ │ │ ├── UninitializedStateVariable.sol
│ │ │ ├── uniswap
│ │ │ │ ├── UniswapV2Swapper.sol
│ │ │ │ └── UniswapV3Swapper.sol
│ │ │ ├── UnprotectedInitialize.sol
│ │ │ ├── UnsafeERC721Mint.sol
│ │ │ ├── UnusedError.sol
│ │ │ ├── UnusedImport.sol
│ │ │ ├── UnusedStateVariables.sol
│ │ │ ├── UsingSelfdestruct.sol
│ │ │ ├── VoidConstructor.sol
│ │ │ ├── WeakRandomness.sol
│ │ │ ├── WrongOrderOfLayout.sol
│ │ │ ├── YulReturn.sol
│ │ │ └── ZeroAddressCheck.sol
│ │ └── test
│ │ └── Counter.t.sol
│ ├── foundry-nft-f23
│ │ ├── .github
│ │ │ └── workflows
│ │ │ └── test.yml
│ │ ├── .gitignore
│ │ ├── foundry.lock
│ │ ├── foundry.toml
│ │ ├── README.md
│ │ ├── remappings.txt
│ │ └── src
│ │ ├── BasicNft.sol
│ │ ├── F1.sol
│ │ ├── F2.sol
│ │ ├── Initializer.sol
│ │ └── inner-core-modules
│ │ └── ICM.sol
│ ├── foundry-nft-f23-icm
│ │ ├── .github
│ │ │ └── workflows
│ │ │ └── test.yml
│ │ ├── .gitignore
│ │ ├── aderyn.toml
│ │ ├── foundry.toml
│ │ ├── README.md
│ │ ├── remappings.txt
│ │ └── src
│ │ ├── BasicNft.sol
│ │ ├── F1.sol
│ │ ├── F2.sol
│ │ ├── Initializer.sol
│ │ └── inner-core-modules
│ │ └── ICM.sol
│ ├── hardhat-js-playground
│ │ ├── .gitignore
│ │ ├── artifacts
│ │ │ ├── build-info
│ │ │ │ └── cee6fe9a9a2f03f7ff10a27ab2746af6.json
│ │ │ └── contracts
│ │ │ ├── Counter.sol
│ │ │ │ ├── Counter.dbg.json
│ │ │ │ └── Counter.json
│ │ │ ├── ExtendedInheritance.sol
│ │ │ │ ├── ExtendedInheritance.dbg.json
│ │ │ │ └── ExtendedInheritance.json
│ │ │ ├── IContractInheritance.sol
│ │ │ │ ├── IContractInheritance.dbg.json
│ │ │ │ └── IContractInheritance.json
│ │ │ ├── InheritanceBase.sol
│ │ │ │ ├── InheritanceBase.dbg.json
│ │ │ │ └── InheritanceBase.json
│ │ │ ├── KeccakContract.sol
│ │ │ │ ├── KeccakContract.dbg.json
│ │ │ │ └── KeccakContract.json
│ │ │ ├── Lock.sol
│ │ │ │ ├── Lock.dbg.json
│ │ │ │ └── Lock.json
│ │ │ └── StateVariables.sol
│ │ │ ├── StateVariables.dbg.json
│ │ │ └── StateVariables.json
│ │ ├── contracts
│ │ │ ├── Counter.sol
│ │ │ ├── ExtendedInheritance.sol
│ │ │ ├── IContractInheritance.sol
│ │ │ ├── InheritanceBase.sol
│ │ │ ├── KeccakContract.sol
│ │ │ ├── Lock.sol
│ │ │ └── StateVariables.sol
│ │ ├── hardhat.config.js
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── scripts
│ │ │ └── deploy.js
│ │ ├── test
│ │ │ └── Lock.js
│ │ └── yarn.lock
│ ├── no-sol-files
│ │ ├── extra
│ │ │ └── HelloAgain.md
│ │ ├── Hello.txt
│ │ └── Hello.yul
│ └── toml
│ ├── nested_project1
│ │ ├── aderyn.toml
│ │ ├── folder1
│ │ │ └── hardhat.config.ts
│ │ ├── folder2
│ │ │ └── hardhat.config.ts
│ │ └── folder3
│ │ └── file.txt
│ └── nested_project2
│ ├── aderyn.toml
│ ├── folder1
│ │ └── foundry.toml
│ └── folder2
│ └── file1.txt
├── tools
│ └── xtask
│ ├── Cargo.toml
│ └── src
│ ├── blesspr.rs
│ ├── cut_release.rs
│ ├── flags.rs
│ ├── main.rs
│ ├── reportgen.rs
│ └── tomlgen.rs
└── typos.toml
```
# Files
--------------------------------------------------------------------------------
/aderyn_core/src/ast/impls/ctx/utils.rs:
--------------------------------------------------------------------------------
```rust
1 | use crate::{ast::*, context::workspace::WorkspaceContext};
2 |
3 | impl ContractDefinition {
4 | /// Returns sequence of all inherited contracts including itself in C3 linearized hierarchy
5 | #[inline]
6 | pub fn c3<'a>(
7 | &'a self,
8 | context: &'a WorkspaceContext,
9 | ) -> impl Iterator<Item = &'a ContractDefinition> {
10 | self.linearized_base_contracts.iter().flat_map(|c_id| context.nodes.get(c_id)).flat_map(
11 | |n| {
12 | if let ASTNode::ContractDefinition(c) = n {
13 | return Some(c);
14 | }
15 | None
16 | },
17 | )
18 | }
19 |
20 | #[inline]
21 | pub fn next_in<'a>(
22 | &'a self,
23 | context: &'a WorkspaceContext,
24 | base_contract: &'a ContractDefinition,
25 | ) -> Option<&'a ContractDefinition> {
26 | let mut base_c3 = base_contract.c3(context);
27 | while let Some(c) = base_c3.next() {
28 | if c.id == self.id {
29 | return base_c3.next();
30 | }
31 | }
32 | None
33 | }
34 |
35 | #[inline]
36 | pub fn is_in<'a>(
37 | &'a self,
38 | context: &'a WorkspaceContext,
39 | base_contract: &'a ContractDefinition,
40 | ) -> bool {
41 | let base_c3 = base_contract.c3(context);
42 | for c in base_c3 {
43 | if c.id == self.id {
44 | return true;
45 | }
46 | }
47 | false
48 | }
49 |
50 | pub fn entrypoint_functions<'a>(
51 | &'a self,
52 | context: &'a WorkspaceContext,
53 | ) -> Option<Vec<&'a FunctionDefinition>> {
54 | context.entrypoint_functions(self)
55 | }
56 | }
57 |
58 | impl FunctionCall {
59 | /// Returns the function definition referenced by the function call. In practice, it's not
60 | /// always the case that the function call will resolve to the referenced declaration. However,
61 | /// the type identifier of the real function (possibly overriding function) would be
62 | /// conserved i.e the same as the suspected target function
63 | ///
64 | /// Also see [`FunctionCall::is_internal_call`]
65 | #[inline]
66 | pub fn suspected_target_function<'a>(
67 | &self,
68 | context: &'a WorkspaceContext,
69 | ) -> Option<&'a FunctionDefinition> {
70 | // The most common forms of expressions when making a function call is
71 | // 1) xyz()
72 | // 2) A.xyz() where A is super or any parent class or library name or a something on which
73 | // library is being used for. (using lib for uint8) .... 6.xyz()
74 | match self.expression.as_ref() {
75 | Expression::Identifier(Identifier { referenced_declaration: Some(id), .. })
76 | | Expression::MemberAccess(MemberAccess { referenced_declaration: Some(id), .. }) => {
77 | if let Some(ASTNode::FunctionDefinition(func)) = context.nodes.get(id) {
78 | Some(func)
79 | } else {
80 | None
81 | }
82 | }
83 | // TODO: Improve this function heuristics by exhausting enum possibilities for
84 | // expression
85 | _ => None,
86 | }
87 | }
88 | /// Returns the function definition or variable declaration referenced by the function call.
89 | /// Also see [`FunctionCall::suspected_target_function`]
90 | #[inline]
91 | pub fn suspected_function_selector(&self, context: &WorkspaceContext) -> Option<String> {
92 | match self.expression.as_ref() {
93 | Expression::Identifier(Identifier { referenced_declaration: Some(id), .. }) => {
94 | if let Some(ASTNode::FunctionDefinition(func)) = context.nodes.get(id) {
95 | func.function_selector.clone()
96 | } else {
97 | None
98 | }
99 | }
100 | Expression::MemberAccess(MemberAccess { referenced_declaration: Some(id), .. }) => {
101 | let suspect = context.nodes.get(id)?;
102 | match suspect {
103 | ASTNode::FunctionDefinition(func) => func.function_selector.clone(),
104 | // could be referencing a public state variable (pseudo getter method)
105 | ASTNode::VariableDeclaration(var) => var.function_selector.clone(),
106 | _ => None,
107 | }
108 | }
109 | // TODO: Improve this function heuristics by exhausting enum possibilities for
110 | // expression.
111 | _ => None,
112 | }
113 | }
114 | }
115 |
116 | impl ModifierInvocation {
117 | /// Returns the modifier definition referenced by the modifier invocation. In practice, it's not
118 | /// always the case that the function call will resolve to the referenced declaration. However,
119 | /// the type identifier of the real modifier (possibly overriding modifier) would be
120 | /// conserved i.e the same as the suspected target modifier
121 | #[inline]
122 | pub fn suspected_target_modifier<'a>(
123 | &self,
124 | context: &'a WorkspaceContext,
125 | ) -> Option<&'a ModifierDefinition> {
126 | let target_id = self.modifier_name.referenced_declaration()?;
127 | if let Some(ASTNode::ModifierDefinition(modifier)) = context.nodes.get(&target_id) {
128 | return Some(modifier);
129 | }
130 | None
131 | }
132 | }
133 |
```
--------------------------------------------------------------------------------
/tests/ast/documentation_on_statements.json:
--------------------------------------------------------------------------------
```json
1 | {"absolutePath":"a","exportedSymbols":{"C":[27]},"id":28,"nodeType":"SourceUnit","nodes":[{"abstract":false,"baseContracts":[],"canonicalName":"C","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":27,"linearizedBaseContracts":[27],"name":"C","nameLocation":"9:1:1","nodeType":"ContractDefinition","nodes":[{"constant":false,"id":2,"mutability":"mutable","name":"a","nameLocation":"50:1:1","nodeType":"VariableDeclaration","scope":27,"src":"45:6:1","stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":1,"name":"uint","nodeType":"ElementaryTypeName","src":"45:4:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"},{"body":{"id":25,"nodeType":"Block","src":"99:229:1","statements":[{"body":{"id":21,"nodeType":"Block","src":"156:66:1","statements":[{"expression":{"id":19,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":17,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":5,"src":"205:1:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"*=","rightHandSide":{"hexValue":"32","id":18,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"210:1:1","typeDescriptions":{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"},"value":"2"},"src":"205:6:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":20,"nodeType":"ExpressionStatement","src":"205:6:1"}]},"condition":{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":13,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"id":11,"name":"i","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":8,"src":"143:1:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"<","rightExpression":{"hexValue":"3230","id":12,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"147:2:1","typeDescriptions":{"typeIdentifier":"t_rational_20_by_1","typeString":"int_const 20"},"value":"20"},"src":"143:6:1","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"id":22,"initializationExpression":{"assignments":[8],"declarations":[{"constant":false,"id":8,"mutability":"mutable","name":"i","nameLocation":"136:1:1","nodeType":"VariableDeclaration","scope":22,"src":"131:6:1","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":7,"name":"uint","nodeType":"ElementaryTypeName","src":"131:4:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":10,"initialValue":{"hexValue":"30","id":9,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"140:1:1","typeDescriptions":{"typeIdentifier":"t_rational_0_by_1","typeString":"int_const 0"},"value":"0"},"nodeType":"VariableDeclarationStatement","src":"131:10:1"},"loopExpression":{"expression":{"id":15,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"151:3:1","subExpression":{"id":14,"name":"i","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":8,"src":"151:1:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":16,"nodeType":"ExpressionStatement","src":"151:3:1"},"nodeType":"ForStatement","src":"126:96:1"},{"expression":{"id":23,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":5,"src":"320:1:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"functionReturnParameters":6,"id":24,"nodeType":"Return","src":"313:8:1"}]},"functionSelector":"26121ff0","id":26,"implemented":true,"kind":"function","modifiers":[],"name":"f","nameLocation":"66:1:1","nodeType":"FunctionDefinition","parameters":{"id":3,"nodeType":"ParameterList","parameters":[],"src":"67:2:1"},"returnParameters":{"id":6,"nodeType":"ParameterList","parameters":[{"constant":false,"id":5,"mutability":"mutable","name":"x","nameLocation":"96:1:1","nodeType":"VariableDeclaration","scope":26,"src":"91:6:1","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":4,"name":"uint","nodeType":"ElementaryTypeName","src":"91:4:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"90:8:1"},"scope":27,"src":"57:271:1","stateMutability":"pure","virtual":false,"visibility":"public"}],"scope":28,"src":"0:330:1","usedErrors":[]}],"src":"0:331:1"}
2 |
```
--------------------------------------------------------------------------------
/aderyn_core/src/context/graph/preprocess/legacy.rs:
--------------------------------------------------------------------------------
```rust
1 | use std::collections::{HashMap, HashSet, hash_map};
2 |
3 | use crate::{
4 | ast::{Expression, IdentifierOrIdentifierPath, NodeID, NodeType},
5 | context::{
6 | browser::{ExtractFunctionCalls, ExtractModifierInvocations},
7 | workspace::WorkspaceContext,
8 | },
9 | };
10 |
11 | use crate::context::graph::{
12 | Error, LegacyWorkspaceCallGraph, RawCallGraph, Result, traits::Transpose,
13 | };
14 |
15 | impl LegacyWorkspaceCallGraph {
16 | /// Formula to create [`WorkspaceCallGraph`] for global preprocessing .
17 | pub fn from_context(context: &WorkspaceContext) -> Result<LegacyWorkspaceCallGraph> {
18 | let mut raw_callgraph: RawCallGraph = HashMap::new();
19 | let mut visited: HashSet<NodeID> = HashSet::new();
20 |
21 | let funcs = context
22 | .function_definitions()
23 | .into_iter()
24 | .filter(|func| func.implemented)
25 | .collect::<Vec<_>>();
26 |
27 | let modifier_definitions = context.modifier_definitions();
28 |
29 | for func in funcs {
30 | dfs_to_create_graph(func.id, &mut raw_callgraph, &mut visited, context)
31 | .map_err(|_| Error::WorkspaceCallGraphDFSError)?;
32 | }
33 |
34 | for modifier in modifier_definitions {
35 | dfs_to_create_graph(modifier.id, &mut raw_callgraph, &mut visited, context)
36 | .map_err(|_| Error::WorkspaceCallGraphDFSError)?;
37 | }
38 |
39 | Ok(LegacyWorkspaceCallGraph { raw_callgraph })
40 | }
41 | }
42 |
43 | /// Make connections from each of the nodes of [`crate::ast::FunctionDefinition`] and
44 | /// [`crate::ast::ModifierDefinition`] with their connected counterparts.
45 | fn dfs_to_create_graph(
46 | id: NodeID,
47 | raw_callgraph: &mut RawCallGraph,
48 | visited: &mut HashSet<NodeID>,
49 | context: &WorkspaceContext,
50 | ) -> Result<()> {
51 | if visited.contains(&id) {
52 | return Ok(());
53 | }
54 |
55 | visited.insert(id);
56 |
57 | // Only deal with `id`s that are in scope right now
58 | if let Some(from_node) = context.nodes.get(&id) {
59 | // referenced_declarations from previous calls in the recursion stack need to be vetted
60 | if from_node.node_type() != NodeType::FunctionDefinition
61 | && from_node.node_type() != NodeType::ModifierDefinition
62 | {
63 | return Ok(());
64 | }
65 |
66 | // connections to FunctionDefinition
67 | let function_calls = ExtractFunctionCalls::from(from_node).extracted;
68 | for function_call in function_calls {
69 | if let Expression::Identifier(identifier) = function_call.expression.as_ref()
70 | && let Some(referenced_function_id) = identifier.referenced_declaration
71 | {
72 | create_connection_if_not_exists(id, referenced_function_id, raw_callgraph);
73 | dfs_to_create_graph(referenced_function_id, raw_callgraph, visited, context)?;
74 | }
75 | }
76 |
77 | // connections to ModifierDefinition
78 | let modifier_invocations = ExtractModifierInvocations::from(from_node).extracted;
79 | for modifier_invocation in &modifier_invocations {
80 | match &modifier_invocation.modifier_name {
81 | IdentifierOrIdentifierPath::Identifier(identifier) => {
82 | if let Some(reference_modifier_id) = identifier.referenced_declaration {
83 | create_connection_if_not_exists(id, reference_modifier_id, raw_callgraph);
84 | dfs_to_create_graph(
85 | reference_modifier_id,
86 | raw_callgraph,
87 | visited,
88 | context,
89 | )?;
90 | }
91 | }
92 | IdentifierOrIdentifierPath::IdentifierPath(identifier_path) => {
93 | let referenced_modifier_id = identifier_path.referenced_declaration;
94 | create_connection_if_not_exists(id, referenced_modifier_id, raw_callgraph);
95 | dfs_to_create_graph(referenced_modifier_id, raw_callgraph, visited, context)?;
96 | }
97 | }
98 | }
99 | }
100 |
101 | // Change the default return to error later in "strict mode" maybe, because if we
102 | // can't find the node that means, the file was not in scope and hence it is not
103 | // available in the context although references to it exist.
104 | Ok(())
105 | }
106 |
107 | fn create_connection_if_not_exists(
108 | from_id: NodeID,
109 | to_id: NodeID,
110 | raw_callgraph: &mut RawCallGraph,
111 | ) {
112 | match raw_callgraph.entry(from_id) {
113 | hash_map::Entry::Occupied(mut o) => {
114 | // Performance Tip: Maybe later use binary search (it requires keeping ascending order
115 | // while inserting tho)
116 | if !o.get().contains(&to_id) {
117 | o.get_mut().push(to_id);
118 | }
119 | }
120 | hash_map::Entry::Vacant(v) => {
121 | v.insert(vec![to_id]);
122 | }
123 | }
124 | }
125 |
126 | impl Transpose for RawCallGraph {
127 | fn reverse(&self) -> Self {
128 | let mut reversed_callgraph = RawCallGraph::default();
129 | for (from_id, tos) in self {
130 | for to_id in tos {
131 | create_connection_if_not_exists(*to_id, *from_id, &mut reversed_callgraph);
132 | }
133 | }
134 | reversed_callgraph
135 | }
136 | }
137 |
```
--------------------------------------------------------------------------------
/aderyn_core/src/detect/low/multiple_placeholders.rs:
--------------------------------------------------------------------------------
```rust
1 | use std::{
2 | collections::{BTreeMap, HashSet},
3 | error::Error,
4 | };
5 |
6 | use crate::{
7 | ast::{NodeID, NodeType},
8 | capture,
9 | context::{
10 | flow::{Cfg, CfgNodeId},
11 | workspace::WorkspaceContext,
12 | },
13 | detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity},
14 | };
15 | use eyre::Result;
16 |
17 | // HOW TO USE THIS TEMPLATE:
18 | // 1. Copy this file and rename it to the snake_case version of the issue you are detecting.
19 | // 2. Rename the TemplateDetector struct and impl to your new issue name.
20 | // 3. Add this file and detector struct to the mod.rs file in the same directory.
21 | // 4. Implement the detect function to find instances of the issue.
22 |
23 | #[derive(Default)]
24 | pub struct MultiplePlaceholdersDetector {
25 | // Keys are: [0] source file name, [1] line number, [2] character location of node.
26 | // Do not add items manually, use `capture!` to add nodes to this BTreeMap.
27 | found_instances: BTreeMap<(String, usize, String), NodeID>,
28 | hints: BTreeMap<(String, usize, String), String>,
29 | }
30 |
31 | impl IssueDetector for MultiplePlaceholdersDetector {
32 | fn detect(&mut self, context: &WorkspaceContext) -> Result<bool, Box<dyn Error>> {
33 | let multiple_placeholders = |cfg: &Cfg, start: CfgNodeId| -> bool {
34 | // collect starting points
35 | let placeholders: HashSet<CfgNodeId> = {
36 | let mut set: HashSet<CfgNodeId> = Default::default();
37 | fn collect_cfg_nodes(
38 | cfg: &Cfg,
39 | visited: &mut HashSet<CfgNodeId>,
40 | curr_node: CfgNodeId,
41 | ) {
42 | if visited.contains(&curr_node) {
43 | return;
44 | }
45 | visited.insert(curr_node);
46 |
47 | for child in curr_node.children(cfg) {
48 | collect_cfg_nodes(cfg, visited, child);
49 | }
50 | }
51 | collect_cfg_nodes(cfg, &mut set, start);
52 | set.into_iter()
53 | .filter(|n| {
54 | cfg.nodes.get(n).is_some_and(|c| {
55 | c.reflect(context)
56 | .is_some_and(|d| d.node_type() == NodeType::PlaceholderStatement)
57 | })
58 | })
59 | .collect()
60 | };
61 |
62 | fn dfs(
63 | context: &WorkspaceContext,
64 | cfg: &Cfg,
65 | visited: &mut HashSet<CfgNodeId>,
66 | curr_node: CfgNodeId,
67 | count: &mut usize,
68 | ) {
69 | if visited.contains(&curr_node) {
70 | return;
71 | }
72 | visited.insert(curr_node);
73 |
74 | if cfg.nodes.get(&curr_node).is_some_and(|c| {
75 | c.reflect(context)
76 | .is_some_and(|d| d.node_type() == NodeType::PlaceholderStatement)
77 | }) {
78 | *count += 1
79 | }
80 |
81 | for child in curr_node.children(cfg) {
82 | dfs(context, cfg, visited, child, count);
83 | }
84 | }
85 |
86 | for starting_point in placeholders {
87 | let mut visited: HashSet<CfgNodeId> = Default::default();
88 | let mut count = 0;
89 | dfs(context, cfg, &mut visited, starting_point, &mut count);
90 | if count > 1 {
91 | return true;
92 | }
93 | }
94 | false
95 | };
96 |
97 | for modifier in context.modifier_definitions() {
98 | let Some((cfg, start, _)) = Cfg::from_modifier_body(context, modifier) else {
99 | continue;
100 | };
101 | if multiple_placeholders(&cfg, start) {
102 | capture!(self, context, modifier);
103 | }
104 | }
105 | Ok(!self.found_instances.is_empty())
106 | }
107 |
108 | fn severity(&self) -> IssueSeverity {
109 | IssueSeverity::Low
110 | }
111 |
112 | fn title(&self) -> String {
113 | String::from("Multiple Placeholders in Modifier")
114 | }
115 |
116 | fn description(&self) -> String {
117 | String::from(
118 | "Design the modifier to only contain 1 placeholder statement. If that is not possible, split the logic into multiple modifiers.",
119 | )
120 | }
121 |
122 | fn instances(&self) -> BTreeMap<(String, usize, String), NodeID> {
123 | self.found_instances.clone()
124 | }
125 |
126 | fn hints(&self) -> BTreeMap<(String, usize, String), String> {
127 | self.hints.clone()
128 | }
129 |
130 | fn name(&self) -> String {
131 | IssueDetectorNamePool::MultiplePlaceholders.to_string()
132 | }
133 | }
134 |
135 | #[cfg(test)]
136 | mod multiple_placeholder_tests {
137 |
138 | use crate::detect::{
139 | detector::IssueDetector, low::multiple_placeholders::MultiplePlaceholdersDetector,
140 | test_utils,
141 | };
142 |
143 | #[test]
144 | fn test_multiple_placeholders() {
145 | let context = test_utils::load_solidity_source_unit(
146 | "../tests/contract-playground/src/MultiplePlaceholders.sol",
147 | );
148 |
149 | let mut detector = MultiplePlaceholdersDetector::default();
150 | let found = detector.detect(&context).unwrap();
151 | assert!(found);
152 | assert_eq!(detector.instances().len(), 3);
153 | }
154 | }
155 |
```
--------------------------------------------------------------------------------
/aderyn_core/src/context/mcp/node_finder/utils.rs:
--------------------------------------------------------------------------------
```rust
1 | use regex::Regex;
2 |
3 | use crate::{
4 | ast::{ASTNode, FunctionKind, NodeID, NodeType},
5 | context::{
6 | mcp::node_finder::render::{NodeInfo, NodeInfoBuilder},
7 | workspace::WorkspaceContext,
8 | },
9 | };
10 |
11 | // Matches functions, modifiers and contracts by their exact names.
12 |
13 | #[inline]
14 | pub fn get_matching_functions(idx: usize, context: &WorkspaceContext, name: &str) -> Vec<NodeInfo> {
15 | get_nodes_by_exact_name_match(idx, context, Some(name), NodeType::FunctionDefinition)
16 | }
17 |
18 | #[inline]
19 | pub fn get_matching_modifiers(idx: usize, context: &WorkspaceContext, name: &str) -> Vec<NodeInfo> {
20 | get_nodes_by_exact_name_match(idx, context, Some(name), NodeType::ModifierDefinition)
21 | }
22 |
23 | #[inline]
24 | pub fn get_matching_contracts(idx: usize, context: &WorkspaceContext, name: &str) -> Vec<NodeInfo> {
25 | get_nodes_by_exact_name_match(idx, context, Some(name), NodeType::ContractDefinition)
26 | }
27 |
28 | // Matches all events and errors.
29 |
30 | #[inline]
31 | pub fn get_all_events(idx: usize, context: &WorkspaceContext) -> Vec<NodeInfo> {
32 | get_nodes_by_exact_name_match(idx, context, None, NodeType::EventDefinition)
33 | }
34 |
35 | #[inline]
36 | pub fn get_all_errors(idx: usize, context: &WorkspaceContext) -> Vec<NodeInfo> {
37 | get_nodes_by_exact_name_match(idx, context, None, NodeType::ErrorDefinition)
38 | }
39 |
40 | // Matches functions, modifiers and state variables whose code snippets match a given regex
41 |
42 | #[inline]
43 | pub fn grep_functions(idx: usize, context: &WorkspaceContext, term: &str) -> Vec<NodeInfo> {
44 | let regex = Regex::new(term).expect("invalid regex was passed");
45 | context
46 | .function_definitions()
47 | .into_iter()
48 | .filter(|&f| {
49 | let f: ASTNode = f.into();
50 | let code_snippet = context.get_code_snippet(&f);
51 | regex.is_match(&code_snippet)
52 | })
53 | .map(|func| {
54 | let func_kind = func.kind();
55 | let mut name = func.name.to_owned();
56 |
57 | if func_kind != &FunctionKind::Function {
58 | if name.is_empty() {
59 | name = func_kind.to_string();
60 | } else {
61 | name.push_str(&format!("( {} )", func_kind));
62 | }
63 | }
64 |
65 | NodeInfoBuilder::default()
66 | .name(name)
67 | .node_id(func.id)
68 | .compilation_unit_index(idx)
69 | .build()
70 | .expect("failed to build node info")
71 | })
72 | .collect()
73 | }
74 |
75 | #[inline]
76 | pub fn grep_modifiers(idx: usize, context: &WorkspaceContext, term: &str) -> Vec<NodeInfo> {
77 | let regex = Regex::new(term).expect("invalid regex was passed");
78 | context
79 | .modifier_definitions()
80 | .into_iter()
81 | .filter(|&m| {
82 | let m: ASTNode = m.into();
83 | let code_snippet = context.get_code_snippet(&m);
84 | regex.is_match(&code_snippet)
85 | })
86 | .map(|modifier| {
87 | NodeInfoBuilder::default()
88 | .name(modifier.name.to_owned())
89 | .node_id(modifier.id)
90 | .compilation_unit_index(idx)
91 | .build()
92 | .expect("failed to build node info")
93 | })
94 | .collect()
95 | }
96 |
97 | #[inline]
98 | pub fn grep_state_variables(idx: usize, context: &WorkspaceContext, term: &str) -> Vec<NodeInfo> {
99 | let regex = Regex::new(term).expect("invalid regex was passed");
100 | context
101 | .contract_definitions()
102 | .into_iter()
103 | .flat_map(|c| c.top_level_variables())
104 | .filter(|v| regex.is_match(&v.name))
105 | .map(|v| {
106 | NodeInfoBuilder::default()
107 | .name(v.name.to_string())
108 | .node_id(v.id)
109 | .compilation_unit_index(idx)
110 | .build()
111 | .expect("failed to build node info")
112 | })
113 | .collect()
114 | }
115 |
116 | // Helper functions
117 |
118 | fn get_nodes_by_exact_name_match(
119 | compilation_unit_index: usize,
120 | context: &WorkspaceContext,
121 | search_term: Option<&str>,
122 | node_ty: NodeType,
123 | ) -> Vec<NodeInfo> {
124 | let mut matching_nodes = vec![];
125 |
126 | let mut add_node = |name: &str, id: NodeID| {
127 | if let Ok(node_info) = NodeInfoBuilder::default()
128 | .name(name.to_string())
129 | .node_id(id)
130 | .compilation_unit_index(compilation_unit_index)
131 | .build()
132 | {
133 | matching_nodes.push(node_info);
134 | }
135 | };
136 |
137 | match node_ty {
138 | NodeType::ContractDefinition => {
139 | context
140 | .contract_definitions()
141 | .iter()
142 | .filter(|m| search_term.is_none_or(|t| t == m.name))
143 | .for_each(|m| add_node(&m.name, m.id));
144 | }
145 | NodeType::FunctionDefinition => {
146 | context
147 | .function_definitions()
148 | .iter()
149 | .filter(|m| search_term.is_none_or(|t| t == m.name))
150 | .for_each(|m| add_node(&m.name, m.id));
151 | }
152 | NodeType::ModifierDefinition => {
153 | context
154 | .modifier_definitions()
155 | .iter()
156 | .filter(|m| search_term.is_none_or(|t| t == m.name))
157 | .for_each(|m| add_node(&m.name, m.id));
158 | }
159 | _ => {}
160 | };
161 | matching_nodes
162 | }
163 |
```
--------------------------------------------------------------------------------
/aderyn_core/src/detect/high/nested_struct_in_mapping.rs:
--------------------------------------------------------------------------------
```rust
1 | use std::{collections::BTreeMap, error::Error};
2 |
3 | use crate::ast::{NodeID, NodeType, TypeName};
4 |
5 | use crate::{
6 | capture,
7 | context::{
8 | browser::ExtractPragmaDirectives,
9 | workspace::{ASTNode, WorkspaceContext},
10 | },
11 | detect::{
12 | detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity},
13 | helpers::pragma_directive_to_semver,
14 | },
15 | };
16 | use eyre::Result;
17 | use semver::VersionReq;
18 |
19 | #[derive(Default)]
20 | pub struct NestedStructInMappingDetector {
21 | // Keys are: [0] source file name, [1] line number, [2] character location of node.
22 | // Do not add items manually, use `capture!` to add nodes to this BTreeMap.
23 | found_instances: BTreeMap<(String, usize, String), NodeID>,
24 | }
25 |
26 | fn version_req_allows_below_0_5_0(version_req: &VersionReq) -> bool {
27 | if version_req.comparators.is_empty() {
28 | return false; // Return false or handle as needed if there are no comparators
29 | }
30 |
31 | let comparator = &version_req.comparators[0];
32 | comparator.major == 0 && comparator.minor.is_some_and(|m| m < 5)
33 | }
34 |
35 | impl IssueDetector for NestedStructInMappingDetector {
36 | fn detect(&mut self, context: &WorkspaceContext) -> Result<bool, Box<dyn Error>> {
37 | // When you have found an instance of the issue,
38 | // use the following macro to add it to `found_instances`:
39 | //
40 | // capture!(self, context, item);
41 |
42 | let mappings = context.variable_declarations().into_iter().filter(|vd| {
43 | if let Some(type_name) = &vd.type_name {
44 | if let TypeName::Mapping(_) = type_name {
45 | return true;
46 | }
47 | return false;
48 | }
49 | false
50 | });
51 |
52 | for mapping in mappings {
53 | if let Some(TypeName::Mapping(mapping_type)) = &mapping.type_name
54 | && let TypeName::UserDefinedTypeName(user_defined_type) = &*mapping_type.value_type
55 | {
56 | let struct_definition_ast_node =
57 | context.nodes.get(&user_defined_type.referenced_declaration);
58 | if let Some(ASTNode::StructDefinition(struct_definition)) =
59 | struct_definition_ast_node
60 | {
61 | for member in struct_definition.members.iter() {
62 | if let Some(member_type_string) = &member.type_descriptions.type_string
63 | && member_type_string.contains("struct")
64 | {
65 | // Check if the contract that this is in allows for solidity
66 | // pragma below 0.5.0
67 | let source_unit_ast_node =
68 | context.get_closest_ancestor(mapping.id, NodeType::SourceUnit);
69 | if let Some(source_unit_ast_node) = source_unit_ast_node {
70 | let pragma_directives =
71 | ExtractPragmaDirectives::from(source_unit_ast_node).extracted;
72 | let version_req =
73 | pragma_directive_to_semver(pragma_directives.first().unwrap())?;
74 | if version_req_allows_below_0_5_0(&version_req) {
75 | capture!(self, context, mapping);
76 | }
77 | }
78 | }
79 | }
80 | }
81 | }
82 | }
83 |
84 | Ok(!self.found_instances.is_empty())
85 | }
86 |
87 | fn severity(&self) -> IssueSeverity {
88 | IssueSeverity::High
89 | }
90 |
91 | fn title(&self) -> String {
92 | String::from("Nested Structs in Mappings pre-0.5.0")
93 | }
94 |
95 | fn description(&self) -> String {
96 | String::from(
97 | "Prior to updates in Solidity 0.5.0, public mappings with nested structs compiled, but produced incorrect values. Refrain from using these, or update to a more recent version of Solidity.",
98 | )
99 | }
100 |
101 | fn instances(&self) -> BTreeMap<(String, usize, String), NodeID> {
102 | self.found_instances.clone()
103 | }
104 |
105 | fn name(&self) -> String {
106 | IssueDetectorNamePool::NestedStructInMapping.to_string()
107 | }
108 | }
109 |
110 | #[cfg(test)]
111 | mod nested_struct_in_mapping_detector_tests {
112 |
113 | use crate::detect::{detector::IssueDetector, high::NestedStructInMappingDetector};
114 |
115 | #[test]
116 |
117 | fn test_nested_struct_in_mapping_detector() {
118 | let context = crate::detect::test_utils::load_solidity_source_unit(
119 | "../tests/contract-playground/src/nested_mappings/NestedMappings.sol",
120 | );
121 |
122 | let mut detector = NestedStructInMappingDetector::default();
123 | let found = detector.detect(&context).unwrap();
124 | assert!(found);
125 | assert_eq!(detector.instances().len(), 1);
126 | }
127 |
128 | #[test]
129 |
130 | fn test_nested_struct_in_mapping_detector_no_issue() {
131 | let context = crate::detect::test_utils::load_solidity_source_unit(
132 | "../tests/contract-playground/src/nested_mappings/LaterVersion.sol",
133 | );
134 |
135 | let mut detector = NestedStructInMappingDetector::default();
136 | let found = detector.detect(&context).unwrap();
137 | assert!(!found);
138 | assert_eq!(detector.instances().len(), 0);
139 | }
140 | }
141 |
```
--------------------------------------------------------------------------------
/tests/ast/documentation_triple.json:
--------------------------------------------------------------------------------
```json
1 | {"absolutePath":"a","exportedSymbols":{"C":[28]},"id":29,"nodeType":"SourceUnit","nodes":[{"abstract":false,"baseContracts":[],"canonicalName":"C","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":28,"linearizedBaseContracts":[28],"name":"C","nameLocation":"9:1:1","nodeType":"ContractDefinition","nodes":[{"constant":false,"documentation":{"id":1,"nodeType":"StructuredDocumentation","src":"17:8:1","text":"test"},"id":3,"mutability":"mutable","name":"a","nameLocation":"35:1:1","nodeType":"VariableDeclaration","scope":28,"src":"30:6:1","stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":2,"name":"uint","nodeType":"ElementaryTypeName","src":"30:4:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"},{"body":{"id":26,"nodeType":"Block","src":"84:181:1","statements":[{"body":{"id":22,"nodeType":"Block","src":"142:75:1","statements":[{"documentation":"tee\n s \"t\" 3","expression":{"id":20,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":18,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":6,"src":"200:1:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"*=","rightHandSide":{"hexValue":"32","id":19,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"205:1:1","typeDescriptions":{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"},"value":"2"},"src":"200:6:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":21,"nodeType":"ExpressionStatement","src":"200:6:1"}]},"condition":{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":14,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"id":12,"name":"i","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":9,"src":"129:1:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"<","rightExpression":{"hexValue":"3230","id":13,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"133:2:1","typeDescriptions":{"typeIdentifier":"t_rational_20_by_1","typeString":"int_const 20"},"value":"20"},"src":"129:6:1","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"documentation":"test2","id":23,"initializationExpression":{"assignments":[9],"declarations":[{"constant":false,"id":9,"mutability":"mutable","name":"i","nameLocation":"122:1:1","nodeType":"VariableDeclaration","scope":23,"src":"117:6:1","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":8,"name":"uint","nodeType":"ElementaryTypeName","src":"117:4:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":11,"initialValue":{"hexValue":"30","id":10,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"126:1:1","typeDescriptions":{"typeIdentifier":"t_rational_0_by_1","typeString":"int_const 0"},"value":"0"},"nodeType":"VariableDeclarationStatement","src":"117:10:1"},"loopExpression":{"expression":{"id":16,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"137:3:1","subExpression":{"id":15,"name":"i","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":9,"src":"137:1:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":17,"nodeType":"ExpressionStatement","src":"137:3:1"},"nodeType":"ForStatement","src":"112:105:1"},{"documentation":"tes \"t4\" ","expression":{"id":24,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":6,"src":"257:1:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"functionReturnParameters":7,"id":25,"nodeType":"Return","src":"250:8:1"}]},"functionSelector":"26121ff0","id":27,"implemented":true,"kind":"function","modifiers":[],"name":"f","nameLocation":"51:1:1","nodeType":"FunctionDefinition","parameters":{"id":4,"nodeType":"ParameterList","parameters":[],"src":"52:2:1"},"returnParameters":{"id":7,"nodeType":"ParameterList","parameters":[{"constant":false,"id":6,"mutability":"mutable","name":"x","nameLocation":"81:1:1","nodeType":"VariableDeclaration","scope":27,"src":"76:6:1","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":5,"name":"uint","nodeType":"ElementaryTypeName","src":"76:4:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"75:8:1"},"scope":28,"src":"42:223:1","stateMutability":"pure","virtual":false,"visibility":"public"}],"scope":29,"src":"0:267:1","usedErrors":[]}],"src":"0:268:1"}
2 |
```
--------------------------------------------------------------------------------
/aderyn_core/src/context/mcp/node_summarizer/utils.rs:
--------------------------------------------------------------------------------
```rust
1 | use crate::{
2 | ast::{ASTNode, NodeID, NodeType},
3 | context::{
4 | graph::RawCallGraph,
5 | mcp::node_summarizer::render::{
6 | EntrypointCallgraphInfo, EntrypointCallgraphInfoBuilder, NodeInfo, NodeInfoBuilder,
7 | },
8 | workspace::WorkspaceContext,
9 | },
10 | };
11 | use std::collections::{HashMap, HashSet, hash_map::Entry};
12 |
13 | pub fn get_containing_contract(context: &WorkspaceContext, node: &ASTNode) -> Option<NodeInfo> {
14 | if let ASTNode::ContractDefinition(_) = node {
15 | return None;
16 | }
17 | let Some(ASTNode::ContractDefinition(parent_contract)) = context.get_closest_ancestor(
18 | node.id().expect("node found without an ID"),
19 | NodeType::ContractDefinition,
20 | ) else {
21 | return None;
22 | };
23 | Some((parent_contract.id, parent_contract.name.clone()).into())
24 | }
25 |
26 | pub fn get_containing_modifier(context: &WorkspaceContext, node: &ASTNode) -> Option<NodeInfo> {
27 | if let ASTNode::ModifierDefinition(_) = node {
28 | return None;
29 | }
30 | let Some(ASTNode::ModifierDefinition(parent_modifier)) = context.get_closest_ancestor(
31 | node.id().expect("node found without an ID"),
32 | NodeType::ModifierDefinition,
33 | ) else {
34 | return None;
35 | };
36 | Some((parent_modifier.id, parent_modifier.name.clone()).into())
37 | }
38 |
39 | pub fn get_containing_function(context: &WorkspaceContext, node: &ASTNode) -> Option<NodeInfo> {
40 | if let ASTNode::FunctionDefinition(_) = node {
41 | return None;
42 | }
43 | let Some(ASTNode::FunctionDefinition(parent_function)) = context.get_closest_ancestor(
44 | node.id().expect("node found without an ID"),
45 | NodeType::FunctionDefinition,
46 | ) else {
47 | return None;
48 | };
49 | Some((parent_function.id, parent_function.name.clone()).into())
50 | }
51 |
52 | impl From<(NodeID, String)> for NodeInfo {
53 | fn from(value: (NodeID, String)) -> Self {
54 | NodeInfoBuilder::default()
55 | .name(value.1)
56 | .node_id(value.0)
57 | .build()
58 | .expect("failed to build node info")
59 | }
60 | }
61 |
62 | pub fn get_containing_callgraphs(
63 | context: &WorkspaceContext,
64 | node: &ASTNode,
65 | ) -> Vec<EntrypointCallgraphInfo> {
66 | // Given node, we want to locate it in the callgraph
67 | let node_id = node.id().expect("node found without an ID");
68 |
69 | let parent_graph_node = match (
70 | context.get_closest_ancestor_including_self(node_id, NodeType::FunctionDefinition),
71 | context.get_closest_ancestor_including_self(node_id, NodeType::ModifierDefinition),
72 | ) {
73 | (Some(ASTNode::FunctionDefinition(func)), _) => Some(func.id),
74 | (_, Some(ASTNode::ModifierDefinition(modifier))) => Some(modifier.id),
75 | (_, _) => None,
76 | };
77 |
78 | let Some(parent_graph_node) = parent_graph_node else {
79 | return vec![];
80 | };
81 |
82 | // Keys => (deployabele contract id, contract name), Values => entrypoint function ids
83 | let mut entrypoint_info: HashMap<(NodeID, String), Vec<NodeID>> = HashMap::new();
84 |
85 | for contract in context.deployable_contracts() {
86 | let Some(contract_callgraph) =
87 | context.callgraphs.as_ref().and_then(|c| c.outward_callgraphs.get(&contract.id))
88 | else {
89 | continue;
90 | };
91 | let Some(entrypoint_ids) = contract
92 | .entrypoint_functions(context)
93 | .map(|funcs| funcs.into_iter().map(|f| f.id).collect::<HashSet<_>>())
94 | else {
95 | continue;
96 | };
97 | let reachable_entrypoints = traverse_cg_and_get_reachable_entrypoints(
98 | parent_graph_node,
99 | contract_callgraph,
100 | &entrypoint_ids,
101 | );
102 |
103 | for entrypoint_id in reachable_entrypoints {
104 | match entrypoint_info.entry((contract.id, contract.name.to_owned())) {
105 | Entry::Occupied(mut o) => {
106 | o.get_mut().push(entrypoint_id);
107 | }
108 | Entry::Vacant(v) => {
109 | v.insert(vec![entrypoint_id]);
110 | }
111 | }
112 | }
113 | }
114 |
115 | entrypoint_info
116 | .into_iter()
117 | .map(|((contract_id, contract_name), entrypoints)| {
118 | EntrypointCallgraphInfoBuilder::default()
119 | .deployable_contract_id(contract_id)
120 | .deployable_contract_name(contract_name)
121 | .entrypoint_ids(entrypoints)
122 | .build()
123 | .expect("failed to build entrypoint callgraph info")
124 | })
125 | .collect()
126 | }
127 |
128 | fn traverse_cg_and_get_reachable_entrypoints(
129 | node_id: NodeID,
130 | outward_cg: &RawCallGraph,
131 | entrypoint_ids: &HashSet<NodeID>,
132 | ) -> HashSet<NodeID> {
133 | // Visit all possible nodes starting from node_id in the outward callgraph. Then collect all the
134 | // nodes which can be potential starting points that lead to node_id in the (real) inward
135 | // callgraph.
136 | let mut worklist = vec![node_id];
137 | let mut visited: HashSet<NodeID> = Default::default();
138 |
139 | while let Some(node) = worklist.pop() {
140 | if visited.contains(&node) {
141 | continue;
142 | }
143 | visited.insert(node);
144 |
145 | if let Some(connections) = outward_cg.get(&node) {
146 | for conn in connections {
147 | worklist.push(*conn);
148 | }
149 | }
150 | }
151 |
152 | visited.into_iter().filter(|f| entrypoint_ids.contains(f)).collect()
153 | }
154 |
```
--------------------------------------------------------------------------------
/benchmarks/unsafe-erc20-functions/report/relative_regression_small.svg:
--------------------------------------------------------------------------------
```
1 | <svg width="450" height="300" viewBox="0 0 450 300" xmlns="http://www.w3.org/2000/svg">
2 | <text x="15" y="130" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000" transform="rotate(270, 15, 130)">
3 | Total sample time (ms)
4 | </text>
5 | <text x="255" y="285" dy="-0.5ex" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
6 | Iterations (x 10^3)
7 | </text>
8 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="244" x2="75" y2="15"/>
9 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="139" y1="244" x2="139" y2="15"/>
10 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="203" y1="244" x2="203" y2="15"/>
11 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="267" y1="244" x2="267" y2="15"/>
12 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="331" y1="244" x2="331" y2="15"/>
13 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="395" y1="244" x2="395" y2="15"/>
14 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="244" x2="434" y2="244"/>
15 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="208" x2="434" y2="208"/>
16 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="171" x2="434" y2="171"/>
17 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="134" x2="434" y2="134"/>
18 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="97" x2="434" y2="97"/>
19 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="61" x2="434" y2="61"/>
20 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="24" x2="434" y2="24"/>
21 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="74,15 74,244 "/>
22 | <text x="65" y="244" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
23 | 0.0
24 | </text>
25 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,244 74,244 "/>
26 | <text x="65" y="208" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
27 | 20.0
28 | </text>
29 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,208 74,208 "/>
30 | <text x="65" y="171" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
31 | 40.0
32 | </text>
33 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,171 74,171 "/>
34 | <text x="65" y="134" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
35 | 60.0
36 | </text>
37 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,134 74,134 "/>
38 | <text x="65" y="97" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
39 | 80.0
40 | </text>
41 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,97 74,97 "/>
42 | <text x="65" y="61" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
43 | 100.0
44 | </text>
45 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,61 74,61 "/>
46 | <text x="65" y="24" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
47 | 120.0
48 | </text>
49 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,24 74,24 "/>
50 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="75,245 434,245 "/>
51 | <text x="75" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
52 | 0
53 | </text>
54 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="75,245 75,250 "/>
55 | <text x="139" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
56 | 0.5
57 | </text>
58 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="139,245 139,250 "/>
59 | <text x="203" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
60 | 1
61 | </text>
62 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="203,245 203,250 "/>
63 | <text x="267" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
64 | 1.5
65 | </text>
66 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="267,245 267,250 "/>
67 | <text x="331" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
68 | 2
69 | </text>
70 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="331,245 331,250 "/>
71 | <text x="395" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
72 | 2.5
73 | </text>
74 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="395,245 395,250 "/>
75 | <polyline fill="none" opacity="1" stroke="#E31A1C" stroke-width="1" points="75,244 434,60 "/>
76 | <polygon opacity="0.25" fill="#E31A1C" points="75,244 434,60 434,60 "/>
77 | <polyline fill="none" opacity="1" stroke="#1F78B4" stroke-width="1" points="75,244 434,15 "/>
78 | <polygon opacity="0.25" fill="#1F78B4" points="75,244 434,19 434,15 "/>
79 | </svg>
80 |
```
--------------------------------------------------------------------------------
/benchmarks/unspecific-solidity-pragma/report/relative_regression_small.svg:
--------------------------------------------------------------------------------
```
1 | <svg width="450" height="300" viewBox="0 0 450 300" xmlns="http://www.w3.org/2000/svg">
2 | <text x="15" y="130" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000" transform="rotate(270, 15, 130)">
3 | Total sample time (ms)
4 | </text>
5 | <text x="255" y="285" dy="-0.5ex" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
6 | Iterations (x 10^3)
7 | </text>
8 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="244" x2="75" y2="15"/>
9 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="133" y1="244" x2="133" y2="15"/>
10 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="192" y1="244" x2="192" y2="15"/>
11 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="251" y1="244" x2="251" y2="15"/>
12 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="310" y1="244" x2="310" y2="15"/>
13 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="369" y1="244" x2="369" y2="15"/>
14 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="428" y1="244" x2="428" y2="15"/>
15 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="244" x2="434" y2="244"/>
16 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="203" x2="434" y2="203"/>
17 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="162" x2="434" y2="162"/>
18 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="120" x2="434" y2="120"/>
19 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="79" x2="434" y2="79"/>
20 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="37" x2="434" y2="37"/>
21 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="74,15 74,244 "/>
22 | <text x="65" y="244" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
23 | 0.0
24 | </text>
25 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,244 74,244 "/>
26 | <text x="65" y="203" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
27 | 20.0
28 | </text>
29 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,203 74,203 "/>
30 | <text x="65" y="162" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
31 | 40.0
32 | </text>
33 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,162 74,162 "/>
34 | <text x="65" y="120" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
35 | 60.0
36 | </text>
37 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,120 74,120 "/>
38 | <text x="65" y="79" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
39 | 80.0
40 | </text>
41 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,79 74,79 "/>
42 | <text x="65" y="37" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
43 | 100.0
44 | </text>
45 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,37 74,37 "/>
46 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="75,245 434,245 "/>
47 | <text x="75" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
48 | 0
49 | </text>
50 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="75,245 75,250 "/>
51 | <text x="133" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
52 | 1
53 | </text>
54 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="133,245 133,250 "/>
55 | <text x="192" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
56 | 2
57 | </text>
58 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="192,245 192,250 "/>
59 | <text x="251" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
60 | 3
61 | </text>
62 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="251,245 251,250 "/>
63 | <text x="310" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
64 | 4
65 | </text>
66 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="310,245 310,250 "/>
67 | <text x="369" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
68 | 5
69 | </text>
70 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="369,245 369,250 "/>
71 | <text x="428" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
72 | 6
73 | </text>
74 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="428,245 428,250 "/>
75 | <polyline fill="none" opacity="1" stroke="#E31A1C" stroke-width="1" points="75,244 434,15 "/>
76 | <polygon opacity="0.25" fill="#E31A1C" points="75,244 434,21 434,15 "/>
77 | <polyline fill="none" opacity="1" stroke="#1F78B4" stroke-width="1" points="75,244 434,40 "/>
78 | <polygon opacity="0.25" fill="#1F78B4" points="75,244 434,41 434,38 "/>
79 | </svg>
80 |
```
--------------------------------------------------------------------------------
/benchmarks/push-zero-opcode/report/both/regression.svg:
--------------------------------------------------------------------------------
```
1 | <svg width="960" height="540" viewBox="0 0 960 540" xmlns="http://www.w3.org/2000/svg">
2 | <text x="480" y="32" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="16.129032258064516" opacity="1" fill="#000000">
3 | push-zero-opcode
4 | </text>
5 | <text x="27" y="263" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000" transform="rotate(270, 27, 263)">
6 | Total sample time (ms)
7 | </text>
8 | <text x="510" y="513" dy="-0.5ex" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
9 | Iterations (x 10^3)
10 | </text>
11 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="472" x2="87" y2="53"/>
12 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="279" y1="472" x2="279" y2="53"/>
13 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="471" y1="472" x2="471" y2="53"/>
14 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="663" y1="472" x2="663" y2="53"/>
15 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="855" y1="472" x2="855" y2="53"/>
16 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="472" x2="932" y2="472"/>
17 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="397" x2="932" y2="397"/>
18 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="321" x2="932" y2="321"/>
19 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="245" x2="932" y2="245"/>
20 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="169" x2="932" y2="169"/>
21 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="93" x2="932" y2="93"/>
22 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="86,53 86,472 "/>
23 | <text x="77" y="472" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
24 | 0.0
25 | </text>
26 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,472 86,472 "/>
27 | <text x="77" y="397" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
28 | 20.0
29 | </text>
30 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,397 86,397 "/>
31 | <text x="77" y="321" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
32 | 40.0
33 | </text>
34 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,321 86,321 "/>
35 | <text x="77" y="245" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
36 | 60.0
37 | </text>
38 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,245 86,245 "/>
39 | <text x="77" y="169" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
40 | 80.0
41 | </text>
42 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,169 86,169 "/>
43 | <text x="77" y="93" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
44 | 100.0
45 | </text>
46 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,93 86,93 "/>
47 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="87,473 932,473 "/>
48 | <text x="87" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
49 | 0
50 | </text>
51 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="87,473 87,478 "/>
52 | <text x="279" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
53 | 0.5
54 | </text>
55 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="279,473 279,478 "/>
56 | <text x="471" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
57 | 1
58 | </text>
59 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="471,473 471,478 "/>
60 | <text x="663" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
61 | 1.5
62 | </text>
63 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="663,473 663,478 "/>
64 | <text x="855" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
65 | 2
66 | </text>
67 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="855,473 855,478 "/>
68 | <polyline fill="none" opacity="1" stroke="#E31A1C" stroke-width="1" points="87,472 932,53 "/>
69 | <polygon opacity="0.25" fill="#E31A1C" points="87,472 932,68 932,53 "/>
70 | <polyline fill="none" opacity="1" stroke="#1F78B4" stroke-width="1" points="87,472 932,119 "/>
71 | <polygon opacity="0.25" fill="#1F78B4" points="87,472 932,128 932,108 "/>
72 | <text x="132" y="68" dy="0.76em" text-anchor="start" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
73 | Base Sample
74 | </text>
75 | <text x="132" y="83" dy="0.76em" text-anchor="start" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
76 | New Sample
77 | </text>
78 | <polyline fill="none" opacity="1" stroke="#E31A1C" stroke-width="2" points="102,73 122,73 "/>
79 | <polyline fill="none" opacity="1" stroke="#1F78B4" stroke-width="2" points="102,88 122,88 "/>
80 | </svg>
81 |
```
--------------------------------------------------------------------------------
/aderyn_core/src/detect/high/const_func_changes_state.rs:
--------------------------------------------------------------------------------
```rust
1 | use std::{collections::BTreeMap, error::Error};
2 |
3 | use crate::ast::{NodeID, StateMutability};
4 |
5 | use crate::{
6 | capture,
7 | context::{
8 | browser::ApproximateStorageChangeFinder,
9 | graph::{CallGraphConsumer, CallGraphDirection, CallGraphVisitor},
10 | workspace::WorkspaceContext,
11 | },
12 | detect::{
13 | detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity},
14 | helpers,
15 | },
16 | };
17 | use eyre::Result;
18 |
19 | #[derive(Default)]
20 | pub struct ConstantFunctionChangesStateDetector {
21 | // Keys are: [0] source file name, [1] line number, [2] character location of node.
22 | // Do not add items manually, use `capture!` to add nodes to this BTreeMap.
23 | found_instances: BTreeMap<(String, usize, String), NodeID>,
24 | }
25 |
26 | impl IssueDetector for ConstantFunctionChangesStateDetector {
27 | fn detect(&mut self, context: &WorkspaceContext) -> Result<bool, Box<dyn Error>> {
28 | for func in helpers::get_implemented_external_and_public_functions(context) {
29 | // Rule applies to only view functions, so ignore the rest
30 | if func.state_mutability() != &StateMutability::View {
31 | continue;
32 | }
33 | // Check if this func is compilable for solc < 0.5.0. If not, move on to the next
34 | if !func.compiles_for_solc_below_0_5_0(context) {
35 | continue;
36 | }
37 | // Now, investigate the function to see if there is scope for any state variable changes
38 | let mut tracker = StateVariableChangeTracker { state_var_has_changed: false, context };
39 |
40 | // Keep legacy for this because it is for solc version below 0.5.0 and the function
41 | // selectors don't exist
42 | let callgraph = CallGraphConsumer::get_legacy(
43 | context,
44 | &[&(func.into())],
45 | CallGraphDirection::Inward,
46 | )?;
47 | callgraph.accept(context, &mut tracker)?;
48 |
49 | if tracker.state_var_has_changed {
50 | capture!(self, context, func);
51 | }
52 | }
53 |
54 | Ok(!self.found_instances.is_empty())
55 | }
56 |
57 | fn severity(&self) -> IssueSeverity {
58 | IssueSeverity::High
59 | }
60 |
61 | fn title(&self) -> String {
62 | String::from("Constant functions changes state")
63 | }
64 |
65 | fn description(&self) -> String {
66 | String::from(
67 | "Function is declared constant/view but it changes state. Ensure that the attributes of contract compiled prior to 0.5 are correct.",
68 | )
69 | }
70 |
71 | fn instances(&self) -> BTreeMap<(String, usize, String), NodeID> {
72 | self.found_instances.clone()
73 | }
74 |
75 | fn name(&self) -> String {
76 | IssueDetectorNamePool::ConstantFunctionChangesState.to_string()
77 | }
78 | }
79 |
80 | struct StateVariableChangeTracker<'a> {
81 | state_var_has_changed: bool,
82 | context: &'a WorkspaceContext,
83 | }
84 |
85 | impl CallGraphVisitor for StateVariableChangeTracker<'_> {
86 | fn visit_any(&mut self, node: &crate::ast::ASTNode) -> eyre::Result<()> {
87 | if self.state_var_has_changed {
88 | return Ok(());
89 | }
90 | // Check for state variable changes
91 | let finder = ApproximateStorageChangeFinder::from(self.context, node);
92 | if finder.state_variables_have_been_manipulated() {
93 | self.state_var_has_changed = true;
94 | }
95 | Ok(())
96 | }
97 | }
98 |
99 | mod func_compilation_solc_pragma_helper {
100 | use std::str::FromStr;
101 |
102 | use semver::{Version, VersionReq};
103 |
104 | use crate::{
105 | ast::{FunctionDefinition, NodeType},
106 | context::{
107 | browser::{ExtractPragmaDirectives, GetClosestAncestorOfTypeX},
108 | workspace::WorkspaceContext,
109 | },
110 | detect::helpers,
111 | };
112 |
113 | impl FunctionDefinition {
114 | pub fn compiles_for_solc_below_0_5_0(&self, context: &WorkspaceContext) -> bool {
115 | if let Some(source_unit) = self.closest_ancestor_of_type(context, NodeType::SourceUnit)
116 | {
117 | let pragma_directives = ExtractPragmaDirectives::from(source_unit).extracted;
118 |
119 | if let Some(pragma_directive) = pragma_directives.first()
120 | && let Ok(pragma_semver) = helpers::pragma_directive_to_semver(pragma_directive)
121 | && version_req_allows_below_0_5_0(&pragma_semver)
122 | {
123 | return true;
124 | }
125 | }
126 | false
127 | }
128 | }
129 |
130 | fn version_req_allows_below_0_5_0(version_req: &VersionReq) -> bool {
131 | // If it matches any 0.4.0 to 0.4.26, return true
132 | for i in 0..=26 {
133 | let version = Version::from_str(&format!("0.4.{}", i)).unwrap();
134 | if version_req.matches(&version) {
135 | return true;
136 | }
137 | }
138 |
139 | // Else, return false
140 | false
141 | }
142 | }
143 |
144 | #[cfg(test)]
145 | mod constant_func_changing_state {
146 |
147 | use crate::detect::{
148 | detector::IssueDetector,
149 | high::const_func_changes_state::ConstantFunctionChangesStateDetector,
150 | };
151 |
152 | #[test]
153 |
154 | fn test_constant_function_changing_state() {
155 | let context = crate::detect::test_utils::load_solidity_source_unit(
156 | "../tests/contract-playground/src/ConstFuncChangeState.sol",
157 | );
158 |
159 | let mut detector = ConstantFunctionChangesStateDetector::default();
160 | let found = detector.detect(&context).unwrap();
161 | assert!(found);
162 | assert_eq!(detector.instances().len(), 1);
163 | }
164 | }
165 |
```
--------------------------------------------------------------------------------
/aderyn_core/src/detect/high/tx_origin_used_for_auth.rs:
--------------------------------------------------------------------------------
```rust
1 | use std::{collections::BTreeMap, error::Error};
2 |
3 | use crate::ast::{ASTNode, Expression, Identifier, NodeID};
4 |
5 | use crate::{
6 | capture,
7 | context::{
8 | browser::ExtractMemberAccesses,
9 | graph::{CallGraphConsumer, CallGraphDirection, CallGraphVisitor},
10 | workspace::WorkspaceContext,
11 | },
12 | detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity},
13 | };
14 | use eyre::Result;
15 |
16 | #[derive(Default)]
17 | pub struct TxOriginUsedForAuthDetector {
18 | // Keys are: [0] source file name, [1] line number, [2] character location of node.
19 | // Do not add items manually, use `capture!` to add nodes to this BTreeMap.
20 | found_instances: BTreeMap<(String, usize, String), NodeID>,
21 | }
22 |
23 | impl IssueDetector for TxOriginUsedForAuthDetector {
24 | fn detect(&mut self, context: &WorkspaceContext) -> Result<bool, Box<dyn Error>> {
25 | for if_statement in context.if_statements() {
26 | // Check within the condition block only
27 | let ast_node: ASTNode = if_statement.condition.clone().into();
28 | self.check_eligibility_and_capture(context, &[&ast_node], &(if_statement.into()))?;
29 | }
30 |
31 | for function_call in context.function_calls() {
32 | if let Expression::Identifier(Identifier { name, .. }) =
33 | function_call.expression.as_ref()
34 | {
35 | if name != "require" {
36 | continue;
37 | }
38 |
39 | // Now, check for arguments of the `require(..., "message")` function call
40 | let arguments = function_call
41 | .arguments
42 | .clone()
43 | .into_iter()
44 | .map(|n| n.into())
45 | .collect::<Vec<ASTNode>>();
46 |
47 | let ast_nodes: &[&ASTNode] = &(arguments.iter().collect::<Vec<_>>());
48 | self.check_eligibility_and_capture(context, ast_nodes, &(function_call.into()))?;
49 | }
50 | }
51 |
52 | Ok(!self.found_instances.is_empty())
53 | }
54 |
55 | fn severity(&self) -> IssueSeverity {
56 | IssueSeverity::High
57 | }
58 |
59 | fn title(&self) -> String {
60 | String::from("Use of `tx.origin` for authentication")
61 | }
62 |
63 | fn description(&self) -> String {
64 | String::from(
65 | "Using `tx.origin` may lead to problems when users are interacting via smart contract with your \
66 | protocol. It is recommended to use `msg.sender` for authentication.",
67 | )
68 | }
69 |
70 | fn instances(&self) -> BTreeMap<(String, usize, String), NodeID> {
71 | self.found_instances.clone()
72 | }
73 |
74 | fn name(&self) -> String {
75 | format!("{}", IssueDetectorNamePool::TxOriginUsedForAuth)
76 | }
77 | }
78 |
79 | impl TxOriginUsedForAuthDetector {
80 | fn check_eligibility_and_capture(
81 | &mut self,
82 | context: &WorkspaceContext,
83 | check_nodes: &[&ASTNode],
84 | capture_node: &ASTNode,
85 | ) -> Result<(), Box<dyn Error>> {
86 | // Boilerplate
87 | let callgraphs = CallGraphConsumer::get(context, check_nodes, CallGraphDirection::Inward)?;
88 | for callgraph in callgraphs {
89 | let mut tracker = MsgSenderAndTxOriginTracker::default();
90 | callgraph.accept(context, &mut tracker)?;
91 |
92 | if tracker.satisfied() {
93 | capture!(self, context, capture_node);
94 | }
95 | }
96 | Ok(())
97 | }
98 | }
99 |
100 | #[derive(Default)]
101 | struct MsgSenderAndTxOriginTracker {
102 | reads_msg_sender: bool,
103 | reads_tx_origin: bool,
104 | }
105 |
106 | impl MsgSenderAndTxOriginTracker {
107 | /// To avoid FP (msg.sender == tx.origin) we require that tx.origin is present and msg.sender is
108 | /// absent for it to be considered satisfied
109 | fn satisfied(&self) -> bool {
110 | self.reads_tx_origin && !self.reads_msg_sender
111 | }
112 | }
113 |
114 | impl CallGraphVisitor for MsgSenderAndTxOriginTracker {
115 | fn visit_any(&mut self, node: &crate::ast::ASTNode) -> eyre::Result<()> {
116 | let member_accesses = ExtractMemberAccesses::from(node).extracted;
117 |
118 | let has_msg_sender = member_accesses.iter().any(|member_access| {
119 | member_access.member_name == "sender"
120 | && if let Expression::Identifier(identifier) = member_access.expression.as_ref() {
121 | identifier.name == "msg"
122 | } else {
123 | false
124 | }
125 | });
126 | self.reads_msg_sender = self.reads_msg_sender || has_msg_sender;
127 |
128 | let has_tx_origin = member_accesses.iter().any(|member_access| {
129 | member_access.member_name == "origin"
130 | && if let Expression::Identifier(identifier) = member_access.expression.as_ref() {
131 | identifier.name == "tx"
132 | } else {
133 | false
134 | }
135 | });
136 | self.reads_tx_origin = self.reads_tx_origin || has_tx_origin;
137 |
138 | Ok(())
139 | }
140 | }
141 |
142 | #[cfg(test)]
143 | mod tx_origin_used_for_auth_detector {
144 |
145 | use crate::detect::{
146 | detector::IssueDetector, high::tx_origin_used_for_auth::TxOriginUsedForAuthDetector,
147 | };
148 |
149 | #[test]
150 |
151 | fn test_tx_origin_used_for_auth() {
152 | let context = crate::detect::test_utils::load_solidity_source_unit(
153 | "../tests/contract-playground/src/TxOriginUsedForAuth.sol",
154 | );
155 |
156 | let mut detector = TxOriginUsedForAuthDetector::default();
157 | let found = detector.detect(&context).unwrap();
158 | assert!(found);
159 | assert_eq!(detector.instances().len(), 3);
160 | }
161 | }
162 |
```
--------------------------------------------------------------------------------
/.github/workflows/reports.yml:
--------------------------------------------------------------------------------
```yaml
1 | on: [push, pull_request, workflow_dispatch]
2 |
3 | name: Reports Workflow
4 |
5 | concurrency:
6 | group: ci-${{ github.ref }}-reports
7 | cancel-in-progress: true
8 |
9 | jobs:
10 | reports-setup:
11 | name: Check Reports
12 | runs-on: ubuntu-latest
13 | outputs:
14 | rust-nightly: nightly-2025-09-20
15 |
16 | strategy:
17 | fail-fast: false
18 | matrix:
19 | task:
20 | - report-workflow
21 | - uniswap_profile-workflow
22 | - sablier
23 | - adhoc-sol-files-workflow
24 | - nft-workflow
25 | - nft-workflow-env
26 | - ccip-functions-report
27 | - hardhat-playground-report
28 | - prb-math-report
29 | - report-json
30 | - adhoc-sol-files-highs-only-json
31 | - sarif-report
32 | - empty-report
33 |
34 | steps:
35 | - name: Checkout Sources
36 | uses: actions/checkout@v4
37 |
38 | - name: Checkout repository with submodules
39 | uses: actions/checkout@v4
40 | with:
41 | submodules: recursive
42 |
43 | - name: Cache submodules
44 | id: cache-submodules
45 | uses: actions/cache@v3
46 | with:
47 | path: .git/modules
48 | key: submodules-${{ runner.os }}-${{ hashFiles('.gitmodules') }}
49 | restore-keys: |
50 | submodules-${{ runner.os }}-${{ hashFiles('.gitmodules') }}
51 |
52 | - name: Install Rust Nightly (2025-01-01)
53 | uses: actions-rs/toolchain@v1
54 | with:
55 | profile: minimal
56 | toolchain: nightly-2025-09-20
57 | override: true
58 |
59 | - name: Setup Node.js
60 | uses: actions/setup-node@v4
61 | with:
62 | node-version: 20
63 | cache: 'npm'
64 |
65 | - name: Install pnpm
66 | uses: pnpm/action-setup@v4
67 | with:
68 | version: 8
69 |
70 | - name: Restore Rust Cache
71 | uses: Swatinem/rust-cache@v2
72 |
73 | - name: Prebuild (${{ matrix.task }})
74 | run: |
75 | cargo build
76 |
77 | - name: Generate Report (${{ matrix.task }})
78 | run: |
79 | case "${{ matrix.task }}" in
80 | report-workflow)
81 | cargo run -- -o ./reports/report-workflow.md --src src/ ./tests/contract-playground/ --skip-update-check
82 | diff ./reports/report.md ./reports/report-workflow.md
83 | ;;
84 | uniswap_profile-workflow)
85 | FOUNDRY_PROFILE=uniswap cargo run -- -o ./reports/uniswap_profile-workflow.md ./tests/contract-playground/ --skip-update-check
86 | diff reports/uniswap_profile.md reports/uniswap_profile-workflow.md
87 | ;;
88 | sablier)
89 | pnpm install --prefix tests/2024-05-Sablier/v2-core
90 | FOUNDRY_PROFILE=uniswap cargo run -- -o ./reports/sablier.md ./tests/2024-05-Sablier --skip-update-check
91 | diff reports/sablier-aderyn-toml-nested-root.md reports/sablier.md
92 | ;;
93 | adhoc-sol-files-workflow)
94 | cargo run -- -o ./reports/adhoc-sol-files-report-workflow.md ./tests/adhoc-sol-files --skip-update-check
95 | diff ./reports/adhoc-sol-files-report.md ./reports/adhoc-sol-files-report-workflow.md
96 | ;;
97 | nft-workflow)
98 | cargo run -- -o ./reports/nft-workflow-report.md --src src/ ./tests/foundry-nft-f23 --skip-update-check
99 | diff ./reports/nft-report.md ./reports/nft-workflow-report.md
100 | ;;
101 | nft-workflow-env)
102 | cargo run -- -o ./reports/nft-workflow-report-icm.md ./tests/foundry-nft-f23-icm --skip-update-check
103 | diff ./reports/nft-report-icm.md ./reports/nft-workflow-report-icm.md
104 | ;;
105 | ccip-functions-report)
106 | cargo run -- -o reports/ccip-functions-report-workflow.md tests/ccip-contracts/contracts --src src/v0.8/functions/ -x "tests/,test/,mocks/" --skip-update-check
107 | diff ./reports/ccip-functions-report.md ./reports/ccip-functions-report-workflow.md
108 | ;;
109 | hardhat-playground-report)
110 | cargo run -- tests/hardhat-js-playground -o reports/hardhat-playground-report-workflow.md --skip-update-check
111 | diff ./reports/hardhat-playground-report.md ./reports/hardhat-playground-report-workflow.md
112 | ;;
113 | prb-math-report)
114 | pnpm install --prefix tests/prb-math/
115 | cargo run -- ./tests/prb-math -o ./reports/prb-math-report-workflow.md --skip-update-check
116 | diff ./reports/prb-math-report.md ./reports/prb-math-report-workflow.md
117 | ;;
118 | report-json)
119 | cargo run -- -o ./reports/report-workflow.json -i src/ -x lib/ ./tests/contract-playground/ --skip-update-check
120 | diff ./reports/report.json ./reports/report-workflow.json
121 | ;;
122 | adhoc-sol-files-highs-only-json)
123 | cargo run -- -o ./reports/adhoc-sol-files-highs-only-report-workflow.json ./tests/adhoc-sol-files --skip-update-check --highs-only
124 | diff ./reports/adhoc-sol-files-highs-only-report.json ./reports/adhoc-sol-files-highs-only-report-workflow.json
125 | ;;
126 | sarif-report)
127 | cargo run -- -o ./reports/ci-report.sarif ./tests/contract-playground/ --skip-update-check
128 | diff ./reports/report.sarif ./reports/ci-report.sarif
129 | ;;
130 | empty-report)
131 | cargo run -- tests/contract-playground -o reports/empty_report_workflow.md -i IgnoreEverything.sol
132 | diff ./reports/empty_report.md ./reports/empty_report_workflow.md
133 | ;;
134 | esac
135 |
```
--------------------------------------------------------------------------------
/aderyn_core/src/ast/impls/disp/expressions.rs:
--------------------------------------------------------------------------------
```rust
1 | use crate::ast::*;
2 | use std::fmt::{Display, Write};
3 |
4 | impl Display for Expression {
5 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
6 | match self {
7 | Expression::Literal(expr) => expr.fmt(f)?,
8 | Expression::Identifier(expr) => expr.fmt(f)?,
9 | Expression::UnaryOperation(expr) => expr.fmt(f)?,
10 | Expression::BinaryOperation(expr) => expr.fmt(f)?,
11 | Expression::Conditional(expr) => expr.fmt(f)?,
12 | Expression::Assignment(expr) => expr.fmt(f)?,
13 | Expression::FunctionCall(expr) => expr.fmt(f)?,
14 | Expression::FunctionCallOptions(expr) => expr.fmt(f)?,
15 | Expression::IndexAccess(expr) => expr.fmt(f)?,
16 | Expression::IndexRangeAccess(expr) => expr.fmt(f)?,
17 | Expression::MemberAccess(expr) => expr.fmt(f)?,
18 | Expression::ElementaryTypeNameExpression(expr) => expr.fmt(f)?,
19 | Expression::TupleExpression(expr) => expr.fmt(f)?,
20 | Expression::NewExpression(expr) => expr.fmt(f)?,
21 | }
22 |
23 | Ok(())
24 | }
25 | }
26 |
27 | impl Display for UnaryOperation {
28 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 | f.write_fmt(format_args!("{}{}", self.sub_expression, self.operator.as_str()))
30 | }
31 | }
32 |
33 | impl Display for BinaryOperation {
34 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 | f.write_fmt(format_args!(
36 | "{} {} {}",
37 | self.left_expression, self.operator, self.right_expression
38 | ))
39 | }
40 | }
41 |
42 | impl Display for Conditional {
43 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 | f.write_fmt(format_args!(
45 | "{} ? {} : {}",
46 | self.condition, self.true_expression, self.false_expression
47 | ))
48 | }
49 | }
50 |
51 | impl Display for Assignment {
52 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 | f.write_fmt(format_args!(
54 | "{} {} {}",
55 | self.left_hand_side, self.operator, self.right_hand_side
56 | ))
57 | }
58 | }
59 |
60 | impl Display for FunctionCall {
61 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 | f.write_fmt(format_args!("{}", self.expression))?;
63 | f.write_str("(")?;
64 |
65 | for (i, argument) in self.arguments.iter().enumerate() {
66 | if i > 0 {
67 | f.write_str(", ")?;
68 | }
69 |
70 | f.write_fmt(format_args!("{argument}"))?;
71 | }
72 |
73 | f.write_str(")")
74 | }
75 | }
76 |
77 | impl Display for FunctionCallOptions {
78 | #[allow(clippy::print_in_format_impl)]
79 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 | let option_count = self.options.len();
81 |
82 | if self.names.len() != option_count {
83 | eprintln!("ERROR: invalid FunctionCallOptions: {:?}, {:?}", self.names, self.options);
84 |
85 | return Err(std::fmt::Error);
86 | }
87 |
88 | f.write_fmt(format_args!("{}", self.expression))?;
89 |
90 | f.write_char('{')?;
91 |
92 | for i in 0..option_count {
93 | if i > 0 {
94 | f.write_str(", ")?;
95 | }
96 |
97 | f.write_fmt(format_args!("{}: {}", self.names[i], self.options[i]))?;
98 | }
99 |
100 | f.write_char('}')?;
101 |
102 | if let Some(arguments) = self.arguments.as_ref() {
103 | f.write_char('(')?;
104 |
105 | for (i, argument) in arguments.iter().enumerate() {
106 | if i > 0 {
107 | f.write_str(", ")?;
108 | }
109 |
110 | f.write_fmt(format_args!("{argument}"))?;
111 | }
112 |
113 | f.write_char(')')?;
114 | }
115 |
116 | Ok(())
117 | }
118 | }
119 |
120 | impl Display for IndexAccess {
121 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 | if let Some(index_expression) = &self.index_expression {
123 | f.write_fmt(format_args!("{}[{}]", self.base_expression, index_expression))
124 | } else {
125 | f.write_fmt(format_args!("{}[]", self.base_expression))
126 | }
127 | }
128 | }
129 |
130 | impl Display for IndexRangeAccess {
131 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132 | f.write_fmt(format_args!("{}[", self.base_expression))?;
133 |
134 | if let Some(start_expression) = self.start_expression.as_ref() {
135 | f.write_fmt(format_args!("{start_expression}"))?;
136 | }
137 |
138 | f.write_str(":")?;
139 |
140 | if let Some(end_expression) = self.end_expression.as_ref() {
141 | f.write_fmt(format_args!("{end_expression}"))?;
142 | }
143 |
144 | f.write_str("]")
145 | }
146 | }
147 |
148 | impl Display for MemberAccess {
149 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 | f.write_fmt(format_args!("{}.{}", self.expression, self.member_name))
151 | }
152 | }
153 |
154 | impl Display for ElementaryTypeNameExpression {
155 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 | f.write_fmt(format_args!("{}", self.type_name))
157 | }
158 | }
159 |
160 | impl Display for TupleExpression {
161 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162 | f.write_str("(")?;
163 |
164 | for (i, component) in self.components.iter().enumerate() {
165 | if i > 0 {
166 | f.write_str(", ")?;
167 | }
168 |
169 | if let Some(component) = component {
170 | f.write_fmt(format_args!("{component}"))?;
171 | }
172 | }
173 |
174 | f.write_str(")")
175 | }
176 | }
177 |
178 | impl Display for NewExpression {
179 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180 | f.write_fmt(format_args!("new {}", self.type_name))
181 | }
182 | }
183 |
```
--------------------------------------------------------------------------------
/aderyn/src/lib.rs:
--------------------------------------------------------------------------------
```rust
1 | use aderyn_driver::detector::{IssueSeverity, get_all_detectors_names, get_issue_detector_by_name};
2 | use semver::Version;
3 | use serde_json::Value;
4 | use std::{cmp::Ordering, fs::File, io::Write, path::PathBuf, str::FromStr};
5 | use strum::IntoEnumIterator;
6 |
7 | pub mod birdsong;
8 | pub mod completions;
9 | pub mod lsp;
10 | pub mod mcp;
11 |
12 | mod panic;
13 |
14 | pub fn create_aderyn_toml_file_at(directory: String) {
15 | let solidity_dir = find_solidity_dir(&directory);
16 | let aderyn_toml_path = PathBuf::from_str(&directory).unwrap().join("aderyn.toml");
17 | let mut file = File::create_new(aderyn_toml_path.clone()).expect("File already exists!");
18 | file.write_fmt(format_args!(
19 | include_str!("../templates/aderyn.toml"),
20 | format!("\"{}\"", &solidity_dir)
21 | ))
22 | .expect("unable to write to aderyn.toml");
23 | println!("Created aderyn.toml at {}", aderyn_toml_path.display());
24 | }
25 |
26 | pub fn find_solidity_dir(root: &str) -> String {
27 | let path = PathBuf::from_str(root).expect("invalid path root");
28 | let indicators = ["hardhat.config.ts", "hardhat.config.js", "foundry.toml", "soldeer.toml"];
29 |
30 | // Check for indicators in the same directory level
31 | for indicator in indicators {
32 | let target = path.join(indicator);
33 | if target.is_file() {
34 | return path.to_string_lossy().to_string();
35 | }
36 | }
37 |
38 | // Check for indicators one level below
39 | for indicator in indicators {
40 | let mut nodes = std::fs::read_dir(path.clone())
41 | .expect("reading path failed")
42 | .flatten()
43 | .collect::<Vec<_>>();
44 | nodes.sort_by(|a, _| {
45 | if a.file_name().to_string_lossy().contains("contract") {
46 | Ordering::Less
47 | } else {
48 | Ordering::Greater
49 | }
50 | });
51 | for node in nodes {
52 | if !node.path().is_dir() {
53 | continue;
54 | }
55 | let target = node.path().join(indicator);
56 | if target.is_file() {
57 | let location = node.path();
58 | let toml_entry = location.strip_prefix(path).expect("stripping failed");
59 | return toml_entry.to_string_lossy().to_string();
60 | }
61 | }
62 | }
63 |
64 | root.to_string()
65 | }
66 |
67 | pub fn initialize_niceties() {
68 | // Crash with a nice message on panic
69 | panic::add_handler();
70 |
71 | // Logger
72 | #[cfg(debug_assertions)]
73 | {
74 | simplelog::WriteLogger::init(
75 | simplelog::LevelFilter::Info,
76 | simplelog::Config::default(),
77 | File::create(concat!(env!("CARGO_MANIFEST_DIR"), "/../app.log")).unwrap(),
78 | )
79 | .unwrap();
80 | }
81 | }
82 |
83 | pub fn print_detail_view(detector_name: &str) {
84 | let all_detector_names = get_all_detectors_names();
85 | if !all_detector_names.contains(&detector_name.to_string()) {
86 | println!("Couldn't recognize detector with name {}", detector_name);
87 | return;
88 | }
89 | let detector = get_issue_detector_by_name(detector_name);
90 | println!("\nDetector {}", detector_name);
91 | println!();
92 | println!("Title");
93 | println!("{}", detector.title());
94 | println!();
95 | println!("Severity");
96 | println!("{}", detector.severity());
97 | println!();
98 | println!("Description");
99 | println!("{}", detector.description());
100 | println!();
101 | }
102 |
103 | pub fn print_all_detectors_view() {
104 | let all_detector_names = get_all_detectors_names();
105 | println!("\nDetector Registry");
106 | println!();
107 | println!("{} Title (Rating)", right_pad("Name", 30));
108 | println!();
109 | for severity in IssueSeverity::iter() {
110 | print_detectors_view_with_severity(severity, &all_detector_names);
111 | println!();
112 | }
113 | println!();
114 | }
115 |
116 | pub fn print_detectors_view_with_severity(severity: IssueSeverity, detectors_names: &[String]) {
117 | let concerned_detectors = detectors_names
118 | .iter()
119 | .filter(|name| {
120 | let detector = get_issue_detector_by_name(name);
121 | detector.severity() == severity
122 | })
123 | .collect::<Vec<_>>();
124 |
125 | if concerned_detectors.is_empty() {
126 | return;
127 | }
128 |
129 | println!("{}\n", severity);
130 | for name in concerned_detectors {
131 | let detector = get_issue_detector_by_name(name);
132 | println!("{} - {}", right_pad(name, 30), detector.title(),);
133 | }
134 | println!();
135 | }
136 |
137 | fn right_pad(s: &str, by: usize) -> String {
138 | if s.len() > by {
139 | return s.to_string();
140 | }
141 | let extra_spaces = by - s.len();
142 | let spaces = " ".repeat(extra_spaces);
143 | let mut new_string = s.to_string();
144 | new_string.push_str(&spaces);
145 | new_string
146 | }
147 |
148 | pub static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
149 |
150 | pub fn aderyn_is_currently_running_newest_version() -> Option<bool> {
151 | let client = reqwest::blocking::Client::builder()
152 | .user_agent(APP_USER_AGENT)
153 | .build()
154 | .expect("client is unable to initialize");
155 |
156 | let latest_version_checker =
157 | client.get("https://api.github.com/repos/Cyfrin/aderyn/releases/latest").send().ok()?;
158 |
159 | let data = latest_version_checker.json::<Value>().ok()?;
160 | let version_string = data.get("tag_name")?.as_str()?;
161 | let newest = Version::parse(version_string.replace("aderyn-v", "").as_str()).ok()?;
162 | let current = Version::parse(env!("CARGO_PKG_VERSION")).expect("Pkg version not available");
163 |
164 | Some(current >= newest)
165 | }
166 |
167 | #[cfg(test)]
168 | mod latest_version_checker_tests {
169 | use super::*;
170 |
171 | #[test]
172 | #[ignore = "fails when frequently run as github will rate limit"]
173 | fn can_get_latest_version_from_github_releases() {
174 | assert!(aderyn_is_currently_running_newest_version().is_some())
175 | }
176 | }
177 |
```
--------------------------------------------------------------------------------
/benchmarks/non-reentrant-before-others/report/both/regression.svg:
--------------------------------------------------------------------------------
```
1 | <svg width="960" height="540" viewBox="0 0 960 540" xmlns="http://www.w3.org/2000/svg">
2 | <text x="480" y="32" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="16.129032258064516" opacity="1" fill="#000000">
3 | non-reentrant-before-others
4 | </text>
5 | <text x="27" y="263" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000" transform="rotate(270, 27, 263)">
6 | Total sample time (ms)
7 | </text>
8 | <text x="510" y="513" dy="-0.5ex" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
9 | Iterations (x 10^3)
10 | </text>
11 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="472" x2="87" y2="53"/>
12 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="237" y1="472" x2="237" y2="53"/>
13 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="388" y1="472" x2="388" y2="53"/>
14 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="539" y1="472" x2="539" y2="53"/>
15 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="690" y1="472" x2="690" y2="53"/>
16 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="841" y1="472" x2="841" y2="53"/>
17 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="472" x2="932" y2="472"/>
18 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="399" x2="932" y2="399"/>
19 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="326" x2="932" y2="326"/>
20 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="253" x2="932" y2="253"/>
21 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="180" x2="932" y2="180"/>
22 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="107" x2="932" y2="107"/>
23 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="86,53 86,472 "/>
24 | <text x="77" y="472" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
25 | 0.0
26 | </text>
27 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,472 86,472 "/>
28 | <text x="77" y="399" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
29 | 20.0
30 | </text>
31 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,399 86,399 "/>
32 | <text x="77" y="326" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
33 | 40.0
34 | </text>
35 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,326 86,326 "/>
36 | <text x="77" y="253" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
37 | 60.0
38 | </text>
39 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,253 86,253 "/>
40 | <text x="77" y="180" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
41 | 80.0
42 | </text>
43 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,180 86,180 "/>
44 | <text x="77" y="107" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
45 | 100.0
46 | </text>
47 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,107 86,107 "/>
48 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="87,473 932,473 "/>
49 | <text x="87" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
50 | 0
51 | </text>
52 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="87,473 87,478 "/>
53 | <text x="237" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
54 | 5
55 | </text>
56 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="237,473 237,478 "/>
57 | <text x="388" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
58 | 10
59 | </text>
60 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="388,473 388,478 "/>
61 | <text x="539" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
62 | 15
63 | </text>
64 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="539,473 539,478 "/>
65 | <text x="690" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
66 | 20
67 | </text>
68 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="690,473 690,478 "/>
69 | <text x="841" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
70 | 25
71 | </text>
72 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="841,473 841,478 "/>
73 | <polyline fill="none" opacity="1" stroke="#E31A1C" stroke-width="1" points="87,472 932,53 "/>
74 | <polygon opacity="0.25" fill="#E31A1C" points="87,472 932,63 932,53 "/>
75 | <polyline fill="none" opacity="1" stroke="#1F78B4" stroke-width="1" points="87,472 932,140 "/>
76 | <polygon opacity="0.25" fill="#1F78B4" points="87,472 932,142 932,136 "/>
77 | <text x="132" y="68" dy="0.76em" text-anchor="start" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
78 | Base Sample
79 | </text>
80 | <text x="132" y="83" dy="0.76em" text-anchor="start" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
81 | New Sample
82 | </text>
83 | <polyline fill="none" opacity="1" stroke="#E31A1C" stroke-width="2" points="102,73 122,73 "/>
84 | <polyline fill="none" opacity="1" stroke="#1F78B4" stroke-width="2" points="102,88 122,88 "/>
85 | </svg>
86 |
```
--------------------------------------------------------------------------------
/aderyn_core/src/detect/low/literal_instead_of_constant.rs:
--------------------------------------------------------------------------------
```rust
1 | use std::{
2 | collections::{BTreeMap, HashMap},
3 | error::Error,
4 | };
5 |
6 | use crate::{
7 | ast::{Literal, LiteralKind, NodeID},
8 | capture,
9 | context::{
10 | browser::{
11 | ExtractFunctionDefinitions, ExtractLiterals, ExtractModifierDefinitions,
12 | GetImmediateParent,
13 | },
14 | workspace::{ASTNode, WorkspaceContext},
15 | },
16 | detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity},
17 | };
18 | use eyre::Result;
19 |
20 | #[derive(Default)]
21 | pub struct LiteralsInsteadOfConstantsDetector {
22 | // Keys are: [0] source file name, [1] line number, [2] character location of node.
23 | // Do not add items manually, use `capture!` to add nodes to this BTreeMap.
24 | found_instances: BTreeMap<(String, usize, String), NodeID>,
25 | }
26 |
27 | impl IssueDetector for LiteralsInsteadOfConstantsDetector {
28 | fn detect(&mut self, context: &WorkspaceContext) -> Result<bool, Box<dyn Error>> {
29 | // Get all contracts
30 | // For each contract
31 | // Get all Function definitions (and to the same for modifiers)
32 | // Get all literals
33 | // For each literal
34 | // if literal.value is not 0 or 1
35 | // if the literal.value appears more than once, then capture all instances
36 |
37 | for contract in context.contract_definitions() {
38 | let mut literal_values_found: HashMap<String, Vec<Literal>> = HashMap::new();
39 |
40 | for function in ExtractFunctionDefinitions::from(contract).extracted.into_iter() {
41 | for literal in ExtractLiterals::from(&function).extracted.into_iter() {
42 | if (literal.kind == LiteralKind::Number
43 | && literal.value != Some(String::from("0"))
44 | && literal.value != Some(String::from("1")))
45 | && literal.value != Some(String::from("2"))
46 | || literal.kind == LiteralKind::HexString
47 | || literal.kind == LiteralKind::Address
48 | {
49 | // If the literal is used as an index access in a variable, don't capture it
50 | if let Some(ASTNode::IndexAccess(_)) = literal.parent(context) {
51 | continue;
52 | }
53 |
54 | if let Some(literal_value) = literal.value.as_ref() {
55 | if literal_values_found.contains_key(literal_value) {
56 | literal_values_found.get_mut(literal_value).unwrap().push(literal);
57 | } else {
58 | literal_values_found.insert(literal_value.clone(), vec![literal]);
59 | }
60 | }
61 | }
62 | }
63 | }
64 |
65 | for modifier in ExtractModifierDefinitions::from(contract).extracted.into_iter() {
66 | for literal in ExtractLiterals::from(&modifier).extracted.into_iter() {
67 | if (literal.kind == LiteralKind::Number
68 | && literal.value != Some(String::from("0"))
69 | && literal.value != Some(String::from("1")))
70 | && literal.value != Some(String::from("2"))
71 | || literal.kind == LiteralKind::HexString
72 | || literal.kind == LiteralKind::Address
73 | {
74 | // If the literal is used as an index access in a variable, don't capture it
75 | if let Some(ASTNode::IndexAccess(_)) = context.get_parent(literal.id) {
76 | continue;
77 | }
78 |
79 | if let Some(literal_value) = literal.value.as_ref() {
80 | if literal_values_found.contains_key(literal_value) {
81 | literal_values_found.get_mut(literal_value).unwrap().push(literal);
82 | } else {
83 | literal_values_found.insert(literal_value.clone(), vec![literal]);
84 | }
85 | }
86 | }
87 | }
88 | }
89 |
90 | for (_, literals) in literal_values_found.iter() {
91 | if literals.len() > 1 {
92 | for literal in literals {
93 | capture!(self, context, literal);
94 | }
95 | }
96 | }
97 | }
98 |
99 | Ok(!self.found_instances.is_empty())
100 | }
101 |
102 | fn title(&self) -> String {
103 | String::from("Literal Instead of Constant")
104 | }
105 |
106 | fn description(&self) -> String {
107 | String::from(
108 | "Define and use `constant` variables instead of using literals. If the same constant literal value is used multiple times, create a constant state variable and reference it throughout the contract.",
109 | )
110 | }
111 |
112 | fn severity(&self) -> IssueSeverity {
113 | IssueSeverity::Low
114 | }
115 |
116 | fn instances(&self) -> BTreeMap<(String, usize, String), NodeID> {
117 | self.found_instances.clone()
118 | }
119 |
120 | fn name(&self) -> String {
121 | format!("{}", IssueDetectorNamePool::LiteralInsteadOfConstant)
122 | }
123 | }
124 |
125 | #[cfg(test)]
126 | mod constants_instead_of_literals_tests {
127 |
128 | use super::LiteralsInsteadOfConstantsDetector;
129 | use crate::detect::detector::IssueDetector;
130 |
131 | #[test]
132 |
133 | fn test_constants_instead_of_literals_by_loading_contract_directly() {
134 | let context = crate::detect::test_utils::load_solidity_source_unit(
135 | "../tests/contract-playground/src/ConstantsLiterals.sol",
136 | );
137 |
138 | let mut detector = LiteralsInsteadOfConstantsDetector::default();
139 | let found = detector.detect(&context).unwrap();
140 | assert!(found);
141 | assert_eq!(detector.instances().len(), 8);
142 | }
143 | }
144 |
```
--------------------------------------------------------------------------------
/aderyn_core/src/context/router/external_calls.rs:
--------------------------------------------------------------------------------
```rust
1 | use super::{ECDest, Router};
2 | use crate::{
3 | ast::{
4 | ASTNode, ContractDefinition, FunctionCall, FunctionDefinition, FunctionKind, Visibility,
5 | },
6 | context::workspace::WorkspaceContext,
7 | };
8 | use std::collections::{HashMap, hash_map::Entry};
9 |
10 | impl Router {
11 | /// Given a function call, resolve the function definition with it's selector.
12 | ///
13 | /// If no function is found with the said selector, it tries to retrieve a fallback function.
14 | /// Since function selector is data that's being passed it cannot point to receive function.
15 | ///
16 | /// Suspect here could be a variable as well
17 | pub(super) fn _resolve_external_call<'a>(
18 | &self,
19 | context: &'a WorkspaceContext,
20 | base_contract: &'a ContractDefinition,
21 | func_call: &'a FunctionCall,
22 | ) -> Option<ECDest> {
23 | // do not resolve if it's internal function call
24 | if func_call.is_internal_call() == Some(true) {
25 | return None;
26 | }
27 |
28 | // works for both public variables and functions
29 | let selector = func_call.suspected_function_selector(context)?;
30 | self._resolve_function_selector(base_contract, selector)
31 | }
32 |
33 | pub(super) fn _resolve_fallback_function<'a>(
34 | &self,
35 | context: &'a WorkspaceContext,
36 | base_contract: &'a ContractDefinition,
37 | ) -> Option<&'a FunctionDefinition> {
38 | // check if it's illegal base contract type
39 | if !base_contract.is_deployable_contract() {
40 | return None;
41 | }
42 | let lookup_index = self.external_calls.get(&base_contract.id)?;
43 | if let Some(ECDest::Fallback(func_id)) = lookup_index.routes.get("FALLBACK")
44 | && let Some(ASTNode::FunctionDefinition(fallback)) = context.nodes.get(func_id)
45 | {
46 | return Some(fallback);
47 | }
48 | None
49 | }
50 |
51 | pub(super) fn _resolve_receive_function<'a>(
52 | &self,
53 | context: &'a WorkspaceContext,
54 | base_contract: &'a ContractDefinition,
55 | ) -> Option<&'a FunctionDefinition> {
56 | // check if it's illegal base contract type
57 | if !base_contract.is_deployable_contract() {
58 | return None;
59 | }
60 | let lookup_index = self.external_calls.get(&base_contract.id)?;
61 | if let Some(ECDest::Receive(func_id)) = lookup_index.routes.get("RECEIVE")
62 | && let Some(ASTNode::FunctionDefinition(fallback)) = context.nodes.get(func_id)
63 | {
64 | return Some(fallback);
65 | }
66 | None
67 | }
68 |
69 | pub(super) fn _resolve_function_selector(
70 | &self,
71 | base_contract: &ContractDefinition,
72 | selector: impl AsRef<str>,
73 | ) -> Option<ECDest> {
74 | // check if it's illegal base contract type
75 | if !base_contract.is_deployable_contract() {
76 | return None;
77 | }
78 |
79 | let lookup_index = self.external_calls.get(&base_contract.id)?;
80 |
81 | match lookup_index.routes.get(selector.as_ref()) {
82 | Some(resolved) => Some(resolved.clone()),
83 | None => lookup_index.routes.get("FALLBACK").cloned(),
84 | }
85 | }
86 | }
87 |
88 | /// If function selector field isn't present, this algorithm cannot work.
89 | /// Therefore, if for some reason it's not found, we return an empty hashmap
90 | pub(super) fn build_ec_router_for_contract(
91 | context: &WorkspaceContext,
92 | base_contract: &ContractDefinition,
93 | ) -> HashMap<String, ECDest> {
94 | let c3 = base_contract.c3(context).collect::<Vec<_>>();
95 | let mut routes = HashMap::new();
96 | for contract in c3.iter() {
97 | // Loop through public state variables
98 | for var in contract.top_level_variables() {
99 | if var.visibility == Visibility::Public {
100 | let Some(func_selector) = var.function_selector.as_ref() else {
101 | return HashMap::new();
102 | };
103 | if let Entry::Vacant(e) = routes.entry(func_selector.to_string()) {
104 | e.insert(ECDest::PseudoExtFn(var.id));
105 | }
106 | }
107 | }
108 | // Loop through externally available functions
109 | for func in contract.function_definitions() {
110 | match *func.kind() {
111 | FunctionKind::Function => {
112 | match func.visibility {
113 | Visibility::Public => {
114 | let Some(func_selector) = func.function_selector.as_ref() else {
115 | return HashMap::new();
116 | };
117 | if let Entry::Vacant(e) = routes.entry(func_selector.to_string()) {
118 | e.insert(ECDest::PublicFn(func.id));
119 | }
120 | }
121 | Visibility::External => {
122 | let Some(func_selector) = func.function_selector.as_ref() else {
123 | return HashMap::new();
124 | };
125 | if let Entry::Vacant(e) = routes.entry(func_selector.to_string()) {
126 | e.insert(ECDest::RealExtFn(func.id));
127 | }
128 | }
129 | _ => {}
130 | };
131 | }
132 | FunctionKind::Receive => {
133 | if let Entry::Vacant(e) = routes.entry("RECEIVE".to_string()) {
134 | e.insert(ECDest::Receive(func.id));
135 | }
136 | }
137 | FunctionKind::Fallback => {
138 | if let Entry::Vacant(e) = routes.entry("FALLBACK".to_string()) {
139 | e.insert(ECDest::Fallback(func.id));
140 | }
141 | }
142 | FunctionKind::FreeFunction => unreachable!(), // can't be inside a contract.
143 | FunctionKind::Constructor => {}
144 | };
145 | }
146 | }
147 | routes
148 | }
149 |
```