"""A web3.py Contract class for the Example contract.

DO NOT EDIT.  This file was generated by pypechain v0.0.46.
See documentation at https://github.com/delvtech/pypechain """

# contracts have PascalCase names
# pylint: disable=invalid-name

# contracts control how many attributes and arguments we have in generated code
# pylint: disable=too-many-instance-attributes
# pylint: disable=too-many-arguments

# we don't need else statement if the other conditionals all have return,
# but it's easier to generate
# pylint: disable=no-else-return

# This file is bound to get very long depending on contract sizes.
# pylint: disable=too-many-lines

# methods are overridden with specific arguments instead of generic *args, **kwargs
# pylint: disable=arguments-differ

# consumers have too many opinions on line length
# pylint: disable=line-too-long

# We use protected classes within pypechain
# pylint: disable=protected-access

# We sometimes define a variable that might not be returned in `call`,
# but we still may want to call the function
# pylint: disable=unused-variable


from __future__ import annotations

import copy
from typing import Any, Iterable, NamedTuple, Sequence, Type, cast, overload

from eth_account.signers.local import LocalAccount
from eth_typing import ABI, ChecksumAddress, HexStr
from hexbytes import HexBytes
from typing_extensions import Self
from web3 import Web3
from web3._utils.events import EventLogErrorFlags
from web3._utils.filters import LogFilter
from web3.contract.contract import Contract, ContractConstructor, ContractEvent, ContractEvents, ContractFunctions
from web3.logs import WARN
from web3.types import BlockIdentifier, StateOverride, TxParams, TxReceipt

from pypechain.core import (
    PypechainBaseContractErrors,
    PypechainBaseError,
    PypechainContractFunction,
    check_txn_receipt,
    combomethod_typed,
    dataclass_to_tuple,
    expand_struct_type_str,
    get_arg_type_names,
    handle_contract_logic_error,
    rename_returned_types,
)

from ..Example import ExampleTypes as Example
from .ExampleTypes import FlipEvent, FlopEvent

structs = {
    "Example.SimpleStruct": Example.SimpleStruct,
    "Example.InnerStruct": Example.InnerStruct,
    "Example.NestedStruct": Example.NestedStruct,
}


class ExampleFlipFlopContractFunction0(PypechainContractFunction):
    """ContractFunction for the flipFlop(int,int) method."""

    _function_name = "flipFlop"
    _type_signature = expand_struct_type_str(tuple(["int", "int"]), structs)

    class ReturnValues(NamedTuple):
        """The return named tuple for FlipFlop."""

        flop: int
        flip: int

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> ReturnValues:
        """returns ReturnValues."""
        # We handle the block identifier = None case here for typing.
        if block_identifier is None:
            block_identifier = self.w3.eth.default_block

        # Define the expected return types from the smart contract call
        return_types = [int, int]

        # Call the function
        try:
            raw_values = super().call(transaction, block_identifier, state_override, ccip_read_enabled)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="call",
                transaction=transaction,
                block_identifier=block_identifier,
            ) from err

        return self.ReturnValues(*rename_returned_types(structs, return_types, raw_values))

    def transact(self, transaction: TxParams | None = None) -> HexBytes:
        try:
            return super().transact(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def estimate_gas(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
    ) -> int:
        try:
            return super().estimate_gas(transaction, block_identifier, state_override)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def build_transaction(self, transaction: TxParams | None = None) -> TxParams:
        try:
            return super().build_transaction(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_and_transact(self, account: LocalAccount, transaction: TxParams | None = None) -> HexBytes:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.

        Returns
        -------
        HexBytes
            The transaction hash.
        """
        if transaction is None:
            transaction_params: TxParams = {}
        else:
            transaction_params: TxParams = transaction

        if "from" in transaction_params:
            # Ensure if transaction is set, it matches
            assert (
                transaction_params["from"] == account.address
            ), f"Transaction from {transaction_params['from']} does not match account {account.address}"
        else:
            transaction_params["from"] = account.address

        if "gas" not in transaction_params:
            # Web3 default gas estimate seems to be underestimating gas, likely due to
            # not looking at pending block. Here, we explicitly call estimate gas
            # if gas isn't passed in.
            transaction_params["gas"] = self.estimate_gas(transaction_params, block_identifier="pending")

        # Build the raw transaction
        raw_transaction = self.build_transaction(transaction_params)

        if "nonce" not in raw_transaction:
            raw_transaction["nonce"] = self.w3.eth.get_transaction_count(account.address, block_identifier="pending")

        # Sign the raw transaction
        # Mismatched types between account and web3py
        signed_transaction = account.sign_transaction(raw_transaction)  # type: ignore

        # Send the signed transaction
        try:
            return self.w3.eth.send_raw_transaction(signed_transaction.raw_transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction_params,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_transact_and_wait(
        self,
        account: LocalAccount,
        transaction: TxParams | None = None,
        timeout: float | None = None,
        poll_latency: float | None = None,
        validate_transaction: bool = False,
    ) -> TxReceipt:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.
        timeout: float, optional
            The number of seconds to wait for the transaction to be mined. Defaults to 120.
        poll_latency: float, optional
            The number of seconds to wait between polling for the transaction receipt. Defaults to 0.1.
        validate_transaction: bool, optional
            Whether to validate the transaction. If True, will throw an exception if the resulting
            tx_receipt returned a failure status.

        Returns
        -------
        HexBytes
            The transaction hash.
        """

        # pylint: disable=too-many-arguments
        # pylint: disable=too-many-positional-arguments

        if timeout is None:
            timeout = 120
        if poll_latency is None:
            poll_latency = 0.1

        tx_hash = self.sign_and_transact(account, transaction)
        tx_receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=timeout, poll_latency=poll_latency)
        # Check the receipt, throwing an error if status == 0
        if validate_transaction:
            return check_txn_receipt(self, tx_hash, tx_receipt)
        else:
            return tx_receipt


class ExampleFlipFlopContractFunction(PypechainContractFunction):
    """ContractFunction for the flipFlop method."""

    # super() call methods are generic, while our version adds values & types
    # pylint: disable=arguments-differ# disable this warning when there is overloading
    # pylint: disable=function-redefined

    _function_name = "flipFlop"

    # Make lookup for function signature -> overloaded function
    # The function signatures are python types, as we need to do a
    # lookup of arguments passed in to contract function
    _functions: dict[str, PypechainContractFunction]

    @overload
    def __call__(self, flip: int, flop: int) -> ExampleFlipFlopContractFunction0:  # type: ignore
        ...

    def __call__(self, *args, **kwargs) -> ExampleFlipFlopContractFunction:  # type: ignore
        clone = super().__call__(
            *(dataclass_to_tuple(arg) for arg in args), **{key: dataclass_to_tuple(arg) for key, arg in kwargs.items()}
        )

        # Arguments is the flattened set of arguments from args and kwargs, ordered by the abi
        # We get the python types of the args passed in, but remapped from tuples -> dataclasses
        arg_types = get_arg_type_names(clone.arguments)

        # Look up the function class based on arg types.
        # We ensure we use a copy of the original object.
        function_obj = copy.copy(self._functions[arg_types])

        function_obj.args = clone.args
        function_obj.kwargs = clone.kwargs

        # The `@overload` of `__call__` takes care of setting the type of this object correctly
        return function_obj  # type: ignore

    @classmethod
    def factory(cls, class_name: str, **kwargs: Any) -> Self:
        out = super().factory(class_name, **kwargs)

        # We initialize our overridden functions here.
        # Note that we use the initialized object to ensure each function
        # is attached to the instanciated object
        # (attached to a specific web3 and contract address)
        out._functions = {
            ExampleFlipFlopContractFunction0._type_signature: ExampleFlipFlopContractFunction0.factory(
                "ExampleFlipFlopContractFunction0", **kwargs
            ),
        }
        return out


class ExampleGuessALetterContractFunction0(PypechainContractFunction):
    """ContractFunction for the guessALetter(int) method."""

    _function_name = "guessALetter"
    _type_signature = expand_struct_type_str(tuple(["int"]), structs)

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> None:
        """returns None."""
        # We handle the block identifier = None case here for typing.
        if block_identifier is None:
            block_identifier = self.w3.eth.default_block

        # Call the function
        try:
            raw_values = super().call(transaction, block_identifier, state_override, ccip_read_enabled)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="call",
                transaction=transaction,
                block_identifier=block_identifier,
            ) from err

    def transact(self, transaction: TxParams | None = None) -> HexBytes:
        try:
            return super().transact(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def estimate_gas(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
    ) -> int:
        try:
            return super().estimate_gas(transaction, block_identifier, state_override)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def build_transaction(self, transaction: TxParams | None = None) -> TxParams:
        try:
            return super().build_transaction(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_and_transact(self, account: LocalAccount, transaction: TxParams | None = None) -> HexBytes:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.

        Returns
        -------
        HexBytes
            The transaction hash.
        """
        if transaction is None:
            transaction_params: TxParams = {}
        else:
            transaction_params: TxParams = transaction

        if "from" in transaction_params:
            # Ensure if transaction is set, it matches
            assert (
                transaction_params["from"] == account.address
            ), f"Transaction from {transaction_params['from']} does not match account {account.address}"
        else:
            transaction_params["from"] = account.address

        if "gas" not in transaction_params:
            # Web3 default gas estimate seems to be underestimating gas, likely due to
            # not looking at pending block. Here, we explicitly call estimate gas
            # if gas isn't passed in.
            transaction_params["gas"] = self.estimate_gas(transaction_params, block_identifier="pending")

        # Build the raw transaction
        raw_transaction = self.build_transaction(transaction_params)

        if "nonce" not in raw_transaction:
            raw_transaction["nonce"] = self.w3.eth.get_transaction_count(account.address, block_identifier="pending")

        # Sign the raw transaction
        # Mismatched types between account and web3py
        signed_transaction = account.sign_transaction(raw_transaction)  # type: ignore

        # Send the signed transaction
        try:
            return self.w3.eth.send_raw_transaction(signed_transaction.raw_transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction_params,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_transact_and_wait(
        self,
        account: LocalAccount,
        transaction: TxParams | None = None,
        timeout: float | None = None,
        poll_latency: float | None = None,
        validate_transaction: bool = False,
    ) -> TxReceipt:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.
        timeout: float, optional
            The number of seconds to wait for the transaction to be mined. Defaults to 120.
        poll_latency: float, optional
            The number of seconds to wait between polling for the transaction receipt. Defaults to 0.1.
        validate_transaction: bool, optional
            Whether to validate the transaction. If True, will throw an exception if the resulting
            tx_receipt returned a failure status.

        Returns
        -------
        HexBytes
            The transaction hash.
        """

        # pylint: disable=too-many-arguments
        # pylint: disable=too-many-positional-arguments

        if timeout is None:
            timeout = 120
        if poll_latency is None:
            poll_latency = 0.1

        tx_hash = self.sign_and_transact(account, transaction)
        tx_receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=timeout, poll_latency=poll_latency)
        # Check the receipt, throwing an error if status == 0
        if validate_transaction:
            return check_txn_receipt(self, tx_hash, tx_receipt)
        else:
            return tx_receipt


class ExampleGuessALetterContractFunction(PypechainContractFunction):
    """ContractFunction for the guessALetter method."""

    # super() call methods are generic, while our version adds values & types
    # pylint: disable=arguments-differ# disable this warning when there is overloading
    # pylint: disable=function-redefined

    _function_name = "guessALetter"

    # Make lookup for function signature -> overloaded function
    # The function signatures are python types, as we need to do a
    # lookup of arguments passed in to contract function
    _functions: dict[str, PypechainContractFunction]

    @overload
    def __call__(self, guess: int) -> ExampleGuessALetterContractFunction0:  # type: ignore
        ...

    def __call__(self, *args, **kwargs) -> ExampleGuessALetterContractFunction:  # type: ignore
        clone = super().__call__(
            *(dataclass_to_tuple(arg) for arg in args), **{key: dataclass_to_tuple(arg) for key, arg in kwargs.items()}
        )

        # Arguments is the flattened set of arguments from args and kwargs, ordered by the abi
        # We get the python types of the args passed in, but remapped from tuples -> dataclasses
        arg_types = get_arg_type_names(clone.arguments)

        # Look up the function class based on arg types.
        # We ensure we use a copy of the original object.
        function_obj = copy.copy(self._functions[arg_types])

        function_obj.args = clone.args
        function_obj.kwargs = clone.kwargs

        # The `@overload` of `__call__` takes care of setting the type of this object correctly
        return function_obj  # type: ignore

    @classmethod
    def factory(cls, class_name: str, **kwargs: Any) -> Self:
        out = super().factory(class_name, **kwargs)

        # We initialize our overridden functions here.
        # Note that we use the initialized object to ensure each function
        # is attached to the instanciated object
        # (attached to a specific web3 and contract address)
        out._functions = {
            ExampleGuessALetterContractFunction0._type_signature: ExampleGuessALetterContractFunction0.factory(
                "ExampleGuessALetterContractFunction0", **kwargs
            ),
        }
        return out


class ExampleMixStructsAndPrimitivesContractFunction0(PypechainContractFunction):
    """ContractFunction for the mixStructsAndPrimitives() method."""

    _function_name = "mixStructsAndPrimitives"
    _type_signature = expand_struct_type_str(tuple([]), structs)

    class ReturnValues(NamedTuple):
        """The return named tuple for MixStructsAndPrimitives."""

        simpleStruct: Example.SimpleStruct
        arg2: Example.NestedStruct
        arg3: int
        name: str
        YesOrNo: bool

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> ReturnValues:
        """returns ReturnValues."""
        # We handle the block identifier = None case here for typing.
        if block_identifier is None:
            block_identifier = self.w3.eth.default_block

        # Define the expected return types from the smart contract call
        return_types = [Example.SimpleStruct, Example.NestedStruct, int, str, bool]

        # Call the function
        try:
            raw_values = super().call(transaction, block_identifier, state_override, ccip_read_enabled)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="call",
                transaction=transaction,
                block_identifier=block_identifier,
            ) from err

        return self.ReturnValues(*rename_returned_types(structs, return_types, raw_values))

    def transact(self, transaction: TxParams | None = None) -> HexBytes:
        try:
            return super().transact(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def estimate_gas(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
    ) -> int:
        try:
            return super().estimate_gas(transaction, block_identifier, state_override)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def build_transaction(self, transaction: TxParams | None = None) -> TxParams:
        try:
            return super().build_transaction(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_and_transact(self, account: LocalAccount, transaction: TxParams | None = None) -> HexBytes:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.

        Returns
        -------
        HexBytes
            The transaction hash.
        """
        if transaction is None:
            transaction_params: TxParams = {}
        else:
            transaction_params: TxParams = transaction

        if "from" in transaction_params:
            # Ensure if transaction is set, it matches
            assert (
                transaction_params["from"] == account.address
            ), f"Transaction from {transaction_params['from']} does not match account {account.address}"
        else:
            transaction_params["from"] = account.address

        if "gas" not in transaction_params:
            # Web3 default gas estimate seems to be underestimating gas, likely due to
            # not looking at pending block. Here, we explicitly call estimate gas
            # if gas isn't passed in.
            transaction_params["gas"] = self.estimate_gas(transaction_params, block_identifier="pending")

        # Build the raw transaction
        raw_transaction = self.build_transaction(transaction_params)

        if "nonce" not in raw_transaction:
            raw_transaction["nonce"] = self.w3.eth.get_transaction_count(account.address, block_identifier="pending")

        # Sign the raw transaction
        # Mismatched types between account and web3py
        signed_transaction = account.sign_transaction(raw_transaction)  # type: ignore

        # Send the signed transaction
        try:
            return self.w3.eth.send_raw_transaction(signed_transaction.raw_transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction_params,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_transact_and_wait(
        self,
        account: LocalAccount,
        transaction: TxParams | None = None,
        timeout: float | None = None,
        poll_latency: float | None = None,
        validate_transaction: bool = False,
    ) -> TxReceipt:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.
        timeout: float, optional
            The number of seconds to wait for the transaction to be mined. Defaults to 120.
        poll_latency: float, optional
            The number of seconds to wait between polling for the transaction receipt. Defaults to 0.1.
        validate_transaction: bool, optional
            Whether to validate the transaction. If True, will throw an exception if the resulting
            tx_receipt returned a failure status.

        Returns
        -------
        HexBytes
            The transaction hash.
        """

        # pylint: disable=too-many-arguments
        # pylint: disable=too-many-positional-arguments

        if timeout is None:
            timeout = 120
        if poll_latency is None:
            poll_latency = 0.1

        tx_hash = self.sign_and_transact(account, transaction)
        tx_receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=timeout, poll_latency=poll_latency)
        # Check the receipt, throwing an error if status == 0
        if validate_transaction:
            return check_txn_receipt(self, tx_hash, tx_receipt)
        else:
            return tx_receipt


class ExampleMixStructsAndPrimitivesContractFunction(PypechainContractFunction):
    """ContractFunction for the mixStructsAndPrimitives method."""

    # super() call methods are generic, while our version adds values & types
    # pylint: disable=arguments-differ# disable this warning when there is overloading
    # pylint: disable=function-redefined

    _function_name = "mixStructsAndPrimitives"

    # Make lookup for function signature -> overloaded function
    # The function signatures are python types, as we need to do a
    # lookup of arguments passed in to contract function
    _functions: dict[str, PypechainContractFunction]

    @overload
    def __call__(self) -> ExampleMixStructsAndPrimitivesContractFunction0:  # type: ignore
        ...

    def __call__(self, *args, **kwargs) -> ExampleMixStructsAndPrimitivesContractFunction:  # type: ignore
        clone = super().__call__(
            *(dataclass_to_tuple(arg) for arg in args), **{key: dataclass_to_tuple(arg) for key, arg in kwargs.items()}
        )

        # Arguments is the flattened set of arguments from args and kwargs, ordered by the abi
        # We get the python types of the args passed in, but remapped from tuples -> dataclasses
        arg_types = get_arg_type_names(clone.arguments)

        # Look up the function class based on arg types.
        # We ensure we use a copy of the original object.
        function_obj = copy.copy(self._functions[arg_types])

        function_obj.args = clone.args
        function_obj.kwargs = clone.kwargs

        # The `@overload` of `__call__` takes care of setting the type of this object correctly
        return function_obj  # type: ignore

    @classmethod
    def factory(cls, class_name: str, **kwargs: Any) -> Self:
        out = super().factory(class_name, **kwargs)

        # We initialize our overridden functions here.
        # Note that we use the initialized object to ensure each function
        # is attached to the instanciated object
        # (attached to a specific web3 and contract address)
        out._functions = {
            ExampleMixStructsAndPrimitivesContractFunction0._type_signature: ExampleMixStructsAndPrimitivesContractFunction0.factory(
                "ExampleMixStructsAndPrimitivesContractFunction0", **kwargs
            ),
        }
        return out


class ExampleNamedSingleStructContractFunction0(PypechainContractFunction):
    """ContractFunction for the namedSingleStruct() method."""

    _function_name = "namedSingleStruct"
    _type_signature = expand_struct_type_str(tuple([]), structs)

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> Example.SimpleStruct:
        """returns Example.SimpleStruct."""
        # We handle the block identifier = None case here for typing.
        if block_identifier is None:
            block_identifier = self.w3.eth.default_block

        # Define the expected return types from the smart contract call
        return_types = Example.SimpleStruct

        # Call the function
        try:
            raw_values = super().call(transaction, block_identifier, state_override, ccip_read_enabled)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="call",
                transaction=transaction,
                block_identifier=block_identifier,
            ) from err

        return cast(Example.SimpleStruct, rename_returned_types(structs, return_types, raw_values))

    def transact(self, transaction: TxParams | None = None) -> HexBytes:
        try:
            return super().transact(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def estimate_gas(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
    ) -> int:
        try:
            return super().estimate_gas(transaction, block_identifier, state_override)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def build_transaction(self, transaction: TxParams | None = None) -> TxParams:
        try:
            return super().build_transaction(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_and_transact(self, account: LocalAccount, transaction: TxParams | None = None) -> HexBytes:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.

        Returns
        -------
        HexBytes
            The transaction hash.
        """
        if transaction is None:
            transaction_params: TxParams = {}
        else:
            transaction_params: TxParams = transaction

        if "from" in transaction_params:
            # Ensure if transaction is set, it matches
            assert (
                transaction_params["from"] == account.address
            ), f"Transaction from {transaction_params['from']} does not match account {account.address}"
        else:
            transaction_params["from"] = account.address

        if "gas" not in transaction_params:
            # Web3 default gas estimate seems to be underestimating gas, likely due to
            # not looking at pending block. Here, we explicitly call estimate gas
            # if gas isn't passed in.
            transaction_params["gas"] = self.estimate_gas(transaction_params, block_identifier="pending")

        # Build the raw transaction
        raw_transaction = self.build_transaction(transaction_params)

        if "nonce" not in raw_transaction:
            raw_transaction["nonce"] = self.w3.eth.get_transaction_count(account.address, block_identifier="pending")

        # Sign the raw transaction
        # Mismatched types between account and web3py
        signed_transaction = account.sign_transaction(raw_transaction)  # type: ignore

        # Send the signed transaction
        try:
            return self.w3.eth.send_raw_transaction(signed_transaction.raw_transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction_params,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_transact_and_wait(
        self,
        account: LocalAccount,
        transaction: TxParams | None = None,
        timeout: float | None = None,
        poll_latency: float | None = None,
        validate_transaction: bool = False,
    ) -> TxReceipt:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.
        timeout: float, optional
            The number of seconds to wait for the transaction to be mined. Defaults to 120.
        poll_latency: float, optional
            The number of seconds to wait between polling for the transaction receipt. Defaults to 0.1.
        validate_transaction: bool, optional
            Whether to validate the transaction. If True, will throw an exception if the resulting
            tx_receipt returned a failure status.

        Returns
        -------
        HexBytes
            The transaction hash.
        """

        # pylint: disable=too-many-arguments
        # pylint: disable=too-many-positional-arguments

        if timeout is None:
            timeout = 120
        if poll_latency is None:
            poll_latency = 0.1

        tx_hash = self.sign_and_transact(account, transaction)
        tx_receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=timeout, poll_latency=poll_latency)
        # Check the receipt, throwing an error if status == 0
        if validate_transaction:
            return check_txn_receipt(self, tx_hash, tx_receipt)
        else:
            return tx_receipt


class ExampleNamedSingleStructContractFunction(PypechainContractFunction):
    """ContractFunction for the namedSingleStruct method."""

    # super() call methods are generic, while our version adds values & types
    # pylint: disable=arguments-differ# disable this warning when there is overloading
    # pylint: disable=function-redefined

    _function_name = "namedSingleStruct"

    # Make lookup for function signature -> overloaded function
    # The function signatures are python types, as we need to do a
    # lookup of arguments passed in to contract function
    _functions: dict[str, PypechainContractFunction]

    @overload
    def __call__(self) -> ExampleNamedSingleStructContractFunction0:  # type: ignore
        ...

    def __call__(self, *args, **kwargs) -> ExampleNamedSingleStructContractFunction:  # type: ignore
        clone = super().__call__(
            *(dataclass_to_tuple(arg) for arg in args), **{key: dataclass_to_tuple(arg) for key, arg in kwargs.items()}
        )

        # Arguments is the flattened set of arguments from args and kwargs, ordered by the abi
        # We get the python types of the args passed in, but remapped from tuples -> dataclasses
        arg_types = get_arg_type_names(clone.arguments)

        # Look up the function class based on arg types.
        # We ensure we use a copy of the original object.
        function_obj = copy.copy(self._functions[arg_types])

        function_obj.args = clone.args
        function_obj.kwargs = clone.kwargs

        # The `@overload` of `__call__` takes care of setting the type of this object correctly
        return function_obj  # type: ignore

    @classmethod
    def factory(cls, class_name: str, **kwargs: Any) -> Self:
        out = super().factory(class_name, **kwargs)

        # We initialize our overridden functions here.
        # Note that we use the initialized object to ensure each function
        # is attached to the instanciated object
        # (attached to a specific web3 and contract address)
        out._functions = {
            ExampleNamedSingleStructContractFunction0._type_signature: ExampleNamedSingleStructContractFunction0.factory(
                "ExampleNamedSingleStructContractFunction0", **kwargs
            ),
        }
        return out


class ExampleNamedTwoMixedStructsContractFunction0(PypechainContractFunction):
    """ContractFunction for the namedTwoMixedStructs() method."""

    _function_name = "namedTwoMixedStructs"
    _type_signature = expand_struct_type_str(tuple([]), structs)

    class ReturnValues(NamedTuple):
        """The return named tuple for NamedTwoMixedStructs."""

        simpleStruct: Example.SimpleStruct
        nestedStruct: Example.NestedStruct

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> ReturnValues:
        """returns ReturnValues."""
        # We handle the block identifier = None case here for typing.
        if block_identifier is None:
            block_identifier = self.w3.eth.default_block

        # Define the expected return types from the smart contract call
        return_types = [Example.SimpleStruct, Example.NestedStruct]

        # Call the function
        try:
            raw_values = super().call(transaction, block_identifier, state_override, ccip_read_enabled)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="call",
                transaction=transaction,
                block_identifier=block_identifier,
            ) from err

        return self.ReturnValues(*rename_returned_types(structs, return_types, raw_values))

    def transact(self, transaction: TxParams | None = None) -> HexBytes:
        try:
            return super().transact(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def estimate_gas(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
    ) -> int:
        try:
            return super().estimate_gas(transaction, block_identifier, state_override)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def build_transaction(self, transaction: TxParams | None = None) -> TxParams:
        try:
            return super().build_transaction(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_and_transact(self, account: LocalAccount, transaction: TxParams | None = None) -> HexBytes:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.

        Returns
        -------
        HexBytes
            The transaction hash.
        """
        if transaction is None:
            transaction_params: TxParams = {}
        else:
            transaction_params: TxParams = transaction

        if "from" in transaction_params:
            # Ensure if transaction is set, it matches
            assert (
                transaction_params["from"] == account.address
            ), f"Transaction from {transaction_params['from']} does not match account {account.address}"
        else:
            transaction_params["from"] = account.address

        if "gas" not in transaction_params:
            # Web3 default gas estimate seems to be underestimating gas, likely due to
            # not looking at pending block. Here, we explicitly call estimate gas
            # if gas isn't passed in.
            transaction_params["gas"] = self.estimate_gas(transaction_params, block_identifier="pending")

        # Build the raw transaction
        raw_transaction = self.build_transaction(transaction_params)

        if "nonce" not in raw_transaction:
            raw_transaction["nonce"] = self.w3.eth.get_transaction_count(account.address, block_identifier="pending")

        # Sign the raw transaction
        # Mismatched types between account and web3py
        signed_transaction = account.sign_transaction(raw_transaction)  # type: ignore

        # Send the signed transaction
        try:
            return self.w3.eth.send_raw_transaction(signed_transaction.raw_transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction_params,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_transact_and_wait(
        self,
        account: LocalAccount,
        transaction: TxParams | None = None,
        timeout: float | None = None,
        poll_latency: float | None = None,
        validate_transaction: bool = False,
    ) -> TxReceipt:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.
        timeout: float, optional
            The number of seconds to wait for the transaction to be mined. Defaults to 120.
        poll_latency: float, optional
            The number of seconds to wait between polling for the transaction receipt. Defaults to 0.1.
        validate_transaction: bool, optional
            Whether to validate the transaction. If True, will throw an exception if the resulting
            tx_receipt returned a failure status.

        Returns
        -------
        HexBytes
            The transaction hash.
        """

        # pylint: disable=too-many-arguments
        # pylint: disable=too-many-positional-arguments

        if timeout is None:
            timeout = 120
        if poll_latency is None:
            poll_latency = 0.1

        tx_hash = self.sign_and_transact(account, transaction)
        tx_receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=timeout, poll_latency=poll_latency)
        # Check the receipt, throwing an error if status == 0
        if validate_transaction:
            return check_txn_receipt(self, tx_hash, tx_receipt)
        else:
            return tx_receipt


class ExampleNamedTwoMixedStructsContractFunction(PypechainContractFunction):
    """ContractFunction for the namedTwoMixedStructs method."""

    # super() call methods are generic, while our version adds values & types
    # pylint: disable=arguments-differ# disable this warning when there is overloading
    # pylint: disable=function-redefined

    _function_name = "namedTwoMixedStructs"

    # Make lookup for function signature -> overloaded function
    # The function signatures are python types, as we need to do a
    # lookup of arguments passed in to contract function
    _functions: dict[str, PypechainContractFunction]

    @overload
    def __call__(self) -> ExampleNamedTwoMixedStructsContractFunction0:  # type: ignore
        ...

    def __call__(self, *args, **kwargs) -> ExampleNamedTwoMixedStructsContractFunction:  # type: ignore
        clone = super().__call__(
            *(dataclass_to_tuple(arg) for arg in args), **{key: dataclass_to_tuple(arg) for key, arg in kwargs.items()}
        )

        # Arguments is the flattened set of arguments from args and kwargs, ordered by the abi
        # We get the python types of the args passed in, but remapped from tuples -> dataclasses
        arg_types = get_arg_type_names(clone.arguments)

        # Look up the function class based on arg types.
        # We ensure we use a copy of the original object.
        function_obj = copy.copy(self._functions[arg_types])

        function_obj.args = clone.args
        function_obj.kwargs = clone.kwargs

        # The `@overload` of `__call__` takes care of setting the type of this object correctly
        return function_obj  # type: ignore

    @classmethod
    def factory(cls, class_name: str, **kwargs: Any) -> Self:
        out = super().factory(class_name, **kwargs)

        # We initialize our overridden functions here.
        # Note that we use the initialized object to ensure each function
        # is attached to the instanciated object
        # (attached to a specific web3 and contract address)
        out._functions = {
            ExampleNamedTwoMixedStructsContractFunction0._type_signature: ExampleNamedTwoMixedStructsContractFunction0.factory(
                "ExampleNamedTwoMixedStructsContractFunction0", **kwargs
            ),
        }
        return out


class ExampleSingleNestedStructContractFunction0(PypechainContractFunction):
    """ContractFunction for the singleNestedStruct(Example.NestedStruct) method."""

    _function_name = "singleNestedStruct"
    _type_signature = expand_struct_type_str(tuple(["Example.NestedStruct"]), structs)

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> Example.NestedStruct:
        """returns Example.NestedStruct."""
        # We handle the block identifier = None case here for typing.
        if block_identifier is None:
            block_identifier = self.w3.eth.default_block

        # Define the expected return types from the smart contract call
        return_types = Example.NestedStruct

        # Call the function
        try:
            raw_values = super().call(transaction, block_identifier, state_override, ccip_read_enabled)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="call",
                transaction=transaction,
                block_identifier=block_identifier,
            ) from err

        return cast(Example.NestedStruct, rename_returned_types(structs, return_types, raw_values))

    def transact(self, transaction: TxParams | None = None) -> HexBytes:
        try:
            return super().transact(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def estimate_gas(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
    ) -> int:
        try:
            return super().estimate_gas(transaction, block_identifier, state_override)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def build_transaction(self, transaction: TxParams | None = None) -> TxParams:
        try:
            return super().build_transaction(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_and_transact(self, account: LocalAccount, transaction: TxParams | None = None) -> HexBytes:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.

        Returns
        -------
        HexBytes
            The transaction hash.
        """
        if transaction is None:
            transaction_params: TxParams = {}
        else:
            transaction_params: TxParams = transaction

        if "from" in transaction_params:
            # Ensure if transaction is set, it matches
            assert (
                transaction_params["from"] == account.address
            ), f"Transaction from {transaction_params['from']} does not match account {account.address}"
        else:
            transaction_params["from"] = account.address

        if "gas" not in transaction_params:
            # Web3 default gas estimate seems to be underestimating gas, likely due to
            # not looking at pending block. Here, we explicitly call estimate gas
            # if gas isn't passed in.
            transaction_params["gas"] = self.estimate_gas(transaction_params, block_identifier="pending")

        # Build the raw transaction
        raw_transaction = self.build_transaction(transaction_params)

        if "nonce" not in raw_transaction:
            raw_transaction["nonce"] = self.w3.eth.get_transaction_count(account.address, block_identifier="pending")

        # Sign the raw transaction
        # Mismatched types between account and web3py
        signed_transaction = account.sign_transaction(raw_transaction)  # type: ignore

        # Send the signed transaction
        try:
            return self.w3.eth.send_raw_transaction(signed_transaction.raw_transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction_params,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_transact_and_wait(
        self,
        account: LocalAccount,
        transaction: TxParams | None = None,
        timeout: float | None = None,
        poll_latency: float | None = None,
        validate_transaction: bool = False,
    ) -> TxReceipt:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.
        timeout: float, optional
            The number of seconds to wait for the transaction to be mined. Defaults to 120.
        poll_latency: float, optional
            The number of seconds to wait between polling for the transaction receipt. Defaults to 0.1.
        validate_transaction: bool, optional
            Whether to validate the transaction. If True, will throw an exception if the resulting
            tx_receipt returned a failure status.

        Returns
        -------
        HexBytes
            The transaction hash.
        """

        # pylint: disable=too-many-arguments
        # pylint: disable=too-many-positional-arguments

        if timeout is None:
            timeout = 120
        if poll_latency is None:
            poll_latency = 0.1

        tx_hash = self.sign_and_transact(account, transaction)
        tx_receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=timeout, poll_latency=poll_latency)
        # Check the receipt, throwing an error if status == 0
        if validate_transaction:
            return check_txn_receipt(self, tx_hash, tx_receipt)
        else:
            return tx_receipt


class ExampleSingleNestedStructContractFunction(PypechainContractFunction):
    """ContractFunction for the singleNestedStruct method."""

    # super() call methods are generic, while our version adds values & types
    # pylint: disable=arguments-differ# disable this warning when there is overloading
    # pylint: disable=function-redefined

    _function_name = "singleNestedStruct"

    # Make lookup for function signature -> overloaded function
    # The function signatures are python types, as we need to do a
    # lookup of arguments passed in to contract function
    _functions: dict[str, PypechainContractFunction]

    @overload
    def __call__(self, nestedStruct: Example.NestedStruct) -> ExampleSingleNestedStructContractFunction0:  # type: ignore
        ...

    def __call__(self, *args, **kwargs) -> ExampleSingleNestedStructContractFunction:  # type: ignore
        clone = super().__call__(
            *(dataclass_to_tuple(arg) for arg in args), **{key: dataclass_to_tuple(arg) for key, arg in kwargs.items()}
        )

        # Arguments is the flattened set of arguments from args and kwargs, ordered by the abi
        # We get the python types of the args passed in, but remapped from tuples -> dataclasses
        arg_types = get_arg_type_names(clone.arguments)

        # Look up the function class based on arg types.
        # We ensure we use a copy of the original object.
        function_obj = copy.copy(self._functions[arg_types])

        function_obj.args = clone.args
        function_obj.kwargs = clone.kwargs

        # The `@overload` of `__call__` takes care of setting the type of this object correctly
        return function_obj  # type: ignore

    @classmethod
    def factory(cls, class_name: str, **kwargs: Any) -> Self:
        out = super().factory(class_name, **kwargs)

        # We initialize our overridden functions here.
        # Note that we use the initialized object to ensure each function
        # is attached to the instanciated object
        # (attached to a specific web3 and contract address)
        out._functions = {
            ExampleSingleNestedStructContractFunction0._type_signature: ExampleSingleNestedStructContractFunction0.factory(
                "ExampleSingleNestedStructContractFunction0", **kwargs
            ),
        }
        return out


class ExampleSingleSimpleStructContractFunction0(PypechainContractFunction):
    """ContractFunction for the singleSimpleStruct(Example.SimpleStruct) method."""

    _function_name = "singleSimpleStruct"
    _type_signature = expand_struct_type_str(tuple(["Example.SimpleStruct"]), structs)

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> Example.SimpleStruct:
        """returns Example.SimpleStruct."""
        # We handle the block identifier = None case here for typing.
        if block_identifier is None:
            block_identifier = self.w3.eth.default_block

        # Define the expected return types from the smart contract call
        return_types = Example.SimpleStruct

        # Call the function
        try:
            raw_values = super().call(transaction, block_identifier, state_override, ccip_read_enabled)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="call",
                transaction=transaction,
                block_identifier=block_identifier,
            ) from err

        return cast(Example.SimpleStruct, rename_returned_types(structs, return_types, raw_values))

    def transact(self, transaction: TxParams | None = None) -> HexBytes:
        try:
            return super().transact(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def estimate_gas(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
    ) -> int:
        try:
            return super().estimate_gas(transaction, block_identifier, state_override)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def build_transaction(self, transaction: TxParams | None = None) -> TxParams:
        try:
            return super().build_transaction(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_and_transact(self, account: LocalAccount, transaction: TxParams | None = None) -> HexBytes:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.

        Returns
        -------
        HexBytes
            The transaction hash.
        """
        if transaction is None:
            transaction_params: TxParams = {}
        else:
            transaction_params: TxParams = transaction

        if "from" in transaction_params:
            # Ensure if transaction is set, it matches
            assert (
                transaction_params["from"] == account.address
            ), f"Transaction from {transaction_params['from']} does not match account {account.address}"
        else:
            transaction_params["from"] = account.address

        if "gas" not in transaction_params:
            # Web3 default gas estimate seems to be underestimating gas, likely due to
            # not looking at pending block. Here, we explicitly call estimate gas
            # if gas isn't passed in.
            transaction_params["gas"] = self.estimate_gas(transaction_params, block_identifier="pending")

        # Build the raw transaction
        raw_transaction = self.build_transaction(transaction_params)

        if "nonce" not in raw_transaction:
            raw_transaction["nonce"] = self.w3.eth.get_transaction_count(account.address, block_identifier="pending")

        # Sign the raw transaction
        # Mismatched types between account and web3py
        signed_transaction = account.sign_transaction(raw_transaction)  # type: ignore

        # Send the signed transaction
        try:
            return self.w3.eth.send_raw_transaction(signed_transaction.raw_transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction_params,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_transact_and_wait(
        self,
        account: LocalAccount,
        transaction: TxParams | None = None,
        timeout: float | None = None,
        poll_latency: float | None = None,
        validate_transaction: bool = False,
    ) -> TxReceipt:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.
        timeout: float, optional
            The number of seconds to wait for the transaction to be mined. Defaults to 120.
        poll_latency: float, optional
            The number of seconds to wait between polling for the transaction receipt. Defaults to 0.1.
        validate_transaction: bool, optional
            Whether to validate the transaction. If True, will throw an exception if the resulting
            tx_receipt returned a failure status.

        Returns
        -------
        HexBytes
            The transaction hash.
        """

        # pylint: disable=too-many-arguments
        # pylint: disable=too-many-positional-arguments

        if timeout is None:
            timeout = 120
        if poll_latency is None:
            poll_latency = 0.1

        tx_hash = self.sign_and_transact(account, transaction)
        tx_receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=timeout, poll_latency=poll_latency)
        # Check the receipt, throwing an error if status == 0
        if validate_transaction:
            return check_txn_receipt(self, tx_hash, tx_receipt)
        else:
            return tx_receipt


class ExampleSingleSimpleStructContractFunction(PypechainContractFunction):
    """ContractFunction for the singleSimpleStruct method."""

    # super() call methods are generic, while our version adds values & types
    # pylint: disable=arguments-differ# disable this warning when there is overloading
    # pylint: disable=function-redefined

    _function_name = "singleSimpleStruct"

    # Make lookup for function signature -> overloaded function
    # The function signatures are python types, as we need to do a
    # lookup of arguments passed in to contract function
    _functions: dict[str, PypechainContractFunction]

    @overload
    def __call__(self, simpleStruct: Example.SimpleStruct) -> ExampleSingleSimpleStructContractFunction0:  # type: ignore
        ...

    def __call__(self, *args, **kwargs) -> ExampleSingleSimpleStructContractFunction:  # type: ignore
        clone = super().__call__(
            *(dataclass_to_tuple(arg) for arg in args), **{key: dataclass_to_tuple(arg) for key, arg in kwargs.items()}
        )

        # Arguments is the flattened set of arguments from args and kwargs, ordered by the abi
        # We get the python types of the args passed in, but remapped from tuples -> dataclasses
        arg_types = get_arg_type_names(clone.arguments)

        # Look up the function class based on arg types.
        # We ensure we use a copy of the original object.
        function_obj = copy.copy(self._functions[arg_types])

        function_obj.args = clone.args
        function_obj.kwargs = clone.kwargs

        # The `@overload` of `__call__` takes care of setting the type of this object correctly
        return function_obj  # type: ignore

    @classmethod
    def factory(cls, class_name: str, **kwargs: Any) -> Self:
        out = super().factory(class_name, **kwargs)

        # We initialize our overridden functions here.
        # Note that we use the initialized object to ensure each function
        # is attached to the instanciated object
        # (attached to a specific web3 and contract address)
        out._functions = {
            ExampleSingleSimpleStructContractFunction0._type_signature: ExampleSingleSimpleStructContractFunction0.factory(
                "ExampleSingleSimpleStructContractFunction0", **kwargs
            ),
        }
        return out


class ExampleTwoMixedStructsContractFunction0(PypechainContractFunction):
    """ContractFunction for the twoMixedStructs() method."""

    _function_name = "twoMixedStructs"
    _type_signature = expand_struct_type_str(tuple([]), structs)

    class ReturnValues(NamedTuple):
        """The return named tuple for TwoMixedStructs."""

        arg1: Example.SimpleStruct
        arg2: Example.NestedStruct

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> ReturnValues:
        """returns ReturnValues."""
        # We handle the block identifier = None case here for typing.
        if block_identifier is None:
            block_identifier = self.w3.eth.default_block

        # Define the expected return types from the smart contract call
        return_types = [Example.SimpleStruct, Example.NestedStruct]

        # Call the function
        try:
            raw_values = super().call(transaction, block_identifier, state_override, ccip_read_enabled)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="call",
                transaction=transaction,
                block_identifier=block_identifier,
            ) from err

        return self.ReturnValues(*rename_returned_types(structs, return_types, raw_values))

    def transact(self, transaction: TxParams | None = None) -> HexBytes:
        try:
            return super().transact(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def estimate_gas(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
    ) -> int:
        try:
            return super().estimate_gas(transaction, block_identifier, state_override)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def build_transaction(self, transaction: TxParams | None = None) -> TxParams:
        try:
            return super().build_transaction(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_and_transact(self, account: LocalAccount, transaction: TxParams | None = None) -> HexBytes:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.

        Returns
        -------
        HexBytes
            The transaction hash.
        """
        if transaction is None:
            transaction_params: TxParams = {}
        else:
            transaction_params: TxParams = transaction

        if "from" in transaction_params:
            # Ensure if transaction is set, it matches
            assert (
                transaction_params["from"] == account.address
            ), f"Transaction from {transaction_params['from']} does not match account {account.address}"
        else:
            transaction_params["from"] = account.address

        if "gas" not in transaction_params:
            # Web3 default gas estimate seems to be underestimating gas, likely due to
            # not looking at pending block. Here, we explicitly call estimate gas
            # if gas isn't passed in.
            transaction_params["gas"] = self.estimate_gas(transaction_params, block_identifier="pending")

        # Build the raw transaction
        raw_transaction = self.build_transaction(transaction_params)

        if "nonce" not in raw_transaction:
            raw_transaction["nonce"] = self.w3.eth.get_transaction_count(account.address, block_identifier="pending")

        # Sign the raw transaction
        # Mismatched types between account and web3py
        signed_transaction = account.sign_transaction(raw_transaction)  # type: ignore

        # Send the signed transaction
        try:
            return self.w3.eth.send_raw_transaction(signed_transaction.raw_transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction_params,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_transact_and_wait(
        self,
        account: LocalAccount,
        transaction: TxParams | None = None,
        timeout: float | None = None,
        poll_latency: float | None = None,
        validate_transaction: bool = False,
    ) -> TxReceipt:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.
        timeout: float, optional
            The number of seconds to wait for the transaction to be mined. Defaults to 120.
        poll_latency: float, optional
            The number of seconds to wait between polling for the transaction receipt. Defaults to 0.1.
        validate_transaction: bool, optional
            Whether to validate the transaction. If True, will throw an exception if the resulting
            tx_receipt returned a failure status.

        Returns
        -------
        HexBytes
            The transaction hash.
        """

        # pylint: disable=too-many-arguments
        # pylint: disable=too-many-positional-arguments

        if timeout is None:
            timeout = 120
        if poll_latency is None:
            poll_latency = 0.1

        tx_hash = self.sign_and_transact(account, transaction)
        tx_receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=timeout, poll_latency=poll_latency)
        # Check the receipt, throwing an error if status == 0
        if validate_transaction:
            return check_txn_receipt(self, tx_hash, tx_receipt)
        else:
            return tx_receipt


class ExampleTwoMixedStructsContractFunction(PypechainContractFunction):
    """ContractFunction for the twoMixedStructs method."""

    # super() call methods are generic, while our version adds values & types
    # pylint: disable=arguments-differ# disable this warning when there is overloading
    # pylint: disable=function-redefined

    _function_name = "twoMixedStructs"

    # Make lookup for function signature -> overloaded function
    # The function signatures are python types, as we need to do a
    # lookup of arguments passed in to contract function
    _functions: dict[str, PypechainContractFunction]

    @overload
    def __call__(self) -> ExampleTwoMixedStructsContractFunction0:  # type: ignore
        ...

    def __call__(self, *args, **kwargs) -> ExampleTwoMixedStructsContractFunction:  # type: ignore
        clone = super().__call__(
            *(dataclass_to_tuple(arg) for arg in args), **{key: dataclass_to_tuple(arg) for key, arg in kwargs.items()}
        )

        # Arguments is the flattened set of arguments from args and kwargs, ordered by the abi
        # We get the python types of the args passed in, but remapped from tuples -> dataclasses
        arg_types = get_arg_type_names(clone.arguments)

        # Look up the function class based on arg types.
        # We ensure we use a copy of the original object.
        function_obj = copy.copy(self._functions[arg_types])

        function_obj.args = clone.args
        function_obj.kwargs = clone.kwargs

        # The `@overload` of `__call__` takes care of setting the type of this object correctly
        return function_obj  # type: ignore

    @classmethod
    def factory(cls, class_name: str, **kwargs: Any) -> Self:
        out = super().factory(class_name, **kwargs)

        # We initialize our overridden functions here.
        # Note that we use the initialized object to ensure each function
        # is attached to the instanciated object
        # (attached to a specific web3 and contract address)
        out._functions = {
            ExampleTwoMixedStructsContractFunction0._type_signature: ExampleTwoMixedStructsContractFunction0.factory(
                "ExampleTwoMixedStructsContractFunction0", **kwargs
            ),
        }
        return out


class ExampleTwoSimpleStructsContractFunction0(PypechainContractFunction):
    """ContractFunction for the twoSimpleStructs() method."""

    _function_name = "twoSimpleStructs"
    _type_signature = expand_struct_type_str(tuple([]), structs)

    class ReturnValues(NamedTuple):
        """The return named tuple for TwoSimpleStructs."""

        arg1: Example.SimpleStruct
        arg2: Example.SimpleStruct

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> ReturnValues:
        """returns ReturnValues."""
        # We handle the block identifier = None case here for typing.
        if block_identifier is None:
            block_identifier = self.w3.eth.default_block

        # Define the expected return types from the smart contract call
        return_types = [Example.SimpleStruct, Example.SimpleStruct]

        # Call the function
        try:
            raw_values = super().call(transaction, block_identifier, state_override, ccip_read_enabled)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="call",
                transaction=transaction,
                block_identifier=block_identifier,
            ) from err

        return self.ReturnValues(*rename_returned_types(structs, return_types, raw_values))

    def transact(self, transaction: TxParams | None = None) -> HexBytes:
        try:
            return super().transact(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def estimate_gas(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
    ) -> int:
        try:
            return super().estimate_gas(transaction, block_identifier, state_override)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def build_transaction(self, transaction: TxParams | None = None) -> TxParams:
        try:
            return super().build_transaction(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_and_transact(self, account: LocalAccount, transaction: TxParams | None = None) -> HexBytes:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.

        Returns
        -------
        HexBytes
            The transaction hash.
        """
        if transaction is None:
            transaction_params: TxParams = {}
        else:
            transaction_params: TxParams = transaction

        if "from" in transaction_params:
            # Ensure if transaction is set, it matches
            assert (
                transaction_params["from"] == account.address
            ), f"Transaction from {transaction_params['from']} does not match account {account.address}"
        else:
            transaction_params["from"] = account.address

        if "gas" not in transaction_params:
            # Web3 default gas estimate seems to be underestimating gas, likely due to
            # not looking at pending block. Here, we explicitly call estimate gas
            # if gas isn't passed in.
            transaction_params["gas"] = self.estimate_gas(transaction_params, block_identifier="pending")

        # Build the raw transaction
        raw_transaction = self.build_transaction(transaction_params)

        if "nonce" not in raw_transaction:
            raw_transaction["nonce"] = self.w3.eth.get_transaction_count(account.address, block_identifier="pending")

        # Sign the raw transaction
        # Mismatched types between account and web3py
        signed_transaction = account.sign_transaction(raw_transaction)  # type: ignore

        # Send the signed transaction
        try:
            return self.w3.eth.send_raw_transaction(signed_transaction.raw_transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction_params,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_transact_and_wait(
        self,
        account: LocalAccount,
        transaction: TxParams | None = None,
        timeout: float | None = None,
        poll_latency: float | None = None,
        validate_transaction: bool = False,
    ) -> TxReceipt:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.
        timeout: float, optional
            The number of seconds to wait for the transaction to be mined. Defaults to 120.
        poll_latency: float, optional
            The number of seconds to wait between polling for the transaction receipt. Defaults to 0.1.
        validate_transaction: bool, optional
            Whether to validate the transaction. If True, will throw an exception if the resulting
            tx_receipt returned a failure status.

        Returns
        -------
        HexBytes
            The transaction hash.
        """

        # pylint: disable=too-many-arguments
        # pylint: disable=too-many-positional-arguments

        if timeout is None:
            timeout = 120
        if poll_latency is None:
            poll_latency = 0.1

        tx_hash = self.sign_and_transact(account, transaction)
        tx_receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=timeout, poll_latency=poll_latency)
        # Check the receipt, throwing an error if status == 0
        if validate_transaction:
            return check_txn_receipt(self, tx_hash, tx_receipt)
        else:
            return tx_receipt


class ExampleTwoSimpleStructsContractFunction(PypechainContractFunction):
    """ContractFunction for the twoSimpleStructs method."""

    # super() call methods are generic, while our version adds values & types
    # pylint: disable=arguments-differ# disable this warning when there is overloading
    # pylint: disable=function-redefined

    _function_name = "twoSimpleStructs"

    # Make lookup for function signature -> overloaded function
    # The function signatures are python types, as we need to do a
    # lookup of arguments passed in to contract function
    _functions: dict[str, PypechainContractFunction]

    @overload
    def __call__(self) -> ExampleTwoSimpleStructsContractFunction0:  # type: ignore
        ...

    def __call__(self, *args, **kwargs) -> ExampleTwoSimpleStructsContractFunction:  # type: ignore
        clone = super().__call__(
            *(dataclass_to_tuple(arg) for arg in args), **{key: dataclass_to_tuple(arg) for key, arg in kwargs.items()}
        )

        # Arguments is the flattened set of arguments from args and kwargs, ordered by the abi
        # We get the python types of the args passed in, but remapped from tuples -> dataclasses
        arg_types = get_arg_type_names(clone.arguments)

        # Look up the function class based on arg types.
        # We ensure we use a copy of the original object.
        function_obj = copy.copy(self._functions[arg_types])

        function_obj.args = clone.args
        function_obj.kwargs = clone.kwargs

        # The `@overload` of `__call__` takes care of setting the type of this object correctly
        return function_obj  # type: ignore

    @classmethod
    def factory(cls, class_name: str, **kwargs: Any) -> Self:
        out = super().factory(class_name, **kwargs)

        # We initialize our overridden functions here.
        # Note that we use the initialized object to ensure each function
        # is attached to the instanciated object
        # (attached to a specific web3 and contract address)
        out._functions = {
            ExampleTwoSimpleStructsContractFunction0._type_signature: ExampleTwoSimpleStructsContractFunction0.factory(
                "ExampleTwoSimpleStructsContractFunction0", **kwargs
            ),
        }
        return out


class ExampleVecOfStructContractFunction0(PypechainContractFunction):
    """ContractFunction for the vecOfStruct(list[Example.SimpleStruct]) method."""

    _function_name = "vecOfStruct"
    _type_signature = expand_struct_type_str(tuple(["list[Example.SimpleStruct]"]), structs)

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> list[Example.SimpleStruct]:
        """returns list[Example.SimpleStruct]."""
        # We handle the block identifier = None case here for typing.
        if block_identifier is None:
            block_identifier = self.w3.eth.default_block

        # Define the expected return types from the smart contract call
        return_types = list[Example.SimpleStruct]

        # Call the function
        try:
            raw_values = super().call(transaction, block_identifier, state_override, ccip_read_enabled)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="call",
                transaction=transaction,
                block_identifier=block_identifier,
            ) from err

        return cast(list[Example.SimpleStruct], rename_returned_types(structs, return_types, raw_values))

    def transact(self, transaction: TxParams | None = None) -> HexBytes:
        try:
            return super().transact(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def estimate_gas(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier | None = None,
        state_override: StateOverride | None = None,
    ) -> int:
        try:
            return super().estimate_gas(transaction, block_identifier, state_override)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def build_transaction(self, transaction: TxParams | None = None) -> TxParams:
        try:
            return super().build_transaction(transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="build",
                transaction=transaction,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_and_transact(self, account: LocalAccount, transaction: TxParams | None = None) -> HexBytes:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.

        Returns
        -------
        HexBytes
            The transaction hash.
        """
        if transaction is None:
            transaction_params: TxParams = {}
        else:
            transaction_params: TxParams = transaction

        if "from" in transaction_params:
            # Ensure if transaction is set, it matches
            assert (
                transaction_params["from"] == account.address
            ), f"Transaction from {transaction_params['from']} does not match account {account.address}"
        else:
            transaction_params["from"] = account.address

        if "gas" not in transaction_params:
            # Web3 default gas estimate seems to be underestimating gas, likely due to
            # not looking at pending block. Here, we explicitly call estimate gas
            # if gas isn't passed in.
            transaction_params["gas"] = self.estimate_gas(transaction_params, block_identifier="pending")

        # Build the raw transaction
        raw_transaction = self.build_transaction(transaction_params)

        if "nonce" not in raw_transaction:
            raw_transaction["nonce"] = self.w3.eth.get_transaction_count(account.address, block_identifier="pending")

        # Sign the raw transaction
        # Mismatched types between account and web3py
        signed_transaction = account.sign_transaction(raw_transaction)  # type: ignore

        # Send the signed transaction
        try:
            return self.w3.eth.send_raw_transaction(signed_transaction.raw_transaction)
        except Exception as err:  # pylint disable=broad-except
            raise handle_contract_logic_error(
                contract_function=self,
                errors_class=ExampleContractErrors,
                err=err,
                contract_call_type="transact",
                transaction=transaction_params,
                block_identifier="pending",  # race condition here, best effort to get block of txn.
            ) from err

    def sign_transact_and_wait(
        self,
        account: LocalAccount,
        transaction: TxParams | None = None,
        timeout: float | None = None,
        poll_latency: float | None = None,
        validate_transaction: bool = False,
    ) -> TxReceipt:
        """Convenience method for signing and sending a transaction using the provided account.

        Arguments
        ---------
        account : LocalAccount
            The account to use for signing and sending the transaction.
        transaction : TxParams | None, optional
            The transaction parameters to use for sending the transaction.
        timeout: float, optional
            The number of seconds to wait for the transaction to be mined. Defaults to 120.
        poll_latency: float, optional
            The number of seconds to wait between polling for the transaction receipt. Defaults to 0.1.
        validate_transaction: bool, optional
            Whether to validate the transaction. If True, will throw an exception if the resulting
            tx_receipt returned a failure status.

        Returns
        -------
        HexBytes
            The transaction hash.
        """

        # pylint: disable=too-many-arguments
        # pylint: disable=too-many-positional-arguments

        if timeout is None:
            timeout = 120
        if poll_latency is None:
            poll_latency = 0.1

        tx_hash = self.sign_and_transact(account, transaction)
        tx_receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=timeout, poll_latency=poll_latency)
        # Check the receipt, throwing an error if status == 0
        if validate_transaction:
            return check_txn_receipt(self, tx_hash, tx_receipt)
        else:
            return tx_receipt


class ExampleVecOfStructContractFunction(PypechainContractFunction):
    """ContractFunction for the vecOfStruct method."""

    # super() call methods are generic, while our version adds values & types
    # pylint: disable=arguments-differ# disable this warning when there is overloading
    # pylint: disable=function-redefined

    _function_name = "vecOfStruct"

    # Make lookup for function signature -> overloaded function
    # The function signatures are python types, as we need to do a
    # lookup of arguments passed in to contract function
    _functions: dict[str, PypechainContractFunction]

    @overload
    def __call__(self, inVecSimpleStruct: list[Example.SimpleStruct]) -> ExampleVecOfStructContractFunction0:  # type: ignore
        ...

    def __call__(self, *args, **kwargs) -> ExampleVecOfStructContractFunction:  # type: ignore
        clone = super().__call__(
            *(dataclass_to_tuple(arg) for arg in args), **{key: dataclass_to_tuple(arg) for key, arg in kwargs.items()}
        )

        # Arguments is the flattened set of arguments from args and kwargs, ordered by the abi
        # We get the python types of the args passed in, but remapped from tuples -> dataclasses
        arg_types = get_arg_type_names(clone.arguments)

        # Look up the function class based on arg types.
        # We ensure we use a copy of the original object.
        function_obj = copy.copy(self._functions[arg_types])

        function_obj.args = clone.args
        function_obj.kwargs = clone.kwargs

        # The `@overload` of `__call__` takes care of setting the type of this object correctly
        return function_obj  # type: ignore

    @classmethod
    def factory(cls, class_name: str, **kwargs: Any) -> Self:
        out = super().factory(class_name, **kwargs)

        # We initialize our overridden functions here.
        # Note that we use the initialized object to ensure each function
        # is attached to the instanciated object
        # (attached to a specific web3 and contract address)
        out._functions = {
            ExampleVecOfStructContractFunction0._type_signature: ExampleVecOfStructContractFunction0.factory(
                "ExampleVecOfStructContractFunction0", **kwargs
            ),
        }
        return out


class ExampleContractFunctions(ContractFunctions):
    """ContractFunctions for the Example contract."""

    flipFlop: ExampleFlipFlopContractFunction

    guessALetter: ExampleGuessALetterContractFunction

    mixStructsAndPrimitives: ExampleMixStructsAndPrimitivesContractFunction

    namedSingleStruct: ExampleNamedSingleStructContractFunction

    namedTwoMixedStructs: ExampleNamedTwoMixedStructsContractFunction

    singleNestedStruct: ExampleSingleNestedStructContractFunction

    singleSimpleStruct: ExampleSingleSimpleStructContractFunction

    twoMixedStructs: ExampleTwoMixedStructsContractFunction

    twoSimpleStructs: ExampleTwoSimpleStructsContractFunction

    vecOfStruct: ExampleVecOfStructContractFunction

    def __init__(
        self,
        abi: ABI,
        w3: "Web3",
        address: ChecksumAddress | None = None,
        decode_tuples: bool | None = False,
    ) -> None:
        super().__init__(abi, w3, address, decode_tuples)
        self.flipFlop = ExampleFlipFlopContractFunction.factory(
            "flipFlop",
            w3=w3,
            contract_abi=abi,
            address=address,
            decode_tuples=decode_tuples,
            abi_element_identifier="flipFlop",
        )
        self.guessALetter = ExampleGuessALetterContractFunction.factory(
            "guessALetter",
            w3=w3,
            contract_abi=abi,
            address=address,
            decode_tuples=decode_tuples,
            abi_element_identifier="guessALetter",
        )
        self.mixStructsAndPrimitives = ExampleMixStructsAndPrimitivesContractFunction.factory(
            "mixStructsAndPrimitives",
            w3=w3,
            contract_abi=abi,
            address=address,
            decode_tuples=decode_tuples,
            abi_element_identifier="mixStructsAndPrimitives",
        )
        self.namedSingleStruct = ExampleNamedSingleStructContractFunction.factory(
            "namedSingleStruct",
            w3=w3,
            contract_abi=abi,
            address=address,
            decode_tuples=decode_tuples,
            abi_element_identifier="namedSingleStruct",
        )
        self.namedTwoMixedStructs = ExampleNamedTwoMixedStructsContractFunction.factory(
            "namedTwoMixedStructs",
            w3=w3,
            contract_abi=abi,
            address=address,
            decode_tuples=decode_tuples,
            abi_element_identifier="namedTwoMixedStructs",
        )
        self.singleNestedStruct = ExampleSingleNestedStructContractFunction.factory(
            "singleNestedStruct",
            w3=w3,
            contract_abi=abi,
            address=address,
            decode_tuples=decode_tuples,
            abi_element_identifier="singleNestedStruct",
        )
        self.singleSimpleStruct = ExampleSingleSimpleStructContractFunction.factory(
            "singleSimpleStruct",
            w3=w3,
            contract_abi=abi,
            address=address,
            decode_tuples=decode_tuples,
            abi_element_identifier="singleSimpleStruct",
        )
        self.twoMixedStructs = ExampleTwoMixedStructsContractFunction.factory(
            "twoMixedStructs",
            w3=w3,
            contract_abi=abi,
            address=address,
            decode_tuples=decode_tuples,
            abi_element_identifier="twoMixedStructs",
        )
        self.twoSimpleStructs = ExampleTwoSimpleStructsContractFunction.factory(
            "twoSimpleStructs",
            w3=w3,
            contract_abi=abi,
            address=address,
            decode_tuples=decode_tuples,
            abi_element_identifier="twoSimpleStructs",
        )
        self.vecOfStruct = ExampleVecOfStructContractFunction.factory(
            "vecOfStruct",
            w3=w3,
            contract_abi=abi,
            address=address,
            decode_tuples=decode_tuples,
            abi_element_identifier="vecOfStruct",
        )


class ExampleFlipContractEvent(ContractEvent):
    """ContractEvent for Flip."""

    # super() get_logs and create_filter methods are generic, while our version adds values & types
    # pylint: disable=arguments-differ

    # pylint: disable=useless-parent-delegation
    def __init__(self, *argument_names: tuple[str]) -> None:
        super().__init__(*argument_names)

    @combomethod_typed
    def get_logs_typed(
        self,
        argument_filters: dict[str, Any] | None = None,
        from_block: BlockIdentifier | None = None,
        to_block: BlockIdentifier | None = None,
        block_hash: HexBytes | None = None,
    ) -> Iterable[FlipEvent]:
        """Extension of `get_logs` that return a typed dataclass of the event."""
        abi_events = super().get_logs(
            argument_filters=argument_filters, from_block=from_block, to_block=to_block, block_hash=block_hash
        )
        # TODO there may be issues with this function if the user uses a middleware that changes event structure.
        return [
            FlipEvent(
                log_index=abi_event.logIndex,
                transaction_index=abi_event.transactionIndex,
                transaction_hash=abi_event.transactionHash,
                address=abi_event.address,
                block_hash=abi_event.blockHash,
                block_number=abi_event.blockNumber,
                args=FlipEvent.FlipEventArgs(
                    flip=abi_event.args["flip"],
                ),
            )
            for abi_event in abi_events
        ]

    @combomethod_typed
    def process_receipt_typed(self, txn_receipt: TxReceipt, errors: EventLogErrorFlags = WARN) -> Iterable[FlipEvent]:
        """Extension of `process_receipt` that return a typed dataclass of the event."""
        abi_events = super().process_receipt(txn_receipt, errors)
        # TODO there may be issues with this function if the user uses a middleware that changes event structure.
        return [
            FlipEvent(
                log_index=abi_event.logIndex,
                transaction_index=abi_event.transactionIndex,
                transaction_hash=abi_event.transactionHash,
                address=abi_event.address,
                block_hash=abi_event.blockHash,
                block_number=abi_event.blockNumber,
                args=FlipEvent.FlipEventArgs(
                    flip=abi_event.args["flip"],
                ),
            )
            for abi_event in abi_events
        ]

    @combomethod_typed
    def create_filter(  # type: ignore
        self,
        *,  # PEP 3102
        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,
    ) -> LogFilter:
        return cast(
            LogFilter,
            super().create_filter(
                argument_filters=argument_filters,
                from_block=from_block,
                to_block=to_block,
                address=address,
                topics=topics,
            ),
        )


class ExampleFlopContractEvent(ContractEvent):
    """ContractEvent for Flop."""

    # super() get_logs and create_filter methods are generic, while our version adds values & types
    # pylint: disable=arguments-differ

    # pylint: disable=useless-parent-delegation
    def __init__(self, *argument_names: tuple[str]) -> None:
        super().__init__(*argument_names)

    @combomethod_typed
    def get_logs_typed(
        self,
        argument_filters: dict[str, Any] | None = None,
        from_block: BlockIdentifier | None = None,
        to_block: BlockIdentifier | None = None,
        block_hash: HexBytes | None = None,
    ) -> Iterable[FlopEvent]:
        """Extension of `get_logs` that return a typed dataclass of the event."""
        abi_events = super().get_logs(
            argument_filters=argument_filters, from_block=from_block, to_block=to_block, block_hash=block_hash
        )
        # TODO there may be issues with this function if the user uses a middleware that changes event structure.
        return [
            FlopEvent(
                log_index=abi_event.logIndex,
                transaction_index=abi_event.transactionIndex,
                transaction_hash=abi_event.transactionHash,
                address=abi_event.address,
                block_hash=abi_event.blockHash,
                block_number=abi_event.blockNumber,
                args=FlopEvent.FlopEventArgs(
                    flop=abi_event.args["flop"],
                ),
            )
            for abi_event in abi_events
        ]

    @combomethod_typed
    def process_receipt_typed(self, txn_receipt: TxReceipt, errors: EventLogErrorFlags = WARN) -> Iterable[FlopEvent]:
        """Extension of `process_receipt` that return a typed dataclass of the event."""
        abi_events = super().process_receipt(txn_receipt, errors)
        # TODO there may be issues with this function if the user uses a middleware that changes event structure.
        return [
            FlopEvent(
                log_index=abi_event.logIndex,
                transaction_index=abi_event.transactionIndex,
                transaction_hash=abi_event.transactionHash,
                address=abi_event.address,
                block_hash=abi_event.blockHash,
                block_number=abi_event.blockNumber,
                args=FlopEvent.FlopEventArgs(
                    flop=abi_event.args["flop"],
                ),
            )
            for abi_event in abi_events
        ]

    @combomethod_typed
    def create_filter(  # type: ignore
        self,
        *,  # PEP 3102
        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,
    ) -> LogFilter:
        return cast(
            LogFilter,
            super().create_filter(
                argument_filters=argument_filters,
                from_block=from_block,
                to_block=to_block,
                address=address,
                topics=topics,
            ),
        )


class ExampleContractEvents(ContractEvents):
    """ContractEvents for the Example contract."""

    Flip: Type[ExampleFlipContractEvent]

    Flop: Type[ExampleFlopContractEvent]

    def __init__(
        self,
        abi: ABI,
        w3: "Web3",
        address: ChecksumAddress | None = None,
    ) -> None:
        super().__init__(abi, w3, address)
        self.Flip = cast(
            Type[ExampleFlipContractEvent],
            ExampleFlipContractEvent.factory("Flip", w3=w3, contract_abi=abi, address=address, event_name="Flip"),
        )
        self.Flop = cast(
            Type[ExampleFlopContractEvent],
            ExampleFlopContractEvent.factory("Flop", w3=w3, contract_abi=abi, address=address, event_name="Flop"),
        )


example_abi: ABI = cast(
    ABI,
    [
        {
            "type": "constructor",
            "inputs": [{"name": "name", "type": "string", "internalType": "string"}],
            "stateMutability": "nonpayable",
        },
        {
            "type": "function",
            "name": "flipFlop",
            "inputs": [
                {"name": "flip", "type": "uint256", "internalType": "uint256"},
                {"name": "flop", "type": "uint256", "internalType": "uint256"},
            ],
            "outputs": [
                {"name": "_flop", "type": "uint256", "internalType": "uint256"},
                {"name": "_flip", "type": "uint256", "internalType": "uint256"},
            ],
            "stateMutability": "nonpayable",
        },
        {
            "type": "function",
            "name": "guessALetter",
            "inputs": [{"name": "guess", "type": "uint8", "internalType": "enum Example.Letters"}],
            "outputs": [],
            "stateMutability": "pure",
        },
        {
            "type": "function",
            "name": "mixStructsAndPrimitives",
            "inputs": [],
            "outputs": [
                {
                    "name": "simpleStruct",
                    "type": "tuple",
                    "internalType": "struct Example.SimpleStruct",
                    "components": [
                        {"name": "intVal", "type": "uint256", "internalType": "uint256"},
                        {"name": "strVal", "type": "string", "internalType": "string"},
                    ],
                },
                {
                    "name": "",
                    "type": "tuple",
                    "internalType": "struct Example.NestedStruct",
                    "components": [
                        {"name": "intVal", "type": "uint256", "internalType": "uint256"},
                        {"name": "strVal", "type": "string", "internalType": "string"},
                        {
                            "name": "innerStruct",
                            "type": "tuple",
                            "internalType": "struct Example.InnerStruct",
                            "components": [{"name": "boolVal", "type": "bool", "internalType": "bool"}],
                        },
                    ],
                },
                {"name": "", "type": "uint256", "internalType": "uint256"},
                {"name": "_name", "type": "string", "internalType": "string"},
                {"name": "YesOrNo", "type": "bool", "internalType": "bool"},
            ],
            "stateMutability": "pure",
        },
        {
            "type": "function",
            "name": "namedSingleStruct",
            "inputs": [],
            "outputs": [
                {
                    "name": "struct1",
                    "type": "tuple",
                    "internalType": "struct Example.SimpleStruct",
                    "components": [
                        {"name": "intVal", "type": "uint256", "internalType": "uint256"},
                        {"name": "strVal", "type": "string", "internalType": "string"},
                    ],
                }
            ],
            "stateMutability": "pure",
        },
        {
            "type": "function",
            "name": "namedTwoMixedStructs",
            "inputs": [],
            "outputs": [
                {
                    "name": "simpleStruct",
                    "type": "tuple",
                    "internalType": "struct Example.SimpleStruct",
                    "components": [
                        {"name": "intVal", "type": "uint256", "internalType": "uint256"},
                        {"name": "strVal", "type": "string", "internalType": "string"},
                    ],
                },
                {
                    "name": "nestedStruct",
                    "type": "tuple",
                    "internalType": "struct Example.NestedStruct",
                    "components": [
                        {"name": "intVal", "type": "uint256", "internalType": "uint256"},
                        {"name": "strVal", "type": "string", "internalType": "string"},
                        {
                            "name": "innerStruct",
                            "type": "tuple",
                            "internalType": "struct Example.InnerStruct",
                            "components": [{"name": "boolVal", "type": "bool", "internalType": "bool"}],
                        },
                    ],
                },
            ],
            "stateMutability": "pure",
        },
        {
            "type": "function",
            "name": "singleNestedStruct",
            "inputs": [
                {
                    "name": "nestedStruct",
                    "type": "tuple",
                    "internalType": "struct Example.NestedStruct",
                    "components": [
                        {"name": "intVal", "type": "uint256", "internalType": "uint256"},
                        {"name": "strVal", "type": "string", "internalType": "string"},
                        {
                            "name": "innerStruct",
                            "type": "tuple",
                            "internalType": "struct Example.InnerStruct",
                            "components": [{"name": "boolVal", "type": "bool", "internalType": "bool"}],
                        },
                    ],
                }
            ],
            "outputs": [
                {
                    "name": "",
                    "type": "tuple",
                    "internalType": "struct Example.NestedStruct",
                    "components": [
                        {"name": "intVal", "type": "uint256", "internalType": "uint256"},
                        {"name": "strVal", "type": "string", "internalType": "string"},
                        {
                            "name": "innerStruct",
                            "type": "tuple",
                            "internalType": "struct Example.InnerStruct",
                            "components": [{"name": "boolVal", "type": "bool", "internalType": "bool"}],
                        },
                    ],
                }
            ],
            "stateMutability": "pure",
        },
        {
            "type": "function",
            "name": "singleSimpleStruct",
            "inputs": [
                {
                    "name": "simpleStruct",
                    "type": "tuple",
                    "internalType": "struct Example.SimpleStruct",
                    "components": [
                        {"name": "intVal", "type": "uint256", "internalType": "uint256"},
                        {"name": "strVal", "type": "string", "internalType": "string"},
                    ],
                }
            ],
            "outputs": [
                {
                    "name": "",
                    "type": "tuple",
                    "internalType": "struct Example.SimpleStruct",
                    "components": [
                        {"name": "intVal", "type": "uint256", "internalType": "uint256"},
                        {"name": "strVal", "type": "string", "internalType": "string"},
                    ],
                }
            ],
            "stateMutability": "pure",
        },
        {
            "type": "function",
            "name": "twoMixedStructs",
            "inputs": [],
            "outputs": [
                {
                    "name": "",
                    "type": "tuple",
                    "internalType": "struct Example.SimpleStruct",
                    "components": [
                        {"name": "intVal", "type": "uint256", "internalType": "uint256"},
                        {"name": "strVal", "type": "string", "internalType": "string"},
                    ],
                },
                {
                    "name": "",
                    "type": "tuple",
                    "internalType": "struct Example.NestedStruct",
                    "components": [
                        {"name": "intVal", "type": "uint256", "internalType": "uint256"},
                        {"name": "strVal", "type": "string", "internalType": "string"},
                        {
                            "name": "innerStruct",
                            "type": "tuple",
                            "internalType": "struct Example.InnerStruct",
                            "components": [{"name": "boolVal", "type": "bool", "internalType": "bool"}],
                        },
                    ],
                },
            ],
            "stateMutability": "pure",
        },
        {
            "type": "function",
            "name": "twoSimpleStructs",
            "inputs": [],
            "outputs": [
                {
                    "name": "",
                    "type": "tuple",
                    "internalType": "struct Example.SimpleStruct",
                    "components": [
                        {"name": "intVal", "type": "uint256", "internalType": "uint256"},
                        {"name": "strVal", "type": "string", "internalType": "string"},
                    ],
                },
                {
                    "name": "",
                    "type": "tuple",
                    "internalType": "struct Example.SimpleStruct",
                    "components": [
                        {"name": "intVal", "type": "uint256", "internalType": "uint256"},
                        {"name": "strVal", "type": "string", "internalType": "string"},
                    ],
                },
            ],
            "stateMutability": "pure",
        },
        {
            "type": "function",
            "name": "vecOfStruct",
            "inputs": [
                {
                    "name": "inVecSimpleStruct",
                    "type": "tuple[]",
                    "internalType": "struct Example.SimpleStruct[]",
                    "components": [
                        {"name": "intVal", "type": "uint256", "internalType": "uint256"},
                        {"name": "strVal", "type": "string", "internalType": "string"},
                    ],
                }
            ],
            "outputs": [
                {
                    "name": "vecSimpleStruct",
                    "type": "tuple[]",
                    "internalType": "struct Example.SimpleStruct[]",
                    "components": [
                        {"name": "intVal", "type": "uint256", "internalType": "uint256"},
                        {"name": "strVal", "type": "string", "internalType": "string"},
                    ],
                }
            ],
            "stateMutability": "pure",
        },
        {
            "type": "event",
            "name": "Flip",
            "inputs": [{"name": "flip", "type": "uint256", "indexed": False, "internalType": "uint256"}],
            "anonymous": False,
        },
        {
            "type": "event",
            "name": "Flop",
            "inputs": [{"name": "flop", "type": "uint256", "indexed": False, "internalType": "uint256"}],
            "anonymous": False,
        },
        {
            "type": "error",
            "name": "WrongChoice",
            "inputs": [
                {"name": "answer", "type": "uint8", "internalType": "enum Example.Letters"},
                {"name": "errorMessage", "type": "string", "internalType": "string"},
            ],
        },
    ],
)


class ExampleWrongChoiceContractError(PypechainBaseError):
    """ContractError for WrongChoice."""

    # Error name
    name: str = "WrongChoice"
    # 4 byte error selector
    selector: str = "0xc13b30d4"
    # error signature, i.e. CustomError(uint256,bool)
    signature: str = "WrongChoice(uint8,string)"
    # Error input types
    abi: ABI = example_abi


class ExampleContractErrors(PypechainBaseContractErrors):
    """ContractErrors for the Example contract."""

    WrongChoice: ExampleWrongChoiceContractError

    def __init__(
        self,
    ) -> None:
        self.WrongChoice = ExampleWrongChoiceContractError()

        self._all = [
            self.WrongChoice,
        ]


class ExampleContract(Contract):
    """A web3.py Contract class for the Example contract."""

    abi: ABI = example_abi
    # We change `bytecode` as needed for linking, but keep
    # `_raw_bytecode` unchanged as an original copy.
    # pylint: disable=line-too-long
    _raw_bytecode: HexStr | None = HexStr(
        " "
    )

    def __init__(self, address: ChecksumAddress | None = None) -> None:
        # Initialize parent Contract class
        super().__init__(address=address)
        self.functions = ExampleContractFunctions(example_abi, self.w3, address)  # type: ignore
        self.events = ExampleContractEvents(example_abi, self.w3, address)  # type: ignore
        self.errors = ExampleContractErrors()

    events: ExampleContractEvents

    errors: ExampleContractErrors = ExampleContractErrors()

    functions: ExampleContractFunctions

    class ConstructorArgs(NamedTuple):
        """Arguments to pass the contract's constructor function."""

        name: str

    @classmethod
    def constructor(cls, name: str) -> ContractConstructor:  # type: ignore
        """Creates a transaction with the contract's constructor function.

        Parameters
        ----------

        w3 : Web3
            A web3 instance.
        account : LocalAccount
            The account to use to deploy the contract.

        Returns
        -------
        Self
            A deployed instance of the contract.

        """
        cls.bytecode = cls._raw_bytecode
        if cls.bytecode is not None:

            # bytecode needs to be in hex for web3
            cls.bytecode = HexBytes(cls.bytecode)

        return super().constructor(dataclass_to_tuple(name))

    @classmethod
    def deploy(cls, w3: Web3, account: LocalAccount | ChecksumAddress, constructor_args: ConstructorArgs) -> Self:
        """Deploys an instance of the contract.

        Parameters
        ----------
        w3 : Web3
            A web3 instance.
        account : LocalAccount
            The account to use to deploy the contract.

        Returns
        -------
        Self
            A deployed instance of the contract.
        """
        deployer = cls.factory(w3=w3)
        constructor_fn = deployer.constructor(
            *constructor_args,
        )

        # if an address is supplied, try to use a web3 default account
        if isinstance(account, str):
            tx_hash = constructor_fn.transact({"from": account})
            tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)

            deployed_contract = deployer(address=tx_receipt.contractAddress)  # type: ignore
            return deployed_contract

        # otherwise use the account provided.
        deployment_tx = constructor_fn.build_transaction()
        current_nonce = w3.eth.get_transaction_count(account.address, "pending")
        deployment_tx.update({"nonce": current_nonce})

        # Sign the transaction with local account private key
        signed_tx = account.sign_transaction(deployment_tx)

        # Send the signed transaction and wait for receipt
        tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction)
        tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)

        deployed_contract = deployer(address=tx_receipt.contractAddress)  # type: ignore
        return deployed_contract

    @classmethod
    def factory(cls, w3: Web3, class_name: str | None = None, **kwargs: Any) -> Type[Self]:
        """Initializes the contract object.

        Parameters
        ----------
        w3 : Web3
            A web3 instance.
        class_name: str | None
            The instance class name.

        Returns
        -------
        Self
            An instance of the contract class.
        """
        contract = super().factory(w3, class_name, **kwargs)
        contract.functions = ExampleContractFunctions(example_abi, w3, None)
        contract.errors = ExampleContractErrors()

        return contract
