Repository: pessimistic-io/slitherin (language: python)

Issue:
Add arithmetic overflow detector and improve unprotected initializer detector
The new detector sees if there are assignments/returns that calculate some arithmetic expressions and if some intermediate calculations
contain a type that is lower than the expected result. Such behavior may lead to unexpected overflow/underflow, e.g., trying to assign the multiplication of two `uint48` variables to `uint256` would look like `uint48 * uint48`, and it may overflow (however, the final type would fit such multiplication).


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
new file mode 100644
index 0000000..66c4349
--- /dev/null
+++ b/.github/workflows/benchmark.yml
@@ -0,0 +1,102 @@
+name: Run Benchmark
+
+on:
+    pull_request:
+        branches:
+            - 'master'
+            - 'develop'
+jobs:
+    RunBenchmarkMainnet: 
+        runs-on: ubuntu-latest
+        steps:
+            - name: Checkout repository
+              uses: actions/checkout@v4
+              with:
+                submodules: 'true'
+            - name: Set up Python
+              uses: actions/setup-python@v4
+              with:
+                python-version: '3.x'
+            - name: Set up Node
+              uses: actions/setup-node@v4
+              with:
+                node-version: '18.x'
+            - name: Update pip
+              run: python -m pip install --upgrade pip
+            - name: Install solc-select
+              run: python -m pip install solc-select
+            - name: Install Slither
+              run: python -m pip install slither-analyzer
+            - name: Install Setuptools
+              run: python -m pip install setuptools
+            - name: Install Slitherin
+              run: python setup.py develop
+            - name: Configure
+              run: |
+                cd slitherin-benchmark/
+                mv example.config.py config.py
+            - name: Install node dependencies
+              run: npm ci
+            - name: Install benchmark requirements
+              run: |
+                cd slitherin-benchmark/
+                python -m pip install -r requirements.txt
+            - name: Run Benchmark
+              run: |
+                cd slitherin-benchmark/
+                python runner.py -i contracts/mainnet -o mainnet.csv --limit 7000
+            - name: 'Upload Artifact'
+              uses: actions/upload-artifact@v3
+              with:
+                name: mainnet
+                path: slitherin-benchmark/mainnet.csv
+    RunBenchmarkOZ: 
+        runs-on: ubuntu-latest
+        steps:
+            - name: Checkout repository
+              uses: actions/checkout@v4
+              with:
+                submodules: 'true'
+            - name: Set up Python
+              uses: actions/setup-python@v4
+              with:
+                python-version: '3.x'
+            - name: Set up Node
+              uses: actions/setup-node@v4
+              with:
+                node-version: '18.x'
+            - name: Update pip
+              run: python -m pip install --upgrade pip
+            - name: Install solc-select
+              run: python -m pip install solc-select
+            - name: Install Slither
+              run: python -m pip install slither-analyzer
+            - name: Install Setuptools
+              run: python -m pip install setuptools
+            - name: Install Slitherin
+              run: python setup.py develop
+            - name: Configure
+              run: |
+                cd slitherin-benchmark/
+                mv example.config.py config.py
+            - name: Install node dependencies
+              run: npm ci
+            - name: Install benchmark requirements
+              run: |
+                cd slitherin-benchmark/
+                python -m pip install -r requirements.txt
+            - name: Run Benchmark
+              run: |
+                cd slitherin-benchmark/
+                python runner.py -i contracts/openzeppelin -o oz.csv -eo oz_extra.csv
+            - name: 'Upload Artifact'
+              uses: actions/upload-artifact@v3
+              with:
+                name: oz
+                path: slitherin-benchmark/oz.csv
+            - name: 'Upload Artifact'
+              uses: actions/upload-artifact@v3
+              with:
+                name: oz_extra
+                path: slitherin-benchmark/oz_extra.csv
+
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..14a2ab0
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "slitherin-benchmark"]
+	path = slitherin-benchmark
+	url = https://github.com/pessimistic-io/slitherin-benchmark.git
diff --git a/README.md b/README.md
index 7c878a3..983d9fa 100644
--- a/README.md
+++ b/README.md
@@ -64,6 +64,10 @@ slitherin . --slither
 ```bash
 slitherin . --separated
 ```
+* Run Arbitrum-specific Slitherin detectors:
+```bash
+slitherin . --arbitrum
+```
 > Keep in mind that Slitherin-cli supports all Slither run options.
 ### Slither
 Slitherin detectors are included into original Slither after the installation. You can use Slither [as usual](https://github.com/crytic/slither#usage).
diff --git a/docs/arb_block_number_timestamp.md b/docs/arb_block_number_timestamp.md
new file mode 100644
index 0000000..85b2f1a
--- /dev/null
+++ b/docs/arb_block_number_timestamp.md
@@ -0,0 +1,19 @@
+# Arbitrum block.number/block.timestamp
+
+## Configuration
+
+- Check: `pess-arb-solidity-version`
+- Severity: `Low`
+- Confidence: `High`
+
+## Description
+
+`block.number` and `block.timestamp` behave different, than how they behave on Ethereum. For details: [arbitrum docs](https://docs.arbitrum.io/for-devs/concepts/differences-between-arbitrum-ethereum/block-numbers-and-time)
+
+## Vulnerable Scenario
+
+[test scenarios](../tests/arbitrum_block_number_timestamp_test.sol)
+
+## Recommendation
+
+Verify, that contract's logic does not break because of this difference
diff --git a/docs/arb_difficulty_randao.md b/docs/arb_difficulty_randao.md
new file mode 100644
index 0000000..49cae6f
--- /dev/null
+++ b/docs/arb_difficulty_randao.md
@@ -0,0 +1,15 @@
+# Arbitrum prevRandao/difficulty
+
+## Configuration
+* Check: `pess-arb-prevrandao-difficulty`
+* Severity: `Medium`
+* Confidence: `High`
+
+## Description
+The detector sees if an Arbitrum contract contains usages of prevRandao/difficulty block context variables or Yul funciton.
+
+## Vulnerable Scenario
+[test scenarios](../tests/arbitrum_prevrandao_difficulty_test.sol)
+
+## Recommendation
+Consider removing usage of prevRandao/difficulty inside Arbitrum contracts as they will constantly return `1` in Arbitrum.
diff --git a/docs/arb_solidity_version.md b/docs/arb_solidity_version.md
new file mode 100644
index 0000000..cfc5b68
--- /dev/null
+++ b/docs/arb_solidity_version.md
@@ -0,0 +1,19 @@
+# Arbitrum solidity version
+
+## Configuration
+
+- Check: `pess-arb-solidity-version`
+- Severity: `High`
+- Confidence: `Low`
+
+## Description
+
+Checks that sol version >= 0.8.20 is not used inside an Arbitrum contract. Solidity in these versions will utilize PUSH0 opcode, which is not supported on Arbitrum.
+
+## Vulnerable Scenario
+
+[test scenarios](../tests/arbitrum_prevrandao_solidity_version_test.sol)
+
+## Recommendation
+
+Either, use versions `0.8.19`` and below, or EVM versions below shanghai
diff --git a/docs/ecrecover.md b/docs/ecrecover.md
index 5300d12..fa557d0 100644
--- a/docs/ecrecover.md
+++ b/docs/ecrecover.md
@@ -12,7 +12,8 @@
 
 ### Potential Improvement
 
-As for now, the detector might not work on asm level.
+- As for now, the detector might not work on asm level.
+- `ecrecover_negative5` in [test scenarios](../tests/ecrecover.sol) should be found since the result of the check is unused. However, currently such scenarious are not covered.
 
 ## Vulnerable Scenario
 
diff --git a/docs/potential_arith_overflow.md b/docs/potential_arith_overflow.md
new file mode 100644
index 0000000..d96c648
--- /dev/null
+++ b/docs/potential_arith_overflow.md
@@ -0,0 +1,21 @@
+# Potential arithmetic overflow
+
+## Configuration
+* Check: `pess-potential-arithmetic-overflow`
+* Severity: `Medium`
+* Confidence: `Medium`
+
+## Description
+The detector sees if there are assignments/returns that calculate some arithmetic expressions and if some intermediate calculations
+contain a type that is lower than the expected result. Such behavior may lead to unexpected overflow/underflow, e.g., trying to assign the multiplication of two `uint48` variables to `uint256` would look like `uint48 * uint48` and it may overflow (however, the final type would fit such multiplication).
+
+### Potential Improvement
+- Handle return statements that return tuples of arithmetic expressions;
+- Handle signed integer underflow for subtraction operation e.g. `int256 = int64 - int64` should produce error (`int64` should be cast to `int256`).
+- Improve the output of the detector (unroll complex expressions in something readable, not just `...`)
+
+## Vulnerable Scenario
+[test scenario](../tests/potential_arith_overflow.sol)
+
+## Recommendation
+Use explicit type casting in sub expressions when the assigment to a larger type is performed.
\ No newline at end of file
diff --git a/docs/strange_setter.md b/docs/strange_setter.md
index c892caf..c20ecff 100644
--- a/docs/strange_setter.md
+++ b/docs/strange_setter.md
@@ -6,15 +6,15 @@
 * Confidence: `Medium`
 
 ## Description
-The detector sees if a contract contains a setter (also constructor) that does not change contract storage variables.
-Setter functions MUST change the values of storage variables.
-Setter functions that do not modify storage variables may lead to contract misfunctions.
+The detector sees if a contract contains a setter (also constructor) that does not change contract storage variables or does not perform external calls using provided arguments.
+Setter functions MUST change the values of storage variables or perform external calls using provided parameters.
+Setter functions that do not use provided variables may lead to contract misfunctions.
 
 ### Potential Improvement
-Remove highlights of mappings.
+Detect shadowing before storage update/external call
 
 ## Vulnerable Scenario
 [test scenario](../tests/strange_setter_test.sol)
 
 ## Recommendation
-Make sure that setter functions modify the states of storage variables.
\ No newline at end of file
+Make sure that setter functions modify the states of storage variables or performs external call using provided arguments.
\ No newline at end of file
diff --git a/setup.py b/setup.py
index d3b5743..66bfb56 100644
--- a/setup.py
+++ b/setup.py
@@ -23,7 +23,7 @@
     packages=find_packages(),
     license="AGPL-3.0",
     python_requires=">=3.8",
-    install_requires=["slither-analyzer>=0.9.3"],
+    install_requires=["slither-analyzer>=0.10.0"],
     extras_requires={
         "dev": ["twine>=4.0.2"],
     },
diff --git a/slitherin-benchmark b/slitherin-benchmark
new file mode 160000
index 0000000..12792f9
--- /dev/null
+++ b/slitherin-benchmark
@@ -0,0 +1,1 @@
+Subproject commit 12792f91792dfe5a90ed9a0fc955606082b92305
diff --git a/slitherin/__init__.py b/slitherin/__init__.py
index f4c2abc..35c1218 100644
--- a/slitherin/__init__.py
+++ b/slitherin/__init__.py
@@ -23,9 +23,22 @@
 from slitherin.detectors.ecrecover import Ecrecover
 from slitherin.detectors.public_vs_external import PublicVsExternal
 from slitherin.detectors.aave.flashloan_callback import AAVEFlashloanCallbackDetector
+from slitherin.detectors.arbitrum.arbitrum_prevrandao_difficulty import (
+    ArbitrumPrevrandaoDifficulty,
+)
+from slitherin.detectors.arbitrum.solidity_version import ArbitrumSolidityVersion
+from slitherin.detectors.arbitrum.block_number_timestamp import (
+    ArbitrumBlockNumberTimestamp,
+)
+from slitherin.detectors.potential_arith_overflow import PotentialArithmOverflow
 
+artbitrum_detectors = [
+    ArbitrumPrevrandaoDifficulty,
+    ArbitrumSolidityVersion,
+    ArbitrumBlockNumberTimestamp,
+]
 
-plugin_detectors = [
+plugin_detectors = artbitrum_detectors + [
     DoubleEntryTokenPossiblity,
     UnprotectedSetter,
     NftApproveWarning,
@@ -49,6 +62,7 @@
     Ecrecover,
     PublicVsExternal,
     AAVEFlashloanCallbackDetector,
+    PotentialArithmOverflow
 ]
 plugin_printers = []
 
diff --git a/slitherin/cli.py b/slitherin/cli.py
index bc7fd6b..2a3b7bf 100644
--- a/slitherin/cli.py
+++ b/slitherin/cli.py
@@ -5,17 +5,22 @@
 import os
 import shutil
 import pty
+import sys
 from pathlib import Path
 import slitherin
 from pkg_resources import iter_entry_points
 
-SLITHERIN_VERSION = "0.5.0"
+from .consts import *
 
 
 def slitherin_detectors_list_as_arguments() -> str:
     return ",".join([detector.ARGUMENT for detector in slitherin.plugin_detectors])
 
 
+def arbitrum_detectors_list_as_arguments() -> str:
+    return ",".join([detector.ARGUMENT for detector in slitherin.artbitrum_detectors])
+
+
 logging.basicConfig()
 LOGGER = logging.getLogger("slitherinLogger")
 LOGGER.setLevel(logging.INFO)
@@ -47,7 +52,34 @@ def run(
         subprocess_cwd,
     )
 
-    pty.spawn(cmd, read)  # this allows to print continuously and with colors
+    master, slave = pty.openpty()
+
+    try:
+        process = subprocess.Popen(cmd, stdout=slave, stderr=slave, text=False)
+        os.close(slave)
+
+        while True:
+            try:
+                output = os.read(master, 1024)
+            except OSError as e:
+                if e.errno == 5:  # Errno 5 corresponds to Input/output error
+                    break
+                else:
+                    raise
+            if not output:
+                break
+            decoded_output = output.decode(sys.stdout.encoding, errors="replace")
+            print(decoded_output, end="")
+
+        return_code = process.wait()
+        if return_code != 0:
+            raise Exception(
+                f"Errored out with code: {return_code}, while running slither"
+            )
+
+    except Exception as e:
+        print(f"Failed to run slither: {str(e)}")
+        raise e
 
 
 def handle_list() -> None:
@@ -61,7 +93,11 @@ def handle_list() -> None:
 
 def handle_parser(args: argparse.Namespace, slither_args) -> None:
     slitherin_detectors = slitherin_detectors_list_as_arguments()
-    slither_with_args = ["slither"] + slither_args
+    slither_with_args = [
+        "slither",
+        "--fail-none",
+    ] + slither_args  # using fail-none flag, so slither will return 0 even though there are findings
+
     if args.pess:
         run(
             slither_with_args + ["--detect", slitherin_detectors],
@@ -79,6 +115,11 @@ def handle_parser(args: argparse.Namespace, slither_args) -> None:
         run(
             slither_with_args + ["--ignore-compile", "--detect", slitherin_detectors],
         )
+    elif args.arbitrum:
+        os.environ["SLITHERIN_ARBITRUM"] = "True"
+        run(slither_with_args + ["--detect", arbitrum_detectors_list_as_arguments()])
+        del os.environ["SLITHERIN_ARBITRUM"]
+
     else:
         run(slither_with_args)
 
@@ -120,6 +161,12 @@ def generate_argument_parser() -> argparse.ArgumentParser:
         action="store_true",
         help="Run slither detectors, then slitherin",
     )
+
+    parser.add_argument(
+        "--arbitrum",
+        action="store_true",
+        help="Run arbitrum detectors",
+    )
     return parser
 
 
diff --git a/slitherin/consts.py b/slitherin/consts.py
new file mode 100644
index 0000000..d20d4c5
--- /dev/null
+++ b/slitherin/consts.py
@@ -0,0 +1,2 @@
+ARBITRUM_KEY = "SLITHERIN_ARBITRUM"
+SLITHERIN_VERSION = "0.6.0"
diff --git a/slitherin/detectors/arbitrum/arbitrum_prevrandao_difficulty.py b/slitherin/detectors/arbitrum/arbitrum_prevrandao_difficulty.py
new file mode 100644
index 0000000..b81b497
--- /dev/null
+++ b/slitherin/detectors/arbitrum/arbitrum_prevrandao_difficulty.py
@@ -0,0 +1,81 @@
+import os
+from typing import List
+from slither.utils.output import Output
+from slither.core.cfg.node import Node
+from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
+from slither.core.declarations import Function
+from slither.slithir.operations.event_call import EventCall
+from slither.analyses.data_dependency.data_dependency import is_dependent
+from slither.slithir.operations import SolidityCall
+from slither.core.declarations.solidity_variables import (
+    SolidityVariableComposed,
+    SolidityFunction,
+)
+
+from ...consts import ARBITRUM_KEY
+
+
+class ArbitrumPrevrandaoDifficulty(AbstractDetector):
+    """
+    Sees if `prevRandao` or `difficulty` is used inside an Arbitrum contract (as they return constant `1` in Arbitrum)
+    """
+
+    ARGUMENT = "pess-arb-prevrandao-difficulty"  # slither will launch the detector with slither.py --detect mydetector
+    HELP = (
+        "PrevRandao or difficulty is used in contract that will be deployed to Arbitrum"
+    )
+    IMPACT = DetectorClassification.MEDIUM
+    CONFIDENCE = DetectorClassification.HIGH
+
+    WIKI = "https://github.com/pessimistic-io/slitherin/blob/master/docs/arb_difficulty_randao.md"
+    WIKI_TITLE = "Usage of prevRandao/difficulty inside the Arbitrum contract"
+    WIKI_DESCRIPTION = "Arbitrum prevRandao/difficulty"
+    WIKI_EXPLOIT_SCENARIO = "N/A"
+    WIKI_RECOMMENDATION = (
+        "Do not use prevRandao/difficulty inside the code of an Arbitrum contract"
+    )
+
+    def _find_randao_or_difficulty(self, f: Function) -> List[Node]:
+        ret = set()
+        for node in f.nodes:
+            for var in node.variables_read:
+                if is_dependent(
+                    var, SolidityVariableComposed("block.prevrandao"), node
+                ) or is_dependent(
+                    var, SolidityVariableComposed("block.difficulty"), node
+                ):
+                    ret.add(node)
+            for ir in node.irs:
+                if (
+                    isinstance(ir, SolidityCall)
+                    and ir.function == SolidityFunction("prevrandao()")
+                    or isinstance(ir, SolidityCall)
+                    and ir.function == SolidityFunction("difficulty()")
+                ):
+                    ret.add(node)
+        return list(ret)
+
+    def _detect(self) -> List[Output]:
+        """Main function"""
+        results = []
+
+        if os.getenv(ARBITRUM_KEY) is None:
+            return results
+
+        for contract in self.compilation_unit.contracts_derived:
+            for f in contract.functions_and_modifiers:
+                nodes = self._find_randao_or_difficulty(f)
+                if nodes:
+                    info = [
+                        f,
+                        " function uses prevRandao/difficulty inside the code of the Arbitrum contract\n",
+                        "\tDangerous usages:\n",
+                    ]
+
+                    nodes.sort(key=lambda x: x.node_id)
+
+                    for node in nodes:
+                        info += ["\t- ", node, "\n"]
+
+                    results.append(self.generate_result(info))
+        return results
diff --git a/slitherin/detectors/arbitrum/block_number_timestamp.py b/slitherin/detectors/arbitrum/block_number_timestamp.py
new file mode 100644
index 0000000..f142f1c
--- /dev/null
+++ b/slitherin/detectors/arbitrum/block_number_timestamp.py
@@ -0,0 +1,81 @@
+import os
+from typing import List
+from slither.utils.output import Output
+from slither.core.cfg.node import Node
+from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
+from slither.core.declarations import Function
+from slither.slithir.operations.event_call import EventCall
+from slither.analyses.data_dependency.data_dependency import is_dependent
+from slither.slithir.operations import SolidityCall
+from slither.core.declarations.solidity_variables import (
+    SolidityVariableComposed,
+    SolidityFunction,
+)
+
+from ...consts import ARBITRUM_KEY
+
+
+class ArbitrumBlockNumberTimestamp(AbstractDetector):
+    """
+    Sees if `block.number` or `block.timtestamp` is used inside an Arbitrum contract
+    """
+
+    ARGUMENT = "pess-arb-block-number-timestamp"  # slither will launch the detector with slither.py --detect mydetector
+    HELP = "`block.number` or `block.timtestamp` is used inside an Arbitrum contract"
+    IMPACT = DetectorClassification.LOW
+    CONFIDENCE = DetectorClassification.HIGH
+
+    WIKI = "https://github.com/pessimistic-io/slitherin/blob/master/docs/arb_block_number_timestamp.md"
+    WIKI_TITLE = (
+        "Usage of `block.number` or `block.timtestamp` inside the Arbitrum contract"
+    )
+    WIKI_DESCRIPTION = "# Arbitrum block.number/block.timestamp"
+    WIKI_EXPLOIT_SCENARIO = "N/A"
+    WIKI_RECOMMENDATION = "Look at docs for details"
+
+    def _find_randao_or_difficulty(self, f: Function) -> List[Node]:
+        ret = set()
+        for node in f.nodes:
+            for var in node.variables_read:
+                if is_dependent(
+                    var, SolidityVariableComposed("block.number"), node
+                ) or is_dependent(
+                    var, SolidityVariableComposed("block.timestamp"), node
+                ):
+                    ret.add(node)
+            for ir in node.irs:
+                if (
+                    isinstance(ir, SolidityCall)
+                    and ir.function == SolidityFunction("number()")
+                    or isinstance(ir, SolidityCall)
+                    and ir.function == SolidityFunction("timestamp()")
+                ):
+                    ret.add(node)
+        return list(ret)
+
+    def _detect(self) -> List[Output]:
+        """Main function"""
+        results = []
+
+        if os.getenv(ARBITRUM_KEY) is None:
+            return results
+
+        for contract in self.compilation_unit.contracts_derived:
+            for f in contract.functions_and_modifiers:
+                nodes = self._find_randao_or_difficulty(f)
+                if nodes:
+                    info = [
+                        f,
+                        " function uses block.number/block.timestamp inside the code of the Arbitrum contract\n"
+                        "They behave different than on Ethereum, for details: (https://docs.arbitrum.io/for-devs/concepts/differences-between-arbitrum-ethereum/block-numbers-and-time)\n",
+                        "Verify, that contract's logic does not break because of these differences\n"
+                        "\t Usages:\n",
+                    ]
+
+                    nodes.sort(key=lambda x: x.node_id)
+
+                    for node in nodes:
+                        info += ["\t- ", node, "\n"]
+
+                    results.append(self.generate_result(info))
+        return results
diff --git a/slitherin/detectors/arbitrum/solidity_version.py b/slitherin/detectors/arbitrum/solidity_version.py
new file mode 100644
index 0000000..34a2def
--- /dev/null
+++ b/slitherin/detectors/arbitrum/solidity_version.py
@@ -0,0 +1,61 @@
+import os
+from typing import List
+from slither.utils.output import Output
+from slither.core.cfg.node import Node
+from slither.detectors.abstract_detector import (
+    AbstractDetector,
+    DetectorClassification,
+    make_solc_versions,
+)
+from slither.core.declarations import Function
+from slither.slithir.operations.event_call import EventCall
+from slither.analyses.data_dependency.data_dependency import is_dependent
+from slither.slithir.operations import SolidityCall
+from slither.core.declarations.solidity_variables import (
+    SolidityVariableComposed,
+    SolidityFunction,
+)
+
+from ...consts import ARBITRUM_KEY
+
+
+class ArbitrumSolidityVersion(AbstractDetector):
+    """
+    Checks that sol version >= 0.8.20 is not used inside an Arbitrum contract
+    """
+
+    ARGUMENT = "pess-arb-solidity-version"  # slither will launch the detector with slither.py --detect mydetector
+    HELP = "sol version >= 0.8.20 is used in contract that will be deployed to Arbitrum (Potential usage of PUSH0, which is not supported)"
+    IMPACT = DetectorClassification.HIGH
+    CONFIDENCE = DetectorClassification.LOW
+
+    WIKI = "https://github.com/pessimistic-io/slitherin/blob/master/docs/arb_difficulty_randao.md"
+    WIKI_TITLE = "Arbitrum solidity version"
+    WIKI_DESCRIPTION = "Potential usage of PUSH0 opcode"
+    WIKI_EXPLOIT_SCENARIO = "N/A"
+    WIKI_RECOMMENDATION = (
+        "Either, use versions 0.8.19 and below, or EVM versions below shanghai"
+    )
+
+    def _detect(self) -> List[Output]:
+        """Main function"""
+        results = []
+
+        if os.getenv(ARBITRUM_KEY) is None:
+            return results
+
+        if (
+            self.compilation_unit.is_solidity
+            and self.compilation_unit.solc_version in make_solc_versions(8, 20, 99)
+        ):
+            results.append(
+                self.generate_result(
+                    [
+                        "Potential usage of solidity version >= 0.8.20 detected. Solidity in these versions will utilize PUSH0 opcode, ",
+                        "which is not supported on Arbitrum. ",
+                        "Either, use versions 0.8.19 and below, or EVM versions below shanghai",
+                    ]
+                )
+            )
+
+        return results
diff --git a/slitherin/detectors/call_forward_to_protected.py b/slitherin/detectors/call_forward_to_protected.py
index 93ab5e5..1464c45 100644
--- a/slitherin/detectors/call_forward_to_protected.py
+++ b/slitherin/detectors/call_forward_to_protected.py
@@ -2,9 +2,23 @@
 from slither.slithir.operations import LowLevelCall
 from slither.core.declarations import Function
 from slither.utils.output import Output
+from pathlib import Path
 from typing import List
 
+OZ_EXCLUDED_CONTRACTS_SUFFIXES: List[str] = [
+    "contracts/utils/Address.sol",
+    "contracts-upgradeable/utils/AddressUpgradeable.sol",
+    "contracts/utils/AddressUpgradeable.sol",
+    "contracts/token/ERC20/utils/SafeERC20.sol"
+]
 
+UNISWAP_EXCLUDED_CONTRACTS_SUFFIXES: List[str] = [
+    "lib/contracts/libraries/TransferHelper.sol"
+]
+
+BALANCER_EXCLUDED_CONTRACTS_SUFFIXES: List[str] = [
+    "v2-solidity-utils/contracts/openzeppelin/SafeERC20.sol"
+]
 class CallForwardToProtected(AbstractDetector):
     """
     Shows cases when contract calls custom addresses and has contract interactions through access control
@@ -35,10 +49,29 @@ def _detect_low_level_custom_address_call(self, fun: Function) -> bool:
                     if str(address_parameter) in str(node):
                         return True
         return False
+    
+    def _pess_is_excluded_from_detector(self, contract: "Contract") -> bool:
+        path = Path(contract.source_mapping.filename.absolute).parts
+        is_zep = "openzeppelin-solidity" in path or \
+                    ("@openzeppelin" in path and path[path.index("@openzeppelin") + 1] == "contracts") or \
+                    "openzeppelin-contracts-upgradeable" in path or \
+                    ("@openzeppelin-contracts-upgradeable" in path and \
+                        (path[path.index("@openzeppelin-contracts-upgradeable") + 1] == "contracts" or \
+                         path[path.index("@openzeppelin-contracts-upgradeable") + 1] == "contracts-upgradeable"))
+
+        if is_zep:
+            return any(map(lambda suffix: contract.source_mapping.filename.absolute.endswith(suffix), OZ_EXCLUDED_CONTRACTS_SUFFIXES)) 
+        elif "@uniswap" in path:
+            return any(map(lambda suffix: contract.source_mapping.filename.absolute.endswith(suffix), UNISWAP_EXCLUDED_CONTRACTS_SUFFIXES)) 
+        elif "@balancer-labs" in path: 
+            return any(map(lambda suffix: contract.source_mapping.filename.absolute.endswith(suffix), BALANCER_EXCLUDED_CONTRACTS_SUFFIXES)) 
+        return False
 
     def _detect(self) -> List[Output]:
         res = []
         for contract in self.compilation_unit.contracts_derived:
+            if self._pess_is_excluded_from_detector(contract):
+                continue
             for f in contract.functions:
                 x = self._detect_low_level_custom_address_call(f)
                 if x:
diff --git a/slitherin/detectors/ecrecover.py b/slitherin/detectors/ecrecover.py
index d8497e0..6d7e8fc 100644
--- a/slitherin/detectors/ecrecover.py
+++ b/slitherin/detectors/ecrecover.py
@@ -9,7 +9,7 @@
 from slither.core.declarations import (
     FunctionContract,
 )
-from slither.core.cfg.node import Node
+from slither.core.cfg.node import Node, NodeType
 from slither.slithir.operations import LowLevelCall
 from slither.analyses.data_dependency.data_dependency import is_dependent
 
@@ -47,6 +47,7 @@ def analyze_function(
                             == "ecrecover(bytes32,uint8,bytes32,bytes32)"
                         ):
                             unchecked_results.add(ir.lvalue)
+                            # print(ir.lvalue, node)
                             var_to_node[ir.lvalue] = node
                         elif (
                             ir.function.name == "require(bool,string)"
@@ -70,6 +71,18 @@ def analyze_function(
 
                 except Exception as e:
                     print("failed", e)
+        
+        for node in function.nodes:
+            if node._node_type == NodeType.RETURN:
+                node_contains_0 = re.search(
+                        r"address\((0|0x0*)\)", str(node)
+                    )  # check if the node contains address(0|0x0..)
+                if not node_contains_0:
+                    continue
+                temp_unchecked = set(unchecked_results)
+                for ur in temp_unchecked:
+                    if is_dependent(ur, ur, node):
+                        unchecked_results.remove(ur)
 
         return [var_to_node[ur] for ur in unchecked_results]
 
diff --git a/slitherin/detectors/potential_arith_overflow.py b/slitherin/detectors/potential_arith_overflow.py
new file mode 100644
index 0000000..61ce44c
--- /dev/null
+++ b/slitherin/detectors/potential_arith_overflow.py
@@ -0,0 +1,118 @@
+from typing import List
+from slither.utils.output import Output
+from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
+from slither.core.declarations import Function
+from slither.core.solidity_types.elementary_type import Int, Uint
+from  slither.core.variables.local_variable import LocalVariable
+import slither.slithir.operations as ops
+import slither.slithir.variables as vrs
+from slither.core.cfg.node import NodeType
+import typing as tp
+
+INT_TYPES = [*Int, *Uint]
+
+class PotentialArithmOverflow(AbstractDetector):
+    """
+    Detects expressions where overflow may occur, but no overflow is expected
+    """
+
+    ARGUMENT = "pess-potential-arithmetic-overflow"  # slither will launch the detector with slither.py --detect mydetector
+    HELP = "Add explicit type casting in temporary expressions during the evaluation of arithmetic expressions in case the final type is larger than the intermediate one."
+    IMPACT = DetectorClassification.MEDIUM
+    CONFIDENCE = DetectorClassification.MEDIUM
+
+    WIKI = (
+        "https://github.com/pessimistic-io/slitherin/blob/master/docs/potential_arith_overflow.md"
+    )
+    WIKI_TITLE = "Potential Arithmetic Overflow"
+    WIKI_DESCRIPTION = "The detector sees if there are assignments/returns that calculate some arithmetic expressions and if some intermediate calculations contain a type that is lower than the expected result. Such behavior may lead to unexpected overflow/underflow, e.g., trying to assign the multiplication of two `uint48` variables to `uint256` would look like `uint48 * uint48` and it may overflow (however, the final type would fit such multiplication)."
+    WIKI_EXPLOIT_SCENARIO = "A transaction will revert with overflow, but the expected behavior that it should not."
+    WIKI_RECOMMENDATION = "Use explicit type casting in sub expressions when the assigment to a larger type is performed."
+    
+    def _has_op_overflowing_sub_expression(self, op: ops.Operation, high_level_bits_needed: tp.Optional[int]) -> (bool, list, str):
+        if isinstance(op, ops.Unary):
+            (is_err, nodes, sub_expr) = self._has_op_overflowing_sub_expression(op.rvalue, high_level_bits_needed) # ! and ~ do not affect result
+            return (is_err, nodes, f"{op.type_str} {sub_expr}")
+        elif isinstance(op, ops.Binary):
+            result_type = str(op.lvalue.type)
+            [l_check_res, l_check_list, l_sub_expr] = self._has_op_overflowing_sub_expression(op.variable_left, high_level_bits_needed)
+            [r_check_res, r_check_list, r_sub_expr] = self._has_op_overflowing_sub_expression(op.variable_right, high_level_bits_needed)
+                
+            cur_expr = f"{l_sub_expr} {op._type.value} {r_sub_expr}"
+            if op._type is ops.BinaryType.ADDITION or op._type is ops.BinaryType.MULTIPLICATION or op._type is ops.BinaryType.POWER:
+                result_bits_cut = result_type.removeprefix("uint").removeprefix("int")
+                result_bits = int(256 if not result_bits_cut else result_bits_cut)
+                if high_level_bits_needed and high_level_bits_needed > result_bits: # result expects bigger type, but expression returns lower and overflow may occur here
+                    return (True, [(cur_expr, result_type), *l_check_list, *r_check_list], cur_expr)
+                else:
+                    return (l_check_res or r_check_res, [*l_check_list, *r_check_list], cur_expr)
+            else: # @todo currently we do not check other operations, return as everything is ok
+                return (l_check_res or r_check_res, [*l_check_list, *r_check_list], cur_expr)
+        elif isinstance(op, ops.OperationWithLValue):
+            return (False, [], f"{op.lvalue}")
+        elif isinstance(op, vrs.Constant):
+            return (False, [], f"{op}")
+        elif isinstance(op, LocalVariable):
+            return (False, [], f"{op}")
+        elif isinstance(op, vrs.state_variable.StateVariable):
+            return (False, [], f"{op}")
+        elif isinstance(op, vrs.TemporaryVariable):
+            return (False, [], f"...")
+        elif isinstance(op, ops.Return):
+            return (False, [], f"{op}")
+        elif isinstance(op, vrs.ReferenceVariable):
+            return (False, [], f"{op}")
+        else:
+            return (False, [], f"{op}")
+
+    def _find_vulnerable_expressions(self, fun: Function) -> list:
+        final_results = []
+        is_active_assembly = False
+        for node in fun.nodes:
+            if node.type is NodeType.ASSEMBLY:
+                is_active_assembly = True
+            elif node.type is NodeType.ENDASSEMBLY:
+                is_active_assembly = False
+            if (node.type is NodeType.VARIABLE or node.type is NodeType.EXPRESSION or node.type is NodeType.RETURN) and not is_active_assembly:
+                irs = node.irs
+                if len(irs) > 0 and isinstance(irs[-1], ops.Assignment) and str(irs[-1].lvalue._type) in INT_TYPES:
+                    expected_bits_cut = str(irs[-1].lvalue._type).removeprefix("uint").removeprefix("int")
+                    expected_bits = int(256 if not expected_bits_cut else expected_bits_cut)
+                    has_problems = False
+                    errors = []
+                    for x in irs[:-1]:
+                        (response_res, response_errors, _) = self._has_op_overflowing_sub_expression(x, expected_bits)
+                        has_problems |= response_res
+                        errors.extend(response_errors)
+                    if has_problems:
+                        final_results.append((node, str(irs[-1].lvalue._type), errors))
+                if len(irs) > 0 and isinstance(irs[-1], ops.Return) and len(fun.return_type) == 1 and str(fun.return_type[0]) in INT_TYPES: # @todo currently works only with single returns
+                    expected_bits_cut = str(fun.return_type[0]).removeprefix("uint").removeprefix("int")
+                    expected_bits = int(256 if not expected_bits_cut else expected_bits_cut)
+                    has_problems = False
+                    errors = []
+                    for x in irs[:-1]:
+                        (response_res, response_errors, _) = self._has_op_overflowing_sub_expression(x, expected_bits)
+                        has_problems |= response_res
+                        errors.extend(response_errors)
+                    if has_problems:
+                        final_results.append((node, str(fun.return_type[0]), errors))
+
+        return final_results
+    
+    def _detect(self) -> List[Output]:
+        """Main function"""
+        res = []
+        for contract in self.compilation_unit.contracts_derived:
+            if contract.is_interface:
+                continue
+            for f in contract.functions_and_modifiers_declared:
+                vulnerable_expressions = self._find_vulnerable_expressions(f)
+                if vulnerable_expressions:
+                    info = [f, f" contains integer variables whose type is larger than the type of one of its intermediate expressions. Consider casting sub expressions explicitly as they might lead to unexpected overflow:\n"]
+                    for (node, node_final_type, op_with_ret_type) in vulnerable_expressions:
+                        info += ["\tIn `", node, "` intermidiate expressions returns type of lower order:", "\n"]
+                        for (op, op_ret_type) in op_with_ret_type:
+                            info += ["\t\t`", str(op), f"` returns {op_ret_type}, but the type of the resulting expression is {node_final_type}.", "\n"]
+                    res.append(self.generate_result(info))
+        return res
diff --git a/slitherin/detectors/public_vs_external.py b/slitherin/detectors/public_vs_external.py
index 92f9905..cc08d79 100644
--- a/slitherin/detectors/public_vs_external.py
+++ b/slitherin/detectors/public_vs_external.py
@@ -29,7 +29,7 @@ def _analyze_contract(self, contract: Contract) -> list:
                     used_functions.add(call.name)
 
         for f in contract.functions_and_modifiers_declared:
-            if f.visibility == "public" and f.name not in used_functions:
+            if f.visibility == "public" and f.name not in used_functions and not f.is_constructor:
                 res.append(f)
         return res
 
diff --git a/slitherin/detectors/strange_setter.py b/slitherin/detectors/strange_setter.py
index 3920fc3..323dc9b 100644
--- a/slitherin/detectors/strange_setter.py
+++ b/slitherin/detectors/strange_setter.py
@@ -2,11 +2,12 @@
 from slither.utils.output import Output
 from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
 from slither.core.declarations import Function
+import slither.core.expressions.new_array as na
 
 
 class StrangeSetter(AbstractDetector):
     """
-    Sees if contract contains a setter, that does not change contract storage variables.
+    Sees if contract contains a setter, that does not change contract storage variables or that does not use arguments for an external call.
     """
 
     ARGUMENT = "pess-strange-setter"  # slither will launch the detector with slither.py --detect mydetector
@@ -18,7 +19,7 @@ class StrangeSetter(AbstractDetector):
         "https://github.com/pessimistic-io/slitherin/blob/master/docs/strange_setter.md"
     )
     WIKI_TITLE = "Strange Setter"
-    WIKI_DESCRIPTION = "Setter must write to storage variables"
+    WIKI_DESCRIPTION = "Setter must write to storage variables or pass arguments to external calls"
     WIKI_EXPLOIT_SCENARIO = "-"
     WIKI_RECOMMENDATION = "Make sure that your setter actually sets something"
 
@@ -30,6 +31,7 @@ def _is_strange_setter(self, fun: Function) -> bool:
         if not fun.parameters:
             # nothing is in the params, so we don't care
             return False
+        used_params = set()
         for (
             fin
         ) in fun.internal_calls:  # branch with for-loop for setters in internal calls
@@ -39,13 +41,21 @@ def _is_strange_setter(self, fun: Function) -> bool:
                         if n.state_variables_written and str(param) in str(
                             n
                         ):  # check if there's a state variable setter using function parameters
-                            return False
+                            used_params.add(param)
         for param in fun.parameters:
             if fun.state_variables_written:
                 for n in fun.nodes:
                     if str(param) in str(n):
-                        return False
-        return True
+                        used_params.add(param)
+        for param in fun.parameters:
+            for external in fun.external_calls_as_expressions:
+                if isinstance(external._called, na.NewArray):
+                    continue
+                for arg in [*external.arguments, external._called._expression]:
+                    if str(arg) == str(param):
+                        used_params.add(param)
+        intersection_len = len(set(fun.parameters) & used_params)
+        return intersection_len != len(fun.parameters)
 
     def _is_strange_constructor(self, fun: Function) -> bool:
         """Checks if constructor sets nothing"""
diff --git a/slitherin/detectors/uniswap-v3/callback.py b/slitherin/detectors/uniswap-v3/callback.py
new file mode 100644
index 0000000..4681a40
--- /dev/null
+++ b/slitherin/detectors/uniswap-v3/callback.py
@@ -0,0 +1,99 @@
+import re
+from typing import List, Tuple
+
+from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
+from slither.slithir.operations import SolidityCall, Condition
+from slither.core.declarations import (
+    FunctionContract,
+    SolidityVariableComposed,
+    Function,
+)
+from slither.core.cfg.node import Node
+from slither.slithir.operations import LowLevelCall
+from slither.analyses.data_dependency.data_dependency import is_dependent
+
+
+class AAVEFlashloanCallbackDetector(AbstractDetector):
+    """
+    Detects if the flashloan callback `executeOperation` has `initiator` and `msg.sender` validation
+    """
+
+    ARGUMENT = "pess-aave-flashloan-callback"  # slither will launch the detector with slither.py --detect mydetector
+    HELP = "see `executeOperation`callback docs"
+    IMPACT = DetectorClassification.HIGH
+    CONFIDENCE = DetectorClassification.HIGH
+
+    WIKI = "https://github.com/pessimistic-io/slitherin/blob/master/docs/aave/flashloan_callback.md"
+    WIKI_TITLE = "AAVE Flashloan callback"
+    WIKI_DESCRIPTION = "Check docs"
+    WIKI_EXPLOIT_SCENARIO = (
+        "Attacker can directly call or initiate flashloan to this address"
+    )
+    WIKI_RECOMMENDATION = "Validate the msg.sender and `initiator` argument"
+
+    def _analyze_function(
+        self,
+        function: FunctionContract,
+        args_to_verify,
+        check_recursive=False,
+        visited=[],
+    ) -> Tuple[set, set]:
+        checked = set()
+        unchecked = set(args_to_verify)
+
+        for node in function.nodes:
+            if node.is_conditional(include_loop=False):  # contains if/assert/require
+                for a in unchecked:
+                    if any([is_dependent(arg, a, node) for arg in node.variables_read]):
+                        checked.add(a)
+
+        unchecked.difference_update(checked)  # remove all checked
+
+        visited.append(function)
+        if check_recursive:
+            for node in function.nodes:
+                for internal_call in node.internal_calls:
+                    # Filter to Function, as internal_call can be a solidity call
+                    if isinstance(internal_call, Function):
+                        _checked, _unchecked = self._analyze_function(
+                            internal_call, unchecked, False, visited
+                        )  # We are going only 1 step into in recursion
+                        checked |= _checked
+                        unchecked = _unchecked
+
+        return checked, unchecked
+
+    def _detect(self):
+        results = []
+        for contract in self.compilation_unit.contracts_derived:
+            for f in contract.functions:
+                if (
+                    f.signature_str
+                    == "executeOperation(address[],uint256[],uint256[],address,bytes) returns(bool)"
+                ):
+                    _, unchecked = self._analyze_function(
+                        f,
+                        [
+                            SolidityVariableComposed("msg.sender"),
+                            f.parameters[3],  # initiator parameter
+                        ],
+                        check_recursive=True,
+                    )
+                    if unchecked:
+                        info = [
+                            "Unchecked function parameters in AAVE callback: ",
+                            f,
+                            "\n",
+                        ]
+                        for var in unchecked:
+                            if var == SolidityVariableComposed("msg.sender"):
+                                info += ["\t", "'msg.sender' is not checked", "\n"]
+                            else:
+                                info += ["\t'", var.name, "' is not checked\n"]
+
+                        tres = self.generate_result(info)
+                        # for var in unchecked:
+                        #     tres.add(var)
+                        # tres.add(f)
+                        results.append(tres)
+        return results
diff --git a/slitherin/detectors/unprotected_initialize.py b/slitherin/detectors/unprotected_initialize.py
index 6e4d55f..4adcd69 100644
--- a/slitherin/detectors/unprotected_initialize.py
+++ b/slitherin/detectors/unprotected_initialize.py
@@ -43,6 +43,14 @@ def _has_require(self, fun: Function) -> bool:
                         if str(variable.type) == "address":
                             return True
         return False
+    
+    def _has_if_with_reverts(self, fun: Function) -> bool:
+        for node in fun.nodes:
+            if node.contains_if():
+                for child in node.sons:
+                    if "revert()" in str(child) or "revert(string)" in str(child):
+                        return True
+        return False
 
     def _detect(self) -> List[Output]:
         """Main function"""
@@ -55,7 +63,8 @@ def _detect(self) -> List[Output]:
                 if x:
                     is_safe = self._has_modifiers(f)
                     is_safe2 = self._has_require(f)
-                    if not is_safe and not is_safe2:
+                    is_safe3 = self._has_if_with_reverts(f)
+                    if not is_safe and not is_safe2 and not is_safe3:
                         res.append(
                             self.generate_result(
                                 [


Test patch:
diff --git a/tests/arbitrum_block_number_timestamp_test.sol b/tests/arbitrum_block_number_timestamp_test.sol
new file mode 100644
index 0000000..a580696
--- /dev/null
+++ b/tests/arbitrum_block_number_timestamp_test.sol
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: SEE LICENSE IN LICENSE
+pragma solidity ^0.8.0;
+
+contract Test {
+    function vulnerableArbNumber() external view returns (uint256 number) {
+        number = block.number;
+    }
+
+    function vulnerableArbTimestamp()
+        external
+        view
+        returns (uint256 difficulty)
+    {
+        difficulty = block.timestamp;
+    }
+
+    function vulnerableArbNumberTimestamp()
+        external
+        view
+        returns (uint256 number, uint256 timestamp)
+    {
+        number = block.number;
+        timestamp = block.timestamp;
+    }
+
+    function vulnerableArbNumberTimestampYul()
+        external
+        view
+        returns (uint256 bnumber, uint256 btimestamp)
+    {
+        assembly {
+            bnumber := number()
+            btimestamp := timestamp()
+        }
+    }
+}
diff --git a/tests/arbitrum_prevrandao_difficulty_test.sol b/tests/arbitrum_prevrandao_difficulty_test.sol
new file mode 100644
index 0000000..2ca5b9d
--- /dev/null
+++ b/tests/arbitrum_prevrandao_difficulty_test.sol
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: SEE LICENSE IN LICENSE
+pragma solidity ^0.8.0;
+contract Test {
+    function vulnerableArbPrevrandao() external view returns (uint256 prevRandao) {
+        prevRandao = block.prevrandao;
+    }
+
+    function vulnerableArbDifficulty() external view returns (uint256 difficulty) {
+        difficulty = block.difficulty;
+    }
+
+    function vulnerableArbPrevrandaoAndDifficulty() external view returns (uint256 prevRandao, uint256 difficulty) {
+        prevRandao = block.prevrandao;
+        difficulty = block.difficulty;
+    }
+
+    function vulnerableArbPrevrandaoYul() external view returns (uint256 prevRandao) {
+        assembly {
+            prevRandao := prevrandao()
+        }
+    }
+
+    function vulnerableAllWithYul() external view returns (uint256 prevRandao1, uint256 diff1, uint256 prevRandao2) {
+        prevRandao1 = block.prevrandao;
+        diff1 = block.difficulty;
+        assembly {
+            prevRandao2 := prevrandao()
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/arbitrum_solidity_version_test.sol b/tests/arbitrum_solidity_version_test.sol
new file mode 100644
index 0000000..fbe7da6
--- /dev/null
+++ b/tests/arbitrum_solidity_version_test.sol
@@ -0,0 +1,4 @@
+// SPDX-License-Identifier: SEE LICENSE IN LICENSE
+pragma solidity 0.8.21;
+
+contract Test {}
diff --git a/tests/call_forward_to_protected_test.sol b/tests/call_forward_to_protected_test.sol
index dd7af8f..8ab4da3 100644
--- a/tests/call_forward_to_protected_test.sol
+++ b/tests/call_forward_to_protected_test.sol
@@ -1,5 +1,8 @@
 pragma solidity ^0.8.0;
 
+import "../node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
+import "../node_modules/@openzeppelin/contracts/utils/Address.sol";
+
 contract call_forward_to_protected_test {
 
     function set_vulnurable(address customAddress) public returns (bool){
@@ -12,3 +15,13 @@ contract call_forward_to_protected_test {
         return success;
     }
 }
+
+contract OZCallForwardToProtectedTest {
+    function safeERC20ShouldBeIgnored(IERC20 target, address to, uint256 value) external {
+        SafeERC20.safeTransfer(target, to, value);
+    }
+
+    function transferHelperShouldBeIgnored(address payable to, uint256 value) external {
+        Address.sendValue(to, value);
+    }
+}
diff --git a/tests/ecrecover.sol b/tests/ecrecover.sol
index 10a5156..c5ecca2 100644
--- a/tests/ecrecover.sol
+++ b/tests/ecrecover.sol
@@ -30,4 +30,19 @@ contract EcrecoverTest {
             revert("smth");
         }
     }
+    
+    function ecrecover_negative4() external returns (bool) {
+        address signer = ecrecover("", 0, 0, 0);
+        return signer == address(0);
+    }
+
+    // @todo Result is checked inside `ecrecover_negative5_part2` but the value from internal call is unused
+    function ecrecover_negative5_part1() external {
+        bool unusedResult = ecrecover_negative5_part2();
+    }
+
+    function ecrecover_negative5_part2() internal returns (bool) {
+        address signer = ecrecover("", 0, 0, 0);
+        return signer == address(0);
+    }
 }
diff --git a/tests/potential_arith_overflow.sol b/tests/potential_arith_overflow.sol
new file mode 100644
index 0000000..af4b8c4
--- /dev/null
+++ b/tests/potential_arith_overflow.sol
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: SEE LICENSE IN LICENSE
+pragma solidity ^0.8.0;
+
+contract Test {
+    uint16 t;
+
+    struct TStruct {
+        uint160 xx;
+    }
+
+    function decimals16() public returns (uint16) {
+        return 10 ** 4;
+    }
+
+    /*
+        `a` has type `uint64`
+        `decimals16()` returns `uint16`
+        `x` has type `uint256`
+        => Printing warning because mul of uint64 and uint16 may overflow (but casting `a` or the result of `decimals16()` safes from it). 
+    */
+    function test_vuln1(uint64 a) external {
+        uint256 x = a * decimals16();
+    }
+
+    function test_vuln2() external returns (uint256 result) {
+        uint48 x = 10;
+        uint48 y = type(uint48).max;
+        uint k = 1;
+        result = x * y / k;
+    }
+
+    function test_vuln2_wrong_fix() external returns (uint256 result) {
+        uint48 x = 10;
+        uint48 y = type(uint48).max;
+        uint k = 1;
+        result = uint256(x * y) / k;
+    }
+
+    function test_vuln3() external returns (uint256 result) {
+        uint48 x;
+        uint32 y;
+        result = x + y;
+    }
+
+    function test_vuln4() external returns (uint256 result) {
+        uint8 x = 10;
+        uint8 y = 20;
+        result = x ** y;
+    }
+
+    function test_vuln5() external returns (uint256) { // @todo handle expression in return statements
+        uint8 x = 10;
+        uint8 y = 20;
+        return x ** y;
+    }
+
+    function test_vuln6() external returns (uint256 result) { 
+        uint8 x = 10;
+        result =  t * x;
+    }
+
+    function test_vuln7() external returns (int64 result) { 
+        int8 x = 10;
+        int32 y;
+        result =  y * x + 1;
+    }
+
+    function test_vuln8() external returns (int64 result) { 
+        int8 x = 10;
+        int32 y;
+        result =  y * x + 1;
+
+        result = x * y - 10 + 100;
+
+        result = x * y * x * y;
+    }
+
+    function test_vuln9() external returns (uint64, uint128) {  // @todo currently we do not handle tuple returns
+        uint32 a;
+        uint32 b;
+        return (a + b, uint128(a) + b);
+    }
+
+    function test_vuln10() external returns (uint64) { 
+        uint32 a;
+        uint32 b;
+        return a + b;
+    }
+
+    function test_vuln11() external { 
+        TStruct memory s = TStruct({xx: 120});
+        uint16 a;
+        uint32 b;
+        s.xx = a + b;
+    }
+
+    function test_ok1() external returns (uint128 result) {
+        uint128 a;
+        uint64 b;
+        result = a * b;
+    }
+
+    function test_ok2() external returns (uint128) { // @todo handle expression in return statements
+        uint128 a;
+        uint64 b;
+        return a * b;
+    }
+
+    function test_ok3() external returns (uint128 result) {
+        uint128 a;
+        uint64 b;
+        uint32 c;
+        uint16 d;
+        result =  (a * b) / c + d;
+    }
+
+    function test_fix_vuln1_ok4(uint64 a) external {
+        uint256 x = uint256(a) * decimals16();
+    }
+
+    function test_fix_vuln1_slither_bug(uint64 a) external { // @note for some reason slither invokes type incorrectly in this case. So fix need to be applied on the left side of binary expression
+        uint256 x = a * uint256(decimals16());
+    }
+
+    function test_should_not_trigger(uint64 a) external { // @note for some reason slither
+        bytes32 a = bytes32(uint256(1));
+        address x = address(uint160(1) + uint160(2));
+    }
+}
\ No newline at end of file
diff --git a/tests/public_vs_external_test.sol b/tests/public_vs_external_test.sol
index cb2e9f9..e767169 100644
--- a/tests/public_vs_external_test.sol
+++ b/tests/public_vs_external_test.sol
@@ -36,3 +36,15 @@ contract MinimalTest2 {
         uint256 k = fucntion_public_negative3();
     }
 }
+
+contract ConstructorTest {
+    constructor() {
+
+    }
+}
+
+contract ConstructorTest2 {
+    constructor(uint256 a, uint8 b) {
+        
+    }
+}
diff --git a/tests/strange_setter_test.sol b/tests/strange_setter_test.sol
index 2c97cc1..d2475c3 100644
--- a/tests/strange_setter_test.sol
+++ b/tests/strange_setter_test.sol
@@ -1,5 +1,8 @@
 pragma solidity ^0.8.0;
 
+interface ExternalContract {
+    function set(bool arg) external;
+}
 // What it should detect:
 // If smth is set in the function, and the function contains parameters,
 // and this parameters were not uset to set.
@@ -37,10 +40,18 @@ contract StrangeSetter {
     }
 
     function setWithInt(bytes32 nameHash, address builder) public onlyOwner {
-        uint256 x = 0; //TODO this is not detected. There are params which are not used
+        uint256 x = 0;
         vulnurable_internal(x);
     }
 
+    function setSwapEnabledExternal_ok(ExternalContract target, bool swapEnabled) external onlyOwner {
+        target.set(swapEnabled);
+    }
+
+    function setUseOnlyOneArg_vulnerable(uint256 arg1, bool isProtectedArg) external onlyOwner {
+        isProtected = isProtectedArg;
+    }
+
     function set_ok(uint256 setter) public onlyOwner {
         toSet = setter;
     }
diff --git a/tests/unprotected_initialize_test.sol b/tests/unprotected_initialize_test.sol
index edb753f..662f235 100644
--- a/tests/unprotected_initialize_test.sol
+++ b/tests/unprotected_initialize_test.sol
@@ -1,3 +1,4 @@
+//SPDX-License-Identifier: UNLICENSED
 pragma solidity ^0.8.0;
 
 interface I {
@@ -43,4 +44,11 @@ contract unprotected_initialize {
     function init_vuln(uint256 setter) external {
         toSet = setter;
     }
+
+    function init_ok3(uint256 setter) external {
+        if (msg.sender != owner) {
+            revert("Not owner");
+        }
+        toSet = setter;
+    }
 }