content: from typing import (
      TYPE_CHECKING,
      Any,
      Callable,
      Collection,
      Generic,
      Iterable,
      NoReturn,
      Sequence,
      Union,
      cast,
  )
  import warnings
  
  from eth_abi.exceptions import (
      InsufficientDataBytes,
  )
  from eth_typing import (
      ABI,
      ABIElement,
      ABIEvent,
      ABIFunction,
      Address,
      ChecksumAddress,
      HexStr,
  )
  from eth_utils import (
      abi_to_signature,
      add_0x_prefix,
      combomethod,
      encode_hex,
      filter_abi_by_type,
      function_abi_to_4byte_selector,
      get_normalized_abi_inputs,
      is_list_like,
      is_text,
      keccak,
      to_bytes,
      to_tuple,
  )
  from hexbytes import (
      HexBytes,
  )
  
  from web3._utils.abi import (
      fallback_func_abi_exists,
      find_constructor_abi_element_by_type,
      get_abi_element_signature,
      get_name_from_abi_element_identifier,
      is_array_type,
      receive_func_abi_exists,
  )
  from web3._utils.abi_element_identifiers import (
      FallbackFn,
      ReceiveFn,
  )
  from web3._utils.compat import (
      Self,
  )
  from web3._utils.contracts import (
      copy_contract_event,
      copy_contract_function,
      decode_transaction_data,
      encode_abi,
      prepare_transaction,
  )
  from web3._utils.datatypes import (
      PropertyCheckingFactory,
  )
  from web3._utils.empty import (
      empty,
  )
  from web3._utils.encoding import (
      hexstr_if_str,
      to_4byte_hex,
      to_hex,
  )
  from web3._utils.events import (
      AsyncEventFilterBuilder,
      EventFilterBuilder,
      get_event_data,
      is_dynamic_sized_type,
  )
  from web3._utils.filters import (
      construct_event_filter_params,
  )
  from web3._utils.normalizers import (
      BASE_RETURN_NORMALIZERS,
  )
  from web3.datastructures import (
      AttributeDict,
      MutableAttributeDict,
  )
  from web3.exceptions import (
      ABIEventNotFound,
      ABIFallbackNotFound,
      ABIFunctionNotFound,
      ABIReceiveNotFound,
      InvalidEventABI,
      LogTopicError,
      MismatchedABI,
      NoABIEventsFound,
      NoABIFound,
      NoABIFunctionsFound,
      Web3AttributeError,
      Web3TypeError,
      Web3ValidationError,
      Web3ValueError,
  )
  from web3.logs import (
      DISCARD,
      IGNORE,
      STRICT,
      WARN,
      EventLogErrorFlags,
  )
  from web3.types import (
      ABIElementIdentifier,
      BlockIdentifier,
      EventData,
      FilterParams,
      LogReceipt,
      StateOverride,
      TContractEvent,
      TContractFn,
      TxParams,
      TxReceipt,
  )
  from web3.utils.abi import (
      _filter_by_argument_count,
      _get_any_abi_signature_with_name,
      _mismatched_abi_error_diagnosis,
      check_if_arguments_can_be_encoded,
      get_abi_element,
      get_abi_element_info,
  )
  
  if TYPE_CHECKING:
      from web3 import (  # noqa: F401
          AsyncWeb3,
          Web3,
      )
  
      from .async_contract import (  # noqa: F401
          AsyncContractEvent,
          AsyncContractFunction,
      )
      from .contract import (  # noqa: F401
          ContractEvent,
          ContractFunction,
      )
  
  
  class BaseContractEvent:
      """
      Base class for contract events
  
      An event accessed via the api `contract.events.myEvents(*args, **kwargs)`
      is a subclass of this class.
      """
  
      address: ChecksumAddress = None
      event_name: str = None
      name: str = None
      abi_element_identifier: ABIElementIdentifier = None
      signature: str = None
      w3: Union["Web3", "AsyncWeb3[Any]"] = None
      contract_abi: ABI = None
      abi: ABIEvent = None
      argument_names: tuple[str, ...] = tuple()
      argument_types: tuple[str, ...] = tuple()
      args: Any = None
      kwargs: Any = None
      _topic: HexStr = None
  
      def __init__(self, *argument_names: str, abi: ABIEvent | None = None) -> None:
          self.abi_element_identifier = type(self).__name__
          self.name = get_name_from_abi_element_identifier(self.abi_element_identifier)
          self.event_name = self.name
  
          if abi:
              self.abi = abi
  
          self.signature = abi_to_signature(self.abi)
  
          if argument_names:
              self.argument_names = argument_names
  
          event_inputs = self.abi.get("inputs", [])
          self.argument_names = tuple([input.get("name", None) for input in event_inputs])
          self.argument_types = tuple([input["type"] for input in event_inputs])
  
      def __repr__(self) -> str:
          if self.abi:
              return f"<Event {abi_to_signature(self.abi)}>"
          return f"<Event {get_abi_element_signature(self.abi_element_identifier)}>"
  
      def __call__(self, *args: Any, **kwargs: Any) -> Self:
          return copy_contract_event(self, *args, **kwargs)
  
      @property
      def topic(self) -> HexStr:
          if self._topic is None:
              self._topic = encode_hex(keccak(text=self.signature))
          return self._topic
  
      @combomethod
      def _get_event_abi(cls) -> ABIEvent:
          if cls.abi:
              return cls.abi
  
          return cast(
              ABIEvent,
              get_abi_element(
                  filter_abi_by_type("event", cls.contract_abi),
                  cls.abi_element_identifier,
                  abi_codec=cls.w3.codec,
              ),
          )
  
      def _set_event_info(self) -> None:
          self.abi = self._get_event_abi()
  
      @combomethod
      def process_receipt(
          self, txn_receipt: TxReceipt, errors: EventLogErrorFlags = WARN
      ) -> Iterable[EventData]:
          return self._parse_logs(txn_receipt=txn_receipt, errors=errors)
  
      @combomethod
      @to_tuple
      def _parse_logs(
          self, txn_receipt: TxReceipt, errors: EventLogErrorFlags
      ) -> Iterable[EventData]:
          try:
              errors.name
          except AttributeError:
              raise Web3AttributeError(
                  f"Error flag must be one of: {EventLogErrorFlags.flag_options()}"
              )
  
          for log in txn_receipt["logs"]:
              try:
                  rich_log = get_event_data(self.w3.codec, self.abi, log)
              except (
                  MismatchedABI,
                  LogTopicError,
                  InvalidEventABI,
                  TypeError,
                  InsufficientDataBytes,
              ) as e:
                  if errors == DISCARD:
                      continue
                  elif errors == IGNORE:
                      # type ignores b/c rich_log set on 1092 conflicts with mutated types
                      new_log = MutableAttributeDict(log)  # type: ignore
                      new_log["errors"] = e
                      rich_log = AttributeDict(new_log)  # type: ignore
                  elif errors == STRICT:
                      raise e
                  else:
                      warnings.warn(
                          f"The log with transaction hash: {log['transactionHash']!r} "
                          f"and logIndex: {log['logIndex']} encountered the following "
                          f"error during processing: {type(e).__name__}({e}). It has "
                          "been discarded.",
                          stacklevel=2,
                      )
                      continue
              yield rich_log
  
      @combomethod
      def process_log(self, log: LogReceipt) -> EventData:
          return get_event_data(self.w3.codec, self.abi, log)
  
      @combomethod
      def _get_event_filter_params(
          self,
          abi: ABIEvent,
          argument_filters: dict[str, Any] | None = None,
          from_block: BlockIdentifier | None = None,
          to_block: BlockIdentifier | None = None,
          block_hash: HexBytes | None = None,
      ) -> FilterParams:
          if not self.address:
              raise Web3TypeError(
                  "This method can be only called on "
                  "an instated contract with an address"
              )
  
          if argument_filters is None:
              argument_filters = dict()
  
          _filters = dict(**argument_filters)
  
          blkhash_set = block_hash is not None
          blknum_set = from_block is not None or to_block is not None
          if blkhash_set and blknum_set:
              raise Web3ValidationError(
                  "`block_hash` cannot be set at the same time as "
                  "`from_block` or `to_block`"
              )
  
          # Construct JSON-RPC raw filter presentation based on human readable
          # Python descriptions. Namely, convert event names to their keccak signatures
          _, event_filter_params = construct_event_filter_params(
              abi,
              self.w3.codec,
              contract_address=self.address,
              argument_filters=_filters,
              from_block=from_block,
              to_block=to_block,
              address=self.address,
          )
  
          if block_hash is not None:
              event_filter_params["blockHash"] = block_hash
  
          return event_filter_params
  
      @classmethod
      def factory(cls, class_name: str, **kwargs: Any) -> Self:
          return PropertyCheckingFactory(class_name, (cls,), kwargs)()
  
      @staticmethod
      def check_for_forbidden_api_filter_arguments(
          event_abi: ABIEvent, _filters: dict[str, Any]
      ) -> None:
          name_indexed_inputs = {_input["name"]: _input for _input in event_abi["inputs"]}
  
          for filter_name, filter_value in _filters.items():
              _input = name_indexed_inputs[filter_name]
              if is_array_type(_input["type"]):
                  raise Web3TypeError(
                      "createFilter no longer supports array type filter arguments. "
                      "see the build_filter method for filtering array type filters."
                  )
              if is_list_like(filter_value) and is_dynamic_sized_type(_input["type"]):
                  raise Web3TypeError(
                      "createFilter no longer supports setting filter argument options "
                      "for dynamic sized types. See the build_filter method for setting "
                      "filters with the match_any method."
                  )
  
      @staticmethod
      def _process_get_logs_argument_filters(
          event_abi: ABIEvent,
          event_logs: Sequence[EventData],
          argument_filters: dict[str, Any] | None,
      ) -> Iterable[EventData]:
          if (
              argument_filters is None
              or len(event_logs) == 0
              or
              # if no non-indexed args in argument filters, since indexed args are
              # filtered pre-call to ``eth_getLogs`` by building specific ``topics``.
              not any(
                  not arg["indexed"]
                  for arg in event_abi["inputs"]
                  if arg["name"] in argument_filters
              )
          ):
              return event_logs
  
          filtered_logs_by_non_indexed_args = []
  
          for log in event_logs:
              match = False
              for arg, match_values in argument_filters.items():
                  if not is_list_like(match_values):
                      match_values = [match_values]
  
                  for abi_arg in event_abi["inputs"]:
                      if abi_arg["name"] == arg:
                          if (
                              # isolate ``string`` values to support substrings
                              abi_arg["type"] == "string"
                              and any(val in log["args"][arg] for val in match_values)
                              or (
                                  # otherwise, do direct value comparison
                                  abi_arg["type"] != "string"
                                  and log["args"][arg] in match_values
                              )
                          ):
                              filtered_logs_by_non_indexed_args.append(log)
                              match = True
                              break
                  if match:
                      break
  
          return filtered_logs_by_non_indexed_args
  
      @combomethod
      def _set_up_filter_builder(
          self,
          argument_filters: dict[str, Any] | None = None,
          from_block: BlockIdentifier | None = None,
          to_block: BlockIdentifier = "latest",
          address: ChecksumAddress | None = None,
          topics: Sequence[Any] | None = None,
          filter_builder: EventFilterBuilder | AsyncEventFilterBuilder = None,
      ) -> None:
          if from_block is None:
              raise Web3TypeError(
                  "Missing mandatory keyword argument to create_filter: `from_block`"
              )
  
          if argument_filters is None:
              argument_filters = dict()
  
          _filters = dict(**argument_filters)
  
          self.check_for_forbidden_api_filter_arguments(self.abi, _filters)
  
          _, event_filter_params = construct_event_filter_params(
              self.abi,
              self.w3.codec,
              contract_address=self.address,
              argument_filters=_filters,
              from_block=from_block,
              to_block=to_block,
              address=address,
              topics=topics,
          )
  
          filter_builder.address = cast(
              ChecksumAddress, event_filter_params.get("address")
          )
          filter_builder.from_block = event_filter_params.get("fromBlock")
          filter_builder.to_block = event_filter_params.get("toBlock")
          match_any_vals = {
              arg: value
              for arg, value in _filters.items()
              if not is_array_type(filter_builder.args[arg].arg_type)
              and is_list_like(value)
          }
          for arg, value in match_any_vals.items():
              filter_builder.args[arg].match_any(*value)
  
          match_single_vals = {
              arg: value
              for arg, value in _filters.items()
              if not is_array_type(filter_builder.args[arg].arg_type)
              and not is_list_like(value)
          }
          for arg, value in match_single_vals.items():
              filter_builder.args[arg].match_single(value)
  
  
  class BaseContractEvents(Generic[TContractEvent]):
      """
      Class containing contract event objects
  
      This is available via:
  
      .. code-block:: python
  
          >>> mycontract.events
          <web3.contract.ContractEvents object at 0x108afde10>
  
      To get list of all supported events in the contract ABI.
      This allows you to iterate over :class:`ContractEvent` proxy classes.
  
      .. code-block:: python
  
          >>> for e in mycontract.events: print(e)
          <class 'web3._utils.datatypes.LogAnonymous'>
          ...
  
      """
  
      def __init__(
          self,
          abi: ABI,
          w3: Union["Web3", "AsyncWeb3[Any]"],
          contract_event_type: type[TContractEvent],
          address: ChecksumAddress | None = None,
      ) -> None:
          self.abi = abi
          self.w3 = w3
          self.address = address
          self.contract_event_type = contract_event_type
          _events: Sequence[ABIEvent] = None
  
          if self.abi:
              _events = sorted(
                  filter_abi_by_type("event", self.abi),
                  key=lambda evt: (evt["name"], len(evt.get("inputs", []))),
              )
              for event in _events:
                  abi_signature = abi_to_signature(event)
                  event_factory = contract_event_type.factory(
                      abi_signature,
                      w3=self.w3,
                      contract_abi=self.abi,
                      address=self.address,
                      abi=event,
                  )
  
                  # Set event name on instance if it does not already exist
                  if event["name"] not in self.__dict__:
                      setattr(self, event["name"], event_factory)
  
                  # Set underscore prefixed event signature on instance
                  # Handles ambiguity in overloaded contract events
                  setattr(self, f"_{abi_signature}", event_factory)
  
          if _events:
              self._events = _events
  
      def __hasattr__(self, event_name: str) -> bool:
          try:
              return event_name in self.__dict__["_events"]
          except ABIEventNotFound:
              return False
  
      def __getattr__(self, event_name: str) -> TContractEvent:
          if super().__getattribute__("abi") is None:
              raise NoABIFound(
                  "There is no ABI found for this contract.",
              )
          elif "_events" not in self.__dict__ or len(self._events) == 0:
              raise NoABIEventsFound(
                  "The abi for this contract contains no event definitions. ",
                  "Are you sure you provided the correct contract abi?",
              )
          elif get_name_from_abi_element_identifier(event_name) not in [
              get_name_from_abi_element_identifier(event["name"])
              for event in self._events
          ]:
              raise ABIEventNotFound(
                  f"The event '{event_name}' was not found in this contract's abi. ",
                  "Are you sure you provided the correct contract abi?",
              )
  
          if "(" not in event_name:
              event_name = _get_any_abi_signature_with_name(event_name, self._events)
          else:
              event_name = f"_{event_name}"
  
          return super().__getattribute__(event_name)
  
      def __getitem__(self, event_name: str) -> TContractEvent:
          return getattr(self, event_name)
  
      def __iter__(self) -> Iterable[TContractEvent]:
          if not hasattr(self, "_events") or not self._events:
              return
  
          for event in self._events:
              yield self[abi_to_signature(event)]
  
  
  class BaseContractFunction:
      """
      Base class for contract functions
  
      A function accessed via the api `contract.functions.myMethod(*args, **kwargs)`
      is a subclass of this class.
      """
  
      address: ChecksumAddress = None
      fn_name: str = None
      name: str = None
      signature: str = None
      abi_element_identifier: ABIElementIdentifier = None
      w3: Union["Web3", "AsyncWeb3[Any]"] = None
      contract_abi: ABI = None
      abi: ABIFunction = None
      transaction: TxParams = None
      arguments: tuple[Any, ...] = None
      decode_tuples: bool | None = None
      argument_names: tuple[str, ...] = tuple()
      argument_types: tuple[str, ...] = tuple()
      args: Any = None
      kwargs: Any = None
  
      def __init__(self, abi: ABIFunction | None = None) -> None:
          if not self.abi_element_identifier:
              self.abi_element_identifier = type(self).__name__
  
          self.name = get_name_from_abi_element_identifier(self.abi_element_identifier)
          self.fn_name = self.name
  
          if abi:
              self.abi = abi
  
          self.signature = abi_to_signature(self.abi)
  
          event_inputs = self.abi.get("inputs", [])
          self.argument_names = tuple([input.get("name", None) for input in event_inputs])
          self.argument_types = tuple([input["type"] for input in event_inputs])
  
      @combomethod
      def _get_abi(cls) -> ABIFunction:
          if not cls.args and not cls.kwargs:
              # If no args or kwargs are provided, get the ABI element by name
              return cast(
                  ABIFunction,
                  get_abi_element(
                      cls.contract_abi,
                      get_abi_element_signature(cls.abi_element_identifier),
                      abi_codec=cls.w3.codec,
                  ),
              )
  
          return cast(
              ABIFunction,
              get_abi_element(
                  cls.contract_abi,
                  get_name_from_abi_element_identifier(cls.abi_element_identifier),
                  *cls.args,
                  abi_codec=cls.w3.codec,
                  **cls.kwargs,
              ),
          )
  
      def _set_function_info(self) -> None:
          self.selector = encode_hex(b"")
          if self.abi_element_identifier in [
              "fallback",
              "receive",
              FallbackFn,
              ReceiveFn,
          ]:
              self.selector = encode_hex(function_abi_to_4byte_selector(self.abi))
              self.arguments = None
          elif is_text(self.abi_element_identifier):
              self.selector = encode_hex(function_abi_to_4byte_selector(self.abi))
              self.arguments = get_normalized_abi_inputs(
                  self.abi, *self.args, **self.kwargs
              )
          else:
              raise Web3TypeError("Unsupported function identifier")
  
      def _get_call_txparams(self, transaction: TxParams | None = None) -> TxParams:
          if transaction is None:
              call_transaction: TxParams = {}
          else:
              call_transaction = cast(TxParams, dict(**transaction))
  
          if "data" in call_transaction:
              raise Web3ValueError("Cannot set 'data' field in call transaction")
  
          if self.address:
              call_transaction.setdefault("to", self.address)
          if self.w3.eth.default_account is not empty:
              call_transaction.setdefault(
                  "from",
                  cast(ChecksumAddress, self.w3.eth.default_account),
              )
  
          if "to" not in call_transaction:
              if isinstance(self, type):
                  raise Web3ValueError(
                      "When using `Contract.[methodtype].[method].call()` from"
                      " a contract factory you "
                      "must provide a `to` address with the transaction"
                  )
              else:
                  raise Web3ValueError(
                      "Please ensure that this contract instance has an address."
                  )
  
          return call_transaction
  
      def _transact(self, transaction: TxParams | None = None) -> TxParams:
          if transaction is None:
              transact_transaction: TxParams = {}
          else:
              transact_transaction = cast(TxParams, dict(**transaction))
  
          if "data" in transact_transaction:
              raise Web3ValueError("Cannot set 'data' field in transact transaction")
  
          if self.address is not None:
              transact_transaction.setdefault("to", self.address)
          if self.w3.eth.default_account is not empty:
              transact_transaction.setdefault(
                  "from", cast(ChecksumAddress, self.w3.eth.default_account)
              )
  
          if "to" not in transact_transaction:
              if isinstance(self, type):
                  raise Web3ValueError(
                      "When using `Contract.transact` from a contract factory you "
                      "must provide a `to` address with the transaction"
                  )
              else:
                  raise Web3ValueError(
                      "Please ensure that this contract instance has an address."
                  )
          return transact_transaction
  
      def _estimate_gas(self, transaction: TxParams | None = None) -> TxParams:
          if transaction is None:
              estimate_gas_transaction: TxParams = {}
          else:
              estimate_gas_transaction = cast(TxParams, dict(**transaction))
  
          if "data" in estimate_gas_transaction:
              raise Web3ValueError("Cannot set 'data' field in estimate_gas transaction")
          if "to" in estimate_gas_transaction:
              raise Web3ValueError("Cannot set to in estimate_gas transaction")
  
          if self.address:
              estimate_gas_transaction.setdefault("to", self.address)
          if self.w3.eth.default_account is not empty:
              estimate_gas_transaction.setdefault(
                  "from", cast(ChecksumAddress, self.w3.eth.default_account)
              )
  
          if "to" not in estimate_gas_transaction:
              if isinstance(self, type):
                  raise Web3ValueError(
                      "When using `Contract.estimate_gas` from a contract factory "
                      "you must provide a `to` address with the transaction"
                  )
              else:
                  raise Web3ValueError(
                      "Please ensure that this contract instance has an address."
                  )
          return estimate_gas_transaction
  
      def _build_transaction(self, transaction: TxParams | None = None) -> TxParams:
          if transaction is None:
              built_transaction: TxParams = {}
          else:
              built_transaction = cast(TxParams, dict(**transaction))
  
          if "data" in built_transaction:
              raise Web3ValueError("Cannot set 'data' field in build transaction")
  
          if not self.address and "to" not in built_transaction:
              raise Web3ValueError(
                  "When using `ContractFunction.build_transaction` from a contract "
                  "factory you must provide a `to` address with the transaction"
              )
          if self.address and "to" in built_transaction:
              raise Web3ValueError(
                  "Cannot set 'to' field in contract call build transaction"
              )
  
          if self.address:
              built_transaction.setdefault("to", self.address)
  
          if "to" not in built_transaction:
              raise Web3ValueError(
                  "Please ensure that this contract instance has an address."
              )
  
          return built_transaction
  
      @combomethod
      def _encode_transaction_data(cls) -> HexStr:
          return add_0x_prefix(encode_abi(cls.w3, cls.abi, cls.arguments, cls.selector))
  
      _return_data_normalizers: tuple[Callable[..., Any], ...] | None = tuple()
  
      def __repr__(self) -> str:
          if self.abi:
              _repr = f"<Function {abi_to_signature(self.abi)}"
              if self.arguments is not None:
                  _repr += f" bound to {self.arguments!r}"
              return _repr + ">"
          return f"<Function {get_abi_element_signature(self.abi_element_identifier)}>"
  
      def __call__(self, *args: Any, **kwargs: Any) -> Self:
          # When a function is called, check arguments to obtain the correct function
          # in the contract. self will be used if all args and kwargs are
          # encodable to self.abi, otherwise the correct function is obtained from
          # the contract.
          if (
              self.abi_element_identifier in [FallbackFn, ReceiveFn]
              or self.abi_element_identifier == "constructor"
          ):
              return copy_contract_function(self, *args, **kwargs)
  
          all_functions = cast(
              list[ABIFunction],
              filter_abi_by_type(
                  "function",
                  self.contract_abi,
              ),
          )
          # Filter functions by name to obtain function signatures
          function_name = get_name_from_abi_element_identifier(
              self.abi_element_identifier
          )
          function_abis = [
              function for function in all_functions if function["name"] == function_name
          ]
          num_args = len(args) + len(kwargs)
          function_abis_with_arg_count = cast(
              list[ABIFunction],
              _filter_by_argument_count(
                  num_args,
                  function_abis,
              ),
          )
  
          if not len(function_abis_with_arg_count):
              # Build an ABI without arguments to determine if one exists
              function_abis_with_arg_count = [
                  ABIFunction({"type": "function", "name": function_name})
              ]
  
          function_abi_matches = []
          contract_function = None
          for abi in function_abis_with_arg_count:
              try:
                  # Search for a function ABI that matches the arguments used
                  function_abi_matches.append(
                      cast(
                          ABIFunction,
                          get_abi_element(
                              function_abis,
                              abi_to_signature(abi),
                              *args,
                              abi_codec=self.w3.codec,
                              **kwargs,
                          ),
                      )
                  )
              except MismatchedABI:
                  # ignore exceptions
                  continue
  
          if len(function_abi_matches) == 1:
              function_abi = function_abi_matches[0]
              if abi_to_signature(self.abi) == abi_to_signature(function_abi):
                  contract_function = self
              else:
                  # Found a match that is not self
                  contract_function = self.__class__.factory(
                      abi_to_signature(function_abi),
                      w3=self.w3,
                      contract_abi=self.contract_abi,
                      address=self.address,
                      abi_element_identifier=abi_to_signature(function_abi),
                      abi=function_abi,
                  )
          else:
              for abi in function_abi_matches:
                  if abi_to_signature(self.abi) == abi_to_signature(abi):
                      contract_function = self
                      break
              else:
                  # Raise exception if multiple found
                  raise MismatchedABI(
                      _mismatched_abi_error_diagnosis(
                          function_name,
                          self.contract_abi,
                          len(function_abi_matches),
                          num_args,
                          *args,
                          abi_codec=self.w3.codec,
                          **kwargs,
                      )
                  )
  
          return copy_contract_function(contract_function, *args, **kwargs)
  
      @classmethod
      def factory(cls, class_name: str, **kwargs: Any) -> Self:
          return PropertyCheckingFactory(class_name, (cls,), kwargs)()
  
      def call(
          self,
          transaction: TxParams | None = None,
          block_identifier: BlockIdentifier | None = None,
          state_override: StateOverride | None = None,
          ccip_read_enabled: bool | None = None,
      ) -> Any:
          """
          Implementation of ``call`` should create a callable contract function
          and execute it using the `eth_call` interface.
          """
          raise NotImplementedError(
              "This method should be implemented in the inherited class"
          )
  
  
  class BaseContractFunctions(Generic[TContractFn]):
      """Class containing contract function objects"""
  
      _functions: Sequence[ABIFunction] = None
  
      def __init__(
          self,
          abi: ABI,
          w3: Union["Web3", "AsyncWeb3[Any]"],
          contract_function_class: type[TContractFn],
          address: ChecksumAddress | None = None,
          decode_tuples: bool | None = False,
      ) -> None:
          self.abi = abi
          self.w3 = w3
          self.address = address
          _functions: Sequence[ABIFunction] = None
  
          if self.abi:
              # Function with least number of inputs is first
              # This ensures ambiguity will always be deterministic
              # Prefer function without arguments if present, otherwise
              # just use the first available
              _functions = sorted(
                  filter_abi_by_type("function", self.abi),
                  key=lambda fn: (fn["name"], len(fn.get("inputs", []))),
              )
              for func in _functions:
                  abi_signature = abi_to_signature(func)
                  function_factory = contract_function_class.factory(
                      abi_signature,
                      w3=self.w3,
                      contract_abi=self.abi,
                      address=self.address,
                      decode_tuples=decode_tuples,
                      abi=func,
                  )
  
                  # Set function name on instance if it does not already exist
                  if func["name"] not in self.__dict__:
                      setattr(self, func["name"], function_factory)
  
                  # Set function signature on instance
                  # Handles ambiguity in overloaded contract functions
                  setattr(self, f"_{abi_signature}", function_factory)
  
          if _functions:
              self._functions = _functions
  
      def __hasattr__(self, function_name: str) -> bool:
          try:
              return function_name in self.__dict__["_functions"]
          except ABIFunctionNotFound:
              return False
  
      def __iter__(self) -> Iterable[TContractFn]:
          if not hasattr(self, "_functions") or not self._functions:
              return
  
          for func in self._functions:
              yield self[abi_to_signature(func)]
  
      def __getattr__(self, function_name: str) -> TContractFn:
          if super().__getattribute__("abi") is None:
              raise NoABIFound(
                  "There is no ABI found for this contract.",
              )
          elif "_functions" not in self.__dict__ or len(self._functions) == 0:
              raise NoABIFunctionsFound(
                  "The abi for this contract contains no function definitions. ",
                  "Are you sure you provided the correct contract abi?",
              )
          elif get_name_from_abi_element_identifier(function_name) not in [
              get_name_from_abi_element_identifier(function["name"])
              for function in self._functions
          ]:
              raise ABIFunctionNotFound(
                  f"The function '{function_name}' was not found in this ",
                  "contract's abi.",
              )
  
          if "(" not in function_name:
              function_name = _get_any_abi_signature_with_name(
                  function_name, self._functions
              )
          else:
  