slither.core.declarations.contract
" Contract module
1"""" 2 Contract module 3""" 4import logging 5from collections import defaultdict 6from pathlib import Path 7from typing import Optional, List, Dict, Callable, Tuple, TYPE_CHECKING, Union, Set, Any 8 9from crytic_compile.platform import Type as PlatformType 10 11from slither.core.cfg.scope import Scope 12from slither.core.source_mapping.source_mapping import SourceMapping 13from slither.utils.using_for import USING_FOR, merge_using_for 14from slither.core.declarations.function import Function, FunctionType, FunctionLanguage 15from slither.utils.erc import ( 16 ERC20_signatures, 17 ERC165_signatures, 18 ERC223_signatures, 19 ERC721_signatures, 20 ERC1820_signatures, 21 ERC777_signatures, 22 ERC1155_signatures, 23 ERC2612_signatures, 24 ERC1363_signatures, 25 ERC4524_signatures, 26 ERC4626_signatures, 27) 28from slither.utils.tests_pattern import is_test_contract 29 30# pylint: disable=too-many-lines,too-many-instance-attributes,import-outside-toplevel,too-many-nested-blocks 31if TYPE_CHECKING: 32 from slither.utils.type_helpers import LibraryCallType, HighLevelCallType, InternalCallType 33 from slither.core.declarations import ( 34 Enum, 35 EventContract, 36 Modifier, 37 EnumContract, 38 StructureContract, 39 FunctionContract, 40 CustomErrorContract, 41 ) 42 from slither.slithir.variables.variable import SlithIRVariable 43 from slither.core.variables import Variable, StateVariable 44 from slither.core.compilation_unit import SlitherCompilationUnit 45 from slither.core.scope.scope import FileScope 46 from slither.core.cfg.node import Node 47 from slither.core.solidity_types import TypeAliasContract 48 49 50LOGGER = logging.getLogger("Contract") 51 52 53class Contract(SourceMapping): # pylint: disable=too-many-public-methods 54 """ 55 Contract class 56 """ 57 58 def __init__(self, compilation_unit: "SlitherCompilationUnit", scope: "FileScope") -> None: 59 super().__init__() 60 61 self._name: Optional[str] = None 62 self._id: Optional[int] = None 63 self._inheritance: List["Contract"] = [] # all contract inherited, c3 linearization 64 self._immediate_inheritance: List["Contract"] = [] # immediate inheritance 65 66 # Constructors called on contract's definition 67 # contract B is A(1) { .. 68 self._explicit_base_constructor_calls: List["Contract"] = [] 69 70 self._enums: Dict[str, "EnumContract"] = {} 71 self._structures: Dict[str, "StructureContract"] = {} 72 self._events: Dict[str, "EventContract"] = {} 73 # map accessible variable from name -> variable 74 # do not contain private variables inherited from contract 75 self._variables: Dict[str, "StateVariable"] = {} 76 self._variables_ordered: List["StateVariable"] = [] 77 # Reference id -> variable declaration (only available for compact AST) 78 self._state_variables_by_ref_id: Dict[int, "StateVariable"] = {} 79 self._modifiers: Dict[str, "Modifier"] = {} 80 self._functions: Dict[str, "FunctionContract"] = {} 81 self._linearizedBaseContracts: List[int] = [] 82 self._custom_errors: Dict[str, "CustomErrorContract"] = {} 83 self._type_aliases: Dict[str, "TypeAliasContract"] = {} 84 85 # The only str is "*" 86 self._using_for: USING_FOR = {} 87 self._using_for_complete: Optional[USING_FOR] = None 88 self._kind: Optional[str] = None 89 self._is_interface: bool = False 90 self._is_library: bool = False 91 self._is_fully_implemented: bool = False 92 self._is_abstract: bool = False 93 94 self._signatures: Optional[List[str]] = None 95 self._signatures_declared: Optional[List[str]] = None 96 97 self._fallback_function: Optional["FunctionContract"] = None 98 self._receive_function: Optional["FunctionContract"] = None 99 100 self._is_upgradeable: Optional[bool] = None 101 self._is_upgradeable_proxy: Optional[bool] = None 102 self._upgradeable_version: Optional[str] = None 103 104 self._initial_state_variables: List["StateVariable"] = [] # ssa 105 106 self._is_incorrectly_parsed: bool = False 107 108 self._available_functions_as_dict: Optional[Dict[str, "Function"]] = None 109 self._all_functions_called: Optional[List["InternalCallType"]] = None 110 111 self.compilation_unit: "SlitherCompilationUnit" = compilation_unit 112 self.file_scope: "FileScope" = scope 113 114 # memoize 115 self._state_variables_used_in_reentrant_targets: Optional[ 116 Dict["StateVariable", Set[Union["StateVariable", "Function"]]] 117 ] = None 118 119 self._comments: Optional[str] = None 120 121 ################################################################################### 122 ################################################################################### 123 # region General's properties 124 ################################################################################### 125 ################################################################################### 126 127 @property 128 def name(self) -> str: 129 """str: Name of the contract.""" 130 assert self._name 131 return self._name 132 133 @name.setter 134 def name(self, name: str) -> None: 135 self._name = name 136 137 @property 138 def id(self) -> int: 139 """Unique id.""" 140 assert self._id is not None 141 return self._id 142 143 @id.setter 144 def id(self, new_id: int) -> None: 145 """Unique id.""" 146 self._id = new_id 147 148 @property 149 def contract_kind(self) -> Optional[str]: 150 """ 151 contract_kind can be None if the legacy ast format is used 152 :return: 153 """ 154 return self._kind 155 156 @contract_kind.setter 157 def contract_kind(self, kind: str) -> None: 158 self._kind = kind 159 160 @property 161 def is_interface(self) -> bool: 162 return self._is_interface 163 164 @is_interface.setter 165 def is_interface(self, is_interface: bool) -> None: 166 self._is_interface = is_interface 167 168 @property 169 def is_library(self) -> bool: 170 return self._is_library 171 172 @is_library.setter 173 def is_library(self, is_library: bool) -> None: 174 self._is_library = is_library 175 176 @property 177 def comments(self) -> Optional[str]: 178 """ 179 Return the comments associated with the contract. 180 181 When using comments, avoid strict text matching, as the solc behavior might change. 182 For example, for old solc version, the first space after the * is not kept, i.e: 183 184 * @title Test Contract 185 * @dev Test comment 186 187 Returns 188 - " @title Test Contract\n @dev Test comment" for newest versions 189 - "@title Test Contract\n@dev Test comment" for older versions 190 191 192 Returns: 193 the comment as a string 194 """ 195 return self._comments 196 197 @comments.setter 198 def comments(self, comments: str): 199 self._comments = comments 200 201 @property 202 def is_fully_implemented(self) -> bool: 203 """ 204 bool: True if the contract defines all functions. 205 In modern Solidity, virtual functions can lack an implementation. 206 Prior to Solidity 0.6.0, functions like the following would be not fully implemented: 207 ```solidity 208 contract ImplicitAbstract{ 209 function f() public; 210 } 211 ``` 212 """ 213 return self._is_fully_implemented 214 215 @is_fully_implemented.setter 216 def is_fully_implemented(self, is_fully_implemented: bool): 217 self._is_fully_implemented = is_fully_implemented 218 219 @property 220 def is_abstract(self) -> bool: 221 """ 222 Note for Solidity < 0.6.0 it will always be false 223 bool: True if the contract is abstract. 224 """ 225 return self._is_abstract 226 227 @is_abstract.setter 228 def is_abstract(self, is_abstract: bool): 229 self._is_abstract = is_abstract 230 231 # endregion 232 ################################################################################### 233 ################################################################################### 234 # region Structures 235 ################################################################################### 236 ################################################################################### 237 238 @property 239 def structures(self) -> List["StructureContract"]: 240 """ 241 list(Structure): List of the structures 242 """ 243 return list(self._structures.values()) 244 245 @property 246 def structures_inherited(self) -> List["StructureContract"]: 247 """ 248 list(Structure): List of the inherited structures 249 """ 250 return [s for s in self.structures if s.contract != self] 251 252 @property 253 def structures_declared(self) -> List["StructureContract"]: 254 """ 255 list(Structues): List of the structures declared within the contract (not inherited) 256 """ 257 return [s for s in self.structures if s.contract == self] 258 259 @property 260 def structures_as_dict(self) -> Dict[str, "StructureContract"]: 261 return self._structures 262 263 # endregion 264 ################################################################################### 265 ################################################################################### 266 # region Enums 267 ################################################################################### 268 ################################################################################### 269 270 @property 271 def enums(self) -> List["EnumContract"]: 272 return list(self._enums.values()) 273 274 @property 275 def enums_inherited(self) -> List["EnumContract"]: 276 """ 277 list(Enum): List of the inherited enums 278 """ 279 return [e for e in self.enums if e.contract != self] 280 281 @property 282 def enums_declared(self) -> List["EnumContract"]: 283 """ 284 list(Enum): List of the enums declared within the contract (not inherited) 285 """ 286 return [e for e in self.enums if e.contract == self] 287 288 @property 289 def enums_as_dict(self) -> Dict[str, "EnumContract"]: 290 return self._enums 291 292 # endregion 293 ################################################################################### 294 ################################################################################### 295 # region Events 296 ################################################################################### 297 ################################################################################### 298 299 @property 300 def events(self) -> List["EventContract"]: 301 """ 302 list(Event): List of the events 303 """ 304 return list(self._events.values()) 305 306 @property 307 def events_inherited(self) -> List["EventContract"]: 308 """ 309 list(Event): List of the inherited events 310 """ 311 return [e for e in self.events if e.contract != self] 312 313 @property 314 def events_declared(self) -> List["EventContract"]: 315 """ 316 list(Event): List of the events declared within the contract (not inherited) 317 """ 318 return [e for e in self.events if e.contract == self] 319 320 @property 321 def events_as_dict(self) -> Dict[str, "EventContract"]: 322 return self._events 323 324 # endregion 325 ################################################################################### 326 ################################################################################### 327 # region Using for 328 ################################################################################### 329 ################################################################################### 330 331 @property 332 def using_for(self) -> USING_FOR: 333 return self._using_for 334 335 @property 336 def using_for_complete(self) -> USING_FOR: 337 """ 338 USING_FOR: Dict of merged local using for directive with top level directive 339 """ 340 341 if self._using_for_complete is None: 342 result = self.using_for 343 top_level_using_for = self.file_scope.using_for_directives 344 for uftl in top_level_using_for: 345 result = merge_using_for(result, uftl.using_for) 346 self._using_for_complete = result 347 return self._using_for_complete 348 349 # endregion 350 ################################################################################### 351 ################################################################################### 352 # region Custom Errors 353 ################################################################################### 354 ################################################################################### 355 356 @property 357 def custom_errors(self) -> List["CustomErrorContract"]: 358 """ 359 list(CustomErrorContract): List of the contract's custom errors 360 """ 361 return list(self._custom_errors.values()) 362 363 @property 364 def custom_errors_inherited(self) -> List["CustomErrorContract"]: 365 """ 366 list(CustomErrorContract): List of the inherited custom errors 367 """ 368 return [s for s in self.custom_errors if s.contract != self] 369 370 @property 371 def custom_errors_declared(self) -> List["CustomErrorContract"]: 372 """ 373 list(CustomErrorContract): List of the custom errors declared within the contract (not inherited) 374 """ 375 return [s for s in self.custom_errors if s.contract == self] 376 377 @property 378 def custom_errors_as_dict(self) -> Dict[str, "CustomErrorContract"]: 379 return self._custom_errors 380 381 # endregion 382 ################################################################################### 383 ################################################################################### 384 # region Custom Errors 385 ################################################################################### 386 ################################################################################### 387 388 @property 389 def type_aliases(self) -> List["TypeAliasContract"]: 390 """ 391 list(TypeAliasContract): List of the contract's custom errors 392 """ 393 return list(self._type_aliases.values()) 394 395 @property 396 def type_aliases_inherited(self) -> List["TypeAliasContract"]: 397 """ 398 list(TypeAliasContract): List of the inherited custom errors 399 """ 400 return [s for s in self.type_aliases if s.contract != self] 401 402 @property 403 def type_aliases_declared(self) -> List["TypeAliasContract"]: 404 """ 405 list(TypeAliasContract): List of the custom errors declared within the contract (not inherited) 406 """ 407 return [s for s in self.type_aliases if s.contract == self] 408 409 @property 410 def type_aliases_as_dict(self) -> Dict[str, "TypeAliasContract"]: 411 return self._type_aliases 412 413 # endregion 414 ################################################################################### 415 ################################################################################### 416 # region Variables 417 ################################################################################### 418 ################################################################################### 419 @property 420 def state_variables_by_ref_id(self) -> Dict[int, "StateVariable"]: 421 """ 422 Returns the state variables by reference id (only available for compact AST). 423 """ 424 return self._state_variables_by_ref_id 425 426 @property 427 def variables(self) -> List["StateVariable"]: 428 """ 429 Returns all the accessible variables (do not include private variable from inherited contract) 430 431 list(StateVariable): List of the state variables. Alias to self.state_variables. 432 """ 433 return list(self.state_variables) 434 435 @property 436 def variables_as_dict(self) -> Dict[str, "StateVariable"]: 437 return self._variables 438 439 @property 440 def state_variables(self) -> List["StateVariable"]: 441 """ 442 Returns all the accessible variables (do not include private variable from inherited contract). 443 Use state_variables_ordered for all the variables following the storage order 444 445 list(StateVariable): List of the state variables. 446 """ 447 return list(self._variables.values()) 448 449 @property 450 def stored_state_variables(self) -> List["StateVariable"]: 451 """ 452 Returns state variables with storage locations, excluding private variables from inherited contracts. 453 Use stored_state_variables_ordered to access variables with storage locations in their declaration order. 454 455 This implementation filters out state variables if they are constant or immutable. It will be 456 updated to accommodate any new non-storage keywords that might replace 'constant' and 'immutable' in the future. 457 458 Returns: 459 List[StateVariable]: A list of state variables with storage locations. 460 """ 461 return [variable for variable in self.state_variables if variable.is_stored] 462 463 @property 464 def stored_state_variables_ordered(self) -> List["StateVariable"]: 465 """ 466 list(StateVariable): List of the state variables with storage locations by order of declaration. 467 468 This implementation filters out state variables if they are constant or immutable. It will be 469 updated to accommodate any new non-storage keywords that might replace 'constant' and 'immutable' in the future. 470 471 Returns: 472 List[StateVariable]: A list of state variables with storage locations ordered by declaration. 473 """ 474 return [variable for variable in self.state_variables_ordered if variable.is_stored] 475 476 @property 477 def state_variables_entry_points(self) -> List["StateVariable"]: 478 """ 479 list(StateVariable): List of the state variables that are public. 480 """ 481 return [var for var in self._variables.values() if var.visibility == "public"] 482 483 @property 484 def state_variables_ordered(self) -> List["StateVariable"]: 485 """ 486 list(StateVariable): List of the state variables by order of declaration. 487 """ 488 return list(self._variables_ordered) 489 490 def add_variables_ordered(self, new_vars: List["StateVariable"]) -> None: 491 self._variables_ordered += new_vars 492 493 @property 494 def state_variables_inherited(self) -> List["StateVariable"]: 495 """ 496 list(StateVariable): List of the inherited state variables 497 """ 498 return [s for s in self.state_variables if s.contract != self] 499 500 @property 501 def state_variables_declared(self) -> List["StateVariable"]: 502 """ 503 list(StateVariable): List of the state variables declared within the contract (not inherited) 504 """ 505 return [s for s in self.state_variables if s.contract == self] 506 507 @property 508 def slithir_variables(self) -> List["SlithIRVariable"]: 509 """ 510 List all of the slithir variables (non SSA) 511 """ 512 slithir_variabless = [f.slithir_variables for f in self.functions + self.modifiers] # type: ignore 513 slithir_variables = [item for sublist in slithir_variabless for item in sublist] 514 return list(set(slithir_variables)) 515 516 @property 517 def state_variables_used_in_reentrant_targets( 518 self, 519 ) -> Dict["StateVariable", Set[Union["StateVariable", "Function"]]]: 520 """ 521 Returns the state variables used in reentrant targets. Heuristics: 522 - Variable used (read/write) in entry points that are reentrant 523 - State variables that are public 524 525 """ 526 from slither.core.variables.state_variable import StateVariable 527 528 if self._state_variables_used_in_reentrant_targets is None: 529 reentrant_functions = [f for f in self.functions_entry_points if f.is_reentrant] 530 variables_used: Dict[ 531 StateVariable, Set[Union[StateVariable, "Function"]] 532 ] = defaultdict(set) 533 for function in reentrant_functions: 534 for ir in function.all_slithir_operations(): 535 state_variables = [v for v in ir.used if isinstance(v, StateVariable)] 536 for state_variable in state_variables: 537 variables_used[state_variable].add(ir.node.function) 538 for variable in [v for v in self.state_variables if v.visibility == "public"]: 539 variables_used[variable].add(variable) 540 self._state_variables_used_in_reentrant_targets = variables_used 541 return self._state_variables_used_in_reentrant_targets 542 543 # endregion 544 ################################################################################### 545 ################################################################################### 546 # region Constructors 547 ################################################################################### 548 ################################################################################### 549 550 @property 551 def constructor(self) -> Optional["Function"]: 552 """ 553 Return the contract's immediate constructor. 554 If there is no immediate constructor, returns the first constructor 555 executed, following the c3 linearization 556 Return None if there is no constructor. 557 """ 558 cst = self.constructors_declared 559 if cst: 560 return cst 561 for inherited_contract in self.inheritance: 562 cst = inherited_contract.constructors_declared 563 if cst: 564 return cst 565 return None 566 567 @property 568 def constructors_declared(self) -> Optional["Function"]: 569 return next( 570 ( 571 func 572 for func in self.functions 573 if func.is_constructor and func.contract_declarer == self 574 ), 575 None, 576 ) 577 578 @property 579 def constructors(self) -> List["FunctionContract"]: 580 """ 581 Return the list of constructors (including inherited) 582 """ 583 return [func for func in self.functions if func.is_constructor] 584 585 @property 586 def explicit_base_constructor_calls(self) -> List["Function"]: 587 """ 588 list(Function): List of the base constructors called explicitly by this contract definition. 589 590 Base constructors called by any constructor definition will not be included. 591 Base constructors implicitly called by the contract definition (without 592 parenthesis) will not be included. 593 594 On "contract B is A(){..}" it returns the constructor of A 595 """ 596 return [c.constructor for c in self._explicit_base_constructor_calls if c.constructor] 597 598 # endregion 599 ################################################################################### 600 ################################################################################### 601 # region Functions and Modifiers 602 ################################################################################### 603 ################################################################################### 604 605 @property 606 def functions_signatures(self) -> List[str]: 607 """ 608 Return the signatures of all the public/eterxnal functions/state variables 609 :return: list(string) the signatures of all the functions that can be called 610 """ 611 if self._signatures is None: 612 sigs = [ 613 v.full_name for v in self.state_variables if v.visibility in ["public", "external"] 614 ] 615 616 sigs += {f.full_name for f in self.functions if f.visibility in ["public", "external"]} 617 self._signatures = list(set(sigs)) 618 return self._signatures 619 620 @property 621 def functions_signatures_declared(self) -> List[str]: 622 """ 623 Return the signatures of the public/eterxnal functions/state variables that are declared by this contract 624 :return: list(string) the signatures of all the functions that can be called and are declared by this contract 625 """ 626 if self._signatures_declared is None: 627 sigs = [ 628 v.full_name 629 for v in self.state_variables_declared 630 if v.visibility in ["public", "external"] 631 ] 632 633 sigs += { 634 f.full_name 635 for f in self.functions_declared 636 if f.visibility in ["public", "external"] 637 } 638 self._signatures_declared = list(set(sigs)) 639 return self._signatures_declared 640 641 @property 642 def functions(self) -> List["FunctionContract"]: 643 """ 644 list(Function): List of the functions 645 """ 646 return list(self._functions.values()) 647 648 def available_functions_as_dict(self) -> Dict[str, "Function"]: 649 if self._available_functions_as_dict is None: 650 self._available_functions_as_dict = { 651 f.full_name: f for f in self._functions.values() if not f.is_shadowed 652 } 653 return self._available_functions_as_dict 654 655 def add_function(self, func: "FunctionContract") -> None: 656 self._functions[func.canonical_name] = func 657 658 def set_functions(self, functions: Dict[str, "FunctionContract"]) -> None: 659 """ 660 Set the functions 661 662 :param functions: dict full_name -> function 663 :return: 664 """ 665 self._functions = functions 666 667 @property 668 def functions_inherited(self) -> List["FunctionContract"]: 669 """ 670 list(Function): List of the inherited functions 671 """ 672 return [f for f in self.functions if f.contract_declarer != self] 673 674 @property 675 def functions_declared(self) -> List["FunctionContract"]: 676 """ 677 list(Function): List of the functions defined within the contract (not inherited) 678 """ 679 return [f for f in self.functions if f.contract_declarer == self] 680 681 @property 682 def functions_entry_points(self) -> List["FunctionContract"]: 683 """ 684 list(Functions): List of public and external functions 685 """ 686 return [ 687 f 688 for f in self.functions 689 if f.visibility in ["public", "external"] and not f.is_shadowed or f.is_fallback 690 ] 691 692 @property 693 def modifiers(self) -> List["Modifier"]: 694 """ 695 list(Modifier): List of the modifiers 696 """ 697 return list(self._modifiers.values()) 698 699 def available_modifiers_as_dict(self) -> Dict[str, "Modifier"]: 700 return {m.full_name: m for m in self._modifiers.values() if not m.is_shadowed} 701 702 def set_modifiers(self, modifiers: Dict[str, "Modifier"]) -> None: 703 """ 704 Set the modifiers 705 706 :param modifiers: dict full_name -> modifier 707 :return: 708 """ 709 self._modifiers = modifiers 710 711 @property 712 def modifiers_inherited(self) -> List["Modifier"]: 713 """ 714 list(Modifier): List of the inherited modifiers 715 """ 716 return [m for m in self.modifiers if m.contract_declarer != self] 717 718 @property 719 def modifiers_declared(self) -> List["Modifier"]: 720 """ 721 list(Modifier): List of the modifiers defined within the contract (not inherited) 722 """ 723 return [m for m in self.modifiers if m.contract_declarer == self] 724 725 @property 726 def functions_and_modifiers(self) -> List["Function"]: 727 """ 728 list(Function|Modifier): List of the functions and modifiers 729 """ 730 return self.functions + self.modifiers # type: ignore 731 732 @property 733 def functions_and_modifiers_inherited(self) -> List["Function"]: 734 """ 735 list(Function|Modifier): List of the inherited functions and modifiers 736 """ 737 return self.functions_inherited + self.modifiers_inherited # type: ignore 738 739 @property 740 def functions_and_modifiers_declared(self) -> List["Function"]: 741 """ 742 list(Function|Modifier): List of the functions and modifiers defined within the contract (not inherited) 743 """ 744 return self.functions_declared + self.modifiers_declared # type: ignore 745 746 @property 747 def fallback_function(self) -> Optional["FunctionContract"]: 748 if self._fallback_function is None: 749 for f in self.functions: 750 if f.is_fallback: 751 self._fallback_function = f 752 break 753 return self._fallback_function 754 755 @property 756 def receive_function(self) -> Optional["FunctionContract"]: 757 if self._receive_function is None: 758 for f in self.functions: 759 if f.is_receive: 760 self._receive_function = f 761 break 762 return self._receive_function 763 764 def available_elements_from_inheritances( 765 self, 766 elements: Dict[str, "Function"], 767 getter_available: Callable[["Contract"], List["FunctionContract"]], 768 ) -> Dict[str, "Function"]: 769 """ 770 771 :param elements: dict(canonical_name -> elements) 772 :param getter_available: fun x 773 :return: 774 """ 775 # keep track of the contracts visited 776 # to prevent an ovveride due to multiple inheritance of the same contract 777 # A is B, C, D is C, --> the second C was already seen 778 inherited_elements: Dict[str, "FunctionContract"] = {} 779 accessible_elements = {} 780 contracts_visited = [] 781 for father in self.inheritance_reverse: 782 functions: Dict[str, "FunctionContract"] = { 783 v.full_name: v 784 for v in getter_available(father) 785 if v.contract not in contracts_visited 786 and v.function_language 787 != FunctionLanguage.Yul # Yul functions are not propagated in the inheritance 788 } 789 contracts_visited.append(father) 790 inherited_elements.update(functions) 791 792 for element in inherited_elements.values(): 793 accessible_elements[element.full_name] = elements[element.canonical_name] 794 795 return accessible_elements 796 797 # endregion 798 ################################################################################### 799 ################################################################################### 800 # region Inheritance 801 ################################################################################### 802 ################################################################################### 803 804 @property 805 def inheritance(self) -> List["Contract"]: 806 """ 807 list(Contract): Inheritance list. Order: the first elem is the first father to be executed 808 """ 809 return list(self._inheritance) 810 811 @property 812 def immediate_inheritance(self) -> List["Contract"]: 813 """ 814 list(Contract): List of contracts immediately inherited from (fathers). Order: order of declaration. 815 """ 816 return list(self._immediate_inheritance) 817 818 @property 819 def inheritance_reverse(self) -> List["Contract"]: 820 """ 821 list(Contract): Inheritance list. Order: the last elem is the first father to be executed 822 """ 823 return list(reversed(self._inheritance)) 824 825 def set_inheritance( 826 self, 827 inheritance: List["Contract"], 828 immediate_inheritance: List["Contract"], 829 called_base_constructor_contracts: List["Contract"], 830 ) -> None: 831 self._inheritance = inheritance 832 self._immediate_inheritance = immediate_inheritance 833 self._explicit_base_constructor_calls = called_base_constructor_contracts 834 835 @property 836 def derived_contracts(self) -> List["Contract"]: 837 """ 838 list(Contract): Return the list of contracts derived from self 839 """ 840 candidates = self.compilation_unit.contracts 841 return [c for c in candidates if self in c.inheritance] # type: ignore 842 843 # endregion 844 ################################################################################### 845 ################################################################################### 846 # region Getters from/to object 847 ################################################################################### 848 ################################################################################### 849 850 def get_functions_reading_from_variable(self, variable: "Variable") -> List["Function"]: 851 """ 852 Return the functions reading the variable 853 """ 854 return [f for f in self.functions if f.is_reading(variable)] 855 856 def get_functions_writing_to_variable(self, variable: "Variable") -> List["Function"]: 857 """ 858 Return the functions writting the variable 859 """ 860 return [f for f in self.functions if f.is_writing(variable)] 861 862 def get_function_from_full_name(self, full_name: str) -> Optional["Function"]: 863 """ 864 Return a function from a full name 865 The full name differs from the solidity's signature are the type are conserved 866 For example contract type are kept, structure are not unrolled, etc 867 Args: 868 full_name (str): signature of the function (without return statement) 869 Returns: 870 Function 871 """ 872 return next( 873 (f for f in self.functions if f.full_name == full_name and not f.is_shadowed), 874 None, 875 ) 876 877 def get_function_from_signature(self, function_signature: str) -> Optional["Function"]: 878 """ 879 Return a function from a signature 880 Args: 881 function_signature (str): signature of the function (without return statement) 882 Returns: 883 Function 884 """ 885 return next( 886 ( 887 f 888 for f in self.functions 889 if f.solidity_signature == function_signature and not f.is_shadowed 890 ), 891 None, 892 ) 893 894 def get_modifier_from_signature(self, modifier_signature: str) -> Optional["Modifier"]: 895 """ 896 Return a modifier from a signature 897 898 :param modifier_signature: 899 """ 900 return next( 901 (m for m in self.modifiers if m.full_name == modifier_signature and not m.is_shadowed), 902 None, 903 ) 904 905 def get_function_from_canonical_name(self, canonical_name: str) -> Optional["Function"]: 906 """ 907 Return a function from a canonical name (contract.signature()) 908 Args: 909 canonical_name (str): canonical name of the function (without return statement) 910 Returns: 911 Function 912 """ 913 return next((f for f in self.functions if f.canonical_name == canonical_name), None) 914 915 def get_modifier_from_canonical_name(self, canonical_name: str) -> Optional["Modifier"]: 916 """ 917 Return a modifier from a canonical name (contract.signature()) 918 Args: 919 canonical_name (str): canonical name of the modifier 920 Returns: 921 Modifier 922 """ 923 return next((m for m in self.modifiers if m.canonical_name == canonical_name), None) 924 925 def get_state_variable_from_name(self, variable_name: str) -> Optional["StateVariable"]: 926 """ 927 Return a state variable from a name 928 929 :param variable_name: 930 """ 931 return next((v for v in self.state_variables if v.name == variable_name), None) 932 933 def get_state_variable_from_canonical_name( 934 self, canonical_name: str 935 ) -> Optional["StateVariable"]: 936 """ 937 Return a state variable from a canonical_name 938 Args: 939 canonical_name (str): name of the variable 940 Returns: 941 StateVariable 942 """ 943 return next((v for v in self.state_variables if v.canonical_name == canonical_name), None) 944 945 def get_structure_from_name(self, structure_name: str) -> Optional["StructureContract"]: 946 """ 947 Return a structure from a name 948 Args: 949 structure_name (str): name of the structure 950 Returns: 951 StructureContract 952 """ 953 return next((st for st in self.structures if st.name == structure_name), None) 954 955 def get_structure_from_canonical_name( 956 self, structure_name: str 957 ) -> Optional["StructureContract"]: 958 """ 959 Return a structure from a canonical name 960 Args: 961 structure_name (str): canonical name of the structure 962 Returns: 963 StructureContract 964 """ 965 return next((st for st in self.structures if st.canonical_name == structure_name), None) 966 967 def get_event_from_signature(self, event_signature: str) -> Optional["Event"]: 968 """ 969 Return an event from a signature 970 Args: 971 event_signature (str): signature of the event 972 Returns: 973 Event 974 """ 975 return next((e for e in self.events if e.full_name == event_signature), None) 976 977 def get_event_from_canonical_name(self, event_canonical_name: str) -> Optional["Event"]: 978 """ 979 Return an event from a canonical name 980 Args: 981 event_canonical_name (str): name of the event 982 Returns: 983 Event 984 """ 985 return next((e for e in self.events if e.canonical_name == event_canonical_name), None) 986 987 def get_enum_from_name(self, enum_name: str) -> Optional["Enum"]: 988 """ 989 Return an enum from a name 990 Args: 991 enum_name (str): name of the enum 992 Returns: 993 Enum 994 """ 995 return next((e for e in self.enums if e.name == enum_name), None) 996 997 def get_enum_from_canonical_name(self, enum_name: str) -> Optional["Enum"]: 998 """ 999 Return an enum from a canonical name 1000 Args: 1001 enum_name (str): canonical name of the enum 1002 Returns: 1003 Enum 1004 """ 1005 return next((e for e in self.enums if e.canonical_name == enum_name), None) 1006 1007 def get_functions_overridden_by(self, function: "Function") -> List["Function"]: 1008 """ 1009 Return the list of functions overridden by the function 1010 Args: 1011 (core.Function) 1012 Returns: 1013 list(core.Function) 1014 1015 """ 1016 return function.overrides 1017 1018 # endregion 1019 ################################################################################### 1020 ################################################################################### 1021 # region Recursive getters 1022 ################################################################################### 1023 ################################################################################### 1024 1025 @property 1026 def all_functions_called(self) -> List["InternalCallType"]: 1027 """ 1028 list(Function): List of functions reachable from the contract 1029 Includes super, and private/internal functions not shadowed 1030 """ 1031 if self._all_functions_called is None: 1032 all_functions = [f for f in self.functions + self.modifiers if not f.is_shadowed] # type: ignore 1033 all_callss = [f.all_internal_calls() for f in all_functions] + [list(all_functions)] 1034 all_calls = [item for sublist in all_callss for item in sublist] 1035 all_calls = list(set(all_calls)) 1036 1037 all_constructors = [c.constructor for c in self.inheritance if c.constructor] 1038 all_constructors = list(set(all_constructors)) 1039 1040 set_all_calls = set(all_calls + list(all_constructors)) 1041 1042 self._all_functions_called = [c for c in set_all_calls if isinstance(c, Function)] 1043 return self._all_functions_called 1044 1045 @property 1046 def all_state_variables_written(self) -> List["StateVariable"]: 1047 """ 1048 list(StateVariable): List all of the state variables written 1049 """ 1050 all_state_variables_writtens = [ 1051 f.all_state_variables_written() for f in self.functions + self.modifiers # type: ignore 1052 ] 1053 all_state_variables_written = [ 1054 item for sublist in all_state_variables_writtens for item in sublist 1055 ] 1056 return list(set(all_state_variables_written)) 1057 1058 @property 1059 def all_state_variables_read(self) -> List["StateVariable"]: 1060 """ 1061 list(StateVariable): List all of the state variables read 1062 """ 1063 all_state_variables_reads = [ 1064 f.all_state_variables_read() for f in self.functions + self.modifiers # type: ignore 1065 ] 1066 all_state_variables_read = [ 1067 item for sublist in all_state_variables_reads for item in sublist 1068 ] 1069 return list(set(all_state_variables_read)) 1070 1071 @property 1072 def all_library_calls(self) -> List["LibraryCallType"]: 1073 """ 1074 list((Contract, Function): List all of the libraries func called 1075 """ 1076 all_high_level_callss = [f.all_library_calls() for f in self.functions + self.modifiers] # type: ignore 1077 all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist] 1078 return list(set(all_high_level_calls)) 1079 1080 @property 1081 def all_high_level_calls(self) -> List["HighLevelCallType"]: 1082 """ 1083 list((Contract, Function|Variable)): List all of the external high level calls 1084 """ 1085 all_high_level_callss = [f.all_high_level_calls() for f in self.functions + self.modifiers] # type: ignore 1086 all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist] 1087 return list(set(all_high_level_calls)) 1088 1089 # endregion 1090 ################################################################################### 1091 ################################################################################### 1092 # region Summary information 1093 ################################################################################### 1094 ################################################################################### 1095 1096 def get_summary( 1097 self, include_shadowed: bool = True 1098 ) -> Tuple[str, List[str], List[str], List, List]: 1099 """Return the function summary 1100 1101 :param include_shadowed: boolean to indicate if shadowed functions should be included (default True) 1102 Returns: 1103 (str, list, list, list, list): (name, inheritance, variables, fuction summaries, modifier summaries) 1104 """ 1105 func_summaries = [ 1106 f.get_summary() for f in self.functions if (not f.is_shadowed or include_shadowed) 1107 ] 1108 modif_summaries = [ 1109 f.get_summary() for f in self.modifiers if (not f.is_shadowed or include_shadowed) 1110 ] 1111 return ( 1112 self.name, 1113 [str(x) for x in self.inheritance], 1114 [str(x) for x in self.variables], 1115 func_summaries, 1116 modif_summaries, 1117 ) 1118 1119 def is_signature_only(self) -> bool: 1120 """Detect if the contract has only abstract functions 1121 1122 Returns: 1123 bool: true if the function are abstract functions 1124 """ 1125 return all((not f.is_implemented) for f in self.functions) 1126 1127 # endregion 1128 ################################################################################### 1129 ################################################################################### 1130 # region ERC conformance 1131 ################################################################################### 1132 ################################################################################### 1133 1134 def ercs(self) -> List[str]: 1135 """ 1136 Return the ERC implemented 1137 :return: list of string 1138 """ 1139 all_erc = [ 1140 ("ERC20", self.is_erc20), 1141 ("ERC165", self.is_erc165), 1142 ("ERC1820", self.is_erc1820), 1143 ("ERC223", self.is_erc223), 1144 ("ERC721", self.is_erc721), 1145 ("ERC777", self.is_erc777), 1146 ("ERC2612", self.is_erc2612), 1147 ("ERC1363", self.is_erc1363), 1148 ("ERC4626", self.is_erc4626), 1149 ] 1150 1151 return [erc for erc, is_erc in all_erc if is_erc()] 1152 1153 def is_erc20(self) -> bool: 1154 """ 1155 Check if the contract is an erc20 token 1156 1157 Note: it does not check for correct return values 1158 :return: Returns a true if the contract is an erc20 1159 """ 1160 full_names = self.functions_signatures 1161 return all(s in full_names for s in ERC20_signatures) 1162 1163 def is_erc165(self) -> bool: 1164 """ 1165 Check if the contract is an erc165 token 1166 1167 Note: it does not check for correct return values 1168 :return: Returns a true if the contract is an erc165 1169 """ 1170 full_names = self.functions_signatures 1171 return all(s in full_names for s in ERC165_signatures) 1172 1173 def is_erc1820(self) -> bool: 1174 """ 1175 Check if the contract is an erc1820 1176 1177 Note: it does not check for correct return values 1178 :return: Returns a true if the contract is an erc165 1179 """ 1180 full_names = self.functions_signatures 1181 return all(s in full_names for s in ERC1820_signatures) 1182 1183 def is_erc223(self) -> bool: 1184 """ 1185 Check if the contract is an erc223 token 1186 1187 Note: it does not check for correct return values 1188 :return: Returns a true if the contract is an erc223 1189 """ 1190 full_names = self.functions_signatures 1191 return all(s in full_names for s in ERC223_signatures) 1192 1193 def is_erc721(self) -> bool: 1194 """ 1195 Check if the contract is an erc721 token 1196 1197 Note: it does not check for correct return values 1198 :return: Returns a true if the contract is an erc721 1199 """ 1200 full_names = self.functions_signatures 1201 return all(s in full_names for s in ERC721_signatures) 1202 1203 def is_erc777(self) -> bool: 1204 """ 1205 Check if the contract is an erc777 1206 1207 Note: it does not check for correct return values 1208 :return: Returns a true if the contract is an erc165 1209 """ 1210 full_names = self.functions_signatures 1211 return all(s in full_names for s in ERC777_signatures) 1212 1213 def is_erc1155(self) -> bool: 1214 """ 1215 Check if the contract is an erc1155 1216 1217 Note: it does not check for correct return values 1218 :return: Returns a true if the contract is an erc1155 1219 """ 1220 full_names = self.functions_signatures 1221 return all(s in full_names for s in ERC1155_signatures) 1222 1223 def is_erc4626(self) -> bool: 1224 """ 1225 Check if the contract is an erc4626 1226 1227 Note: it does not check for correct return values 1228 :return: Returns a true if the contract is an erc4626 1229 """ 1230 full_names = self.functions_signatures 1231 return all(s in full_names for s in ERC4626_signatures) 1232 1233 def is_erc2612(self) -> bool: 1234 """ 1235 Check if the contract is an erc2612 1236 1237 Note: it does not check for correct return values 1238 :return: Returns a true if the contract is an erc2612 1239 """ 1240 full_names = self.functions_signatures 1241 return all(s in full_names for s in ERC2612_signatures) 1242 1243 def is_erc1363(self) -> bool: 1244 """ 1245 Check if the contract is an erc1363 1246 1247 Note: it does not check for correct return values 1248 :return: Returns a true if the contract is an erc1363 1249 """ 1250 full_names = self.functions_signatures 1251 return all(s in full_names for s in ERC1363_signatures) 1252 1253 def is_erc4524(self) -> bool: 1254 """ 1255 Check if the contract is an erc4524 1256 1257 Note: it does not check for correct return values 1258 :return: Returns a true if the contract is an erc4524 1259 """ 1260 full_names = self.functions_signatures 1261 return all(s in full_names for s in ERC4524_signatures) 1262 1263 @property 1264 def is_token(self) -> bool: 1265 """ 1266 Check if the contract follows one of the standard ERC token 1267 :return: 1268 """ 1269 return ( 1270 self.is_erc20() 1271 or self.is_erc721() 1272 or self.is_erc165() 1273 or self.is_erc223() 1274 or self.is_erc777() 1275 or self.is_erc1155() 1276 ) 1277 1278 def is_possible_erc20(self) -> bool: 1279 """ 1280 Checks if the provided contract could be attempting to implement ERC20 standards. 1281 1282 :return: Returns a boolean indicating if the provided contract met the token standard. 1283 """ 1284 # We do not check for all the functions, as name(), symbol(), might give too many FPs 1285 full_names = self.functions_signatures 1286 return ( 1287 "transfer(address,uint256)" in full_names 1288 or "transferFrom(address,address,uint256)" in full_names 1289 or "approve(address,uint256)" in full_names 1290 ) 1291 1292 def is_possible_erc721(self) -> bool: 1293 """ 1294 Checks if the provided contract could be attempting to implement ERC721 standards. 1295 1296 :return: Returns a boolean indicating if the provided contract met the token standard. 1297 """ 1298 # We do not check for all the functions, as name(), symbol(), might give too many FPs 1299 full_names = self.functions_signatures 1300 return ( 1301 "ownerOf(uint256)" in full_names 1302 or "safeTransferFrom(address,address,uint256,bytes)" in full_names 1303 or "safeTransferFrom(address,address,uint256)" in full_names 1304 or "setApprovalForAll(address,bool)" in full_names 1305 or "getApproved(uint256)" in full_names 1306 or "isApprovedForAll(address,address)" in full_names 1307 ) 1308 1309 @property 1310 def is_possible_token(self) -> bool: 1311 """ 1312 Check if the contract is a potential token (it might not implement all the functions) 1313 :return: 1314 """ 1315 return self.is_possible_erc20() or self.is_possible_erc721() 1316 1317 # endregion 1318 ################################################################################### 1319 ################################################################################### 1320 # region Dependencies 1321 ################################################################################### 1322 ################################################################################### 1323 1324 def is_from_dependency(self) -> bool: 1325 return self.compilation_unit.core.crytic_compile.is_dependency( 1326 self.source_mapping.filename.absolute 1327 ) 1328 1329 # endregion 1330 ################################################################################### 1331 ################################################################################### 1332 # region Test 1333 ################################################################################### 1334 ################################################################################### 1335 1336 @property 1337 def is_truffle_migration(self) -> bool: 1338 """ 1339 Return true if the contract is the Migrations contract needed for Truffle 1340 :return: 1341 """ 1342 if self.compilation_unit.core.crytic_compile.platform == PlatformType.TRUFFLE: 1343 if self.name == "Migrations": 1344 paths = Path(self.source_mapping.filename.absolute).parts 1345 if len(paths) >= 2: 1346 return paths[-2] == "contracts" and paths[-1] == "migrations.sol" 1347 return False 1348 1349 @property 1350 def is_test(self) -> bool: 1351 return is_test_contract(self) or self.is_truffle_migration # type: ignore 1352 1353 # endregion 1354 ################################################################################### 1355 ################################################################################### 1356 # region Function analyses 1357 ################################################################################### 1358 ################################################################################### 1359 1360 def update_read_write_using_ssa(self) -> None: 1361 for function in self.functions + list(self.modifiers): 1362 function.update_read_write_using_ssa() 1363 1364 # endregion 1365 ################################################################################### 1366 ################################################################################### 1367 # region Upgradeability 1368 ################################################################################### 1369 ################################################################################### 1370 1371 @property 1372 def is_upgradeable(self) -> bool: 1373 if self._is_upgradeable is None: 1374 self._is_upgradeable = False 1375 initializable = self.file_scope.get_contract_from_name("Initializable") 1376 if initializable: 1377 if initializable in self.inheritance: 1378 self._is_upgradeable = True 1379 else: 1380 for contract in self.inheritance + [self]: 1381 # This might lead to false positive 1382 # Not sure why pylint is having a trouble here 1383 # pylint: disable=no-member 1384 lower_name = contract.name.lower() 1385 if "upgradeable" in lower_name or "upgradable" in lower_name: 1386 self._is_upgradeable = True 1387 break 1388 if "initializable" in lower_name: 1389 self._is_upgradeable = True 1390 break 1391 return self._is_upgradeable 1392 1393 @is_upgradeable.setter 1394 def is_upgradeable(self, upgradeable: bool) -> None: 1395 self._is_upgradeable = upgradeable 1396 1397 @property 1398 def is_upgradeable_proxy(self) -> bool: 1399 from slither.core.cfg.node import NodeType 1400 from slither.slithir.operations import LowLevelCall 1401 1402 if self._is_upgradeable_proxy is None: 1403 self._is_upgradeable_proxy = False 1404 if "Proxy" in self.name: 1405 self._is_upgradeable_proxy = True 1406 return True 1407 for f in self.functions: 1408 if f.is_fallback: 1409 for node in f.all_nodes(): 1410 for ir in node.irs: 1411 if isinstance(ir, LowLevelCall) and ir.function_name == "delegatecall": 1412 self._is_upgradeable_proxy = True 1413 return self._is_upgradeable_proxy 1414 if node.type == NodeType.ASSEMBLY: 1415 inline_asm = node.inline_asm 1416 if inline_asm: 1417 if "delegatecall" in inline_asm: 1418 self._is_upgradeable_proxy = True 1419 return self._is_upgradeable_proxy 1420 return self._is_upgradeable_proxy 1421 1422 @is_upgradeable_proxy.setter 1423 def is_upgradeable_proxy(self, upgradeable_proxy: bool) -> None: 1424 self._is_upgradeable_proxy = upgradeable_proxy 1425 1426 @property 1427 def upgradeable_version(self) -> Optional[str]: 1428 return self._upgradeable_version 1429 1430 @upgradeable_version.setter 1431 def upgradeable_version(self, version_name: str) -> None: 1432 self._upgradeable_version = version_name 1433 1434 # endregion 1435 ################################################################################### 1436 ################################################################################### 1437 # region Internals 1438 ################################################################################### 1439 ################################################################################### 1440 1441 @property 1442 def is_incorrectly_constructed(self) -> bool: 1443 """ 1444 Return true if there was an internal Slither's issue when analyzing the contract 1445 :return: 1446 """ 1447 return self._is_incorrectly_parsed 1448 1449 @is_incorrectly_constructed.setter 1450 def is_incorrectly_constructed(self, incorrect: bool) -> None: 1451 self._is_incorrectly_parsed = incorrect 1452 1453 def add_constructor_variables(self) -> None: 1454 from slither.core.declarations.function_contract import FunctionContract 1455 1456 if self.state_variables: 1457 for (idx, variable_candidate) in enumerate(self.state_variables): 1458 if variable_candidate.expression and not variable_candidate.is_constant: 1459 1460 constructor_variable = FunctionContract(self.compilation_unit) 1461 constructor_variable.set_function_type(FunctionType.CONSTRUCTOR_VARIABLES) 1462 constructor_variable.set_contract(self) # type: ignore 1463 constructor_variable.set_contract_declarer(self) # type: ignore 1464 constructor_variable.set_visibility("internal") 1465 # For now, source mapping of the constructor variable is the whole contract 1466 # Could be improved with a targeted source mapping 1467 constructor_variable.set_offset(self.source_mapping, self.compilation_unit) 1468 self._functions[constructor_variable.canonical_name] = constructor_variable 1469 1470 prev_node = self._create_node( 1471 constructor_variable, 0, variable_candidate, constructor_variable 1472 ) 1473 variable_candidate.node_initialization = prev_node 1474 counter = 1 1475 for v in self.state_variables[idx + 1 :]: 1476 if v.expression and not v.is_constant: 1477 next_node = self._create_node( 1478 constructor_variable, counter, v, prev_node.scope 1479 ) 1480 v.node_initialization = next_node 1481 prev_node.add_son(next_node) 1482 next_node.add_father(prev_node) 1483 prev_node = next_node 1484 counter += 1 1485 break 1486 1487 for (idx, variable_candidate) in enumerate(self.state_variables): 1488 if variable_candidate.expression and variable_candidate.is_constant: 1489 1490 constructor_variable = FunctionContract(self.compilation_unit) 1491 constructor_variable.set_function_type( 1492 FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES 1493 ) 1494 constructor_variable.set_contract(self) # type: ignore 1495 constructor_variable.set_contract_declarer(self) # type: ignore 1496 constructor_variable.set_visibility("internal") 1497 # For now, source mapping of the constructor variable is the whole contract 1498 # Could be improved with a targeted source mapping 1499 constructor_variable.set_offset(self.source_mapping, self.compilation_unit) 1500 self._functions[constructor_variable.canonical_name] = constructor_variable 1501 1502 prev_node = self._create_node( 1503 constructor_variable, 0, variable_candidate, constructor_variable 1504 ) 1505 variable_candidate.node_initialization = prev_node 1506 counter = 1 1507 for v in self.state_variables[idx + 1 :]: 1508 if v.expression and v.is_constant: 1509 next_node = self._create_node( 1510 constructor_variable, counter, v, prev_node.scope 1511 ) 1512 v.node_initialization = next_node 1513 prev_node.add_son(next_node) 1514 next_node.add_father(prev_node) 1515 prev_node = next_node 1516 counter += 1 1517 1518 break 1519 1520 def _create_node( 1521 self, func: Function, counter: int, variable: "Variable", scope: Union[Scope, Function] 1522 ) -> "Node": 1523 from slither.core.cfg.node import Node, NodeType 1524 from slither.core.expressions import ( 1525 AssignmentOperationType, 1526 AssignmentOperation, 1527 Identifier, 1528 ) 1529 1530 # Function uses to create node for state variable declaration statements 1531 node = Node(NodeType.OTHER_ENTRYPOINT, counter, scope, func.file_scope) 1532 node.set_offset(variable.source_mapping, self.compilation_unit) 1533 node.set_function(func) 1534 func.add_node(node) 1535 assert variable.expression 1536 expression = AssignmentOperation( 1537 Identifier(variable), 1538 variable.expression, 1539 AssignmentOperationType.ASSIGN, 1540 variable.type, 1541 ) 1542 1543 expression.set_offset(variable.source_mapping, self.compilation_unit) 1544 node.add_expression(expression) 1545 return node 1546 1547 # endregion 1548 ################################################################################### 1549 ################################################################################### 1550 # region SlithIR 1551 ################################################################################### 1552 ################################################################################### 1553 1554 def convert_expression_to_slithir_ssa(self) -> None: 1555 """ 1556 Assume generate_slithir_and_analyze was called on all functions 1557 1558 :return: 1559 """ 1560 from slither.slithir.variables import StateIRVariable 1561 1562 all_ssa_state_variables_instances = {} 1563 1564 for contract in self.inheritance: 1565 for v in contract.state_variables_declared: 1566 new_var = StateIRVariable(v) 1567 all_ssa_state_variables_instances[v.canonical_name] = new_var 1568 self._initial_state_variables.append(new_var) 1569 1570 for v in self.variables: 1571 if v.contract == self: 1572 new_var = StateIRVariable(v) 1573 all_ssa_state_variables_instances[v.canonical_name] = new_var 1574 self._initial_state_variables.append(new_var) 1575 1576 for func in self.functions + list(self.modifiers): 1577 func.generate_slithir_ssa(all_ssa_state_variables_instances) 1578 1579 def fix_phi(self) -> None: 1580 last_state_variables_instances: Dict[str, List["StateVariable"]] = {} 1581 initial_state_variables_instances: Dict[str, "StateVariable"] = {} 1582 for v in self._initial_state_variables: 1583 last_state_variables_instances[v.canonical_name] = [] 1584 initial_state_variables_instances[v.canonical_name] = v 1585 1586 for func in self.functions + list(self.modifiers): 1587 result = func.get_last_ssa_state_variables_instances() 1588 for variable_name, instances in result.items(): 1589 # TODO: investigate the next operation 1590 last_state_variables_instances[variable_name] += list(instances) 1591 1592 for func in self.functions + list(self.modifiers): 1593 func.fix_phi(last_state_variables_instances, initial_state_variables_instances) 1594 1595 # endregion 1596 ################################################################################### 1597 ################################################################################### 1598 # region Built in definitions 1599 ################################################################################### 1600 ################################################################################### 1601 1602 def __eq__(self, other: Any) -> bool: 1603 if isinstance(other, str): 1604 return other == self.name 1605 return NotImplemented 1606 1607 def __neq__(self, other: Any) -> bool: 1608 if isinstance(other, str): 1609 return other != self.name 1610 return NotImplemented 1611 1612 def __str__(self) -> str: 1613 return self.name 1614 1615 def __hash__(self) -> int: 1616 return self._id # type:ignore 1617 1618 # endregion
54class Contract(SourceMapping): # pylint: disable=too-many-public-methods 55 """ 56 Contract class 57 """ 58 59 def __init__(self, compilation_unit: "SlitherCompilationUnit", scope: "FileScope") -> None: 60 super().__init__() 61 62 self._name: Optional[str] = None 63 self._id: Optional[int] = None 64 self._inheritance: List["Contract"] = [] # all contract inherited, c3 linearization 65 self._immediate_inheritance: List["Contract"] = [] # immediate inheritance 66 67 # Constructors called on contract's definition 68 # contract B is A(1) { .. 69 self._explicit_base_constructor_calls: List["Contract"] = [] 70 71 self._enums: Dict[str, "EnumContract"] = {} 72 self._structures: Dict[str, "StructureContract"] = {} 73 self._events: Dict[str, "EventContract"] = {} 74 # map accessible variable from name -> variable 75 # do not contain private variables inherited from contract 76 self._variables: Dict[str, "StateVariable"] = {} 77 self._variables_ordered: List["StateVariable"] = [] 78 # Reference id -> variable declaration (only available for compact AST) 79 self._state_variables_by_ref_id: Dict[int, "StateVariable"] = {} 80 self._modifiers: Dict[str, "Modifier"] = {} 81 self._functions: Dict[str, "FunctionContract"] = {} 82 self._linearizedBaseContracts: List[int] = [] 83 self._custom_errors: Dict[str, "CustomErrorContract"] = {} 84 self._type_aliases: Dict[str, "TypeAliasContract"] = {} 85 86 # The only str is "*" 87 self._using_for: USING_FOR = {} 88 self._using_for_complete: Optional[USING_FOR] = None 89 self._kind: Optional[str] = None 90 self._is_interface: bool = False 91 self._is_library: bool = False 92 self._is_fully_implemented: bool = False 93 self._is_abstract: bool = False 94 95 self._signatures: Optional[List[str]] = None 96 self._signatures_declared: Optional[List[str]] = None 97 98 self._fallback_function: Optional["FunctionContract"] = None 99 self._receive_function: Optional["FunctionContract"] = None 100 101 self._is_upgradeable: Optional[bool] = None 102 self._is_upgradeable_proxy: Optional[bool] = None 103 self._upgradeable_version: Optional[str] = None 104 105 self._initial_state_variables: List["StateVariable"] = [] # ssa 106 107 self._is_incorrectly_parsed: bool = False 108 109 self._available_functions_as_dict: Optional[Dict[str, "Function"]] = None 110 self._all_functions_called: Optional[List["InternalCallType"]] = None 111 112 self.compilation_unit: "SlitherCompilationUnit" = compilation_unit 113 self.file_scope: "FileScope" = scope 114 115 # memoize 116 self._state_variables_used_in_reentrant_targets: Optional[ 117 Dict["StateVariable", Set[Union["StateVariable", "Function"]]] 118 ] = None 119 120 self._comments: Optional[str] = None 121 122 ################################################################################### 123 ################################################################################### 124 # region General's properties 125 ################################################################################### 126 ################################################################################### 127 128 @property 129 def name(self) -> str: 130 """str: Name of the contract.""" 131 assert self._name 132 return self._name 133 134 @name.setter 135 def name(self, name: str) -> None: 136 self._name = name 137 138 @property 139 def id(self) -> int: 140 """Unique id.""" 141 assert self._id is not None 142 return self._id 143 144 @id.setter 145 def id(self, new_id: int) -> None: 146 """Unique id.""" 147 self._id = new_id 148 149 @property 150 def contract_kind(self) -> Optional[str]: 151 """ 152 contract_kind can be None if the legacy ast format is used 153 :return: 154 """ 155 return self._kind 156 157 @contract_kind.setter 158 def contract_kind(self, kind: str) -> None: 159 self._kind = kind 160 161 @property 162 def is_interface(self) -> bool: 163 return self._is_interface 164 165 @is_interface.setter 166 def is_interface(self, is_interface: bool) -> None: 167 self._is_interface = is_interface 168 169 @property 170 def is_library(self) -> bool: 171 return self._is_library 172 173 @is_library.setter 174 def is_library(self, is_library: bool) -> None: 175 self._is_library = is_library 176 177 @property 178 def comments(self) -> Optional[str]: 179 """ 180 Return the comments associated with the contract. 181 182 When using comments, avoid strict text matching, as the solc behavior might change. 183 For example, for old solc version, the first space after the * is not kept, i.e: 184 185 * @title Test Contract 186 * @dev Test comment 187 188 Returns 189 - " @title Test Contract\n @dev Test comment" for newest versions 190 - "@title Test Contract\n@dev Test comment" for older versions 191 192 193 Returns: 194 the comment as a string 195 """ 196 return self._comments 197 198 @comments.setter 199 def comments(self, comments: str): 200 self._comments = comments 201 202 @property 203 def is_fully_implemented(self) -> bool: 204 """ 205 bool: True if the contract defines all functions. 206 In modern Solidity, virtual functions can lack an implementation. 207 Prior to Solidity 0.6.0, functions like the following would be not fully implemented: 208 ```solidity 209 contract ImplicitAbstract{ 210 function f() public; 211 } 212 ``` 213 """ 214 return self._is_fully_implemented 215 216 @is_fully_implemented.setter 217 def is_fully_implemented(self, is_fully_implemented: bool): 218 self._is_fully_implemented = is_fully_implemented 219 220 @property 221 def is_abstract(self) -> bool: 222 """ 223 Note for Solidity < 0.6.0 it will always be false 224 bool: True if the contract is abstract. 225 """ 226 return self._is_abstract 227 228 @is_abstract.setter 229 def is_abstract(self, is_abstract: bool): 230 self._is_abstract = is_abstract 231 232 # endregion 233 ################################################################################### 234 ################################################################################### 235 # region Structures 236 ################################################################################### 237 ################################################################################### 238 239 @property 240 def structures(self) -> List["StructureContract"]: 241 """ 242 list(Structure): List of the structures 243 """ 244 return list(self._structures.values()) 245 246 @property 247 def structures_inherited(self) -> List["StructureContract"]: 248 """ 249 list(Structure): List of the inherited structures 250 """ 251 return [s for s in self.structures if s.contract != self] 252 253 @property 254 def structures_declared(self) -> List["StructureContract"]: 255 """ 256 list(Structues): List of the structures declared within the contract (not inherited) 257 """ 258 return [s for s in self.structures if s.contract == self] 259 260 @property 261 def structures_as_dict(self) -> Dict[str, "StructureContract"]: 262 return self._structures 263 264 # endregion 265 ################################################################################### 266 ################################################################################### 267 # region Enums 268 ################################################################################### 269 ################################################################################### 270 271 @property 272 def enums(self) -> List["EnumContract"]: 273 return list(self._enums.values()) 274 275 @property 276 def enums_inherited(self) -> List["EnumContract"]: 277 """ 278 list(Enum): List of the inherited enums 279 """ 280 return [e for e in self.enums if e.contract != self] 281 282 @property 283 def enums_declared(self) -> List["EnumContract"]: 284 """ 285 list(Enum): List of the enums declared within the contract (not inherited) 286 """ 287 return [e for e in self.enums if e.contract == self] 288 289 @property 290 def enums_as_dict(self) -> Dict[str, "EnumContract"]: 291 return self._enums 292 293 # endregion 294 ################################################################################### 295 ################################################################################### 296 # region Events 297 ################################################################################### 298 ################################################################################### 299 300 @property 301 def events(self) -> List["EventContract"]: 302 """ 303 list(Event): List of the events 304 """ 305 return list(self._events.values()) 306 307 @property 308 def events_inherited(self) -> List["EventContract"]: 309 """ 310 list(Event): List of the inherited events 311 """ 312 return [e for e in self.events if e.contract != self] 313 314 @property 315 def events_declared(self) -> List["EventContract"]: 316 """ 317 list(Event): List of the events declared within the contract (not inherited) 318 """ 319 return [e for e in self.events if e.contract == self] 320 321 @property 322 def events_as_dict(self) -> Dict[str, "EventContract"]: 323 return self._events 324 325 # endregion 326 ################################################################################### 327 ################################################################################### 328 # region Using for 329 ################################################################################### 330 ################################################################################### 331 332 @property 333 def using_for(self) -> USING_FOR: 334 return self._using_for 335 336 @property 337 def using_for_complete(self) -> USING_FOR: 338 """ 339 USING_FOR: Dict of merged local using for directive with top level directive 340 """ 341 342 if self._using_for_complete is None: 343 result = self.using_for 344 top_level_using_for = self.file_scope.using_for_directives 345 for uftl in top_level_using_for: 346 result = merge_using_for(result, uftl.using_for) 347 self._using_for_complete = result 348 return self._using_for_complete 349 350 # endregion 351 ################################################################################### 352 ################################################################################### 353 # region Custom Errors 354 ################################################################################### 355 ################################################################################### 356 357 @property 358 def custom_errors(self) -> List["CustomErrorContract"]: 359 """ 360 list(CustomErrorContract): List of the contract's custom errors 361 """ 362 return list(self._custom_errors.values()) 363 364 @property 365 def custom_errors_inherited(self) -> List["CustomErrorContract"]: 366 """ 367 list(CustomErrorContract): List of the inherited custom errors 368 """ 369 return [s for s in self.custom_errors if s.contract != self] 370 371 @property 372 def custom_errors_declared(self) -> List["CustomErrorContract"]: 373 """ 374 list(CustomErrorContract): List of the custom errors declared within the contract (not inherited) 375 """ 376 return [s for s in self.custom_errors if s.contract == self] 377 378 @property 379 def custom_errors_as_dict(self) -> Dict[str, "CustomErrorContract"]: 380 return self._custom_errors 381 382 # endregion 383 ################################################################################### 384 ################################################################################### 385 # region Custom Errors 386 ################################################################################### 387 ################################################################################### 388 389 @property 390 def type_aliases(self) -> List["TypeAliasContract"]: 391 """ 392 list(TypeAliasContract): List of the contract's custom errors 393 """ 394 return list(self._type_aliases.values()) 395 396 @property 397 def type_aliases_inherited(self) -> List["TypeAliasContract"]: 398 """ 399 list(TypeAliasContract): List of the inherited custom errors 400 """ 401 return [s for s in self.type_aliases if s.contract != self] 402 403 @property 404 def type_aliases_declared(self) -> List["TypeAliasContract"]: 405 """ 406 list(TypeAliasContract): List of the custom errors declared within the contract (not inherited) 407 """ 408 return [s for s in self.type_aliases if s.contract == self] 409 410 @property 411 def type_aliases_as_dict(self) -> Dict[str, "TypeAliasContract"]: 412 return self._type_aliases 413 414 # endregion 415 ################################################################################### 416 ################################################################################### 417 # region Variables 418 ################################################################################### 419 ################################################################################### 420 @property 421 def state_variables_by_ref_id(self) -> Dict[int, "StateVariable"]: 422 """ 423 Returns the state variables by reference id (only available for compact AST). 424 """ 425 return self._state_variables_by_ref_id 426 427 @property 428 def variables(self) -> List["StateVariable"]: 429 """ 430 Returns all the accessible variables (do not include private variable from inherited contract) 431 432 list(StateVariable): List of the state variables. Alias to self.state_variables. 433 """ 434 return list(self.state_variables) 435 436 @property 437 def variables_as_dict(self) -> Dict[str, "StateVariable"]: 438 return self._variables 439 440 @property 441 def state_variables(self) -> List["StateVariable"]: 442 """ 443 Returns all the accessible variables (do not include private variable from inherited contract). 444 Use state_variables_ordered for all the variables following the storage order 445 446 list(StateVariable): List of the state variables. 447 """ 448 return list(self._variables.values()) 449 450 @property 451 def stored_state_variables(self) -> List["StateVariable"]: 452 """ 453 Returns state variables with storage locations, excluding private variables from inherited contracts. 454 Use stored_state_variables_ordered to access variables with storage locations in their declaration order. 455 456 This implementation filters out state variables if they are constant or immutable. It will be 457 updated to accommodate any new non-storage keywords that might replace 'constant' and 'immutable' in the future. 458 459 Returns: 460 List[StateVariable]: A list of state variables with storage locations. 461 """ 462 return [variable for variable in self.state_variables if variable.is_stored] 463 464 @property 465 def stored_state_variables_ordered(self) -> List["StateVariable"]: 466 """ 467 list(StateVariable): List of the state variables with storage locations by order of declaration. 468 469 This implementation filters out state variables if they are constant or immutable. It will be 470 updated to accommodate any new non-storage keywords that might replace 'constant' and 'immutable' in the future. 471 472 Returns: 473 List[StateVariable]: A list of state variables with storage locations ordered by declaration. 474 """ 475 return [variable for variable in self.state_variables_ordered if variable.is_stored] 476 477 @property 478 def state_variables_entry_points(self) -> List["StateVariable"]: 479 """ 480 list(StateVariable): List of the state variables that are public. 481 """ 482 return [var for var in self._variables.values() if var.visibility == "public"] 483 484 @property 485 def state_variables_ordered(self) -> List["StateVariable"]: 486 """ 487 list(StateVariable): List of the state variables by order of declaration. 488 """ 489 return list(self._variables_ordered) 490 491 def add_variables_ordered(self, new_vars: List["StateVariable"]) -> None: 492 self._variables_ordered += new_vars 493 494 @property 495 def state_variables_inherited(self) -> List["StateVariable"]: 496 """ 497 list(StateVariable): List of the inherited state variables 498 """ 499 return [s for s in self.state_variables if s.contract != self] 500 501 @property 502 def state_variables_declared(self) -> List["StateVariable"]: 503 """ 504 list(StateVariable): List of the state variables declared within the contract (not inherited) 505 """ 506 return [s for s in self.state_variables if s.contract == self] 507 508 @property 509 def slithir_variables(self) -> List["SlithIRVariable"]: 510 """ 511 List all of the slithir variables (non SSA) 512 """ 513 slithir_variabless = [f.slithir_variables for f in self.functions + self.modifiers] # type: ignore 514 slithir_variables = [item for sublist in slithir_variabless for item in sublist] 515 return list(set(slithir_variables)) 516 517 @property 518 def state_variables_used_in_reentrant_targets( 519 self, 520 ) -> Dict["StateVariable", Set[Union["StateVariable", "Function"]]]: 521 """ 522 Returns the state variables used in reentrant targets. Heuristics: 523 - Variable used (read/write) in entry points that are reentrant 524 - State variables that are public 525 526 """ 527 from slither.core.variables.state_variable import StateVariable 528 529 if self._state_variables_used_in_reentrant_targets is None: 530 reentrant_functions = [f for f in self.functions_entry_points if f.is_reentrant] 531 variables_used: Dict[ 532 StateVariable, Set[Union[StateVariable, "Function"]] 533 ] = defaultdict(set) 534 for function in reentrant_functions: 535 for ir in function.all_slithir_operations(): 536 state_variables = [v for v in ir.used if isinstance(v, StateVariable)] 537 for state_variable in state_variables: 538 variables_used[state_variable].add(ir.node.function) 539 for variable in [v for v in self.state_variables if v.visibility == "public"]: 540 variables_used[variable].add(variable) 541 self._state_variables_used_in_reentrant_targets = variables_used 542 return self._state_variables_used_in_reentrant_targets 543 544 # endregion 545 ################################################################################### 546 ################################################################################### 547 # region Constructors 548 ################################################################################### 549 ################################################################################### 550 551 @property 552 def constructor(self) -> Optional["Function"]: 553 """ 554 Return the contract's immediate constructor. 555 If there is no immediate constructor, returns the first constructor 556 executed, following the c3 linearization 557 Return None if there is no constructor. 558 """ 559 cst = self.constructors_declared 560 if cst: 561 return cst 562 for inherited_contract in self.inheritance: 563 cst = inherited_contract.constructors_declared 564 if cst: 565 return cst 566 return None 567 568 @property 569 def constructors_declared(self) -> Optional["Function"]: 570 return next( 571 ( 572 func 573 for func in self.functions 574 if func.is_constructor and func.contract_declarer == self 575 ), 576 None, 577 ) 578 579 @property 580 def constructors(self) -> List["FunctionContract"]: 581 """ 582 Return the list of constructors (including inherited) 583 """ 584 return [func for func in self.functions if func.is_constructor] 585 586 @property 587 def explicit_base_constructor_calls(self) -> List["Function"]: 588 """ 589 list(Function): List of the base constructors called explicitly by this contract definition. 590 591 Base constructors called by any constructor definition will not be included. 592 Base constructors implicitly called by the contract definition (without 593 parenthesis) will not be included. 594 595 On "contract B is A(){..}" it returns the constructor of A 596 """ 597 return [c.constructor for c in self._explicit_base_constructor_calls if c.constructor] 598 599 # endregion 600 ################################################################################### 601 ################################################################################### 602 # region Functions and Modifiers 603 ################################################################################### 604 ################################################################################### 605 606 @property 607 def functions_signatures(self) -> List[str]: 608 """ 609 Return the signatures of all the public/eterxnal functions/state variables 610 :return: list(string) the signatures of all the functions that can be called 611 """ 612 if self._signatures is None: 613 sigs = [ 614 v.full_name for v in self.state_variables if v.visibility in ["public", "external"] 615 ] 616 617 sigs += {f.full_name for f in self.functions if f.visibility in ["public", "external"]} 618 self._signatures = list(set(sigs)) 619 return self._signatures 620 621 @property 622 def functions_signatures_declared(self) -> List[str]: 623 """ 624 Return the signatures of the public/eterxnal functions/state variables that are declared by this contract 625 :return: list(string) the signatures of all the functions that can be called and are declared by this contract 626 """ 627 if self._signatures_declared is None: 628 sigs = [ 629 v.full_name 630 for v in self.state_variables_declared 631 if v.visibility in ["public", "external"] 632 ] 633 634 sigs += { 635 f.full_name 636 for f in self.functions_declared 637 if f.visibility in ["public", "external"] 638 } 639 self._signatures_declared = list(set(sigs)) 640 return self._signatures_declared 641 642 @property 643 def functions(self) -> List["FunctionContract"]: 644 """ 645 list(Function): List of the functions 646 """ 647 return list(self._functions.values()) 648 649 def available_functions_as_dict(self) -> Dict[str, "Function"]: 650 if self._available_functions_as_dict is None: 651 self._available_functions_as_dict = { 652 f.full_name: f for f in self._functions.values() if not f.is_shadowed 653 } 654 return self._available_functions_as_dict 655 656 def add_function(self, func: "FunctionContract") -> None: 657 self._functions[func.canonical_name] = func 658 659 def set_functions(self, functions: Dict[str, "FunctionContract"]) -> None: 660 """ 661 Set the functions 662 663 :param functions: dict full_name -> function 664 :return: 665 """ 666 self._functions = functions 667 668 @property 669 def functions_inherited(self) -> List["FunctionContract"]: 670 """ 671 list(Function): List of the inherited functions 672 """ 673 return [f for f in self.functions if f.contract_declarer != self] 674 675 @property 676 def functions_declared(self) -> List["FunctionContract"]: 677 """ 678 list(Function): List of the functions defined within the contract (not inherited) 679 """ 680 return [f for f in self.functions if f.contract_declarer == self] 681 682 @property 683 def functions_entry_points(self) -> List["FunctionContract"]: 684 """ 685 list(Functions): List of public and external functions 686 """ 687 return [ 688 f 689 for f in self.functions 690 if f.visibility in ["public", "external"] and not f.is_shadowed or f.is_fallback 691 ] 692 693 @property 694 def modifiers(self) -> List["Modifier"]: 695 """ 696 list(Modifier): List of the modifiers 697 """ 698 return list(self._modifiers.values()) 699 700 def available_modifiers_as_dict(self) -> Dict[str, "Modifier"]: 701 return {m.full_name: m for m in self._modifiers.values() if not m.is_shadowed} 702 703 def set_modifiers(self, modifiers: Dict[str, "Modifier"]) -> None: 704 """ 705 Set the modifiers 706 707 :param modifiers: dict full_name -> modifier 708 :return: 709 """ 710 self._modifiers = modifiers 711 712 @property 713 def modifiers_inherited(self) -> List["Modifier"]: 714 """ 715 list(Modifier): List of the inherited modifiers 716 """ 717 return [m for m in self.modifiers if m.contract_declarer != self] 718 719 @property 720 def modifiers_declared(self) -> List["Modifier"]: 721 """ 722 list(Modifier): List of the modifiers defined within the contract (not inherited) 723 """ 724 return [m for m in self.modifiers if m.contract_declarer == self] 725 726 @property 727 def functions_and_modifiers(self) -> List["Function"]: 728 """ 729 list(Function|Modifier): List of the functions and modifiers 730 """ 731 return self.functions + self.modifiers # type: ignore 732 733 @property 734 def functions_and_modifiers_inherited(self) -> List["Function"]: 735 """ 736 list(Function|Modifier): List of the inherited functions and modifiers 737 """ 738 return self.functions_inherited + self.modifiers_inherited # type: ignore 739 740 @property 741 def functions_and_modifiers_declared(self) -> List["Function"]: 742 """ 743 list(Function|Modifier): List of the functions and modifiers defined within the contract (not inherited) 744 """ 745 return self.functions_declared + self.modifiers_declared # type: ignore 746 747 @property 748 def fallback_function(self) -> Optional["FunctionContract"]: 749 if self._fallback_function is None: 750 for f in self.functions: 751 if f.is_fallback: 752 self._fallback_function = f 753 break 754 return self._fallback_function 755 756 @property 757 def receive_function(self) -> Optional["FunctionContract"]: 758 if self._receive_function is None: 759 for f in self.functions: 760 if f.is_receive: 761 self._receive_function = f 762 break 763 return self._receive_function 764 765 def available_elements_from_inheritances( 766 self, 767 elements: Dict[str, "Function"], 768 getter_available: Callable[["Contract"], List["FunctionContract"]], 769 ) -> Dict[str, "Function"]: 770 """ 771 772 :param elements: dict(canonical_name -> elements) 773 :param getter_available: fun x 774 :return: 775 """ 776 # keep track of the contracts visited 777 # to prevent an ovveride due to multiple inheritance of the same contract 778 # A is B, C, D is C, --> the second C was already seen 779 inherited_elements: Dict[str, "FunctionContract"] = {} 780 accessible_elements = {} 781 contracts_visited = [] 782 for father in self.inheritance_reverse: 783 functions: Dict[str, "FunctionContract"] = { 784 v.full_name: v 785 for v in getter_available(father) 786 if v.contract not in contracts_visited 787 and v.function_language 788 != FunctionLanguage.Yul # Yul functions are not propagated in the inheritance 789 } 790 contracts_visited.append(father) 791 inherited_elements.update(functions) 792 793 for element in inherited_elements.values(): 794 accessible_elements[element.full_name] = elements[element.canonical_name] 795 796 return accessible_elements 797 798 # endregion 799 ################################################################################### 800 ################################################################################### 801 # region Inheritance 802 ################################################################################### 803 ################################################################################### 804 805 @property 806 def inheritance(self) -> List["Contract"]: 807 """ 808 list(Contract): Inheritance list. Order: the first elem is the first father to be executed 809 """ 810 return list(self._inheritance) 811 812 @property 813 def immediate_inheritance(self) -> List["Contract"]: 814 """ 815 list(Contract): List of contracts immediately inherited from (fathers). Order: order of declaration. 816 """ 817 return list(self._immediate_inheritance) 818 819 @property 820 def inheritance_reverse(self) -> List["Contract"]: 821 """ 822 list(Contract): Inheritance list. Order: the last elem is the first father to be executed 823 """ 824 return list(reversed(self._inheritance)) 825 826 def set_inheritance( 827 self, 828 inheritance: List["Contract"], 829 immediate_inheritance: List["Contract"], 830 called_base_constructor_contracts: List["Contract"], 831 ) -> None: 832 self._inheritance = inheritance 833 self._immediate_inheritance = immediate_inheritance 834 self._explicit_base_constructor_calls = called_base_constructor_contracts 835 836 @property 837 def derived_contracts(self) -> List["Contract"]: 838 """ 839 list(Contract): Return the list of contracts derived from self 840 """ 841 candidates = self.compilation_unit.contracts 842 return [c for c in candidates if self in c.inheritance] # type: ignore 843 844 # endregion 845 ################################################################################### 846 ################################################################################### 847 # region Getters from/to object 848 ################################################################################### 849 ################################################################################### 850 851 def get_functions_reading_from_variable(self, variable: "Variable") -> List["Function"]: 852 """ 853 Return the functions reading the variable 854 """ 855 return [f for f in self.functions if f.is_reading(variable)] 856 857 def get_functions_writing_to_variable(self, variable: "Variable") -> List["Function"]: 858 """ 859 Return the functions writting the variable 860 """ 861 return [f for f in self.functions if f.is_writing(variable)] 862 863 def get_function_from_full_name(self, full_name: str) -> Optional["Function"]: 864 """ 865 Return a function from a full name 866 The full name differs from the solidity's signature are the type are conserved 867 For example contract type are kept, structure are not unrolled, etc 868 Args: 869 full_name (str): signature of the function (without return statement) 870 Returns: 871 Function 872 """ 873 return next( 874 (f for f in self.functions if f.full_name == full_name and not f.is_shadowed), 875 None, 876 ) 877 878 def get_function_from_signature(self, function_signature: str) -> Optional["Function"]: 879 """ 880 Return a function from a signature 881 Args: 882 function_signature (str): signature of the function (without return statement) 883 Returns: 884 Function 885 """ 886 return next( 887 ( 888 f 889 for f in self.functions 890 if f.solidity_signature == function_signature and not f.is_shadowed 891 ), 892 None, 893 ) 894 895 def get_modifier_from_signature(self, modifier_signature: str) -> Optional["Modifier"]: 896 """ 897 Return a modifier from a signature 898 899 :param modifier_signature: 900 """ 901 return next( 902 (m for m in self.modifiers if m.full_name == modifier_signature and not m.is_shadowed), 903 None, 904 ) 905 906 def get_function_from_canonical_name(self, canonical_name: str) -> Optional["Function"]: 907 """ 908 Return a function from a canonical name (contract.signature()) 909 Args: 910 canonical_name (str): canonical name of the function (without return statement) 911 Returns: 912 Function 913 """ 914 return next((f for f in self.functions if f.canonical_name == canonical_name), None) 915 916 def get_modifier_from_canonical_name(self, canonical_name: str) -> Optional["Modifier"]: 917 """ 918 Return a modifier from a canonical name (contract.signature()) 919 Args: 920 canonical_name (str): canonical name of the modifier 921 Returns: 922 Modifier 923 """ 924 return next((m for m in self.modifiers if m.canonical_name == canonical_name), None) 925 926 def get_state_variable_from_name(self, variable_name: str) -> Optional["StateVariable"]: 927 """ 928 Return a state variable from a name 929 930 :param variable_name: 931 """ 932 return next((v for v in self.state_variables if v.name == variable_name), None) 933 934 def get_state_variable_from_canonical_name( 935 self, canonical_name: str 936 ) -> Optional["StateVariable"]: 937 """ 938 Return a state variable from a canonical_name 939 Args: 940 canonical_name (str): name of the variable 941 Returns: 942 StateVariable 943 """ 944 return next((v for v in self.state_variables if v.canonical_name == canonical_name), None) 945 946 def get_structure_from_name(self, structure_name: str) -> Optional["StructureContract"]: 947 """ 948 Return a structure from a name 949 Args: 950 structure_name (str): name of the structure 951 Returns: 952 StructureContract 953 """ 954 return next((st for st in self.structures if st.name == structure_name), None) 955 956 def get_structure_from_canonical_name( 957 self, structure_name: str 958 ) -> Optional["StructureContract"]: 959 """ 960 Return a structure from a canonical name 961 Args: 962 structure_name (str): canonical name of the structure 963 Returns: 964 StructureContract 965 """ 966 return next((st for st in self.structures if st.canonical_name == structure_name), None) 967 968 def get_event_from_signature(self, event_signature: str) -> Optional["Event"]: 969 """ 970 Return an event from a signature 971 Args: 972 event_signature (str): signature of the event 973 Returns: 974 Event 975 """ 976 return next((e for e in self.events if e.full_name == event_signature), None) 977 978 def get_event_from_canonical_name(self, event_canonical_name: str) -> Optional["Event"]: 979 """ 980 Return an event from a canonical name 981 Args: 982 event_canonical_name (str): name of the event 983 Returns: 984 Event 985 """ 986 return next((e for e in self.events if e.canonical_name == event_canonical_name), None) 987 988 def get_enum_from_name(self, enum_name: str) -> Optional["Enum"]: 989 """ 990 Return an enum from a name 991 Args: 992 enum_name (str): name of the enum 993 Returns: 994 Enum 995 """ 996 return next((e for e in self.enums if e.name == enum_name), None) 997 998 def get_enum_from_canonical_name(self, enum_name: str) -> Optional["Enum"]: 999 """ 1000 Return an enum from a canonical name 1001 Args: 1002 enum_name (str): canonical name of the enum 1003 Returns: 1004 Enum 1005 """ 1006 return next((e for e in self.enums if e.canonical_name == enum_name), None) 1007 1008 def get_functions_overridden_by(self, function: "Function") -> List["Function"]: 1009 """ 1010 Return the list of functions overridden by the function 1011 Args: 1012 (core.Function) 1013 Returns: 1014 list(core.Function) 1015 1016 """ 1017 return function.overrides 1018 1019 # endregion 1020 ################################################################################### 1021 ################################################################################### 1022 # region Recursive getters 1023 ################################################################################### 1024 ################################################################################### 1025 1026 @property 1027 def all_functions_called(self) -> List["InternalCallType"]: 1028 """ 1029 list(Function): List of functions reachable from the contract 1030 Includes super, and private/internal functions not shadowed 1031 """ 1032 if self._all_functions_called is None: 1033 all_functions = [f for f in self.functions + self.modifiers if not f.is_shadowed] # type: ignore 1034 all_callss = [f.all_internal_calls() for f in all_functions] + [list(all_functions)] 1035 all_calls = [item for sublist in all_callss for item in sublist] 1036 all_calls = list(set(all_calls)) 1037 1038 all_constructors = [c.constructor for c in self.inheritance if c.constructor] 1039 all_constructors = list(set(all_constructors)) 1040 1041 set_all_calls = set(all_calls + list(all_constructors)) 1042 1043 self._all_functions_called = [c for c in set_all_calls if isinstance(c, Function)] 1044 return self._all_functions_called 1045 1046 @property 1047 def all_state_variables_written(self) -> List["StateVariable"]: 1048 """ 1049 list(StateVariable): List all of the state variables written 1050 """ 1051 all_state_variables_writtens = [ 1052 f.all_state_variables_written() for f in self.functions + self.modifiers # type: ignore 1053 ] 1054 all_state_variables_written = [ 1055 item for sublist in all_state_variables_writtens for item in sublist 1056 ] 1057 return list(set(all_state_variables_written)) 1058 1059 @property 1060 def all_state_variables_read(self) -> List["StateVariable"]: 1061 """ 1062 list(StateVariable): List all of the state variables read 1063 """ 1064 all_state_variables_reads = [ 1065 f.all_state_variables_read() for f in self.functions + self.modifiers # type: ignore 1066 ] 1067 all_state_variables_read = [ 1068 item for sublist in all_state_variables_reads for item in sublist 1069 ] 1070 return list(set(all_state_variables_read)) 1071 1072 @property 1073 def all_library_calls(self) -> List["LibraryCallType"]: 1074 """ 1075 list((Contract, Function): List all of the libraries func called 1076 """ 1077 all_high_level_callss = [f.all_library_calls() for f in self.functions + self.modifiers] # type: ignore 1078 all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist] 1079 return list(set(all_high_level_calls)) 1080 1081 @property 1082 def all_high_level_calls(self) -> List["HighLevelCallType"]: 1083 """ 1084 list((Contract, Function|Variable)): List all of the external high level calls 1085 """ 1086 all_high_level_callss = [f.all_high_level_calls() for f in self.functions + self.modifiers] # type: ignore 1087 all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist] 1088 return list(set(all_high_level_calls)) 1089 1090 # endregion 1091 ################################################################################### 1092 ################################################################################### 1093 # region Summary information 1094 ################################################################################### 1095 ################################################################################### 1096 1097 def get_summary( 1098 self, include_shadowed: bool = True 1099 ) -> Tuple[str, List[str], List[str], List, List]: 1100 """Return the function summary 1101 1102 :param include_shadowed: boolean to indicate if shadowed functions should be included (default True) 1103 Returns: 1104 (str, list, list, list, list): (name, inheritance, variables, fuction summaries, modifier summaries) 1105 """ 1106 func_summaries = [ 1107 f.get_summary() for f in self.functions if (not f.is_shadowed or include_shadowed) 1108 ] 1109 modif_summaries = [ 1110 f.get_summary() for f in self.modifiers if (not f.is_shadowed or include_shadowed) 1111 ] 1112 return ( 1113 self.name, 1114 [str(x) for x in self.inheritance], 1115 [str(x) for x in self.variables], 1116 func_summaries, 1117 modif_summaries, 1118 ) 1119 1120 def is_signature_only(self) -> bool: 1121 """Detect if the contract has only abstract functions 1122 1123 Returns: 1124 bool: true if the function are abstract functions 1125 """ 1126 return all((not f.is_implemented) for f in self.functions) 1127 1128 # endregion 1129 ################################################################################### 1130 ################################################################################### 1131 # region ERC conformance 1132 ################################################################################### 1133 ################################################################################### 1134 1135 def ercs(self) -> List[str]: 1136 """ 1137 Return the ERC implemented 1138 :return: list of string 1139 """ 1140 all_erc = [ 1141 ("ERC20", self.is_erc20), 1142 ("ERC165", self.is_erc165), 1143 ("ERC1820", self.is_erc1820), 1144 ("ERC223", self.is_erc223), 1145 ("ERC721", self.is_erc721), 1146 ("ERC777", self.is_erc777), 1147 ("ERC2612", self.is_erc2612), 1148 ("ERC1363", self.is_erc1363), 1149 ("ERC4626", self.is_erc4626), 1150 ] 1151 1152 return [erc for erc, is_erc in all_erc if is_erc()] 1153 1154 def is_erc20(self) -> bool: 1155 """ 1156 Check if the contract is an erc20 token 1157 1158 Note: it does not check for correct return values 1159 :return: Returns a true if the contract is an erc20 1160 """ 1161 full_names = self.functions_signatures 1162 return all(s in full_names for s in ERC20_signatures) 1163 1164 def is_erc165(self) -> bool: 1165 """ 1166 Check if the contract is an erc165 token 1167 1168 Note: it does not check for correct return values 1169 :return: Returns a true if the contract is an erc165 1170 """ 1171 full_names = self.functions_signatures 1172 return all(s in full_names for s in ERC165_signatures) 1173 1174 def is_erc1820(self) -> bool: 1175 """ 1176 Check if the contract is an erc1820 1177 1178 Note: it does not check for correct return values 1179 :return: Returns a true if the contract is an erc165 1180 """ 1181 full_names = self.functions_signatures 1182 return all(s in full_names for s in ERC1820_signatures) 1183 1184 def is_erc223(self) -> bool: 1185 """ 1186 Check if the contract is an erc223 token 1187 1188 Note: it does not check for correct return values 1189 :return: Returns a true if the contract is an erc223 1190 """ 1191 full_names = self.functions_signatures 1192 return all(s in full_names for s in ERC223_signatures) 1193 1194 def is_erc721(self) -> bool: 1195 """ 1196 Check if the contract is an erc721 token 1197 1198 Note: it does not check for correct return values 1199 :return: Returns a true if the contract is an erc721 1200 """ 1201 full_names = self.functions_signatures 1202 return all(s in full_names for s in ERC721_signatures) 1203 1204 def is_erc777(self) -> bool: 1205 """ 1206 Check if the contract is an erc777 1207 1208 Note: it does not check for correct return values 1209 :return: Returns a true if the contract is an erc165 1210 """ 1211 full_names = self.functions_signatures 1212 return all(s in full_names for s in ERC777_signatures) 1213 1214 def is_erc1155(self) -> bool: 1215 """ 1216 Check if the contract is an erc1155 1217 1218 Note: it does not check for correct return values 1219 :return: Returns a true if the contract is an erc1155 1220 """ 1221 full_names = self.functions_signatures 1222 return all(s in full_names for s in ERC1155_signatures) 1223 1224 def is_erc4626(self) -> bool: 1225 """ 1226 Check if the contract is an erc4626 1227 1228 Note: it does not check for correct return values 1229 :return: Returns a true if the contract is an erc4626 1230 """ 1231 full_names = self.functions_signatures 1232 return all(s in full_names for s in ERC4626_signatures) 1233 1234 def is_erc2612(self) -> bool: 1235 """ 1236 Check if the contract is an erc2612 1237 1238 Note: it does not check for correct return values 1239 :return: Returns a true if the contract is an erc2612 1240 """ 1241 full_names = self.functions_signatures 1242 return all(s in full_names for s in ERC2612_signatures) 1243 1244 def is_erc1363(self) -> bool: 1245 """ 1246 Check if the contract is an erc1363 1247 1248 Note: it does not check for correct return values 1249 :return: Returns a true if the contract is an erc1363 1250 """ 1251 full_names = self.functions_signatures 1252 return all(s in full_names for s in ERC1363_signatures) 1253 1254 def is_erc4524(self) -> bool: 1255 """ 1256 Check if the contract is an erc4524 1257 1258 Note: it does not check for correct return values 1259 :return: Returns a true if the contract is an erc4524 1260 """ 1261 full_names = self.functions_signatures 1262 return all(s in full_names for s in ERC4524_signatures) 1263 1264 @property 1265 def is_token(self) -> bool: 1266 """ 1267 Check if the contract follows one of the standard ERC token 1268 :return: 1269 """ 1270 return ( 1271 self.is_erc20() 1272 or self.is_erc721() 1273 or self.is_erc165() 1274 or self.is_erc223() 1275 or self.is_erc777() 1276 or self.is_erc1155() 1277 ) 1278 1279 def is_possible_erc20(self) -> bool: 1280 """ 1281 Checks if the provided contract could be attempting to implement ERC20 standards. 1282 1283 :return: Returns a boolean indicating if the provided contract met the token standard. 1284 """ 1285 # We do not check for all the functions, as name(), symbol(), might give too many FPs 1286 full_names = self.functions_signatures 1287 return ( 1288 "transfer(address,uint256)" in full_names 1289 or "transferFrom(address,address,uint256)" in full_names 1290 or "approve(address,uint256)" in full_names 1291 ) 1292 1293 def is_possible_erc721(self) -> bool: 1294 """ 1295 Checks if the provided contract could be attempting to implement ERC721 standards. 1296 1297 :return: Returns a boolean indicating if the provided contract met the token standard. 1298 """ 1299 # We do not check for all the functions, as name(), symbol(), might give too many FPs 1300 full_names = self.functions_signatures 1301 return ( 1302 "ownerOf(uint256)" in full_names 1303 or "safeTransferFrom(address,address,uint256,bytes)" in full_names 1304 or "safeTransferFrom(address,address,uint256)" in full_names 1305 or "setApprovalForAll(address,bool)" in full_names 1306 or "getApproved(uint256)" in full_names 1307 or "isApprovedForAll(address,address)" in full_names 1308 ) 1309 1310 @property 1311 def is_possible_token(self) -> bool: 1312 """ 1313 Check if the contract is a potential token (it might not implement all the functions) 1314 :return: 1315 """ 1316 return self.is_possible_erc20() or self.is_possible_erc721() 1317 1318 # endregion 1319 ################################################################################### 1320 ################################################################################### 1321 # region Dependencies 1322 ################################################################################### 1323 ################################################################################### 1324 1325 def is_from_dependency(self) -> bool: 1326 return self.compilation_unit.core.crytic_compile.is_dependency( 1327 self.source_mapping.filename.absolute 1328 ) 1329 1330 # endregion 1331 ################################################################################### 1332 ################################################################################### 1333 # region Test 1334 ################################################################################### 1335 ################################################################################### 1336 1337 @property 1338 def is_truffle_migration(self) -> bool: 1339 """ 1340 Return true if the contract is the Migrations contract needed for Truffle 1341 :return: 1342 """ 1343 if self.compilation_unit.core.crytic_compile.platform == PlatformType.TRUFFLE: 1344 if self.name == "Migrations": 1345 paths = Path(self.source_mapping.filename.absolute).parts 1346 if len(paths) >= 2: 1347 return paths[-2] == "contracts" and paths[-1] == "migrations.sol" 1348 return False 1349 1350 @property 1351 def is_test(self) -> bool: 1352 return is_test_contract(self) or self.is_truffle_migration # type: ignore 1353 1354 # endregion 1355 ################################################################################### 1356 ################################################################################### 1357 # region Function analyses 1358 ################################################################################### 1359 ################################################################################### 1360 1361 def update_read_write_using_ssa(self) -> None: 1362 for function in self.functions + list(self.modifiers): 1363 function.update_read_write_using_ssa() 1364 1365 # endregion 1366 ################################################################################### 1367 ################################################################################### 1368 # region Upgradeability 1369 ################################################################################### 1370 ################################################################################### 1371 1372 @property 1373 def is_upgradeable(self) -> bool: 1374 if self._is_upgradeable is None: 1375 self._is_upgradeable = False 1376 initializable = self.file_scope.get_contract_from_name("Initializable") 1377 if initializable: 1378 if initializable in self.inheritance: 1379 self._is_upgradeable = True 1380 else: 1381 for contract in self.inheritance + [self]: 1382 # This might lead to false positive 1383 # Not sure why pylint is having a trouble here 1384 # pylint: disable=no-member 1385 lower_name = contract.name.lower() 1386 if "upgradeable" in lower_name or "upgradable" in lower_name: 1387 self._is_upgradeable = True 1388 break 1389 if "initializable" in lower_name: 1390 self._is_upgradeable = True 1391 break 1392 return self._is_upgradeable 1393 1394 @is_upgradeable.setter 1395 def is_upgradeable(self, upgradeable: bool) -> None: 1396 self._is_upgradeable = upgradeable 1397 1398 @property 1399 def is_upgradeable_proxy(self) -> bool: 1400 from slither.core.cfg.node import NodeType 1401 from slither.slithir.operations import LowLevelCall 1402 1403 if self._is_upgradeable_proxy is None: 1404 self._is_upgradeable_proxy = False 1405 if "Proxy" in self.name: 1406 self._is_upgradeable_proxy = True 1407 return True 1408 for f in self.functions: 1409 if f.is_fallback: 1410 for node in f.all_nodes(): 1411 for ir in node.irs: 1412 if isinstance(ir, LowLevelCall) and ir.function_name == "delegatecall": 1413 self._is_upgradeable_proxy = True 1414 return self._is_upgradeable_proxy 1415 if node.type == NodeType.ASSEMBLY: 1416 inline_asm = node.inline_asm 1417 if inline_asm: 1418 if "delegatecall" in inline_asm: 1419 self._is_upgradeable_proxy = True 1420 return self._is_upgradeable_proxy 1421 return self._is_upgradeable_proxy 1422 1423 @is_upgradeable_proxy.setter 1424 def is_upgradeable_proxy(self, upgradeable_proxy: bool) -> None: 1425 self._is_upgradeable_proxy = upgradeable_proxy 1426 1427 @property 1428 def upgradeable_version(self) -> Optional[str]: 1429 return self._upgradeable_version 1430 1431 @upgradeable_version.setter 1432 def upgradeable_version(self, version_name: str) -> None: 1433 self._upgradeable_version = version_name 1434 1435 # endregion 1436 ################################################################################### 1437 ################################################################################### 1438 # region Internals 1439 ################################################################################### 1440 ################################################################################### 1441 1442 @property 1443 def is_incorrectly_constructed(self) -> bool: 1444 """ 1445 Return true if there was an internal Slither's issue when analyzing the contract 1446 :return: 1447 """ 1448 return self._is_incorrectly_parsed 1449 1450 @is_incorrectly_constructed.setter 1451 def is_incorrectly_constructed(self, incorrect: bool) -> None: 1452 self._is_incorrectly_parsed = incorrect 1453 1454 def add_constructor_variables(self) -> None: 1455 from slither.core.declarations.function_contract import FunctionContract 1456 1457 if self.state_variables: 1458 for (idx, variable_candidate) in enumerate(self.state_variables): 1459 if variable_candidate.expression and not variable_candidate.is_constant: 1460 1461 constructor_variable = FunctionContract(self.compilation_unit) 1462 constructor_variable.set_function_type(FunctionType.CONSTRUCTOR_VARIABLES) 1463 constructor_variable.set_contract(self) # type: ignore 1464 constructor_variable.set_contract_declarer(self) # type: ignore 1465 constructor_variable.set_visibility("internal") 1466 # For now, source mapping of the constructor variable is the whole contract 1467 # Could be improved with a targeted source mapping 1468 constructor_variable.set_offset(self.source_mapping, self.compilation_unit) 1469 self._functions[constructor_variable.canonical_name] = constructor_variable 1470 1471 prev_node = self._create_node( 1472 constructor_variable, 0, variable_candidate, constructor_variable 1473 ) 1474 variable_candidate.node_initialization = prev_node 1475 counter = 1 1476 for v in self.state_variables[idx + 1 :]: 1477 if v.expression and not v.is_constant: 1478 next_node = self._create_node( 1479 constructor_variable, counter, v, prev_node.scope 1480 ) 1481 v.node_initialization = next_node 1482 prev_node.add_son(next_node) 1483 next_node.add_father(prev_node) 1484 prev_node = next_node 1485 counter += 1 1486 break 1487 1488 for (idx, variable_candidate) in enumerate(self.state_variables): 1489 if variable_candidate.expression and variable_candidate.is_constant: 1490 1491 constructor_variable = FunctionContract(self.compilation_unit) 1492 constructor_variable.set_function_type( 1493 FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES 1494 ) 1495 constructor_variable.set_contract(self) # type: ignore 1496 constructor_variable.set_contract_declarer(self) # type: ignore 1497 constructor_variable.set_visibility("internal") 1498 # For now, source mapping of the constructor variable is the whole contract 1499 # Could be improved with a targeted source mapping 1500 constructor_variable.set_offset(self.source_mapping, self.compilation_unit) 1501 self._functions[constructor_variable.canonical_name] = constructor_variable 1502 1503 prev_node = self._create_node( 1504 constructor_variable, 0, variable_candidate, constructor_variable 1505 ) 1506 variable_candidate.node_initialization = prev_node 1507 counter = 1 1508 for v in self.state_variables[idx + 1 :]: 1509 if v.expression and v.is_constant: 1510 next_node = self._create_node( 1511 constructor_variable, counter, v, prev_node.scope 1512 ) 1513 v.node_initialization = next_node 1514 prev_node.add_son(next_node) 1515 next_node.add_father(prev_node) 1516 prev_node = next_node 1517 counter += 1 1518 1519 break 1520 1521 def _create_node( 1522 self, func: Function, counter: int, variable: "Variable", scope: Union[Scope, Function] 1523 ) -> "Node": 1524 from slither.core.cfg.node import Node, NodeType 1525 from slither.core.expressions import ( 1526 AssignmentOperationType, 1527 AssignmentOperation, 1528 Identifier, 1529 ) 1530 1531 # Function uses to create node for state variable declaration statements 1532 node = Node(NodeType.OTHER_ENTRYPOINT, counter, scope, func.file_scope) 1533 node.set_offset(variable.source_mapping, self.compilation_unit) 1534 node.set_function(func) 1535 func.add_node(node) 1536 assert variable.expression 1537 expression = AssignmentOperation( 1538 Identifier(variable), 1539 variable.expression, 1540 AssignmentOperationType.ASSIGN, 1541 variable.type, 1542 ) 1543 1544 expression.set_offset(variable.source_mapping, self.compilation_unit) 1545 node.add_expression(expression) 1546 return node 1547 1548 # endregion 1549 ################################################################################### 1550 ################################################################################### 1551 # region SlithIR 1552 ################################################################################### 1553 ################################################################################### 1554 1555 def convert_expression_to_slithir_ssa(self) -> None: 1556 """ 1557 Assume generate_slithir_and_analyze was called on all functions 1558 1559 :return: 1560 """ 1561 from slither.slithir.variables import StateIRVariable 1562 1563 all_ssa_state_variables_instances = {} 1564 1565 for contract in self.inheritance: 1566 for v in contract.state_variables_declared: 1567 new_var = StateIRVariable(v) 1568 all_ssa_state_variables_instances[v.canonical_name] = new_var 1569 self._initial_state_variables.append(new_var) 1570 1571 for v in self.variables: 1572 if v.contract == self: 1573 new_var = StateIRVariable(v) 1574 all_ssa_state_variables_instances[v.canonical_name] = new_var 1575 self._initial_state_variables.append(new_var) 1576 1577 for func in self.functions + list(self.modifiers): 1578 func.generate_slithir_ssa(all_ssa_state_variables_instances) 1579 1580 def fix_phi(self) -> None: 1581 last_state_variables_instances: Dict[str, List["StateVariable"]] = {} 1582 initial_state_variables_instances: Dict[str, "StateVariable"] = {} 1583 for v in self._initial_state_variables: 1584 last_state_variables_instances[v.canonical_name] = [] 1585 initial_state_variables_instances[v.canonical_name] = v 1586 1587 for func in self.functions + list(self.modifiers): 1588 result = func.get_last_ssa_state_variables_instances() 1589 for variable_name, instances in result.items(): 1590 # TODO: investigate the next operation 1591 last_state_variables_instances[variable_name] += list(instances) 1592 1593 for func in self.functions + list(self.modifiers): 1594 func.fix_phi(last_state_variables_instances, initial_state_variables_instances) 1595 1596 # endregion 1597 ################################################################################### 1598 ################################################################################### 1599 # region Built in definitions 1600 ################################################################################### 1601 ################################################################################### 1602 1603 def __eq__(self, other: Any) -> bool: 1604 if isinstance(other, str): 1605 return other == self.name 1606 return NotImplemented 1607 1608 def __neq__(self, other: Any) -> bool: 1609 if isinstance(other, str): 1610 return other != self.name 1611 return NotImplemented 1612 1613 def __str__(self) -> str: 1614 return self.name 1615 1616 def __hash__(self) -> int: 1617 return self._id # type:ignore 1618 1619 # endregion
Contract class
59 def __init__(self, compilation_unit: "SlitherCompilationUnit", scope: "FileScope") -> None: 60 super().__init__() 61 62 self._name: Optional[str] = None 63 self._id: Optional[int] = None 64 self._inheritance: List["Contract"] = [] # all contract inherited, c3 linearization 65 self._immediate_inheritance: List["Contract"] = [] # immediate inheritance 66 67 # Constructors called on contract's definition 68 # contract B is A(1) { .. 69 self._explicit_base_constructor_calls: List["Contract"] = [] 70 71 self._enums: Dict[str, "EnumContract"] = {} 72 self._structures: Dict[str, "StructureContract"] = {} 73 self._events: Dict[str, "EventContract"] = {} 74 # map accessible variable from name -> variable 75 # do not contain private variables inherited from contract 76 self._variables: Dict[str, "StateVariable"] = {} 77 self._variables_ordered: List["StateVariable"] = [] 78 # Reference id -> variable declaration (only available for compact AST) 79 self._state_variables_by_ref_id: Dict[int, "StateVariable"] = {} 80 self._modifiers: Dict[str, "Modifier"] = {} 81 self._functions: Dict[str, "FunctionContract"] = {} 82 self._linearizedBaseContracts: List[int] = [] 83 self._custom_errors: Dict[str, "CustomErrorContract"] = {} 84 self._type_aliases: Dict[str, "TypeAliasContract"] = {} 85 86 # The only str is "*" 87 self._using_for: USING_FOR = {} 88 self._using_for_complete: Optional[USING_FOR] = None 89 self._kind: Optional[str] = None 90 self._is_interface: bool = False 91 self._is_library: bool = False 92 self._is_fully_implemented: bool = False 93 self._is_abstract: bool = False 94 95 self._signatures: Optional[List[str]] = None 96 self._signatures_declared: Optional[List[str]] = None 97 98 self._fallback_function: Optional["FunctionContract"] = None 99 self._receive_function: Optional["FunctionContract"] = None 100 101 self._is_upgradeable: Optional[bool] = None 102 self._is_upgradeable_proxy: Optional[bool] = None 103 self._upgradeable_version: Optional[str] = None 104 105 self._initial_state_variables: List["StateVariable"] = [] # ssa 106 107 self._is_incorrectly_parsed: bool = False 108 109 self._available_functions_as_dict: Optional[Dict[str, "Function"]] = None 110 self._all_functions_called: Optional[List["InternalCallType"]] = None 111 112 self.compilation_unit: "SlitherCompilationUnit" = compilation_unit 113 self.file_scope: "FileScope" = scope 114 115 # memoize 116 self._state_variables_used_in_reentrant_targets: Optional[ 117 Dict["StateVariable", Set[Union["StateVariable", "Function"]]] 118 ] = None 119 120 self._comments: Optional[str] = None
128 @property 129 def name(self) -> str: 130 """str: Name of the contract.""" 131 assert self._name 132 return self._name
str: Name of the contract.
138 @property 139 def id(self) -> int: 140 """Unique id.""" 141 assert self._id is not None 142 return self._id
Unique id.
149 @property 150 def contract_kind(self) -> Optional[str]: 151 """ 152 contract_kind can be None if the legacy ast format is used 153 :return: 154 """ 155 return self._kind
contract_kind can be None if the legacy ast format is used
Returns
177 @property 178 def comments(self) -> Optional[str]: 179 """ 180 Return the comments associated with the contract. 181 182 When using comments, avoid strict text matching, as the solc behavior might change. 183 For example, for old solc version, the first space after the * is not kept, i.e: 184 185 * @title Test Contract 186 * @dev Test comment 187 188 Returns 189 - " @title Test Contract\n @dev Test comment" for newest versions 190 - "@title Test Contract\n@dev Test comment" for older versions 191 192 193 Returns: 194 the comment as a string 195 """ 196 return self._comments
Return the comments associated with the contract.
When using comments, avoid strict text matching, as the solc behavior might change.
For example, for old solc version, the first space after the * is not kept, i.e:
* @title Test Contract
* @dev Test comment
Returns
- " @title Test Contract
@dev Test comment" for newest versions - "@title Test Contract @dev Test comment" for older versions
Returns:
the comment as a string
202 @property 203 def is_fully_implemented(self) -> bool: 204 """ 205 bool: True if the contract defines all functions. 206 In modern Solidity, virtual functions can lack an implementation. 207 Prior to Solidity 0.6.0, functions like the following would be not fully implemented: 208 ```solidity 209 contract ImplicitAbstract{ 210 function f() public; 211 } 212 ``` 213 """ 214 return self._is_fully_implemented
bool: True if the contract defines all functions. In modern Solidity, virtual functions can lack an implementation. Prior to Solidity 0.6.0, functions like the following would be not fully implemented:
contract ImplicitAbstract{
function f() public;
}
220 @property 221 def is_abstract(self) -> bool: 222 """ 223 Note for Solidity < 0.6.0 it will always be false 224 bool: True if the contract is abstract. 225 """ 226 return self._is_abstract
Note for Solidity < 0.6.0 it will always be false bool: True if the contract is abstract.
239 @property 240 def structures(self) -> List["StructureContract"]: 241 """ 242 list(Structure): List of the structures 243 """ 244 return list(self._structures.values())
list(Structure): List of the structures
246 @property 247 def structures_inherited(self) -> List["StructureContract"]: 248 """ 249 list(Structure): List of the inherited structures 250 """ 251 return [s for s in self.structures if s.contract != self]
list(Structure): List of the inherited structures
253 @property 254 def structures_declared(self) -> List["StructureContract"]: 255 """ 256 list(Structues): List of the structures declared within the contract (not inherited) 257 """ 258 return [s for s in self.structures if s.contract == self]
list(Structues): List of the structures declared within the contract (not inherited)
275 @property 276 def enums_inherited(self) -> List["EnumContract"]: 277 """ 278 list(Enum): List of the inherited enums 279 """ 280 return [e for e in self.enums if e.contract != self]
list(Enum): List of the inherited enums
282 @property 283 def enums_declared(self) -> List["EnumContract"]: 284 """ 285 list(Enum): List of the enums declared within the contract (not inherited) 286 """ 287 return [e for e in self.enums if e.contract == self]
list(Enum): List of the enums declared within the contract (not inherited)
300 @property 301 def events(self) -> List["EventContract"]: 302 """ 303 list(Event): List of the events 304 """ 305 return list(self._events.values())
list(Event): List of the events
307 @property 308 def events_inherited(self) -> List["EventContract"]: 309 """ 310 list(Event): List of the inherited events 311 """ 312 return [e for e in self.events if e.contract != self]
list(Event): List of the inherited events
314 @property 315 def events_declared(self) -> List["EventContract"]: 316 """ 317 list(Event): List of the events declared within the contract (not inherited) 318 """ 319 return [e for e in self.events if e.contract == self]
list(Event): List of the events declared within the contract (not inherited)
336 @property 337 def using_for_complete(self) -> USING_FOR: 338 """ 339 USING_FOR: Dict of merged local using for directive with top level directive 340 """ 341 342 if self._using_for_complete is None: 343 result = self.using_for 344 top_level_using_for = self.file_scope.using_for_directives 345 for uftl in top_level_using_for: 346 result = merge_using_for(result, uftl.using_for) 347 self._using_for_complete = result 348 return self._using_for_complete
USING_FOR: Dict of merged local using for directive with top level directive
357 @property 358 def custom_errors(self) -> List["CustomErrorContract"]: 359 """ 360 list(CustomErrorContract): List of the contract's custom errors 361 """ 362 return list(self._custom_errors.values())
list(CustomErrorContract): List of the contract's custom errors
364 @property 365 def custom_errors_inherited(self) -> List["CustomErrorContract"]: 366 """ 367 list(CustomErrorContract): List of the inherited custom errors 368 """ 369 return [s for s in self.custom_errors if s.contract != self]
list(CustomErrorContract): List of the inherited custom errors
371 @property 372 def custom_errors_declared(self) -> List["CustomErrorContract"]: 373 """ 374 list(CustomErrorContract): List of the custom errors declared within the contract (not inherited) 375 """ 376 return [s for s in self.custom_errors if s.contract == self]
list(CustomErrorContract): List of the custom errors declared within the contract (not inherited)
389 @property 390 def type_aliases(self) -> List["TypeAliasContract"]: 391 """ 392 list(TypeAliasContract): List of the contract's custom errors 393 """ 394 return list(self._type_aliases.values())
list(TypeAliasContract): List of the contract's custom errors
396 @property 397 def type_aliases_inherited(self) -> List["TypeAliasContract"]: 398 """ 399 list(TypeAliasContract): List of the inherited custom errors 400 """ 401 return [s for s in self.type_aliases if s.contract != self]
list(TypeAliasContract): List of the inherited custom errors
403 @property 404 def type_aliases_declared(self) -> List["TypeAliasContract"]: 405 """ 406 list(TypeAliasContract): List of the custom errors declared within the contract (not inherited) 407 """ 408 return [s for s in self.type_aliases if s.contract == self]
list(TypeAliasContract): List of the custom errors declared within the contract (not inherited)
420 @property 421 def state_variables_by_ref_id(self) -> Dict[int, "StateVariable"]: 422 """ 423 Returns the state variables by reference id (only available for compact AST). 424 """ 425 return self._state_variables_by_ref_id
Returns the state variables by reference id (only available for compact AST).
427 @property 428 def variables(self) -> List["StateVariable"]: 429 """ 430 Returns all the accessible variables (do not include private variable from inherited contract) 431 432 list(StateVariable): List of the state variables. Alias to self.state_variables. 433 """ 434 return list(self.state_variables)
Returns all the accessible variables (do not include private variable from inherited contract)
list(StateVariable): List of the state variables. Alias to self.state_variables.
440 @property 441 def state_variables(self) -> List["StateVariable"]: 442 """ 443 Returns all the accessible variables (do not include private variable from inherited contract). 444 Use state_variables_ordered for all the variables following the storage order 445 446 list(StateVariable): List of the state variables. 447 """ 448 return list(self._variables.values())
Returns all the accessible variables (do not include private variable from inherited contract). Use state_variables_ordered for all the variables following the storage order
list(StateVariable): List of the state variables.
450 @property 451 def stored_state_variables(self) -> List["StateVariable"]: 452 """ 453 Returns state variables with storage locations, excluding private variables from inherited contracts. 454 Use stored_state_variables_ordered to access variables with storage locations in their declaration order. 455 456 This implementation filters out state variables if they are constant or immutable. It will be 457 updated to accommodate any new non-storage keywords that might replace 'constant' and 'immutable' in the future. 458 459 Returns: 460 List[StateVariable]: A list of state variables with storage locations. 461 """ 462 return [variable for variable in self.state_variables if variable.is_stored]
Returns state variables with storage locations, excluding private variables from inherited contracts. Use stored_state_variables_ordered to access variables with storage locations in their declaration order.
This implementation filters out state variables if they are constant or immutable. It will be updated to accommodate any new non-storage keywords that might replace 'constant' and 'immutable' in the future.
Returns: List[StateVariable]: A list of state variables with storage locations.
464 @property 465 def stored_state_variables_ordered(self) -> List["StateVariable"]: 466 """ 467 list(StateVariable): List of the state variables with storage locations by order of declaration. 468 469 This implementation filters out state variables if they are constant or immutable. It will be 470 updated to accommodate any new non-storage keywords that might replace 'constant' and 'immutable' in the future. 471 472 Returns: 473 List[StateVariable]: A list of state variables with storage locations ordered by declaration. 474 """ 475 return [variable for variable in self.state_variables_ordered if variable.is_stored]
list(StateVariable): List of the state variables with storage locations by order of declaration.
This implementation filters out state variables if they are constant or immutable. It will be updated to accommodate any new non-storage keywords that might replace 'constant' and 'immutable' in the future.
Returns: List[StateVariable]: A list of state variables with storage locations ordered by declaration.
477 @property 478 def state_variables_entry_points(self) -> List["StateVariable"]: 479 """ 480 list(StateVariable): List of the state variables that are public. 481 """ 482 return [var for var in self._variables.values() if var.visibility == "public"]
list(StateVariable): List of the state variables that are public.
484 @property 485 def state_variables_ordered(self) -> List["StateVariable"]: 486 """ 487 list(StateVariable): List of the state variables by order of declaration. 488 """ 489 return list(self._variables_ordered)
list(StateVariable): List of the state variables by order of declaration.
494 @property 495 def state_variables_inherited(self) -> List["StateVariable"]: 496 """ 497 list(StateVariable): List of the inherited state variables 498 """ 499 return [s for s in self.state_variables if s.contract != self]
list(StateVariable): List of the inherited state variables
501 @property 502 def state_variables_declared(self) -> List["StateVariable"]: 503 """ 504 list(StateVariable): List of the state variables declared within the contract (not inherited) 505 """ 506 return [s for s in self.state_variables if s.contract == self]
list(StateVariable): List of the state variables declared within the contract (not inherited)
508 @property 509 def slithir_variables(self) -> List["SlithIRVariable"]: 510 """ 511 List all of the slithir variables (non SSA) 512 """ 513 slithir_variabless = [f.slithir_variables for f in self.functions + self.modifiers] # type: ignore 514 slithir_variables = [item for sublist in slithir_variabless for item in sublist] 515 return list(set(slithir_variables))
List all of the slithir variables (non SSA)
517 @property 518 def state_variables_used_in_reentrant_targets( 519 self, 520 ) -> Dict["StateVariable", Set[Union["StateVariable", "Function"]]]: 521 """ 522 Returns the state variables used in reentrant targets. Heuristics: 523 - Variable used (read/write) in entry points that are reentrant 524 - State variables that are public 525 526 """ 527 from slither.core.variables.state_variable import StateVariable 528 529 if self._state_variables_used_in_reentrant_targets is None: 530 reentrant_functions = [f for f in self.functions_entry_points if f.is_reentrant] 531 variables_used: Dict[ 532 StateVariable, Set[Union[StateVariable, "Function"]] 533 ] = defaultdict(set) 534 for function in reentrant_functions: 535 for ir in function.all_slithir_operations(): 536 state_variables = [v for v in ir.used if isinstance(v, StateVariable)] 537 for state_variable in state_variables: 538 variables_used[state_variable].add(ir.node.function) 539 for variable in [v for v in self.state_variables if v.visibility == "public"]: 540 variables_used[variable].add(variable) 541 self._state_variables_used_in_reentrant_targets = variables_used 542 return self._state_variables_used_in_reentrant_targets
Returns the state variables used in reentrant targets. Heuristics:
- Variable used (read/write) in entry points that are reentrant
- State variables that are public
551 @property 552 def constructor(self) -> Optional["Function"]: 553 """ 554 Return the contract's immediate constructor. 555 If there is no immediate constructor, returns the first constructor 556 executed, following the c3 linearization 557 Return None if there is no constructor. 558 """ 559 cst = self.constructors_declared 560 if cst: 561 return cst 562 for inherited_contract in self.inheritance: 563 cst = inherited_contract.constructors_declared 564 if cst: 565 return cst 566 return None
Return the contract's immediate constructor. If there is no immediate constructor, returns the first constructor executed, following the c3 linearization Return None if there is no constructor.
579 @property 580 def constructors(self) -> List["FunctionContract"]: 581 """ 582 Return the list of constructors (including inherited) 583 """ 584 return [func for func in self.functions if func.is_constructor]
Return the list of constructors (including inherited)
586 @property 587 def explicit_base_constructor_calls(self) -> List["Function"]: 588 """ 589 list(Function): List of the base constructors called explicitly by this contract definition. 590 591 Base constructors called by any constructor definition will not be included. 592 Base constructors implicitly called by the contract definition (without 593 parenthesis) will not be included. 594 595 On "contract B is A(){..}" it returns the constructor of A 596 """ 597 return [c.constructor for c in self._explicit_base_constructor_calls if c.constructor]
list(Function): List of the base constructors called explicitly by this contract definition.
Base constructors called by any constructor definition will not be included. Base constructors implicitly called by the contract definition (without parenthesis) will not be included.
On "contract B is A(){..}" it returns the constructor of A
606 @property 607 def functions_signatures(self) -> List[str]: 608 """ 609 Return the signatures of all the public/eterxnal functions/state variables 610 :return: list(string) the signatures of all the functions that can be called 611 """ 612 if self._signatures is None: 613 sigs = [ 614 v.full_name for v in self.state_variables if v.visibility in ["public", "external"] 615 ] 616 617 sigs += {f.full_name for f in self.functions if f.visibility in ["public", "external"]} 618 self._signatures = list(set(sigs)) 619 return self._signatures
Return the signatures of all the public/eterxnal functions/state variables
Returns
list(string) the signatures of all the functions that can be called
621 @property 622 def functions_signatures_declared(self) -> List[str]: 623 """ 624 Return the signatures of the public/eterxnal functions/state variables that are declared by this contract 625 :return: list(string) the signatures of all the functions that can be called and are declared by this contract 626 """ 627 if self._signatures_declared is None: 628 sigs = [ 629 v.full_name 630 for v in self.state_variables_declared 631 if v.visibility in ["public", "external"] 632 ] 633 634 sigs += { 635 f.full_name 636 for f in self.functions_declared 637 if f.visibility in ["public", "external"] 638 } 639 self._signatures_declared = list(set(sigs)) 640 return self._signatures_declared
Return the signatures of the public/eterxnal functions/state variables that are declared by this contract
Returns
list(string) the signatures of all the functions that can be called and are declared by this contract
642 @property 643 def functions(self) -> List["FunctionContract"]: 644 """ 645 list(Function): List of the functions 646 """ 647 return list(self._functions.values())
list(Function): List of the functions
659 def set_functions(self, functions: Dict[str, "FunctionContract"]) -> None: 660 """ 661 Set the functions 662 663 :param functions: dict full_name -> function 664 :return: 665 """ 666 self._functions = functions
Set the functions
Parameters
- functions: dict full_name -> function
Returns
668 @property 669 def functions_inherited(self) -> List["FunctionContract"]: 670 """ 671 list(Function): List of the inherited functions 672 """ 673 return [f for f in self.functions if f.contract_declarer != self]
list(Function): List of the inherited functions
675 @property 676 def functions_declared(self) -> List["FunctionContract"]: 677 """ 678 list(Function): List of the functions defined within the contract (not inherited) 679 """ 680 return [f for f in self.functions if f.contract_declarer == self]
list(Function): List of the functions defined within the contract (not inherited)
682 @property 683 def functions_entry_points(self) -> List["FunctionContract"]: 684 """ 685 list(Functions): List of public and external functions 686 """ 687 return [ 688 f 689 for f in self.functions 690 if f.visibility in ["public", "external"] and not f.is_shadowed or f.is_fallback 691 ]
list(Functions): List of public and external functions
693 @property 694 def modifiers(self) -> List["Modifier"]: 695 """ 696 list(Modifier): List of the modifiers 697 """ 698 return list(self._modifiers.values())
list(Modifier): List of the modifiers
703 def set_modifiers(self, modifiers: Dict[str, "Modifier"]) -> None: 704 """ 705 Set the modifiers 706 707 :param modifiers: dict full_name -> modifier 708 :return: 709 """ 710 self._modifiers = modifiers
Set the modifiers
Parameters
- modifiers: dict full_name -> modifier
Returns
712 @property 713 def modifiers_inherited(self) -> List["Modifier"]: 714 """ 715 list(Modifier): List of the inherited modifiers 716 """ 717 return [m for m in self.modifiers if m.contract_declarer != self]
list(Modifier): List of the inherited modifiers
719 @property 720 def modifiers_declared(self) -> List["Modifier"]: 721 """ 722 list(Modifier): List of the modifiers defined within the contract (not inherited) 723 """ 724 return [m for m in self.modifiers if m.contract_declarer == self]
list(Modifier): List of the modifiers defined within the contract (not inherited)
726 @property 727 def functions_and_modifiers(self) -> List["Function"]: 728 """ 729 list(Function|Modifier): List of the functions and modifiers 730 """ 731 return self.functions + self.modifiers # type: ignore
list(Function|Modifier): List of the functions and modifiers
733 @property 734 def functions_and_modifiers_inherited(self) -> List["Function"]: 735 """ 736 list(Function|Modifier): List of the inherited functions and modifiers 737 """ 738 return self.functions_inherited + self.modifiers_inherited # type: ignore
list(Function|Modifier): List of the inherited functions and modifiers
740 @property 741 def functions_and_modifiers_declared(self) -> List["Function"]: 742 """ 743 list(Function|Modifier): List of the functions and modifiers defined within the contract (not inherited) 744 """ 745 return self.functions_declared + self.modifiers_declared # type: ignore
list(Function|Modifier): List of the functions and modifiers defined within the contract (not inherited)
765 def available_elements_from_inheritances( 766 self, 767 elements: Dict[str, "Function"], 768 getter_available: Callable[["Contract"], List["FunctionContract"]], 769 ) -> Dict[str, "Function"]: 770 """ 771 772 :param elements: dict(canonical_name -> elements) 773 :param getter_available: fun x 774 :return: 775 """ 776 # keep track of the contracts visited 777 # to prevent an ovveride due to multiple inheritance of the same contract 778 # A is B, C, D is C, --> the second C was already seen 779 inherited_elements: Dict[str, "FunctionContract"] = {} 780 accessible_elements = {} 781 contracts_visited = [] 782 for father in self.inheritance_reverse: 783 functions: Dict[str, "FunctionContract"] = { 784 v.full_name: v 785 for v in getter_available(father) 786 if v.contract not in contracts_visited 787 and v.function_language 788 != FunctionLanguage.Yul # Yul functions are not propagated in the inheritance 789 } 790 contracts_visited.append(father) 791 inherited_elements.update(functions) 792 793 for element in inherited_elements.values(): 794 accessible_elements[element.full_name] = elements[element.canonical_name] 795 796 return accessible_elements
Parameters
- elements: dict(canonical_name -> elements)
- getter_available: fun x
Returns
805 @property 806 def inheritance(self) -> List["Contract"]: 807 """ 808 list(Contract): Inheritance list. Order: the first elem is the first father to be executed 809 """ 810 return list(self._inheritance)
list(Contract): Inheritance list. Order: the first elem is the first father to be executed
812 @property 813 def immediate_inheritance(self) -> List["Contract"]: 814 """ 815 list(Contract): List of contracts immediately inherited from (fathers). Order: order of declaration. 816 """ 817 return list(self._immediate_inheritance)
list(Contract): List of contracts immediately inherited from (fathers). Order: order of declaration.
819 @property 820 def inheritance_reverse(self) -> List["Contract"]: 821 """ 822 list(Contract): Inheritance list. Order: the last elem is the first father to be executed 823 """ 824 return list(reversed(self._inheritance))
list(Contract): Inheritance list. Order: the last elem is the first father to be executed
826 def set_inheritance( 827 self, 828 inheritance: List["Contract"], 829 immediate_inheritance: List["Contract"], 830 called_base_constructor_contracts: List["Contract"], 831 ) -> None: 832 self._inheritance = inheritance 833 self._immediate_inheritance = immediate_inheritance 834 self._explicit_base_constructor_calls = called_base_constructor_contracts
836 @property 837 def derived_contracts(self) -> List["Contract"]: 838 """ 839 list(Contract): Return the list of contracts derived from self 840 """ 841 candidates = self.compilation_unit.contracts 842 return [c for c in candidates if self in c.inheritance] # type: ignore
list(Contract): Return the list of contracts derived from self
851 def get_functions_reading_from_variable(self, variable: "Variable") -> List["Function"]: 852 """ 853 Return the functions reading the variable 854 """ 855 return [f for f in self.functions if f.is_reading(variable)]
Return the functions reading the variable
857 def get_functions_writing_to_variable(self, variable: "Variable") -> List["Function"]: 858 """ 859 Return the functions writting the variable 860 """ 861 return [f for f in self.functions if f.is_writing(variable)]
Return the functions writting the variable
863 def get_function_from_full_name(self, full_name: str) -> Optional["Function"]: 864 """ 865 Return a function from a full name 866 The full name differs from the solidity's signature are the type are conserved 867 For example contract type are kept, structure are not unrolled, etc 868 Args: 869 full_name (str): signature of the function (without return statement) 870 Returns: 871 Function 872 """ 873 return next( 874 (f for f in self.functions if f.full_name == full_name and not f.is_shadowed), 875 None, 876 )
Return a function from a full name The full name differs from the solidity's signature are the type are conserved For example contract type are kept, structure are not unrolled, etc Args: full_name (str): signature of the function (without return statement) Returns: Function
878 def get_function_from_signature(self, function_signature: str) -> Optional["Function"]: 879 """ 880 Return a function from a signature 881 Args: 882 function_signature (str): signature of the function (without return statement) 883 Returns: 884 Function 885 """ 886 return next( 887 ( 888 f 889 for f in self.functions 890 if f.solidity_signature == function_signature and not f.is_shadowed 891 ), 892 None, 893 )
Return a function from a signature Args: function_signature (str): signature of the function (without return statement) Returns: Function
895 def get_modifier_from_signature(self, modifier_signature: str) -> Optional["Modifier"]: 896 """ 897 Return a modifier from a signature 898 899 :param modifier_signature: 900 """ 901 return next( 902 (m for m in self.modifiers if m.full_name == modifier_signature and not m.is_shadowed), 903 None, 904 )
Return a modifier from a signature
Parameters
- modifier_signature:
906 def get_function_from_canonical_name(self, canonical_name: str) -> Optional["Function"]: 907 """ 908 Return a function from a canonical name (contract.signature()) 909 Args: 910 canonical_name (str): canonical name of the function (without return statement) 911 Returns: 912 Function 913 """ 914 return next((f for f in self.functions if f.canonical_name == canonical_name), None)
Return a function from a canonical name (contract.signature()) Args: canonical_name (str): canonical name of the function (without return statement) Returns: Function
916 def get_modifier_from_canonical_name(self, canonical_name: str) -> Optional["Modifier"]: 917 """ 918 Return a modifier from a canonical name (contract.signature()) 919 Args: 920 canonical_name (str): canonical name of the modifier 921 Returns: 922 Modifier 923 """ 924 return next((m for m in self.modifiers if m.canonical_name == canonical_name), None)
Return a modifier from a canonical name (contract.signature()) Args: canonical_name (str): canonical name of the modifier Returns: Modifier
926 def get_state_variable_from_name(self, variable_name: str) -> Optional["StateVariable"]: 927 """ 928 Return a state variable from a name 929 930 :param variable_name: 931 """ 932 return next((v for v in self.state_variables if v.name == variable_name), None)
Return a state variable from a name
Parameters
- variable_name:
934 def get_state_variable_from_canonical_name( 935 self, canonical_name: str 936 ) -> Optional["StateVariable"]: 937 """ 938 Return a state variable from a canonical_name 939 Args: 940 canonical_name (str): name of the variable 941 Returns: 942 StateVariable 943 """ 944 return next((v for v in self.state_variables if v.canonical_name == canonical_name), None)
Return a state variable from a canonical_name Args: canonical_name (str): name of the variable Returns: StateVariable
946 def get_structure_from_name(self, structure_name: str) -> Optional["StructureContract"]: 947 """ 948 Return a structure from a name 949 Args: 950 structure_name (str): name of the structure 951 Returns: 952 StructureContract 953 """ 954 return next((st for st in self.structures if st.name == structure_name), None)
Return a structure from a name Args: structure_name (str): name of the structure Returns: StructureContract
956 def get_structure_from_canonical_name( 957 self, structure_name: str 958 ) -> Optional["StructureContract"]: 959 """ 960 Return a structure from a canonical name 961 Args: 962 structure_name (str): canonical name of the structure 963 Returns: 964 StructureContract 965 """ 966 return next((st for st in self.structures if st.canonical_name == structure_name), None)
Return a structure from a canonical name Args: structure_name (str): canonical name of the structure Returns: StructureContract
968 def get_event_from_signature(self, event_signature: str) -> Optional["Event"]: 969 """ 970 Return an event from a signature 971 Args: 972 event_signature (str): signature of the event 973 Returns: 974 Event 975 """ 976 return next((e for e in self.events if e.full_name == event_signature), None)
Return an event from a signature Args: event_signature (str): signature of the event Returns: Event
978 def get_event_from_canonical_name(self, event_canonical_name: str) -> Optional["Event"]: 979 """ 980 Return an event from a canonical name 981 Args: 982 event_canonical_name (str): name of the event 983 Returns: 984 Event 985 """ 986 return next((e for e in self.events if e.canonical_name == event_canonical_name), None)
Return an event from a canonical name Args: event_canonical_name (str): name of the event Returns: Event
988 def get_enum_from_name(self, enum_name: str) -> Optional["Enum"]: 989 """ 990 Return an enum from a name 991 Args: 992 enum_name (str): name of the enum 993 Returns: 994 Enum 995 """ 996 return next((e for e in self.enums if e.name == enum_name), None)
Return an enum from a name Args: enum_name (str): name of the enum Returns: Enum
998 def get_enum_from_canonical_name(self, enum_name: str) -> Optional["Enum"]: 999 """ 1000 Return an enum from a canonical name 1001 Args: 1002 enum_name (str): canonical name of the enum 1003 Returns: 1004 Enum 1005 """ 1006 return next((e for e in self.enums if e.canonical_name == enum_name), None)
Return an enum from a canonical name Args: enum_name (str): canonical name of the enum Returns: Enum
1008 def get_functions_overridden_by(self, function: "Function") -> List["Function"]: 1009 """ 1010 Return the list of functions overridden by the function 1011 Args: 1012 (core.Function) 1013 Returns: 1014 list(core.Function) 1015 1016 """ 1017 return function.overrides
Return the list of functions overridden by the function Args: (core.Function) Returns: list(core.Function)
1026 @property 1027 def all_functions_called(self) -> List["InternalCallType"]: 1028 """ 1029 list(Function): List of functions reachable from the contract 1030 Includes super, and private/internal functions not shadowed 1031 """ 1032 if self._all_functions_called is None: 1033 all_functions = [f for f in self.functions + self.modifiers if not f.is_shadowed] # type: ignore 1034 all_callss = [f.all_internal_calls() for f in all_functions] + [list(all_functions)] 1035 all_calls = [item for sublist in all_callss for item in sublist] 1036 all_calls = list(set(all_calls)) 1037 1038 all_constructors = [c.constructor for c in self.inheritance if c.constructor] 1039 all_constructors = list(set(all_constructors)) 1040 1041 set_all_calls = set(all_calls + list(all_constructors)) 1042 1043 self._all_functions_called = [c for c in set_all_calls if isinstance(c, Function)] 1044 return self._all_functions_called
list(Function): List of functions reachable from the contract Includes super, and private/internal functions not shadowed
1046 @property 1047 def all_state_variables_written(self) -> List["StateVariable"]: 1048 """ 1049 list(StateVariable): List all of the state variables written 1050 """ 1051 all_state_variables_writtens = [ 1052 f.all_state_variables_written() for f in self.functions + self.modifiers # type: ignore 1053 ] 1054 all_state_variables_written = [ 1055 item for sublist in all_state_variables_writtens for item in sublist 1056 ] 1057 return list(set(all_state_variables_written))
list(StateVariable): List all of the state variables written
1059 @property 1060 def all_state_variables_read(self) -> List["StateVariable"]: 1061 """ 1062 list(StateVariable): List all of the state variables read 1063 """ 1064 all_state_variables_reads = [ 1065 f.all_state_variables_read() for f in self.functions + self.modifiers # type: ignore 1066 ] 1067 all_state_variables_read = [ 1068 item for sublist in all_state_variables_reads for item in sublist 1069 ] 1070 return list(set(all_state_variables_read))
list(StateVariable): List all of the state variables read
1072 @property 1073 def all_library_calls(self) -> List["LibraryCallType"]: 1074 """ 1075 list((Contract, Function): List all of the libraries func called 1076 """ 1077 all_high_level_callss = [f.all_library_calls() for f in self.functions + self.modifiers] # type: ignore 1078 all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist] 1079 return list(set(all_high_level_calls))
list((Contract, Function): List all of the libraries func called
1081 @property 1082 def all_high_level_calls(self) -> List["HighLevelCallType"]: 1083 """ 1084 list((Contract, Function|Variable)): List all of the external high level calls 1085 """ 1086 all_high_level_callss = [f.all_high_level_calls() for f in self.functions + self.modifiers] # type: ignore 1087 all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist] 1088 return list(set(all_high_level_calls))
list((Contract, Function|Variable)): List all of the external high level calls
1097 def get_summary( 1098 self, include_shadowed: bool = True 1099 ) -> Tuple[str, List[str], List[str], List, List]: 1100 """Return the function summary 1101 1102 :param include_shadowed: boolean to indicate if shadowed functions should be included (default True) 1103 Returns: 1104 (str, list, list, list, list): (name, inheritance, variables, fuction summaries, modifier summaries) 1105 """ 1106 func_summaries = [ 1107 f.get_summary() for f in self.functions if (not f.is_shadowed or include_shadowed) 1108 ] 1109 modif_summaries = [ 1110 f.get_summary() for f in self.modifiers if (not f.is_shadowed or include_shadowed) 1111 ] 1112 return ( 1113 self.name, 1114 [str(x) for x in self.inheritance], 1115 [str(x) for x in self.variables], 1116 func_summaries, 1117 modif_summaries, 1118 )
Return the function summary
Parameters
- include_shadowed: boolean to indicate if shadowed functions should be included (default True) Returns: (str, list, list, list, list): (name, inheritance, variables, fuction summaries, modifier summaries)
1120 def is_signature_only(self) -> bool: 1121 """Detect if the contract has only abstract functions 1122 1123 Returns: 1124 bool: true if the function are abstract functions 1125 """ 1126 return all((not f.is_implemented) for f in self.functions)
Detect if the contract has only abstract functions
Returns: bool: true if the function are abstract functions
1135 def ercs(self) -> List[str]: 1136 """ 1137 Return the ERC implemented 1138 :return: list of string 1139 """ 1140 all_erc = [ 1141 ("ERC20", self.is_erc20), 1142 ("ERC165", self.is_erc165), 1143 ("ERC1820", self.is_erc1820), 1144 ("ERC223", self.is_erc223), 1145 ("ERC721", self.is_erc721), 1146 ("ERC777", self.is_erc777), 1147 ("ERC2612", self.is_erc2612), 1148 ("ERC1363", self.is_erc1363), 1149 ("ERC4626", self.is_erc4626), 1150 ] 1151 1152 return [erc for erc, is_erc in all_erc if is_erc()]
Return the ERC implemented
Returns
list of string
1154 def is_erc20(self) -> bool: 1155 """ 1156 Check if the contract is an erc20 token 1157 1158 Note: it does not check for correct return values 1159 :return: Returns a true if the contract is an erc20 1160 """ 1161 full_names = self.functions_signatures 1162 return all(s in full_names for s in ERC20_signatures)
Check if the contract is an erc20 token
Note: it does not check for correct return values
Returns
Returns a true if the contract is an erc20
1164 def is_erc165(self) -> bool: 1165 """ 1166 Check if the contract is an erc165 token 1167 1168 Note: it does not check for correct return values 1169 :return: Returns a true if the contract is an erc165 1170 """ 1171 full_names = self.functions_signatures 1172 return all(s in full_names for s in ERC165_signatures)
Check if the contract is an erc165 token
Note: it does not check for correct return values
Returns
Returns a true if the contract is an erc165
1174 def is_erc1820(self) -> bool: 1175 """ 1176 Check if the contract is an erc1820 1177 1178 Note: it does not check for correct return values 1179 :return: Returns a true if the contract is an erc165 1180 """ 1181 full_names = self.functions_signatures 1182 return all(s in full_names for s in ERC1820_signatures)
Check if the contract is an erc1820
Note: it does not check for correct return values
Returns
Returns a true if the contract is an erc165
1184 def is_erc223(self) -> bool: 1185 """ 1186 Check if the contract is an erc223 token 1187 1188 Note: it does not check for correct return values 1189 :return: Returns a true if the contract is an erc223 1190 """ 1191 full_names = self.functions_signatures 1192 return all(s in full_names for s in ERC223_signatures)
Check if the contract is an erc223 token
Note: it does not check for correct return values
Returns
Returns a true if the contract is an erc223
1194 def is_erc721(self) -> bool: 1195 """ 1196 Check if the contract is an erc721 token 1197 1198 Note: it does not check for correct return values 1199 :return: Returns a true if the contract is an erc721 1200 """ 1201 full_names = self.functions_signatures 1202 return all(s in full_names for s in ERC721_signatures)
Check if the contract is an erc721 token
Note: it does not check for correct return values
Returns
Returns a true if the contract is an erc721
1204 def is_erc777(self) -> bool: 1205 """ 1206 Check if the contract is an erc777 1207 1208 Note: it does not check for correct return values 1209 :return: Returns a true if the contract is an erc165 1210 """ 1211 full_names = self.functions_signatures 1212 return all(s in full_names for s in ERC777_signatures)
Check if the contract is an erc777
Note: it does not check for correct return values
Returns
Returns a true if the contract is an erc165
1214 def is_erc1155(self) -> bool: 1215 """ 1216 Check if the contract is an erc1155 1217 1218 Note: it does not check for correct return values 1219 :return: Returns a true if the contract is an erc1155 1220 """ 1221 full_names = self.functions_signatures 1222 return all(s in full_names for s in ERC1155_signatures)
Check if the contract is an erc1155
Note: it does not check for correct return values
Returns
Returns a true if the contract is an erc1155
1224 def is_erc4626(self) -> bool: 1225 """ 1226 Check if the contract is an erc4626 1227 1228 Note: it does not check for correct return values 1229 :return: Returns a true if the contract is an erc4626 1230 """ 1231 full_names = self.functions_signatures 1232 return all(s in full_names for s in ERC4626_signatures)
Check if the contract is an erc4626
Note: it does not check for correct return values
Returns
Returns a true if the contract is an erc4626
1234 def is_erc2612(self) -> bool: 1235 """ 1236 Check if the contract is an erc2612 1237 1238 Note: it does not check for correct return values 1239 :return: Returns a true if the contract is an erc2612 1240 """ 1241 full_names = self.functions_signatures 1242 return all(s in full_names for s in ERC2612_signatures)
Check if the contract is an erc2612
Note: it does not check for correct return values
Returns
Returns a true if the contract is an erc2612
1244 def is_erc1363(self) -> bool: 1245 """ 1246 Check if the contract is an erc1363 1247 1248 Note: it does not check for correct return values 1249 :return: Returns a true if the contract is an erc1363 1250 """ 1251 full_names = self.functions_signatures 1252 return all(s in full_names for s in ERC1363_signatures)
Check if the contract is an erc1363
Note: it does not check for correct return values
Returns
Returns a true if the contract is an erc1363
1254 def is_erc4524(self) -> bool: 1255 """ 1256 Check if the contract is an erc4524 1257 1258 Note: it does not check for correct return values 1259 :return: Returns a true if the contract is an erc4524 1260 """ 1261 full_names = self.functions_signatures 1262 return all(s in full_names for s in ERC4524_signatures)
Check if the contract is an erc4524
Note: it does not check for correct return values
Returns
Returns a true if the contract is an erc4524
1264 @property 1265 def is_token(self) -> bool: 1266 """ 1267 Check if the contract follows one of the standard ERC token 1268 :return: 1269 """ 1270 return ( 1271 self.is_erc20() 1272 or self.is_erc721() 1273 or self.is_erc165() 1274 or self.is_erc223() 1275 or self.is_erc777() 1276 or self.is_erc1155() 1277 )
Check if the contract follows one of the standard ERC token
Returns
1279 def is_possible_erc20(self) -> bool: 1280 """ 1281 Checks if the provided contract could be attempting to implement ERC20 standards. 1282 1283 :return: Returns a boolean indicating if the provided contract met the token standard. 1284 """ 1285 # We do not check for all the functions, as name(), symbol(), might give too many FPs 1286 full_names = self.functions_signatures 1287 return ( 1288 "transfer(address,uint256)" in full_names 1289 or "transferFrom(address,address,uint256)" in full_names 1290 or "approve(address,uint256)" in full_names 1291 )
Checks if the provided contract could be attempting to implement ERC20 standards.
Returns
Returns a boolean indicating if the provided contract met the token standard.
1293 def is_possible_erc721(self) -> bool: 1294 """ 1295 Checks if the provided contract could be attempting to implement ERC721 standards. 1296 1297 :return: Returns a boolean indicating if the provided contract met the token standard. 1298 """ 1299 # We do not check for all the functions, as name(), symbol(), might give too many FPs 1300 full_names = self.functions_signatures 1301 return ( 1302 "ownerOf(uint256)" in full_names 1303 or "safeTransferFrom(address,address,uint256,bytes)" in full_names 1304 or "safeTransferFrom(address,address,uint256)" in full_names 1305 or "setApprovalForAll(address,bool)" in full_names 1306 or "getApproved(uint256)" in full_names 1307 or "isApprovedForAll(address,address)" in full_names 1308 )
Checks if the provided contract could be attempting to implement ERC721 standards.
Returns
Returns a boolean indicating if the provided contract met the token standard.
1310 @property 1311 def is_possible_token(self) -> bool: 1312 """ 1313 Check if the contract is a potential token (it might not implement all the functions) 1314 :return: 1315 """ 1316 return self.is_possible_erc20() or self.is_possible_erc721()
Check if the contract is a potential token (it might not implement all the functions)
Returns
1337 @property 1338 def is_truffle_migration(self) -> bool: 1339 """ 1340 Return true if the contract is the Migrations contract needed for Truffle 1341 :return: 1342 """ 1343 if self.compilation_unit.core.crytic_compile.platform == PlatformType.TRUFFLE: 1344 if self.name == "Migrations": 1345 paths = Path(self.source_mapping.filename.absolute).parts 1346 if len(paths) >= 2: 1347 return paths[-2] == "contracts" and paths[-1] == "migrations.sol" 1348 return False
Return true if the contract is the Migrations contract needed for Truffle
Returns
1372 @property 1373 def is_upgradeable(self) -> bool: 1374 if self._is_upgradeable is None: 1375 self._is_upgradeable = False 1376 initializable = self.file_scope.get_contract_from_name("Initializable") 1377 if initializable: 1378 if initializable in self.inheritance: 1379 self._is_upgradeable = True 1380 else: 1381 for contract in self.inheritance + [self]: 1382 # This might lead to false positive 1383 # Not sure why pylint is having a trouble here 1384 # pylint: disable=no-member 1385 lower_name = contract.name.lower() 1386 if "upgradeable" in lower_name or "upgradable" in lower_name: 1387 self._is_upgradeable = True 1388 break 1389 if "initializable" in lower_name: 1390 self._is_upgradeable = True 1391 break 1392 return self._is_upgradeable
1398 @property 1399 def is_upgradeable_proxy(self) -> bool: 1400 from slither.core.cfg.node import NodeType 1401 from slither.slithir.operations import LowLevelCall 1402 1403 if self._is_upgradeable_proxy is None: 1404 self._is_upgradeable_proxy = False 1405 if "Proxy" in self.name: 1406 self._is_upgradeable_proxy = True 1407 return True 1408 for f in self.functions: 1409 if f.is_fallback: 1410 for node in f.all_nodes(): 1411 for ir in node.irs: 1412 if isinstance(ir, LowLevelCall) and ir.function_name == "delegatecall": 1413 self._is_upgradeable_proxy = True 1414 return self._is_upgradeable_proxy 1415 if node.type == NodeType.ASSEMBLY: 1416 inline_asm = node.inline_asm 1417 if inline_asm: 1418 if "delegatecall" in inline_asm: 1419 self._is_upgradeable_proxy = True 1420 return self._is_upgradeable_proxy 1421 return self._is_upgradeable_proxy
1442 @property 1443 def is_incorrectly_constructed(self) -> bool: 1444 """ 1445 Return true if there was an internal Slither's issue when analyzing the contract 1446 :return: 1447 """ 1448 return self._is_incorrectly_parsed
Return true if there was an internal Slither's issue when analyzing the contract
Returns
1454 def add_constructor_variables(self) -> None: 1455 from slither.core.declarations.function_contract import FunctionContract 1456 1457 if self.state_variables: 1458 for (idx, variable_candidate) in enumerate(self.state_variables): 1459 if variable_candidate.expression and not variable_candidate.is_constant: 1460 1461 constructor_variable = FunctionContract(self.compilation_unit) 1462 constructor_variable.set_function_type(FunctionType.CONSTRUCTOR_VARIABLES) 1463 constructor_variable.set_contract(self) # type: ignore 1464 constructor_variable.set_contract_declarer(self) # type: ignore 1465 constructor_variable.set_visibility("internal") 1466 # For now, source mapping of the constructor variable is the whole contract 1467 # Could be improved with a targeted source mapping 1468 constructor_variable.set_offset(self.source_mapping, self.compilation_unit) 1469 self._functions[constructor_variable.canonical_name] = constructor_variable 1470 1471 prev_node = self._create_node( 1472 constructor_variable, 0, variable_candidate, constructor_variable 1473 ) 1474 variable_candidate.node_initialization = prev_node 1475 counter = 1 1476 for v in self.state_variables[idx + 1 :]: 1477 if v.expression and not v.is_constant: 1478 next_node = self._create_node( 1479 constructor_variable, counter, v, prev_node.scope 1480 ) 1481 v.node_initialization = next_node 1482 prev_node.add_son(next_node) 1483 next_node.add_father(prev_node) 1484 prev_node = next_node 1485 counter += 1 1486 break 1487 1488 for (idx, variable_candidate) in enumerate(self.state_variables): 1489 if variable_candidate.expression and variable_candidate.is_constant: 1490 1491 constructor_variable = FunctionContract(self.compilation_unit) 1492 constructor_variable.set_function_type( 1493 FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES 1494 ) 1495 constructor_variable.set_contract(self) # type: ignore 1496 constructor_variable.set_contract_declarer(self) # type: ignore 1497 constructor_variable.set_visibility("internal") 1498 # For now, source mapping of the constructor variable is the whole contract 1499 # Could be improved with a targeted source mapping 1500 constructor_variable.set_offset(self.source_mapping, self.compilation_unit) 1501 self._functions[constructor_variable.canonical_name] = constructor_variable 1502 1503 prev_node = self._create_node( 1504 constructor_variable, 0, variable_candidate, constructor_variable 1505 ) 1506 variable_candidate.node_initialization = prev_node 1507 counter = 1 1508 for v in self.state_variables[idx + 1 :]: 1509 if v.expression and v.is_constant: 1510 next_node = self._create_node( 1511 constructor_variable, counter, v, prev_node.scope 1512 ) 1513 v.node_initialization = next_node 1514 prev_node.add_son(next_node) 1515 next_node.add_father(prev_node) 1516 prev_node = next_node 1517 counter += 1 1518 1519 break
1555 def convert_expression_to_slithir_ssa(self) -> None: 1556 """ 1557 Assume generate_slithir_and_analyze was called on all functions 1558 1559 :return: 1560 """ 1561 from slither.slithir.variables import StateIRVariable 1562 1563 all_ssa_state_variables_instances = {} 1564 1565 for contract in self.inheritance: 1566 for v in contract.state_variables_declared: 1567 new_var = StateIRVariable(v) 1568 all_ssa_state_variables_instances[v.canonical_name] = new_var 1569 self._initial_state_variables.append(new_var) 1570 1571 for v in self.variables: 1572 if v.contract == self: 1573 new_var = StateIRVariable(v) 1574 all_ssa_state_variables_instances[v.canonical_name] = new_var 1575 self._initial_state_variables.append(new_var) 1576 1577 for func in self.functions + list(self.modifiers): 1578 func.generate_slithir_ssa(all_ssa_state_variables_instances)
Assume generate_slithir_and_analyze was called on all functions
Returns
1580 def fix_phi(self) -> None: 1581 last_state_variables_instances: Dict[str, List["StateVariable"]] = {} 1582 initial_state_variables_instances: Dict[str, "StateVariable"] = {} 1583 for v in self._initial_state_variables: 1584 last_state_variables_instances[v.canonical_name] = [] 1585 initial_state_variables_instances[v.canonical_name] = v 1586 1587 for func in self.functions + list(self.modifiers): 1588 result = func.get_last_ssa_state_variables_instances() 1589 for variable_name, instances in result.items(): 1590 # TODO: investigate the next operation 1591 last_state_variables_instances[variable_name] += list(instances) 1592 1593 for func in self.functions + list(self.modifiers): 1594 func.fix_phi(last_state_variables_instances, initial_state_variables_instances)