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