slither.utils.type

  1import math
  2from typing import List, Union, Set
  3
  4from slither.core.solidity_types import (
  5    ArrayType,
  6    MappingType,
  7    ElementaryType,
  8    UserDefinedType,
  9    TypeAlias,
 10)
 11from slither.core.solidity_types.type import Type
 12from slither.core.variables.variable import Variable
 13
 14
 15def _convert_type_for_solidity_signature_to_string(
 16    types: Union[Type, List[Type]], seen: Set[Type]
 17) -> str:
 18    if isinstance(types, Type):
 19        # Array might be struct, so we need to go again through the conversion here
 20        # We could have this logic in convert_type_for_solidity_signature
 21        # But the slither type system is not straightforward to manipulate here
 22        # And it would require to create a new ArrayType, which a potential List[Type] as input
 23        # Which is currently not supported. This comes down to (uint, uint)[] not being possible in Solidity
 24        # While having an array of a struct of two uint leads to a (uint, uint)[] signature
 25        if isinstance(types, ArrayType):
 26            underlying_type = convert_type_for_solidity_signature(types.type, seen)
 27            underlying_type_str = _convert_type_for_solidity_signature_to_string(
 28                underlying_type, seen
 29            )
 30            return underlying_type_str + "[]"
 31
 32        return str(types)
 33
 34    first_item = True
 35
 36    ret = "("
 37    for underlying_type in types:
 38        if first_item:
 39            ret += _convert_type_for_solidity_signature_to_string(underlying_type, seen)
 40        else:
 41            ret += "," + _convert_type_for_solidity_signature_to_string(underlying_type, seen)
 42        first_item = False
 43
 44    ret += ")"
 45    return ret
 46
 47
 48def convert_type_for_solidity_signature_to_string(t: Type) -> str:
 49    seen: Set[Type] = set()
 50    types = convert_type_for_solidity_signature(t, seen)
 51    return _convert_type_for_solidity_signature_to_string(types, seen)
 52
 53
 54def convert_type_for_solidity_signature(t: Type, seen: Set[Type]) -> Union[Type, List[Type]]:
 55    # pylint: disable=import-outside-toplevel
 56    from slither.core.declarations import Contract, Enum, Structure
 57
 58    # Solidity allows recursive type for structure definition if its not used in public /external
 59    # When this happens we can reach an infinite loop. If we detect a loop, we just stop converting the underlying type
 60    # This is ok, because it wont happen for public/external function
 61    #
 62    # contract A{
 63    #
 64    #   struct St{
 65    #       St[] a;
 66    #       uint b;
 67    #   }
 68    #
 69    #    function f(St memory s) internal{}
 70    #
 71    # }
 72    if t in seen:
 73        return t
 74    seen.add(t)
 75
 76    if isinstance(t, UserDefinedType):
 77        underlying_type = t.type
 78        if isinstance(underlying_type, Contract):
 79            return ElementaryType("address")
 80        if isinstance(underlying_type, Enum):
 81            number_values = len(underlying_type.values)
 82            # IF below 65536, avoid calling log2
 83            if number_values <= 256:
 84                uint = "8"
 85            elif number_values <= 65536:
 86                uint = "16"
 87            else:
 88                uint = str(int(math.log2(number_values)))
 89            return ElementaryType(f"uint{uint}")
 90        if isinstance(underlying_type, Structure):
 91            # We can't have recursive types for structure, so recursion is ok here
 92            types = [
 93                convert_type_for_solidity_signature(x.type, seen)
 94                for x in underlying_type.elems_ordered
 95            ]
 96            return types
 97
 98    if isinstance(t, TypeAlias):
 99        return t.type
100
101    return t
102
103
104def _export_nested_types_from_variable(
105    current_type: Type, ret: List[Type], seen: Set[Type]
106) -> None:
107    """
108    Export the list of nested types (mapping/array)
109    :param variable:
110    :return: list(Type)
111    """
112    if isinstance(current_type, MappingType):
113        underlying_type = convert_type_for_solidity_signature(current_type.type_from, seen)
114        if isinstance(underlying_type, list):
115            ret.extend(underlying_type)
116        else:
117            ret.append(underlying_type)
118        next_type = current_type.type_to
119
120    elif isinstance(current_type, ArrayType):
121        ret.append(ElementaryType("uint256"))
122        next_type = current_type.type
123    else:
124        return
125    _export_nested_types_from_variable(next_type, ret, seen)
126
127
128def export_nested_types_from_variable(variable: Variable) -> List[Type]:
129    """
130    Export the list of nested types (mapping/array)
131    :param variable:
132    :return: list(Type)
133    """
134    l: List[Type] = []
135    seen: Set[Type] = set()
136    _export_nested_types_from_variable(variable.type, l, seen)
137    return l
138
139
140def _export_return_type_from_variable(underlying_type: Type, all_types: bool) -> List[Type]:
141    # pylint: disable=import-outside-toplevel
142    from slither.core.declarations import Structure
143
144    if isinstance(underlying_type, MappingType):
145        if not all_types:
146            return []
147        return export_return_type_from_variable(underlying_type.type_to)
148
149    if isinstance(underlying_type, ArrayType):
150        if not all_types:
151            return []
152        return export_return_type_from_variable(underlying_type.type)
153
154    if isinstance(underlying_type, UserDefinedType) and isinstance(underlying_type.type, Structure):
155        ret = []
156        for r in underlying_type.type.elems_ordered:
157            ret += export_return_type_from_variable(r, all_types=False)
158
159        return ret
160
161    return [underlying_type]
162
163
164def export_return_type_from_variable(
165    variable_or_type: Union[Type, Variable], all_types: bool = True
166) -> List[Type]:
167    """
168    Return the type returned by a variable.
169    If all_types set to false, filter array/mapping. This is useful as the mapping/array in a structure are not
170    returned by solidity
171    :param variable_or_type
172    :param all_types
173    :return: Type
174    """
175    # pylint: disable=import-outside-toplevel
176    from slither.core.declarations import Structure
177
178    if isinstance(variable_or_type, Type):
179        return _export_return_type_from_variable(variable_or_type, all_types)
180
181    if isinstance(variable_or_type.type, MappingType):
182        if not all_types:
183            return []
184        return export_return_type_from_variable(variable_or_type.type.type_to)
185
186    if isinstance(variable_or_type.type, ArrayType):
187        if not all_types:
188            return []
189        return export_return_type_from_variable(variable_or_type.type.type)
190
191    if isinstance(variable_or_type.type, UserDefinedType) and isinstance(
192        variable_or_type.type.type, Structure
193    ):
194        ret = []
195        for r in variable_or_type.type.type.elems_ordered:
196            ret += export_return_type_from_variable(r, all_types=False)
197        return ret
198
199    return [variable_or_type.type]
200
201
202def is_underlying_type_address(t: "Type") -> bool:
203    """
204    Return true if the underlying type is an address
205    i.e. if the type is an address or a contract
206    """
207    # pylint: disable=import-outside-toplevel
208    from slither.core.declarations.contract import Contract
209
210    if t == ElementaryType("address"):
211        return True
212    if isinstance(t, UserDefinedType) and isinstance(t.type, Contract):
213        return True
214    return False
def convert_type_for_solidity_signature_to_string(t: slither.core.solidity_types.type.Type) -> str:
49def convert_type_for_solidity_signature_to_string(t: Type) -> str:
50    seen: Set[Type] = set()
51    types = convert_type_for_solidity_signature(t, seen)
52    return _convert_type_for_solidity_signature_to_string(types, seen)
 55def convert_type_for_solidity_signature(t: Type, seen: Set[Type]) -> Union[Type, List[Type]]:
 56    # pylint: disable=import-outside-toplevel
 57    from slither.core.declarations import Contract, Enum, Structure
 58
 59    # Solidity allows recursive type for structure definition if its not used in public /external
 60    # When this happens we can reach an infinite loop. If we detect a loop, we just stop converting the underlying type
 61    # This is ok, because it wont happen for public/external function
 62    #
 63    # contract A{
 64    #
 65    #   struct St{
 66    #       St[] a;
 67    #       uint b;
 68    #   }
 69    #
 70    #    function f(St memory s) internal{}
 71    #
 72    # }
 73    if t in seen:
 74        return t
 75    seen.add(t)
 76
 77    if isinstance(t, UserDefinedType):
 78        underlying_type = t.type
 79        if isinstance(underlying_type, Contract):
 80            return ElementaryType("address")
 81        if isinstance(underlying_type, Enum):
 82            number_values = len(underlying_type.values)
 83            # IF below 65536, avoid calling log2
 84            if number_values <= 256:
 85                uint = "8"
 86            elif number_values <= 65536:
 87                uint = "16"
 88            else:
 89                uint = str(int(math.log2(number_values)))
 90            return ElementaryType(f"uint{uint}")
 91        if isinstance(underlying_type, Structure):
 92            # We can't have recursive types for structure, so recursion is ok here
 93            types = [
 94                convert_type_for_solidity_signature(x.type, seen)
 95                for x in underlying_type.elems_ordered
 96            ]
 97            return types
 98
 99    if isinstance(t, TypeAlias):
100        return t.type
101
102    return t
def export_nested_types_from_variable( variable: slither.core.variables.variable.Variable) -> List[slither.core.solidity_types.type.Type]:
129def export_nested_types_from_variable(variable: Variable) -> List[Type]:
130    """
131    Export the list of nested types (mapping/array)
132    :param variable:
133    :return: list(Type)
134    """
135    l: List[Type] = []
136    seen: Set[Type] = set()
137    _export_nested_types_from_variable(variable.type, l, seen)
138    return l

Export the list of nested types (mapping/array)

Parameters
  • variable:
Returns

list(Type)

def export_return_type_from_variable( variable_or_type: Union[slither.core.solidity_types.type.Type, slither.core.variables.variable.Variable], all_types: bool = True) -> List[slither.core.solidity_types.type.Type]:
165def export_return_type_from_variable(
166    variable_or_type: Union[Type, Variable], all_types: bool = True
167) -> List[Type]:
168    """
169    Return the type returned by a variable.
170    If all_types set to false, filter array/mapping. This is useful as the mapping/array in a structure are not
171    returned by solidity
172    :param variable_or_type
173    :param all_types
174    :return: Type
175    """
176    # pylint: disable=import-outside-toplevel
177    from slither.core.declarations import Structure
178
179    if isinstance(variable_or_type, Type):
180        return _export_return_type_from_variable(variable_or_type, all_types)
181
182    if isinstance(variable_or_type.type, MappingType):
183        if not all_types:
184            return []
185        return export_return_type_from_variable(variable_or_type.type.type_to)
186
187    if isinstance(variable_or_type.type, ArrayType):
188        if not all_types:
189            return []
190        return export_return_type_from_variable(variable_or_type.type.type)
191
192    if isinstance(variable_or_type.type, UserDefinedType) and isinstance(
193        variable_or_type.type.type, Structure
194    ):
195        ret = []
196        for r in variable_or_type.type.type.elems_ordered:
197            ret += export_return_type_from_variable(r, all_types=False)
198        return ret
199
200    return [variable_or_type.type]

Return the type returned by a variable. If all_types set to false, filter array/mapping. This is useful as the mapping/array in a structure are not returned by solidity :param variable_or_type :param all_types

Returns

Type

def is_underlying_type_address(t: slither.core.solidity_types.type.Type) -> bool:
203def is_underlying_type_address(t: "Type") -> bool:
204    """
205    Return true if the underlying type is an address
206    i.e. if the type is an address or a contract
207    """
208    # pylint: disable=import-outside-toplevel
209    from slither.core.declarations.contract import Contract
210
211    if t == ElementaryType("address"):
212        return True
213    if isinstance(t, UserDefinedType) and isinstance(t.type, Contract):
214        return True
215    return False

Return true if the underlying type is an address i.e. if the type is an address or a contract