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.core.declarations import ( 33 Enum, 34 EventContract, 35 Modifier, 36 EnumContract, 37 StructureContract, 38 FunctionContract, 39 CustomErrorContract, 40 ) 41 from slither.slithir.operations import HighLevelCall, LibraryCall 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["Function"]] = 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 stored_state_variables_ordered for all the storage variables following the storage order 444 Use transient_state_variables_ordered for all the transient 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 state_variables_entry_points(self) -> List["StateVariable"]: 452 """ 453 list(StateVariable): List of the state variables that are public. 454 """ 455 return [var for var in self._variables.values() if var.visibility == "public"] 456 457 @property 458 def state_variables_ordered(self) -> List["StateVariable"]: 459 """ 460 list(StateVariable): List of the state variables by order of declaration. 461 """ 462 return self._variables_ordered 463 464 def add_state_variables_ordered(self, new_vars: List["StateVariable"]) -> None: 465 self._variables_ordered += new_vars 466 467 @property 468 def storage_variables_ordered(self) -> List["StateVariable"]: 469 """ 470 list(StateVariable): List of the state variables in storage location by order of declaration. 471 """ 472 return [v for v in self._variables_ordered if v.is_stored] 473 474 @property 475 def transient_variables_ordered(self) -> List["StateVariable"]: 476 """ 477 list(StateVariable): List of the state variables in transient location by order of declaration. 478 """ 479 return [v for v in self._variables_ordered if v.is_transient] 480 481 @property 482 def state_variables_inherited(self) -> List["StateVariable"]: 483 """ 484 list(StateVariable): List of the inherited state variables 485 """ 486 return [s for s in self.state_variables if s.contract != self] 487 488 @property 489 def state_variables_declared(self) -> List["StateVariable"]: 490 """ 491 list(StateVariable): List of the state variables declared within the contract (not inherited) 492 """ 493 return [s for s in self.state_variables if s.contract == self] 494 495 @property 496 def slithir_variables(self) -> List["SlithIRVariable"]: 497 """ 498 List all of the slithir variables (non SSA) 499 """ 500 slithir_variabless = [f.slithir_variables for f in self.functions + self.modifiers] # type: ignore 501 slithir_variables = [item for sublist in slithir_variabless for item in sublist] 502 return list(set(slithir_variables)) 503 504 @property 505 def state_variables_used_in_reentrant_targets( 506 self, 507 ) -> Dict["StateVariable", Set[Union["StateVariable", "Function"]]]: 508 """ 509 Returns the state variables used in reentrant targets. Heuristics: 510 - Variable used (read/write) in entry points that are reentrant 511 - State variables that are public 512 513 """ 514 from slither.core.variables.state_variable import StateVariable 515 516 if self._state_variables_used_in_reentrant_targets is None: 517 reentrant_functions = [f for f in self.functions_entry_points if f.is_reentrant] 518 variables_used: Dict[ 519 StateVariable, Set[Union[StateVariable, "Function"]] 520 ] = defaultdict(set) 521 for function in reentrant_functions: 522 for ir in function.all_slithir_operations(): 523 state_variables = [v for v in ir.used if isinstance(v, StateVariable)] 524 for state_variable in state_variables: 525 variables_used[state_variable].add(ir.node.function) 526 for variable in [v for v in self.state_variables if v.visibility == "public"]: 527 variables_used[variable].add(variable) 528 self._state_variables_used_in_reentrant_targets = variables_used 529 return self._state_variables_used_in_reentrant_targets 530 531 # endregion 532 ################################################################################### 533 ################################################################################### 534 # region Constructors 535 ################################################################################### 536 ################################################################################### 537 538 @property 539 def constructor(self) -> Optional["Function"]: 540 """ 541 Return the contract's immediate constructor. 542 If there is no immediate constructor, returns the first constructor 543 executed, following the c3 linearization 544 Return None if there is no constructor. 545 """ 546 cst = self.constructors_declared 547 if cst: 548 return cst 549 for inherited_contract in self.inheritance: 550 cst = inherited_contract.constructors_declared 551 if cst: 552 return cst 553 return None 554 555 @property 556 def constructors_declared(self) -> Optional["Function"]: 557 return next( 558 ( 559 func 560 for func in self.functions 561 if func.is_constructor and func.contract_declarer == self 562 ), 563 None, 564 ) 565 566 @property 567 def constructors(self) -> List["FunctionContract"]: 568 """ 569 Return the list of constructors (including inherited) 570 """ 571 return [func for func in self.functions if func.is_constructor] 572 573 @property 574 def explicit_base_constructor_calls(self) -> List["Function"]: 575 """ 576 list(Function): List of the base constructors called explicitly by this contract definition. 577 578 Base constructors called by any constructor definition will not be included. 579 Base constructors implicitly called by the contract definition (without 580 parenthesis) will not be included. 581 582 On "contract B is A(){..}" it returns the constructor of A 583 """ 584 return [c.constructor for c in self._explicit_base_constructor_calls if c.constructor] 585 586 # endregion 587 ################################################################################### 588 ################################################################################### 589 # region Functions and Modifiers 590 ################################################################################### 591 ################################################################################### 592 593 @property 594 def functions_signatures(self) -> List[str]: 595 """ 596 Return the signatures of all the public/eterxnal functions/state variables 597 :return: list(string) the signatures of all the functions that can be called 598 """ 599 if self._signatures is None: 600 sigs = [ 601 v.full_name for v in self.state_variables if v.visibility in ["public", "external"] 602 ] 603 604 sigs += {f.full_name for f in self.functions if f.visibility in ["public", "external"]} 605 self._signatures = list(set(sigs)) 606 return self._signatures 607 608 @property 609 def functions_signatures_declared(self) -> List[str]: 610 """ 611 Return the signatures of the public/eterxnal functions/state variables that are declared by this contract 612 :return: list(string) the signatures of all the functions that can be called and are declared by this contract 613 """ 614 if self._signatures_declared is None: 615 sigs = [ 616 v.full_name 617 for v in self.state_variables_declared 618 if v.visibility in ["public", "external"] 619 ] 620 621 sigs += { 622 f.full_name 623 for f in self.functions_declared 624 if f.visibility in ["public", "external"] 625 } 626 self._signatures_declared = list(set(sigs)) 627 return self._signatures_declared 628 629 @property 630 def functions(self) -> List["FunctionContract"]: 631 """ 632 list(Function): List of the functions 633 """ 634 return list(self._functions.values()) 635 636 def available_functions_as_dict(self) -> Dict[str, "Function"]: 637 if self._available_functions_as_dict is None: 638 self._available_functions_as_dict = { 639 f.full_name: f for f in self._functions.values() if not f.is_shadowed 640 } 641 return self._available_functions_as_dict 642 643 def add_function(self, func: "FunctionContract") -> None: 644 self._functions[func.canonical_name] = func 645 646 def set_functions(self, functions: Dict[str, "FunctionContract"]) -> None: 647 """ 648 Set the functions 649 650 :param functions: dict full_name -> function 651 :return: 652 """ 653 self._functions = functions 654 655 @property 656 def functions_inherited(self) -> List["FunctionContract"]: 657 """ 658 list(Function): List of the inherited functions 659 """ 660 return [f for f in self.functions if f.contract_declarer != self] 661 662 @property 663 def functions_declared(self) -> List["FunctionContract"]: 664 """ 665 list(Function): List of the functions defined within the contract (not inherited) 666 """ 667 return [f for f in self.functions if f.contract_declarer == self] 668 669 @property 670 def functions_entry_points(self) -> List["FunctionContract"]: 671 """ 672 list(Functions): List of public and external functions 673 """ 674 return [ 675 f 676 for f in self.functions 677 if f.visibility in ["public", "external"] and not f.is_shadowed or f.is_fallback 678 ] 679 680 @property 681 def modifiers(self) -> List["Modifier"]: 682 """ 683 list(Modifier): List of the modifiers 684 """ 685 return list(self._modifiers.values()) 686 687 def available_modifiers_as_dict(self) -> Dict[str, "Modifier"]: 688 return {m.full_name: m for m in self._modifiers.values() if not m.is_shadowed} 689 690 def set_modifiers(self, modifiers: Dict[str, "Modifier"]) -> None: 691 """ 692 Set the modifiers 693 694 :param modifiers: dict full_name -> modifier 695 :return: 696 """ 697 self._modifiers = modifiers 698 699 @property 700 def modifiers_inherited(self) -> List["Modifier"]: 701 """ 702 list(Modifier): List of the inherited modifiers 703 """ 704 return [m for m in self.modifiers if m.contract_declarer != self] 705 706 @property 707 def modifiers_declared(self) -> List["Modifier"]: 708 """ 709 list(Modifier): List of the modifiers defined within the contract (not inherited) 710 """ 711 return [m for m in self.modifiers if m.contract_declarer == self] 712 713 @property 714 def functions_and_modifiers(self) -> List["Function"]: 715 """ 716 list(Function|Modifier): List of the functions and modifiers 717 """ 718 return self.functions + self.modifiers # type: ignore 719 720 @property 721 def functions_and_modifiers_inherited(self) -> List["Function"]: 722 """ 723 list(Function|Modifier): List of the inherited functions and modifiers 724 """ 725 return self.functions_inherited + self.modifiers_inherited # type: ignore 726 727 @property 728 def functions_and_modifiers_declared(self) -> List["Function"]: 729 """ 730 list(Function|Modifier): List of the functions and modifiers defined within the contract (not inherited) 731 """ 732 return self.functions_declared + self.modifiers_declared # type: ignore 733 734 @property 735 def fallback_function(self) -> Optional["FunctionContract"]: 736 if self._fallback_function is None: 737 for f in self.functions: 738 if f.is_fallback: 739 self._fallback_function = f 740 break 741 return self._fallback_function 742 743 @property 744 def receive_function(self) -> Optional["FunctionContract"]: 745 if self._receive_function is None: 746 for f in self.functions: 747 if f.is_receive: 748 self._receive_function = f 749 break 750 return self._receive_function 751 752 def available_elements_from_inheritances( 753 self, 754 elements: Dict[str, "Function"], 755 getter_available: Callable[["Contract"], List["FunctionContract"]], 756 ) -> Dict[str, "Function"]: 757 """ 758 759 :param elements: dict(canonical_name -> elements) 760 :param getter_available: fun x 761 :return: 762 """ 763 # keep track of the contracts visited 764 # to prevent an ovveride due to multiple inheritance of the same contract 765 # A is B, C, D is C, --> the second C was already seen 766 inherited_elements: Dict[str, "FunctionContract"] = {} 767 accessible_elements = {} 768 contracts_visited = [] 769 for father in self.inheritance_reverse: 770 functions: Dict[str, "FunctionContract"] = { 771 v.full_name: v 772 for v in getter_available(father) 773 if v.contract not in contracts_visited 774 and v.function_language 775 != FunctionLanguage.Yul # Yul functions are not propagated in the inheritance 776 } 777 contracts_visited.append(father) 778 inherited_elements.update(functions) 779 780 for element in inherited_elements.values(): 781 accessible_elements[element.full_name] = elements[element.canonical_name] 782 783 return accessible_elements 784 785 # endregion 786 ################################################################################### 787 ################################################################################### 788 # region Inheritance 789 ################################################################################### 790 ################################################################################### 791 792 @property 793 def inheritance(self) -> List["Contract"]: 794 """ 795 list(Contract): Inheritance list. Order: the first elem is the first father to be executed 796 """ 797 return list(self._inheritance) 798 799 @property 800 def immediate_inheritance(self) -> List["Contract"]: 801 """ 802 list(Contract): List of contracts immediately inherited from (fathers). Order: order of declaration. 803 """ 804 return list(self._immediate_inheritance) 805 806 @property 807 def inheritance_reverse(self) -> List["Contract"]: 808 """ 809 list(Contract): Inheritance list. Order: the last elem is the first father to be executed 810 """ 811 return list(reversed(self._inheritance)) 812 813 def set_inheritance( 814 self, 815 inheritance: List["Contract"], 816 immediate_inheritance: List["Contract"], 817 called_base_constructor_contracts: List["Contract"], 818 ) -> None: 819 self._inheritance = inheritance 820 self._immediate_inheritance = immediate_inheritance 821 self._explicit_base_constructor_calls = called_base_constructor_contracts 822 823 @property 824 def derived_contracts(self) -> List["Contract"]: 825 """ 826 list(Contract): Return the list of contracts derived from self 827 """ 828 candidates = self.compilation_unit.contracts 829 return [c for c in candidates if self in c.inheritance] # type: ignore 830 831 # endregion 832 ################################################################################### 833 ################################################################################### 834 # region Getters from/to object 835 ################################################################################### 836 ################################################################################### 837 838 def get_functions_reading_from_variable(self, variable: "Variable") -> List["Function"]: 839 """ 840 Return the functions reading the variable 841 """ 842 return [f for f in self.functions if f.is_reading(variable)] 843 844 def get_functions_writing_to_variable(self, variable: "Variable") -> List["Function"]: 845 """ 846 Return the functions writting the variable 847 """ 848 return [f for f in self.functions if f.is_writing(variable)] 849 850 def get_function_from_full_name(self, full_name: str) -> Optional["Function"]: 851 """ 852 Return a function from a full name 853 The full name differs from the solidity's signature are the type are conserved 854 For example contract type are kept, structure are not unrolled, etc 855 Args: 856 full_name (str): signature of the function (without return statement) 857 Returns: 858 Function 859 """ 860 return next( 861 (f for f in self.functions if f.full_name == full_name and not f.is_shadowed), 862 None, 863 ) 864 865 def get_function_from_signature(self, function_signature: str) -> Optional["Function"]: 866 """ 867 Return a function from a signature 868 Args: 869 function_signature (str): signature of the function (without return statement) 870 Returns: 871 Function 872 """ 873 return next( 874 ( 875 f 876 for f in self.functions 877 if f.solidity_signature == function_signature and not f.is_shadowed 878 ), 879 None, 880 ) 881 882 def get_modifier_from_signature(self, modifier_signature: str) -> Optional["Modifier"]: 883 """ 884 Return a modifier from a signature 885 886 :param modifier_signature: 887 """ 888 return next( 889 (m for m in self.modifiers if m.full_name == modifier_signature and not m.is_shadowed), 890 None, 891 ) 892 893 def get_function_from_canonical_name(self, canonical_name: str) -> Optional["Function"]: 894 """ 895 Return a function from a canonical name (contract.signature()) 896 Args: 897 canonical_name (str): canonical name of the function (without return statement) 898 Returns: 899 Function 900 """ 901 return next((f for f in self.functions if f.canonical_name == canonical_name), None) 902 903 def get_modifier_from_canonical_name(self, canonical_name: str) -> Optional["Modifier"]: 904 """ 905 Return a modifier from a canonical name (contract.signature()) 906 Args: 907 canonical_name (str): canonical name of the modifier 908 Returns: 909 Modifier 910 """ 911 return next((m for m in self.modifiers if m.canonical_name == canonical_name), None) 912 913 def get_state_variable_from_name(self, variable_name: str) -> Optional["StateVariable"]: 914 """ 915 Return a state variable from a name 916 917 :param variable_name: 918 """ 919 return next((v for v in self.state_variables if v.name == variable_name), None) 920 921 def get_state_variable_from_canonical_name( 922 self, canonical_name: str 923 ) -> Optional["StateVariable"]: 924 """ 925 Return a state variable from a canonical_name 926 Args: 927 canonical_name (str): name of the variable 928 Returns: 929 StateVariable 930 """ 931 return next((v for v in self.state_variables if v.canonical_name == canonical_name), None) 932 933 def get_structure_from_name(self, structure_name: str) -> Optional["StructureContract"]: 934 """ 935 Return a structure from a name 936 Args: 937 structure_name (str): name of the structure 938 Returns: 939 StructureContract 940 """ 941 return next((st for st in self.structures if st.name == structure_name), None) 942 943 def get_structure_from_canonical_name( 944 self, structure_name: str 945 ) -> Optional["StructureContract"]: 946 """ 947 Return a structure from a canonical name 948 Args: 949 structure_name (str): canonical name of the structure 950 Returns: 951 StructureContract 952 """ 953 return next((st for st in self.structures if st.canonical_name == structure_name), None) 954 955 def get_event_from_signature(self, event_signature: str) -> Optional["Event"]: 956 """ 957 Return an event from a signature 958 Args: 959 event_signature (str): signature of the event 960 Returns: 961 Event 962 """ 963 return next((e for e in self.events if e.full_name == event_signature), None) 964 965 def get_event_from_canonical_name(self, event_canonical_name: str) -> Optional["Event"]: 966 """ 967 Return an event from a canonical name 968 Args: 969 event_canonical_name (str): name of the event 970 Returns: 971 Event 972 """ 973 return next((e for e in self.events if e.canonical_name == event_canonical_name), None) 974 975 def get_enum_from_name(self, enum_name: str) -> Optional["Enum"]: 976 """ 977 Return an enum from a name 978 Args: 979 enum_name (str): name of the enum 980 Returns: 981 Enum 982 """ 983 return next((e for e in self.enums if e.name == enum_name), None) 984 985 def get_enum_from_canonical_name(self, enum_name: str) -> Optional["Enum"]: 986 """ 987 Return an enum from a canonical name 988 Args: 989 enum_name (str): canonical name of the enum 990 Returns: 991 Enum 992 """ 993 return next((e for e in self.enums if e.canonical_name == enum_name), None) 994 995 def get_functions_overridden_by(self, function: "Function") -> List["Function"]: 996 """ 997 Return the list of functions overridden by the function 998 Args: 999 (core.Function) 1000 Returns: 1001 list(core.Function) 1002 1003 """ 1004 return function.overrides 1005 1006 # endregion 1007 ################################################################################### 1008 ################################################################################### 1009 # region Recursive getters 1010 ################################################################################### 1011 ################################################################################### 1012 1013 @property 1014 def all_functions_called(self) -> List["Function"]: 1015 """ 1016 list(Function): List of functions reachable from the contract 1017 Includes super, and private/internal functions not shadowed 1018 """ 1019 from slither.slithir.operations import Operation 1020 1021 if self._all_functions_called is None: 1022 all_functions = [f for f in self.functions + self.modifiers if not f.is_shadowed] # type: ignore 1023 all_callss = [f.all_internal_calls() for f in all_functions] + [list(all_functions)] 1024 all_calls = [ 1025 item.function if isinstance(item, Operation) else item 1026 for sublist in all_callss 1027 for item in sublist 1028 ] 1029 all_calls = list(set(all_calls)) 1030 1031 all_constructors = [c.constructor for c in self.inheritance if c.constructor] 1032 all_constructors = list(set(all_constructors)) 1033 1034 set_all_calls = set(all_calls + list(all_constructors)) 1035 1036 self._all_functions_called = [c for c in set_all_calls if isinstance(c, Function)] 1037 return self._all_functions_called 1038 1039 @property 1040 def all_state_variables_written(self) -> List["StateVariable"]: 1041 """ 1042 list(StateVariable): List all of the state variables written 1043 """ 1044 all_state_variables_writtens = [ 1045 f.all_state_variables_written() for f in self.functions + self.modifiers # type: ignore 1046 ] 1047 all_state_variables_written = [ 1048 item for sublist in all_state_variables_writtens for item in sublist 1049 ] 1050 return list(set(all_state_variables_written)) 1051 1052 @property 1053 def all_state_variables_read(self) -> List["StateVariable"]: 1054 """ 1055 list(StateVariable): List all of the state variables read 1056 """ 1057 all_state_variables_reads = [ 1058 f.all_state_variables_read() for f in self.functions + self.modifiers # type: ignore 1059 ] 1060 all_state_variables_read = [ 1061 item for sublist in all_state_variables_reads for item in sublist 1062 ] 1063 return list(set(all_state_variables_read)) 1064 1065 @property 1066 def all_library_calls(self) -> List["LibraryCall"]: 1067 """ 1068 list(LibraryCall): List all of the libraries func called 1069 """ 1070 all_high_level_callss = [f.all_library_calls() for f in self.functions + self.modifiers] # type: ignore 1071 all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist] 1072 return list(set(all_high_level_calls)) 1073 1074 @property 1075 def all_high_level_calls(self) -> List[Tuple["Contract", "HighLevelCall"]]: 1076 """ 1077 list(Tuple("Contract", "HighLevelCall")): List all of the external high level calls 1078 """ 1079 all_high_level_callss = [f.all_high_level_calls() for f in self.functions + self.modifiers] # type: ignore 1080 all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist] 1081 return list(set(all_high_level_calls)) 1082 1083 # endregion 1084 ################################################################################### 1085 ################################################################################### 1086 # region Summary information 1087 ################################################################################### 1088 ################################################################################### 1089 1090 def get_summary( 1091 self, include_shadowed: bool = True 1092 ) -> Tuple[str, List[str], List[str], List, List]: 1093 """Return the function summary 1094 1095 :param include_shadowed: boolean to indicate if shadowed functions should be included (default True) 1096 Returns: 1097 (str, list, list, list, list): (name, inheritance, variables, fuction summaries, modifier summaries) 1098 """ 1099 func_summaries = [ 1100 f.get_summary() for f in self.functions if (not f.is_shadowed or include_shadowed) 1101 ] 1102 modif_summaries = [ 1103 f.get_summary() for f in self.modifiers if (not f.is_shadowed or include_shadowed) 1104 ] 1105 return ( 1106 self.name, 1107 [str(x) for x in self.inheritance], 1108 [str(x) for x in self.variables], 1109 func_summaries, 1110 modif_summaries, 1111 ) 1112 1113 def is_signature_only(self) -> bool: 1114 """Detect if the contract has only abstract functions 1115 1116 Returns: 1117 bool: true if the function are abstract functions 1118 """ 1119 return all((not f.is_implemented) for f in self.functions) 1120 1121 # endregion 1122 ################################################################################### 1123 ################################################################################### 1124 # region ERC conformance 1125 ################################################################################### 1126 ################################################################################### 1127 1128 def ercs(self) -> List[str]: 1129 """ 1130 Return the ERC implemented 1131 :return: list of string 1132 """ 1133 all_erc = [ 1134 ("ERC20", self.is_erc20), 1135 ("ERC165", self.is_erc165), 1136 ("ERC1820", self.is_erc1820), 1137 ("ERC223", self.is_erc223), 1138 ("ERC721", self.is_erc721), 1139 ("ERC777", self.is_erc777), 1140 ("ERC2612", self.is_erc2612), 1141 ("ERC1363", self.is_erc1363), 1142 ("ERC4626", self.is_erc4626), 1143 ] 1144 1145 return [erc for erc, is_erc in all_erc if is_erc()] 1146 1147 def is_erc20(self) -> bool: 1148 """ 1149 Check if the contract is an erc20 token 1150 1151 Note: it does not check for correct return values 1152 :return: Returns a true if the contract is an erc20 1153 """ 1154 full_names = self.functions_signatures 1155 return all(s in full_names for s in ERC20_signatures) 1156 1157 def is_erc165(self) -> bool: 1158 """ 1159 Check if the contract is an erc165 token 1160 1161 Note: it does not check for correct return values 1162 :return: Returns a true if the contract is an erc165 1163 """ 1164 full_names = self.functions_signatures 1165 return all(s in full_names for s in ERC165_signatures) 1166 1167 def is_erc1820(self) -> bool: 1168 """ 1169 Check if the contract is an erc1820 1170 1171 Note: it does not check for correct return values 1172 :return: Returns a true if the contract is an erc165 1173 """ 1174 full_names = self.functions_signatures 1175 return all(s in full_names for s in ERC1820_signatures) 1176 1177 def is_erc223(self) -> bool: 1178 """ 1179 Check if the contract is an erc223 token 1180 1181 Note: it does not check for correct return values 1182 :return: Returns a true if the contract is an erc223 1183 """ 1184 full_names = self.functions_signatures 1185 return all(s in full_names for s in ERC223_signatures) 1186 1187 def is_erc721(self) -> bool: 1188 """ 1189 Check if the contract is an erc721 token 1190 1191 Note: it does not check for correct return values 1192 :return: Returns a true if the contract is an erc721 1193 """ 1194 full_names = self.functions_signatures 1195 return all(s in full_names for s in ERC721_signatures) 1196 1197 def is_erc777(self) -> bool: 1198 """ 1199 Check if the contract is an erc777 1200 1201 Note: it does not check for correct return values 1202 :return: Returns a true if the contract is an erc165 1203 """ 1204 full_names = self.functions_signatures 1205 return all(s in full_names for s in ERC777_signatures) 1206 1207 def is_erc1155(self) -> bool: 1208 """ 1209 Check if the contract is an erc1155 1210 1211 Note: it does not check for correct return values 1212 :return: Returns a true if the contract is an erc1155 1213 """ 1214 full_names = self.functions_signatures 1215 return all(s in full_names for s in ERC1155_signatures) 1216 1217 def is_erc4626(self) -> bool: 1218 """ 1219 Check if the contract is an erc4626 1220 1221 Note: it does not check for correct return values 1222 :return: Returns a true if the contract is an erc4626 1223 """ 1224 full_names = self.functions_signatures 1225 return all(s in full_names for s in ERC4626_signatures) 1226 1227 def is_erc2612(self) -> bool: 1228 """ 1229 Check if the contract is an erc2612 1230 1231 Note: it does not check for correct return values 1232 :return: Returns a true if the contract is an erc2612 1233 """ 1234 full_names = self.functions_signatures 1235 return all(s in full_names for s in ERC2612_signatures) 1236 1237 def is_erc1363(self) -> bool: 1238 """ 1239 Check if the contract is an erc1363 1240 1241 Note: it does not check for correct return values 1242 :return: Returns a true if the contract is an erc1363 1243 """ 1244 full_names = self.functions_signatures 1245 return all(s in full_names for s in ERC1363_signatures) 1246 1247 def is_erc4524(self) -> bool: 1248 """ 1249 Check if the contract is an erc4524 1250 1251 Note: it does not check for correct return values 1252 :return: Returns a true if the contract is an erc4524 1253 """ 1254 full_names = self.functions_signatures 1255 return all(s in full_names for s in ERC4524_signatures) 1256 1257 @property 1258 def is_token(self) -> bool: 1259 """ 1260 Check if the contract follows one of the standard ERC token 1261 :return: 1262 """ 1263 return ( 1264 self.is_erc20() 1265 or self.is_erc721() 1266 or self.is_erc165() 1267 or self.is_erc223() 1268 or self.is_erc777() 1269 or self.is_erc1155() 1270 ) 1271 1272 def is_possible_erc20(self) -> bool: 1273 """ 1274 Checks if the provided contract could be attempting to implement ERC20 standards. 1275 1276 :return: Returns a boolean indicating if the provided contract met the token standard. 1277 """ 1278 # We do not check for all the functions, as name(), symbol(), might give too many FPs 1279 full_names = self.functions_signatures 1280 return ( 1281 "transfer(address,uint256)" in full_names 1282 or "transferFrom(address,address,uint256)" in full_names 1283 or "approve(address,uint256)" in full_names 1284 ) 1285 1286 def is_possible_erc721(self) -> bool: 1287 """ 1288 Checks if the provided contract could be attempting to implement ERC721 standards. 1289 1290 :return: Returns a boolean indicating if the provided contract met the token standard. 1291 """ 1292 # We do not check for all the functions, as name(), symbol(), might give too many FPs 1293 full_names = self.functions_signatures 1294 return ( 1295 "ownerOf(uint256)" in full_names 1296 or "safeTransferFrom(address,address,uint256,bytes)" in full_names 1297 or "safeTransferFrom(address,address,uint256)" in full_names 1298 or "setApprovalForAll(address,bool)" in full_names 1299 or "getApproved(uint256)" in full_names 1300 or "isApprovedForAll(address,address)" in full_names 1301 ) 1302 1303 @property 1304 def is_possible_token(self) -> bool: 1305 """ 1306 Check if the contract is a potential token (it might not implement all the functions) 1307 :return: 1308 """ 1309 return self.is_possible_erc20() or self.is_possible_erc721() 1310 1311 # endregion 1312 ################################################################################### 1313 ################################################################################### 1314 # region Dependencies 1315 ################################################################################### 1316 ################################################################################### 1317 1318 def is_from_dependency(self) -> bool: 1319 return self.compilation_unit.core.crytic_compile.is_dependency( 1320 self.source_mapping.filename.absolute 1321 ) 1322 1323 # endregion 1324 ################################################################################### 1325 ################################################################################### 1326 # region Test 1327 ################################################################################### 1328 ################################################################################### 1329 1330 @property 1331 def is_truffle_migration(self) -> bool: 1332 """ 1333 Return true if the contract is the Migrations contract needed for Truffle 1334 :return: 1335 """ 1336 if self.compilation_unit.core.crytic_compile.platform == PlatformType.TRUFFLE: 1337 if self.name == "Migrations": 1338 paths = Path(self.source_mapping.filename.absolute).parts 1339 if len(paths) >= 2: 1340 return paths[-2] == "contracts" and paths[-1] == "migrations.sol" 1341 return False 1342 1343 @property 1344 def is_test(self) -> bool: 1345 return is_test_contract(self) or self.is_truffle_migration # type: ignore 1346 1347 # endregion 1348 ################################################################################### 1349 ################################################################################### 1350 # region Function analyses 1351 ################################################################################### 1352 ################################################################################### 1353 1354 def update_read_write_using_ssa(self) -> None: 1355 for function in self.functions + list(self.modifiers): 1356 function.update_read_write_using_ssa() 1357 1358 # endregion 1359 ################################################################################### 1360 ################################################################################### 1361 # region Upgradeability 1362 ################################################################################### 1363 ################################################################################### 1364 1365 @property 1366 def is_upgradeable(self) -> bool: 1367 if self._is_upgradeable is None: 1368 self._is_upgradeable = False 1369 initializable = self.file_scope.get_contract_from_name("Initializable") 1370 if initializable: 1371 if initializable in self.inheritance: 1372 self._is_upgradeable = True 1373 else: 1374 for contract in self.inheritance + [self]: 1375 # This might lead to false positive 1376 # Not sure why pylint is having a trouble here 1377 # pylint: disable=no-member 1378 lower_name = contract.name.lower() 1379 if "upgradeable" in lower_name or "upgradable" in lower_name: 1380 self._is_upgradeable = True 1381 break 1382 if "initializable" in lower_name: 1383 self._is_upgradeable = True 1384 break 1385 return self._is_upgradeable 1386 1387 @is_upgradeable.setter 1388 def is_upgradeable(self, upgradeable: bool) -> None: 1389 self._is_upgradeable = upgradeable 1390 1391 @property 1392 def is_upgradeable_proxy(self) -> bool: 1393 from slither.core.cfg.node import NodeType 1394 from slither.slithir.operations import LowLevelCall 1395 1396 if self._is_upgradeable_proxy is None: 1397 self._is_upgradeable_proxy = False 1398 if "Proxy" in self.name: 1399 self._is_upgradeable_proxy = True 1400 return True 1401 for f in self.functions: 1402 if f.is_fallback: 1403 for node in f.all_nodes(): 1404 for ir in node.irs: 1405 if isinstance(ir, LowLevelCall) and ir.function_name == "delegatecall": 1406 self._is_upgradeable_proxy = True 1407 return self._is_upgradeable_proxy 1408 if node.type == NodeType.ASSEMBLY: 1409 inline_asm = node.inline_asm 1410 if inline_asm: 1411 if "delegatecall" in inline_asm: 1412 self._is_upgradeable_proxy = True 1413 return self._is_upgradeable_proxy 1414 return self._is_upgradeable_proxy 1415 1416 @is_upgradeable_proxy.setter 1417 def is_upgradeable_proxy(self, upgradeable_proxy: bool) -> None: 1418 self._is_upgradeable_proxy = upgradeable_proxy 1419 1420 @property 1421 def upgradeable_version(self) -> Optional[str]: 1422 return self._upgradeable_version 1423 1424 @upgradeable_version.setter 1425 def upgradeable_version(self, version_name: str) -> None: 1426 self._upgradeable_version = version_name 1427 1428 # endregion 1429 ################################################################################### 1430 ################################################################################### 1431 # region Internals 1432 ################################################################################### 1433 ################################################################################### 1434 1435 @property 1436 def is_incorrectly_constructed(self) -> bool: 1437 """ 1438 Return true if there was an internal Slither's issue when analyzing the contract 1439 :return: 1440 """ 1441 return self._is_incorrectly_parsed 1442 1443 @is_incorrectly_constructed.setter 1444 def is_incorrectly_constructed(self, incorrect: bool) -> None: 1445 self._is_incorrectly_parsed = incorrect 1446 1447 def add_constructor_variables(self) -> None: 1448 from slither.core.declarations.function_contract import FunctionContract 1449 1450 if self.state_variables: 1451 for (idx, variable_candidate) in enumerate(self.state_variables): 1452 if variable_candidate.expression and not variable_candidate.is_constant: 1453 1454 constructor_variable = FunctionContract(self.compilation_unit) 1455 constructor_variable.set_function_type(FunctionType.CONSTRUCTOR_VARIABLES) 1456 constructor_variable.set_contract(self) # type: ignore 1457 constructor_variable.set_contract_declarer(self) # type: ignore 1458 constructor_variable.set_visibility("internal") 1459 # For now, source mapping of the constructor variable is the whole contract 1460 # Could be improved with a targeted source mapping 1461 constructor_variable.set_offset(self.source_mapping, self.compilation_unit) 1462 self._functions[constructor_variable.canonical_name] = constructor_variable 1463 1464 prev_node = self._create_node( 1465 constructor_variable, 0, variable_candidate, constructor_variable 1466 ) 1467 variable_candidate.node_initialization = prev_node 1468 counter = 1 1469 for v in self.state_variables[idx + 1 :]: 1470 if v.expression and not v.is_constant: 1471 next_node = self._create_node( 1472 constructor_variable, counter, v, prev_node.scope 1473 ) 1474 v.node_initialization = next_node 1475 prev_node.add_son(next_node) 1476 next_node.add_father(prev_node) 1477 prev_node = next_node 1478 counter += 1 1479 break 1480 1481 for (idx, variable_candidate) in enumerate(self.state_variables): 1482 if variable_candidate.expression and variable_candidate.is_constant: 1483 1484 constructor_variable = FunctionContract(self.compilation_unit) 1485 constructor_variable.set_function_type( 1486 FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES 1487 ) 1488 constructor_variable.set_contract(self) # type: ignore 1489 constructor_variable.set_contract_declarer(self) # type: ignore 1490 constructor_variable.set_visibility("internal") 1491 # For now, source mapping of the constructor variable is the whole contract 1492 # Could be improved with a targeted source mapping 1493 constructor_variable.set_offset(self.source_mapping, self.compilation_unit) 1494 self._functions[constructor_variable.canonical_name] = constructor_variable 1495 1496 prev_node = self._create_node( 1497 constructor_variable, 0, variable_candidate, constructor_variable 1498 ) 1499 variable_candidate.node_initialization = prev_node 1500 counter = 1 1501 for v in self.state_variables[idx + 1 :]: 1502 if v.expression and v.is_constant: 1503 next_node = self._create_node( 1504 constructor_variable, counter, v, prev_node.scope 1505 ) 1506 v.node_initialization = next_node 1507 prev_node.add_son(next_node) 1508 next_node.add_father(prev_node) 1509 prev_node = next_node 1510 counter += 1 1511 1512 break 1513 1514 def _create_node( 1515 self, func: Function, counter: int, variable: "Variable", scope: Union[Scope, Function] 1516 ) -> "Node": 1517 from slither.core.cfg.node import Node, NodeType 1518 from slither.core.expressions import ( 1519 AssignmentOperationType, 1520 AssignmentOperation, 1521 Identifier, 1522 ) 1523 1524 # Function uses to create node for state variable declaration statements 1525 node = Node(NodeType.OTHER_ENTRYPOINT, counter, scope, func.file_scope) 1526 node.set_offset(variable.source_mapping, self.compilation_unit) 1527 node.set_function(func) 1528 func.add_node(node) 1529 assert variable.expression 1530 expression = AssignmentOperation( 1531 Identifier(variable), 1532 variable.expression, 1533 AssignmentOperationType.ASSIGN, 1534 variable.type, 1535 ) 1536 1537 expression.set_offset(variable.source_mapping, self.compilation_unit) 1538 node.add_expression(expression) 1539 return node 1540 1541 # endregion 1542 ################################################################################### 1543 ################################################################################### 1544 # region SlithIR 1545 ################################################################################### 1546 ################################################################################### 1547 1548 def convert_expression_to_slithir_ssa(self) -> None: 1549 """ 1550 Assume generate_slithir_and_analyze was called on all functions 1551 1552 :return: 1553 """ 1554 from slither.slithir.variables import StateIRVariable 1555 1556 all_ssa_state_variables_instances = {} 1557 1558 for contract in self.inheritance: 1559 for v in contract.state_variables_declared: 1560 new_var = StateIRVariable(v) 1561 all_ssa_state_variables_instances[v.canonical_name] = new_var 1562 self._initial_state_variables.append(new_var) 1563 1564 for v in self.variables: 1565 if v.contract == self: 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 func in self.functions + list(self.modifiers): 1571 func.generate_slithir_ssa(all_ssa_state_variables_instances) 1572 1573 def fix_phi(self) -> None: 1574 last_state_variables_instances: Dict[str, List["StateVariable"]] = {} 1575 initial_state_variables_instances: Dict[str, "StateVariable"] = {} 1576 for v in self._initial_state_variables: 1577 last_state_variables_instances[v.canonical_name] = [] 1578 initial_state_variables_instances[v.canonical_name] = v 1579 1580 for func in self.functions + list(self.modifiers): 1581 result = func.get_last_ssa_state_variables_instances() 1582 for variable_name, instances in result.items(): 1583 # TODO: investigate the next operation 1584 last_state_variables_instances[variable_name] += list(instances) 1585 1586 for func in self.functions + list(self.modifiers): 1587 func.fix_phi(last_state_variables_instances, initial_state_variables_instances) 1588 1589 # endregion 1590 ################################################################################### 1591 ################################################################################### 1592 # region Built in definitions 1593 ################################################################################### 1594 ################################################################################### 1595 1596 def __eq__(self, other: Any) -> bool: 1597 if isinstance(other, str): 1598 return other == self.name 1599 return NotImplemented 1600 1601 def __neq__(self, other: Any) -> bool: 1602 if isinstance(other, str): 1603 return other != self.name 1604 return NotImplemented 1605 1606 def __str__(self) -> str: 1607 return self.name 1608 1609 def __hash__(self) -> int: 1610 return self._id # type:ignore 1611 1612 # 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["Function"]] = 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 stored_state_variables_ordered for all the storage variables following the storage order 445 Use transient_state_variables_ordered for all the transient variables following the storage order 446 447 list(StateVariable): List of the state variables. 448 """ 449 return list(self._variables.values()) 450 451 @property 452 def state_variables_entry_points(self) -> List["StateVariable"]: 453 """ 454 list(StateVariable): List of the state variables that are public. 455 """ 456 return [var for var in self._variables.values() if var.visibility == "public"] 457 458 @property 459 def state_variables_ordered(self) -> List["StateVariable"]: 460 """ 461 list(StateVariable): List of the state variables by order of declaration. 462 """ 463 return self._variables_ordered 464 465 def add_state_variables_ordered(self, new_vars: List["StateVariable"]) -> None: 466 self._variables_ordered += new_vars 467 468 @property 469 def storage_variables_ordered(self) -> List["StateVariable"]: 470 """ 471 list(StateVariable): List of the state variables in storage location by order of declaration. 472 """ 473 return [v for v in self._variables_ordered if v.is_stored] 474 475 @property 476 def transient_variables_ordered(self) -> List["StateVariable"]: 477 """ 478 list(StateVariable): List of the state variables in transient location by order of declaration. 479 """ 480 return [v for v in self._variables_ordered if v.is_transient] 481 482 @property 483 def state_variables_inherited(self) -> List["StateVariable"]: 484 """ 485 list(StateVariable): List of the inherited state variables 486 """ 487 return [s for s in self.state_variables if s.contract != self] 488 489 @property 490 def state_variables_declared(self) -> List["StateVariable"]: 491 """ 492 list(StateVariable): List of the state variables declared within the contract (not inherited) 493 """ 494 return [s for s in self.state_variables if s.contract == self] 495 496 @property 497 def slithir_variables(self) -> List["SlithIRVariable"]: 498 """ 499 List all of the slithir variables (non SSA) 500 """ 501 slithir_variabless = [f.slithir_variables for f in self.functions + self.modifiers] # type: ignore 502 slithir_variables = [item for sublist in slithir_variabless for item in sublist] 503 return list(set(slithir_variables)) 504 505 @property 506 def state_variables_used_in_reentrant_targets( 507 self, 508 ) -> Dict["StateVariable", Set[Union["StateVariable", "Function"]]]: 509 """ 510 Returns the state variables used in reentrant targets. Heuristics: 511 - Variable used (read/write) in entry points that are reentrant 512 - State variables that are public 513 514 """ 515 from slither.core.variables.state_variable import StateVariable 516 517 if self._state_variables_used_in_reentrant_targets is None: 518 reentrant_functions = [f for f in self.functions_entry_points if f.is_reentrant] 519 variables_used: Dict[ 520 StateVariable, Set[Union[StateVariable, "Function"]] 521 ] = defaultdict(set) 522 for function in reentrant_functions: 523 for ir in function.all_slithir_operations(): 524 state_variables = [v for v in ir.used if isinstance(v, StateVariable)] 525 for state_variable in state_variables: 526 variables_used[state_variable].add(ir.node.function) 527 for variable in [v for v in self.state_variables if v.visibility == "public"]: 528 variables_used[variable].add(variable) 529 self._state_variables_used_in_reentrant_targets = variables_used 530 return self._state_variables_used_in_reentrant_targets 531 532 # endregion 533 ################################################################################### 534 ################################################################################### 535 # region Constructors 536 ################################################################################### 537 ################################################################################### 538 539 @property 540 def constructor(self) -> Optional["Function"]: 541 """ 542 Return the contract's immediate constructor. 543 If there is no immediate constructor, returns the first constructor 544 executed, following the c3 linearization 545 Return None if there is no constructor. 546 """ 547 cst = self.constructors_declared 548 if cst: 549 return cst 550 for inherited_contract in self.inheritance: 551 cst = inherited_contract.constructors_declared 552 if cst: 553 return cst 554 return None 555 556 @property 557 def constructors_declared(self) -> Optional["Function"]: 558 return next( 559 ( 560 func 561 for func in self.functions 562 if func.is_constructor and func.contract_declarer == self 563 ), 564 None, 565 ) 566 567 @property 568 def constructors(self) -> List["FunctionContract"]: 569 """ 570 Return the list of constructors (including inherited) 571 """ 572 return [func for func in self.functions if func.is_constructor] 573 574 @property 575 def explicit_base_constructor_calls(self) -> List["Function"]: 576 """ 577 list(Function): List of the base constructors called explicitly by this contract definition. 578 579 Base constructors called by any constructor definition will not be included. 580 Base constructors implicitly called by the contract definition (without 581 parenthesis) will not be included. 582 583 On "contract B is A(){..}" it returns the constructor of A 584 """ 585 return [c.constructor for c in self._explicit_base_constructor_calls if c.constructor] 586 587 # endregion 588 ################################################################################### 589 ################################################################################### 590 # region Functions and Modifiers 591 ################################################################################### 592 ################################################################################### 593 594 @property 595 def functions_signatures(self) -> List[str]: 596 """ 597 Return the signatures of all the public/eterxnal functions/state variables 598 :return: list(string) the signatures of all the functions that can be called 599 """ 600 if self._signatures is None: 601 sigs = [ 602 v.full_name for v in self.state_variables if v.visibility in ["public", "external"] 603 ] 604 605 sigs += {f.full_name for f in self.functions if f.visibility in ["public", "external"]} 606 self._signatures = list(set(sigs)) 607 return self._signatures 608 609 @property 610 def functions_signatures_declared(self) -> List[str]: 611 """ 612 Return the signatures of the public/eterxnal functions/state variables that are declared by this contract 613 :return: list(string) the signatures of all the functions that can be called and are declared by this contract 614 """ 615 if self._signatures_declared is None: 616 sigs = [ 617 v.full_name 618 for v in self.state_variables_declared 619 if v.visibility in ["public", "external"] 620 ] 621 622 sigs += { 623 f.full_name 624 for f in self.functions_declared 625 if f.visibility in ["public", "external"] 626 } 627 self._signatures_declared = list(set(sigs)) 628 return self._signatures_declared 629 630 @property 631 def functions(self) -> List["FunctionContract"]: 632 """ 633 list(Function): List of the functions 634 """ 635 return list(self._functions.values()) 636 637 def available_functions_as_dict(self) -> Dict[str, "Function"]: 638 if self._available_functions_as_dict is None: 639 self._available_functions_as_dict = { 640 f.full_name: f for f in self._functions.values() if not f.is_shadowed 641 } 642 return self._available_functions_as_dict 643 644 def add_function(self, func: "FunctionContract") -> None: 645 self._functions[func.canonical_name] = func 646 647 def set_functions(self, functions: Dict[str, "FunctionContract"]) -> None: 648 """ 649 Set the functions 650 651 :param functions: dict full_name -> function 652 :return: 653 """ 654 self._functions = functions 655 656 @property 657 def functions_inherited(self) -> List["FunctionContract"]: 658 """ 659 list(Function): List of the inherited functions 660 """ 661 return [f for f in self.functions if f.contract_declarer != self] 662 663 @property 664 def functions_declared(self) -> List["FunctionContract"]: 665 """ 666 list(Function): List of the functions defined within the contract (not inherited) 667 """ 668 return [f for f in self.functions if f.contract_declarer == self] 669 670 @property 671 def functions_entry_points(self) -> List["FunctionContract"]: 672 """ 673 list(Functions): List of public and external functions 674 """ 675 return [ 676 f 677 for f in self.functions 678 if f.visibility in ["public", "external"] and not f.is_shadowed or f.is_fallback 679 ] 680 681 @property 682 def modifiers(self) -> List["Modifier"]: 683 """ 684 list(Modifier): List of the modifiers 685 """ 686 return list(self._modifiers.values()) 687 688 def available_modifiers_as_dict(self) -> Dict[str, "Modifier"]: 689 return {m.full_name: m for m in self._modifiers.values() if not m.is_shadowed} 690 691 def set_modifiers(self, modifiers: Dict[str, "Modifier"]) -> None: 692 """ 693 Set the modifiers 694 695 :param modifiers: dict full_name -> modifier 696 :return: 697 """ 698 self._modifiers = modifiers 699 700 @property 701 def modifiers_inherited(self) -> List["Modifier"]: 702 """ 703 list(Modifier): List of the inherited modifiers 704 """ 705 return [m for m in self.modifiers if m.contract_declarer != self] 706 707 @property 708 def modifiers_declared(self) -> List["Modifier"]: 709 """ 710 list(Modifier): List of the modifiers defined within the contract (not inherited) 711 """ 712 return [m for m in self.modifiers if m.contract_declarer == self] 713 714 @property 715 def functions_and_modifiers(self) -> List["Function"]: 716 """ 717 list(Function|Modifier): List of the functions and modifiers 718 """ 719 return self.functions + self.modifiers # type: ignore 720 721 @property 722 def functions_and_modifiers_inherited(self) -> List["Function"]: 723 """ 724 list(Function|Modifier): List of the inherited functions and modifiers 725 """ 726 return self.functions_inherited + self.modifiers_inherited # type: ignore 727 728 @property 729 def functions_and_modifiers_declared(self) -> List["Function"]: 730 """ 731 list(Function|Modifier): List of the functions and modifiers defined within the contract (not inherited) 732 """ 733 return self.functions_declared + self.modifiers_declared # type: ignore 734 735 @property 736 def fallback_function(self) -> Optional["FunctionContract"]: 737 if self._fallback_function is None: 738 for f in self.functions: 739 if f.is_fallback: 740 self._fallback_function = f 741 break 742 return self._fallback_function 743 744 @property 745 def receive_function(self) -> Optional["FunctionContract"]: 746 if self._receive_function is None: 747 for f in self.functions: 748 if f.is_receive: 749 self._receive_function = f 750 break 751 return self._receive_function 752 753 def available_elements_from_inheritances( 754 self, 755 elements: Dict[str, "Function"], 756 getter_available: Callable[["Contract"], List["FunctionContract"]], 757 ) -> Dict[str, "Function"]: 758 """ 759 760 :param elements: dict(canonical_name -> elements) 761 :param getter_available: fun x 762 :return: 763 """ 764 # keep track of the contracts visited 765 # to prevent an ovveride due to multiple inheritance of the same contract 766 # A is B, C, D is C, --> the second C was already seen 767 inherited_elements: Dict[str, "FunctionContract"] = {} 768 accessible_elements = {} 769 contracts_visited = [] 770 for father in self.inheritance_reverse: 771 functions: Dict[str, "FunctionContract"] = { 772 v.full_name: v 773 for v in getter_available(father) 774 if v.contract not in contracts_visited 775 and v.function_language 776 != FunctionLanguage.Yul # Yul functions are not propagated in the inheritance 777 } 778 contracts_visited.append(father) 779 inherited_elements.update(functions) 780 781 for element in inherited_elements.values(): 782 accessible_elements[element.full_name] = elements[element.canonical_name] 783 784 return accessible_elements 785 786 # endregion 787 ################################################################################### 788 ################################################################################### 789 # region Inheritance 790 ################################################################################### 791 ################################################################################### 792 793 @property 794 def inheritance(self) -> List["Contract"]: 795 """ 796 list(Contract): Inheritance list. Order: the first elem is the first father to be executed 797 """ 798 return list(self._inheritance) 799 800 @property 801 def immediate_inheritance(self) -> List["Contract"]: 802 """ 803 list(Contract): List of contracts immediately inherited from (fathers). Order: order of declaration. 804 """ 805 return list(self._immediate_inheritance) 806 807 @property 808 def inheritance_reverse(self) -> List["Contract"]: 809 """ 810 list(Contract): Inheritance list. Order: the last elem is the first father to be executed 811 """ 812 return list(reversed(self._inheritance)) 813 814 def set_inheritance( 815 self, 816 inheritance: List["Contract"], 817 immediate_inheritance: List["Contract"], 818 called_base_constructor_contracts: List["Contract"], 819 ) -> None: 820 self._inheritance = inheritance 821 self._immediate_inheritance = immediate_inheritance 822 self._explicit_base_constructor_calls = called_base_constructor_contracts 823 824 @property 825 def derived_contracts(self) -> List["Contract"]: 826 """ 827 list(Contract): Return the list of contracts derived from self 828 """ 829 candidates = self.compilation_unit.contracts 830 return [c for c in candidates if self in c.inheritance] # type: ignore 831 832 # endregion 833 ################################################################################### 834 ################################################################################### 835 # region Getters from/to object 836 ################################################################################### 837 ################################################################################### 838 839 def get_functions_reading_from_variable(self, variable: "Variable") -> List["Function"]: 840 """ 841 Return the functions reading the variable 842 """ 843 return [f for f in self.functions if f.is_reading(variable)] 844 845 def get_functions_writing_to_variable(self, variable: "Variable") -> List["Function"]: 846 """ 847 Return the functions writting the variable 848 """ 849 return [f for f in self.functions if f.is_writing(variable)] 850 851 def get_function_from_full_name(self, full_name: str) -> Optional["Function"]: 852 """ 853 Return a function from a full name 854 The full name differs from the solidity's signature are the type are conserved 855 For example contract type are kept, structure are not unrolled, etc 856 Args: 857 full_name (str): signature of the function (without return statement) 858 Returns: 859 Function 860 """ 861 return next( 862 (f for f in self.functions if f.full_name == full_name and not f.is_shadowed), 863 None, 864 ) 865 866 def get_function_from_signature(self, function_signature: str) -> Optional["Function"]: 867 """ 868 Return a function from a signature 869 Args: 870 function_signature (str): signature of the function (without return statement) 871 Returns: 872 Function 873 """ 874 return next( 875 ( 876 f 877 for f in self.functions 878 if f.solidity_signature == function_signature and not f.is_shadowed 879 ), 880 None, 881 ) 882 883 def get_modifier_from_signature(self, modifier_signature: str) -> Optional["Modifier"]: 884 """ 885 Return a modifier from a signature 886 887 :param modifier_signature: 888 """ 889 return next( 890 (m for m in self.modifiers if m.full_name == modifier_signature and not m.is_shadowed), 891 None, 892 ) 893 894 def get_function_from_canonical_name(self, canonical_name: str) -> Optional["Function"]: 895 """ 896 Return a function from a canonical name (contract.signature()) 897 Args: 898 canonical_name (str): canonical name of the function (without return statement) 899 Returns: 900 Function 901 """ 902 return next((f for f in self.functions if f.canonical_name == canonical_name), None) 903 904 def get_modifier_from_canonical_name(self, canonical_name: str) -> Optional["Modifier"]: 905 """ 906 Return a modifier from a canonical name (contract.signature()) 907 Args: 908 canonical_name (str): canonical name of the modifier 909 Returns: 910 Modifier 911 """ 912 return next((m for m in self.modifiers if m.canonical_name == canonical_name), None) 913 914 def get_state_variable_from_name(self, variable_name: str) -> Optional["StateVariable"]: 915 """ 916 Return a state variable from a name 917 918 :param variable_name: 919 """ 920 return next((v for v in self.state_variables if v.name == variable_name), None) 921 922 def get_state_variable_from_canonical_name( 923 self, canonical_name: str 924 ) -> Optional["StateVariable"]: 925 """ 926 Return a state variable from a canonical_name 927 Args: 928 canonical_name (str): name of the variable 929 Returns: 930 StateVariable 931 """ 932 return next((v for v in self.state_variables if v.canonical_name == canonical_name), None) 933 934 def get_structure_from_name(self, structure_name: str) -> Optional["StructureContract"]: 935 """ 936 Return a structure from a name 937 Args: 938 structure_name (str): name of the structure 939 Returns: 940 StructureContract 941 """ 942 return next((st for st in self.structures if st.name == structure_name), None) 943 944 def get_structure_from_canonical_name( 945 self, structure_name: str 946 ) -> Optional["StructureContract"]: 947 """ 948 Return a structure from a canonical name 949 Args: 950 structure_name (str): canonical name of the structure 951 Returns: 952 StructureContract 953 """ 954 return next((st for st in self.structures if st.canonical_name == structure_name), None) 955 956 def get_event_from_signature(self, event_signature: str) -> Optional["Event"]: 957 """ 958 Return an event from a signature 959 Args: 960 event_signature (str): signature of the event 961 Returns: 962 Event 963 """ 964 return next((e for e in self.events if e.full_name == event_signature), None) 965 966 def get_event_from_canonical_name(self, event_canonical_name: str) -> Optional["Event"]: 967 """ 968 Return an event from a canonical name 969 Args: 970 event_canonical_name (str): name of the event 971 Returns: 972 Event 973 """ 974 return next((e for e in self.events if e.canonical_name == event_canonical_name), None) 975 976 def get_enum_from_name(self, enum_name: str) -> Optional["Enum"]: 977 """ 978 Return an enum from a name 979 Args: 980 enum_name (str): name of the enum 981 Returns: 982 Enum 983 """ 984 return next((e for e in self.enums if e.name == enum_name), None) 985 986 def get_enum_from_canonical_name(self, enum_name: str) -> Optional["Enum"]: 987 """ 988 Return an enum from a canonical name 989 Args: 990 enum_name (str): canonical name of the enum 991 Returns: 992 Enum 993 """ 994 return next((e for e in self.enums if e.canonical_name == enum_name), None) 995 996 def get_functions_overridden_by(self, function: "Function") -> List["Function"]: 997 """ 998 Return the list of functions overridden by the function 999 Args: 1000 (core.Function) 1001 Returns: 1002 list(core.Function) 1003 1004 """ 1005 return function.overrides 1006 1007 # endregion 1008 ################################################################################### 1009 ################################################################################### 1010 # region Recursive getters 1011 ################################################################################### 1012 ################################################################################### 1013 1014 @property 1015 def all_functions_called(self) -> List["Function"]: 1016 """ 1017 list(Function): List of functions reachable from the contract 1018 Includes super, and private/internal functions not shadowed 1019 """ 1020 from slither.slithir.operations import Operation 1021 1022 if self._all_functions_called is None: 1023 all_functions = [f for f in self.functions + self.modifiers if not f.is_shadowed] # type: ignore 1024 all_callss = [f.all_internal_calls() for f in all_functions] + [list(all_functions)] 1025 all_calls = [ 1026 item.function if isinstance(item, Operation) else item 1027 for sublist in all_callss 1028 for item in sublist 1029 ] 1030 all_calls = list(set(all_calls)) 1031 1032 all_constructors = [c.constructor for c in self.inheritance if c.constructor] 1033 all_constructors = list(set(all_constructors)) 1034 1035 set_all_calls = set(all_calls + list(all_constructors)) 1036 1037 self._all_functions_called = [c for c in set_all_calls if isinstance(c, Function)] 1038 return self._all_functions_called 1039 1040 @property 1041 def all_state_variables_written(self) -> List["StateVariable"]: 1042 """ 1043 list(StateVariable): List all of the state variables written 1044 """ 1045 all_state_variables_writtens = [ 1046 f.all_state_variables_written() for f in self.functions + self.modifiers # type: ignore 1047 ] 1048 all_state_variables_written = [ 1049 item for sublist in all_state_variables_writtens for item in sublist 1050 ] 1051 return list(set(all_state_variables_written)) 1052 1053 @property 1054 def all_state_variables_read(self) -> List["StateVariable"]: 1055 """ 1056 list(StateVariable): List all of the state variables read 1057 """ 1058 all_state_variables_reads = [ 1059 f.all_state_variables_read() for f in self.functions + self.modifiers # type: ignore 1060 ] 1061 all_state_variables_read = [ 1062 item for sublist in all_state_variables_reads for item in sublist 1063 ] 1064 return list(set(all_state_variables_read)) 1065 1066 @property 1067 def all_library_calls(self) -> List["LibraryCall"]: 1068 """ 1069 list(LibraryCall): List all of the libraries func called 1070 """ 1071 all_high_level_callss = [f.all_library_calls() for f in self.functions + self.modifiers] # type: ignore 1072 all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist] 1073 return list(set(all_high_level_calls)) 1074 1075 @property 1076 def all_high_level_calls(self) -> List[Tuple["Contract", "HighLevelCall"]]: 1077 """ 1078 list(Tuple("Contract", "HighLevelCall")): List all of the external high level calls 1079 """ 1080 all_high_level_callss = [f.all_high_level_calls() for f in self.functions + self.modifiers] # type: ignore 1081 all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist] 1082 return list(set(all_high_level_calls)) 1083 1084 # endregion 1085 ################################################################################### 1086 ################################################################################### 1087 # region Summary information 1088 ################################################################################### 1089 ################################################################################### 1090 1091 def get_summary( 1092 self, include_shadowed: bool = True 1093 ) -> Tuple[str, List[str], List[str], List, List]: 1094 """Return the function summary 1095 1096 :param include_shadowed: boolean to indicate if shadowed functions should be included (default True) 1097 Returns: 1098 (str, list, list, list, list): (name, inheritance, variables, fuction summaries, modifier summaries) 1099 """ 1100 func_summaries = [ 1101 f.get_summary() for f in self.functions if (not f.is_shadowed or include_shadowed) 1102 ] 1103 modif_summaries = [ 1104 f.get_summary() for f in self.modifiers if (not f.is_shadowed or include_shadowed) 1105 ] 1106 return ( 1107 self.name, 1108 [str(x) for x in self.inheritance], 1109 [str(x) for x in self.variables], 1110 func_summaries, 1111 modif_summaries, 1112 ) 1113 1114 def is_signature_only(self) -> bool: 1115 """Detect if the contract has only abstract functions 1116 1117 Returns: 1118 bool: true if the function are abstract functions 1119 """ 1120 return all((not f.is_implemented) for f in self.functions) 1121 1122 # endregion 1123 ################################################################################### 1124 ################################################################################### 1125 # region ERC conformance 1126 ################################################################################### 1127 ################################################################################### 1128 1129 def ercs(self) -> List[str]: 1130 """ 1131 Return the ERC implemented 1132 :return: list of string 1133 """ 1134 all_erc = [ 1135 ("ERC20", self.is_erc20), 1136 ("ERC165", self.is_erc165), 1137 ("ERC1820", self.is_erc1820), 1138 ("ERC223", self.is_erc223), 1139 ("ERC721", self.is_erc721), 1140 ("ERC777", self.is_erc777), 1141 ("ERC2612", self.is_erc2612), 1142 ("ERC1363", self.is_erc1363), 1143 ("ERC4626", self.is_erc4626), 1144 ] 1145 1146 return [erc for erc, is_erc in all_erc if is_erc()] 1147 1148 def is_erc20(self) -> bool: 1149 """ 1150 Check if the contract is an erc20 token 1151 1152 Note: it does not check for correct return values 1153 :return: Returns a true if the contract is an erc20 1154 """ 1155 full_names = self.functions_signatures 1156 return all(s in full_names for s in ERC20_signatures) 1157 1158 def is_erc165(self) -> bool: 1159 """ 1160 Check if the contract is an erc165 token 1161 1162 Note: it does not check for correct return values 1163 :return: Returns a true if the contract is an erc165 1164 """ 1165 full_names = self.functions_signatures 1166 return all(s in full_names for s in ERC165_signatures) 1167 1168 def is_erc1820(self) -> bool: 1169 """ 1170 Check if the contract is an erc1820 1171 1172 Note: it does not check for correct return values 1173 :return: Returns a true if the contract is an erc165 1174 """ 1175 full_names = self.functions_signatures 1176 return all(s in full_names for s in ERC1820_signatures) 1177 1178 def is_erc223(self) -> bool: 1179 """ 1180 Check if the contract is an erc223 token 1181 1182 Note: it does not check for correct return values 1183 :return: Returns a true if the contract is an erc223 1184 """ 1185 full_names = self.functions_signatures 1186 return all(s in full_names for s in ERC223_signatures) 1187 1188 def is_erc721(self) -> bool: 1189 """ 1190 Check if the contract is an erc721 token 1191 1192 Note: it does not check for correct return values 1193 :return: Returns a true if the contract is an erc721 1194 """ 1195 full_names = self.functions_signatures 1196 return all(s in full_names for s in ERC721_signatures) 1197 1198 def is_erc777(self) -> bool: 1199 """ 1200 Check if the contract is an erc777 1201 1202 Note: it does not check for correct return values 1203 :return: Returns a true if the contract is an erc165 1204 """ 1205 full_names = self.functions_signatures 1206 return all(s in full_names for s in ERC777_signatures) 1207 1208 def is_erc1155(self) -> bool: 1209 """ 1210 Check if the contract is an erc1155 1211 1212 Note: it does not check for correct return values 1213 :return: Returns a true if the contract is an erc1155 1214 """ 1215 full_names = self.functions_signatures 1216 return all(s in full_names for s in ERC1155_signatures) 1217 1218 def is_erc4626(self) -> bool: 1219 """ 1220 Check if the contract is an erc4626 1221 1222 Note: it does not check for correct return values 1223 :return: Returns a true if the contract is an erc4626 1224 """ 1225 full_names = self.functions_signatures 1226 return all(s in full_names for s in ERC4626_signatures) 1227 1228 def is_erc2612(self) -> bool: 1229 """ 1230 Check if the contract is an erc2612 1231 1232 Note: it does not check for correct return values 1233 :return: Returns a true if the contract is an erc2612 1234 """ 1235 full_names = self.functions_signatures 1236 return all(s in full_names for s in ERC2612_signatures) 1237 1238 def is_erc1363(self) -> bool: 1239 """ 1240 Check if the contract is an erc1363 1241 1242 Note: it does not check for correct return values 1243 :return: Returns a true if the contract is an erc1363 1244 """ 1245 full_names = self.functions_signatures 1246 return all(s in full_names for s in ERC1363_signatures) 1247 1248 def is_erc4524(self) -> bool: 1249 """ 1250 Check if the contract is an erc4524 1251 1252 Note: it does not check for correct return values 1253 :return: Returns a true if the contract is an erc4524 1254 """ 1255 full_names = self.functions_signatures 1256 return all(s in full_names for s in ERC4524_signatures) 1257 1258 @property 1259 def is_token(self) -> bool: 1260 """ 1261 Check if the contract follows one of the standard ERC token 1262 :return: 1263 """ 1264 return ( 1265 self.is_erc20() 1266 or self.is_erc721() 1267 or self.is_erc165() 1268 or self.is_erc223() 1269 or self.is_erc777() 1270 or self.is_erc1155() 1271 ) 1272 1273 def is_possible_erc20(self) -> bool: 1274 """ 1275 Checks if the provided contract could be attempting to implement ERC20 standards. 1276 1277 :return: Returns a boolean indicating if the provided contract met the token standard. 1278 """ 1279 # We do not check for all the functions, as name(), symbol(), might give too many FPs 1280 full_names = self.functions_signatures 1281 return ( 1282 "transfer(address,uint256)" in full_names 1283 or "transferFrom(address,address,uint256)" in full_names 1284 or "approve(address,uint256)" in full_names 1285 ) 1286 1287 def is_possible_erc721(self) -> bool: 1288 """ 1289 Checks if the provided contract could be attempting to implement ERC721 standards. 1290 1291 :return: Returns a boolean indicating if the provided contract met the token standard. 1292 """ 1293 # We do not check for all the functions, as name(), symbol(), might give too many FPs 1294 full_names = self.functions_signatures 1295 return ( 1296 "ownerOf(uint256)" in full_names 1297 or "safeTransferFrom(address,address,uint256,bytes)" in full_names 1298 or "safeTransferFrom(address,address,uint256)" in full_names 1299 or "setApprovalForAll(address,bool)" in full_names 1300 or "getApproved(uint256)" in full_names 1301 or "isApprovedForAll(address,address)" in full_names 1302 ) 1303 1304 @property 1305 def is_possible_token(self) -> bool: 1306 """ 1307 Check if the contract is a potential token (it might not implement all the functions) 1308 :return: 1309 """ 1310 return self.is_possible_erc20() or self.is_possible_erc721() 1311 1312 # endregion 1313 ################################################################################### 1314 ################################################################################### 1315 # region Dependencies 1316 ################################################################################### 1317 ################################################################################### 1318 1319 def is_from_dependency(self) -> bool: 1320 return self.compilation_unit.core.crytic_compile.is_dependency( 1321 self.source_mapping.filename.absolute 1322 ) 1323 1324 # endregion 1325 ################################################################################### 1326 ################################################################################### 1327 # region Test 1328 ################################################################################### 1329 ################################################################################### 1330 1331 @property 1332 def is_truffle_migration(self) -> bool: 1333 """ 1334 Return true if the contract is the Migrations contract needed for Truffle 1335 :return: 1336 """ 1337 if self.compilation_unit.core.crytic_compile.platform == PlatformType.TRUFFLE: 1338 if self.name == "Migrations": 1339 paths = Path(self.source_mapping.filename.absolute).parts 1340 if len(paths) >= 2: 1341 return paths[-2] == "contracts" and paths[-1] == "migrations.sol" 1342 return False 1343 1344 @property 1345 def is_test(self) -> bool: 1346 return is_test_contract(self) or self.is_truffle_migration # type: ignore 1347 1348 # endregion 1349 ################################################################################### 1350 ################################################################################### 1351 # region Function analyses 1352 ################################################################################### 1353 ################################################################################### 1354 1355 def update_read_write_using_ssa(self) -> None: 1356 for function in self.functions + list(self.modifiers): 1357 function.update_read_write_using_ssa() 1358 1359 # endregion 1360 ################################################################################### 1361 ################################################################################### 1362 # region Upgradeability 1363 ################################################################################### 1364 ################################################################################### 1365 1366 @property 1367 def is_upgradeable(self) -> bool: 1368 if self._is_upgradeable is None: 1369 self._is_upgradeable = False 1370 initializable = self.file_scope.get_contract_from_name("Initializable") 1371 if initializable: 1372 if initializable in self.inheritance: 1373 self._is_upgradeable = True 1374 else: 1375 for contract in self.inheritance + [self]: 1376 # This might lead to false positive 1377 # Not sure why pylint is having a trouble here 1378 # pylint: disable=no-member 1379 lower_name = contract.name.lower() 1380 if "upgradeable" in lower_name or "upgradable" in lower_name: 1381 self._is_upgradeable = True 1382 break 1383 if "initializable" in lower_name: 1384 self._is_upgradeable = True 1385 break 1386 return self._is_upgradeable 1387 1388 @is_upgradeable.setter 1389 def is_upgradeable(self, upgradeable: bool) -> None: 1390 self._is_upgradeable = upgradeable 1391 1392 @property 1393 def is_upgradeable_proxy(self) -> bool: 1394 from slither.core.cfg.node import NodeType 1395 from slither.slithir.operations import LowLevelCall 1396 1397 if self._is_upgradeable_proxy is None: 1398 self._is_upgradeable_proxy = False 1399 if "Proxy" in self.name: 1400 self._is_upgradeable_proxy = True 1401 return True 1402 for f in self.functions: 1403 if f.is_fallback: 1404 for node in f.all_nodes(): 1405 for ir in node.irs: 1406 if isinstance(ir, LowLevelCall) and ir.function_name == "delegatecall": 1407 self._is_upgradeable_proxy = True 1408 return self._is_upgradeable_proxy 1409 if node.type == NodeType.ASSEMBLY: 1410 inline_asm = node.inline_asm 1411 if inline_asm: 1412 if "delegatecall" in inline_asm: 1413 self._is_upgradeable_proxy = True 1414 return self._is_upgradeable_proxy 1415 return self._is_upgradeable_proxy 1416 1417 @is_upgradeable_proxy.setter 1418 def is_upgradeable_proxy(self, upgradeable_proxy: bool) -> None: 1419 self._is_upgradeable_proxy = upgradeable_proxy 1420 1421 @property 1422 def upgradeable_version(self) -> Optional[str]: 1423 return self._upgradeable_version 1424 1425 @upgradeable_version.setter 1426 def upgradeable_version(self, version_name: str) -> None: 1427 self._upgradeable_version = version_name 1428 1429 # endregion 1430 ################################################################################### 1431 ################################################################################### 1432 # region Internals 1433 ################################################################################### 1434 ################################################################################### 1435 1436 @property 1437 def is_incorrectly_constructed(self) -> bool: 1438 """ 1439 Return true if there was an internal Slither's issue when analyzing the contract 1440 :return: 1441 """ 1442 return self._is_incorrectly_parsed 1443 1444 @is_incorrectly_constructed.setter 1445 def is_incorrectly_constructed(self, incorrect: bool) -> None: 1446 self._is_incorrectly_parsed = incorrect 1447 1448 def add_constructor_variables(self) -> None: 1449 from slither.core.declarations.function_contract import FunctionContract 1450 1451 if self.state_variables: 1452 for (idx, variable_candidate) in enumerate(self.state_variables): 1453 if variable_candidate.expression and not variable_candidate.is_constant: 1454 1455 constructor_variable = FunctionContract(self.compilation_unit) 1456 constructor_variable.set_function_type(FunctionType.CONSTRUCTOR_VARIABLES) 1457 constructor_variable.set_contract(self) # type: ignore 1458 constructor_variable.set_contract_declarer(self) # type: ignore 1459 constructor_variable.set_visibility("internal") 1460 # For now, source mapping of the constructor variable is the whole contract 1461 # Could be improved with a targeted source mapping 1462 constructor_variable.set_offset(self.source_mapping, self.compilation_unit) 1463 self._functions[constructor_variable.canonical_name] = constructor_variable 1464 1465 prev_node = self._create_node( 1466 constructor_variable, 0, variable_candidate, constructor_variable 1467 ) 1468 variable_candidate.node_initialization = prev_node 1469 counter = 1 1470 for v in self.state_variables[idx + 1 :]: 1471 if v.expression and not v.is_constant: 1472 next_node = self._create_node( 1473 constructor_variable, counter, v, prev_node.scope 1474 ) 1475 v.node_initialization = next_node 1476 prev_node.add_son(next_node) 1477 next_node.add_father(prev_node) 1478 prev_node = next_node 1479 counter += 1 1480 break 1481 1482 for (idx, variable_candidate) in enumerate(self.state_variables): 1483 if variable_candidate.expression and variable_candidate.is_constant: 1484 1485 constructor_variable = FunctionContract(self.compilation_unit) 1486 constructor_variable.set_function_type( 1487 FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES 1488 ) 1489 constructor_variable.set_contract(self) # type: ignore 1490 constructor_variable.set_contract_declarer(self) # type: ignore 1491 constructor_variable.set_visibility("internal") 1492 # For now, source mapping of the constructor variable is the whole contract 1493 # Could be improved with a targeted source mapping 1494 constructor_variable.set_offset(self.source_mapping, self.compilation_unit) 1495 self._functions[constructor_variable.canonical_name] = constructor_variable 1496 1497 prev_node = self._create_node( 1498 constructor_variable, 0, variable_candidate, constructor_variable 1499 ) 1500 variable_candidate.node_initialization = prev_node 1501 counter = 1 1502 for v in self.state_variables[idx + 1 :]: 1503 if v.expression and v.is_constant: 1504 next_node = self._create_node( 1505 constructor_variable, counter, v, prev_node.scope 1506 ) 1507 v.node_initialization = next_node 1508 prev_node.add_son(next_node) 1509 next_node.add_father(prev_node) 1510 prev_node = next_node 1511 counter += 1 1512 1513 break 1514 1515 def _create_node( 1516 self, func: Function, counter: int, variable: "Variable", scope: Union[Scope, Function] 1517 ) -> "Node": 1518 from slither.core.cfg.node import Node, NodeType 1519 from slither.core.expressions import ( 1520 AssignmentOperationType, 1521 AssignmentOperation, 1522 Identifier, 1523 ) 1524 1525 # Function uses to create node for state variable declaration statements 1526 node = Node(NodeType.OTHER_ENTRYPOINT, counter, scope, func.file_scope) 1527 node.set_offset(variable.source_mapping, self.compilation_unit) 1528 node.set_function(func) 1529 func.add_node(node) 1530 assert variable.expression 1531 expression = AssignmentOperation( 1532 Identifier(variable), 1533 variable.expression, 1534 AssignmentOperationType.ASSIGN, 1535 variable.type, 1536 ) 1537 1538 expression.set_offset(variable.source_mapping, self.compilation_unit) 1539 node.add_expression(expression) 1540 return node 1541 1542 # endregion 1543 ################################################################################### 1544 ################################################################################### 1545 # region SlithIR 1546 ################################################################################### 1547 ################################################################################### 1548 1549 def convert_expression_to_slithir_ssa(self) -> None: 1550 """ 1551 Assume generate_slithir_and_analyze was called on all functions 1552 1553 :return: 1554 """ 1555 from slither.slithir.variables import StateIRVariable 1556 1557 all_ssa_state_variables_instances = {} 1558 1559 for contract in self.inheritance: 1560 for v in contract.state_variables_declared: 1561 new_var = StateIRVariable(v) 1562 all_ssa_state_variables_instances[v.canonical_name] = new_var 1563 self._initial_state_variables.append(new_var) 1564 1565 for v in self.variables: 1566 if v.contract == self: 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 func in self.functions + list(self.modifiers): 1572 func.generate_slithir_ssa(all_ssa_state_variables_instances) 1573 1574 def fix_phi(self) -> None: 1575 last_state_variables_instances: Dict[str, List["StateVariable"]] = {} 1576 initial_state_variables_instances: Dict[str, "StateVariable"] = {} 1577 for v in self._initial_state_variables: 1578 last_state_variables_instances[v.canonical_name] = [] 1579 initial_state_variables_instances[v.canonical_name] = v 1580 1581 for func in self.functions + list(self.modifiers): 1582 result = func.get_last_ssa_state_variables_instances() 1583 for variable_name, instances in result.items(): 1584 # TODO: investigate the next operation 1585 last_state_variables_instances[variable_name] += list(instances) 1586 1587 for func in self.functions + list(self.modifiers): 1588 func.fix_phi(last_state_variables_instances, initial_state_variables_instances) 1589 1590 # endregion 1591 ################################################################################### 1592 ################################################################################### 1593 # region Built in definitions 1594 ################################################################################### 1595 ################################################################################### 1596 1597 def __eq__(self, other: Any) -> bool: 1598 if isinstance(other, str): 1599 return other == self.name 1600 return NotImplemented 1601 1602 def __neq__(self, other: Any) -> bool: 1603 if isinstance(other, str): 1604 return other != self.name 1605 return NotImplemented 1606 1607 def __str__(self) -> str: 1608 return self.name 1609 1610 def __hash__(self) -> int: 1611 return self._id # type:ignore 1612 1613 # 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["Function"]] = 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 stored_state_variables_ordered for all the storage variables following the storage order 445 Use transient_state_variables_ordered for all the transient variables following the storage order 446 447 list(StateVariable): List of the state variables. 448 """ 449 return list(self._variables.values())
Returns all the accessible variables (do not include private variable from inherited contract). Use stored_state_variables_ordered for all the storage variables following the storage order Use transient_state_variables_ordered for all the transient variables following the storage order
list(StateVariable): List of the state variables.
451 @property 452 def state_variables_entry_points(self) -> List["StateVariable"]: 453 """ 454 list(StateVariable): List of the state variables that are public. 455 """ 456 return [var for var in self._variables.values() if var.visibility == "public"]
list(StateVariable): List of the state variables that are public.
458 @property 459 def state_variables_ordered(self) -> List["StateVariable"]: 460 """ 461 list(StateVariable): List of the state variables by order of declaration. 462 """ 463 return self._variables_ordered
list(StateVariable): List of the state variables by order of declaration.
468 @property 469 def storage_variables_ordered(self) -> List["StateVariable"]: 470 """ 471 list(StateVariable): List of the state variables in storage location by order of declaration. 472 """ 473 return [v for v in self._variables_ordered if v.is_stored]
list(StateVariable): List of the state variables in storage location by order of declaration.
475 @property 476 def transient_variables_ordered(self) -> List["StateVariable"]: 477 """ 478 list(StateVariable): List of the state variables in transient location by order of declaration. 479 """ 480 return [v for v in self._variables_ordered if v.is_transient]
list(StateVariable): List of the state variables in transient location by order of declaration.
482 @property 483 def state_variables_inherited(self) -> List["StateVariable"]: 484 """ 485 list(StateVariable): List of the inherited state variables 486 """ 487 return [s for s in self.state_variables if s.contract != self]
list(StateVariable): List of the inherited state variables
489 @property 490 def state_variables_declared(self) -> List["StateVariable"]: 491 """ 492 list(StateVariable): List of the state variables declared within the contract (not inherited) 493 """ 494 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)
496 @property 497 def slithir_variables(self) -> List["SlithIRVariable"]: 498 """ 499 List all of the slithir variables (non SSA) 500 """ 501 slithir_variabless = [f.slithir_variables for f in self.functions + self.modifiers] # type: ignore 502 slithir_variables = [item for sublist in slithir_variabless for item in sublist] 503 return list(set(slithir_variables))
List all of the slithir variables (non SSA)
505 @property 506 def state_variables_used_in_reentrant_targets( 507 self, 508 ) -> Dict["StateVariable", Set[Union["StateVariable", "Function"]]]: 509 """ 510 Returns the state variables used in reentrant targets. Heuristics: 511 - Variable used (read/write) in entry points that are reentrant 512 - State variables that are public 513 514 """ 515 from slither.core.variables.state_variable import StateVariable 516 517 if self._state_variables_used_in_reentrant_targets is None: 518 reentrant_functions = [f for f in self.functions_entry_points if f.is_reentrant] 519 variables_used: Dict[ 520 StateVariable, Set[Union[StateVariable, "Function"]] 521 ] = defaultdict(set) 522 for function in reentrant_functions: 523 for ir in function.all_slithir_operations(): 524 state_variables = [v for v in ir.used if isinstance(v, StateVariable)] 525 for state_variable in state_variables: 526 variables_used[state_variable].add(ir.node.function) 527 for variable in [v for v in self.state_variables if v.visibility == "public"]: 528 variables_used[variable].add(variable) 529 self._state_variables_used_in_reentrant_targets = variables_used 530 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
539 @property 540 def constructor(self) -> Optional["Function"]: 541 """ 542 Return the contract's immediate constructor. 543 If there is no immediate constructor, returns the first constructor 544 executed, following the c3 linearization 545 Return None if there is no constructor. 546 """ 547 cst = self.constructors_declared 548 if cst: 549 return cst 550 for inherited_contract in self.inheritance: 551 cst = inherited_contract.constructors_declared 552 if cst: 553 return cst 554 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.
567 @property 568 def constructors(self) -> List["FunctionContract"]: 569 """ 570 Return the list of constructors (including inherited) 571 """ 572 return [func for func in self.functions if func.is_constructor]
Return the list of constructors (including inherited)
574 @property 575 def explicit_base_constructor_calls(self) -> List["Function"]: 576 """ 577 list(Function): List of the base constructors called explicitly by this contract definition. 578 579 Base constructors called by any constructor definition will not be included. 580 Base constructors implicitly called by the contract definition (without 581 parenthesis) will not be included. 582 583 On "contract B is A(){..}" it returns the constructor of A 584 """ 585 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
594 @property 595 def functions_signatures(self) -> List[str]: 596 """ 597 Return the signatures of all the public/eterxnal functions/state variables 598 :return: list(string) the signatures of all the functions that can be called 599 """ 600 if self._signatures is None: 601 sigs = [ 602 v.full_name for v in self.state_variables if v.visibility in ["public", "external"] 603 ] 604 605 sigs += {f.full_name for f in self.functions if f.visibility in ["public", "external"]} 606 self._signatures = list(set(sigs)) 607 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
609 @property 610 def functions_signatures_declared(self) -> List[str]: 611 """ 612 Return the signatures of the public/eterxnal functions/state variables that are declared by this contract 613 :return: list(string) the signatures of all the functions that can be called and are declared by this contract 614 """ 615 if self._signatures_declared is None: 616 sigs = [ 617 v.full_name 618 for v in self.state_variables_declared 619 if v.visibility in ["public", "external"] 620 ] 621 622 sigs += { 623 f.full_name 624 for f in self.functions_declared 625 if f.visibility in ["public", "external"] 626 } 627 self._signatures_declared = list(set(sigs)) 628 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
630 @property 631 def functions(self) -> List["FunctionContract"]: 632 """ 633 list(Function): List of the functions 634 """ 635 return list(self._functions.values())
list(Function): List of the functions
647 def set_functions(self, functions: Dict[str, "FunctionContract"]) -> None: 648 """ 649 Set the functions 650 651 :param functions: dict full_name -> function 652 :return: 653 """ 654 self._functions = functions
Set the functions
Parameters
- functions: dict full_name -> function
Returns
656 @property 657 def functions_inherited(self) -> List["FunctionContract"]: 658 """ 659 list(Function): List of the inherited functions 660 """ 661 return [f for f in self.functions if f.contract_declarer != self]
list(Function): List of the inherited functions
663 @property 664 def functions_declared(self) -> List["FunctionContract"]: 665 """ 666 list(Function): List of the functions defined within the contract (not inherited) 667 """ 668 return [f for f in self.functions if f.contract_declarer == self]
list(Function): List of the functions defined within the contract (not inherited)
670 @property 671 def functions_entry_points(self) -> List["FunctionContract"]: 672 """ 673 list(Functions): List of public and external functions 674 """ 675 return [ 676 f 677 for f in self.functions 678 if f.visibility in ["public", "external"] and not f.is_shadowed or f.is_fallback 679 ]
list(Functions): List of public and external functions
681 @property 682 def modifiers(self) -> List["Modifier"]: 683 """ 684 list(Modifier): List of the modifiers 685 """ 686 return list(self._modifiers.values())
list(Modifier): List of the modifiers
691 def set_modifiers(self, modifiers: Dict[str, "Modifier"]) -> None: 692 """ 693 Set the modifiers 694 695 :param modifiers: dict full_name -> modifier 696 :return: 697 """ 698 self._modifiers = modifiers
Set the modifiers
Parameters
- modifiers: dict full_name -> modifier
Returns
700 @property 701 def modifiers_inherited(self) -> List["Modifier"]: 702 """ 703 list(Modifier): List of the inherited modifiers 704 """ 705 return [m for m in self.modifiers if m.contract_declarer != self]
list(Modifier): List of the inherited modifiers
707 @property 708 def modifiers_declared(self) -> List["Modifier"]: 709 """ 710 list(Modifier): List of the modifiers defined within the contract (not inherited) 711 """ 712 return [m for m in self.modifiers if m.contract_declarer == self]
list(Modifier): List of the modifiers defined within the contract (not inherited)
714 @property 715 def functions_and_modifiers(self) -> List["Function"]: 716 """ 717 list(Function|Modifier): List of the functions and modifiers 718 """ 719 return self.functions + self.modifiers # type: ignore
list(Function|Modifier): List of the functions and modifiers
721 @property 722 def functions_and_modifiers_inherited(self) -> List["Function"]: 723 """ 724 list(Function|Modifier): List of the inherited functions and modifiers 725 """ 726 return self.functions_inherited + self.modifiers_inherited # type: ignore
list(Function|Modifier): List of the inherited functions and modifiers
728 @property 729 def functions_and_modifiers_declared(self) -> List["Function"]: 730 """ 731 list(Function|Modifier): List of the functions and modifiers defined within the contract (not inherited) 732 """ 733 return self.functions_declared + self.modifiers_declared # type: ignore
list(Function|Modifier): List of the functions and modifiers defined within the contract (not inherited)
753 def available_elements_from_inheritances( 754 self, 755 elements: Dict[str, "Function"], 756 getter_available: Callable[["Contract"], List["FunctionContract"]], 757 ) -> Dict[str, "Function"]: 758 """ 759 760 :param elements: dict(canonical_name -> elements) 761 :param getter_available: fun x 762 :return: 763 """ 764 # keep track of the contracts visited 765 # to prevent an ovveride due to multiple inheritance of the same contract 766 # A is B, C, D is C, --> the second C was already seen 767 inherited_elements: Dict[str, "FunctionContract"] = {} 768 accessible_elements = {} 769 contracts_visited = [] 770 for father in self.inheritance_reverse: 771 functions: Dict[str, "FunctionContract"] = { 772 v.full_name: v 773 for v in getter_available(father) 774 if v.contract not in contracts_visited 775 and v.function_language 776 != FunctionLanguage.Yul # Yul functions are not propagated in the inheritance 777 } 778 contracts_visited.append(father) 779 inherited_elements.update(functions) 780 781 for element in inherited_elements.values(): 782 accessible_elements[element.full_name] = elements[element.canonical_name] 783 784 return accessible_elements
Parameters
- elements: dict(canonical_name -> elements)
- getter_available: fun x
Returns
793 @property 794 def inheritance(self) -> List["Contract"]: 795 """ 796 list(Contract): Inheritance list. Order: the first elem is the first father to be executed 797 """ 798 return list(self._inheritance)
list(Contract): Inheritance list. Order: the first elem is the first father to be executed
800 @property 801 def immediate_inheritance(self) -> List["Contract"]: 802 """ 803 list(Contract): List of contracts immediately inherited from (fathers). Order: order of declaration. 804 """ 805 return list(self._immediate_inheritance)
list(Contract): List of contracts immediately inherited from (fathers). Order: order of declaration.
807 @property 808 def inheritance_reverse(self) -> List["Contract"]: 809 """ 810 list(Contract): Inheritance list. Order: the last elem is the first father to be executed 811 """ 812 return list(reversed(self._inheritance))
list(Contract): Inheritance list. Order: the last elem is the first father to be executed
814 def set_inheritance( 815 self, 816 inheritance: List["Contract"], 817 immediate_inheritance: List["Contract"], 818 called_base_constructor_contracts: List["Contract"], 819 ) -> None: 820 self._inheritance = inheritance 821 self._immediate_inheritance = immediate_inheritance 822 self._explicit_base_constructor_calls = called_base_constructor_contracts
824 @property 825 def derived_contracts(self) -> List["Contract"]: 826 """ 827 list(Contract): Return the list of contracts derived from self 828 """ 829 candidates = self.compilation_unit.contracts 830 return [c for c in candidates if self in c.inheritance] # type: ignore
list(Contract): Return the list of contracts derived from self
839 def get_functions_reading_from_variable(self, variable: "Variable") -> List["Function"]: 840 """ 841 Return the functions reading the variable 842 """ 843 return [f for f in self.functions if f.is_reading(variable)]
Return the functions reading the variable
845 def get_functions_writing_to_variable(self, variable: "Variable") -> List["Function"]: 846 """ 847 Return the functions writting the variable 848 """ 849 return [f for f in self.functions if f.is_writing(variable)]
Return the functions writting the variable
851 def get_function_from_full_name(self, full_name: str) -> Optional["Function"]: 852 """ 853 Return a function from a full name 854 The full name differs from the solidity's signature are the type are conserved 855 For example contract type are kept, structure are not unrolled, etc 856 Args: 857 full_name (str): signature of the function (without return statement) 858 Returns: 859 Function 860 """ 861 return next( 862 (f for f in self.functions if f.full_name == full_name and not f.is_shadowed), 863 None, 864 )
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
866 def get_function_from_signature(self, function_signature: str) -> Optional["Function"]: 867 """ 868 Return a function from a signature 869 Args: 870 function_signature (str): signature of the function (without return statement) 871 Returns: 872 Function 873 """ 874 return next( 875 ( 876 f 877 for f in self.functions 878 if f.solidity_signature == function_signature and not f.is_shadowed 879 ), 880 None, 881 )
Return a function from a signature Args: function_signature (str): signature of the function (without return statement) Returns: Function
883 def get_modifier_from_signature(self, modifier_signature: str) -> Optional["Modifier"]: 884 """ 885 Return a modifier from a signature 886 887 :param modifier_signature: 888 """ 889 return next( 890 (m for m in self.modifiers if m.full_name == modifier_signature and not m.is_shadowed), 891 None, 892 )
Return a modifier from a signature
Parameters
- modifier_signature:
894 def get_function_from_canonical_name(self, canonical_name: str) -> Optional["Function"]: 895 """ 896 Return a function from a canonical name (contract.signature()) 897 Args: 898 canonical_name (str): canonical name of the function (without return statement) 899 Returns: 900 Function 901 """ 902 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
904 def get_modifier_from_canonical_name(self, canonical_name: str) -> Optional["Modifier"]: 905 """ 906 Return a modifier from a canonical name (contract.signature()) 907 Args: 908 canonical_name (str): canonical name of the modifier 909 Returns: 910 Modifier 911 """ 912 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
914 def get_state_variable_from_name(self, variable_name: str) -> Optional["StateVariable"]: 915 """ 916 Return a state variable from a name 917 918 :param variable_name: 919 """ 920 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:
922 def get_state_variable_from_canonical_name( 923 self, canonical_name: str 924 ) -> Optional["StateVariable"]: 925 """ 926 Return a state variable from a canonical_name 927 Args: 928 canonical_name (str): name of the variable 929 Returns: 930 StateVariable 931 """ 932 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
934 def get_structure_from_name(self, structure_name: str) -> Optional["StructureContract"]: 935 """ 936 Return a structure from a name 937 Args: 938 structure_name (str): name of the structure 939 Returns: 940 StructureContract 941 """ 942 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
944 def get_structure_from_canonical_name( 945 self, structure_name: str 946 ) -> Optional["StructureContract"]: 947 """ 948 Return a structure from a canonical name 949 Args: 950 structure_name (str): canonical name of the structure 951 Returns: 952 StructureContract 953 """ 954 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
956 def get_event_from_signature(self, event_signature: str) -> Optional["Event"]: 957 """ 958 Return an event from a signature 959 Args: 960 event_signature (str): signature of the event 961 Returns: 962 Event 963 """ 964 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
966 def get_event_from_canonical_name(self, event_canonical_name: str) -> Optional["Event"]: 967 """ 968 Return an event from a canonical name 969 Args: 970 event_canonical_name (str): name of the event 971 Returns: 972 Event 973 """ 974 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
976 def get_enum_from_name(self, enum_name: str) -> Optional["Enum"]: 977 """ 978 Return an enum from a name 979 Args: 980 enum_name (str): name of the enum 981 Returns: 982 Enum 983 """ 984 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
986 def get_enum_from_canonical_name(self, enum_name: str) -> Optional["Enum"]: 987 """ 988 Return an enum from a canonical name 989 Args: 990 enum_name (str): canonical name of the enum 991 Returns: 992 Enum 993 """ 994 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
996 def get_functions_overridden_by(self, function: "Function") -> List["Function"]: 997 """ 998 Return the list of functions overridden by the function 999 Args: 1000 (core.Function) 1001 Returns: 1002 list(core.Function) 1003 1004 """ 1005 return function.overrides
Return the list of functions overridden by the function Args: (core.Function) Returns: list(core.Function)
1014 @property 1015 def all_functions_called(self) -> List["Function"]: 1016 """ 1017 list(Function): List of functions reachable from the contract 1018 Includes super, and private/internal functions not shadowed 1019 """ 1020 from slither.slithir.operations import Operation 1021 1022 if self._all_functions_called is None: 1023 all_functions = [f for f in self.functions + self.modifiers if not f.is_shadowed] # type: ignore 1024 all_callss = [f.all_internal_calls() for f in all_functions] + [list(all_functions)] 1025 all_calls = [ 1026 item.function if isinstance(item, Operation) else item 1027 for sublist in all_callss 1028 for item in sublist 1029 ] 1030 all_calls = list(set(all_calls)) 1031 1032 all_constructors = [c.constructor for c in self.inheritance if c.constructor] 1033 all_constructors = list(set(all_constructors)) 1034 1035 set_all_calls = set(all_calls + list(all_constructors)) 1036 1037 self._all_functions_called = [c for c in set_all_calls if isinstance(c, Function)] 1038 return self._all_functions_called
list(Function): List of functions reachable from the contract Includes super, and private/internal functions not shadowed
1040 @property 1041 def all_state_variables_written(self) -> List["StateVariable"]: 1042 """ 1043 list(StateVariable): List all of the state variables written 1044 """ 1045 all_state_variables_writtens = [ 1046 f.all_state_variables_written() for f in self.functions + self.modifiers # type: ignore 1047 ] 1048 all_state_variables_written = [ 1049 item for sublist in all_state_variables_writtens for item in sublist 1050 ] 1051 return list(set(all_state_variables_written))
list(StateVariable): List all of the state variables written
1053 @property 1054 def all_state_variables_read(self) -> List["StateVariable"]: 1055 """ 1056 list(StateVariable): List all of the state variables read 1057 """ 1058 all_state_variables_reads = [ 1059 f.all_state_variables_read() for f in self.functions + self.modifiers # type: ignore 1060 ] 1061 all_state_variables_read = [ 1062 item for sublist in all_state_variables_reads for item in sublist 1063 ] 1064 return list(set(all_state_variables_read))
list(StateVariable): List all of the state variables read
1066 @property 1067 def all_library_calls(self) -> List["LibraryCall"]: 1068 """ 1069 list(LibraryCall): List all of the libraries func called 1070 """ 1071 all_high_level_callss = [f.all_library_calls() for f in self.functions + self.modifiers] # type: ignore 1072 all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist] 1073 return list(set(all_high_level_calls))
list(LibraryCall): List all of the libraries func called
1075 @property 1076 def all_high_level_calls(self) -> List[Tuple["Contract", "HighLevelCall"]]: 1077 """ 1078 list(Tuple("Contract", "HighLevelCall")): List all of the external high level calls 1079 """ 1080 all_high_level_callss = [f.all_high_level_calls() for f in self.functions + self.modifiers] # type: ignore 1081 all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist] 1082 return list(set(all_high_level_calls))
list(Tuple("Contract", "HighLevelCall")): List all of the external high level calls
1091 def get_summary( 1092 self, include_shadowed: bool = True 1093 ) -> Tuple[str, List[str], List[str], List, List]: 1094 """Return the function summary 1095 1096 :param include_shadowed: boolean to indicate if shadowed functions should be included (default True) 1097 Returns: 1098 (str, list, list, list, list): (name, inheritance, variables, fuction summaries, modifier summaries) 1099 """ 1100 func_summaries = [ 1101 f.get_summary() for f in self.functions if (not f.is_shadowed or include_shadowed) 1102 ] 1103 modif_summaries = [ 1104 f.get_summary() for f in self.modifiers if (not f.is_shadowed or include_shadowed) 1105 ] 1106 return ( 1107 self.name, 1108 [str(x) for x in self.inheritance], 1109 [str(x) for x in self.variables], 1110 func_summaries, 1111 modif_summaries, 1112 )
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)
1114 def is_signature_only(self) -> bool: 1115 """Detect if the contract has only abstract functions 1116 1117 Returns: 1118 bool: true if the function are abstract functions 1119 """ 1120 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
1129 def ercs(self) -> List[str]: 1130 """ 1131 Return the ERC implemented 1132 :return: list of string 1133 """ 1134 all_erc = [ 1135 ("ERC20", self.is_erc20), 1136 ("ERC165", self.is_erc165), 1137 ("ERC1820", self.is_erc1820), 1138 ("ERC223", self.is_erc223), 1139 ("ERC721", self.is_erc721), 1140 ("ERC777", self.is_erc777), 1141 ("ERC2612", self.is_erc2612), 1142 ("ERC1363", self.is_erc1363), 1143 ("ERC4626", self.is_erc4626), 1144 ] 1145 1146 return [erc for erc, is_erc in all_erc if is_erc()]
Return the ERC implemented
Returns
list of string
1148 def is_erc20(self) -> bool: 1149 """ 1150 Check if the contract is an erc20 token 1151 1152 Note: it does not check for correct return values 1153 :return: Returns a true if the contract is an erc20 1154 """ 1155 full_names = self.functions_signatures 1156 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
1158 def is_erc165(self) -> bool: 1159 """ 1160 Check if the contract is an erc165 token 1161 1162 Note: it does not check for correct return values 1163 :return: Returns a true if the contract is an erc165 1164 """ 1165 full_names = self.functions_signatures 1166 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
1168 def is_erc1820(self) -> bool: 1169 """ 1170 Check if the contract is an erc1820 1171 1172 Note: it does not check for correct return values 1173 :return: Returns a true if the contract is an erc165 1174 """ 1175 full_names = self.functions_signatures 1176 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
1178 def is_erc223(self) -> bool: 1179 """ 1180 Check if the contract is an erc223 token 1181 1182 Note: it does not check for correct return values 1183 :return: Returns a true if the contract is an erc223 1184 """ 1185 full_names = self.functions_signatures 1186 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
1188 def is_erc721(self) -> bool: 1189 """ 1190 Check if the contract is an erc721 token 1191 1192 Note: it does not check for correct return values 1193 :return: Returns a true if the contract is an erc721 1194 """ 1195 full_names = self.functions_signatures 1196 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
1198 def is_erc777(self) -> bool: 1199 """ 1200 Check if the contract is an erc777 1201 1202 Note: it does not check for correct return values 1203 :return: Returns a true if the contract is an erc165 1204 """ 1205 full_names = self.functions_signatures 1206 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
1208 def is_erc1155(self) -> bool: 1209 """ 1210 Check if the contract is an erc1155 1211 1212 Note: it does not check for correct return values 1213 :return: Returns a true if the contract is an erc1155 1214 """ 1215 full_names = self.functions_signatures 1216 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
1218 def is_erc4626(self) -> bool: 1219 """ 1220 Check if the contract is an erc4626 1221 1222 Note: it does not check for correct return values 1223 :return: Returns a true if the contract is an erc4626 1224 """ 1225 full_names = self.functions_signatures 1226 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
1228 def is_erc2612(self) -> bool: 1229 """ 1230 Check if the contract is an erc2612 1231 1232 Note: it does not check for correct return values 1233 :return: Returns a true if the contract is an erc2612 1234 """ 1235 full_names = self.functions_signatures 1236 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
1238 def is_erc1363(self) -> bool: 1239 """ 1240 Check if the contract is an erc1363 1241 1242 Note: it does not check for correct return values 1243 :return: Returns a true if the contract is an erc1363 1244 """ 1245 full_names = self.functions_signatures 1246 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
1248 def is_erc4524(self) -> bool: 1249 """ 1250 Check if the contract is an erc4524 1251 1252 Note: it does not check for correct return values 1253 :return: Returns a true if the contract is an erc4524 1254 """ 1255 full_names = self.functions_signatures 1256 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
1258 @property 1259 def is_token(self) -> bool: 1260 """ 1261 Check if the contract follows one of the standard ERC token 1262 :return: 1263 """ 1264 return ( 1265 self.is_erc20() 1266 or self.is_erc721() 1267 or self.is_erc165() 1268 or self.is_erc223() 1269 or self.is_erc777() 1270 or self.is_erc1155() 1271 )
Check if the contract follows one of the standard ERC token
Returns
1273 def is_possible_erc20(self) -> bool: 1274 """ 1275 Checks if the provided contract could be attempting to implement ERC20 standards. 1276 1277 :return: Returns a boolean indicating if the provided contract met the token standard. 1278 """ 1279 # We do not check for all the functions, as name(), symbol(), might give too many FPs 1280 full_names = self.functions_signatures 1281 return ( 1282 "transfer(address,uint256)" in full_names 1283 or "transferFrom(address,address,uint256)" in full_names 1284 or "approve(address,uint256)" in full_names 1285 )
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.
1287 def is_possible_erc721(self) -> bool: 1288 """ 1289 Checks if the provided contract could be attempting to implement ERC721 standards. 1290 1291 :return: Returns a boolean indicating if the provided contract met the token standard. 1292 """ 1293 # We do not check for all the functions, as name(), symbol(), might give too many FPs 1294 full_names = self.functions_signatures 1295 return ( 1296 "ownerOf(uint256)" in full_names 1297 or "safeTransferFrom(address,address,uint256,bytes)" in full_names 1298 or "safeTransferFrom(address,address,uint256)" in full_names 1299 or "setApprovalForAll(address,bool)" in full_names 1300 or "getApproved(uint256)" in full_names 1301 or "isApprovedForAll(address,address)" in full_names 1302 )
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.
1304 @property 1305 def is_possible_token(self) -> bool: 1306 """ 1307 Check if the contract is a potential token (it might not implement all the functions) 1308 :return: 1309 """ 1310 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
1331 @property 1332 def is_truffle_migration(self) -> bool: 1333 """ 1334 Return true if the contract is the Migrations contract needed for Truffle 1335 :return: 1336 """ 1337 if self.compilation_unit.core.crytic_compile.platform == PlatformType.TRUFFLE: 1338 if self.name == "Migrations": 1339 paths = Path(self.source_mapping.filename.absolute).parts 1340 if len(paths) >= 2: 1341 return paths[-2] == "contracts" and paths[-1] == "migrations.sol" 1342 return False
Return true if the contract is the Migrations contract needed for Truffle
Returns
1366 @property 1367 def is_upgradeable(self) -> bool: 1368 if self._is_upgradeable is None: 1369 self._is_upgradeable = False 1370 initializable = self.file_scope.get_contract_from_name("Initializable") 1371 if initializable: 1372 if initializable in self.inheritance: 1373 self._is_upgradeable = True 1374 else: 1375 for contract in self.inheritance + [self]: 1376 # This might lead to false positive 1377 # Not sure why pylint is having a trouble here 1378 # pylint: disable=no-member 1379 lower_name = contract.name.lower() 1380 if "upgradeable" in lower_name or "upgradable" in lower_name: 1381 self._is_upgradeable = True 1382 break 1383 if "initializable" in lower_name: 1384 self._is_upgradeable = True 1385 break 1386 return self._is_upgradeable
1392 @property 1393 def is_upgradeable_proxy(self) -> bool: 1394 from slither.core.cfg.node import NodeType 1395 from slither.slithir.operations import LowLevelCall 1396 1397 if self._is_upgradeable_proxy is None: 1398 self._is_upgradeable_proxy = False 1399 if "Proxy" in self.name: 1400 self._is_upgradeable_proxy = True 1401 return True 1402 for f in self.functions: 1403 if f.is_fallback: 1404 for node in f.all_nodes(): 1405 for ir in node.irs: 1406 if isinstance(ir, LowLevelCall) and ir.function_name == "delegatecall": 1407 self._is_upgradeable_proxy = True 1408 return self._is_upgradeable_proxy 1409 if node.type == NodeType.ASSEMBLY: 1410 inline_asm = node.inline_asm 1411 if inline_asm: 1412 if "delegatecall" in inline_asm: 1413 self._is_upgradeable_proxy = True 1414 return self._is_upgradeable_proxy 1415 return self._is_upgradeable_proxy
1436 @property 1437 def is_incorrectly_constructed(self) -> bool: 1438 """ 1439 Return true if there was an internal Slither's issue when analyzing the contract 1440 :return: 1441 """ 1442 return self._is_incorrectly_parsed
Return true if there was an internal Slither's issue when analyzing the contract
Returns
1448 def add_constructor_variables(self) -> None: 1449 from slither.core.declarations.function_contract import FunctionContract 1450 1451 if self.state_variables: 1452 for (idx, variable_candidate) in enumerate(self.state_variables): 1453 if variable_candidate.expression and not variable_candidate.is_constant: 1454 1455 constructor_variable = FunctionContract(self.compilation_unit) 1456 constructor_variable.set_function_type(FunctionType.CONSTRUCTOR_VARIABLES) 1457 constructor_variable.set_contract(self) # type: ignore 1458 constructor_variable.set_contract_declarer(self) # type: ignore 1459 constructor_variable.set_visibility("internal") 1460 # For now, source mapping of the constructor variable is the whole contract 1461 # Could be improved with a targeted source mapping 1462 constructor_variable.set_offset(self.source_mapping, self.compilation_unit) 1463 self._functions[constructor_variable.canonical_name] = constructor_variable 1464 1465 prev_node = self._create_node( 1466 constructor_variable, 0, variable_candidate, constructor_variable 1467 ) 1468 variable_candidate.node_initialization = prev_node 1469 counter = 1 1470 for v in self.state_variables[idx + 1 :]: 1471 if v.expression and not v.is_constant: 1472 next_node = self._create_node( 1473 constructor_variable, counter, v, prev_node.scope 1474 ) 1475 v.node_initialization = next_node 1476 prev_node.add_son(next_node) 1477 next_node.add_father(prev_node) 1478 prev_node = next_node 1479 counter += 1 1480 break 1481 1482 for (idx, variable_candidate) in enumerate(self.state_variables): 1483 if variable_candidate.expression and variable_candidate.is_constant: 1484 1485 constructor_variable = FunctionContract(self.compilation_unit) 1486 constructor_variable.set_function_type( 1487 FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES 1488 ) 1489 constructor_variable.set_contract(self) # type: ignore 1490 constructor_variable.set_contract_declarer(self) # type: ignore 1491 constructor_variable.set_visibility("internal") 1492 # For now, source mapping of the constructor variable is the whole contract 1493 # Could be improved with a targeted source mapping 1494 constructor_variable.set_offset(self.source_mapping, self.compilation_unit) 1495 self._functions[constructor_variable.canonical_name] = constructor_variable 1496 1497 prev_node = self._create_node( 1498 constructor_variable, 0, variable_candidate, constructor_variable 1499 ) 1500 variable_candidate.node_initialization = prev_node 1501 counter = 1 1502 for v in self.state_variables[idx + 1 :]: 1503 if v.expression and v.is_constant: 1504 next_node = self._create_node( 1505 constructor_variable, counter, v, prev_node.scope 1506 ) 1507 v.node_initialization = next_node 1508 prev_node.add_son(next_node) 1509 next_node.add_father(prev_node) 1510 prev_node = next_node 1511 counter += 1 1512 1513 break
1549 def convert_expression_to_slithir_ssa(self) -> None: 1550 """ 1551 Assume generate_slithir_and_analyze was called on all functions 1552 1553 :return: 1554 """ 1555 from slither.slithir.variables import StateIRVariable 1556 1557 all_ssa_state_variables_instances = {} 1558 1559 for contract in self.inheritance: 1560 for v in contract.state_variables_declared: 1561 new_var = StateIRVariable(v) 1562 all_ssa_state_variables_instances[v.canonical_name] = new_var 1563 self._initial_state_variables.append(new_var) 1564 1565 for v in self.variables: 1566 if v.contract == self: 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 func in self.functions + list(self.modifiers): 1572 func.generate_slithir_ssa(all_ssa_state_variables_instances)
Assume generate_slithir_and_analyze was called on all functions
Returns
1574 def fix_phi(self) -> None: 1575 last_state_variables_instances: Dict[str, List["StateVariable"]] = {} 1576 initial_state_variables_instances: Dict[str, "StateVariable"] = {} 1577 for v in self._initial_state_variables: 1578 last_state_variables_instances[v.canonical_name] = [] 1579 initial_state_variables_instances[v.canonical_name] = v 1580 1581 for func in self.functions + list(self.modifiers): 1582 result = func.get_last_ssa_state_variables_instances() 1583 for variable_name, instances in result.items(): 1584 # TODO: investigate the next operation 1585 last_state_variables_instances[variable_name] += list(instances) 1586 1587 for func in self.functions + list(self.modifiers): 1588 func.fix_phi(last_state_variables_instances, initial_state_variables_instances)