diff --git a/slither/core/declarations/solidity_variables.py b/slither/core/declarations/solidity_variables.py index 650d9f013f..268e8c9efb 100644 --- a/slither/core/declarations/solidity_variables.py +++ b/slither/core/declarations/solidity_variables.py @@ -72,6 +72,8 @@ "log3(bytes32,bytes32,bytes32,bytes32)": [], "blockhash(uint256)": ["bytes32"], "prevrandao()": ["uint256"], + # Solidity 0.8.35 comptime builtin: ERC-7201 namespaced storage base slot + "erc7201(string)": ["uint256"], # the following need a special handling # as they are recognized as a SolidityVariableComposed # and converted to a SolidityFunction by SlithIR diff --git a/slither/visitors/expression/constants_folding.py b/slither/visitors/expression/constants_folding.py index ba221f8f5d..83d52262b0 100644 --- a/slither/visitors/expression/constants_folding.py +++ b/slither/visitors/expression/constants_folding.py @@ -98,7 +98,10 @@ def _post_identifier(self, expression: Identifier) -> None: cf = ConstantFolding(expr, self._type) expr = cf.result() assert isinstance(expr, Literal) - set_val(expression, convert_string_to_int(expr.converted_value)) + if str(expr.type) == "string": + set_val(expression, expr.converted_value) + else: + set_val(expression, convert_string_to_int(expr.converted_value)) else: raise NotConstant elif isinstance(expression.value, SolidityFunction): @@ -276,6 +279,16 @@ def _post_call_expression(self, expression: expressions.CallExpression) -> None: digest = keccak.new(digest_bits=256) digest.update(str(args[0]).encode("utf8")) set_val(expression, digest.digest()) + elif called.name == "erc7201(string)": + # Solidity 0.8.35 comptime builtin + # keccak256(keccak256(id) - 1) & ~0xff + inner = keccak.new(digest_bits=256) + inner.update(str(args[0]).encode("utf8")) + inner_int = int.from_bytes(inner.digest(), "big") + minus_one = (inner_int - 1) & ((1 << 256) - 1) + outer = keccak.new(digest_bits=256) + outer.update(minus_one.to_bytes(32, "big")) + set_val(expression, int.from_bytes(outer.digest(), "big") & ~0xFF) else: raise NotConstant diff --git a/tests/unit/core/test_data/storage_layout/storage_layout-0.8.35.sol b/tests/unit/core/test_data/storage_layout/storage_layout-0.8.35.sol new file mode 100644 index 0000000000..020f1e0c5e --- /dev/null +++ b/tests/unit/core/test_data/storage_layout/storage_layout-0.8.35.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.35; + +string constant NS = "openzeppelin.storage.MyContract"; + +contract MyContract layout at erc7201(NS) { + uint256 x; + uint256 y; +} diff --git a/tests/unit/core/test_storage_layout.py b/tests/unit/core/test_storage_layout.py index 8bc4f16d12..45e14cb218 100644 --- a/tests/unit/core/test_storage_layout.py +++ b/tests/unit/core/test_storage_layout.py @@ -15,6 +15,11 @@ def test_custom_storage_layout(solc_binary_path): _test_storage_layout(solc_binary_path, "0.8.33", "storage_layout-0.8.33.sol") +def test_erc7201_storage_layout(solc_binary_path): + # 0.8.35 erc7201 comptime builtin used as the base slot of `layout at` + _test_storage_layout(solc_binary_path, "0.8.35", "storage_layout-0.8.35.sol") + + def _test_storage_layout(solc_binary_path, solc_version, test_file): # the storage layout has not yet changed between solidity versions so we will test with one version of the compiler solc_path = solc_binary_path(solc_version)