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( t: slither.core.solidity_types.type.Type, seen: Set[slither.core.solidity_types.type.Type]) -> Union[slither.core.solidity_types.type.Type, List[slither.core.solidity_types.type.Type]]:
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
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