We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[Bug]: Slither detects already triaged results
### Describe the issue:

Slither seems to be detecting issues that have already been triaged into the `slither.db.json` from time to time.
This makes it difficult to get consistent results between runs after triaging findings, and also generates false positives when running Slither in CI.

This could be related to the ID of the findings that is stored in the DB file, which seems to vary between runs.

### Code example to reproduce the issue:

Public sample to reproduce the issue with instructions here: https://github.com/balancer/balancer-v2-monorepo/pull/2514.

### Version:

`0.9.3`

### Relevant log output:

```shell
# Following the instructions in the PR mentioned above, this is the output after triaging all the results.
# Running slither two times in a row outputs different results:

juan@M16:~/prj/bal/pkg/solidity-utils$ yarn slither
Nothing to compile
INFO:Detectors:
LogExpMath.exp(int256) (contracts/math/LogExpMath.sol#146-281) performs a multiplication on the result of a division:
	- product = (product * a3) / ONE_20 (contracts/math/LogExpMath.sol#198)
	- product = (product * a4) / ONE_20 (contracts/math/LogExpMath.sol#202)
LogExpMath.exp(int256) (contracts/math/LogExpMath.sol#146-281) performs a multiplication on the result of a division:
	- product = (product * a3) / ONE_20 (contracts/math/LogExpMath.sol#198)
	- product = (product * a5) / ONE_20 (contracts/math/LogExpMath.sol#206)
LogExpMath.exp(int256) (contracts/math/LogExpMath.sol#146-281) performs a multiplication on the result of a division:
	- product = (product * a7) / ONE_20 (contracts/math/LogExpMath.sol#214)
	- product = (product * a8) / ONE_20 (contracts/math/LogExpMath.sol#218)
LogExpMath._ln(int256) (contracts/math/LogExpMath.sol#326-458) performs a multiplication on the result of a division:
	- a = (a * ONE_20) / a3 (contracts/math/LogExpMath.sol#372)
	- a = (a * ONE_20) / a4 (contracts/math/LogExpMath.sol#377)
LogExpMath._ln(int256) (contracts/math/LogExpMath.sol#326-458) performs a multiplication on the result of a division:
	- a = (a * ONE_20) / a5 (contracts/math/LogExpMath.sol#382)
	- a = (a * ONE_20) / a6 (contracts/math/LogExpMath.sol#387)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#divide-before-multiply
INFO:Slither:. analyzed (72 contracts with 85 detectors), 5 result(s) found


juan@M16:~/prj/bal/pkg/solidity-utils$ yarn slither
Nothing to compile
INFO:Detectors:
LogExpMath.exp(int256) (contracts/math/LogExpMath.sol#146-281) performs a multiplication on the result of a division:
	- product = (product * a3) / ONE_20 (contracts/math/LogExpMath.sol#198)
	- product = (product * a5) / ONE_20 (contracts/math/LogExpMath.sol#206)
LogExpMath.exp(int256) (contracts/math/LogExpMath.sol#146-281) performs a multiplication on the result of a division:
	- product = (product * a4) / ONE_20 (contracts/math/LogExpMath.sol#202)
	- product = (product * a6) / ONE_20 (contracts/math/LogExpMath.sol#210)
LogExpMath.exp(int256) (contracts/math/LogExpMath.sol#146-281) performs a multiplication on the result of a division:
	- product = (product * a7) / ONE_20 (contracts/math/LogExpMath.sol#214)
	- product = (product * a8) / ONE_20 (contracts/math/LogExpMath.sol#218)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#divide-before-multiply
INFO:Slither:. analyzed (72 contracts with 85 detectors), 3 result(s) found
juan@M16:~/prj/bal/pkg/solidity-utils$
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `slither/detectors/statements/divide_before_multiply.py`
Content:
```
1 """
2 Module detecting possible loss of precision due to divide before multiple
3 """
4 from collections import defaultdict
5 from typing import DefaultDict, List, Set, Tuple
6 
7 from slither.core.cfg.node import Node
8 from slither.core.declarations.contract import Contract
9 from slither.core.declarations.function_contract import FunctionContract
10 from slither.detectors.abstract_detector import (
11     AbstractDetector,
12     DetectorClassification,
13     DETECTOR_INFO,
14 )
15 from slither.slithir.operations import Binary, Assignment, BinaryType, LibraryCall, Operation
16 from slither.slithir.utils.utils import LVALUE
17 from slither.slithir.variables import Constant
18 from slither.utils.output import Output
19 
20 
21 def is_division(ir: Operation) -> bool:
22     if isinstance(ir, Binary):
23         if ir.type == BinaryType.DIVISION:
24             return True
25 
26     if isinstance(ir, LibraryCall):
27         if ir.function.name and ir.function.name.lower() in [
28             "div",
29             "safediv",
30         ]:
31             if len(ir.arguments) == 2:
32                 if ir.lvalue:
33                     return True
34     return False
35 
36 
37 def is_multiplication(ir: Operation) -> bool:
38     if isinstance(ir, Binary):
39         if ir.type == BinaryType.MULTIPLICATION:
40             return True
41 
42     if isinstance(ir, LibraryCall):
43         if ir.function.name and ir.function.name.lower() in [
44             "mul",
45             "safemul",
46         ]:
47             if len(ir.arguments) == 2:
48                 if ir.lvalue:
49                     return True
50     return False
51 
52 
53 def is_assert(node: Node) -> bool:
54     if node.contains_require_or_assert():
55         return True
56     # Old Solidity code where using an internal 'assert(bool)' function
57     # While we dont check that this function is correct, we assume it is
58     # To avoid too many FP
59     if "assert(bool)" in [c.full_name for c in node.internal_calls]:
60         return True
61     return False
62 
63 
64 # pylint: disable=too-many-branches
65 def _explore(
66     to_explore: Set[Node], f_results: List[List[Node]], divisions: DefaultDict[LVALUE, List[Node]]
67 ) -> None:
68     explored = set()
69     while to_explore:  # pylint: disable=too-many-nested-blocks
70         node = to_explore.pop()
71 
72         if node in explored:
73             continue
74         explored.add(node)
75 
76         equality_found = False
77         # List of nodes related to one bug instance
78         node_results: List[Node] = []
79 
80         for ir in node.irs:
81             if isinstance(ir, Assignment):
82                 if ir.rvalue in divisions:
83                     # Avoid dupplicate. We dont use set so we keep the order of the nodes
84                     if node not in divisions[ir.rvalue]:  # type: ignore
85                         divisions[ir.lvalue] = divisions[ir.rvalue] + [node]  # type: ignore
86                     else:
87                         divisions[ir.lvalue] = divisions[ir.rvalue]  # type: ignore
88 
89             if is_division(ir):
90                 divisions[ir.lvalue] = [node]  # type: ignore
91 
92             if is_multiplication(ir):
93                 mul_arguments = ir.read if isinstance(ir, Binary) else ir.arguments  # type: ignore
94                 nodes = []
95                 for r in mul_arguments:
96                     if not isinstance(r, Constant) and (r in divisions):
97                         # Dont add node already present to avoid dupplicate
98                         # We dont use set to keep the order of the nodes
99                         if node in divisions[r]:
100                             nodes += [n for n in divisions[r] if n not in nodes]
101                         else:
102                             nodes += [n for n in divisions[r] + [node] if n not in nodes]
103                 if nodes:
104                     node_results = nodes
105 
106             if isinstance(ir, Binary) and ir.type == BinaryType.EQUAL:
107                 equality_found = True
108 
109         if node_results:
110             # We do not track the case where the multiplication is done in a require() or assert()
111             # Which also contains a ==, to prevent FP due to the form
112             # assert(a == b * c + a % b)
113             if not (is_assert(node) and equality_found):
114                 f_results.append(node_results)
115 
116         for son in node.sons:
117             to_explore.add(son)
118 
119 
120 def detect_divide_before_multiply(
121     contract: Contract,
122 ) -> List[Tuple[FunctionContract, List[Node]]]:
123     """
124     Detects and returns all nodes with multiplications of division results.
125     :param contract: Contract to detect assignment within.
126     :return: A list of nodes with multiplications of divisions.
127     """
128 
129     # Create our result set.
130     # List of tuple (function -> list(list(nodes)))
131     # Each list(nodes) of the list is one bug instances
132     # Each node in the list(nodes) is involved in the bug
133     results: List[Tuple[FunctionContract, List[Node]]] = []
134 
135     # Loop for each function and modifier.
136     for function in contract.functions_declared:
137         if not function.entry_point:
138             continue
139 
140         # List of list(nodes)
141         # Each list(nodes) is one bug instances
142         f_results: List[List[Node]] = []
143 
144         # lvalue -> node
145         # track all the division results (and the assignment of the division results)
146         divisions: DefaultDict[LVALUE, List[Node]] = defaultdict(list)
147 
148         _explore({function.entry_point}, f_results, divisions)
149 
150         for f_result in f_results:
151             results.append((function, f_result))
152 
153     # Return the resulting set of nodes with divisions before multiplications
154     return results
155 
156 
157 class DivideBeforeMultiply(AbstractDetector):
158     """
159     Divide before multiply
160     """
161 
162     ARGUMENT = "divide-before-multiply"
163     HELP = "Imprecise arithmetic operations order"
164     IMPACT = DetectorClassification.MEDIUM
165     CONFIDENCE = DetectorClassification.MEDIUM
166 
167     WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#divide-before-multiply"
168 
169     WIKI_TITLE = "Divide before multiply"
170     WIKI_DESCRIPTION = """Solidity's integer division truncates. Thus, performing division before multiplication can lead to precision loss."""
171 
172     # region wiki_exploit_scenario
173     WIKI_EXPLOIT_SCENARIO = """
174 ```solidity
175 contract A {
176 	function f(uint n) public {
177         coins = (oldSupply / n) * interest;
178     }
179 }
180 ```
181 If `n` is greater than `oldSupply`, `coins` will be zero. For example, with `oldSupply = 5; n = 10, interest = 2`, coins will be zero.  
182 If `(oldSupply * interest / n)` was used, `coins` would have been `1`.   
183 In general, it's usually a good idea to re-arrange arithmetic to perform multiplication before division, unless the limit of a smaller type makes this dangerous."""
184     # endregion wiki_exploit_scenario
185 
186     WIKI_RECOMMENDATION = """Consider ordering multiplication before division."""
187 
188     def _detect(self) -> List[Output]:
189         """
190         Detect divisions before multiplications
191         """
192         results = []
193         for contract in self.contracts:
194             divisions_before_multiplications = detect_divide_before_multiply(contract)
195             if divisions_before_multiplications:
196                 for (func, nodes) in divisions_before_multiplications:
197 
198                     info: DETECTOR_INFO = [
199                         func,
200                         " performs a multiplication on the result of a division:\n",
201                     ]
202 
203                     # sort the nodes to get deterministic results
204                     nodes.sort(key=lambda x: x.node_id)
205 
206                     for node in nodes:
207                         info += ["\t- ", node, "\n"]
208 
209                     res = self.generate_result(info)
210                     results.append(res)
211 
212         return results
213
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/slither/detectors/statements/divide_before_multiply.py b/slither/detectors/statements/divide_before_multiply.py
--- a/slither/detectors/statements/divide_before_multiply.py
+++ b/slither/detectors/statements/divide_before_multiply.py
@@ -2,7 +2,7 @@
 Module detecting possible loss of precision due to divide before multiple
 """
 from collections import defaultdict
-from typing import DefaultDict, List, Set, Tuple
+from typing import DefaultDict, List, Tuple
 
 from slither.core.cfg.node import Node
 from slither.core.declarations.contract import Contract
@@ -63,7 +63,7 @@
 
 # pylint: disable=too-many-branches
 def _explore(
-    to_explore: Set[Node], f_results: List[List[Node]], divisions: DefaultDict[LVALUE, List[Node]]
+    to_explore: List[Node], f_results: List[List[Node]], divisions: DefaultDict[LVALUE, List[Node]]
 ) -> None:
     explored = set()
     while to_explore:  # pylint: disable=too-many-nested-blocks
@@ -114,7 +114,7 @@
                 f_results.append(node_results)
 
         for son in node.sons:
-            to_explore.add(son)
+            to_explore.append(son)
 
 
 def detect_divide_before_multiply(
@@ -145,7 +145,7 @@
         # track all the division results (and the assignment of the division results)
         divisions: DefaultDict[LVALUE, List[Node]] = defaultdict(list)
 
-        _explore({function.entry_point}, f_results, divisions)
+        _explore([function.entry_point], f_results, divisions)
 
         for f_result in f_results:
             results.append((function, f_result))

{"golden_diff": "diff --git a/slither/detectors/statements/divide_before_multiply.py b/slither/detectors/statements/divide_before_multiply.py\n--- a/slither/detectors/statements/divide_before_multiply.py\n+++ b/slither/detectors/statements/divide_before_multiply.py\n@@ -2,7 +2,7 @@\n Module detecting possible loss of precision due to divide before multiple\n \"\"\"\n from collections import defaultdict\n-from typing import DefaultDict, List, Set, Tuple\n+from typing import DefaultDict, List, Tuple\n \n from slither.core.cfg.node import Node\n from slither.core.declarations.contract import Contract\n@@ -63,7 +63,7 @@\n \n # pylint: disable=too-many-branches\n def _explore(\n-    to_explore: Set[Node], f_results: List[List[Node]], divisions: DefaultDict[LVALUE, List[Node]]\n+    to_explore: List[Node], f_results: List[List[Node]], divisions: DefaultDict[LVALUE, List[Node]]\n ) -> None:\n     explored = set()\n     while to_explore:  # pylint: disable=too-many-nested-blocks\n@@ -114,7 +114,7 @@\n                 f_results.append(node_results)\n \n         for son in node.sons:\n-            to_explore.add(son)\n+            to_explore.append(son)\n \n \n def detect_divide_before_multiply(\n@@ -145,7 +145,7 @@\n         # track all the division results (and the assignment of the division results)\n         divisions: DefaultDict[LVALUE, List[Node]] = defaultdict(list)\n \n-        _explore({function.entry_point}, f_results, divisions)\n+        _explore([function.entry_point], f_results, divisions)\n \n         for f_result in f_results:\n             results.append((function, f_result))\n", "issue": "[Bug]: Slither detects already triaged results\n### Describe the issue:\r\n\r\nSlither seems to be detecting issues that have already been triaged into the `slither.db.json` from time to time.\r\nThis makes it difficult to get consistent results between runs after triaging findings, and also generates false positives when running Slither in CI.\r\n\r\nThis could be related to the ID of the findings that is stored in the DB file, which seems to vary between runs.\r\n\r\n### Code example to reproduce the issue:\r\n\r\nPublic sample to reproduce the issue with instructions here: https://github.com/balancer/balancer-v2-monorepo/pull/2514.\r\n\r\n### Version:\r\n\r\n`0.9.3`\r\n\r\n### Relevant log output:\r\n\r\n```shell\r\n# Following the instructions in the PR mentioned above, this is the output after triaging all the results.\r\n# Running slither two times in a row outputs different results:\r\n\r\njuan@M16:~/prj/bal/pkg/solidity-utils$ yarn slither\r\nNothing to compile\r\nINFO:Detectors:\r\nLogExpMath.exp(int256) (contracts/math/LogExpMath.sol#146-281) performs a multiplication on the result of a division:\r\n\t- product = (product * a3) / ONE_20 (contracts/math/LogExpMath.sol#198)\r\n\t- product = (product * a4) / ONE_20 (contracts/math/LogExpMath.sol#202)\r\nLogExpMath.exp(int256) (contracts/math/LogExpMath.sol#146-281) performs a multiplication on the result of a division:\r\n\t- product = (product * a3) / ONE_20 (contracts/math/LogExpMath.sol#198)\r\n\t- product = (product * a5) / ONE_20 (contracts/math/LogExpMath.sol#206)\r\nLogExpMath.exp(int256) (contracts/math/LogExpMath.sol#146-281) performs a multiplication on the result of a division:\r\n\t- product = (product * a7) / ONE_20 (contracts/math/LogExpMath.sol#214)\r\n\t- product = (product * a8) / ONE_20 (contracts/math/LogExpMath.sol#218)\r\nLogExpMath._ln(int256) (contracts/math/LogExpMath.sol#326-458) performs a multiplication on the result of a division:\r\n\t- a = (a * ONE_20) / a3 (contracts/math/LogExpMath.sol#372)\r\n\t- a = (a * ONE_20) / a4 (contracts/math/LogExpMath.sol#377)\r\nLogExpMath._ln(int256) (contracts/math/LogExpMath.sol#326-458) performs a multiplication on the result of a division:\r\n\t- a = (a * ONE_20) / a5 (contracts/math/LogExpMath.sol#382)\r\n\t- a = (a * ONE_20) / a6 (contracts/math/LogExpMath.sol#387)\r\nReference: https://github.com/crytic/slither/wiki/Detector-Documentation#divide-before-multiply\r\nINFO:Slither:. analyzed (72 contracts with 85 detectors), 5 result(s) found\r\n\r\n\r\njuan@M16:~/prj/bal/pkg/solidity-utils$ yarn slither\r\nNothing to compile\r\nINFO:Detectors:\r\nLogExpMath.exp(int256) (contracts/math/LogExpMath.sol#146-281) performs a multiplication on the result of a division:\r\n\t- product = (product * a3) / ONE_20 (contracts/math/LogExpMath.sol#198)\r\n\t- product = (product * a5) / ONE_20 (contracts/math/LogExpMath.sol#206)\r\nLogExpMath.exp(int256) (contracts/math/LogExpMath.sol#146-281) performs a multiplication on the result of a division:\r\n\t- product = (product * a4) / ONE_20 (contracts/math/LogExpMath.sol#202)\r\n\t- product = (product * a6) / ONE_20 (contracts/math/LogExpMath.sol#210)\r\nLogExpMath.exp(int256) (contracts/math/LogExpMath.sol#146-281) performs a multiplication on the result of a division:\r\n\t- product = (product * a7) / ONE_20 (contracts/math/LogExpMath.sol#214)\r\n\t- product = (product * a8) / ONE_20 (contracts/math/LogExpMath.sol#218)\r\nReference: https://github.com/crytic/slither/wiki/Detector-Documentation#divide-before-multiply\r\nINFO:Slither:. analyzed (72 contracts with 85 detectors), 3 result(s) found\r\njuan@M16:~/prj/bal/pkg/solidity-utils$\r\n```\r\n\n", "before_files": [{"content": "\"\"\"\nModule detecting possible loss of precision due to divide before multiple\n\"\"\"\nfrom collections import defaultdict\nfrom typing import DefaultDict, List, Set, Tuple\n\nfrom slither.core.cfg.node import Node\nfrom slither.core.declarations.contract import Contract\nfrom slither.core.declarations.function_contract import FunctionContract\nfrom slither.detectors.abstract_detector import (\n    AbstractDetector,\n    DetectorClassification,\n    DETECTOR_INFO,\n)\nfrom slither.slithir.operations import Binary, Assignment, BinaryType, LibraryCall, Operation\nfrom slither.slithir.utils.utils import LVALUE\nfrom slither.slithir.variables import Constant\nfrom slither.utils.output import Output\n\n\ndef is_division(ir: Operation) -> bool:\n    if isinstance(ir, Binary):\n        if ir.type == BinaryType.DIVISION:\n            return True\n\n    if isinstance(ir, LibraryCall):\n        if ir.function.name and ir.function.name.lower() in [\n            \"div\",\n            \"safediv\",\n        ]:\n            if len(ir.arguments) == 2:\n                if ir.lvalue:\n                    return True\n    return False\n\n\ndef is_multiplication(ir: Operation) -> bool:\n    if isinstance(ir, Binary):\n        if ir.type == BinaryType.MULTIPLICATION:\n            return True\n\n    if isinstance(ir, LibraryCall):\n        if ir.function.name and ir.function.name.lower() in [\n            \"mul\",\n            \"safemul\",\n        ]:\n            if len(ir.arguments) == 2:\n                if ir.lvalue:\n                    return True\n    return False\n\n\ndef is_assert(node: Node) -> bool:\n    if node.contains_require_or_assert():\n        return True\n    # Old Solidity code where using an internal 'assert(bool)' function\n    # While we dont check that this function is correct, we assume it is\n    # To avoid too many FP\n    if \"assert(bool)\" in [c.full_name for c in node.internal_calls]:\n        return True\n    return False\n\n\n# pylint: disable=too-many-branches\ndef _explore(\n    to_explore: Set[Node], f_results: List[List[Node]], divisions: DefaultDict[LVALUE, List[Node]]\n) -> None:\n    explored = set()\n    while to_explore:  # pylint: disable=too-many-nested-blocks\n        node = to_explore.pop()\n\n        if node in explored:\n            continue\n        explored.add(node)\n\n        equality_found = False\n        # List of nodes related to one bug instance\n        node_results: List[Node] = []\n\n        for ir in node.irs:\n            if isinstance(ir, Assignment):\n                if ir.rvalue in divisions:\n                    # Avoid dupplicate. We dont use set so we keep the order of the nodes\n                    if node not in divisions[ir.rvalue]:  # type: ignore\n                        divisions[ir.lvalue] = divisions[ir.rvalue] + [node]  # type: ignore\n                    else:\n                        divisions[ir.lvalue] = divisions[ir.rvalue]  # type: ignore\n\n            if is_division(ir):\n                divisions[ir.lvalue] = [node]  # type: ignore\n\n            if is_multiplication(ir):\n                mul_arguments = ir.read if isinstance(ir, Binary) else ir.arguments  # type: ignore\n                nodes = []\n                for r in mul_arguments:\n                    if not isinstance(r, Constant) and (r in divisions):\n                        # Dont add node already present to avoid dupplicate\n                        # We dont use set to keep the order of the nodes\n                        if node in divisions[r]:\n                            nodes += [n for n in divisions[r] if n not in nodes]\n                        else:\n                            nodes += [n for n in divisions[r] + [node] if n not in nodes]\n                if nodes:\n                    node_results = nodes\n\n            if isinstance(ir, Binary) and ir.type == BinaryType.EQUAL:\n                equality_found = True\n\n        if node_results:\n            # We do not track the case where the multiplication is done in a require() or assert()\n            # Which also contains a ==, to prevent FP due to the form\n            # assert(a == b * c + a % b)\n            if not (is_assert(node) and equality_found):\n                f_results.append(node_results)\n\n        for son in node.sons:\n            to_explore.add(son)\n\n\ndef detect_divide_before_multiply(\n    contract: Contract,\n) -> List[Tuple[FunctionContract, List[Node]]]:\n    \"\"\"\n    Detects and returns all nodes with multiplications of division results.\n    :param contract: Contract to detect assignment within.\n    :return: A list of nodes with multiplications of divisions.\n    \"\"\"\n\n    # Create our result set.\n    # List of tuple (function -> list(list(nodes)))\n    # Each list(nodes) of the list is one bug instances\n    # Each node in the list(nodes) is involved in the bug\n    results: List[Tuple[FunctionContract, List[Node]]] = []\n\n    # Loop for each function and modifier.\n    for function in contract.functions_declared:\n        if not function.entry_point:\n            continue\n\n        # List of list(nodes)\n        # Each list(nodes) is one bug instances\n        f_results: List[List[Node]] = []\n\n        # lvalue -> node\n        # track all the division results (and the assignment of the division results)\n        divisions: DefaultDict[LVALUE, List[Node]] = defaultdict(list)\n\n        _explore({function.entry_point}, f_results, divisions)\n\n        for f_result in f_results:\n            results.append((function, f_result))\n\n    # Return the resulting set of nodes with divisions before multiplications\n    return results\n\n\nclass DivideBeforeMultiply(AbstractDetector):\n    \"\"\"\n    Divide before multiply\n    \"\"\"\n\n    ARGUMENT = \"divide-before-multiply\"\n    HELP = \"Imprecise arithmetic operations order\"\n    IMPACT = DetectorClassification.MEDIUM\n    CONFIDENCE = DetectorClassification.MEDIUM\n\n    WIKI = \"https://github.com/crytic/slither/wiki/Detector-Documentation#divide-before-multiply\"\n\n    WIKI_TITLE = \"Divide before multiply\"\n    WIKI_DESCRIPTION = \"\"\"Solidity's integer division truncates. Thus, performing division before multiplication can lead to precision loss.\"\"\"\n\n    # region wiki_exploit_scenario\n    WIKI_EXPLOIT_SCENARIO = \"\"\"\n```solidity\ncontract A {\n\tfunction f(uint n) public {\n        coins = (oldSupply / n) * interest;\n    }\n}\n```\nIf `n` is greater than `oldSupply`, `coins` will be zero. For example, with `oldSupply = 5; n = 10, interest = 2`, coins will be zero.  \nIf `(oldSupply * interest / n)` was used, `coins` would have been `1`.   \nIn general, it's usually a good idea to re-arrange arithmetic to perform multiplication before division, unless the limit of a smaller type makes this dangerous.\"\"\"\n    # endregion wiki_exploit_scenario\n\n    WIKI_RECOMMENDATION = \"\"\"Consider ordering multiplication before division.\"\"\"\n\n    def _detect(self) -> List[Output]:\n        \"\"\"\n        Detect divisions before multiplications\n        \"\"\"\n        results = []\n        for contract in self.contracts:\n            divisions_before_multiplications = detect_divide_before_multiply(contract)\n            if divisions_before_multiplications:\n                for (func, nodes) in divisions_before_multiplications:\n\n                    info: DETECTOR_INFO = [\n                        func,\n                        \" performs a multiplication on the result of a division:\\n\",\n                    ]\n\n                    # sort the nodes to get deterministic results\n                    nodes.sort(key=lambda x: x.node_id)\n\n                    for node in nodes:\n                        info += [\"\\t- \", node, \"\\n\"]\n\n                    res = self.generate_result(info)\n                    results.append(res)\n\n        return results\n", "path": "slither/detectors/statements/divide_before_multiply.py"}], "after_files": [{"content": "\"\"\"\nModule detecting possible loss of precision due to divide before multiple\n\"\"\"\nfrom collections import defaultdict\nfrom typing import DefaultDict, List, Tuple\n\nfrom slither.core.cfg.node import Node\nfrom slither.core.declarations.contract import Contract\nfrom slither.core.declarations.function_contract import FunctionContract\nfrom slither.detectors.abstract_detector import (\n    AbstractDetector,\n    DetectorClassification,\n    DETECTOR_INFO,\n)\nfrom slither.slithir.operations import Binary, Assignment, BinaryType, LibraryCall, Operation\nfrom slither.slithir.utils.utils import LVALUE\nfrom slither.slithir.variables import Constant\nfrom slither.utils.output import Output\n\n\ndef is_division(ir: Operation) -> bool:\n    if isinstance(ir, Binary):\n        if ir.type == BinaryType.DIVISION:\n            return True\n\n    if isinstance(ir, LibraryCall):\n        if ir.function.name and ir.function.name.lower() in [\n            \"div\",\n            \"safediv\",\n        ]:\n            if len(ir.arguments) == 2:\n                if ir.lvalue:\n                    return True\n    return False\n\n\ndef is_multiplication(ir: Operation) -> bool:\n    if isinstance(ir, Binary):\n        if ir.type == BinaryType.MULTIPLICATION:\n            return True\n\n    if isinstance(ir, LibraryCall):\n        if ir.function.name and ir.function.name.lower() in [\n            \"mul\",\n            \"safemul\",\n        ]:\n            if len(ir.arguments) == 2:\n                if ir.lvalue:\n                    return True\n    return False\n\n\ndef is_assert(node: Node) -> bool:\n    if node.contains_require_or_assert():\n        return True\n    # Old Solidity code where using an internal 'assert(bool)' function\n    # While we dont check that this function is correct, we assume it is\n    # To avoid too many FP\n    if \"assert(bool)\" in [c.full_name for c in node.internal_calls]:\n        return True\n    return False\n\n\n# pylint: disable=too-many-branches\ndef _explore(\n    to_explore: List[Node], f_results: List[List[Node]], divisions: DefaultDict[LVALUE, List[Node]]\n) -> None:\n    explored = set()\n    while to_explore:  # pylint: disable=too-many-nested-blocks\n        node = to_explore.pop()\n\n        if node in explored:\n            continue\n        explored.add(node)\n\n        equality_found = False\n        # List of nodes related to one bug instance\n        node_results: List[Node] = []\n\n        for ir in node.irs:\n            if isinstance(ir, Assignment):\n                if ir.rvalue in divisions:\n                    # Avoid dupplicate. We dont use set so we keep the order of the nodes\n                    if node not in divisions[ir.rvalue]:  # type: ignore\n                        divisions[ir.lvalue] = divisions[ir.rvalue] + [node]  # type: ignore\n                    else:\n                        divisions[ir.lvalue] = divisions[ir.rvalue]  # type: ignore\n\n            if is_division(ir):\n                divisions[ir.lvalue] = [node]  # type: ignore\n\n            if is_multiplication(ir):\n                mul_arguments = ir.read if isinstance(ir, Binary) else ir.arguments  # type: ignore\n                nodes = []\n                for r in mul_arguments:\n                    if not isinstance(r, Constant) and (r in divisions):\n                        # Dont add node already present to avoid dupplicate\n                        # We dont use set to keep the order of the nodes\n                        if node in divisions[r]:\n                            nodes += [n for n in divisions[r] if n not in nodes]\n                        else:\n                            nodes += [n for n in divisions[r] + [node] if n not in nodes]\n                if nodes:\n                    node_results = nodes\n\n            if isinstance(ir, Binary) and ir.type == BinaryType.EQUAL:\n                equality_found = True\n\n        if node_results:\n            # We do not track the case where the multiplication is done in a require() or assert()\n            # Which also contains a ==, to prevent FP due to the form\n            # assert(a == b * c + a % b)\n            if not (is_assert(node) and equality_found):\n                f_results.append(node_results)\n\n        for son in node.sons:\n            to_explore.append(son)\n\n\ndef detect_divide_before_multiply(\n    contract: Contract,\n) -> List[Tuple[FunctionContract, List[Node]]]:\n    \"\"\"\n    Detects and returns all nodes with multiplications of division results.\n    :param contract: Contract to detect assignment within.\n    :return: A list of nodes with multiplications of divisions.\n    \"\"\"\n\n    # Create our result set.\n    # List of tuple (function -> list(list(nodes)))\n    # Each list(nodes) of the list is one bug instances\n    # Each node in the list(nodes) is involved in the bug\n    results: List[Tuple[FunctionContract, List[Node]]] = []\n\n    # Loop for each function and modifier.\n    for function in contract.functions_declared:\n        if not function.entry_point:\n            continue\n\n        # List of list(nodes)\n        # Each list(nodes) is one bug instances\n        f_results: List[List[Node]] = []\n\n        # lvalue -> node\n        # track all the division results (and the assignment of the division results)\n        divisions: DefaultDict[LVALUE, List[Node]] = defaultdict(list)\n\n        _explore([function.entry_point], f_results, divisions)\n\n        for f_result in f_results:\n            results.append((function, f_result))\n\n    # Return the resulting set of nodes with divisions before multiplications\n    return results\n\n\nclass DivideBeforeMultiply(AbstractDetector):\n    \"\"\"\n    Divide before multiply\n    \"\"\"\n\n    ARGUMENT = \"divide-before-multiply\"\n    HELP = \"Imprecise arithmetic operations order\"\n    IMPACT = DetectorClassification.MEDIUM\n    CONFIDENCE = DetectorClassification.MEDIUM\n\n    WIKI = \"https://github.com/crytic/slither/wiki/Detector-Documentation#divide-before-multiply\"\n\n    WIKI_TITLE = \"Divide before multiply\"\n    WIKI_DESCRIPTION = \"\"\"Solidity's integer division truncates. Thus, performing division before multiplication can lead to precision loss.\"\"\"\n\n    # region wiki_exploit_scenario\n    WIKI_EXPLOIT_SCENARIO = \"\"\"\n```solidity\ncontract A {\n\tfunction f(uint n) public {\n        coins = (oldSupply / n) * interest;\n    }\n}\n```\nIf `n` is greater than `oldSupply`, `coins` will be zero. For example, with `oldSupply = 5; n = 10, interest = 2`, coins will be zero.  \nIf `(oldSupply * interest / n)` was used, `coins` would have been `1`.   \nIn general, it's usually a good idea to re-arrange arithmetic to perform multiplication before division, unless the limit of a smaller type makes this dangerous.\"\"\"\n    # endregion wiki_exploit_scenario\n\n    WIKI_RECOMMENDATION = \"\"\"Consider ordering multiplication before division.\"\"\"\n\n    def _detect(self) -> List[Output]:\n        \"\"\"\n        Detect divisions before multiplications\n        \"\"\"\n        results = []\n        for contract in self.contracts:\n            divisions_before_multiplications = detect_divide_before_multiply(contract)\n            if divisions_before_multiplications:\n                for (func, nodes) in divisions_before_multiplications:\n\n                    info: DETECTOR_INFO = [\n                        func,\n                        \" performs a multiplication on the result of a division:\\n\",\n                    ]\n\n                    # sort the nodes to get deterministic results\n                    nodes.sort(key=lambda x: x.node_id)\n\n                    for node in nodes:\n                        info += [\"\\t- \", node, \"\\n\"]\n\n                    res = self.generate_result(info)\n                    results.append(res)\n\n        return results\n", "path": "slither/detectors/statements/divide_before_multiply.py"}]}