mirror of https://github.com/crytic/slither
commit
134dd904f3
@ -1,3 +1,9 @@ |
|||||||
contract A{ |
pragma solidity 0.8.19; |
||||||
|
|
||||||
|
error RevertIt(); |
||||||
|
|
||||||
|
contract Example { |
||||||
|
function reverts() external pure { |
||||||
|
revert RevertIt(); |
||||||
|
} |
||||||
} |
} |
@ -1,5 +1,16 @@ |
|||||||
import "./a.sol"; |
import "./a.sol"; |
||||||
|
|
||||||
contract B is A{ |
pragma solidity 0.8.19; |
||||||
|
|
||||||
|
enum B { |
||||||
|
a, |
||||||
|
b |
||||||
|
} |
||||||
|
|
||||||
|
contract T { |
||||||
|
Example e = new Example(); |
||||||
|
function b() public returns(uint) { |
||||||
|
B b = B.a; |
||||||
|
return 4; |
||||||
|
} |
||||||
} |
} |
@ -0,0 +1,95 @@ |
|||||||
|
#!/usr/bin/env bash |
||||||
|
|
||||||
|
### Test slither-interface |
||||||
|
|
||||||
|
DIR_TESTS="tests/tools/interface" |
||||||
|
|
||||||
|
solc-select use 0.8.19 --always-install |
||||||
|
|
||||||
|
#Test 1 - Etherscan target |
||||||
|
slither-interface WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 |
||||||
|
DIFF=$(diff crytic-export/interfaces/IWETH9.sol "$DIR_TESTS/test_1.sol" --strip-trailing-cr) |
||||||
|
if [ "$DIFF" != "" ] |
||||||
|
then |
||||||
|
echo "slither-interface test 1 failed" |
||||||
|
cat "crytic-export/interfaces/IWETH9.sol" |
||||||
|
echo "" |
||||||
|
cat "$DIR_TESTS/test_1.sol" |
||||||
|
exit 255 |
||||||
|
fi |
||||||
|
|
||||||
|
|
||||||
|
#Test 2 - Local file target |
||||||
|
slither-interface Mock tests/tools/interface/ContractMock.sol |
||||||
|
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_2.sol" --strip-trailing-cr) |
||||||
|
if [ "$DIFF" != "" ] |
||||||
|
then |
||||||
|
echo "slither-interface test 2 failed" |
||||||
|
cat "crytic-export/interfaces/IMock.sol" |
||||||
|
echo "" |
||||||
|
cat "$DIR_TESTS/test_2.sol" |
||||||
|
exit 255 |
||||||
|
fi |
||||||
|
|
||||||
|
|
||||||
|
#Test 3 - unroll structs |
||||||
|
slither-interface Mock tests/tools/interface/ContractMock.sol --unroll-structs |
||||||
|
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_3.sol" --strip-trailing-cr) |
||||||
|
if [ "$DIFF" != "" ] |
||||||
|
then |
||||||
|
echo "slither-interface test 3 failed" |
||||||
|
cat "crytic-export/interfaces/IMock.sol" |
||||||
|
echo "" |
||||||
|
cat "$DIR_TESTS/test_3.sol" |
||||||
|
exit 255 |
||||||
|
fi |
||||||
|
|
||||||
|
#Test 4 - exclude structs |
||||||
|
slither-interface Mock tests/tools/interface/ContractMock.sol --exclude-structs |
||||||
|
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_4.sol" --strip-trailing-cr) |
||||||
|
if [ "$DIFF" != "" ] |
||||||
|
then |
||||||
|
echo "slither-interface test 4 failed" |
||||||
|
cat "crytic-export/interfaces/IMock.sol" |
||||||
|
echo "" |
||||||
|
cat "$DIR_TESTS/test_4.sol" |
||||||
|
exit 255 |
||||||
|
fi |
||||||
|
|
||||||
|
#Test 5 - exclude errors |
||||||
|
slither-interface Mock tests/tools/interface/ContractMock.sol --exclude-errors |
||||||
|
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_5.sol" --strip-trailing-cr) |
||||||
|
if [ "$DIFF" != "" ] |
||||||
|
then |
||||||
|
echo "slither-interface test 5 failed" |
||||||
|
cat "crytic-export/interfaces/IMock.sol" |
||||||
|
echo "" |
||||||
|
cat "$DIR_TESTS/test_5.sol" |
||||||
|
exit 255 |
||||||
|
fi |
||||||
|
|
||||||
|
#Test 6 - exclude enums |
||||||
|
slither-interface Mock tests/tools/interface/ContractMock.sol --exclude-enums |
||||||
|
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_6.sol" --strip-trailing-cr) |
||||||
|
if [ "$DIFF" != "" ] |
||||||
|
then |
||||||
|
echo "slither-interface test 6 failed" |
||||||
|
cat "crytic-export/interfaces/IMock.sol" |
||||||
|
echo "" |
||||||
|
cat "$DIR_TESTS/test_6.sol" |
||||||
|
exit 255 |
||||||
|
fi |
||||||
|
|
||||||
|
#Test 7 - exclude events |
||||||
|
slither-interface Mock tests/tools/interface/ContractMock.sol --exclude-events |
||||||
|
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_7.sol" --strip-trailing-cr) |
||||||
|
if [ "$DIFF" != "" ] |
||||||
|
then |
||||||
|
echo "slither-interface test 7 failed" |
||||||
|
cat "crytic-export/interfaces/IMock.sol" |
||||||
|
echo "" |
||||||
|
cat "$DIR_TESTS/test_7.sol" |
||||||
|
exit 255 |
||||||
|
fi |
||||||
|
|
||||||
|
rm -r crytic-export |
@ -1,32 +1,23 @@ |
|||||||
from typing import Union, TYPE_CHECKING |
from typing import TYPE_CHECKING |
||||||
|
|
||||||
|
|
||||||
from slither.core.expressions.expression import Expression |
from slither.core.expressions.expression import Expression |
||||||
from slither.core.solidity_types.type import Type |
|
||||||
|
|
||||||
if TYPE_CHECKING: |
if TYPE_CHECKING: |
||||||
from slither.core.solidity_types.elementary_type import ElementaryType |
from slither.core.solidity_types.array_type import ArrayType |
||||||
from slither.core.solidity_types.type_alias import TypeAliasTopLevel |
|
||||||
|
|
||||||
|
|
||||||
class NewArray(Expression): |
class NewArray(Expression): |
||||||
|
def __init__(self, array_type: "ArrayType") -> None: |
||||||
# note: dont conserve the size of the array if provided |
|
||||||
def __init__( |
|
||||||
self, depth: int, array_type: Union["TypeAliasTopLevel", "ElementaryType"] |
|
||||||
) -> None: |
|
||||||
super().__init__() |
super().__init__() |
||||||
assert isinstance(array_type, Type) |
# pylint: disable=import-outside-toplevel |
||||||
self._depth: int = depth |
from slither.core.solidity_types.array_type import ArrayType |
||||||
self._array_type: Type = array_type |
|
||||||
|
|
||||||
@property |
assert isinstance(array_type, ArrayType) |
||||||
def array_type(self) -> Type: |
self._array_type = array_type |
||||||
return self._array_type |
|
||||||
|
|
||||||
@property |
@property |
||||||
def depth(self) -> int: |
def array_type(self) -> "ArrayType": |
||||||
return self._depth |
return self._array_type |
||||||
|
|
||||||
def __str__(self): |
def __str__(self): |
||||||
return "new " + str(self._array_type) + "[]" * self._depth |
return "new " + str(self._array_type) |
||||||
|
@ -0,0 +1,104 @@ |
|||||||
|
""" |
||||||
|
Module detecting usage of more than one dynamic type in abi.encodePacked() arguments which could lead to collision |
||||||
|
""" |
||||||
|
|
||||||
|
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification |
||||||
|
from slither.core.declarations.solidity_variables import SolidityFunction |
||||||
|
from slither.slithir.operations import SolidityCall |
||||||
|
from slither.analyses.data_dependency.data_dependency import is_tainted |
||||||
|
from slither.core.solidity_types import ElementaryType |
||||||
|
from slither.core.solidity_types import ArrayType |
||||||
|
|
||||||
|
|
||||||
|
def _is_dynamic_type(arg): |
||||||
|
""" |
||||||
|
Args: |
||||||
|
arg (function argument) |
||||||
|
Returns: |
||||||
|
Bool |
||||||
|
""" |
||||||
|
if isinstance(arg.type, ElementaryType) and (arg.type.name in ["string", "bytes"]): |
||||||
|
return True |
||||||
|
if isinstance(arg.type, ArrayType) and arg.type.length is None: |
||||||
|
return True |
||||||
|
|
||||||
|
return False |
||||||
|
|
||||||
|
|
||||||
|
def _detect_abi_encodePacked_collision(contract): |
||||||
|
""" |
||||||
|
Args: |
||||||
|
contract (Contract) |
||||||
|
Returns: |
||||||
|
list((Function), (list (Node))) |
||||||
|
""" |
||||||
|
ret = [] |
||||||
|
# pylint: disable=too-many-nested-blocks |
||||||
|
for f in contract.functions_and_modifiers_declared: |
||||||
|
for n in f.nodes: |
||||||
|
for ir in n.irs: |
||||||
|
if isinstance(ir, SolidityCall) and ir.function == SolidityFunction( |
||||||
|
"abi.encodePacked()" |
||||||
|
): |
||||||
|
dynamic_type_count = 0 |
||||||
|
for arg in ir.arguments: |
||||||
|
if is_tainted(arg, contract) and _is_dynamic_type(arg): |
||||||
|
dynamic_type_count += 1 |
||||||
|
elif dynamic_type_count > 1: |
||||||
|
ret.append((f, n)) |
||||||
|
dynamic_type_count = 0 |
||||||
|
else: |
||||||
|
dynamic_type_count = 0 |
||||||
|
if dynamic_type_count > 1: |
||||||
|
ret.append((f, n)) |
||||||
|
return ret |
||||||
|
|
||||||
|
|
||||||
|
class EncodePackedCollision(AbstractDetector): |
||||||
|
""" |
||||||
|
Detect usage of more than one dynamic type in abi.encodePacked() arguments which could to collision |
||||||
|
""" |
||||||
|
|
||||||
|
ARGUMENT = "encode-packed-collision" |
||||||
|
HELP = "ABI encodePacked Collision" |
||||||
|
IMPACT = DetectorClassification.HIGH |
||||||
|
CONFIDENCE = DetectorClassification.HIGH |
||||||
|
|
||||||
|
WIKI = ( |
||||||
|
"https://github.com/crytic/slither/wiki/Detector-Documentation#abi-encodePacked-collision" |
||||||
|
) |
||||||
|
|
||||||
|
WIKI_TITLE = "ABI encodePacked Collision" |
||||||
|
WIKI_DESCRIPTION = """Detect collision due to dynamic type usages in `abi.encodePacked`""" |
||||||
|
|
||||||
|
WIKI_EXPLOIT_SCENARIO = """ |
||||||
|
```solidity |
||||||
|
contract Sign { |
||||||
|
function get_hash_for_signature(string name, string doc) external returns(bytes32) { |
||||||
|
return keccak256(abi.encodePacked(name, doc)); |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
Bob calls `get_hash_for_signature` with (`bob`, `This is the content`). The hash returned is used as an ID. |
||||||
|
Eve creates a collision with the ID using (`bo`, `bThis is the content`) and compromises the system. |
||||||
|
""" |
||||||
|
WIKI_RECOMMENDATION = """Do not use more than one dynamic type in `abi.encodePacked()` |
||||||
|
(see the [Solidity documentation](https://solidity.readthedocs.io/en/v0.5.10/abi-spec.html?highlight=abi.encodePacked#non-standard-packed-modeDynamic)). |
||||||
|
Use `abi.encode()`, preferably.""" |
||||||
|
|
||||||
|
def _detect(self): |
||||||
|
"""Detect usage of more than one dynamic type in abi.encodePacked(..) arguments which could lead to collision""" |
||||||
|
results = [] |
||||||
|
for c in self.compilation_unit.contracts: |
||||||
|
values = _detect_abi_encodePacked_collision(c) |
||||||
|
for func, node in values: |
||||||
|
info = [ |
||||||
|
func, |
||||||
|
" calls abi.encodePacked() with multiple dynamic arguments:\n\t- ", |
||||||
|
node, |
||||||
|
"\n", |
||||||
|
] |
||||||
|
json = self.generate_result(info) |
||||||
|
results.append(json) |
||||||
|
|
||||||
|
return results |
@ -0,0 +1,223 @@ |
|||||||
|
from typing import List |
||||||
|
|
||||||
|
from slither.core.declarations import Contract, Structure, Enum |
||||||
|
from slither.core.declarations.using_for_top_level import UsingForTopLevel |
||||||
|
from slither.core.solidity_types import ( |
||||||
|
UserDefinedType, |
||||||
|
Type, |
||||||
|
ElementaryType, |
||||||
|
TypeAlias, |
||||||
|
MappingType, |
||||||
|
ArrayType, |
||||||
|
) |
||||||
|
from slither.core.solidity_types.elementary_type import Uint, Int, Byte |
||||||
|
from slither.detectors.abstract_detector import ( |
||||||
|
AbstractDetector, |
||||||
|
DetectorClassification, |
||||||
|
DETECTOR_INFO, |
||||||
|
) |
||||||
|
from slither.utils.output import Output |
||||||
|
|
||||||
|
|
||||||
|
def _is_correctly_used(type_: Type, library: Contract) -> bool: |
||||||
|
""" |
||||||
|
Checks if a `using library for type_` statement is used correctly (that is, does library contain any function |
||||||
|
with type_ as the first argument). |
||||||
|
""" |
||||||
|
for f in library.functions: |
||||||
|
if len(f.parameters) == 0: |
||||||
|
continue |
||||||
|
if f.parameters[0].type and not _implicitly_convertible_to(type_, f.parameters[0].type): |
||||||
|
continue |
||||||
|
return True |
||||||
|
return False |
||||||
|
|
||||||
|
|
||||||
|
def _implicitly_convertible_to(type1: Type, type2: Type) -> bool: |
||||||
|
""" |
||||||
|
Returns True if type1 may be implicitly converted to type2. |
||||||
|
""" |
||||||
|
if isinstance(type1, TypeAlias) or isinstance(type2, TypeAlias): |
||||||
|
if isinstance(type1, TypeAlias) and isinstance(type2, TypeAlias): |
||||||
|
return type1.type == type2.type |
||||||
|
return False |
||||||
|
|
||||||
|
if isinstance(type1, UserDefinedType) and isinstance(type2, UserDefinedType): |
||||||
|
if isinstance(type1.type, Contract) and isinstance(type2.type, Contract): |
||||||
|
return _implicitly_convertible_to_for_contracts(type1.type, type2.type) |
||||||
|
|
||||||
|
if isinstance(type1.type, Structure) and isinstance(type2.type, Structure): |
||||||
|
return type1.type.canonical_name == type2.type.canonical_name |
||||||
|
|
||||||
|
if isinstance(type1.type, Enum) and isinstance(type2.type, Enum): |
||||||
|
return type1.type.canonical_name == type2.type.canonical_name |
||||||
|
|
||||||
|
if isinstance(type1, ElementaryType) and isinstance(type2, ElementaryType): |
||||||
|
return _implicitly_convertible_to_for_elementary_types(type1, type2) |
||||||
|
|
||||||
|
if isinstance(type1, MappingType) and isinstance(type2, MappingType): |
||||||
|
return _implicitly_convertible_to_for_mappings(type1, type2) |
||||||
|
|
||||||
|
if isinstance(type1, ArrayType) and isinstance(type2, ArrayType): |
||||||
|
return _implicitly_convertible_to_for_arrays(type1, type2) |
||||||
|
|
||||||
|
return False |
||||||
|
|
||||||
|
|
||||||
|
def _implicitly_convertible_to_for_arrays(type1: ArrayType, type2: ArrayType) -> bool: |
||||||
|
""" |
||||||
|
Returns True if type1 may be implicitly converted to type2. |
||||||
|
""" |
||||||
|
return _implicitly_convertible_to(type1.type, type2.type) |
||||||
|
|
||||||
|
|
||||||
|
def _implicitly_convertible_to_for_mappings(type1: MappingType, type2: MappingType) -> bool: |
||||||
|
""" |
||||||
|
Returns True if type1 may be implicitly converted to type2. |
||||||
|
""" |
||||||
|
return type1.type_from == type2.type_from and type1.type_to == type2.type_to |
||||||
|
|
||||||
|
|
||||||
|
def _implicitly_convertible_to_for_elementary_types( |
||||||
|
type1: ElementaryType, type2: ElementaryType |
||||||
|
) -> bool: |
||||||
|
""" |
||||||
|
Returns True if type1 may be implicitly converted to type2. |
||||||
|
""" |
||||||
|
if type1.type == "bool" and type2.type == "bool": |
||||||
|
return True |
||||||
|
if type1.type == "string" and type2.type == "string": |
||||||
|
return True |
||||||
|
if type1.type == "bytes" and type2.type == "bytes": |
||||||
|
return True |
||||||
|
if type1.type == "address" and type2.type == "address": |
||||||
|
return _implicitly_convertible_to_for_addresses(type1, type2) |
||||||
|
if type1.type in Uint and type2.type in Uint: |
||||||
|
return _implicitly_convertible_to_for_uints(type1, type2) |
||||||
|
if type1.type in Int and type2.type in Int: |
||||||
|
return _implicitly_convertible_to_for_ints(type1, type2) |
||||||
|
if ( |
||||||
|
type1.type != "bytes" |
||||||
|
and type2.type != "bytes" |
||||||
|
and type1.type in Byte |
||||||
|
and type2.type in Byte |
||||||
|
): |
||||||
|
return _implicitly_convertible_to_for_bytes(type1, type2) |
||||||
|
return False |
||||||
|
|
||||||
|
|
||||||
|
def _implicitly_convertible_to_for_bytes(type1: ElementaryType, type2: ElementaryType) -> bool: |
||||||
|
""" |
||||||
|
Returns True if type1 may be implicitly converted to type2 assuming they are both bytes. |
||||||
|
""" |
||||||
|
assert type1.type in Byte and type2.type in Byte |
||||||
|
assert type1.size is not None |
||||||
|
assert type2.size is not None |
||||||
|
|
||||||
|
return type1.size <= type2.size |
||||||
|
|
||||||
|
|
||||||
|
def _implicitly_convertible_to_for_addresses(type1: ElementaryType, type2: ElementaryType) -> bool: |
||||||
|
""" |
||||||
|
Returns True if type1 may be implicitly converted to type2 assuming they are both addresses. |
||||||
|
""" |
||||||
|
assert type1.type == "address" and type2.type == "address" |
||||||
|
# payable attribute to be implemented; for now, always return True |
||||||
|
return True |
||||||
|
|
||||||
|
|
||||||
|
def _implicitly_convertible_to_for_ints(type1: ElementaryType, type2: ElementaryType) -> bool: |
||||||
|
""" |
||||||
|
Returns True if type1 may be implicitly converted to type2 assuming they are both ints. |
||||||
|
""" |
||||||
|
assert type1.type in Int and type2.type in Int |
||||||
|
assert type1.size is not None |
||||||
|
assert type2.size is not None |
||||||
|
|
||||||
|
return type1.size <= type2.size |
||||||
|
|
||||||
|
|
||||||
|
def _implicitly_convertible_to_for_uints(type1: ElementaryType, type2: ElementaryType) -> bool: |
||||||
|
""" |
||||||
|
Returns True if type1 may be implicitly converted to type2 assuming they are both uints. |
||||||
|
""" |
||||||
|
assert type1.type in Uint and type2.type in Uint |
||||||
|
assert type1.size is not None |
||||||
|
assert type2.size is not None |
||||||
|
|
||||||
|
return type1.size <= type2.size |
||||||
|
|
||||||
|
|
||||||
|
def _implicitly_convertible_to_for_contracts(contract1: Contract, contract2: Contract) -> bool: |
||||||
|
""" |
||||||
|
Returns True if contract1 may be implicitly converted to contract2. |
||||||
|
""" |
||||||
|
return contract1 == contract2 or contract2 in contract1.inheritance |
||||||
|
|
||||||
|
|
||||||
|
class IncorrectUsingFor(AbstractDetector): |
||||||
|
""" |
||||||
|
Detector for incorrect using-for statement usage. |
||||||
|
""" |
||||||
|
|
||||||
|
ARGUMENT = "incorrect-using-for" |
||||||
|
HELP = "Detects using-for statement usage when no function from a given library matches a given type" |
||||||
|
IMPACT = DetectorClassification.INFORMATIONAL |
||||||
|
CONFIDENCE = DetectorClassification.HIGH |
||||||
|
|
||||||
|
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-using-for-usage" |
||||||
|
|
||||||
|
WIKI_TITLE = "Incorrect usage of using-for statement" |
||||||
|
WIKI_DESCRIPTION = ( |
||||||
|
"In Solidity, it is possible to use libraries for certain types, by the `using-for` statement " |
||||||
|
"(`using <library> for <type>`). However, the Solidity compiler doesn't check whether a given " |
||||||
|
"library has at least one function matching a given type. If it doesn't, such a statement has " |
||||||
|
"no effect and may be confusing. " |
||||||
|
) |
||||||
|
|
||||||
|
# region wiki_exploit_scenario |
||||||
|
WIKI_EXPLOIT_SCENARIO = """ |
||||||
|
```solidity |
||||||
|
library L { |
||||||
|
function f(bool) public pure {} |
||||||
|
} |
||||||
|
|
||||||
|
using L for uint; |
||||||
|
``` |
||||||
|
Such a code will compile despite the fact that `L` has no function with `uint` as its first argument.""" |
||||||
|
# endregion wiki_exploit_scenario |
||||||
|
WIKI_RECOMMENDATION = ( |
||||||
|
"Make sure that the libraries used in `using-for` statements have at least one function " |
||||||
|
"matching a type used in these statements. " |
||||||
|
) |
||||||
|
|
||||||
|
def _append_result( |
||||||
|
self, results: List[Output], uf: UsingForTopLevel, type_: Type, library: Contract |
||||||
|
) -> None: |
||||||
|
info: DETECTOR_INFO = [ |
||||||
|
f"using-for statement at {uf.source_mapping} is incorrect - no matching function for {type_} found in ", |
||||||
|
library, |
||||||
|
".\n", |
||||||
|
] |
||||||
|
res = self.generate_result(info) |
||||||
|
results.append(res) |
||||||
|
|
||||||
|
def _detect(self) -> List[Output]: |
||||||
|
results: List[Output] = [] |
||||||
|
|
||||||
|
for uf in self.compilation_unit.using_for_top_level: |
||||||
|
# UsingForTopLevel.using_for is a dict with a single entry, which is mapped to a list of functions/libraries |
||||||
|
# the following code extracts the type from using-for and skips using-for statements with functions |
||||||
|
type_ = list(uf.using_for.keys())[0] |
||||||
|
for lib_or_fcn in uf.using_for[type_]: |
||||||
|
# checking for using-for with functions is already performed by the compiler; we only consider libraries |
||||||
|
if isinstance(lib_or_fcn, UserDefinedType): |
||||||
|
lib_or_fcn_type = lib_or_fcn.type |
||||||
|
if ( |
||||||
|
isinstance(type_, Type) |
||||||
|
and isinstance(lib_or_fcn_type, Contract) |
||||||
|
and not _is_correctly_used(type_, lib_or_fcn_type) |
||||||
|
): |
||||||
|
self._append_result(results, uf, type_, lib_or_fcn_type) |
||||||
|
|
||||||
|
return results |
@ -0,0 +1,105 @@ |
|||||||
|
import argparse |
||||||
|
import logging |
||||||
|
from pathlib import Path |
||||||
|
|
||||||
|
from crytic_compile import cryticparser |
||||||
|
|
||||||
|
from slither import Slither |
||||||
|
from slither.utils.code_generation import generate_interface |
||||||
|
|
||||||
|
logging.basicConfig() |
||||||
|
logger = logging.getLogger("Slither-Interface") |
||||||
|
logger.setLevel(logging.INFO) |
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace: |
||||||
|
""" |
||||||
|
Parse the underlying arguments for the program. |
||||||
|
:return: Returns the arguments for the program. |
||||||
|
""" |
||||||
|
parser = argparse.ArgumentParser( |
||||||
|
description="Generates code for a Solidity interface from contract", |
||||||
|
usage=("slither-interface <ContractName> <source file or deployment address>"), |
||||||
|
) |
||||||
|
|
||||||
|
parser.add_argument( |
||||||
|
"contract_source", |
||||||
|
help="The name of the contract (case sensitive) followed by the deployed contract address if verified on etherscan or project directory/filename for local contracts.", |
||||||
|
nargs="+", |
||||||
|
) |
||||||
|
|
||||||
|
parser.add_argument( |
||||||
|
"--unroll-structs", |
||||||
|
help="Whether to use structures' underlying types instead of the user-defined type", |
||||||
|
default=False, |
||||||
|
action="store_true", |
||||||
|
) |
||||||
|
|
||||||
|
parser.add_argument( |
||||||
|
"--exclude-events", |
||||||
|
help="Excludes event signatures in the interface", |
||||||
|
default=False, |
||||||
|
action="store_true", |
||||||
|
) |
||||||
|
|
||||||
|
parser.add_argument( |
||||||
|
"--exclude-errors", |
||||||
|
help="Excludes custom error signatures in the interface", |
||||||
|
default=False, |
||||||
|
action="store_true", |
||||||
|
) |
||||||
|
|
||||||
|
parser.add_argument( |
||||||
|
"--exclude-enums", |
||||||
|
help="Excludes enum definitions in the interface", |
||||||
|
default=False, |
||||||
|
action="store_true", |
||||||
|
) |
||||||
|
|
||||||
|
parser.add_argument( |
||||||
|
"--exclude-structs", |
||||||
|
help="Exclude struct definitions in the interface", |
||||||
|
default=False, |
||||||
|
action="store_true", |
||||||
|
) |
||||||
|
|
||||||
|
cryticparser.init(parser) |
||||||
|
|
||||||
|
return parser.parse_args() |
||||||
|
|
||||||
|
|
||||||
|
def main() -> None: |
||||||
|
args = parse_args() |
||||||
|
|
||||||
|
contract_name, target = args.contract_source |
||||||
|
slither = Slither(target, **vars(args)) |
||||||
|
|
||||||
|
_contract = slither.get_contract_from_name(contract_name)[0] |
||||||
|
|
||||||
|
interface = generate_interface( |
||||||
|
contract=_contract, |
||||||
|
unroll_structs=args.unroll_structs, |
||||||
|
include_events=not args.exclude_events, |
||||||
|
include_errors=not args.exclude_errors, |
||||||
|
include_enums=not args.exclude_enums, |
||||||
|
include_structs=not args.exclude_structs, |
||||||
|
) |
||||||
|
|
||||||
|
# add version pragma |
||||||
|
interface = ( |
||||||
|
f"pragma solidity {_contract.compilation_unit.pragma_directives[0].version};\n\n" |
||||||
|
+ interface |
||||||
|
) |
||||||
|
|
||||||
|
# write interface to file |
||||||
|
export = Path("crytic-export", "interfaces") |
||||||
|
export.mkdir(parents=True, exist_ok=True) |
||||||
|
filename = f"I{contract_name}.sol" |
||||||
|
path = Path(export, filename) |
||||||
|
logger.info(f" Interface exported to {path}") |
||||||
|
with open(path, "w", encoding="utf8") as f: |
||||||
|
f.write(interface) |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
main() |
@ -1 +1 @@ |
|||||||
from .read_storage import SlitherReadStorage |
from .read_storage import SlitherReadStorage, RpcInfo |
||||||
|
@ -0,0 +1,15 @@ |
|||||||
|
EncodePackedCollision.bad4(bytes,bytes) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#34-36) calls abi.encodePacked() with multiple dynamic arguments: |
||||||
|
- packed = abi.encodePacked(a,a2,a3,a) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#35) |
||||||
|
|
||||||
|
EncodePackedCollision.bad2(string,uint256[]) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#24-26) calls abi.encodePacked() with multiple dynamic arguments: |
||||||
|
- packed = abi.encodePacked(stra,arra) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#25) |
||||||
|
|
||||||
|
EncodePackedCollision.bad3_get_hash_for_signature(string,string) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#29-31) calls abi.encodePacked() with multiple dynamic arguments: |
||||||
|
- keccak256(bytes)(abi.encodePacked(name,doc)) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#30) |
||||||
|
|
||||||
|
EncodePackedCollision.bad0(string,string) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#14-16) calls abi.encodePacked() with multiple dynamic arguments: |
||||||
|
- packed = abi.encodePacked(stra,strb) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#15) |
||||||
|
|
||||||
|
EncodePackedCollision.bad1(string,bytes) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#19-21) calls abi.encodePacked() with multiple dynamic arguments: |
||||||
|
- packed = abi.encodePacked(stra,bytesa) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#20) |
||||||
|
|
@ -0,0 +1,24 @@ |
|||||||
|
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#84 is incorrect - no matching function for bytes17[] found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||||
|
|
||||||
|
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#85 is incorrect - no matching function for uint256 found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||||
|
|
||||||
|
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#90 is incorrect - no matching function for mapping(int256 => uint128) found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||||
|
|
||||||
|
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#86 is incorrect - no matching function for int256 found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||||
|
|
||||||
|
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#89 is incorrect - no matching function for E2 found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||||
|
|
||||||
|
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#93 is incorrect - no matching function for bytes[][] found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||||
|
|
||||||
|
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#92 is incorrect - no matching function for string[][] found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||||
|
|
||||||
|
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#91 is incorrect - no matching function for mapping(int128 => uint256) found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||||
|
|
||||||
|
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#87 is incorrect - no matching function for bytes18 found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||||
|
|
||||||
|
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#88 is incorrect - no matching function for S2 found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||||
|
|
||||||
|
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#83 is incorrect - no matching function for C3 found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||||
|
|
||||||
|
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#94 is incorrect - no matching function for custom_int found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||||
|
|
@ -1,5 +1,10 @@ |
|||||||
Contract locking ether found: |
Contract locking ether found: |
||||||
Contract OnlyLocked (tests/e2e/detectors/test_data/locked-ether/0.4.25/locked_ether.sol#26) has payable functions: |
Contract OnlyLocked (tests/e2e/detectors/test_data/locked-ether/0.4.25/locked_ether.sol#37) has payable functions: |
||||||
|
- Locked.receive() (tests/e2e/detectors/test_data/locked-ether/0.4.25/locked_ether.sol#4-6) |
||||||
|
But does not have a function to withdraw the ether |
||||||
|
|
||||||
|
Contract locking ether found: |
||||||
|
Contract UnlockedAssembly (tests/e2e/detectors/test_data/locked-ether/0.4.25/locked_ether.sol#27-35) has payable functions: |
||||||
- Locked.receive() (tests/e2e/detectors/test_data/locked-ether/0.4.25/locked_ether.sol#4-6) |
- Locked.receive() (tests/e2e/detectors/test_data/locked-ether/0.4.25/locked_ether.sol#4-6) |
||||||
But does not have a function to withdraw the ether |
But does not have a function to withdraw the ether |
||||||
|
|
||||||
|
@ -1,5 +1,10 @@ |
|||||||
Contract locking ether found: |
Contract locking ether found: |
||||||
Contract OnlyLocked (tests/e2e/detectors/test_data/locked-ether/0.5.16/locked_ether.sol#26) has payable functions: |
Contract OnlyLocked (tests/e2e/detectors/test_data/locked-ether/0.5.16/locked_ether.sol#37) has payable functions: |
||||||
|
- Locked.receive() (tests/e2e/detectors/test_data/locked-ether/0.5.16/locked_ether.sol#4-6) |
||||||
|
But does not have a function to withdraw the ether |
||||||
|
|
||||||
|
Contract locking ether found: |
||||||
|
Contract UnlockedAssembly (tests/e2e/detectors/test_data/locked-ether/0.5.16/locked_ether.sol#27-35) has payable functions: |
||||||
- Locked.receive() (tests/e2e/detectors/test_data/locked-ether/0.5.16/locked_ether.sol#4-6) |
- Locked.receive() (tests/e2e/detectors/test_data/locked-ether/0.5.16/locked_ether.sol#4-6) |
||||||
But does not have a function to withdraw the ether |
But does not have a function to withdraw the ether |
||||||
|
|
||||||
|
@ -1,5 +1,5 @@ |
|||||||
Contract locking ether found: |
Contract locking ether found: |
||||||
Contract OnlyLocked (tests/e2e/detectors/test_data/locked-ether/0.6.11/locked_ether.sol#26) has payable functions: |
Contract OnlyLocked (tests/e2e/detectors/test_data/locked-ether/0.6.11/locked_ether.sol#36) has payable functions: |
||||||
- Locked.receive_eth() (tests/e2e/detectors/test_data/locked-ether/0.6.11/locked_ether.sol#4-6) |
- Locked.receive_eth() (tests/e2e/detectors/test_data/locked-ether/0.6.11/locked_ether.sol#4-6) |
||||||
But does not have a function to withdraw the ether |
But does not have a function to withdraw the ether |
||||||
|
|
||||||
|
@ -1,5 +1,5 @@ |
|||||||
Contract locking ether found: |
Contract locking ether found: |
||||||
Contract OnlyLocked (tests/e2e/detectors/test_data/locked-ether/0.7.6/locked_ether.sol#26) has payable functions: |
Contract OnlyLocked (tests/e2e/detectors/test_data/locked-ether/0.7.6/locked_ether.sol#36) has payable functions: |
||||||
- Locked.receive_eth() (tests/e2e/detectors/test_data/locked-ether/0.7.6/locked_ether.sol#4-6) |
- Locked.receive_eth() (tests/e2e/detectors/test_data/locked-ether/0.7.6/locked_ether.sol#4-6) |
||||||
But does not have a function to withdraw the ether |
But does not have a function to withdraw the ether |
||||||
|
|
||||||
|
@ -1,2 +1,2 @@ |
|||||||
C.f() (tests/e2e/detectors/test_data/incorrect-shift/0.7.6/shift_parameter_mixup.sol#3-7) contains an incorrect shift operation: a = 8 >> a (tests/e2e/detectors/test_data/incorrect-shift/0.7.6/shift_parameter_mixup.sol#5) |
C.f() (tests/e2e/detectors/test_data/incorrect-shift/0.7.6/shift_parameter_mixup.sol#3-8) contains an incorrect shift operation: a = 8 >> a (tests/e2e/detectors/test_data/incorrect-shift/0.7.6/shift_parameter_mixup.sol#5) |
||||||
|
|
||||||
|
@ -1,2 +1,2 @@ |
|||||||
Uninitialized.func().uint_not_init (tests/e2e/detectors/test_data/uninitialized-local/0.6.11/uninitialized_local_variable.sol#4) is a local variable never initialized |
Uninitialized.func().uint_not_init (tests/e2e/detectors/test_data/uninitialized-local/0.6.11/uninitialized_local_variable.sol#8) is a local variable never initialized |
||||||
|
|
||||||
|
@ -1,2 +1,2 @@ |
|||||||
Uninitialized.func().uint_not_init (tests/e2e/detectors/test_data/uninitialized-local/0.7.6/uninitialized_local_variable.sol#4) is a local variable never initialized |
Uninitialized.func().uint_not_init (tests/e2e/detectors/test_data/uninitialized-local/0.7.6/uninitialized_local_variable.sol#8) is a local variable never initialized |
||||||
|
|
||||||
|
@ -1,4 +1,8 @@ |
|||||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#17-29) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#18) |
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#18-37) ignores return value by t.g() (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#31) |
||||||
|
|
||||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#17-29) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#22) |
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#18-37) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#19) |
||||||
|
|
||||||
|
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#18-37) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#23) |
||||||
|
|
||||||
|
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#18-37) ignores return value by (e) = t.g() (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#36) |
||||||
|
|
||||||
|
@ -1,4 +1,8 @@ |
|||||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#17-29) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#18) |
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#18-37) ignores return value by (e) = t.g() (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#36) |
||||||
|
|
||||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#17-29) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#22) |
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#18-37) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#23) |
||||||
|
|
||||||
|
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#18-37) ignores return value by t.g() (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#31) |
||||||
|
|
||||||
|
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#18-37) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#19) |
||||||
|
|
||||||
|
@ -1,4 +1,8 @@ |
|||||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#17-29) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#22) |
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#18-37) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#19) |
||||||
|
|
||||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#17-29) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#18) |
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#18-37) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#23) |
||||||
|
|
||||||
|
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#18-37) ignores return value by t.g() (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#31) |
||||||
|
|
||||||
|
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#18-37) ignores return value by (e) = t.g() (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#36) |
||||||
|
|
||||||
|
@ -1,4 +1,8 @@ |
|||||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#17-29) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#22) |
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#18-37) ignores return value by t.g() (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#31) |
||||||
|
|
||||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#17-29) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#18) |
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#18-37) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#23) |
||||||
|
|
||||||
|
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#18-37) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#19) |
||||||
|
|
||||||
|
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#18-37) ignores return value by (e) = t.g() (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#36) |
||||||
|
|
||||||
|
@ -0,0 +1,78 @@ |
|||||||
|
contract ABIencodePacked{ |
||||||
|
|
||||||
|
uint a; |
||||||
|
string str1 = "a"; |
||||||
|
string str2 = "bc"; |
||||||
|
bytes _bytes = "hello world"; |
||||||
|
uint[] arr; |
||||||
|
uint[2] arr2; |
||||||
|
string[3] str_arr3; /* This nested dynamic type is not supported in abi.encodePacked mode by solc */ |
||||||
|
string[] str_array; /* This nested dynamic type is not supported in abi.encodePacked mode by solc */ |
||||||
|
bytes[] bytes_array; /* This nested dynamic type and tuples are not supported in abi.encodePacked mode by solc */ |
||||||
|
|
||||||
|
/* Two dynamic types */ |
||||||
|
function bad0(string calldata stra, string calldata strb) external{ |
||||||
|
bytes memory packed = abi.encodePacked(stra, strb); |
||||||
|
} |
||||||
|
|
||||||
|
/* Two dynamic types */ |
||||||
|
function bad1(string calldata stra, bytes calldata bytesa) external{ |
||||||
|
bytes memory packed = abi.encodePacked(stra, bytesa); |
||||||
|
} |
||||||
|
|
||||||
|
/* Two dynamic types */ |
||||||
|
function bad2(string calldata stra, uint[] calldata arra) external{ |
||||||
|
bytes memory packed = abi.encodePacked(stra, arra); |
||||||
|
} |
||||||
|
|
||||||
|
/* Two dynamic types */ |
||||||
|
function bad3_get_hash_for_signature(string calldata name, string calldata doc) external returns (bytes32) { |
||||||
|
return keccak256(abi.encodePacked(name, doc)); |
||||||
|
} |
||||||
|
|
||||||
|
/* Two dynamic types between non dynamic types */ |
||||||
|
function bad4(bytes calldata a2, bytes calldata a3) external { |
||||||
|
bytes memory packed = abi.encodePacked(a, a2, a3, a); |
||||||
|
} |
||||||
|
|
||||||
|
/* Two dynamic types but static values*/ |
||||||
|
function good0() external{ |
||||||
|
bytes memory packed = abi.encodePacked(str1, str2); |
||||||
|
} |
||||||
|
|
||||||
|
/* Two dynamic types but static values*/ |
||||||
|
function good1() external{ |
||||||
|
bytes memory packed = abi.encodePacked(str1, _bytes); |
||||||
|
} |
||||||
|
|
||||||
|
/* Two dynamic types but static values*/ |
||||||
|
function good2() external{ |
||||||
|
bytes memory packed = abi.encodePacked(str1, arr); |
||||||
|
} |
||||||
|
|
||||||
|
/* No dynamic types */ |
||||||
|
function good3() external{ |
||||||
|
bytes memory packed = abi.encodePacked(a); |
||||||
|
} |
||||||
|
|
||||||
|
/* One dynamic type */ |
||||||
|
function good4() external{ |
||||||
|
bytes memory packed = abi.encodePacked(str1); |
||||||
|
} |
||||||
|
|
||||||
|
/* One dynamic type */ |
||||||
|
function good5() external{ |
||||||
|
bytes memory packed = abi.encodePacked(a, str1); |
||||||
|
} |
||||||
|
|
||||||
|
/* One dynamic type */ |
||||||
|
function good6() external{ |
||||||
|
bytes memory packed = abi.encodePacked(str1, arr2); |
||||||
|
} |
||||||
|
|
||||||
|
/* Two dynamic types but not consecutive*/ |
||||||
|
function good7(string calldata a, uint b, string calldata c) external{ |
||||||
|
bytes memory packed = abi.encodePacked(a, b, c); |
||||||
|
} |
||||||
|
} |
||||||
|
|
Binary file not shown.
Binary file not shown.
@ -1,8 +1,9 @@ |
|||||||
contract C { |
contract C { |
||||||
|
|
||||||
function f() internal returns (uint a) { |
function f() internal returns (uint a, uint b) { |
||||||
assembly { |
assembly { |
||||||
a := shr(a, 8) |
a := shr(a, 8) |
||||||
|
b := shl(248, 0xff) |
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
Binary file not shown.
@ -0,0 +1,94 @@ |
|||||||
|
pragma solidity 0.8.17; |
||||||
|
|
||||||
|
struct S1 |
||||||
|
{ |
||||||
|
uint __; |
||||||
|
} |
||||||
|
|
||||||
|
struct S2 |
||||||
|
{ |
||||||
|
uint128 __; |
||||||
|
} |
||||||
|
|
||||||
|
enum E1 |
||||||
|
{ |
||||||
|
A, |
||||||
|
B |
||||||
|
} |
||||||
|
|
||||||
|
enum E2 |
||||||
|
{ |
||||||
|
A, |
||||||
|
B |
||||||
|
} |
||||||
|
|
||||||
|
contract C0 |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
contract C1 is C0 |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
contract C2 is C1 |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
contract C3 |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
type custom_uint is uint248; |
||||||
|
type custom_int is int248; |
||||||
|
|
||||||
|
library L |
||||||
|
{ |
||||||
|
function f0(C0) public pure {} |
||||||
|
function f1(bool) public pure {} |
||||||
|
function f2(string memory) public pure {} |
||||||
|
function f3(bytes memory) public pure {} |
||||||
|
function f4(uint248) public pure {} |
||||||
|
function f5(int248) public pure {} |
||||||
|
function f6(address) public pure {} |
||||||
|
function f7(bytes17) public pure {} |
||||||
|
function f8(S1 memory) public pure {} |
||||||
|
function f9(E1) public pure {} |
||||||
|
function f10(mapping(int => uint) storage) public pure {} |
||||||
|
function f11(string[] memory) public pure {} |
||||||
|
function f12(bytes[][][] memory) public pure {} |
||||||
|
function f13(custom_uint) public pure {} |
||||||
|
} |
||||||
|
|
||||||
|
// the following statements are correct |
||||||
|
using L for C2; |
||||||
|
using L for bool; |
||||||
|
using L for string; |
||||||
|
using L for bytes; |
||||||
|
using L for uint240; |
||||||
|
using L for int16; |
||||||
|
using L for address; |
||||||
|
using L for bytes16; |
||||||
|
using L for S1; |
||||||
|
using L for E1; |
||||||
|
using L for mapping(int => uint); |
||||||
|
using L for string[]; |
||||||
|
using L for bytes[][][]; |
||||||
|
using L for custom_uint; |
||||||
|
|
||||||
|
// the following statements are incorrect |
||||||
|
using L for C3; |
||||||
|
using L for bytes17[]; |
||||||
|
using L for uint; |
||||||
|
using L for int; |
||||||
|
using L for bytes18; |
||||||
|
using L for S2; |
||||||
|
using L for E2; |
||||||
|
using L for mapping(int => uint128); |
||||||
|
using L for mapping(int128 => uint); |
||||||
|
using L for string[][]; |
||||||
|
using L for bytes[][]; |
||||||
|
using L for custom_int; |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue