slither.core.declarations.contract

" Contract module

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

Contract class

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

str: Name of the contract.

id: int
142    @property
143    def id(self) -> int:
144        """Unique id."""
145        assert self._id is not None
146        return self._id

Unique id.

contract_kind: str | None
153    @property
154    def contract_kind(self) -> str | None:
155        """
156        contract_kind can be None if the legacy ast format is used
157        :return:
158        """
159        return self._kind

contract_kind can be None if the legacy ast format is used

Returns
is_interface: bool
165    @property
166    def is_interface(self) -> bool:
167        return self._is_interface
is_library: bool
173    @property
174    def is_library(self) -> bool:
175        return self._is_library
comments: str | None
181    @property
182    def comments(self) -> str | None:
183        """
184        Return the comments associated with the contract.
185
186        When using comments, avoid strict text matching, as the solc behavior might change.
187        For example, for old solc version, the first space after the * is not kept, i.e:
188
189          * @title Test Contract
190          * @dev Test comment
191
192        Returns
193        - " @title Test Contract\n @dev Test comment" for newest versions
194        - "@title Test Contract\n@dev Test comment" for older versions
195
196
197        Returns:
198            the comment as a string
199        """
200        return self._comments

Return the comments associated with the contract.

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

      * @title Test Contract
      * @dev Test comment

    Returns
    - " @title Test Contract

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

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

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

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

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

custom_storage_layout: int | None
236    @property
237    def custom_storage_layout(self) -> int | None:
238        """
239        Return the persistent storage slot starting position if a custom storage layout is used.
240        Otherwise None.
241        int: Storage slot starting position.
242        """
243        return self._custom_storage_layout

Return the persistent storage slot starting position if a custom storage layout is used. Otherwise None. int: Storage slot starting position.

256    @property
257    def structures(self) -> list["StructureContract"]:
258        """
259        list(Structure): List of the structures
260        """
261        return list(self._structures.values())

list(Structure): List of the structures

structures_inherited: list[slither.core.declarations.structure_contract.StructureContract]
263    @property
264    def structures_inherited(self) -> list["StructureContract"]:
265        """
266        list(Structure): List of the inherited structures
267        """
268        return [s for s in self.structures if s.contract != self]

list(Structure): List of the inherited structures

270    @property
271    def structures_declared(self) -> list["StructureContract"]:
272        """
273        list(Structues): List of the structures declared within the contract (not inherited)
274        """
275        return [s for s in self.structures if s.contract == self]

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

structures_as_dict: dict[str, slither.core.declarations.structure_contract.StructureContract]
277    @property
278    def structures_as_dict(self) -> dict[str, "StructureContract"]:
279        return self._structures
288    @property
289    def enums(self) -> list["EnumContract"]:
290        return list(self._enums.values())
enums_inherited: list[slither.core.declarations.enum_contract.EnumContract]
292    @property
293    def enums_inherited(self) -> list["EnumContract"]:
294        """
295        list(Enum): List of the inherited enums
296        """
297        return [e for e in self.enums if e.contract != self]

list(Enum): List of the inherited enums

enums_declared: list[slither.core.declarations.enum_contract.EnumContract]
299    @property
300    def enums_declared(self) -> list["EnumContract"]:
301        """
302        list(Enum): List of the enums declared within the contract (not inherited)
303        """
304        return [e for e in self.enums if e.contract == self]

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

enums_as_dict: dict[str, slither.core.declarations.enum_contract.EnumContract]
306    @property
307    def enums_as_dict(self) -> dict[str, "EnumContract"]:
308        return self._enums
317    @property
318    def events(self) -> list["EventContract"]:
319        """
320        list(Event): List of the events
321        """
322        return list(self._events.values())

list(Event): List of the events

events_inherited: list[slither.core.declarations.event_contract.EventContract]
324    @property
325    def events_inherited(self) -> list["EventContract"]:
326        """
327        list(Event): List of the inherited events
328        """
329        return [e for e in self.events if e.contract != self]

list(Event): List of the inherited events

events_declared: list[slither.core.declarations.event_contract.EventContract]
331    @property
332    def events_declared(self) -> list["EventContract"]:
333        """
334        list(Event): List of the events declared within the contract (not inherited)
335        """
336        return [e for e in self.events if e.contract == self]

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

events_as_dict: dict[str, slither.core.declarations.event_contract.EventContract]
338    @property
339    def events_as_dict(self) -> dict[str, "EventContract"]:
340        return self._events
using_for: 'dict[USING_FOR_KEY, USING_FOR_ITEM]'
349    @property
350    def using_for(self) -> USING_FOR:
351        return self._using_for
using_for_complete: 'dict[USING_FOR_KEY, USING_FOR_ITEM]'
353    @property
354    def using_for_complete(self) -> USING_FOR:
355        """
356        USING_FOR: Dict of merged local using for directive with top level directive
357        """
358
359        if self._using_for_complete is None:
360            result = self.using_for
361            top_level_using_for = self.file_scope.using_for_directives
362            for uftl in top_level_using_for:
363                result = merge_using_for(result, uftl.using_for)
364            self._using_for_complete = result
365        return self._using_for_complete

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

374    @property
375    def custom_errors(self) -> list["CustomErrorContract"]:
376        """
377        list(CustomErrorContract): List of the contract's custom errors
378        """
379        return list(self._custom_errors.values())

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

custom_errors_inherited: list[slither.core.declarations.custom_error_contract.CustomErrorContract]
381    @property
382    def custom_errors_inherited(self) -> list["CustomErrorContract"]:
383        """
384        list(CustomErrorContract): List of the inherited custom errors
385        """
386        return [s for s in self.custom_errors if s.contract != self]

list(CustomErrorContract): List of the inherited custom errors

388    @property
389    def custom_errors_declared(self) -> list["CustomErrorContract"]:
390        """
391        list(CustomErrorContract): List of the custom errors declared within the contract (not inherited)
392        """
393        return [s for s in self.custom_errors if s.contract == self]

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

custom_errors_as_dict: dict[str, slither.core.declarations.custom_error_contract.CustomErrorContract]
395    @property
396    def custom_errors_as_dict(self) -> dict[str, "CustomErrorContract"]:
397        return self._custom_errors
406    @property
407    def type_aliases(self) -> list["TypeAliasContract"]:
408        """
409        list(TypeAliasContract): List of the contract's custom errors
410        """
411        return list(self._type_aliases.values())

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

type_aliases_inherited: list[slither.core.solidity_types.type_alias.TypeAliasContract]
413    @property
414    def type_aliases_inherited(self) -> list["TypeAliasContract"]:
415        """
416        list(TypeAliasContract): List of the inherited custom errors
417        """
418        return [s for s in self.type_aliases if s.contract != self]

list(TypeAliasContract): List of the inherited custom errors

type_aliases_declared: list[slither.core.solidity_types.type_alias.TypeAliasContract]
420    @property
421    def type_aliases_declared(self) -> list["TypeAliasContract"]:
422        """
423        list(TypeAliasContract): List of the custom errors declared within the contract (not inherited)
424        """
425        return [s for s in self.type_aliases if s.contract == self]

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

type_aliases_as_dict: dict[str, slither.core.solidity_types.type_alias.TypeAliasContract]
427    @property
428    def type_aliases_as_dict(self) -> dict[str, "TypeAliasContract"]:
429        return self._type_aliases
state_variables_by_ref_id: dict[int, slither.core.variables.state_variable.StateVariable]
437    @property
438    def state_variables_by_ref_id(self) -> dict[int, "StateVariable"]:
439        """
440        Returns the state variables by reference id (only available for compact AST).
441        """
442        return self._state_variables_by_ref_id

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

444    @property
445    def variables(self) -> list["StateVariable"]:
446        """
447        Returns all the accessible variables (do not include private variable from inherited contract)
448
449        list(StateVariable): List of the state variables. Alias to self.state_variables.
450        """
451        return list(self.state_variables)

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

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

variables_as_dict: dict[str, slither.core.variables.state_variable.StateVariable]
453    @property
454    def variables_as_dict(self) -> dict[str, "StateVariable"]:
455        return self._variables
state_variables: list[slither.core.variables.state_variable.StateVariable]
457    @property
458    def state_variables(self) -> list["StateVariable"]:
459        """
460        Returns all the accessible variables (do not include private variable from inherited contract).
461        Use stored_state_variables_ordered for all the storage variables following the storage order
462        Use transient_state_variables_ordered for all the transient variables following the storage order
463
464        list(StateVariable): List of the state variables.
465        """
466        return list(self._variables.values())

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

list(StateVariable): List of the state variables.

state_variables_entry_points: list[slither.core.variables.state_variable.StateVariable]
468    @property
469    def state_variables_entry_points(self) -> list["StateVariable"]:
470        """
471        list(StateVariable): List of the state variables that are public.
472        """
473        return [var for var in self._variables.values() if var.visibility == "public"]

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

state_variables_ordered: list[slither.core.variables.state_variable.StateVariable]
475    @property
476    def state_variables_ordered(self) -> list["StateVariable"]:
477        """
478        list(StateVariable): List of the state variables by order of declaration.
479        """
480        return self._variables_ordered

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

def add_state_variables_ordered( self, new_vars: list[slither.core.variables.state_variable.StateVariable]) -> None:
482    def add_state_variables_ordered(self, new_vars: list["StateVariable"]) -> None:
483        self._variables_ordered += new_vars
storage_variables_ordered: list[slither.core.variables.state_variable.StateVariable]
485    @property
486    def storage_variables_ordered(self) -> list["StateVariable"]:
487        """
488        list(StateVariable): List of the state variables in storage location by order of declaration.
489        """
490        return [v for v in self._variables_ordered if v.is_stored]

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

transient_variables_ordered: list[slither.core.variables.state_variable.StateVariable]
492    @property
493    def transient_variables_ordered(self) -> list["StateVariable"]:
494        """
495        list(StateVariable): List of the state variables in transient location by order of declaration.
496        """
497        return [v for v in self._variables_ordered if v.is_transient]

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

state_variables_inherited: list[slither.core.variables.state_variable.StateVariable]
499    @property
500    def state_variables_inherited(self) -> list["StateVariable"]:
501        """
502        list(StateVariable): List of the inherited state variables
503        """
504        return [s for s in self.state_variables if s.contract != self]

list(StateVariable): List of the inherited state variables

state_variables_declared: list[slither.core.variables.state_variable.StateVariable]
506    @property
507    def state_variables_declared(self) -> list["StateVariable"]:
508        """
509        list(StateVariable): List of the state variables declared within the contract (not inherited)
510        """
511        return [s for s in self.state_variables if s.contract == self]

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

slithir_variables: list[slither.slithir.variables.variable.SlithIRVariable]
513    @property
514    def slithir_variables(self) -> list["SlithIRVariable"]:
515        """
516        List all of the slithir variables (non SSA)
517        """
518        slithir_variabless = [f.slithir_variables for f in self.functions + self.modifiers]  # type: ignore
519        slithir_variables = [item for sublist in slithir_variabless for item in sublist]
520        return list(set(slithir_variables))

List all of the slithir variables (non SSA)

522    @property
523    def state_variables_used_in_reentrant_targets(
524        self,
525    ) -> dict["StateVariable", set[Union["StateVariable", "Function"]]]:
526        """
527        Returns the state variables used in reentrant targets. Heuristics:
528        - Variable used (read/write) in entry points that are reentrant
529        - State variables that are public
530
531        """
532        from slither.core.variables.state_variable import StateVariable
533
534        if self._state_variables_used_in_reentrant_targets is None:
535            reentrant_functions = [f for f in self.functions_entry_points if f.is_reentrant]
536            variables_used: dict[StateVariable, set[StateVariable | Function]] = defaultdict(set)
537            for function in reentrant_functions:
538                for ir in function.all_slithir_operations():
539                    state_variables = [v for v in ir.used if isinstance(v, StateVariable)]
540                    for state_variable in state_variables:
541                        variables_used[state_variable].add(ir.node.function)
542            for variable in [v for v in self.state_variables if v.visibility == "public"]:
543                variables_used[variable].add(variable)
544            self._state_variables_used_in_reentrant_targets = variables_used
545        return self._state_variables_used_in_reentrant_targets

Returns the state variables used in reentrant targets. Heuristics:

  • Variable used (read/write) in entry points that are reentrant
  • State variables that are public
constructor: Optional[slither.core.declarations.function.Function]
554    @property
555    def constructor(self) -> Optional["Function"]:
556        """
557        Return the contract's immediate constructor.
558        If there is no immediate constructor, returns the first constructor
559        executed, following the c3 linearization
560        Return None if there is no constructor.
561        """
562        cst = self.constructors_declared
563        if cst:
564            return cst
565        for inherited_contract in self.inheritance:
566            cst = inherited_contract.constructors_declared
567            if cst:
568                return cst
569        return None

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

constructors_declared: Optional[slither.core.declarations.function.Function]
571    @property
572    def constructors_declared(self) -> Optional["Function"]:
573        return next(
574            (
575                func
576                for func in self.functions
577                if func.is_constructor and func.contract_declarer == self
578            ),
579            None,
580        )
582    @property
583    def constructors(self) -> list["FunctionContract"]:
584        """
585        Return the list of constructors (including inherited)
586        """
587        return [func for func in self.functions if func.is_constructor]

Return the list of constructors (including inherited)

explicit_base_constructor_calls: list[slither.core.declarations.function.Function]
589    @property
590    def explicit_base_constructor_calls(self) -> list["Function"]:
591        """
592        list(Function): List of the base constructors called explicitly by this contract definition.
593
594                        Base constructors called by any constructor definition will not be included.
595                        Base constructors implicitly called by the contract definition (without
596                        parenthesis) will not be included.
597
598                        On "contract B is A(){..}" it returns the constructor of A
599        """
600        return [c.constructor for c in self._explicit_base_constructor_calls if c.constructor]

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

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

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

functions_signatures: list[str]
609    @property
610    def functions_signatures(self) -> list[str]:
611        """
612        Return the signatures of all the public/eterxnal functions/state variables
613        :return: list(string) the signatures of all the functions that can be called
614        """
615        if self._signatures is None:
616            sigs = [
617                v.full_name for v in self.state_variables if v.visibility in ["public", "external"]
618            ]
619
620            sigs += {f.full_name for f in self.functions if f.visibility in ["public", "external"]}
621            self._signatures = list(set(sigs))
622        return self._signatures

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

Returns

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

functions_signatures_declared: list[str]
624    @property
625    def functions_signatures_declared(self) -> list[str]:
626        """
627        Return the signatures of the public/eterxnal functions/state variables that are declared by this contract
628        :return: list(string) the signatures of all the functions that can be called and are declared by this contract
629        """
630        if self._signatures_declared is None:
631            sigs = [
632                v.full_name
633                for v in self.state_variables_declared
634                if v.visibility in ["public", "external"]
635            ]
636
637            sigs += {
638                f.full_name
639                for f in self.functions_declared
640                if f.visibility in ["public", "external"]
641            }
642            self._signatures_declared = list(set(sigs))
643        return self._signatures_declared

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

Returns

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

645    @property
646    def functions(self) -> list["FunctionContract"]:
647        """
648        list(Function): List of the functions
649        """
650        return list(self._functions.values())

list(Function): List of the functions

def available_functions_as_dict(self) -> dict[str, slither.core.declarations.function.Function]:
652    def available_functions_as_dict(self) -> dict[str, "Function"]:
653        if self._available_functions_as_dict is None:
654            self._available_functions_as_dict = {
655                f.full_name: f for f in self._functions.values() if not f.is_shadowed
656            }
657        return self._available_functions_as_dict
def add_function( self, func: slither.core.declarations.function_contract.FunctionContract) -> None:
659    def add_function(self, func: "FunctionContract") -> None:
660        self._functions[func.canonical_name] = func
def set_functions( self, functions: dict[str, slither.core.declarations.function_contract.FunctionContract]) -> None:
662    def set_functions(self, functions: dict[str, "FunctionContract"]) -> None:
663        """
664        Set the functions
665
666        :param functions:  dict full_name -> function
667        :return:
668        """
669        self._functions = functions

Set the functions

Parameters
  • functions: dict full_name -> function
Returns
functions_inherited: list[slither.core.declarations.function_contract.FunctionContract]
671    @property
672    def functions_inherited(self) -> list["FunctionContract"]:
673        """
674        list(Function): List of the inherited functions
675        """
676        return [f for f in self.functions if f.contract_declarer != self]

list(Function): List of the inherited functions

functions_declared: list[slither.core.declarations.function_contract.FunctionContract]
678    @property
679    def functions_declared(self) -> list["FunctionContract"]:
680        """
681        list(Function): List of the functions defined within the contract (not inherited)
682        """
683        return [f for f in self.functions if f.contract_declarer == self]

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

functions_entry_points: list[slither.core.declarations.function_contract.FunctionContract]
685    @property
686    def functions_entry_points(self) -> list["FunctionContract"]:
687        """
688        list(Functions): List of public and external functions
689        """
690        return [
691            f
692            for f in self.functions
693            if (f.visibility in ["public", "external"] and not f.is_shadowed) or f.is_fallback
694        ]

list(Functions): List of public and external functions

modifiers: list[slither.core.declarations.modifier.Modifier]
696    @property
697    def modifiers(self) -> list["Modifier"]:
698        """
699        list(Modifier): List of the modifiers
700        """
701        return list(self._modifiers.values())

list(Modifier): List of the modifiers

def available_modifiers_as_dict(self) -> dict[str, slither.core.declarations.modifier.Modifier]:
703    def available_modifiers_as_dict(self) -> dict[str, "Modifier"]:
704        return {m.full_name: m for m in self._modifiers.values() if not m.is_shadowed}
def set_modifiers( self, modifiers: dict[str, slither.core.declarations.modifier.Modifier]) -> None:
706    def set_modifiers(self, modifiers: dict[str, "Modifier"]) -> None:
707        """
708        Set the modifiers
709
710        :param modifiers:  dict full_name -> modifier
711        :return:
712        """
713        self._modifiers = modifiers

Set the modifiers

Parameters
  • modifiers: dict full_name -> modifier
Returns
modifiers_inherited: list[slither.core.declarations.modifier.Modifier]
715    @property
716    def modifiers_inherited(self) -> list["Modifier"]:
717        """
718        list(Modifier): List of the inherited modifiers
719        """
720        return [m for m in self.modifiers if m.contract_declarer != self]

list(Modifier): List of the inherited modifiers

modifiers_declared: list[slither.core.declarations.modifier.Modifier]
722    @property
723    def modifiers_declared(self) -> list["Modifier"]:
724        """
725        list(Modifier): List of the modifiers defined within the contract (not inherited)
726        """
727        return [m for m in self.modifiers if m.contract_declarer == self]

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

functions_and_modifiers: list[slither.core.declarations.function.Function]
729    @property
730    def functions_and_modifiers(self) -> list["Function"]:
731        """
732        list(Function|Modifier): List of the functions and modifiers
733        """
734        return self.functions + self.modifiers  # type: ignore

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

functions_and_modifiers_inherited: list[slither.core.declarations.function.Function]
736    @property
737    def functions_and_modifiers_inherited(self) -> list["Function"]:
738        """
739        list(Function|Modifier): List of the inherited functions and modifiers
740        """
741        return self.functions_inherited + self.modifiers_inherited  # type: ignore

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

functions_and_modifiers_declared: list[slither.core.declarations.function.Function]
743    @property
744    def functions_and_modifiers_declared(self) -> list["Function"]:
745        """
746        list(Function|Modifier): List of the functions and modifiers defined within the contract (not inherited)
747        """
748        return self.functions_declared + self.modifiers_declared  # type: ignore

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

fallback_function: Optional[slither.core.declarations.function_contract.FunctionContract]
750    @property
751    def fallback_function(self) -> Optional["FunctionContract"]:
752        if self._fallback_function is None:
753            for f in self.functions:
754                if f.is_fallback:
755                    self._fallback_function = f
756                    break
757        return self._fallback_function
receive_function: Optional[slither.core.declarations.function_contract.FunctionContract]
759    @property
760    def receive_function(self) -> Optional["FunctionContract"]:
761        if self._receive_function is None:
762            for f in self.functions:
763                if f.is_receive:
764                    self._receive_function = f
765                    break
766        return self._receive_function
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]:
768    def available_elements_from_inheritances(
769        self,
770        elements: dict[str, "Function"],
771        getter_available: Callable[["Contract"], list["FunctionContract"]],
772    ) -> dict[str, "Function"]:
773        """
774
775        :param elements: dict(canonical_name -> elements)
776        :param getter_available: fun x
777        :return:
778        """
779        # keep track of the contracts visited
780        # to prevent an ovveride due to multiple inheritance of the same contract
781        # A is B, C, D is C, --> the second C was already seen
782        inherited_elements: dict[str, FunctionContract] = {}
783        accessible_elements = {}
784        contracts_visited = []
785        for father in self.inheritance_reverse:
786            functions: dict[str, FunctionContract] = {
787                v.full_name: v
788                for v in getter_available(father)
789                if v.contract not in contracts_visited
790                and v.function_language
791                != FunctionLanguage.Yul  # Yul functions are not propagated in the inheritance
792            }
793            contracts_visited.append(father)
794            inherited_elements.update(functions)
795
796        for element in inherited_elements.values():
797            accessible_elements[element.full_name] = elements[element.canonical_name]
798
799        return accessible_elements
Parameters
  • elements: dict(canonical_name -> elements)
  • getter_available: fun x
Returns
inheritance: list[Contract]
808    @property
809    def inheritance(self) -> list["Contract"]:
810        """
811        list(Contract): Inheritance list. Order: the first elem is the first father to be executed
812        """
813        return list(self._inheritance)

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

immediate_inheritance: list[Contract]
815    @property
816    def immediate_inheritance(self) -> list["Contract"]:
817        """
818        list(Contract): List of contracts immediately inherited from (fathers). Order: order of declaration.
819        """
820        return list(self._immediate_inheritance)

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

inheritance_reverse: list[Contract]
822    @property
823    def inheritance_reverse(self) -> list["Contract"]:
824        """
825        list(Contract): Inheritance list. Order: the last elem is the first father to be executed
826        """
827        return list(reversed(self._inheritance))

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

def set_inheritance( self, inheritance: list[Contract], immediate_inheritance: list[Contract], called_base_constructor_contracts: list[Contract]) -> None:
829    def set_inheritance(
830        self,
831        inheritance: list["Contract"],
832        immediate_inheritance: list["Contract"],
833        called_base_constructor_contracts: list["Contract"],
834    ) -> None:
835        self._inheritance = inheritance
836        self._immediate_inheritance = immediate_inheritance
837        self._explicit_base_constructor_calls = called_base_constructor_contracts
derived_contracts: list[Contract]
839    @property
840    def derived_contracts(self) -> list["Contract"]:
841        """
842        list(Contract): Return the list of contracts derived from self
843        """
844        return self.compilation_unit.derived_contracts_map.get(self, [])

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

def get_functions_reading_from_variable( self, variable: slither.core.variables.variable.Variable) -> list[slither.core.declarations.function.Function]:
853    def get_functions_reading_from_variable(self, variable: "Variable") -> list["Function"]:
854        """
855        Return the functions reading the variable
856        """
857        return [f for f in self.functions if f.is_reading(variable)]

Return the functions reading the variable

def get_functions_writing_to_variable( self, variable: slither.core.variables.variable.Variable) -> list[slither.core.declarations.function.Function]:
859    def get_functions_writing_to_variable(self, variable: "Variable") -> list["Function"]:
860        """
861        Return the functions writing the variable
862        """
863        return [f for f in self.functions if f.is_writing(variable)]

Return the functions writing the variable

def get_function_from_full_name( self, full_name: str) -> Optional[slither.core.declarations.function.Function]:
865    def get_function_from_full_name(self, full_name: str) -> Optional["Function"]:
866        """
867            Return a function from a full name
868            The full name differs from the solidity's signature are the type are conserved
869            For example contract type are kept, structure are not unrolled, etc
870        Args:
871            full_name (str): signature of the function (without return statement)
872        Returns:
873            Function
874        """
875        return next(
876            (f for f in self.functions if f.full_name == full_name and not f.is_shadowed),
877            None,
878        )

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

def get_function_from_signature( self, function_signature: str) -> Optional[slither.core.declarations.function.Function]:
880    def get_function_from_signature(self, function_signature: str) -> Optional["Function"]:
881        """
882            Return a function from a signature
883        Args:
884            function_signature (str): signature of the function (without return statement)
885        Returns:
886            Function
887        """
888        return next(
889            (
890                f
891                for f in self.functions
892                if f.solidity_signature == function_signature and not f.is_shadowed
893            ),
894            None,
895        )

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

def get_modifier_from_signature( self, modifier_signature: str) -> Optional[slither.core.declarations.modifier.Modifier]:
897    def get_modifier_from_signature(self, modifier_signature: str) -> Optional["Modifier"]:
898        """
899        Return a modifier from a signature
900
901        :param modifier_signature:
902        """
903        return next(
904            (m for m in self.modifiers if m.full_name == modifier_signature and not m.is_shadowed),
905            None,
906        )

Return a modifier from a signature

Parameters
  • modifier_signature:
def get_function_from_canonical_name( self, canonical_name: str) -> Optional[slither.core.declarations.function.Function]:
908    def get_function_from_canonical_name(self, canonical_name: str) -> Optional["Function"]:
909        """
910            Return a function from a canonical name (contract.signature())
911        Args:
912            canonical_name (str): canonical name of the function (without return statement)
913        Returns:
914            Function
915        """
916        return next((f for f in self.functions if f.canonical_name == canonical_name), None)

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

def get_modifier_from_canonical_name( self, canonical_name: str) -> Optional[slither.core.declarations.modifier.Modifier]:
918    def get_modifier_from_canonical_name(self, canonical_name: str) -> Optional["Modifier"]:
919        """
920            Return a modifier from a canonical name (contract.signature())
921        Args:
922            canonical_name (str): canonical name of the modifier
923        Returns:
924            Modifier
925        """
926        return next((m for m in self.modifiers if m.canonical_name == canonical_name), None)

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

def get_state_variable_from_name( self, variable_name: str) -> Optional[slither.core.variables.state_variable.StateVariable]:
928    def get_state_variable_from_name(self, variable_name: str) -> Optional["StateVariable"]:
929        """
930        Return a state variable from a name
931
932        :param variable_name:
933        """
934        return next((v for v in self.state_variables if v.name == variable_name), None)

Return a state variable from a name

Parameters
  • variable_name:
def get_state_variable_from_canonical_name( self, canonical_name: str) -> Optional[slither.core.variables.state_variable.StateVariable]:
936    def get_state_variable_from_canonical_name(
937        self, canonical_name: str
938    ) -> Optional["StateVariable"]:
939        """
940            Return a state variable from a canonical_name
941        Args:
942            canonical_name (str): name of the variable
943        Returns:
944            StateVariable
945        """
946        return next((v for v in self.state_variables if v.canonical_name == canonical_name), None)

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

def get_structure_from_name( self, structure_name: str) -> Optional[slither.core.declarations.structure_contract.StructureContract]:
948    def get_structure_from_name(self, structure_name: str) -> Optional["StructureContract"]:
949        """
950            Return a structure from a name
951        Args:
952            structure_name (str): name of the structure
953        Returns:
954            StructureContract
955        """
956        return next((st for st in self.structures if st.name == structure_name), None)

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

def get_structure_from_canonical_name( self, structure_name: str) -> Optional[slither.core.declarations.structure_contract.StructureContract]:
958    def get_structure_from_canonical_name(
959        self, structure_name: str
960    ) -> Optional["StructureContract"]:
961        """
962            Return a structure from a canonical name
963        Args:
964            structure_name (str): canonical name of the structure
965        Returns:
966            StructureContract
967        """
968        return next((st for st in self.structures if st.canonical_name == structure_name), None)

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

def get_event_from_signature(self, event_signature: str) -> Optional[ForwardRef('Event')]:
970    def get_event_from_signature(self, event_signature: str) -> Optional["Event"]:
971        """
972            Return an event from a signature
973        Args:
974            event_signature (str): signature of the event
975        Returns:
976            Event
977        """
978        return next((e for e in self.events if e.full_name == event_signature), None)

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

def get_event_from_canonical_name(self, event_canonical_name: str) -> Optional[ForwardRef('Event')]:
980    def get_event_from_canonical_name(self, event_canonical_name: str) -> Optional["Event"]:
981        """
982            Return an event from a canonical name
983        Args:
984            event_canonical_name (str): name of the event
985        Returns:
986            Event
987        """
988        return next((e for e in self.events if e.canonical_name == event_canonical_name), None)

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

def get_enum_from_name(self, enum_name: str) -> Optional[slither.core.declarations.enum.Enum]:
990    def get_enum_from_name(self, enum_name: str) -> Optional["Enum"]:
991        """
992            Return an enum from a name
993        Args:
994            enum_name (str): name of the enum
995        Returns:
996            Enum
997        """
998        return next((e for e in self.enums if e.name == enum_name), None)

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

def get_enum_from_canonical_name(self, enum_name: str) -> Optional[slither.core.declarations.enum.Enum]:
1000    def get_enum_from_canonical_name(self, enum_name: str) -> Optional["Enum"]:
1001        """
1002            Return an enum from a canonical name
1003        Args:
1004            enum_name (str): canonical name of the enum
1005        Returns:
1006            Enum
1007        """
1008        return next((e for e in self.enums if e.canonical_name == enum_name), None)

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

def get_functions_overridden_by( self, function: slither.core.declarations.function.Function) -> list[slither.core.declarations.function.Function]:
1010    def get_functions_overridden_by(self, function: "Function") -> list["Function"]:
1011        """
1012            Return the list of functions overridden by the function
1013        Args:
1014            (core.Function)
1015        Returns:
1016            list(core.Function)
1017
1018        """
1019        return function.overrides

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

all_functions_called: list[slither.core.declarations.function.Function]
1028    @property
1029    def all_functions_called(self) -> list["Function"]:
1030        """
1031        list(Function): List of functions reachable from the contract
1032        Includes super, and private/internal functions not shadowed
1033        """
1034        from slither.slithir.operations import Operation
1035
1036        if self._all_functions_called is None:
1037            all_functions = [f for f in self.functions + self.modifiers if not f.is_shadowed]  # type: ignore
1038            all_callss = [f.all_internal_calls() for f in all_functions] + [list(all_functions)]
1039            all_calls = [
1040                item.function if isinstance(item, Operation) else item
1041                for sublist in all_callss
1042                for item in sublist
1043            ]
1044            all_calls = list(set(all_calls))
1045
1046            all_constructors = [c.constructor for c in self.inheritance if c.constructor]
1047            all_constructors = list(set(all_constructors))
1048
1049            set_all_calls = set(all_calls + list(all_constructors))
1050
1051            self._all_functions_called = [c for c in set_all_calls if isinstance(c, Function)]
1052        return self._all_functions_called

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

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

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

all_state_variables_read: list[slither.core.variables.state_variable.StateVariable]
1068    @property
1069    def all_state_variables_read(self) -> list["StateVariable"]:
1070        """
1071        list(StateVariable): List all of the state variables read
1072        """
1073        all_state_variables_reads = [
1074            f.all_state_variables_read()
1075            for f in self.functions + self.modifiers  # type: ignore
1076        ]
1077        all_state_variables_read = [
1078            item for sublist in all_state_variables_reads for item in sublist
1079        ]
1080        return list(set(all_state_variables_read))

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

all_library_calls: list[slither.slithir.operations.library_call.LibraryCall]
1082    @property
1083    def all_library_calls(self) -> list["LibraryCall"]:
1084        """
1085        list(LibraryCall): List all of the libraries func called
1086        """
1087        all_high_level_callss = [f.all_library_calls() for f in self.functions + self.modifiers]  # type: ignore
1088        all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist]
1089        return list(set(all_high_level_calls))

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

all_high_level_calls: list[tuple[Contract, slither.slithir.operations.high_level_call.HighLevelCall]]
1091    @property
1092    def all_high_level_calls(self) -> list[tuple["Contract", "HighLevelCall"]]:
1093        """
1094        list(Tuple("Contract", "HighLevelCall")): List all of the external high level calls
1095        """
1096        all_high_level_callss = [f.all_high_level_calls() for f in self.functions + self.modifiers]  # type: ignore
1097        all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist]
1098        return list(set(all_high_level_calls))

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

def get_summary( self, include_shadowed: bool = True) -> tuple[str, list[str], list[str], list, list]:
1107    def get_summary(
1108        self, include_shadowed: bool = True
1109    ) -> tuple[str, list[str], list[str], list, list]:
1110        """Return the function summary
1111
1112        :param include_shadowed: boolean to indicate if shadowed functions should be included (default True)
1113        Returns:
1114            (str, list, list, list, list): (name, inheritance, variables, fuction summaries, modifier summaries)
1115        """
1116        func_summaries = [
1117            f.get_summary() for f in self.functions if (not f.is_shadowed or include_shadowed)
1118        ]
1119        modif_summaries = [
1120            f.get_summary() for f in self.modifiers if (not f.is_shadowed or include_shadowed)
1121        ]
1122        return (
1123            self.name,
1124            [str(x) for x in self.inheritance],
1125            [str(x) for x in self.variables],
1126            func_summaries,
1127            modif_summaries,
1128        )

Return the function summary

Parameters
  • include_shadowed: boolean to indicate if shadowed functions should be included (default True) Returns: (str, list, list, list, list): (name, inheritance, variables, fuction summaries, modifier summaries)
def is_signature_only(self) -> bool:
1130    def is_signature_only(self) -> bool:
1131        """Detect if the contract has only abstract functions
1132
1133        Returns:
1134            bool: true if the function are abstract functions
1135        """
1136        return all((not f.is_implemented) for f in self.functions)

Detect if the contract has only abstract functions

Returns: bool: true if the function are abstract functions

def ercs(self) -> list[str]:
1145    def ercs(self) -> list[str]:
1146        """
1147        Return the ERC implemented
1148        :return: list of string
1149        """
1150        all_erc = [
1151            ("ERC20", self.is_erc20),
1152            ("ERC165", self.is_erc165),
1153            ("ERC1820", self.is_erc1820),
1154            ("ERC223", self.is_erc223),
1155            ("ERC721", self.is_erc721),
1156            ("ERC777", self.is_erc777),
1157            ("ERC2612", self.is_erc2612),
1158            ("ERC1363", self.is_erc1363),
1159            ("ERC4626", self.is_erc4626),
1160        ]
1161
1162        return [erc for erc, is_erc in all_erc if is_erc()]

Return the ERC implemented

Returns

list of string

def is_erc20(self) -> bool:
1164    def is_erc20(self) -> bool:
1165        """
1166            Check if the contract is an erc20 token
1167
1168            Note: it does not check for correct return values
1169        :return: Returns a true if the contract is an erc20
1170        """
1171        full_names = self.functions_signatures
1172        return all(s in full_names for s in ERC20_signatures)

Check if the contract is an erc20 token

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc20

def is_erc165(self) -> bool:
1174    def is_erc165(self) -> bool:
1175        """
1176            Check if the contract is an erc165 token
1177
1178            Note: it does not check for correct return values
1179        :return: Returns a true if the contract is an erc165
1180        """
1181        full_names = self.functions_signatures
1182        return all(s in full_names for s in ERC165_signatures)

Check if the contract is an erc165 token

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc165

def is_erc1820(self) -> bool:
1184    def is_erc1820(self) -> bool:
1185        """
1186            Check if the contract is an erc1820
1187
1188            Note: it does not check for correct return values
1189        :return: Returns a true if the contract is an erc165
1190        """
1191        full_names = self.functions_signatures
1192        return all(s in full_names for s in ERC1820_signatures)

Check if the contract is an erc1820

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc165

def is_erc223(self) -> bool:
1194    def is_erc223(self) -> bool:
1195        """
1196            Check if the contract is an erc223 token
1197
1198            Note: it does not check for correct return values
1199        :return: Returns a true if the contract is an erc223
1200        """
1201        full_names = self.functions_signatures
1202        return all(s in full_names for s in ERC223_signatures)

Check if the contract is an erc223 token

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc223

def is_erc721(self) -> bool:
1204    def is_erc721(self) -> bool:
1205        """
1206            Check if the contract is an erc721 token
1207
1208            Note: it does not check for correct return values
1209        :return: Returns a true if the contract is an erc721
1210        """
1211        full_names = self.functions_signatures
1212        return all(s in full_names for s in ERC721_signatures)

Check if the contract is an erc721 token

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc721

def is_erc777(self) -> bool:
1214    def is_erc777(self) -> bool:
1215        """
1216            Check if the contract is an erc777
1217
1218            Note: it does not check for correct return values
1219        :return: Returns a true if the contract is an erc165
1220        """
1221        full_names = self.functions_signatures
1222        return all(s in full_names for s in ERC777_signatures)

Check if the contract is an erc777

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc165

def is_erc1155(self) -> bool:
1224    def is_erc1155(self) -> bool:
1225        """
1226            Check if the contract is an erc1155
1227
1228            Note: it does not check for correct return values
1229        :return: Returns a true if the contract is an erc1155
1230        """
1231        full_names = self.functions_signatures
1232        return all(s in full_names for s in ERC1155_signatures)

Check if the contract is an erc1155

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc1155

def is_erc4626(self) -> bool:
1234    def is_erc4626(self) -> bool:
1235        """
1236            Check if the contract is an erc4626
1237
1238            Note: it does not check for correct return values
1239        :return: Returns a true if the contract is an erc4626
1240        """
1241        full_names = self.functions_signatures
1242        return all(s in full_names for s in ERC4626_signatures)

Check if the contract is an erc4626

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc4626

def is_erc2612(self) -> bool:
1244    def is_erc2612(self) -> bool:
1245        """
1246            Check if the contract is an erc2612
1247
1248            Note: it does not check for correct return values
1249        :return: Returns a true if the contract is an erc2612
1250        """
1251        full_names = self.functions_signatures
1252        return all(s in full_names for s in ERC2612_signatures)

Check if the contract is an erc2612

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc2612

def is_erc1363(self) -> bool:
1254    def is_erc1363(self) -> bool:
1255        """
1256            Check if the contract is an erc1363
1257
1258            Note: it does not check for correct return values
1259        :return: Returns a true if the contract is an erc1363
1260        """
1261        full_names = self.functions_signatures
1262        return all(s in full_names for s in ERC1363_signatures)

Check if the contract is an erc1363

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc1363

def is_erc4524(self) -> bool:
1264    def is_erc4524(self) -> bool:
1265        """
1266            Check if the contract is an erc4524
1267
1268            Note: it does not check for correct return values
1269        :return: Returns a true if the contract is an erc4524
1270        """
1271        full_names = self.functions_signatures
1272        return all(s in full_names for s in ERC4524_signatures)

Check if the contract is an erc4524

Note: it does not check for correct return values
Returns

Returns a true if the contract is an erc4524

is_token: bool
1274    @property
1275    def is_token(self) -> bool:
1276        """
1277        Check if the contract follows one of the standard ERC token
1278        :return:
1279        """
1280        return (
1281            self.is_erc20()
1282            or self.is_erc721()
1283            or self.is_erc165()
1284            or self.is_erc223()
1285            or self.is_erc777()
1286            or self.is_erc1155()
1287        )

Check if the contract follows one of the standard ERC token

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

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

Returns

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

def is_possible_erc721(self) -> bool:
1303    def is_possible_erc721(self) -> bool:
1304        """
1305        Checks if the provided contract could be attempting to implement ERC721 standards.
1306
1307        :return: Returns a boolean indicating if the provided contract met the token standard.
1308        """
1309        # We do not check for all the functions, as name(), symbol(), might give too many FPs
1310        full_names = self.functions_signatures
1311        return (
1312            "ownerOf(uint256)" in full_names
1313            or "safeTransferFrom(address,address,uint256,bytes)" in full_names
1314            or "safeTransferFrom(address,address,uint256)" in full_names
1315            or "setApprovalForAll(address,bool)" in full_names
1316            or "getApproved(uint256)" in full_names
1317            or "isApprovedForAll(address,address)" in full_names
1318        )

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

Returns

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

is_possible_token: bool
1320    @property
1321    def is_possible_token(self) -> bool:
1322        """
1323        Check if the contract is a potential token (it might not implement all the functions)
1324        :return:
1325        """
1326        return self.is_possible_erc20() or self.is_possible_erc721()

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

Returns
def is_from_dependency(self) -> bool:
1335    def is_from_dependency(self) -> bool:
1336        return self.compilation_unit.core.crytic_compile.is_dependency(
1337            self.source_mapping.filename.absolute
1338        )
is_truffle_migration: bool
1347    @property
1348    def is_truffle_migration(self) -> bool:
1349        """
1350        Return true if the contract is the Migrations contract needed for Truffle
1351        :return:
1352        """
1353        if self.compilation_unit.core.crytic_compile.platform == PlatformType.TRUFFLE:
1354            if self.name == "Migrations":
1355                paths = Path(self.source_mapping.filename.absolute).parts
1356                if len(paths) >= 2:
1357                    return paths[-2] == "contracts" and paths[-1] == "migrations.sol"
1358        return False

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

Returns
is_test: bool
1360    @property
1361    def is_test(self) -> bool:
1362        return is_test_contract(self) or self.is_truffle_migration  # type: ignore
def update_read_write_using_ssa(self) -> None:
1371    def update_read_write_using_ssa(self) -> None:
1372        for function in self.functions + list(self.modifiers):
1373            function.update_read_write_using_ssa()
is_upgradeable: bool
1382    @property
1383    def is_upgradeable(self) -> bool:
1384        if self._is_upgradeable is None:
1385            self._is_upgradeable = False
1386            initializable = self.file_scope.get_contract_from_name("Initializable")
1387            if initializable:
1388                if initializable in self.inheritance:
1389                    self._is_upgradeable = True
1390            else:
1391                for contract in self.inheritance + [self]:
1392                    # This might lead to false positive
1393                    lower_name = contract.name.lower()
1394                    if "upgradeable" in lower_name or "upgradable" in lower_name:
1395                        self._is_upgradeable = True
1396                        break
1397                    if "initializable" in lower_name:
1398                        self._is_upgradeable = True
1399                        break
1400        return self._is_upgradeable
is_upgradeable_proxy: bool
1406    @property
1407    def is_upgradeable_proxy(self) -> bool:
1408        from slither.core.cfg.node import NodeType
1409        from slither.slithir.operations import LowLevelCall
1410
1411        if self._is_upgradeable_proxy is None:
1412            self._is_upgradeable_proxy = False
1413            if "Proxy" in self.name:
1414                self._is_upgradeable_proxy = True
1415                return True
1416            for f in self.functions:
1417                if f.is_fallback:
1418                    for node in f.all_nodes():
1419                        for ir in node.irs:
1420                            if isinstance(ir, LowLevelCall) and ir.function_name == "delegatecall":
1421                                self._is_upgradeable_proxy = True
1422                                return self._is_upgradeable_proxy
1423                        if node.type == NodeType.ASSEMBLY:
1424                            inline_asm = node.inline_asm
1425                            if inline_asm:
1426                                if "delegatecall" in inline_asm:
1427                                    self._is_upgradeable_proxy = True
1428                                    return self._is_upgradeable_proxy
1429        return self._is_upgradeable_proxy
upgradeable_version: str | None
1435    @property
1436    def upgradeable_version(self) -> str | None:
1437        return self._upgradeable_version
is_incorrectly_constructed: bool
1450    @property
1451    def is_incorrectly_constructed(self) -> bool:
1452        """
1453        Return true if there was an internal Slither's issue when analyzing the contract
1454        :return:
1455        """
1456        return self._is_incorrectly_parsed

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

Returns
def add_constructor_variables(self) -> None:
1462    def add_constructor_variables(self) -> None:
1463        from slither.core.declarations.function_contract import FunctionContract
1464
1465        if self.state_variables:
1466            for idx, variable_candidate in enumerate(self.state_variables):
1467                if variable_candidate.expression and not variable_candidate.is_constant:
1468                    constructor_variable = FunctionContract(self.compilation_unit)
1469                    constructor_variable.set_function_type(FunctionType.CONSTRUCTOR_VARIABLES)
1470                    constructor_variable.set_contract(self)  # type: ignore
1471                    constructor_variable.set_contract_declarer(self)  # type: ignore
1472                    constructor_variable.set_visibility("internal")
1473                    # For now, source mapping of the constructor variable is the whole contract
1474                    # Could be improved with a targeted source mapping
1475                    constructor_variable.set_offset(self.source_mapping, self.compilation_unit)
1476                    self._functions[constructor_variable.canonical_name] = constructor_variable
1477
1478                    prev_node = self._create_node(
1479                        constructor_variable, 0, variable_candidate, constructor_variable
1480                    )
1481                    variable_candidate.node_initialization = prev_node
1482                    counter = 1
1483                    for v in self.state_variables[idx + 1 :]:
1484                        if v.expression and not v.is_constant:
1485                            next_node = self._create_node(
1486                                constructor_variable, counter, v, prev_node.scope
1487                            )
1488                            v.node_initialization = next_node
1489                            prev_node.add_son(next_node)
1490                            next_node.add_father(prev_node)
1491                            prev_node = next_node
1492                            counter += 1
1493                    break
1494
1495            for idx, variable_candidate in enumerate(self.state_variables):
1496                if variable_candidate.expression and variable_candidate.is_constant:
1497                    constructor_variable = FunctionContract(self.compilation_unit)
1498                    constructor_variable.set_function_type(
1499                        FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES
1500                    )
1501                    constructor_variable.set_contract(self)  # type: ignore
1502                    constructor_variable.set_contract_declarer(self)  # type: ignore
1503                    constructor_variable.set_visibility("internal")
1504                    # For now, source mapping of the constructor variable is the whole contract
1505                    # Could be improved with a targeted source mapping
1506                    constructor_variable.set_offset(self.source_mapping, self.compilation_unit)
1507                    self._functions[constructor_variable.canonical_name] = constructor_variable
1508
1509                    prev_node = self._create_node(
1510                        constructor_variable, 0, variable_candidate, constructor_variable
1511                    )
1512                    variable_candidate.node_initialization = prev_node
1513                    counter = 1
1514                    for v in self.state_variables[idx + 1 :]:
1515                        if v.expression and v.is_constant:
1516                            next_node = self._create_node(
1517                                constructor_variable, counter, v, prev_node.scope
1518                            )
1519                            v.node_initialization = next_node
1520                            prev_node.add_son(next_node)
1521                            next_node.add_father(prev_node)
1522                            prev_node = next_node
1523                            counter += 1
1524
1525                    break
def convert_expression_to_slithir_ssa(self) -> None:
1561    def convert_expression_to_slithir_ssa(self) -> None:
1562        """
1563        Assume generate_slithir_and_analyze was called on all functions
1564
1565        :return:
1566        """
1567        from slither.slithir.variables import StateIRVariable
1568
1569        all_ssa_state_variables_instances = {}
1570
1571        for contract in self.inheritance:
1572            for v in contract.state_variables_declared:
1573                new_var = StateIRVariable(v)
1574                all_ssa_state_variables_instances[v.canonical_name] = new_var
1575                self._initial_state_variables.append(new_var)
1576
1577        for v in self.variables:
1578            if v.contract == self:
1579                new_var = StateIRVariable(v)
1580                all_ssa_state_variables_instances[v.canonical_name] = new_var
1581                self._initial_state_variables.append(new_var)
1582
1583        for func in self.functions + list(self.modifiers):
1584            func.generate_slithir_ssa(all_ssa_state_variables_instances)

Assume generate_slithir_and_analyze was called on all functions

Returns
def fix_phi(self) -> None:
1586    def fix_phi(self) -> None:
1587        last_state_variables_instances: dict[str, list[StateVariable]] = {}
1588        initial_state_variables_instances: dict[str, StateVariable] = {}
1589        for v in self._initial_state_variables:
1590            last_state_variables_instances[v.canonical_name] = []
1591            initial_state_variables_instances[v.canonical_name] = v
1592
1593        for func in self.functions + list(self.modifiers):
1594            result = func.get_last_ssa_state_variables_instances()
1595            for variable_name, instances in result.items():
1596                # TODO: investigate the next operation
1597                last_state_variables_instances[variable_name] += list(instances)
1598
1599        for func in self.functions + list(self.modifiers):
1600            func.fix_phi(last_state_variables_instances, initial_state_variables_instances)