slither.core.declarations.contract

" Contract module

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

Contract class

 59    def __init__(self, compilation_unit: "SlitherCompilationUnit", scope: "FileScope") -> None:
 60        super().__init__()
 61
 62        self._name: Optional[str] = None
 63        self._id: Optional[int] = None
 64        self._inheritance: List["Contract"] = []  # all contract inherited, c3 linearization
 65        self._immediate_inheritance: List["Contract"] = []  # immediate inheritance
 66
 67        # Constructors called on contract's definition
 68        # contract B is A(1) { ..
 69        self._explicit_base_constructor_calls: List["Contract"] = []
 70
 71        self._enums: Dict[str, "EnumContract"] = {}
 72        self._structures: Dict[str, "StructureContract"] = {}
 73        self._events: Dict[str, "EventContract"] = {}
 74        # map accessible variable from name -> variable
 75        # do not contain private variables inherited from contract
 76        self._variables: Dict[str, "StateVariable"] = {}
 77        self._variables_ordered: List["StateVariable"] = []
 78        # Reference id -> variable declaration (only available for compact AST)
 79        self._state_variables_by_ref_id: Dict[int, "StateVariable"] = {}
 80        self._modifiers: Dict[str, "Modifier"] = {}
 81        self._functions: Dict[str, "FunctionContract"] = {}
 82        self._linearizedBaseContracts: List[int] = []
 83        self._custom_errors: Dict[str, "CustomErrorContract"] = {}
 84        self._type_aliases: Dict[str, "TypeAliasContract"] = {}
 85
 86        # The only str is "*"
 87        self._using_for: USING_FOR = {}
 88        self._using_for_complete: Optional[USING_FOR] = None
 89        self._kind: Optional[str] = None
 90        self._is_interface: bool = False
 91        self._is_library: bool = False
 92        self._is_fully_implemented: bool = False
 93        self._is_abstract: bool = False
 94
 95        self._signatures: Optional[List[str]] = None
 96        self._signatures_declared: Optional[List[str]] = None
 97
 98        self._fallback_function: Optional["FunctionContract"] = None
 99        self._receive_function: Optional["FunctionContract"] = None
100
101        self._is_upgradeable: Optional[bool] = None
102        self._is_upgradeable_proxy: Optional[bool] = None
103        self._upgradeable_version: Optional[str] = None
104
105        self._initial_state_variables: List["StateVariable"] = []  # ssa
106
107        self._is_incorrectly_parsed: bool = False
108
109        self._available_functions_as_dict: Optional[Dict[str, "Function"]] = None
110        self._all_functions_called: Optional[List["Function"]] = None
111
112        self.compilation_unit: "SlitherCompilationUnit" = compilation_unit
113        self.file_scope: "FileScope" = scope
114
115        # memoize
116        self._state_variables_used_in_reentrant_targets: Optional[
117            Dict["StateVariable", Set[Union["StateVariable", "Function"]]]
118        ] = None
119
120        self._comments: Optional[str] = None
name: str
128    @property
129    def name(self) -> str:
130        """str: Name of the contract."""
131        assert self._name
132        return self._name

str: Name of the contract.

id: int
138    @property
139    def id(self) -> int:
140        """Unique id."""
141        assert self._id is not None
142        return self._id

Unique id.

contract_kind: Union[str, NoneType]
149    @property
150    def contract_kind(self) -> Optional[str]:
151        """
152        contract_kind can be None if the legacy ast format is used
153        :return:
154        """
155        return self._kind

contract_kind can be None if the legacy ast format is used

Returns
is_interface: bool
161    @property
162    def is_interface(self) -> bool:
163        return self._is_interface
is_library: bool
169    @property
170    def is_library(self) -> bool:
171        return self._is_library
comments: Union[str, NoneType]
177    @property
178    def comments(self) -> Optional[str]:
179        """
180        Return the comments associated with the contract.
181
182        When using comments, avoid strict text matching, as the solc behavior might change.
183        For example, for old solc version, the first space after the * is not kept, i.e:
184
185          * @title Test Contract
186          * @dev Test comment
187
188        Returns
189        - " @title Test Contract\n @dev Test comment" for newest versions
190        - "@title Test Contract\n@dev Test comment" for older versions
191
192
193        Returns:
194            the comment as a string
195        """
196        return self._comments

Return the comments associated with the contract.

    When using comments, avoid strict text matching, as the solc behavior might change.
    For example, for old solc version, the first space after the * is not kept, i.e:

      * @title Test Contract
      * @dev Test comment

    Returns
    - " @title Test Contract

@dev Test comment" for newest versions - "@title Test Contract @dev Test comment" for older versions

    Returns:
        the comment as a string
is_fully_implemented: bool
202    @property
203    def is_fully_implemented(self) -> bool:
204        """
205        bool: True if the contract defines all functions.
206        In modern Solidity, virtual functions can lack an implementation.
207        Prior to Solidity 0.6.0, functions like the following would be not fully implemented:
208        ```solidity
209        contract ImplicitAbstract{
210            function f() public;
211        }
212        ```
213        """
214        return self._is_fully_implemented

bool: True if the contract defines all functions. In modern Solidity, virtual functions can lack an implementation. Prior to Solidity 0.6.0, functions like the following would be not fully implemented:

contract ImplicitAbstract{
    function f() public;
}
is_abstract: bool
220    @property
221    def is_abstract(self) -> bool:
222        """
223        Note for Solidity < 0.6.0 it will always be false
224        bool: True if the contract is abstract.
225        """
226        return self._is_abstract

Note for Solidity < 0.6.0 it will always be false bool: True if the contract is abstract.

239    @property
240    def structures(self) -> List["StructureContract"]:
241        """
242        list(Structure): List of the structures
243        """
244        return list(self._structures.values())

list(Structure): List of the structures

structures_inherited: list[slither.core.declarations.structure_contract.StructureContract]
246    @property
247    def structures_inherited(self) -> List["StructureContract"]:
248        """
249        list(Structure): List of the inherited structures
250        """
251        return [s for s in self.structures if s.contract != self]

list(Structure): List of the inherited structures

253    @property
254    def structures_declared(self) -> List["StructureContract"]:
255        """
256        list(Structues): List of the structures declared within the contract (not inherited)
257        """
258        return [s for s in self.structures if s.contract == self]

list(Structues): List of the structures declared within the contract (not inherited)

structures_as_dict: dict[str, slither.core.declarations.structure_contract.StructureContract]
260    @property
261    def structures_as_dict(self) -> Dict[str, "StructureContract"]:
262        return self._structures
271    @property
272    def enums(self) -> List["EnumContract"]:
273        return list(self._enums.values())
enums_inherited: list[slither.core.declarations.enum_contract.EnumContract]
275    @property
276    def enums_inherited(self) -> List["EnumContract"]:
277        """
278        list(Enum): List of the inherited enums
279        """
280        return [e for e in self.enums if e.contract != self]

list(Enum): List of the inherited enums

enums_declared: list[slither.core.declarations.enum_contract.EnumContract]
282    @property
283    def enums_declared(self) -> List["EnumContract"]:
284        """
285        list(Enum): List of the enums declared within the contract (not inherited)
286        """
287        return [e for e in self.enums if e.contract == self]

list(Enum): List of the enums declared within the contract (not inherited)

enums_as_dict: dict[str, slither.core.declarations.enum_contract.EnumContract]
289    @property
290    def enums_as_dict(self) -> Dict[str, "EnumContract"]:
291        return self._enums
300    @property
301    def events(self) -> List["EventContract"]:
302        """
303        list(Event): List of the events
304        """
305        return list(self._events.values())

list(Event): List of the events

events_inherited: list[slither.core.declarations.event_contract.EventContract]
307    @property
308    def events_inherited(self) -> List["EventContract"]:
309        """
310        list(Event): List of the inherited events
311        """
312        return [e for e in self.events if e.contract != self]

list(Event): List of the inherited events

events_declared: list[slither.core.declarations.event_contract.EventContract]
314    @property
315    def events_declared(self) -> List["EventContract"]:
316        """
317        list(Event): List of the events declared within the contract (not inherited)
318        """
319        return [e for e in self.events if e.contract == self]

list(Event): List of the events declared within the contract (not inherited)

events_as_dict: dict[str, slither.core.declarations.event_contract.EventContract]
321    @property
322    def events_as_dict(self) -> Dict[str, "EventContract"]:
323        return self._events
332    @property
333    def using_for(self) -> USING_FOR:
334        return self._using_for
336    @property
337    def using_for_complete(self) -> USING_FOR:
338        """
339        USING_FOR: Dict of merged local using for directive with top level directive
340        """
341
342        if self._using_for_complete is None:
343            result = self.using_for
344            top_level_using_for = self.file_scope.using_for_directives
345            for uftl in top_level_using_for:
346                result = merge_using_for(result, uftl.using_for)
347            self._using_for_complete = result
348        return self._using_for_complete

USING_FOR: Dict of merged local using for directive with top level directive

357    @property
358    def custom_errors(self) -> List["CustomErrorContract"]:
359        """
360        list(CustomErrorContract): List of the contract's custom errors
361        """
362        return list(self._custom_errors.values())

list(CustomErrorContract): List of the contract's custom errors

custom_errors_inherited: list[slither.core.declarations.custom_error_contract.CustomErrorContract]
364    @property
365    def custom_errors_inherited(self) -> List["CustomErrorContract"]:
366        """
367        list(CustomErrorContract): List of the inherited custom errors
368        """
369        return [s for s in self.custom_errors if s.contract != self]

list(CustomErrorContract): List of the inherited custom errors

371    @property
372    def custom_errors_declared(self) -> List["CustomErrorContract"]:
373        """
374        list(CustomErrorContract): List of the custom errors declared within the contract (not inherited)
375        """
376        return [s for s in self.custom_errors if s.contract == self]

list(CustomErrorContract): List of the custom errors declared within the contract (not inherited)

custom_errors_as_dict: dict[str, slither.core.declarations.custom_error_contract.CustomErrorContract]
378    @property
379    def custom_errors_as_dict(self) -> Dict[str, "CustomErrorContract"]:
380        return self._custom_errors
389    @property
390    def type_aliases(self) -> List["TypeAliasContract"]:
391        """
392        list(TypeAliasContract): List of the contract's custom errors
393        """
394        return list(self._type_aliases.values())

list(TypeAliasContract): List of the contract's custom errors

type_aliases_inherited: list[slither.core.solidity_types.type_alias.TypeAliasContract]
396    @property
397    def type_aliases_inherited(self) -> List["TypeAliasContract"]:
398        """
399        list(TypeAliasContract): List of the inherited custom errors
400        """
401        return [s for s in self.type_aliases if s.contract != self]

list(TypeAliasContract): List of the inherited custom errors

type_aliases_declared: list[slither.core.solidity_types.type_alias.TypeAliasContract]
403    @property
404    def type_aliases_declared(self) -> List["TypeAliasContract"]:
405        """
406        list(TypeAliasContract): List of the custom errors declared within the contract (not inherited)
407        """
408        return [s for s in self.type_aliases if s.contract == self]

list(TypeAliasContract): List of the custom errors declared within the contract (not inherited)

type_aliases_as_dict: dict[str, slither.core.solidity_types.type_alias.TypeAliasContract]
410    @property
411    def type_aliases_as_dict(self) -> Dict[str, "TypeAliasContract"]:
412        return self._type_aliases
state_variables_by_ref_id: dict[int, slither.core.variables.state_variable.StateVariable]
420    @property
421    def state_variables_by_ref_id(self) -> Dict[int, "StateVariable"]:
422        """
423        Returns the state variables by reference id (only available for compact AST).
424        """
425        return self._state_variables_by_ref_id

Returns the state variables by reference id (only available for compact AST).

427    @property
428    def variables(self) -> List["StateVariable"]:
429        """
430        Returns all the accessible variables (do not include private variable from inherited contract)
431
432        list(StateVariable): List of the state variables. Alias to self.state_variables.
433        """
434        return list(self.state_variables)

Returns all the accessible variables (do not include private variable from inherited contract)

list(StateVariable): List of the state variables. Alias to self.state_variables.

variables_as_dict: dict[str, slither.core.variables.state_variable.StateVariable]
436    @property
437    def variables_as_dict(self) -> Dict[str, "StateVariable"]:
438        return self._variables
state_variables: list[slither.core.variables.state_variable.StateVariable]
440    @property
441    def state_variables(self) -> List["StateVariable"]:
442        """
443        Returns all the accessible variables (do not include private variable from inherited contract).
444        Use stored_state_variables_ordered for all the storage variables following the storage order
445        Use transient_state_variables_ordered for all the transient variables following the storage order
446
447        list(StateVariable): List of the state variables.
448        """
449        return list(self._variables.values())

Returns all the accessible variables (do not include private variable from inherited contract). Use stored_state_variables_ordered for all the storage variables following the storage order Use transient_state_variables_ordered for all the transient variables following the storage order

list(StateVariable): List of the state variables.

state_variables_entry_points: list[slither.core.variables.state_variable.StateVariable]
451    @property
452    def state_variables_entry_points(self) -> List["StateVariable"]:
453        """
454        list(StateVariable): List of the state variables that are public.
455        """
456        return [var for var in self._variables.values() if var.visibility == "public"]

list(StateVariable): List of the state variables that are public.

state_variables_ordered: list[slither.core.variables.state_variable.StateVariable]
458    @property
459    def state_variables_ordered(self) -> List["StateVariable"]:
460        """
461        list(StateVariable): List of the state variables by order of declaration.
462        """
463        return self._variables_ordered

list(StateVariable): List of the state variables by order of declaration.

def add_state_variables_ordered( self, new_vars: list[slither.core.variables.state_variable.StateVariable]) -> None:
465    def add_state_variables_ordered(self, new_vars: List["StateVariable"]) -> None:
466        self._variables_ordered += new_vars
storage_variables_ordered: list[slither.core.variables.state_variable.StateVariable]
468    @property
469    def storage_variables_ordered(self) -> List["StateVariable"]:
470        """
471        list(StateVariable): List of the state variables in storage location by order of declaration.
472        """
473        return [v for v in self._variables_ordered if v.is_stored]

list(StateVariable): List of the state variables in storage location by order of declaration.

transient_variables_ordered: list[slither.core.variables.state_variable.StateVariable]
475    @property
476    def transient_variables_ordered(self) -> List["StateVariable"]:
477        """
478        list(StateVariable): List of the state variables in transient location by order of declaration.
479        """
480        return [v for v in self._variables_ordered if v.is_transient]

list(StateVariable): List of the state variables in transient location by order of declaration.

state_variables_inherited: list[slither.core.variables.state_variable.StateVariable]
482    @property
483    def state_variables_inherited(self) -> List["StateVariable"]:
484        """
485        list(StateVariable): List of the inherited state variables
486        """
487        return [s for s in self.state_variables if s.contract != self]

list(StateVariable): List of the inherited state variables

state_variables_declared: list[slither.core.variables.state_variable.StateVariable]
489    @property
490    def state_variables_declared(self) -> List["StateVariable"]:
491        """
492        list(StateVariable): List of the state variables declared within the contract (not inherited)
493        """
494        return [s for s in self.state_variables if s.contract == self]

list(StateVariable): List of the state variables declared within the contract (not inherited)

slithir_variables: list[slither.slithir.variables.variable.SlithIRVariable]
496    @property
497    def slithir_variables(self) -> List["SlithIRVariable"]:
498        """
499        List all of the slithir variables (non SSA)
500        """
501        slithir_variabless = [f.slithir_variables for f in self.functions + self.modifiers]  # type: ignore
502        slithir_variables = [item for sublist in slithir_variabless for item in sublist]
503        return list(set(slithir_variables))

List all of the slithir variables (non SSA)

505    @property
506    def state_variables_used_in_reentrant_targets(
507        self,
508    ) -> Dict["StateVariable", Set[Union["StateVariable", "Function"]]]:
509        """
510        Returns the state variables used in reentrant targets. Heuristics:
511        - Variable used (read/write) in entry points that are reentrant
512        - State variables that are public
513
514        """
515        from slither.core.variables.state_variable import StateVariable
516
517        if self._state_variables_used_in_reentrant_targets is None:
518            reentrant_functions = [f for f in self.functions_entry_points if f.is_reentrant]
519            variables_used: Dict[
520                StateVariable, Set[Union[StateVariable, "Function"]]
521            ] = defaultdict(set)
522            for function in reentrant_functions:
523                for ir in function.all_slithir_operations():
524                    state_variables = [v for v in ir.used if isinstance(v, StateVariable)]
525                    for state_variable in state_variables:
526                        variables_used[state_variable].add(ir.node.function)
527            for variable in [v for v in self.state_variables if v.visibility == "public"]:
528                variables_used[variable].add(variable)
529            self._state_variables_used_in_reentrant_targets = variables_used
530        return self._state_variables_used_in_reentrant_targets

Returns the state variables used in reentrant targets. Heuristics:

  • Variable used (read/write) in entry points that are reentrant
  • State variables that are public
constructor: Union[slither.core.declarations.function.Function, NoneType]
539    @property
540    def constructor(self) -> Optional["Function"]:
541        """
542        Return the contract's immediate constructor.
543        If there is no immediate constructor, returns the first constructor
544        executed, following the c3 linearization
545        Return None if there is no constructor.
546        """
547        cst = self.constructors_declared
548        if cst:
549            return cst
550        for inherited_contract in self.inheritance:
551            cst = inherited_contract.constructors_declared
552            if cst:
553                return cst
554        return None

Return the contract's immediate constructor. If there is no immediate constructor, returns the first constructor executed, following the c3 linearization Return None if there is no constructor.

constructors_declared: Union[slither.core.declarations.function.Function, NoneType]
556    @property
557    def constructors_declared(self) -> Optional["Function"]:
558        return next(
559            (
560                func
561                for func in self.functions
562                if func.is_constructor and func.contract_declarer == self
563            ),
564            None,
565        )
567    @property
568    def constructors(self) -> List["FunctionContract"]:
569        """
570        Return the list of constructors (including inherited)
571        """
572        return [func for func in self.functions if func.is_constructor]

Return the list of constructors (including inherited)

explicit_base_constructor_calls: list[slither.core.declarations.function.Function]
574    @property
575    def explicit_base_constructor_calls(self) -> List["Function"]:
576        """
577        list(Function): List of the base constructors called explicitly by this contract definition.
578
579                        Base constructors called by any constructor definition will not be included.
580                        Base constructors implicitly called by the contract definition (without
581                        parenthesis) will not be included.
582
583                        On "contract B is A(){..}" it returns the constructor of A
584        """
585        return [c.constructor for c in self._explicit_base_constructor_calls if c.constructor]

list(Function): List of the base constructors called explicitly by this contract definition.

Base constructors called by any constructor definition will not be included. Base constructors implicitly called by the contract definition (without parenthesis) will not be included.

On "contract B is A(){..}" it returns the constructor of A

functions_signatures: List[str]
594    @property
595    def functions_signatures(self) -> List[str]:
596        """
597        Return the signatures of all the public/eterxnal functions/state variables
598        :return: list(string) the signatures of all the functions that can be called
599        """
600        if self._signatures is None:
601            sigs = [
602                v.full_name for v in self.state_variables if v.visibility in ["public", "external"]
603            ]
604
605            sigs += {f.full_name for f in self.functions if f.visibility in ["public", "external"]}
606            self._signatures = list(set(sigs))
607        return self._signatures

Return the signatures of all the public/eterxnal functions/state variables

Returns

list(string) the signatures of all the functions that can be called

functions_signatures_declared: List[str]
609    @property
610    def functions_signatures_declared(self) -> List[str]:
611        """
612        Return the signatures of the public/eterxnal functions/state variables that are declared by this contract
613        :return: list(string) the signatures of all the functions that can be called and are declared by this contract
614        """
615        if self._signatures_declared is None:
616            sigs = [
617                v.full_name
618                for v in self.state_variables_declared
619                if v.visibility in ["public", "external"]
620            ]
621
622            sigs += {
623                f.full_name
624                for f in self.functions_declared
625                if f.visibility in ["public", "external"]
626            }
627            self._signatures_declared = list(set(sigs))
628        return self._signatures_declared

Return the signatures of the public/eterxnal functions/state variables that are declared by this contract

Returns

list(string) the signatures of all the functions that can be called and are declared by this contract

630    @property
631    def functions(self) -> List["FunctionContract"]:
632        """
633        list(Function): List of the functions
634        """
635        return list(self._functions.values())

list(Function): List of the functions

def available_functions_as_dict(self) -> dict[str, slither.core.declarations.function.Function]:
637    def available_functions_as_dict(self) -> Dict[str, "Function"]:
638        if self._available_functions_as_dict is None:
639            self._available_functions_as_dict = {
640                f.full_name: f for f in self._functions.values() if not f.is_shadowed
641            }
642        return self._available_functions_as_dict
def add_function( self, func: slither.core.declarations.function_contract.FunctionContract) -> None:
644    def add_function(self, func: "FunctionContract") -> None:
645        self._functions[func.canonical_name] = func
def set_functions( self, functions: dict[str, slither.core.declarations.function_contract.FunctionContract]) -> None:
647    def set_functions(self, functions: Dict[str, "FunctionContract"]) -> None:
648        """
649        Set the functions
650
651        :param functions:  dict full_name -> function
652        :return:
653        """
654        self._functions = functions

Set the functions

Parameters
  • functions: dict full_name -> function
Returns
functions_inherited: list[slither.core.declarations.function_contract.FunctionContract]
656    @property
657    def functions_inherited(self) -> List["FunctionContract"]:
658        """
659        list(Function): List of the inherited functions
660        """
661        return [f for f in self.functions if f.contract_declarer != self]

list(Function): List of the inherited functions

functions_declared: list[slither.core.declarations.function_contract.FunctionContract]
663    @property
664    def functions_declared(self) -> List["FunctionContract"]:
665        """
666        list(Function): List of the functions defined within the contract (not inherited)
667        """
668        return [f for f in self.functions if f.contract_declarer == self]

list(Function): List of the functions defined within the contract (not inherited)

functions_entry_points: list[slither.core.declarations.function_contract.FunctionContract]
670    @property
671    def functions_entry_points(self) -> List["FunctionContract"]:
672        """
673        list(Functions): List of public and external functions
674        """
675        return [
676            f
677            for f in self.functions
678            if f.visibility in ["public", "external"] and not f.is_shadowed or f.is_fallback
679        ]

list(Functions): List of public and external functions

modifiers: list[slither.core.declarations.modifier.Modifier]
681    @property
682    def modifiers(self) -> List["Modifier"]:
683        """
684        list(Modifier): List of the modifiers
685        """
686        return list(self._modifiers.values())

list(Modifier): List of the modifiers

def available_modifiers_as_dict(self) -> dict[str, slither.core.declarations.modifier.Modifier]:
688    def available_modifiers_as_dict(self) -> Dict[str, "Modifier"]:
689        return {m.full_name: m for m in self._modifiers.values() if not m.is_shadowed}
def set_modifiers( self, modifiers: dict[str, slither.core.declarations.modifier.Modifier]) -> None:
691    def set_modifiers(self, modifiers: Dict[str, "Modifier"]) -> None:
692        """
693        Set the modifiers
694
695        :param modifiers:  dict full_name -> modifier
696        :return:
697        """
698        self._modifiers = modifiers

Set the modifiers

Parameters
  • modifiers: dict full_name -> modifier
Returns
modifiers_inherited: list[slither.core.declarations.modifier.Modifier]
700    @property
701    def modifiers_inherited(self) -> List["Modifier"]:
702        """
703        list(Modifier): List of the inherited modifiers
704        """
705        return [m for m in self.modifiers if m.contract_declarer != self]

list(Modifier): List of the inherited modifiers

modifiers_declared: list[slither.core.declarations.modifier.Modifier]
707    @property
708    def modifiers_declared(self) -> List["Modifier"]:
709        """
710        list(Modifier): List of the modifiers defined within the contract (not inherited)
711        """
712        return [m for m in self.modifiers if m.contract_declarer == self]

list(Modifier): List of the modifiers defined within the contract (not inherited)

functions_and_modifiers: list[slither.core.declarations.function.Function]
714    @property
715    def functions_and_modifiers(self) -> List["Function"]:
716        """
717        list(Function|Modifier): List of the functions and modifiers
718        """
719        return self.functions + self.modifiers  # type: ignore

list(Function|Modifier): List of the functions and modifiers

functions_and_modifiers_inherited: list[slither.core.declarations.function.Function]
721    @property
722    def functions_and_modifiers_inherited(self) -> List["Function"]:
723        """
724        list(Function|Modifier): List of the inherited functions and modifiers
725        """
726        return self.functions_inherited + self.modifiers_inherited  # type: ignore

list(Function|Modifier): List of the inherited functions and modifiers

functions_and_modifiers_declared: list[slither.core.declarations.function.Function]
728    @property
729    def functions_and_modifiers_declared(self) -> List["Function"]:
730        """
731        list(Function|Modifier): List of the functions and modifiers defined within the contract (not inherited)
732        """
733        return self.functions_declared + self.modifiers_declared  # type: ignore

list(Function|Modifier): List of the functions and modifiers defined within the contract (not inherited)

fallback_function: Union[slither.core.declarations.function_contract.FunctionContract, NoneType]
735    @property
736    def fallback_function(self) -> Optional["FunctionContract"]:
737        if self._fallback_function is None:
738            for f in self.functions:
739                if f.is_fallback:
740                    self._fallback_function = f
741                    break
742        return self._fallback_function
receive_function: Union[slither.core.declarations.function_contract.FunctionContract, NoneType]
744    @property
745    def receive_function(self) -> Optional["FunctionContract"]:
746        if self._receive_function is None:
747            for f in self.functions:
748                if f.is_receive:
749                    self._receive_function = f
750                    break
751        return self._receive_function
def available_elements_from_inheritances( self, elements: dict[str, slither.core.declarations.function.Function], getter_available: Callable[Contract, list[slither.core.declarations.function_contract.FunctionContract]]) -> dict[str, slither.core.declarations.function.Function]:
753    def available_elements_from_inheritances(
754        self,
755        elements: Dict[str, "Function"],
756        getter_available: Callable[["Contract"], List["FunctionContract"]],
757    ) -> Dict[str, "Function"]:
758        """
759
760        :param elements: dict(canonical_name -> elements)
761        :param getter_available: fun x
762        :return:
763        """
764        # keep track of the contracts visited
765        # to prevent an ovveride due to multiple inheritance of the same contract
766        # A is B, C, D is C, --> the second C was already seen
767        inherited_elements: Dict[str, "FunctionContract"] = {}
768        accessible_elements = {}
769        contracts_visited = []
770        for father in self.inheritance_reverse:
771            functions: Dict[str, "FunctionContract"] = {
772                v.full_name: v
773                for v in getter_available(father)
774                if v.contract not in contracts_visited
775                and v.function_language
776                != FunctionLanguage.Yul  # Yul functions are not propagated in the inheritance
777            }
778            contracts_visited.append(father)
779            inherited_elements.update(functions)
780
781        for element in inherited_elements.values():
782            accessible_elements[element.full_name] = elements[element.canonical_name]
783
784        return accessible_elements
Parameters
  • elements: dict(canonical_name -> elements)
  • getter_available: fun x
Returns
inheritance: list[Contract]
793    @property
794    def inheritance(self) -> List["Contract"]:
795        """
796        list(Contract): Inheritance list. Order: the first elem is the first father to be executed
797        """
798        return list(self._inheritance)

list(Contract): Inheritance list. Order: the first elem is the first father to be executed

immediate_inheritance: list[Contract]
800    @property
801    def immediate_inheritance(self) -> List["Contract"]:
802        """
803        list(Contract): List of contracts immediately inherited from (fathers). Order: order of declaration.
804        """
805        return list(self._immediate_inheritance)

list(Contract): List of contracts immediately inherited from (fathers). Order: order of declaration.

inheritance_reverse: list[Contract]
807    @property
808    def inheritance_reverse(self) -> List["Contract"]:
809        """
810        list(Contract): Inheritance list. Order: the last elem is the first father to be executed
811        """
812        return list(reversed(self._inheritance))

list(Contract): Inheritance list. Order: the last elem is the first father to be executed

def set_inheritance( self, inheritance: list[Contract], immediate_inheritance: list[Contract], called_base_constructor_contracts: list[Contract]) -> None:
814    def set_inheritance(
815        self,
816        inheritance: List["Contract"],
817        immediate_inheritance: List["Contract"],
818        called_base_constructor_contracts: List["Contract"],
819    ) -> None:
820        self._inheritance = inheritance
821        self._immediate_inheritance = immediate_inheritance
822        self._explicit_base_constructor_calls = called_base_constructor_contracts
derived_contracts: list[Contract]
824    @property
825    def derived_contracts(self) -> List["Contract"]:
826        """
827        list(Contract): Return the list of contracts derived from self
828        """
829        candidates = self.compilation_unit.contracts
830        return [c for c in candidates if self in c.inheritance]  # type: ignore

list(Contract): Return the list of contracts derived from self

def get_functions_reading_from_variable( self, variable: slither.core.variables.variable.Variable) -> list[slither.core.declarations.function.Function]:
839    def get_functions_reading_from_variable(self, variable: "Variable") -> List["Function"]:
840        """
841        Return the functions reading the variable
842        """
843        return [f for f in self.functions if f.is_reading(variable)]

Return the functions reading the variable

def get_functions_writing_to_variable( self, variable: slither.core.variables.variable.Variable) -> list[slither.core.declarations.function.Function]:
845    def get_functions_writing_to_variable(self, variable: "Variable") -> List["Function"]:
846        """
847        Return the functions writting the variable
848        """
849        return [f for f in self.functions if f.is_writing(variable)]

Return the functions writting the variable

def get_function_from_full_name( self, full_name: str) -> Union[slither.core.declarations.function.Function, NoneType]:
851    def get_function_from_full_name(self, full_name: str) -> Optional["Function"]:
852        """
853            Return a function from a full name
854            The full name differs from the solidity's signature are the type are conserved
855            For example contract type are kept, structure are not unrolled, etc
856        Args:
857            full_name (str): signature of the function (without return statement)
858        Returns:
859            Function
860        """
861        return next(
862            (f for f in self.functions if f.full_name == full_name and not f.is_shadowed),
863            None,
864        )

Return a function from a full name The full name differs from the solidity's signature are the type are conserved For example contract type are kept, structure are not unrolled, etc Args: full_name (str): signature of the function (without return statement) Returns: Function

def get_function_from_signature( self, function_signature: str) -> Union[slither.core.declarations.function.Function, NoneType]:
866    def get_function_from_signature(self, function_signature: str) -> Optional["Function"]:
867        """
868            Return a function from a signature
869        Args:
870            function_signature (str): signature of the function (without return statement)
871        Returns:
872            Function
873        """
874        return next(
875            (
876                f
877                for f in self.functions
878                if f.solidity_signature == function_signature and not f.is_shadowed
879            ),
880            None,
881        )

Return a function from a signature Args: function_signature (str): signature of the function (without return statement) Returns: Function

def get_modifier_from_signature( self, modifier_signature: str) -> Union[slither.core.declarations.modifier.Modifier, NoneType]:
883    def get_modifier_from_signature(self, modifier_signature: str) -> Optional["Modifier"]:
884        """
885        Return a modifier from a signature
886
887        :param modifier_signature:
888        """
889        return next(
890            (m for m in self.modifiers if m.full_name == modifier_signature and not m.is_shadowed),
891            None,
892        )

Return a modifier from a signature

Parameters
  • modifier_signature:
def get_function_from_canonical_name( self, canonical_name: str) -> Union[slither.core.declarations.function.Function, NoneType]:
894    def get_function_from_canonical_name(self, canonical_name: str) -> Optional["Function"]:
895        """
896            Return a function from a canonical name (contract.signature())
897        Args:
898            canonical_name (str): canonical name of the function (without return statement)
899        Returns:
900            Function
901        """
902        return next((f for f in self.functions if f.canonical_name == canonical_name), None)

Return a function from a canonical name (contract.signature()) Args: canonical_name (str): canonical name of the function (without return statement) Returns: Function

def get_modifier_from_canonical_name( self, canonical_name: str) -> Union[slither.core.declarations.modifier.Modifier, NoneType]:
904    def get_modifier_from_canonical_name(self, canonical_name: str) -> Optional["Modifier"]:
905        """
906            Return a modifier from a canonical name (contract.signature())
907        Args:
908            canonical_name (str): canonical name of the modifier
909        Returns:
910            Modifier
911        """
912        return next((m for m in self.modifiers if m.canonical_name == canonical_name), None)

Return a modifier from a canonical name (contract.signature()) Args: canonical_name (str): canonical name of the modifier Returns: Modifier

def get_state_variable_from_name( self, variable_name: str) -> Union[slither.core.variables.state_variable.StateVariable, NoneType]:
914    def get_state_variable_from_name(self, variable_name: str) -> Optional["StateVariable"]:
915        """
916        Return a state variable from a name
917
918        :param variable_name:
919        """
920        return next((v for v in self.state_variables if v.name == variable_name), None)

Return a state variable from a name

Parameters
  • variable_name:
def get_state_variable_from_canonical_name( self, canonical_name: str) -> Union[slither.core.variables.state_variable.StateVariable, NoneType]:
922    def get_state_variable_from_canonical_name(
923        self, canonical_name: str
924    ) -> Optional["StateVariable"]:
925        """
926            Return a state variable from a canonical_name
927        Args:
928            canonical_name (str): name of the variable
929        Returns:
930            StateVariable
931        """
932        return next((v for v in self.state_variables if v.canonical_name == canonical_name), None)

Return a state variable from a canonical_name Args: canonical_name (str): name of the variable Returns: StateVariable

def get_structure_from_name( self, structure_name: str) -> Union[slither.core.declarations.structure_contract.StructureContract, NoneType]:
934    def get_structure_from_name(self, structure_name: str) -> Optional["StructureContract"]:
935        """
936            Return a structure from a name
937        Args:
938            structure_name (str): name of the structure
939        Returns:
940            StructureContract
941        """
942        return next((st for st in self.structures if st.name == structure_name), None)

Return a structure from a name Args: structure_name (str): name of the structure Returns: StructureContract

def get_structure_from_canonical_name( self, structure_name: str) -> Union[slither.core.declarations.structure_contract.StructureContract, NoneType]:
944    def get_structure_from_canonical_name(
945        self, structure_name: str
946    ) -> Optional["StructureContract"]:
947        """
948            Return a structure from a canonical name
949        Args:
950            structure_name (str): canonical name of the structure
951        Returns:
952            StructureContract
953        """
954        return next((st for st in self.structures if st.canonical_name == structure_name), None)

Return a structure from a canonical name Args: structure_name (str): canonical name of the structure Returns: StructureContract

def get_event_from_signature(self, event_signature: str) -> Union[ForwardRef('Event'), NoneType]:
956    def get_event_from_signature(self, event_signature: str) -> Optional["Event"]:
957        """
958            Return an event from a signature
959        Args:
960            event_signature (str): signature of the event
961        Returns:
962            Event
963        """
964        return next((e for e in self.events if e.full_name == event_signature), None)

Return an event from a signature Args: event_signature (str): signature of the event Returns: Event

def get_event_from_canonical_name(self, event_canonical_name: str) -> Union[ForwardRef('Event'), NoneType]:
966    def get_event_from_canonical_name(self, event_canonical_name: str) -> Optional["Event"]:
967        """
968            Return an event from a canonical name
969        Args:
970            event_canonical_name (str): name of the event
971        Returns:
972            Event
973        """
974        return next((e for e in self.events if e.canonical_name == event_canonical_name), None)

Return an event from a canonical name Args: event_canonical_name (str): name of the event Returns: Event

def get_enum_from_name( self, enum_name: str) -> Union[slither.core.declarations.enum.Enum, NoneType]:
976    def get_enum_from_name(self, enum_name: str) -> Optional["Enum"]:
977        """
978            Return an enum from a name
979        Args:
980            enum_name (str): name of the enum
981        Returns:
982            Enum
983        """
984        return next((e for e in self.enums if e.name == enum_name), None)

Return an enum from a name Args: enum_name (str): name of the enum Returns: Enum

def get_enum_from_canonical_name( self, enum_name: str) -> Union[slither.core.declarations.enum.Enum, NoneType]:
986    def get_enum_from_canonical_name(self, enum_name: str) -> Optional["Enum"]:
987        """
988            Return an enum from a canonical name
989        Args:
990            enum_name (str): canonical name of the enum
991        Returns:
992            Enum
993        """
994        return next((e for e in self.enums if e.canonical_name == enum_name), None)

Return an enum from a canonical name Args: enum_name (str): canonical name of the enum Returns: Enum

def get_functions_overridden_by( self, function: slither.core.declarations.function.Function) -> list[slither.core.declarations.function.Function]:
 996    def get_functions_overridden_by(self, function: "Function") -> List["Function"]:
 997        """
 998            Return the list of functions overridden by the function
 999        Args:
1000            (core.Function)
1001        Returns:
1002            list(core.Function)
1003
1004        """
1005        return function.overrides

Return the list of functions overridden by the function Args: (core.Function) Returns: list(core.Function)

all_functions_called: list[slither.core.declarations.function.Function]
1014    @property
1015    def all_functions_called(self) -> List["Function"]:
1016        """
1017        list(Function): List of functions reachable from the contract
1018        Includes super, and private/internal functions not shadowed
1019        """
1020        from slither.slithir.operations import Operation
1021
1022        if self._all_functions_called is None:
1023            all_functions = [f for f in self.functions + self.modifiers if not f.is_shadowed]  # type: ignore
1024            all_callss = [f.all_internal_calls() for f in all_functions] + [list(all_functions)]
1025            all_calls = [
1026                item.function if isinstance(item, Operation) else item
1027                for sublist in all_callss
1028                for item in sublist
1029            ]
1030            all_calls = list(set(all_calls))
1031
1032            all_constructors = [c.constructor for c in self.inheritance if c.constructor]
1033            all_constructors = list(set(all_constructors))
1034
1035            set_all_calls = set(all_calls + list(all_constructors))
1036
1037            self._all_functions_called = [c for c in set_all_calls if isinstance(c, Function)]
1038        return self._all_functions_called

list(Function): List of functions reachable from the contract Includes super, and private/internal functions not shadowed

all_state_variables_written: list[slither.core.variables.state_variable.StateVariable]
1040    @property
1041    def all_state_variables_written(self) -> List["StateVariable"]:
1042        """
1043        list(StateVariable): List all of the state variables written
1044        """
1045        all_state_variables_writtens = [
1046            f.all_state_variables_written() for f in self.functions + self.modifiers  # type: ignore
1047        ]
1048        all_state_variables_written = [
1049            item for sublist in all_state_variables_writtens for item in sublist
1050        ]
1051        return list(set(all_state_variables_written))

list(StateVariable): List all of the state variables written

all_state_variables_read: list[slither.core.variables.state_variable.StateVariable]
1053    @property
1054    def all_state_variables_read(self) -> List["StateVariable"]:
1055        """
1056        list(StateVariable): List all of the state variables read
1057        """
1058        all_state_variables_reads = [
1059            f.all_state_variables_read() for f in self.functions + self.modifiers  # type: ignore
1060        ]
1061        all_state_variables_read = [
1062            item for sublist in all_state_variables_reads for item in sublist
1063        ]
1064        return list(set(all_state_variables_read))

list(StateVariable): List all of the state variables read

all_library_calls: list[slither.slithir.operations.library_call.LibraryCall]
1066    @property
1067    def all_library_calls(self) -> List["LibraryCall"]:
1068        """
1069        list(LibraryCall): List all of the libraries func called
1070        """
1071        all_high_level_callss = [f.all_library_calls() for f in self.functions + self.modifiers]  # type: ignore
1072        all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist]
1073        return list(set(all_high_level_calls))

list(LibraryCall): List all of the libraries func called

all_high_level_calls: list[tuple[Contract, slither.slithir.operations.high_level_call.HighLevelCall]]
1075    @property
1076    def all_high_level_calls(self) -> List[Tuple["Contract", "HighLevelCall"]]:
1077        """
1078        list(Tuple("Contract", "HighLevelCall")): List all of the external high level calls
1079        """
1080        all_high_level_callss = [f.all_high_level_calls() for f in self.functions + self.modifiers]  # type: ignore
1081        all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist]
1082        return list(set(all_high_level_calls))

list(Tuple("Contract", "HighLevelCall")): List all of the external high level calls

def get_summary( self, include_shadowed: bool = True) -> Tuple[str, List[str], List[str], List, List]:
1091    def get_summary(
1092        self, include_shadowed: bool = True
1093    ) -> Tuple[str, List[str], List[str], List, List]:
1094        """Return the function summary
1095
1096        :param include_shadowed: boolean to indicate if shadowed functions should be included (default True)
1097        Returns:
1098            (str, list, list, list, list): (name, inheritance, variables, fuction summaries, modifier summaries)
1099        """
1100        func_summaries = [
1101            f.get_summary() for f in self.functions if (not f.is_shadowed or include_shadowed)
1102        ]
1103        modif_summaries = [
1104            f.get_summary() for f in self.modifiers if (not f.is_shadowed or include_shadowed)
1105        ]
1106        return (
1107            self.name,
1108            [str(x) for x in self.inheritance],
1109            [str(x) for x in self.variables],
1110            func_summaries,
1111            modif_summaries,
1112        )

Return the function summary

Parameters
  • include_shadowed: boolean to indicate if shadowed functions should be included (default True) Returns: (str, list, list, list, list): (name, inheritance, variables, fuction summaries, modifier summaries)
def is_signature_only(self) -> bool:
1114    def is_signature_only(self) -> bool:
1115        """Detect if the contract has only abstract functions
1116
1117        Returns:
1118            bool: true if the function are abstract functions
1119        """
1120        return all((not f.is_implemented) for f in self.functions)

Detect if the contract has only abstract functions

Returns: bool: true if the function are abstract functions

def ercs(self) -> List[str]:
1129    def ercs(self) -> List[str]:
1130        """
1131        Return the ERC implemented
1132        :return: list of string
1133        """
1134        all_erc = [
1135            ("ERC20", self.is_erc20),
1136            ("ERC165", self.is_erc165),
1137            ("ERC1820", self.is_erc1820),
1138            ("ERC223", self.is_erc223),
1139            ("ERC721", self.is_erc721),
1140            ("ERC777", self.is_erc777),
1141            ("ERC2612", self.is_erc2612),
1142            ("ERC1363", self.is_erc1363),
1143            ("ERC4626", self.is_erc4626),
1144        ]
1145
1146        return [erc for erc, is_erc in all_erc if is_erc()]

Return the ERC implemented

Returns

list of string

def is_erc20(self) -> bool:
1148    def is_erc20(self) -> bool:
1149        """
1150            Check if the contract is an erc20 token
1151
1152            Note: it does not check for correct return values
1153        :return: Returns a true if the contract is an erc20
1154        """
1155        full_names = self.functions_signatures
1156        return all(s in full_names for s in ERC20_signatures)

Check if the contract is an erc20 token

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc20

def is_erc165(self) -> bool:
1158    def is_erc165(self) -> bool:
1159        """
1160            Check if the contract is an erc165 token
1161
1162            Note: it does not check for correct return values
1163        :return: Returns a true if the contract is an erc165
1164        """
1165        full_names = self.functions_signatures
1166        return all(s in full_names for s in ERC165_signatures)

Check if the contract is an erc165 token

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc165

def is_erc1820(self) -> bool:
1168    def is_erc1820(self) -> bool:
1169        """
1170            Check if the contract is an erc1820
1171
1172            Note: it does not check for correct return values
1173        :return: Returns a true if the contract is an erc165
1174        """
1175        full_names = self.functions_signatures
1176        return all(s in full_names for s in ERC1820_signatures)

Check if the contract is an erc1820

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc165

def is_erc223(self) -> bool:
1178    def is_erc223(self) -> bool:
1179        """
1180            Check if the contract is an erc223 token
1181
1182            Note: it does not check for correct return values
1183        :return: Returns a true if the contract is an erc223
1184        """
1185        full_names = self.functions_signatures
1186        return all(s in full_names for s in ERC223_signatures)

Check if the contract is an erc223 token

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc223

def is_erc721(self) -> bool:
1188    def is_erc721(self) -> bool:
1189        """
1190            Check if the contract is an erc721 token
1191
1192            Note: it does not check for correct return values
1193        :return: Returns a true if the contract is an erc721
1194        """
1195        full_names = self.functions_signatures
1196        return all(s in full_names for s in ERC721_signatures)

Check if the contract is an erc721 token

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc721

def is_erc777(self) -> bool:
1198    def is_erc777(self) -> bool:
1199        """
1200            Check if the contract is an erc777
1201
1202            Note: it does not check for correct return values
1203        :return: Returns a true if the contract is an erc165
1204        """
1205        full_names = self.functions_signatures
1206        return all(s in full_names for s in ERC777_signatures)

Check if the contract is an erc777

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc165

def is_erc1155(self) -> bool:
1208    def is_erc1155(self) -> bool:
1209        """
1210            Check if the contract is an erc1155
1211
1212            Note: it does not check for correct return values
1213        :return: Returns a true if the contract is an erc1155
1214        """
1215        full_names = self.functions_signatures
1216        return all(s in full_names for s in ERC1155_signatures)

Check if the contract is an erc1155

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc1155

def is_erc4626(self) -> bool:
1218    def is_erc4626(self) -> bool:
1219        """
1220            Check if the contract is an erc4626
1221
1222            Note: it does not check for correct return values
1223        :return: Returns a true if the contract is an erc4626
1224        """
1225        full_names = self.functions_signatures
1226        return all(s in full_names for s in ERC4626_signatures)

Check if the contract is an erc4626

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc4626

def is_erc2612(self) -> bool:
1228    def is_erc2612(self) -> bool:
1229        """
1230            Check if the contract is an erc2612
1231
1232            Note: it does not check for correct return values
1233        :return: Returns a true if the contract is an erc2612
1234        """
1235        full_names = self.functions_signatures
1236        return all(s in full_names for s in ERC2612_signatures)

Check if the contract is an erc2612

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc2612

def is_erc1363(self) -> bool:
1238    def is_erc1363(self) -> bool:
1239        """
1240            Check if the contract is an erc1363
1241
1242            Note: it does not check for correct return values
1243        :return: Returns a true if the contract is an erc1363
1244        """
1245        full_names = self.functions_signatures
1246        return all(s in full_names for s in ERC1363_signatures)

Check if the contract is an erc1363

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc1363

def is_erc4524(self) -> bool:
1248    def is_erc4524(self) -> bool:
1249        """
1250            Check if the contract is an erc4524
1251
1252            Note: it does not check for correct return values
1253        :return: Returns a true if the contract is an erc4524
1254        """
1255        full_names = self.functions_signatures
1256        return all(s in full_names for s in ERC4524_signatures)

Check if the contract is an erc4524

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc4524

is_token: bool
1258    @property
1259    def is_token(self) -> bool:
1260        """
1261        Check if the contract follows one of the standard ERC token
1262        :return:
1263        """
1264        return (
1265            self.is_erc20()
1266            or self.is_erc721()
1267            or self.is_erc165()
1268            or self.is_erc223()
1269            or self.is_erc777()
1270            or self.is_erc1155()
1271        )

Check if the contract follows one of the standard ERC token

Returns
def is_possible_erc20(self) -> bool:
1273    def is_possible_erc20(self) -> bool:
1274        """
1275        Checks if the provided contract could be attempting to implement ERC20 standards.
1276
1277        :return: Returns a boolean indicating if the provided contract met the token standard.
1278        """
1279        # We do not check for all the functions, as name(), symbol(), might give too many FPs
1280        full_names = self.functions_signatures
1281        return (
1282            "transfer(address,uint256)" in full_names
1283            or "transferFrom(address,address,uint256)" in full_names
1284            or "approve(address,uint256)" in full_names
1285        )

Checks if the provided contract could be attempting to implement ERC20 standards.

Returns

Returns a boolean indicating if the provided contract met the token standard.

def is_possible_erc721(self) -> bool:
1287    def is_possible_erc721(self) -> bool:
1288        """
1289        Checks if the provided contract could be attempting to implement ERC721 standards.
1290
1291        :return: Returns a boolean indicating if the provided contract met the token standard.
1292        """
1293        # We do not check for all the functions, as name(), symbol(), might give too many FPs
1294        full_names = self.functions_signatures
1295        return (
1296            "ownerOf(uint256)" in full_names
1297            or "safeTransferFrom(address,address,uint256,bytes)" in full_names
1298            or "safeTransferFrom(address,address,uint256)" in full_names
1299            or "setApprovalForAll(address,bool)" in full_names
1300            or "getApproved(uint256)" in full_names
1301            or "isApprovedForAll(address,address)" in full_names
1302        )

Checks if the provided contract could be attempting to implement ERC721 standards.

Returns

Returns a boolean indicating if the provided contract met the token standard.

is_possible_token: bool
1304    @property
1305    def is_possible_token(self) -> bool:
1306        """
1307        Check if the contract is a potential token (it might not implement all the functions)
1308        :return:
1309        """
1310        return self.is_possible_erc20() or self.is_possible_erc721()

Check if the contract is a potential token (it might not implement all the functions)

Returns
def is_from_dependency(self) -> bool:
1319    def is_from_dependency(self) -> bool:
1320        return self.compilation_unit.core.crytic_compile.is_dependency(
1321            self.source_mapping.filename.absolute
1322        )
is_truffle_migration: bool
1331    @property
1332    def is_truffle_migration(self) -> bool:
1333        """
1334        Return true if the contract is the Migrations contract needed for Truffle
1335        :return:
1336        """
1337        if self.compilation_unit.core.crytic_compile.platform == PlatformType.TRUFFLE:
1338            if self.name == "Migrations":
1339                paths = Path(self.source_mapping.filename.absolute).parts
1340                if len(paths) >= 2:
1341                    return paths[-2] == "contracts" and paths[-1] == "migrations.sol"
1342        return False

Return true if the contract is the Migrations contract needed for Truffle

Returns
is_test: bool
1344    @property
1345    def is_test(self) -> bool:
1346        return is_test_contract(self) or self.is_truffle_migration  # type: ignore
def update_read_write_using_ssa(self) -> None:
1355    def update_read_write_using_ssa(self) -> None:
1356        for function in self.functions + list(self.modifiers):
1357            function.update_read_write_using_ssa()
is_upgradeable: bool
1366    @property
1367    def is_upgradeable(self) -> bool:
1368        if self._is_upgradeable is None:
1369            self._is_upgradeable = False
1370            initializable = self.file_scope.get_contract_from_name("Initializable")
1371            if initializable:
1372                if initializable in self.inheritance:
1373                    self._is_upgradeable = True
1374            else:
1375                for contract in self.inheritance + [self]:
1376                    # This might lead to false positive
1377                    # Not sure why pylint is having a trouble here
1378                    # pylint: disable=no-member
1379                    lower_name = contract.name.lower()
1380                    if "upgradeable" in lower_name or "upgradable" in lower_name:
1381                        self._is_upgradeable = True
1382                        break
1383                    if "initializable" in lower_name:
1384                        self._is_upgradeable = True
1385                        break
1386        return self._is_upgradeable
is_upgradeable_proxy: bool
1392    @property
1393    def is_upgradeable_proxy(self) -> bool:
1394        from slither.core.cfg.node import NodeType
1395        from slither.slithir.operations import LowLevelCall
1396
1397        if self._is_upgradeable_proxy is None:
1398            self._is_upgradeable_proxy = False
1399            if "Proxy" in self.name:
1400                self._is_upgradeable_proxy = True
1401                return True
1402            for f in self.functions:
1403                if f.is_fallback:
1404                    for node in f.all_nodes():
1405                        for ir in node.irs:
1406                            if isinstance(ir, LowLevelCall) and ir.function_name == "delegatecall":
1407                                self._is_upgradeable_proxy = True
1408                                return self._is_upgradeable_proxy
1409                        if node.type == NodeType.ASSEMBLY:
1410                            inline_asm = node.inline_asm
1411                            if inline_asm:
1412                                if "delegatecall" in inline_asm:
1413                                    self._is_upgradeable_proxy = True
1414                                    return self._is_upgradeable_proxy
1415        return self._is_upgradeable_proxy
upgradeable_version: Union[str, NoneType]
1421    @property
1422    def upgradeable_version(self) -> Optional[str]:
1423        return self._upgradeable_version
is_incorrectly_constructed: bool
1436    @property
1437    def is_incorrectly_constructed(self) -> bool:
1438        """
1439        Return true if there was an internal Slither's issue when analyzing the contract
1440        :return:
1441        """
1442        return self._is_incorrectly_parsed

Return true if there was an internal Slither's issue when analyzing the contract

Returns
def add_constructor_variables(self) -> None:
1448    def add_constructor_variables(self) -> None:
1449        from slither.core.declarations.function_contract import FunctionContract
1450
1451        if self.state_variables:
1452            for (idx, variable_candidate) in enumerate(self.state_variables):
1453                if variable_candidate.expression and not variable_candidate.is_constant:
1454
1455                    constructor_variable = FunctionContract(self.compilation_unit)
1456                    constructor_variable.set_function_type(FunctionType.CONSTRUCTOR_VARIABLES)
1457                    constructor_variable.set_contract(self)  # type: ignore
1458                    constructor_variable.set_contract_declarer(self)  # type: ignore
1459                    constructor_variable.set_visibility("internal")
1460                    # For now, source mapping of the constructor variable is the whole contract
1461                    # Could be improved with a targeted source mapping
1462                    constructor_variable.set_offset(self.source_mapping, self.compilation_unit)
1463                    self._functions[constructor_variable.canonical_name] = constructor_variable
1464
1465                    prev_node = self._create_node(
1466                        constructor_variable, 0, variable_candidate, constructor_variable
1467                    )
1468                    variable_candidate.node_initialization = prev_node
1469                    counter = 1
1470                    for v in self.state_variables[idx + 1 :]:
1471                        if v.expression and not v.is_constant:
1472                            next_node = self._create_node(
1473                                constructor_variable, counter, v, prev_node.scope
1474                            )
1475                            v.node_initialization = next_node
1476                            prev_node.add_son(next_node)
1477                            next_node.add_father(prev_node)
1478                            prev_node = next_node
1479                            counter += 1
1480                    break
1481
1482            for (idx, variable_candidate) in enumerate(self.state_variables):
1483                if variable_candidate.expression and variable_candidate.is_constant:
1484
1485                    constructor_variable = FunctionContract(self.compilation_unit)
1486                    constructor_variable.set_function_type(
1487                        FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES
1488                    )
1489                    constructor_variable.set_contract(self)  # type: ignore
1490                    constructor_variable.set_contract_declarer(self)  # type: ignore
1491                    constructor_variable.set_visibility("internal")
1492                    # For now, source mapping of the constructor variable is the whole contract
1493                    # Could be improved with a targeted source mapping
1494                    constructor_variable.set_offset(self.source_mapping, self.compilation_unit)
1495                    self._functions[constructor_variable.canonical_name] = constructor_variable
1496
1497                    prev_node = self._create_node(
1498                        constructor_variable, 0, variable_candidate, constructor_variable
1499                    )
1500                    variable_candidate.node_initialization = prev_node
1501                    counter = 1
1502                    for v in self.state_variables[idx + 1 :]:
1503                        if v.expression and v.is_constant:
1504                            next_node = self._create_node(
1505                                constructor_variable, counter, v, prev_node.scope
1506                            )
1507                            v.node_initialization = next_node
1508                            prev_node.add_son(next_node)
1509                            next_node.add_father(prev_node)
1510                            prev_node = next_node
1511                            counter += 1
1512
1513                    break
def convert_expression_to_slithir_ssa(self) -> None:
1549    def convert_expression_to_slithir_ssa(self) -> None:
1550        """
1551        Assume generate_slithir_and_analyze was called on all functions
1552
1553        :return:
1554        """
1555        from slither.slithir.variables import StateIRVariable
1556
1557        all_ssa_state_variables_instances = {}
1558
1559        for contract in self.inheritance:
1560            for v in contract.state_variables_declared:
1561                new_var = StateIRVariable(v)
1562                all_ssa_state_variables_instances[v.canonical_name] = new_var
1563                self._initial_state_variables.append(new_var)
1564
1565        for v in self.variables:
1566            if v.contract == self:
1567                new_var = StateIRVariable(v)
1568                all_ssa_state_variables_instances[v.canonical_name] = new_var
1569                self._initial_state_variables.append(new_var)
1570
1571        for func in self.functions + list(self.modifiers):
1572            func.generate_slithir_ssa(all_ssa_state_variables_instances)

Assume generate_slithir_and_analyze was called on all functions

Returns
def fix_phi(self) -> None:
1574    def fix_phi(self) -> None:
1575        last_state_variables_instances: Dict[str, List["StateVariable"]] = {}
1576        initial_state_variables_instances: Dict[str, "StateVariable"] = {}
1577        for v in self._initial_state_variables:
1578            last_state_variables_instances[v.canonical_name] = []
1579            initial_state_variables_instances[v.canonical_name] = v
1580
1581        for func in self.functions + list(self.modifiers):
1582            result = func.get_last_ssa_state_variables_instances()
1583            for variable_name, instances in result.items():
1584                # TODO: investigate the next operation
1585                last_state_variables_instances[variable_name] += list(instances)
1586
1587        for func in self.functions + list(self.modifiers):
1588            func.fix_phi(last_state_variables_instances, initial_state_variables_instances)