Merge branch 'dev' into top-level-type-aliases

pull/1579/head
alpharush 2 years ago committed by GitHub
commit c2a397e307
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      .github/workflows/IR.yml
  2. 5
      .github/workflows/ci.yml
  3. 3
      .github/workflows/detectors.yml
  4. 87
      .github/workflows/doctor.yml
  5. 3
      .github/workflows/features.yml
  6. 10
      CODEOWNERS
  7. 5
      Dockerfile
  8. 2
      setup.py
  9. 45
      slither/__main__.py
  10. 4
      slither/core/children/child_function.py
  11. 3
      slither/core/declarations/function.py
  12. 4
      slither/core/expressions/identifier.py
  13. 21
      slither/core/expressions/literal.py
  14. 4
      slither/core/slither_core.py
  15. 4
      slither/core/variables/local_variable.py
  16. 3
      slither/detectors/all_detectors.py
  17. 45
      slither/detectors/variables/could_be_constant.py
  18. 44
      slither/detectors/variables/could_be_immutable.py
  19. 125
      slither/detectors/variables/possible_const_state_variables.py
  20. 125
      slither/detectors/variables/unchanged_state_variables.py
  21. 6
      slither/formatters/variables/unchanged_state_variables.py
  22. 53
      slither/printers/guidance/echidna.py
  23. 48
      slither/slither.py
  24. 25
      slither/slithir/convert.py
  25. 4
      slither/solc_parsing/declarations/function.py
  26. 35
      slither/solc_parsing/slither_compilation_unit_solc.py
  27. 4
      slither/solc_parsing/yul/evm_functions.py
  28. 86
      slither/solc_parsing/yul/parse_yul.py
  29. 5
      slither/tools/doctor/__main__.py
  30. 2
      slither/tools/doctor/checks/__init__.py
  31. 85
      slither/tools/doctor/checks/paths.py
  32. 10
      slither/tools/doctor/checks/versions.py
  33. 5
      slither/tools/documentation/README.md
  34. 0
      slither/tools/documentation/__init__.py
  35. 280
      slither/tools/documentation/__main__.py
  36. 6
      slither/tools/slither_format/slither_format.py
  37. 60
      slither/utils/codex.py
  38. 1
      slither/utils/command_line.py
  39. 82
      slither/visitors/expression/constants_folding.py
  40. 21
      slither/visitors/slithir/expression_to_slithir.py
  41. BIN
      tests/ast-parsing/compile/complex_imports/import_aliases_issue_1319/test.sol-0.5.12-compact.zip
  42. BIN
      tests/ast-parsing/compile/complex_imports/import_aliases_issue_1319/test.sol-0.5.12-legacy.zip
  43. BIN
      tests/ast-parsing/compile/custom-error-selector.sol-0.8.10-compact.zip
  44. BIN
      tests/ast-parsing/compile/custom-error-selector.sol-0.8.11-compact.zip
  45. BIN
      tests/ast-parsing/compile/custom-error-selector.sol-0.8.12-compact.zip
  46. BIN
      tests/ast-parsing/compile/custom-error-selector.sol-0.8.13-compact.zip
  47. BIN
      tests/ast-parsing/compile/custom-error-selector.sol-0.8.14-compact.zip
  48. BIN
      tests/ast-parsing/compile/custom-error-selector.sol-0.8.15-compact.zip
  49. BIN
      tests/ast-parsing/compile/custom-error-selector.sol-0.8.4-compact.zip
  50. BIN
      tests/ast-parsing/compile/custom-error-selector.sol-0.8.5-compact.zip
  51. BIN
      tests/ast-parsing/compile/custom-error-selector.sol-0.8.6-compact.zip
  52. BIN
      tests/ast-parsing/compile/custom-error-selector.sol-0.8.7-compact.zip
  53. BIN
      tests/ast-parsing/compile/custom-error-selector.sol-0.8.8-compact.zip
  54. BIN
      tests/ast-parsing/compile/custom-error-selector.sol-0.8.9-compact.zip
  55. BIN
      tests/ast-parsing/compile/user_defined_value_type/using-for-0.8.8.sol-0.8.10-compact.zip
  56. BIN
      tests/ast-parsing/compile/user_defined_value_type/using-for-0.8.8.sol-0.8.11-compact.zip
  57. BIN
      tests/ast-parsing/compile/user_defined_value_type/using-for-0.8.8.sol-0.8.12-compact.zip
  58. BIN
      tests/ast-parsing/compile/user_defined_value_type/using-for-0.8.8.sol-0.8.13-compact.zip
  59. BIN
      tests/ast-parsing/compile/user_defined_value_type/using-for-0.8.8.sol-0.8.14-compact.zip
  60. BIN
      tests/ast-parsing/compile/user_defined_value_type/using-for-0.8.8.sol-0.8.15-compact.zip
  61. BIN
      tests/ast-parsing/compile/user_defined_value_type/using-for-0.8.8.sol-0.8.8-compact.zip
  62. BIN
      tests/ast-parsing/compile/using-for-in-library-0.8.0.sol-0.8.15-compact.zip
  63. BIN
      tests/ast-parsing/compile/yul-top-level-0.8.0.sol-0.8.0-compact.zip
  64. 1
      tests/ast-parsing/complex_imports/import_aliases_issue_1319/import.sol
  65. 9
      tests/ast-parsing/complex_imports/import_aliases_issue_1319/test.sol
  66. 9
      tests/ast-parsing/complex_imports/import_aliases_issue_1319/test_fail.sol
  67. 13
      tests/ast-parsing/custom-error-selector.sol
  68. 6
      tests/ast-parsing/expected/complex_imports/import_aliases_issue_1319/test.sol-0.5.12-compact.json
  69. 6
      tests/ast-parsing/expected/complex_imports/import_aliases_issue_1319/test.sol-0.5.12-legacy.json
  70. 10
      tests/ast-parsing/expected/custom-error-selector.sol-0.8.10-compact.json
  71. 10
      tests/ast-parsing/expected/custom-error-selector.sol-0.8.11-compact.json
  72. 10
      tests/ast-parsing/expected/custom-error-selector.sol-0.8.12-compact.json
  73. 10
      tests/ast-parsing/expected/custom-error-selector.sol-0.8.13-compact.json
  74. 10
      tests/ast-parsing/expected/custom-error-selector.sol-0.8.14-compact.json
  75. 10
      tests/ast-parsing/expected/custom-error-selector.sol-0.8.15-compact.json
  76. 10
      tests/ast-parsing/expected/custom-error-selector.sol-0.8.4-compact.json
  77. 10
      tests/ast-parsing/expected/custom-error-selector.sol-0.8.5-compact.json
  78. 10
      tests/ast-parsing/expected/custom-error-selector.sol-0.8.6-compact.json
  79. 10
      tests/ast-parsing/expected/custom-error-selector.sol-0.8.7-compact.json
  80. 10
      tests/ast-parsing/expected/custom-error-selector.sol-0.8.8-compact.json
  81. 10
      tests/ast-parsing/expected/custom-error-selector.sol-0.8.9-compact.json
  82. 11
      tests/ast-parsing/expected/user_defined_value_type/using-for-0.8.8.sol-0.8.10-compact.json
  83. 11
      tests/ast-parsing/expected/user_defined_value_type/using-for-0.8.8.sol-0.8.11-compact.json
  84. 11
      tests/ast-parsing/expected/user_defined_value_type/using-for-0.8.8.sol-0.8.12-compact.json
  85. 11
      tests/ast-parsing/expected/user_defined_value_type/using-for-0.8.8.sol-0.8.13-compact.json
  86. 11
      tests/ast-parsing/expected/user_defined_value_type/using-for-0.8.8.sol-0.8.14-compact.json
  87. 11
      tests/ast-parsing/expected/user_defined_value_type/using-for-0.8.8.sol-0.8.15-compact.json
  88. 11
      tests/ast-parsing/expected/user_defined_value_type/using-for-0.8.8.sol-0.8.8-compact.json
  89. 8
      tests/ast-parsing/expected/using-for-in-library-0.8.0.sol-0.8.15-compact.json
  90. 22
      tests/ast-parsing/user_defined_value_type/using-for-0.8.8.sol
  91. 14
      tests/ast-parsing/using-for-in-library-0.8.0.sol
  92. 16
      tests/ast-parsing/yul-top-level-0.8.0.sol
  93. 14
      tests/constant_folding_binop.sol
  94. 5
      tests/detectors/constable-states/0.4.25/const_state_variables.sol
  95. 224
      tests/detectors/constable-states/0.4.25/const_state_variables.sol.0.4.25.CouldBeConstant.json
  96. 6
      tests/detectors/constable-states/0.5.16/const_state_variables.sol
  97. 276
      tests/detectors/constable-states/0.5.16/const_state_variables.sol.0.5.16.CouldBeConstant.json
  98. 39
      tests/detectors/constable-states/0.6.11/const_state_variables.sol
  99. 307
      tests/detectors/constable-states/0.6.11/const_state_variables.sol.0.6.11.CouldBeConstant.json
  100. 38
      tests/detectors/constable-states/0.7.6/const_state_variables.sol
  101. Some files were not shown because too many files have changed in this diff Show More

@ -34,9 +34,14 @@ jobs:
- name: Install dependencies
run: |
pip install ".[dev]"
solc-select install all
solc-select use 0.8.11
solc-select install 0.5.0
solc-select use 0.8.11 --always-install
- name: Install old solc
if: matrix.os == 'ubuntu-latest'
run: solc-select install 0.4.0
- name: Test with pytest
run: |
pytest tests/test_ssa_generation.py
pytest tests/test_ssa_generation.py

@ -55,8 +55,9 @@ jobs:
- name: Install dependencies
run: |
pip install ".[dev]"
solc-select install all
solc-select use 0.5.1
solc-select use 0.4.25 --always-install
solc-select use 0.8.0 --always-install
solc-select use 0.5.1 --always-install
pip install typing_extensions==4.1.1
pip install importlib_metadata==4.8.3

@ -35,8 +35,7 @@ jobs:
run: |
pip install ".[dev]"
solc-select install all
solc-select use 0.7.3
solc-select use 0.7.3 --always-install
- name: Test with pytest
run: |
pytest tests/test_detectors.py

@ -0,0 +1,87 @@
---
name: CI
defaults:
run:
shell: bash
on:
workflow_dispatch:
pull_request:
paths:
- 'slither/tools/doctor/**'
- '.github/workflows/doctor.yml'
jobs:
slither-doctor:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: ["ubuntu-latest", "windows-2022"]
python: ["3.8", "3.9", "3.10", "3.11"]
exclude:
# strange failure
- os: windows-2022
python: 3.8
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}
- name: Try system-wide Slither
run: |
echo "::group::Install slither"
pip3 install .
echo "::endgroup::"
# escape cwd so python doesn't pick up local module
cd /
echo "::group::Via module"
python3 -m slither.tools.doctor .
echo "::endgroup::"
echo "::group::Via binary"
slither-doctor .
echo "::endgroup::"
- name: Try user Slither
run: |
echo "::group::Install slither"
pip3 install --user .
echo "::endgroup::"
# escape cwd so python doesn't pick up local module
cd /
echo "::group::Via module"
python3 -m slither.tools.doctor .
echo "::endgroup::"
echo "::group::Via binary"
slither-doctor .
echo "::endgroup::"
- name: Try venv Slither
run: |
echo "::group::Install slither"
python3 -m venv venv
source venv/bin/activate || source venv/Scripts/activate
hash -r
pip3 install .
echo "::endgroup::"
# escape cwd so python doesn't pick up local module
cd /
echo "::group::Via module"
python3 -m slither.tools.doctor .
echo "::endgroup::"
echo "::group::Via binary"
slither-doctor .
echo "::endgroup::"

@ -35,8 +35,7 @@ jobs:
run: |
pip install ".[dev]"
solc-select install all
solc-select use 0.8.0
solc-select use 0.8.0 --always-install
cd tests/test_node_modules/
npm install hardhat

@ -1,4 +1,6 @@
* @montyly @0xalpharush @smonicas
/slither/tools/read_storage @0xalpharush
/slither/slithir/ @montyly
/slither/analyses/ @montyly
* @montyly @0xalpharush @smonicas
/slither/tools/read_storage/ @0xalpharush
/slither/tools/doctor/ @elopez
/slither/slithir/ @montyly
/slither/analyses/ @montyly
/.github/workflows/ @elopez

@ -2,6 +2,7 @@
FROM ubuntu:jammy AS python-wheels
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
gcc \
git \
python3-dev \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
@ -9,7 +10,7 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-ins
COPY . /slither
RUN cd /slither && \
echo pip3 install --no-cache-dir --upgrade pip && \
pip3 install --no-cache-dir --upgrade pip && \
pip3 wheel -w /wheels . solc-select pip setuptools wheel
@ -44,7 +45,7 @@ ENV PATH="/home/slither/.local/bin:${PATH}"
# no-index ensures we install the freshly-built wheels
RUN --mount=type=bind,target=/mnt,source=/wheels,from=python-wheels \
pip3 install --user --no-cache-dir --upgrade --no-index --find-links /mnt pip slither-analyzer solc-select
pip3 install --user --no-cache-dir --upgrade --no-index --find-links /mnt --no-deps /mnt/*.whl
RUN solc-select install 0.4.25 && solc-select use 0.4.25

@ -12,6 +12,7 @@ setup(
packages=find_packages(),
python_requires=">=3.8",
install_requires=[
"packaging",
"prettytable>=0.7.2",
"pycryptodome>=3.4.6",
# "crytic-compile>=0.2.4",
@ -46,6 +47,7 @@ setup(
"slither-mutate = slither.tools.mutator.__main__:main",
"slither-read-storage = slither.tools.read_storage.__main__:main",
"slither-doctor = slither.tools.doctor.__main__:main",
"slither-documentation = slither.tools.documentation.__main__:main",
]
},
)

@ -24,6 +24,7 @@ from slither.detectors.abstract_detector import AbstractDetector, DetectorClassi
from slither.printers import all_printers
from slither.printers.abstract_printer import AbstractPrinter
from slither.slither import Slither
from slither.utils import codex
from slither.utils.output import output_to_json, output_to_zip, output_to_sarif, ZIP_TYPES_ACCEPTED
from slither.utils.output_capture import StandardOutputCapture
from slither.utils.colors import red, set_colorization_enabled
@ -314,7 +315,6 @@ def parse_args(
"Checklist (consider using https://github.com/crytic/slither-action)"
)
group_misc = parser.add_argument_group("Additional options")
group_codex = parser.add_argument_group("Codex (https://beta.openai.com/docs/guides/code)")
group_detector.add_argument(
"--detect",
@ -555,47 +555,14 @@ def parse_args(
default=False,
)
group_codex.add_argument(
"--codex",
help="Enable codex (require an OpenAI API Key)",
action="store_true",
default=defaults_flag_in_config["codex"],
)
group_codex.add_argument(
"--codex-log",
help="Log codex queries (in crytic_export/codex/)",
group_misc.add_argument(
"--no-fail",
help="Do not fail in case of parsing (echidna mode only)",
action="store_true",
default=False,
)
group_codex.add_argument(
"--codex-contracts",
help="Comma separated list of contracts to submit to OpenAI Codex",
action="store",
default=defaults_flag_in_config["codex_contracts"],
default=defaults_flag_in_config["no_fail"],
)
group_codex.add_argument(
"--codex-model",
help="Name of the Codex model to use (affects pricing). Defaults to 'text-davinci-003'",
action="store",
default=defaults_flag_in_config["codex_model"],
)
group_codex.add_argument(
"--codex-temperature",
help="Temperature to use with Codex. Lower number indicates a more precise answer while higher numbers return more creative answers. Defaults to 0",
action="store",
default=defaults_flag_in_config["codex_temperature"],
)
group_codex.add_argument(
"--codex-max-tokens",
help="Maximum amount of tokens to use on the response. This number plus the size of the prompt can be no larger than the limit (4097 for text-davinci-003)",
action="store",
default=defaults_flag_in_config["codex_max_tokens"],
)
codex.init_parser(parser)
# debugger command
parser.add_argument("--debug", help=argparse.SUPPRESS, action="store_true", default=False)

@ -5,11 +5,11 @@ if TYPE_CHECKING:
class ChildFunction:
def __init__(self):
def __init__(self) -> None:
super().__init__()
self._function = None
def set_function(self, function: "Function"):
def set_function(self, function: "Function") -> None:
self._function = function
@property

@ -220,6 +220,9 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
self._id: Optional[str] = None
# To be improved with a parsing of the documentation
self.has_documentation: bool = False
###################################################################################
###################################################################################
# region General properties

@ -7,7 +7,7 @@ if TYPE_CHECKING:
class Identifier(ExpressionTyped):
def __init__(self, value):
def __init__(self, value) -> None:
super().__init__()
self._value: "Variable" = value
@ -15,5 +15,5 @@ class Identifier(ExpressionTyped):
def value(self) -> "Variable":
return self._value
def __str__(self):
def __str__(self) -> str:
return str(self._value)

@ -10,16 +10,25 @@ if TYPE_CHECKING:
class Literal(Expression):
def __init__(self, value, custom_type, subdenomination=None):
def __init__(
self, value: Union[int, str], custom_type: "Type", subdenomination: Optional[str] = None
):
super().__init__()
self._value: Union[int, str] = value
self._value = value
self._type = custom_type
self._subdenomination: Optional[str] = subdenomination
self._subdenomination = subdenomination
@property
def value(self) -> Union[int, str]:
return self._value
@property
def converted_value(self) -> Union[int, str]:
"""Return the value of the literal, accounting for subdenomination e.g. ether"""
if self.subdenomination:
return convert_subdenomination(self._value, self.subdenomination)
return self._value
@property
def type(self) -> "Type":
return self._type
@ -28,9 +37,9 @@ class Literal(Expression):
def subdenomination(self) -> Optional[str]:
return self._subdenomination
def __str__(self):
def __str__(self) -> str:
if self.subdenomination:
return str(convert_subdenomination(self._value, self.subdenomination))
return str(self.converted_value)
if self.type in Int + Uint + Fixed + Ufixed + ["address"]:
return str(convert_string_to_int(self._value))
@ -38,7 +47,7 @@ class Literal(Expression):
# be sure to handle any character
return str(self._value)
def __eq__(self, other):
def __eq__(self, other) -> bool:
if not isinstance(other, Literal):
return False
return (self.value, self.subdenomination) == (other.value, other.subdenomination)

@ -92,6 +92,10 @@ class SlitherCore(Context):
# But we allow to alter this (ex: file.sol:1) for vscode integration
self.line_prefix: str = "#"
# Use by the echidna printer
# If true, partial analysis is allowed
self.no_fail = False
@property
def compilation_units(self) -> List[SlitherCompilationUnit]:
return list(self._compilation_units)

@ -11,11 +11,11 @@ from slither.core.declarations.structure import Structure
class LocalVariable(ChildFunction, Variable):
def __init__(self):
def __init__(self) -> None:
super().__init__()
self._location: Optional[str] = None
def set_location(self, loc: str):
def set_location(self, loc: str) -> None:
self._location = loc
@property

@ -19,7 +19,8 @@ from .reentrancy.reentrancy_eth import ReentrancyEth
from .reentrancy.reentrancy_no_gas import ReentrancyNoGas
from .reentrancy.reentrancy_events import ReentrancyEvent
from .variables.unused_state_variables import UnusedStateVars
from .variables.possible_const_state_variables import ConstCandidateStateVars
from .variables.could_be_constant import CouldBeConstant
from .variables.could_be_immutable import CouldBeImmutable
from .statements.tx_origin import TxOrigin
from .statements.assembly import Assembly
from .operations.low_level_calls import LowLevelCalls

@ -0,0 +1,45 @@
from typing import List, Dict
from slither.utils.output import Output
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.formatters.variables.unchanged_state_variables import custom_format
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from .unchanged_state_variables import UnchangedStateVariables
class CouldBeConstant(AbstractDetector):
"""
State variables that could be declared as constant.
Not all types for constants are implemented in Solidity as of 0.4.25.
The only supported types are value types and strings (ElementaryType).
Reference: https://solidity.readthedocs.io/en/latest/contracts.html#constant-state-variables
"""
ARGUMENT = "constable-states"
HELP = "State variables that could be declared constant"
IMPACT = DetectorClassification.OPTIMIZATION
CONFIDENCE = DetectorClassification.HIGH
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-constant"
WIKI_TITLE = "State variables that could be declared constant"
WIKI_DESCRIPTION = "State variables that are not updated following deployment should be declared constant to save gas."
WIKI_RECOMMENDATION = "Add the `constant` attribute to state variables that never change."
def _detect(self) -> List[Output]:
"""Detect state variables that could be constant"""
results = {}
unchanged_state_variables = UnchangedStateVariables(self.compilation_unit)
unchanged_state_variables.detect()
for variable in unchanged_state_variables.constant_candidates:
results[variable.canonical_name] = self.generate_result(
[variable, " should be constant \n"]
)
# Order by canonical name for deterministic results
return [results[k] for k in sorted(results)]
@staticmethod
def _format(compilation_unit: SlitherCompilationUnit, result: Dict) -> None:
custom_format(compilation_unit, result, "constant")

@ -0,0 +1,44 @@
from typing import List, Dict
from slither.utils.output import Output
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.formatters.variables.unchanged_state_variables import custom_format
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from .unchanged_state_variables import UnchangedStateVariables
class CouldBeImmutable(AbstractDetector):
"""
State variables that could be declared immutable.
# Immutable attribute available in Solidity 0.6.5 and above
# https://blog.soliditylang.org/2020/04/06/solidity-0.6.5-release-announcement/
"""
# VULNERABLE_SOLC_VERSIONS =
ARGUMENT = "immutable-states"
HELP = "State variables that could be declared immutable"
IMPACT = DetectorClassification.OPTIMIZATION
CONFIDENCE = DetectorClassification.HIGH
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-immutable"
WIKI_TITLE = "State variables that could be declared immutable"
WIKI_DESCRIPTION = "State variables that are not updated following deployment should be declared immutable to save gas."
WIKI_RECOMMENDATION = "Add the `immutable` attribute to state variables that never change or are set only in the constructor."
def _detect(self) -> List[Output]:
"""Detect state variables that could be immutable"""
results = {}
unchanged_state_variables = UnchangedStateVariables(self.compilation_unit)
unchanged_state_variables.detect()
for variable in unchanged_state_variables.immutable_candidates:
results[variable.canonical_name] = self.generate_result(
[variable, " should be immutable \n"]
)
# Order by canonical name for deterministic results
return [results[k] for k in sorted(results)]
@staticmethod
def _format(compilation_unit: SlitherCompilationUnit, result: Dict) -> None:
custom_format(compilation_unit, result, "immutable")

@ -1,125 +0,0 @@
"""
Module detecting state variables that could be declared as constant
"""
from typing import Set, List, Dict
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.solidity_types.elementary_type import ElementaryType
from slither.core.solidity_types.user_defined_type import UserDefinedType
from slither.core.variables.variable import Variable
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils.output import Output
from slither.visitors.expression.export_values import ExportValues
from slither.core.declarations import Contract, Function
from slither.core.declarations.solidity_variables import SolidityFunction
from slither.core.variables.state_variable import StateVariable
from slither.formatters.variables.possible_const_state_variables import custom_format
def _is_valid_type(v: StateVariable) -> bool:
t = v.type
if isinstance(t, ElementaryType):
return True
if isinstance(t, UserDefinedType) and isinstance(t.type, Contract):
return True
return False
def _valid_candidate(v: StateVariable) -> bool:
return _is_valid_type(v) and not (v.is_constant or v.is_immutable)
def _is_constant_var(v: Variable) -> bool:
if isinstance(v, StateVariable):
return v.is_constant
return False
class ConstCandidateStateVars(AbstractDetector):
"""
State variables that could be declared as constant detector.
Not all types for constants are implemented in Solidity as of 0.4.25.
The only supported types are value types and strings (ElementaryType).
Reference: https://solidity.readthedocs.io/en/latest/contracts.html#constant-state-variables
"""
ARGUMENT = "constable-states"
HELP = "State variables that could be declared constant"
IMPACT = DetectorClassification.OPTIMIZATION
CONFIDENCE = DetectorClassification.HIGH
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-constant"
WIKI_TITLE = "State variables that could be declared constant"
WIKI_DESCRIPTION = "Constant state variables should be declared constant to save gas."
WIKI_RECOMMENDATION = "Add the `constant` attributes to state variables that never change."
# https://solidity.readthedocs.io/en/v0.5.2/contracts.html#constant-state-variables
valid_solidity_function = [
SolidityFunction("keccak256()"),
SolidityFunction("keccak256(bytes)"),
SolidityFunction("sha256()"),
SolidityFunction("sha256(bytes)"),
SolidityFunction("ripemd160()"),
SolidityFunction("ripemd160(bytes)"),
SolidityFunction("ecrecover(bytes32,uint8,bytes32,bytes32)"),
SolidityFunction("addmod(uint256,uint256,uint256)"),
SolidityFunction("mulmod(uint256,uint256,uint256)"),
]
def _constant_initial_expression(self, v: Variable) -> bool:
if not v.expression:
return True
export = ExportValues(v.expression)
values = export.result()
if not values:
return True
if all((val in self.valid_solidity_function or _is_constant_var(val) for val in values)):
return True
return False
def _detect(self) -> List[Output]:
"""Detect state variables that could be const"""
results = []
all_variables_l = [c.state_variables for c in self.compilation_unit.contracts]
all_variables: Set[StateVariable] = {
item for sublist in all_variables_l for item in sublist
}
all_non_constant_elementary_variables = {v for v in all_variables if _valid_candidate(v)}
all_functions_nested = [c.all_functions_called for c in self.compilation_unit.contracts]
all_functions = list(
{
item1
for sublist in all_functions_nested
for item1 in sublist
if isinstance(item1, Function)
}
)
all_variables_written = [
f.state_variables_written for f in all_functions if not f.is_constructor_variables
]
all_variables_written = {item for sublist in all_variables_written for item in sublist}
constable_variables: List[Variable] = [
v
for v in all_non_constant_elementary_variables
if (v not in all_variables_written) and self._constant_initial_expression(v)
]
# Order for deterministic results
constable_variables = sorted(constable_variables, key=lambda x: x.canonical_name)
# Create a result for each finding
for v in constable_variables:
info = [v, " should be constant\n"]
json = self.generate_result(info)
results.append(json)
return results
@staticmethod
def _format(compilation_unit: SlitherCompilationUnit, result: Dict) -> None:
custom_format(compilation_unit, result)

@ -0,0 +1,125 @@
"""
Module detecting state variables that could be declared as constant
"""
from typing import Set, List
from packaging import version
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.solidity_types.elementary_type import ElementaryType
from slither.core.solidity_types.user_defined_type import UserDefinedType
from slither.core.variables.variable import Variable
from slither.visitors.expression.export_values import ExportValues
from slither.core.declarations import Contract, Function
from slither.core.declarations.solidity_variables import SolidityFunction
from slither.core.variables.state_variable import StateVariable
from slither.core.expressions import CallExpression, NewContract
def _is_valid_type(v: StateVariable) -> bool:
t = v.type
if isinstance(t, ElementaryType):
return True
if isinstance(t, UserDefinedType) and isinstance(t.type, Contract):
return True
return False
def _valid_candidate(v: StateVariable) -> bool:
return _is_valid_type(v) and not (v.is_constant or v.is_immutable)
def _is_constant_var(v: Variable) -> bool:
if isinstance(v, StateVariable):
return v.is_constant
return False
# https://solidity.readthedocs.io/en/v0.5.2/contracts.html#constant-state-variables
valid_solidity_function = [
SolidityFunction("keccak256()"),
SolidityFunction("keccak256(bytes)"),
SolidityFunction("sha256()"),
SolidityFunction("sha256(bytes)"),
SolidityFunction("ripemd160()"),
SolidityFunction("ripemd160(bytes)"),
SolidityFunction("ecrecover(bytes32,uint8,bytes32,bytes32)"),
SolidityFunction("addmod(uint256,uint256,uint256)"),
SolidityFunction("mulmod(uint256,uint256,uint256)"),
]
def _constant_initial_expression(v: Variable) -> bool:
if not v.expression:
return True
# B b = new B(); b cannot be constant, so filter out and recommend it be immutable
if isinstance(v.expression, CallExpression) and isinstance(v.expression.called, NewContract):
return False
export = ExportValues(v.expression)
values = export.result()
if not values:
return True
return all((val in valid_solidity_function or _is_constant_var(val) for val in values))
class UnchangedStateVariables:
"""
Find state variables that could be declared as constant or immutable (not written after deployment).
"""
def __init__(self, compilation_unit: SlitherCompilationUnit):
self.compilation_unit = compilation_unit
self._constant_candidates: List[StateVariable] = []
self._immutable_candidates: List[StateVariable] = []
@property
def immutable_candidates(self) -> List[StateVariable]:
"""Return the immutable candidates"""
return self._immutable_candidates
@property
def constant_candidates(self) -> List[StateVariable]:
"""Return the constant candidates"""
return self._constant_candidates
def detect(self):
"""Detect state variables that could be constant or immutable"""
for c in self.compilation_unit.contracts_derived:
variables = []
functions = []
variables.append(c.state_variables)
functions.append(c.all_functions_called)
valid_candidates: Set[StateVariable] = {
item for sublist in variables for item in sublist if _valid_candidate(item)
}
all_functions: List[Function] = list(
{item1 for sublist in functions for item1 in sublist if isinstance(item1, Function)}
)
variables_written = []
constructor_variables_written = []
variables_initialized = []
for f in all_functions:
if f.is_constructor_variables:
variables_initialized.extend(f.state_variables_written)
elif f.is_constructor:
constructor_variables_written.extend(f.state_variables_written)
else:
variables_written.extend(f.state_variables_written)
for v in valid_candidates:
if v not in variables_written:
if _constant_initial_expression(v) and v not in constructor_variables_written:
self.constant_candidates.append(v)
elif (
v in constructor_variables_written or v in variables_initialized
) and version.parse(self.compilation_unit.solc_version) >= version.parse(
"0.6.5"
):
self.immutable_candidates.append(v)

@ -5,7 +5,7 @@ from slither.formatters.exceptions import FormatError, FormatImpossible
from slither.formatters.utils.patches import create_patch
def custom_format(compilation_unit: SlitherCompilationUnit, result):
def custom_format(compilation_unit: SlitherCompilationUnit, result, attribute: str) -> None:
elements = result["elements"]
for element in elements:
@ -15,14 +15,14 @@ def custom_format(compilation_unit: SlitherCompilationUnit, result):
contract = scope.get_contract_from_name(contract_name)
var = contract.get_state_variable_from_name(element["name"])
if not var.expression:
raise FormatImpossible(f"{var.name} is uninitialized and cannot become constant.")
raise FormatImpossible(f"{var.name} is uninitialized and cannot become {attribute}.")
_patch(
compilation_unit,
result,
element["source_mapping"]["filename_absolute"],
element["name"],
"constant " + element["name"],
f"{attribute} " + element["name"],
element["source_mapping"]["start"],
element["source_mapping"]["start"] + element["source_mapping"]["length"],
)

@ -30,6 +30,7 @@ from slither.slithir.operations import (
)
from slither.slithir.operations.binary import Binary
from slither.slithir.variables import Constant
from slither.visitors.expression.constants_folding import ConstantFolding
def _get_name(f: Union[Function, Variable]) -> str:
@ -175,6 +176,11 @@ def _extract_constants_from_irs( # pylint: disable=too-many-branches,too-many-n
all_cst_used_in_binary[str(ir.type)].append(
ConstantValue(str(r.value), str(r.type))
)
if isinstance(ir.variable_left, Constant) and isinstance(ir.variable_right, Constant):
if ir.lvalue:
type_ = ir.lvalue.type
cst = ConstantFolding(ir.expression, type_).result()
all_cst_used.append(ConstantValue(str(cst.value), str(type_)))
if isinstance(ir, TypeConversion):
if isinstance(ir.variable, Constant):
all_cst_used.append(ConstantValue(str(ir.variable.value), str(ir.type)))
@ -323,27 +329,32 @@ def _call_a_parameter(slither: SlitherCore) -> Dict[str, List[Dict]]:
ret: Dict[str, List[Dict]] = defaultdict(list)
for contract in slither.contracts: # pylint: disable=too-many-nested-blocks
for function in contract.functions_entry_points:
for ir in function.all_slithir_operations():
if isinstance(ir, HighLevelCall):
for idx, parameter in enumerate(function.parameters):
if is_dependent(ir.destination, parameter, function):
ret[contract.name].append(
{
"function": _get_name(function),
"parameter_idx": idx,
"signature": _get_name(ir.function),
}
)
if isinstance(ir, LowLevelCall):
for idx, parameter in enumerate(function.parameters):
if is_dependent(ir.destination, parameter, function):
ret[contract.name].append(
{
"function": _get_name(function),
"parameter_idx": idx,
"signature": None,
}
)
try:
for ir in function.all_slithir_operations():
if isinstance(ir, HighLevelCall):
for idx, parameter in enumerate(function.parameters):
if is_dependent(ir.destination, parameter, function):
ret[contract.name].append(
{
"function": _get_name(function),
"parameter_idx": idx,
"signature": _get_name(ir.function),
}
)
if isinstance(ir, LowLevelCall):
for idx, parameter in enumerate(function.parameters):
if is_dependent(ir.destination, parameter, function):
ret[contract.name].append(
{
"function": _get_name(function),
"parameter_idx": idx,
"signature": None,
}
)
except Exception as e:
if slither.no_fail:
continue
raise e
return ret

@ -91,6 +91,8 @@ class Slither(SlitherCore): # pylint: disable=too-many-instance-attributes
self.codex_max_tokens = kwargs.get("codex_max_tokens", 300)
self.codex_log = kwargs.get("codex_log", False)
self.no_fail = kwargs.get("no_fail", False)
self._parsers: List[SlitherCompilationUnitSolc] = []
try:
if isinstance(target, CryticCompile):
@ -128,41 +130,27 @@ class Slither(SlitherCore): # pylint: disable=too-many-instance-attributes
triage_mode = kwargs.get("triage_mode", False)
self._triage_mode = triage_mode
self._init_parsing_and_analyses(kwargs.get("skip_analyze", False))
def _init_parsing_and_analyses(self, skip_analyze: bool) -> None:
for parser in self._parsers:
parser.parse_contracts()
try:
parser.parse_contracts()
except Exception as e:
if self.no_fail:
continue
raise e
# skip_analyze is only used for testing
if not kwargs.get("skip_analyze", False):
if not skip_analyze:
for parser in self._parsers:
parser.analyze_contracts()
# def _init_from_raw_json(self, filename):
# if not os.path.isfile(filename):
# raise SlitherError(
# "{} does not exist (are you in the correct directory?)".format(filename)
# )
# assert filename.endswith("json")
# with open(filename, encoding="utf8") as astFile:
# stdout = astFile.read()
# if not stdout:
# to_log = f"Empty AST file: {filename}"
# raise SlitherError(to_log)
# contracts_json = stdout.split("\n=")
#
# self._parser = SlitherCompilationUnitSolc(filename, self)
#
# for c in contracts_json:
# self._parser.parse_top_level_from_json(c)
# def _init_from_list(self, contract):
# self._parser = SlitherCompilationUnitSolc("", self)
# for c in contract:
# if "absolutePath" in c:
# path = c["absolutePath"]
# else:
# path = c["attributes"]["absolutePath"]
# self._parser.parse_top_level_from_loaded_json(c, path)
try:
parser.analyze_contracts()
except Exception as e:
if self.no_fail:
continue
raise e
@property
def detectors(self):

@ -379,7 +379,7 @@ def integrate_value_gas(result):
###################################################################################
def propagate_type_and_convert_call(result, node):
def propagate_type_and_convert_call(result: List[Operation], node: "Node") -> List[Operation]:
"""
Propagate the types variables and convert tmp call to real call operation
"""
@ -664,7 +664,24 @@ def propagate_types(ir, node: "Node"): # pylint: disable=too-many-locals
if ir.variable_right == "selector" and isinstance(ir.variable_left, (CustomError)):
assignment = Assignment(
ir.lvalue,
Constant(str(get_function_id(ir.variable_left.solidity_signature))),
Constant(
str(get_function_id(ir.variable_left.solidity_signature)),
ElementaryType("bytes4"),
),
ElementaryType("bytes4"),
)
assignment.set_expression(ir.expression)
assignment.set_node(ir.node)
assignment.lvalue.set_type(ElementaryType("bytes4"))
return assignment
if isinstance(ir.variable_right, (CustomError)):
assignment = Assignment(
ir.lvalue,
Constant(
str(get_function_id(ir.variable_left.solidity_signature)),
ElementaryType("bytes4"),
),
ElementaryType("bytes4"),
)
assignment.set_expression(ir.expression)
@ -736,7 +753,7 @@ def propagate_types(ir, node: "Node"): # pylint: disable=too-many-locals
if f:
ir.lvalue.set_type(f)
else:
# Allow propgation for variable access through contract's nale
# Allow propgation for variable access through contract's name
# like Base_contract.my_variable
v = next(
(
@ -1819,7 +1836,7 @@ def _find_source_mapping_references(irs: List[Operation]):
###################################################################################
def apply_ir_heuristics(irs, node):
def apply_ir_heuristics(irs: List[Operation], node: "Node"):
"""
Apply a set of heuristic to improve slithIR
"""

@ -91,6 +91,9 @@ class FunctionSolc(CallerContextExpression):
Union[LocalVariableSolc, LocalVariableInitFromTupleSolc]
] = []
if "documentation" in function_data:
function.has_documentation = True
@property
def underlying_function(self) -> Function:
return self._function
@ -340,7 +343,6 @@ class FunctionSolc(CallerContextExpression):
node,
[self._function.name, f"asm_{len(self._node_to_yulobject)}"],
scope,
parent_func=self._function,
)
self._node_to_yulobject[node] = yul_object
return yul_object

@ -48,16 +48,22 @@ def _handle_import_aliases(
"""
for symbol_alias in symbol_aliases:
if (
"foreign" in symbol_alias
and "name" in symbol_alias["foreign"]
and "local" in symbol_alias
):
original_name = symbol_alias["foreign"]["name"]
local_name = symbol_alias["local"]
import_directive.renaming[local_name] = original_name
# Assuming that two imports cannot collide in renaming
scope.renaming[local_name] = original_name
if "foreign" in symbol_alias and "local" in symbol_alias:
if isinstance(symbol_alias["foreign"], dict) and "name" in symbol_alias["foreign"]:
original_name = symbol_alias["foreign"]["name"]
local_name = symbol_alias["local"]
import_directive.renaming[local_name] = original_name
# Assuming that two imports cannot collide in renaming
scope.renaming[local_name] = original_name
# This path should only be hit for the malformed AST of solc 0.5.12 where
# the foreign identifier cannot be found but is required to resolve the alias.
# see https://github.com/crytic/slither/issues/1319
elif symbol_alias["local"]:
raise SlitherException(
"Cannot resolve local alias for import directive due to malformed AST. Please upgrade to solc 0.6.0 or higher."
)
class SlitherCompilationUnitSolc(CallerContextExpression):
@ -513,7 +519,7 @@ Please rename it, this name is reserved for Slither's internals"""
self._analyze_third_part(contracts_to_be_analyzed, libraries)
[c.set_is_analyzed(False) for c in self._underlying_contract_to_parser.values()]
self._analyze_using_for(contracts_to_be_analyzed)
self._analyze_using_for(contracts_to_be_analyzed, libraries)
self._parsed = True
@ -625,9 +631,14 @@ Please rename it, this name is reserved for Slither's internals"""
else:
contracts_to_be_analyzed += [contract]
def _analyze_using_for(self, contracts_to_be_analyzed: List[ContractSolc]):
def _analyze_using_for(
self, contracts_to_be_analyzed: List[ContractSolc], libraries: List[ContractSolc]
):
self._analyze_top_level_using_for()
for lib in libraries:
lib.analyze_using_for()
while contracts_to_be_analyzed:
contract = contracts_to_be_analyzed[0]

@ -264,9 +264,9 @@ binary_ops = {
class YulBuiltin: # pylint: disable=too-few-public-methods
def __init__(self, name):
def __init__(self, name: str) -> None:
self._name = name
@property
def name(self):
def name(self) -> str:
return self._name

@ -24,6 +24,7 @@ from slither.core.expressions import (
UnaryOperation,
)
from slither.core.expressions.expression import Expression
from slither.core.scope.scope import FileScope
from slither.core.solidity_types import ElementaryType
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.variables.local_variable import LocalVariable
@ -51,30 +52,35 @@ class YulNode:
def underlying_node(self) -> Node:
return self._node
def add_unparsed_expression(self, expression: Dict):
def add_unparsed_expression(self, expression: Dict) -> None:
assert self._unparsed_expression is None
self._unparsed_expression = expression
def analyze_expressions(self):
def analyze_expressions(self) -> None:
if self._node.type == NodeType.VARIABLE and not self._node.expression:
self._node.add_expression(self._node.variable_declaration.expression)
expression = self._node.variable_declaration.expression
if expression:
self._node.add_expression(expression)
if self._unparsed_expression:
expression = parse_yul(self._scope, self, self._unparsed_expression)
self._node.add_expression(expression)
if expression:
self._node.add_expression(expression)
if self._node.expression:
if self._node.type == NodeType.VARIABLE:
# Update the expression to be an assignement to the variable
_expression = AssignmentOperation(
Identifier(self._node.variable_declaration),
self._node.expression,
AssignmentOperationType.ASSIGN,
self._node.variable_declaration.type,
)
_expression.set_offset(
self._node.expression.source_mapping, self._node.compilation_unit
)
self._node.add_expression(_expression, bypass_verif_empty=True)
variable_declaration = self._node.variable_declaration
if variable_declaration:
_expression = AssignmentOperation(
Identifier(self._node.variable_declaration),
self._node.expression,
AssignmentOperationType.ASSIGN,
variable_declaration.type,
)
_expression.set_offset(
self._node.expression.source_mapping, self._node.compilation_unit
)
self._node.add_expression(_expression, bypass_verif_empty=True)
expression = self._node.expression
read_var = ReadVar(expression)
@ -122,13 +128,13 @@ class YulScope(metaclass=abc.ABCMeta):
]
def __init__(
self, contract: Optional[Contract], yul_id: List[str], parent_func: Function = None
):
self, contract: Optional[Contract], yul_id: List[str], parent_func: Function
) -> None:
self._contract = contract
self._id: List[str] = yul_id
self._yul_local_variables: List[YulLocalVariable] = []
self._yul_local_functions: List[YulFunction] = []
self._parent_func = parent_func
self._parent_func: Function = parent_func
@property
def id(self) -> List[str]:
@ -155,10 +161,14 @@ class YulScope(metaclass=abc.ABCMeta):
def new_node(self, node_type: NodeType, src: Union[str, Dict]) -> YulNode:
pass
def add_yul_local_variable(self, var):
@property
def file_scope(self) -> FileScope:
return self._parent_func.file_scope
def add_yul_local_variable(self, var: "YulLocalVariable") -> None:
self._yul_local_variables.append(var)
def get_yul_local_variable_from_name(self, variable_name):
def get_yul_local_variable_from_name(self, variable_name: str) -> Optional["YulLocalVariable"]:
return next(
(
v
@ -168,10 +178,10 @@ class YulScope(metaclass=abc.ABCMeta):
None,
)
def add_yul_local_function(self, func):
def add_yul_local_function(self, func: "YulFunction") -> None:
self._yul_local_functions.append(func)
def get_yul_local_function_from_name(self, func_name):
def get_yul_local_function_from_name(self, func_name: str) -> Optional["YulLocalVariable"]:
return next(
(v for v in self._yul_local_functions if v.underlying.name == func_name),
None,
@ -242,7 +252,7 @@ class YulFunction(YulScope):
def function(self) -> Function:
return self._function
def convert_body(self):
def convert_body(self) -> None:
node = self.new_node(NodeType.ENTRYPOINT, self._ast["src"])
link_underlying_nodes(self._entrypoint, node)
@ -258,7 +268,7 @@ class YulFunction(YulScope):
convert_yul(self, node, self._ast["body"], self.node_scope)
def parse_body(self):
def parse_body(self) -> None:
for node in self._nodes:
node.analyze_expressions()
@ -289,9 +299,8 @@ class YulBlock(YulScope):
entrypoint: Node,
yul_id: List[str],
node_scope: Union[Scope, Function],
**kwargs,
):
super().__init__(contract, yul_id, **kwargs)
super().__init__(contract, yul_id, entrypoint.function)
self._entrypoint: YulNode = YulNode(entrypoint, self)
self._nodes: List[YulNode] = []
@ -318,7 +327,7 @@ class YulBlock(YulScope):
def convert(self, ast: Dict) -> YulNode:
return convert_yul(self, self._entrypoint, ast, self.node_scope)
def analyze_expressions(self):
def analyze_expressions(self) -> None:
for node in self._nodes:
node.analyze_expressions()
@ -361,18 +370,22 @@ def convert_yul_function_definition(
while not isinstance(top_node_scope, Function):
top_node_scope = top_node_scope.father
func: Union[FunctionTopLevel, FunctionContract]
if isinstance(top_node_scope, FunctionTopLevel):
scope = root.contract.file_scope
scope = root.file_scope
func = FunctionTopLevel(root.compilation_unit, scope)
# Note: we do not add the function in the scope
# While its a top level function, it is not accessible outside of the function definition
# In practice we should probably have a specific function type for function defined within a function
else:
func = FunctionContract(root.compilation_unit)
func.function_language = FunctionLanguage.Yul
yul_function = YulFunction(func, root, ast, node_scope)
root.contract.add_function(func)
if root.contract:
root.contract.add_function(func)
root.compilation_unit.add_function(func)
root.add_yul_local_function(yul_function)
@ -774,14 +787,15 @@ def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[
# check function-scoped variables
parent_func = root.parent_func
if parent_func:
variable = parent_func.get_local_variable_from_name(name)
if variable:
return Identifier(variable)
local_variable = parent_func.get_local_variable_from_name(name)
if local_variable:
return Identifier(local_variable)
if isinstance(parent_func, FunctionContract):
variable = parent_func.contract.get_state_variable_from_name(name)
if variable:
return Identifier(variable)
assert parent_func.contract
state_variable = parent_func.contract.get_state_variable_from_name(name)
if state_variable:
return Identifier(state_variable)
# check yul-scoped variable
variable = root.get_yul_local_variable_from_name(name)
@ -798,7 +812,7 @@ def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[
if magic_suffix:
return magic_suffix
ret, _ = find_top_level(name, root.contract.file_scope)
ret, _ = find_top_level(name, root.file_scope)
if ret:
return Identifier(ret)
@ -840,7 +854,7 @@ def parse_yul_unsupported(_root: YulScope, _node: YulNode, ast: Dict) -> Optiona
def parse_yul(root: YulScope, node: YulNode, ast: Dict) -> Optional[Expression]:
op = parsers.get(ast["nodeType"], parse_yul_unsupported)(root, node, ast)
op: Expression = parsers.get(ast["nodeType"], parse_yul_unsupported)(root, node, ast)
if op:
op.set_offset(ast["src"], root.compilation_unit)
return op

@ -1,4 +1,6 @@
import argparse
import logging
import sys
from crytic_compile import cryticparser
@ -25,6 +27,9 @@ def parse_args() -> argparse.Namespace:
def main():
# log on stdout to keep output in order
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, force=True)
args = parse_args()
kwargs = vars(args)

@ -1,6 +1,7 @@
from typing import Callable, List
from dataclasses import dataclass
from slither.tools.doctor.checks.paths import check_slither_path
from slither.tools.doctor.checks.platform import compile_project, detect_platform
from slither.tools.doctor.checks.versions import show_versions
@ -12,6 +13,7 @@ class Check:
ALL_CHECKS: List[Check] = [
Check("PATH configuration", check_slither_path),
Check("Software versions", show_versions),
Check("Project platform", detect_platform),
Check("Project compilation", compile_project),

@ -0,0 +1,85 @@
from pathlib import Path
from typing import List, Optional, Tuple
import shutil
import sys
import sysconfig
from slither.utils.colors import yellow, green, red
def path_is_relative_to(path: Path, relative_to: Path) -> bool:
"""
Check if a path is relative to another one.
Compatibility wrapper for Path.is_relative_to
"""
if sys.version_info >= (3, 9, 0):
return path.is_relative_to(relative_to)
path_parts = path.resolve().parts
relative_to_parts = relative_to.resolve().parts
if len(path_parts) < len(relative_to_parts):
return False
for (a, b) in zip(path_parts, relative_to_parts):
if a != b:
return False
return True
def check_path_config(name: str) -> Tuple[bool, Optional[Path], List[Path]]:
"""
Check if a given Python binary/script is in PATH.
:return: Returns if the binary on PATH corresponds to this installation,
its path (if present), and a list of possible paths where this
binary might be found.
"""
binary_path = shutil.which(name)
possible_paths = []
for scheme in sysconfig.get_scheme_names():
script_path = Path(sysconfig.get_path("scripts", scheme))
purelib_path = Path(sysconfig.get_path("purelib", scheme))
script_binary_path = shutil.which(name, path=script_path)
if script_binary_path is not None:
possible_paths.append((script_path, purelib_path))
binary_here = False
if binary_path is not None:
binary_path = Path(binary_path)
this_code = Path(__file__)
this_binary = list(filter(lambda x: path_is_relative_to(this_code, x[1]), possible_paths))
binary_here = len(this_binary) > 0 and all(
path_is_relative_to(binary_path, script) for script, _ in this_binary
)
return binary_here, binary_path, list(set(script for script, _ in possible_paths))
def check_slither_path(**_kwargs) -> None:
binary_here, binary_path, possible_paths = check_path_config("slither")
show_paths = False
if binary_path:
print(green(f"`slither` found in PATH at `{binary_path}`."))
if binary_here:
print(green("Its location matches this slither-doctor installation."))
else:
print(
yellow(
"This path does not correspond to this slither-doctor installation.\n"
+ "Double-check the order of directories in PATH if you have several Slither installations."
)
)
show_paths = True
else:
print(red("`slither` was not found in PATH."))
show_paths = True
if show_paths:
print()
print("Consider adding one of the following directories to PATH:")
for path in possible_paths:
print(f" * {path}")

@ -3,19 +3,19 @@ import json
from typing import Optional
import urllib
from packaging.version import parse, LegacyVersion, Version
from packaging.version import parse, Version
from slither.utils.colors import yellow, green
def get_installed_version(name: str) -> Optional[LegacyVersion | Version]:
def get_installed_version(name: str) -> Optional[Version]:
try:
return parse(metadata.version(name))
except metadata.PackageNotFoundError:
return None
def get_github_version(name: str) -> Optional[LegacyVersion | Version]:
def get_github_version(name: str) -> Optional[Version]:
try:
with urllib.request.urlopen(
f"https://api.github.com/repos/crytic/{name}/releases/latest"
@ -45,7 +45,9 @@ def show_versions(**_kwargs) -> None:
for name, (installed, latest) in versions.items():
color = yellow if name in outdated else green
print(f"{name + ':':<16}{color(installed or 'N/A'):<16} (latest is {latest or 'Unknown'})")
print(
f"{name + ':':<16}{color(str(installed) or 'N/A'):<16} (latest is {str(latest) or 'Unknown'})"
)
if len(outdated) > 0:
print()

@ -0,0 +1,5 @@
# slither-documentation
`slither-documentation` uses [codex](https://beta.openai.com) to generate natspec documenation.
This tool is experimental. See [solmate documentation](https://github.com/montyly/solmate/pull/1) for an example of usage.

@ -0,0 +1,280 @@
import argparse
import logging
import uuid
from typing import Optional, Dict, List
from crytic_compile import cryticparser
from slither import Slither
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.declarations import Function
from slither.formatters.utils.patches import create_patch, apply_patch, create_diff
from slither.utils import codex
logging.basicConfig()
logging.getLogger("Slither").setLevel(logging.INFO)
logger = logging.getLogger("Slither")
def parse_args() -> argparse.Namespace:
"""
Parse the underlying arguments for the program.
:return: Returns the arguments for the program.
"""
parser = argparse.ArgumentParser(description="Demo", usage="slither-documentation filename")
parser.add_argument("project", help="The target directory/Solidity file.")
parser.add_argument(
"--overwrite", help="Overwrite the files (be careful).", action="store_true", default=False
)
parser.add_argument(
"--force-answer-parsing",
help="Apply heuristics to better parse codex output (might lead to incorrect results)",
action="store_true",
default=False,
)
parser.add_argument(
"--retry",
help="Retry failed query (default 1). Each retry increases the temperature by 0.1",
action="store",
default=1,
)
# Add default arguments from crytic-compile
cryticparser.init(parser)
codex.init_parser(parser, always_enable_codex=True)
return parser.parse_args()
def _use_tab(char: str) -> Optional[bool]:
"""
Check if the char is a tab
Args:
char:
Returns:
"""
if char == " ":
return False
if char == "\t":
return True
return None
def _post_processesing(
answer: str, starting_column: int, use_tab: Optional[bool], force_and_stopped: bool
) -> Optional[str]:
"""
Clean answers from codex
Args:
answer:
starting_column:
Returns:
"""
if answer.count("/**") != 1:
return None
# Sometimes codex will miss the */, even if it finished properly the request
# In this case, we allow slither-documentation to force the */
if answer.count("*/") != 1:
if force_and_stopped:
answer += "*/"
else:
return None
if answer.find("/**") > answer.find("*/"):
return None
answer = answer[answer.find("/**") : answer.find("*/") + 2]
answer_lines = answer.splitlines()
# Add indentation to all the lines, aside the first one
space_char = "\t" if use_tab else " "
if len(answer_lines) > 0:
answer = (
answer_lines[0]
+ "\n"
+ "\n".join(
[space_char * (starting_column - 1) + line for line in answer_lines[1:] if line]
)
)
answer += "\n" + space_char * (starting_column - 1)
return answer
return answer_lines[0]
def _handle_codex(
answer: Dict, starting_column: int, use_tab: Optional[bool], force: bool
) -> Optional[str]:
if "choices" in answer:
if answer["choices"]:
if "text" in answer["choices"][0]:
has_stopped = answer["choices"][0].get("finish_reason", "") == "stop"
answer_processed = _post_processesing(
answer["choices"][0]["text"], starting_column, use_tab, force and has_stopped
)
if answer_processed is None:
return None
return answer_processed
return None
# pylint: disable=too-many-locals,too-many-arguments
def _handle_function(
function: Function,
overwrite: bool,
all_patches: Dict,
logging_file: Optional[str],
slither: Slither,
retry: int,
force: bool,
) -> bool:
if (
function.source_mapping.is_dependency
or function.has_documentation
or function.is_constructor_variables
):
return overwrite
prompt = "Create a natpsec documentation for this solidity code with only notice and dev.\n"
src_mapping = function.source_mapping
content = function.compilation_unit.core.source_code[src_mapping.filename.absolute]
start = src_mapping.start
end = src_mapping.start + src_mapping.length
prompt += content[start:end]
use_tab = _use_tab(content[start - 1])
if use_tab is None and src_mapping.starting_column > 1:
logger.info(f"Non standard space indentation found {content[start - 1:end]}")
if overwrite:
logger.info("Disable overwrite to avoid mistakes")
overwrite = False
openai = codex.openai_module() # type: ignore
if openai is None:
raise ImportError
if logging_file:
codex.log_codex(logging_file, "Q: " + prompt)
tentative = 0
answer_processed: Optional[str] = None
while tentative < retry:
tentative += 1
answer = openai.Completion.create( # type: ignore
prompt=prompt,
model=slither.codex_model,
temperature=min(slither.codex_temperature + tentative * 0.1, 1),
max_tokens=slither.codex_max_tokens,
)
if logging_file:
codex.log_codex(logging_file, "A: " + str(answer))
answer_processed = _handle_codex(answer, src_mapping.starting_column, use_tab, force)
if answer_processed:
break
logger.info(
f"Codex could not generate a well formatted answer for {function.canonical_name}"
)
logger.info(answer)
if not answer_processed:
return overwrite
create_patch(all_patches, src_mapping.filename.absolute, start, start, "", answer_processed)
return overwrite
def _handle_compilation_unit(
slither: Slither,
compilation_unit: SlitherCompilationUnit,
overwrite: bool,
force: bool,
retry: int,
) -> None:
logging_file: Optional[str]
if slither.codex_log:
logging_file = str(uuid.uuid4())
else:
logging_file = None
for scope in compilation_unit.scopes.values():
# Dont send tests file
if (
".t.sol" in scope.filename.absolute
or "mock" in scope.filename.absolute.lower()
or "test" in scope.filename.absolute.lower()
):
continue
functions_target: List[Function] = []
for contract in scope.contracts.values():
functions_target += contract.functions_declared
functions_target += list(scope.functions)
all_patches: Dict = {}
for function in functions_target:
overwrite = _handle_function(
function, overwrite, all_patches, logging_file, slither, retry, force
)
# all_patches["patches"] should have only 1 file
if "patches" not in all_patches:
continue
for file in all_patches["patches"]:
original_txt = compilation_unit.core.source_code[file].encode("utf8")
patched_txt = original_txt
patches = all_patches["patches"][file]
offset = 0
patches.sort(key=lambda x: x["start"])
for patch in patches:
patched_txt, offset = apply_patch(patched_txt, patch, offset)
if overwrite:
with open(file, "w", encoding="utf8") as f:
f.write(patched_txt.decode("utf8"))
else:
diff = create_diff(compilation_unit, original_txt, patched_txt, file)
with open(f"{file}.patch", "w", encoding="utf8") as f:
f.write(diff)
def main() -> None:
args = parse_args()
logger.info("This tool is a WIP, use it with cautious")
logger.info("Be aware of OpenAI ToS: https://openai.com/api/policies/terms/")
slither = Slither(args.project, **vars(args))
try:
for compilation_unit in slither.compilation_units:
_handle_compilation_unit(
slither,
compilation_unit,
args.overwrite,
args.force_answer_parsing,
int(args.retry),
)
except ImportError:
pass
if __name__ == "__main__":
main()

@ -9,7 +9,8 @@ from slither.detectors.attributes.incorrect_solc import IncorrectSolc
from slither.detectors.attributes.constant_pragma import ConstantPragma
from slither.detectors.naming_convention.naming_convention import NamingConvention
from slither.detectors.functions.external_function import ExternalFunction
from slither.detectors.variables.possible_const_state_variables import ConstCandidateStateVars
from slither.detectors.variables.could_be_constant import CouldBeConstant
from slither.detectors.variables.could_be_immutable import CouldBeImmutable
from slither.detectors.attributes.const_functions_asm import ConstantFunctionsAsm
from slither.detectors.attributes.const_functions_state import ConstantFunctionsState
from slither.utils.colors import yellow
@ -23,7 +24,8 @@ all_detectors: Dict[str, Type[AbstractDetector]] = {
"pragma": ConstantPragma,
"naming-convention": NamingConvention,
"external-function": ExternalFunction,
"constable-states": ConstCandidateStateVars,
"constable-states": CouldBeConstant,
"immutable-states": CouldBeImmutable,
"constant-function-asm": ConstantFunctionsAsm,
"constant-functions-state": ConstantFunctionsState,
}

@ -1,10 +1,70 @@
import logging
import os
from argparse import ArgumentParser
from pathlib import Path
from slither.utils.command_line import defaults_flag_in_config
logger = logging.getLogger("Slither")
def init_parser(parser: ArgumentParser, always_enable_codex: bool = False) -> None:
"""
Init the cli arg with codex features
Args:
parser:
always_enable_codex (Optional(bool)): if true, --codex is not enabled
Returns:
"""
group_codex = parser.add_argument_group("Codex (https://beta.openai.com/docs/guides/code)")
if not always_enable_codex:
group_codex.add_argument(
"--codex",
help="Enable codex (require an OpenAI API Key)",
action="store_true",
default=defaults_flag_in_config["codex"],
)
group_codex.add_argument(
"--codex-log",
help="Log codex queries (in crytic_export/codex/)",
action="store_true",
default=False,
)
group_codex.add_argument(
"--codex-contracts",
help="Comma separated list of contracts to submit to OpenAI Codex",
action="store",
default=defaults_flag_in_config["codex_contracts"],
)
group_codex.add_argument(
"--codex-model",
help="Name of the Codex model to use (affects pricing). Defaults to 'text-davinci-003'",
action="store",
default=defaults_flag_in_config["codex_model"],
)
group_codex.add_argument(
"--codex-temperature",
help="Temperature to use with Codex. Lower number indicates a more precise answer while higher numbers return more creative answers. Defaults to 0",
action="store",
default=defaults_flag_in_config["codex_temperature"],
)
group_codex.add_argument(
"--codex-max-tokens",
help="Maximum amount of tokens to use on the response. This number plus the size of the prompt can be no larger than the limit (4097 for text-davinci-003)",
action="store",
default=defaults_flag_in_config["codex_max_tokens"],
)
# TODO: investigate how to set the correct return type
# So that the other modules can work with openai
def openai_module(): # type: ignore

@ -60,6 +60,7 @@ defaults_flag_in_config = {
"zip": None,
"zip_type": "lzma",
"show_ignored_findings": False,
"no_fail": False,
**DEFAULTS_FLAG_IN_CONFIG_CRYTIC_COMPILE,
}

@ -1,4 +1,12 @@
from slither.core.expressions import BinaryOperationType, Literal, UnaryOperationType
from fractions import Fraction
from slither.core.expressions import (
BinaryOperationType,
Literal,
UnaryOperationType,
Identifier,
BinaryOperation,
UnaryOperation,
)
from slither.utils.integer_conversion import convert_string_to_fraction, convert_string_to_int
from slither.visitors.expression.expression import ExpressionVisitor
@ -27,9 +35,15 @@ class ConstantFolding(ExpressionVisitor):
super().__init__(expression)
def result(self):
return Literal(int(get_val(self._expression)), self._type)
def _post_identifier(self, expression):
value = get_val(self._expression)
if isinstance(value, Fraction):
value = int(value)
# emulate 256-bit wrapping
if str(self._type).startswith("uint"):
value = value & (2**256 - 1)
return Literal(value, self._type)
def _post_identifier(self, expression: Identifier):
if not expression.value.is_constant:
raise NotConstant
expr = expression.value.expression
@ -37,9 +51,10 @@ class ConstantFolding(ExpressionVisitor):
if not isinstance(expr, Literal):
cf = ConstantFolding(expr, self._type)
expr = cf.result()
set_val(expression, convert_string_to_int(expr.value))
set_val(expression, convert_string_to_int(expr.converted_value))
def _post_binary_operation(self, expression):
# pylint: disable=too-many-branches
def _post_binary_operation(self, expression: BinaryOperation):
left = get_val(expression.expression_left)
right = get_val(expression.expression_right)
if expression.type == BinaryOperationType.POWER:
@ -53,18 +68,39 @@ class ConstantFolding(ExpressionVisitor):
elif expression.type == BinaryOperationType.ADDITION:
set_val(expression, left + right)
elif expression.type == BinaryOperationType.SUBTRACTION:
if (left - right) < 0:
# Could trigger underflow
raise NotConstant
set_val(expression, left - right)
# Convert to int for operations not supported by Fraction
elif expression.type == BinaryOperationType.LEFT_SHIFT:
set_val(expression, left << right)
set_val(expression, int(left) << int(right))
elif expression.type == BinaryOperationType.RIGHT_SHIFT:
set_val(expression, left >> right)
set_val(expression, int(left) >> int(right))
elif expression.type == BinaryOperationType.AND:
set_val(expression, int(left) & int(right))
elif expression.type == BinaryOperationType.CARET:
set_val(expression, int(left) ^ int(right))
elif expression.type == BinaryOperationType.OR:
set_val(expression, int(left) | int(right))
elif expression.type == BinaryOperationType.LESS:
set_val(expression, int(left) < int(right))
elif expression.type == BinaryOperationType.LESS_EQUAL:
set_val(expression, int(left) <= int(right))
elif expression.type == BinaryOperationType.GREATER:
set_val(expression, int(left) > int(right))
elif expression.type == BinaryOperationType.GREATER_EQUAL:
set_val(expression, int(left) >= int(right))
elif expression.type == BinaryOperationType.EQUAL:
set_val(expression, int(left) == int(right))
elif expression.type == BinaryOperationType.NOT_EQUAL:
set_val(expression, int(left) != int(right))
# Convert boolean literals from string to bool
elif expression.type == BinaryOperationType.ANDAND:
set_val(expression, left == "true" and right == "true")
elif expression.type == BinaryOperationType.OROR:
set_val(expression, left == "true" or right == "true")
else:
raise NotConstant
def _post_unary_operation(self, expression):
def _post_unary_operation(self, expression: UnaryOperation):
# Case of uint a = -7; uint[-a] arr;
if expression.type == UnaryOperationType.MINUS_PRE:
expr = expression.expression
@ -72,15 +108,18 @@ class ConstantFolding(ExpressionVisitor):
cf = ConstantFolding(expr, self._type)
expr = cf.result()
assert isinstance(expr, Literal)
set_val(expression, -convert_string_to_fraction(expr.value))
set_val(expression, -convert_string_to_fraction(expr.converted_value))
else:
raise NotConstant
def _post_literal(self, expression):
try:
set_val(expression, convert_string_to_fraction(expression.value))
except ValueError as e:
raise NotConstant from e
def _post_literal(self, expression: Literal):
if expression.converted_value in ["true", "false"]:
set_val(expression, expression.converted_value)
else:
try:
set_val(expression, convert_string_to_fraction(expression.converted_value))
except ValueError as e:
raise NotConstant from e
def _post_assignement_operation(self, expression):
raise NotConstant
@ -115,9 +154,12 @@ class ConstantFolding(ExpressionVisitor):
cf = ConstantFolding(expression.expressions[0], self._type)
expr = cf.result()
assert isinstance(expr, Literal)
set_val(expression, convert_string_to_fraction(expr.value))
set_val(expression, convert_string_to_fraction(expr.converted_value))
return
raise NotConstant
def _post_type_conversion(self, expression):
raise NotConstant
cf = ConstantFolding(expression.expression, self._type)
expr = cf.result()
assert isinstance(expr, Literal)
set_val(expression, convert_string_to_fraction(expr.converted_value))

@ -282,10 +282,15 @@ class ExpressionToSlithIR(ExpressionVisitor):
and expression_called.member_name in ["wrap", "unwrap"]
and len(args) == 1
):
# wrap: underlying_type -> alias
# unwrap: alias -> underlying_type
dest_type = (
called if expression_called.member_name == "wrap" else called.underlying_type
)
val = TemporaryVariable(self._node)
var = TypeConversion(val, args[0], called)
var = TypeConversion(val, args[0], dest_type)
var.set_expression(expression)
val.set_type(called)
val.set_type(dest_type)
self._result.append(var)
set_val(expression, val)
@ -455,14 +460,18 @@ class ExpressionToSlithIR(ExpressionVisitor):
set_val(expression, expr)
return
# Early lookup to detect user defined types from other contracts definitions
# contract A { type MyInt is int}
# contract B { function f() public{ A.MyInt test = A.MyInt.wrap(1);}}
# The logic is handled by _post_call_expression
if isinstance(expr, Contract):
# Early lookup to detect user defined types from other contracts definitions
# contract A { type MyInt is int}
# contract B { function f() public{ A.MyInt test = A.MyInt.wrap(1);}}
# The logic is handled by _post_call_expression
if expression.member_name in expr.file_scope.user_defined_types:
set_val(expression, expr.file_scope.user_defined_types[expression.member_name])
return
# Lookup errors referred to as member of contract e.g. Test.myError.selector
if expression.member_name in expr.custom_errors_as_dict:
set_val(expression, expr.custom_errors_as_dict[expression.member_name])
return
val = ReferenceVariable(self._node)
member = Member(expr, Constant(expression.member_name), val)

@ -0,0 +1,9 @@
pragma solidity 0.5.12;
import {A} from "./import.sol";
contract Z is A {
function test() public pure returns (uint) {
return 1;
}
}

@ -0,0 +1,9 @@
pragma solidity 0.5.12;
import {A as X, A as Y} from "./import.sol";
contract Z is X {
function test() public pure returns (uint) {
return 1;
}
}

@ -0,0 +1,13 @@
contract Test {
error myError();
}
interface VM {
function expectRevert(bytes4) external;
function expectRevert(bytes calldata) external;
}
contract A {
function b(address c) public {
VM(c).expectRevert(Test.myError.selector);
}
}

@ -0,0 +1,6 @@
{
"A": {},
"Z": {
"test()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}

@ -0,0 +1,6 @@
{
"A": {},
"Z": {
"test()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}

@ -0,0 +1,10 @@
{
"Test": {},
"VM": {
"expectRevert(bytes4)": "digraph{\n}\n",
"expectRevert(bytes)": "digraph{\n}\n"
},
"A": {
"b(address)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
}
}

@ -0,0 +1,10 @@
{
"Test": {},
"VM": {
"expectRevert(bytes4)": "digraph{\n}\n",
"expectRevert(bytes)": "digraph{\n}\n"
},
"A": {
"b(address)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
}
}

@ -0,0 +1,10 @@
{
"Test": {},
"VM": {
"expectRevert(bytes4)": "digraph{\n}\n",
"expectRevert(bytes)": "digraph{\n}\n"
},
"A": {
"b(address)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
}
}

@ -0,0 +1,10 @@
{
"Test": {},
"VM": {
"expectRevert(bytes4)": "digraph{\n}\n",
"expectRevert(bytes)": "digraph{\n}\n"
},
"A": {
"b(address)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
}
}

@ -0,0 +1,10 @@
{
"Test": {},
"VM": {
"expectRevert(bytes4)": "digraph{\n}\n",
"expectRevert(bytes)": "digraph{\n}\n"
},
"A": {
"b(address)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
}
}

@ -0,0 +1,10 @@
{
"Test": {},
"VM": {
"expectRevert(bytes4)": "digraph{\n}\n",
"expectRevert(bytes)": "digraph{\n}\n"
},
"A": {
"b(address)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
}
}

@ -0,0 +1,10 @@
{
"Test": {},
"VM": {
"expectRevert(bytes4)": "digraph{\n}\n",
"expectRevert(bytes)": "digraph{\n}\n"
},
"A": {
"b(address)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
}
}

@ -0,0 +1,10 @@
{
"Test": {},
"VM": {
"expectRevert(bytes4)": "digraph{\n}\n",
"expectRevert(bytes)": "digraph{\n}\n"
},
"A": {
"b(address)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
}
}

@ -0,0 +1,10 @@
{
"Test": {},
"VM": {
"expectRevert(bytes4)": "digraph{\n}\n",
"expectRevert(bytes)": "digraph{\n}\n"
},
"A": {
"b(address)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
}
}

@ -0,0 +1,10 @@
{
"Test": {},
"VM": {
"expectRevert(bytes4)": "digraph{\n}\n",
"expectRevert(bytes)": "digraph{\n}\n"
},
"A": {
"b(address)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
}
}

@ -0,0 +1,10 @@
{
"Test": {},
"VM": {
"expectRevert(bytes4)": "digraph{\n}\n",
"expectRevert(bytes)": "digraph{\n}\n"
},
"A": {
"b(address)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
}
}

@ -0,0 +1,10 @@
{
"Test": {},
"VM": {
"expectRevert(bytes4)": "digraph{\n}\n",
"expectRevert(bytes)": "digraph{\n}\n"
},
"A": {
"b(address)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
}
}

@ -0,0 +1,11 @@
{
"MyLib": {
"a()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: RETURN 2\n\"];\n}\n"
},
"A": {
"b(MyType)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
},
"B": {
"c(uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}

@ -0,0 +1,11 @@
{
"MyLib": {
"a()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: RETURN 2\n\"];\n}\n"
},
"A": {
"b(MyType)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
},
"B": {
"c(uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}

@ -0,0 +1,11 @@
{
"MyLib": {
"a()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: RETURN 2\n\"];\n}\n"
},
"A": {
"b(MyType)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
},
"B": {
"c(uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}

@ -0,0 +1,11 @@
{
"MyLib": {
"a()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: RETURN 2\n\"];\n}\n"
},
"A": {
"b(MyType)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
},
"B": {
"c(uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}

@ -0,0 +1,11 @@
{
"MyLib": {
"a()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: RETURN 2\n\"];\n}\n"
},
"A": {
"b(MyType)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
},
"B": {
"c(uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}

@ -0,0 +1,11 @@
{
"MyLib": {
"a()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: RETURN 2\n\"];\n}\n"
},
"A": {
"b(MyType)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
},
"B": {
"c(uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}

@ -0,0 +1,11 @@
{
"MyLib": {
"a()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: RETURN 2\n\"];\n}\n"
},
"A": {
"b(MyType)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
},
"B": {
"c(uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}

@ -0,0 +1,8 @@
{
"A": {
"a(uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
},
"B": {
"b(uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}

@ -0,0 +1,22 @@
type MyType is uint256;
library MyLib {
using A for MyType;
using B for uint;
function a() internal returns(uint){
MyType myvar = MyType.wrap(4);
return MyType.unwrap(myvar.b()).c();
}
}
library A {
function b(MyType e) public returns(MyType){
return MyType.wrap(3);
}
}
library B {
function c(uint e) public returns(uint){
return 345;
}
}

@ -0,0 +1,14 @@
library A {
using B for uint256;
function a(uint256 v) public view returns (uint) {
return v.b();
}
}
library B {
function b(uint256 v) public view returns (uint) {
return 1;
}
}

@ -0,0 +1,16 @@
function top_level_yul(int256 c) pure returns (uint result) {
assembly {
function internal_yul(a) -> b {
b := a
}
result := internal_yul(c)
}
}
contract Test {
function test() public{
top_level_yul(10);
}
}

@ -0,0 +1,14 @@
contract BinOp {
uint a = 1 & 2;
uint b = 1 ^ 2;
uint c = 1 | 2;
bool d = 2 < 1;
bool e = 1 > 2;
bool f = 1 <= 2;
bool g = 1 >= 2;
bool h = 1 == 2;
bool i = 1 != 2;
bool j = true && false;
bool k = true || false;
uint l = uint(1) - uint(2);
}

@ -44,7 +44,12 @@ contract MyConc{
address not_constant = msg.sender;
uint not_constant_2 = getNumber();
uint not_constant_3 = 10 + block.number;
uint not_constant_5;
constructor(uint b) public {
not_constant_5 = b;
}
function getNumber() public returns(uint){
return block.number;
}

@ -4,19 +4,19 @@
"elements": [
{
"type": "variable",
"name": "myFriendsAddress",
"name": "text2",
"source_mapping": {
"start": 132,
"length": 76,
"start": 333,
"length": 20,
"filename_relative": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"is_dependency": false,
"lines": [
7
14
],
"starting_column": 5,
"ending_column": 81
"ending_column": 25
},
"type_specific_fields": {
"parent": {
@ -56,10 +56,10 @@
}
}
],
"description": "A.myFriendsAddress (tests/detectors/constable-states/0.4.25/const_state_variables.sol#7) should be constant\n",
"markdown": "[A.myFriendsAddress](tests/detectors/constable-states/0.4.25/const_state_variables.sol#L7) should be constant\n",
"first_markdown_element": "tests/detectors/constable-states/0.4.25/const_state_variables.sol#L7",
"id": "1454db80653b732bf6acbe54ff0ae4707002207a2a8216708c12d61c88a43e5f",
"description": "A.text2 (tests/detectors/constable-states/0.4.25/const_state_variables.sol#14) should be constant \n",
"markdown": "[A.text2](tests/detectors/constable-states/0.4.25/const_state_variables.sol#L14) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.4.25/const_state_variables.sol#L14",
"id": "2f06e04545cea7e7a8998c65d5419f335bf2579a6ce6a832eac9c87392fd5c1a",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
@ -68,19 +68,79 @@
"elements": [
{
"type": "variable",
"name": "test",
"name": "mySistersAddress",
"source_mapping": {
"start": 237,
"length": 20,
"start": 496,
"length": 76,
"filename_relative": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"is_dependency": false,
"lines": [
10
26
],
"starting_column": 5,
"ending_column": 25
"ending_column": 81
},
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "B",
"source_mapping": {
"start": 473,
"length": 271,
"filename_relative": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"is_dependency": false,
"lines": [
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37
],
"starting_column": 1,
"ending_column": 2
}
}
}
}
],
"description": "B.mySistersAddress (tests/detectors/constable-states/0.4.25/const_state_variables.sol#26) should be constant \n",
"markdown": "[B.mySistersAddress](tests/detectors/constable-states/0.4.25/const_state_variables.sol#L26) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.4.25/const_state_variables.sol#L26",
"id": "3b5bff93954a48a79387e7981e8c45d78edc575a0988a10f1c7f439b9f930539",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
},
{
"elements": [
{
"type": "variable",
"name": "myFriendsAddress",
"source_mapping": {
"start": 132,
"length": 76,
"filename_relative": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"is_dependency": false,
"lines": [
7
],
"starting_column": 5,
"ending_column": 81
},
"type_specific_fields": {
"parent": {
@ -120,10 +180,10 @@
}
}
],
"description": "A.test (tests/detectors/constable-states/0.4.25/const_state_variables.sol#10) should be constant\n",
"markdown": "[A.test](tests/detectors/constable-states/0.4.25/const_state_variables.sol#L10) should be constant\n",
"first_markdown_element": "tests/detectors/constable-states/0.4.25/const_state_variables.sol#L10",
"id": "5d9e3fb413322b71a93e90f7e89bd8c83cd4884d577d039598c681fe9db38b1d",
"description": "A.myFriendsAddress (tests/detectors/constable-states/0.4.25/const_state_variables.sol#7) should be constant \n",
"markdown": "[A.myFriendsAddress](tests/detectors/constable-states/0.4.25/const_state_variables.sol#L7) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.4.25/const_state_variables.sol#L7",
"id": "52fd72f6870c4b504d1bcf9fb44249658e2077474d66208a33a47d2668b8db49",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
@ -132,19 +192,19 @@
"elements": [
{
"type": "variable",
"name": "should_be_constant_2",
"name": "should_be_constant",
"source_mapping": {
"start": 841,
"length": 33,
"start": 793,
"length": 42,
"filename_relative": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"is_dependency": false,
"lines": [
43
42
],
"starting_column": 5,
"ending_column": 38
"ending_column": 47
},
"type_specific_fields": {
"parent": {
@ -152,7 +212,7 @@
"name": "MyConc",
"source_mapping": {
"start": 746,
"length": 342,
"length": 423,
"filename_relative": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
@ -171,7 +231,12 @@
49,
50,
51,
52
52,
53,
54,
55,
56,
57
],
"starting_column": 1,
"ending_column": 2
@ -180,70 +245,10 @@
}
}
],
"description": "MyConc.should_be_constant_2 (tests/detectors/constable-states/0.4.25/const_state_variables.sol#43) should be constant\n",
"markdown": "[MyConc.should_be_constant_2](tests/detectors/constable-states/0.4.25/const_state_variables.sol#L43) should be constant\n",
"first_markdown_element": "tests/detectors/constable-states/0.4.25/const_state_variables.sol#L43",
"id": "9a48a4122de1a6a4774a9f1e0d4917bd0fa08f17b4af41b86ba07689e51bf711",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
},
{
"elements": [
{
"type": "variable",
"name": "mySistersAddress",
"source_mapping": {
"start": 496,
"length": 76,
"filename_relative": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"is_dependency": false,
"lines": [
26
],
"starting_column": 5,
"ending_column": 81
},
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "B",
"source_mapping": {
"start": 473,
"length": 271,
"filename_relative": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"is_dependency": false,
"lines": [
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37
],
"starting_column": 1,
"ending_column": 2
}
}
}
}
],
"description": "B.mySistersAddress (tests/detectors/constable-states/0.4.25/const_state_variables.sol#26) should be constant\n",
"markdown": "[B.mySistersAddress](tests/detectors/constable-states/0.4.25/const_state_variables.sol#L26) should be constant\n",
"first_markdown_element": "tests/detectors/constable-states/0.4.25/const_state_variables.sol#L26",
"id": "bee93a722c8eae4a48aade67d8ef537d84c106f48fc9eb738c795fce10d3bc63",
"description": "MyConc.should_be_constant (tests/detectors/constable-states/0.4.25/const_state_variables.sol#42) should be constant \n",
"markdown": "[MyConc.should_be_constant](tests/detectors/constable-states/0.4.25/const_state_variables.sol#L42) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.4.25/const_state_variables.sol#L42",
"id": "8d08797efc8230b480ec669c7e2bf53c3b3d16bc59bf7770934b34fd892934f8",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
@ -252,19 +257,19 @@
"elements": [
{
"type": "variable",
"name": "should_be_constant",
"name": "should_be_constant_2",
"source_mapping": {
"start": 793,
"length": 42,
"start": 841,
"length": 33,
"filename_relative": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"is_dependency": false,
"lines": [
42
43
],
"starting_column": 5,
"ending_column": 47
"ending_column": 38
},
"type_specific_fields": {
"parent": {
@ -272,7 +277,7 @@
"name": "MyConc",
"source_mapping": {
"start": 746,
"length": 342,
"length": 423,
"filename_relative": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
@ -291,7 +296,12 @@
49,
50,
51,
52
52,
53,
54,
55,
56,
57
],
"starting_column": 1,
"ending_column": 2
@ -300,10 +310,10 @@
}
}
],
"description": "MyConc.should_be_constant (tests/detectors/constable-states/0.4.25/const_state_variables.sol#42) should be constant\n",
"markdown": "[MyConc.should_be_constant](tests/detectors/constable-states/0.4.25/const_state_variables.sol#L42) should be constant\n",
"first_markdown_element": "tests/detectors/constable-states/0.4.25/const_state_variables.sol#L42",
"id": "cbcafa2a3efba4d21ac1b51b4b823e5082d556bc3d6cf3fd2ab3188f9f218fc1",
"description": "MyConc.should_be_constant_2 (tests/detectors/constable-states/0.4.25/const_state_variables.sol#43) should be constant \n",
"markdown": "[MyConc.should_be_constant_2](tests/detectors/constable-states/0.4.25/const_state_variables.sol#L43) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.4.25/const_state_variables.sol#L43",
"id": "d08c6d1e331083b42c45c222691dd1e6d880814c66d114971875337ca61ba9c9",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
@ -312,16 +322,16 @@
"elements": [
{
"type": "variable",
"name": "text2",
"name": "test",
"source_mapping": {
"start": 333,
"start": 237,
"length": 20,
"filename_relative": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.4.25/const_state_variables.sol",
"is_dependency": false,
"lines": [
14
10
],
"starting_column": 5,
"ending_column": 25
@ -364,10 +374,10 @@
}
}
],
"description": "A.text2 (tests/detectors/constable-states/0.4.25/const_state_variables.sol#14) should be constant\n",
"markdown": "[A.text2](tests/detectors/constable-states/0.4.25/const_state_variables.sol#L14) should be constant\n",
"first_markdown_element": "tests/detectors/constable-states/0.4.25/const_state_variables.sol#L14",
"id": "df11e6201c4558a8c5cd90b55b134b9ca8f07203b2264d3aa93bd7745e8cb4ba",
"description": "A.test (tests/detectors/constable-states/0.4.25/const_state_variables.sol#10) should be constant \n",
"markdown": "[A.test](tests/detectors/constable-states/0.4.25/const_state_variables.sol#L10) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.4.25/const_state_variables.sol#L10",
"id": "e407a1b57b4d25949ef7c4e6d97197605857099a94774a9c7a848d7dd3463668",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"

@ -41,10 +41,16 @@ contract MyConc{
uint constant A = 1;
bytes32 should_be_constant = sha256('abc');
uint should_be_constant_2 = A + 1;
B should_be_constant_3 = B(address(0));
address not_constant = msg.sender;
uint not_constant_2 = getNumber();
uint not_constant_3 = 10 + block.number;
uint not_constant_5;
constructor(uint b) public {
not_constant_5 = b;
}
function getNumber() public returns(uint){
return block.number;
}

@ -4,50 +4,52 @@
"elements": [
{
"type": "variable",
"name": "myFriendsAddress",
"name": "should_be_constant_3",
"source_mapping": {
"start": 132,
"length": 76,
"start": 880,
"length": 38,
"filename_relative": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"is_dependency": false,
"lines": [
7
44
],
"starting_column": 5,
"ending_column": 81
"ending_column": 43
},
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "A",
"name": "MyConc",
"source_mapping": {
"start": 29,
"length": 441,
"start": 746,
"length": 467,
"filename_relative": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"is_dependency": false,
"lines": [
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52,
53,
54,
55,
56,
57,
58
],
"starting_column": 1,
"ending_column": 2
@ -56,10 +58,10 @@
}
}
],
"description": "A.myFriendsAddress (tests/detectors/constable-states/0.5.16/const_state_variables.sol#7) should be constant\n",
"markdown": "[A.myFriendsAddress](tests/detectors/constable-states/0.5.16/const_state_variables.sol#L7) should be constant\n",
"first_markdown_element": "tests/detectors/constable-states/0.5.16/const_state_variables.sol#L7",
"id": "1454db80653b732bf6acbe54ff0ae4707002207a2a8216708c12d61c88a43e5f",
"description": "MyConc.should_be_constant_3 (tests/detectors/constable-states/0.5.16/const_state_variables.sol#44) should be constant \n",
"markdown": "[MyConc.should_be_constant_3](tests/detectors/constable-states/0.5.16/const_state_variables.sol#L44) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.5.16/const_state_variables.sol#L44",
"id": "29247b0a9939e854ad51bf3b2f58705156aa8b7e446e646b1832467d362b5b3e",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
@ -68,16 +70,16 @@
"elements": [
{
"type": "variable",
"name": "test",
"name": "text2",
"source_mapping": {
"start": 237,
"start": 333,
"length": 20,
"filename_relative": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"is_dependency": false,
"lines": [
10
14
],
"starting_column": 5,
"ending_column": 25
@ -120,10 +122,10 @@
}
}
],
"description": "A.test (tests/detectors/constable-states/0.5.16/const_state_variables.sol#10) should be constant\n",
"markdown": "[A.test](tests/detectors/constable-states/0.5.16/const_state_variables.sol#L10) should be constant\n",
"first_markdown_element": "tests/detectors/constable-states/0.5.16/const_state_variables.sol#L10",
"id": "5d9e3fb413322b71a93e90f7e89bd8c83cd4884d577d039598c681fe9db38b1d",
"description": "A.text2 (tests/detectors/constable-states/0.5.16/const_state_variables.sol#14) should be constant \n",
"markdown": "[A.text2](tests/detectors/constable-states/0.5.16/const_state_variables.sol#L14) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.5.16/const_state_variables.sol#L14",
"id": "2f06e04545cea7e7a8998c65d5419f335bf2579a6ce6a832eac9c87392fd5c1a",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
@ -132,46 +134,46 @@
"elements": [
{
"type": "variable",
"name": "should_be_constant_2",
"name": "mySistersAddress",
"source_mapping": {
"start": 841,
"length": 33,
"start": 496,
"length": 76,
"filename_relative": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"is_dependency": false,
"lines": [
43
26
],
"starting_column": 5,
"ending_column": 38
"ending_column": 81
},
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "MyConc",
"name": "B",
"source_mapping": {
"start": 746,
"length": 342,
"start": 473,
"length": 271,
"filename_relative": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"is_dependency": false,
"lines": [
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37
],
"starting_column": 1,
"ending_column": 2
@ -180,10 +182,10 @@
}
}
],
"description": "MyConc.should_be_constant_2 (tests/detectors/constable-states/0.5.16/const_state_variables.sol#43) should be constant\n",
"markdown": "[MyConc.should_be_constant_2](tests/detectors/constable-states/0.5.16/const_state_variables.sol#L43) should be constant\n",
"first_markdown_element": "tests/detectors/constable-states/0.5.16/const_state_variables.sol#L43",
"id": "9a48a4122de1a6a4774a9f1e0d4917bd0fa08f17b4af41b86ba07689e51bf711",
"description": "B.mySistersAddress (tests/detectors/constable-states/0.5.16/const_state_variables.sol#26) should be constant \n",
"markdown": "[B.mySistersAddress](tests/detectors/constable-states/0.5.16/const_state_variables.sol#L26) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.5.16/const_state_variables.sol#L26",
"id": "3b5bff93954a48a79387e7981e8c45d78edc575a0988a10f1c7f439b9f930539",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
@ -192,16 +194,16 @@
"elements": [
{
"type": "variable",
"name": "mySistersAddress",
"name": "myFriendsAddress",
"source_mapping": {
"start": 496,
"start": 132,
"length": 76,
"filename_relative": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"is_dependency": false,
"lines": [
26
7
],
"starting_column": 5,
"ending_column": 81
@ -209,29 +211,33 @@
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "B",
"name": "A",
"source_mapping": {
"start": 473,
"length": 271,
"start": 29,
"length": 441,
"filename_relative": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"is_dependency": false,
"lines": [
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21
],
"starting_column": 1,
"ending_column": 2
@ -240,10 +246,10 @@
}
}
],
"description": "B.mySistersAddress (tests/detectors/constable-states/0.5.16/const_state_variables.sol#26) should be constant\n",
"markdown": "[B.mySistersAddress](tests/detectors/constable-states/0.5.16/const_state_variables.sol#L26) should be constant\n",
"first_markdown_element": "tests/detectors/constable-states/0.5.16/const_state_variables.sol#L26",
"id": "bee93a722c8eae4a48aade67d8ef537d84c106f48fc9eb738c795fce10d3bc63",
"description": "A.myFriendsAddress (tests/detectors/constable-states/0.5.16/const_state_variables.sol#7) should be constant \n",
"markdown": "[A.myFriendsAddress](tests/detectors/constable-states/0.5.16/const_state_variables.sol#L7) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.5.16/const_state_variables.sol#L7",
"id": "52fd72f6870c4b504d1bcf9fb44249658e2077474d66208a33a47d2668b8db49",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
@ -272,7 +278,7 @@
"name": "MyConc",
"source_mapping": {
"start": 746,
"length": 342,
"length": 467,
"filename_relative": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
@ -291,7 +297,13 @@
49,
50,
51,
52
52,
53,
54,
55,
56,
57,
58
],
"starting_column": 1,
"ending_column": 2
@ -300,10 +312,10 @@
}
}
],
"description": "MyConc.should_be_constant (tests/detectors/constable-states/0.5.16/const_state_variables.sol#42) should be constant\n",
"markdown": "[MyConc.should_be_constant](tests/detectors/constable-states/0.5.16/const_state_variables.sol#L42) should be constant\n",
"description": "MyConc.should_be_constant (tests/detectors/constable-states/0.5.16/const_state_variables.sol#42) should be constant \n",
"markdown": "[MyConc.should_be_constant](tests/detectors/constable-states/0.5.16/const_state_variables.sol#L42) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.5.16/const_state_variables.sol#L42",
"id": "cbcafa2a3efba4d21ac1b51b4b823e5082d556bc3d6cf3fd2ab3188f9f218fc1",
"id": "8d08797efc8230b480ec669c7e2bf53c3b3d16bc59bf7770934b34fd892934f8",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
@ -312,16 +324,82 @@
"elements": [
{
"type": "variable",
"name": "text2",
"name": "should_be_constant_2",
"source_mapping": {
"start": 333,
"start": 841,
"length": 33,
"filename_relative": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"is_dependency": false,
"lines": [
43
],
"starting_column": 5,
"ending_column": 38
},
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "MyConc",
"source_mapping": {
"start": 746,
"length": 467,
"filename_relative": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"is_dependency": false,
"lines": [
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52,
53,
54,
55,
56,
57,
58
],
"starting_column": 1,
"ending_column": 2
}
}
}
}
],
"description": "MyConc.should_be_constant_2 (tests/detectors/constable-states/0.5.16/const_state_variables.sol#43) should be constant \n",
"markdown": "[MyConc.should_be_constant_2](tests/detectors/constable-states/0.5.16/const_state_variables.sol#L43) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.5.16/const_state_variables.sol#L43",
"id": "d08c6d1e331083b42c45c222691dd1e6d880814c66d114971875337ca61ba9c9",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
},
{
"elements": [
{
"type": "variable",
"name": "test",
"source_mapping": {
"start": 237,
"length": 20,
"filename_relative": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.5.16/const_state_variables.sol",
"is_dependency": false,
"lines": [
14
10
],
"starting_column": 5,
"ending_column": 25
@ -364,10 +442,10 @@
}
}
],
"description": "A.text2 (tests/detectors/constable-states/0.5.16/const_state_variables.sol#14) should be constant\n",
"markdown": "[A.text2](tests/detectors/constable-states/0.5.16/const_state_variables.sol#L14) should be constant\n",
"first_markdown_element": "tests/detectors/constable-states/0.5.16/const_state_variables.sol#L14",
"id": "df11e6201c4558a8c5cd90b55b134b9ca8f07203b2264d3aa93bd7745e8cb4ba",
"description": "A.test (tests/detectors/constable-states/0.5.16/const_state_variables.sol#10) should be constant \n",
"markdown": "[A.test](tests/detectors/constable-states/0.5.16/const_state_variables.sol#L10) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.5.16/const_state_variables.sol#L10",
"id": "e407a1b57b4d25949ef7c4e6d97197605857099a94774a9c7a848d7dd3463668",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"

@ -1,5 +1,3 @@
//pragma solidity ^0.4.24;
contract A {
@ -36,17 +34,46 @@ contract B is A {
}
}
contract MyConc{
contract Bad {
uint constant A = 1;
bytes32 should_be_constant = sha256('abc');
uint should_be_constant_2 = A + 1;
address not_constant = msg.sender;
uint not_constant_2 = getNumber();
uint not_constant_3 = 10 + block.number;
B should_be_constant_3 = B(address(0));
address should_be_immutable = msg.sender;
uint should_be_immutable_2 = getNumber();
uint should_be_immutable_3 = 10 + block.number;
B should_be_immutable_4 = new B();
uint should_be_immutable_5;
constructor(uint b) public {
should_be_immutable_5 = b;
}
function getNumber() public returns(uint){
return block.number;
}
}
contract Good {
uint constant A = 1;
bytes32 constant should_be_constant = sha256('abc');
uint constant should_be_constant_2 = A + 1;
B constant should_be_constant_3 = B(address(0));
address immutable should_be_immutable = msg.sender;
uint immutable should_be_immutable_2 = getNumber();
uint immutable should_be_immutable_3 = 10 + block.number;
B immutable should_be_immutable_4 = new B();
uint immutable should_be_immutable_5;
constructor(uint b) public {
should_be_immutable_5 = b;
}
function getNumber() public returns(uint){
return block.number;
}
}

@ -4,32 +4,34 @@
"elements": [
{
"type": "variable",
"name": "myFriendsAddress",
"name": "text2",
"source_mapping": {
"start": 132,
"length": 76,
"start": 305,
"length": 20,
"filename_relative": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"is_dependency": false,
"lines": [
7
12
],
"starting_column": 5,
"ending_column": 81
"ending_column": 25
},
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "A",
"source_mapping": {
"start": 29,
"start": 1,
"length": 441,
"filename_relative": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"is_dependency": false,
"lines": [
2,
3,
4,
5,
6,
@ -45,9 +47,7 @@
16,
17,
18,
19,
20,
21
19
],
"starting_column": 1,
"ending_column": 2
@ -56,10 +56,10 @@
}
}
],
"description": "A.myFriendsAddress (tests/detectors/constable-states/0.6.11/const_state_variables.sol#7) should be constant\n",
"markdown": "[A.myFriendsAddress](tests/detectors/constable-states/0.6.11/const_state_variables.sol#L7) should be constant\n",
"first_markdown_element": "tests/detectors/constable-states/0.6.11/const_state_variables.sol#L7",
"id": "1454db80653b732bf6acbe54ff0ae4707002207a2a8216708c12d61c88a43e5f",
"description": "A.text2 (tests/detectors/constable-states/0.6.11/const_state_variables.sol#12) should be constant \n",
"markdown": "[A.text2](tests/detectors/constable-states/0.6.11/const_state_variables.sol#L12) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.6.11/const_state_variables.sol#L12",
"id": "2f06e04545cea7e7a8998c65d5419f335bf2579a6ce6a832eac9c87392fd5c1a",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
@ -68,50 +68,53 @@
"elements": [
{
"type": "variable",
"name": "test",
"name": "should_be_constant_2",
"source_mapping": {
"start": 237,
"length": 20,
"start": 811,
"length": 33,
"filename_relative": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"is_dependency": false,
"lines": [
10
41
],
"starting_column": 5,
"ending_column": 25
"ending_column": 38
},
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "A",
"name": "Bad",
"source_mapping": {
"start": 29,
"length": 441,
"start": 718,
"length": 539,
"filename_relative": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"is_dependency": false,
"lines": [
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21
37,
38,
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52,
53,
54,
55,
56,
57
],
"starting_column": 1,
"ending_column": 2
@ -120,10 +123,10 @@
}
}
],
"description": "A.test (tests/detectors/constable-states/0.6.11/const_state_variables.sol#10) should be constant\n",
"markdown": "[A.test](tests/detectors/constable-states/0.6.11/const_state_variables.sol#L10) should be constant\n",
"first_markdown_element": "tests/detectors/constable-states/0.6.11/const_state_variables.sol#L10",
"id": "5d9e3fb413322b71a93e90f7e89bd8c83cd4884d577d039598c681fe9db38b1d",
"description": "Bad.should_be_constant_2 (tests/detectors/constable-states/0.6.11/const_state_variables.sol#41) should be constant \n",
"markdown": "[Bad.should_be_constant_2](tests/detectors/constable-states/0.6.11/const_state_variables.sol#L41) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.6.11/const_state_variables.sol#L41",
"id": "3a8b682f7960750cd8228d6cd3d0bb5d7d6f9faaf1a044de2fa7069d8e475af2",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
@ -132,46 +135,46 @@
"elements": [
{
"type": "variable",
"name": "should_be_constant_2",
"name": "mySistersAddress",
"source_mapping": {
"start": 841,
"length": 33,
"start": 468,
"length": 76,
"filename_relative": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"is_dependency": false,
"lines": [
43
24
],
"starting_column": 5,
"ending_column": 38
"ending_column": 81
},
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "MyConc",
"name": "B",
"source_mapping": {
"start": 746,
"length": 342,
"start": 445,
"length": 271,
"filename_relative": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"is_dependency": false,
"lines": [
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35
],
"starting_column": 1,
"ending_column": 2
@ -180,10 +183,10 @@
}
}
],
"description": "MyConc.should_be_constant_2 (tests/detectors/constable-states/0.6.11/const_state_variables.sol#43) should be constant\n",
"markdown": "[MyConc.should_be_constant_2](tests/detectors/constable-states/0.6.11/const_state_variables.sol#L43) should be constant\n",
"first_markdown_element": "tests/detectors/constable-states/0.6.11/const_state_variables.sol#L43",
"id": "9a48a4122de1a6a4774a9f1e0d4917bd0fa08f17b4af41b86ba07689e51bf711",
"description": "B.mySistersAddress (tests/detectors/constable-states/0.6.11/const_state_variables.sol#24) should be constant \n",
"markdown": "[B.mySistersAddress](tests/detectors/constable-states/0.6.11/const_state_variables.sol#L24) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.6.11/const_state_variables.sol#L24",
"id": "3b5bff93954a48a79387e7981e8c45d78edc575a0988a10f1c7f439b9f930539",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
@ -192,16 +195,16 @@
"elements": [
{
"type": "variable",
"name": "mySistersAddress",
"name": "myFriendsAddress",
"source_mapping": {
"start": 496,
"start": 104,
"length": 76,
"filename_relative": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"is_dependency": false,
"lines": [
26
5
],
"starting_column": 5,
"ending_column": 81
@ -209,29 +212,33 @@
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "B",
"name": "A",
"source_mapping": {
"start": 473,
"length": 271,
"start": 1,
"length": 441,
"filename_relative": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"is_dependency": false,
"lines": [
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19
],
"starting_column": 1,
"ending_column": 2
@ -240,10 +247,10 @@
}
}
],
"description": "B.mySistersAddress (tests/detectors/constable-states/0.6.11/const_state_variables.sol#26) should be constant\n",
"markdown": "[B.mySistersAddress](tests/detectors/constable-states/0.6.11/const_state_variables.sol#L26) should be constant\n",
"first_markdown_element": "tests/detectors/constable-states/0.6.11/const_state_variables.sol#L26",
"id": "bee93a722c8eae4a48aade67d8ef537d84c106f48fc9eb738c795fce10d3bc63",
"description": "A.myFriendsAddress (tests/detectors/constable-states/0.6.11/const_state_variables.sol#5) should be constant \n",
"markdown": "[A.myFriendsAddress](tests/detectors/constable-states/0.6.11/const_state_variables.sol#L5) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.6.11/const_state_variables.sol#L5",
"id": "52fd72f6870c4b504d1bcf9fb44249658e2077474d66208a33a47d2668b8db49",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
@ -254,14 +261,14 @@
"type": "variable",
"name": "should_be_constant",
"source_mapping": {
"start": 793,
"start": 763,
"length": 42,
"filename_relative": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"is_dependency": false,
"lines": [
42
40
],
"starting_column": 5,
"ending_column": 47
@ -269,15 +276,84 @@
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "MyConc",
"name": "Bad",
"source_mapping": {
"start": 718,
"length": 539,
"filename_relative": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"is_dependency": false,
"lines": [
37,
38,
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52,
53,
54,
55,
56,
57
],
"starting_column": 1,
"ending_column": 2
}
}
}
}
],
"description": "Bad.should_be_constant (tests/detectors/constable-states/0.6.11/const_state_variables.sol#40) should be constant \n",
"markdown": "[Bad.should_be_constant](tests/detectors/constable-states/0.6.11/const_state_variables.sol#L40) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.6.11/const_state_variables.sol#L40",
"id": "87097c03d57b72ad7c15336eb44e5a30054c50f8daff32e08bc4fbd97852961c",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
},
{
"elements": [
{
"type": "variable",
"name": "should_be_constant_3",
"source_mapping": {
"start": 850,
"length": 38,
"filename_relative": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"is_dependency": false,
"lines": [
42
],
"starting_column": 5,
"ending_column": 43
},
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "Bad",
"source_mapping": {
"start": 746,
"length": 342,
"start": 718,
"length": 539,
"filename_relative": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"is_dependency": false,
"lines": [
37,
38,
39,
40,
41,
@ -291,7 +367,12 @@
49,
50,
51,
52
52,
53,
54,
55,
56,
57
],
"starting_column": 1,
"ending_column": 2
@ -300,10 +381,10 @@
}
}
],
"description": "MyConc.should_be_constant (tests/detectors/constable-states/0.6.11/const_state_variables.sol#42) should be constant\n",
"markdown": "[MyConc.should_be_constant](tests/detectors/constable-states/0.6.11/const_state_variables.sol#L42) should be constant\n",
"description": "Bad.should_be_constant_3 (tests/detectors/constable-states/0.6.11/const_state_variables.sol#42) should be constant \n",
"markdown": "[Bad.should_be_constant_3](tests/detectors/constable-states/0.6.11/const_state_variables.sol#L42) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.6.11/const_state_variables.sol#L42",
"id": "cbcafa2a3efba4d21ac1b51b4b823e5082d556bc3d6cf3fd2ab3188f9f218fc1",
"id": "8e991c1370b1adb10f01f2d7e48f341dee92a98b91b56ccb291d9149d2da97d0",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
@ -312,16 +393,16 @@
"elements": [
{
"type": "variable",
"name": "text2",
"name": "test",
"source_mapping": {
"start": 333,
"start": 209,
"length": 20,
"filename_relative": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"is_dependency": false,
"lines": [
14
8
],
"starting_column": 5,
"ending_column": 25
@ -331,13 +412,15 @@
"type": "contract",
"name": "A",
"source_mapping": {
"start": 29,
"start": 1,
"length": 441,
"filename_relative": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/constable-states/0.6.11/const_state_variables.sol",
"is_dependency": false,
"lines": [
2,
3,
4,
5,
6,
@ -353,9 +436,7 @@
16,
17,
18,
19,
20,
21
19
],
"starting_column": 1,
"ending_column": 2
@ -364,10 +445,10 @@
}
}
],
"description": "A.text2 (tests/detectors/constable-states/0.6.11/const_state_variables.sol#14) should be constant\n",
"markdown": "[A.text2](tests/detectors/constable-states/0.6.11/const_state_variables.sol#L14) should be constant\n",
"first_markdown_element": "tests/detectors/constable-states/0.6.11/const_state_variables.sol#L14",
"id": "df11e6201c4558a8c5cd90b55b134b9ca8f07203b2264d3aa93bd7745e8cb4ba",
"description": "A.test (tests/detectors/constable-states/0.6.11/const_state_variables.sol#8) should be constant \n",
"markdown": "[A.test](tests/detectors/constable-states/0.6.11/const_state_variables.sol#L8) should be constant \n",
"first_markdown_element": "tests/detectors/constable-states/0.6.11/const_state_variables.sol#L8",
"id": "e407a1b57b4d25949ef7c4e6d97197605857099a94774a9c7a848d7dd3463668",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"

@ -1,5 +1,3 @@
//pragma solidity ^0.4.24;
contract A {
@ -36,15 +34,43 @@ contract B is A {
}
}
contract MyConc{
contract Bad {
uint constant A = 1;
bytes32 should_be_constant = sha256('abc');
uint should_be_constant_2 = A + 1;
address not_constant = msg.sender;
uint not_constant_2 = getNumber();
uint not_constant_3 = 10 + block.number;
B should_be_constant_3 = B(address(0));
address should_be_immutable = msg.sender;
uint should_be_immutable_2 = getNumber();
uint should_be_immutable_3 = 10 + block.number;
B should_be_immutable_4 = new B();
uint should_be_immutable_5;
constructor(uint b) {
should_be_immutable_5 = b;
}
function getNumber() public returns(uint){
return block.number;
}
}
contract Good {
uint constant A = 1;
bytes32 constant should_be_constant = sha256('abc');
uint constant should_be_constant_2 = A + 1;
B constant should_be_constant_3 = B(address(0));
address immutable should_be_immutable = msg.sender;
uint immutable should_be_immutable_2 = getNumber();
uint immutable should_be_immutable_3 = 10 + block.number;
B immutable should_be_immutable_4 = new B();
uint immutable should_be_immutable_5;
constructor(uint b) {
should_be_immutable_5 = b;
}
function getNumber() public returns(uint){
return block.number;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save