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

DO NOT EDIT.  This file was generated by pypechain.  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 overriden 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


from __future__ import annotations

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

from eth_abi.codec import ABICodec
from eth_abi.registry import registry as default_registry
from eth_account.signers.local import LocalAccount
from eth_typing import ChecksumAddress, HexStr
from hexbytes import HexBytes
from typing_extensions import Self
from web3 import Web3
from web3._utils.filters import LogFilter
from web3.contract.contract import (
    Contract,
    ContractConstructor,
    ContractEvent,
    ContractEvents,
    ContractFunction,
    ContractFunctions,
)
from web3.exceptions import FallbackNotFound
from web3.types import ABI, ABIFunction, BlockIdentifier, CallOverride, EventData, TxParams

from .ExampleTypes import InnerStruct, NestedStruct, SimpleStruct
from .utilities import dataclass_to_tuple, get_abi_input_types, rename_returned_types

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


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

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

        flop: int
        flip: int

    def __call__(self, flip: int, flop: int) -> ExampleFlipFlopContractFunction:  # type: ignore
        clone = super().__call__(dataclass_to_tuple(flip), dataclass_to_tuple(flop))
        self.kwargs = clone.kwargs
        self.args = clone.args
        return self

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier = "latest",
        state_override: CallOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> ReturnValues:
        """returns ReturnValues."""
        # Define the expected return types from the smart contract call

        return_types = [int, int]

        # Call the function

        raw_values = super().call(transaction, block_identifier, state_override, ccip_read_enabled)
        return self.ReturnValues(*rename_returned_types(structs, return_types, raw_values))


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

    def __call__(self, guess: int) -> ExampleGuessALetterContractFunction:  # type: ignore
        clone = super().__call__(dataclass_to_tuple(guess))
        self.kwargs = clone.kwargs
        self.args = clone.args
        return self

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier = "latest",
        state_override: CallOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> None:
        """returns None."""
        # Define the expected return types from the smart contract call

        # Call the function


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

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

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

    def __call__(self) -> ExampleMixStructsAndPrimitivesContractFunction:  # type: ignore
        clone = super().__call__()
        self.kwargs = clone.kwargs
        self.args = clone.args
        return self

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier = "latest",
        state_override: CallOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> ReturnValues:
        """returns ReturnValues."""
        # Define the expected return types from the smart contract call

        return_types = [SimpleStruct, NestedStruct, int, str, bool]

        # Call the function

        raw_values = super().call(transaction, block_identifier, state_override, ccip_read_enabled)
        return self.ReturnValues(*rename_returned_types(structs, return_types, raw_values))


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

    def __call__(self) -> ExampleNamedSingleStructContractFunction:  # type: ignore
        clone = super().__call__()
        self.kwargs = clone.kwargs
        self.args = clone.args
        return self

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier = "latest",
        state_override: CallOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> SimpleStruct:
        """returns SimpleStruct."""
        # Define the expected return types from the smart contract call

        return_types = SimpleStruct

        # Call the function

        raw_values = super().call(transaction, block_identifier, state_override, ccip_read_enabled)
        return cast(SimpleStruct, rename_returned_types(structs, return_types, raw_values))


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

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

        simpleStruct: SimpleStruct
        nestedStruct: NestedStruct

    def __call__(self) -> ExampleNamedTwoMixedStructsContractFunction:  # type: ignore
        clone = super().__call__()
        self.kwargs = clone.kwargs
        self.args = clone.args
        return self

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier = "latest",
        state_override: CallOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> ReturnValues:
        """returns ReturnValues."""
        # Define the expected return types from the smart contract call

        return_types = [SimpleStruct, NestedStruct]

        # Call the function

        raw_values = super().call(transaction, block_identifier, state_override, ccip_read_enabled)
        return self.ReturnValues(*rename_returned_types(structs, return_types, raw_values))


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

    def __call__(self, nestedStruct: NestedStruct) -> ExampleSingleNestedStructContractFunction:  # type: ignore
        clone = super().__call__(dataclass_to_tuple(nestedStruct))
        self.kwargs = clone.kwargs
        self.args = clone.args
        return self

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier = "latest",
        state_override: CallOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> NestedStruct:
        """returns NestedStruct."""
        # Define the expected return types from the smart contract call

        return_types = NestedStruct

        # Call the function

        raw_values = super().call(transaction, block_identifier, state_override, ccip_read_enabled)
        return cast(NestedStruct, rename_returned_types(structs, return_types, raw_values))


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

    def __call__(self, simpleStruct: SimpleStruct) -> ExampleSingleSimpleStructContractFunction:  # type: ignore
        clone = super().__call__(dataclass_to_tuple(simpleStruct))
        self.kwargs = clone.kwargs
        self.args = clone.args
        return self

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier = "latest",
        state_override: CallOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> SimpleStruct:
        """returns SimpleStruct."""
        # Define the expected return types from the smart contract call

        return_types = SimpleStruct

        # Call the function

        raw_values = super().call(transaction, block_identifier, state_override, ccip_read_enabled)
        return cast(SimpleStruct, rename_returned_types(structs, return_types, raw_values))


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

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

        arg1: SimpleStruct
        arg2: NestedStruct

    def __call__(self) -> ExampleTwoMixedStructsContractFunction:  # type: ignore
        clone = super().__call__()
        self.kwargs = clone.kwargs
        self.args = clone.args
        return self

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier = "latest",
        state_override: CallOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> ReturnValues:
        """returns ReturnValues."""
        # Define the expected return types from the smart contract call

        return_types = [SimpleStruct, NestedStruct]

        # Call the function

        raw_values = super().call(transaction, block_identifier, state_override, ccip_read_enabled)
        return self.ReturnValues(*rename_returned_types(structs, return_types, raw_values))


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

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

        arg1: SimpleStruct
        arg2: SimpleStruct

    def __call__(self) -> ExampleTwoSimpleStructsContractFunction:  # type: ignore
        clone = super().__call__()
        self.kwargs = clone.kwargs
        self.args = clone.args
        return self

    def call(
        self,
        transaction: TxParams | None = None,
        block_identifier: BlockIdentifier = "latest",
        state_override: CallOverride | None = None,
        ccip_read_enabled: bool | None = None,
    ) -> ReturnValues:
        """returns ReturnValues."""
        # Define the expected return types from the smart contract call

        return_types = [SimpleStruct, SimpleStruct]

        # Call the function

        raw_values = super().call(transaction, block_identifier, state_override, ccip_read_enabled)
        return self.ReturnValues(*rename_returned_types(structs, return_types, raw_values))


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

    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,
            function_identifier="flipFlop",
        )
        self.guessALetter = ExampleGuessALetterContractFunction.factory(
            "guessALetter",
            w3=w3,
            contract_abi=abi,
            address=address,
            decode_tuples=decode_tuples,
            function_identifier="guessALetter",
        )
        self.mixStructsAndPrimitives = ExampleMixStructsAndPrimitivesContractFunction.factory(
            "mixStructsAndPrimitives",
            w3=w3,
            contract_abi=abi,
            address=address,
            decode_tuples=decode_tuples,
            function_identifier="mixStructsAndPrimitives",
        )
        self.namedSingleStruct = ExampleNamedSingleStructContractFunction.factory(
            "namedSingleStruct",
            w3=w3,
            contract_abi=abi,
            address=address,
            decode_tuples=decode_tuples,
            function_identifier="namedSingleStruct",
        )
        self.namedTwoMixedStructs = ExampleNamedTwoMixedStructsContractFunction.factory(
            "namedTwoMixedStructs",
            w3=w3,
            contract_abi=abi,
            address=address,
            decode_tuples=decode_tuples,
            function_identifier="namedTwoMixedStructs",
        )
        self.singleNestedStruct = ExampleSingleNestedStructContractFunction.factory(
            "singleNestedStruct",
            w3=w3,
            contract_abi=abi,
            address=address,
            decode_tuples=decode_tuples,
            function_identifier="singleNestedStruct",
        )
        self.singleSimpleStruct = ExampleSingleSimpleStructContractFunction.factory(
            "singleSimpleStruct",
            w3=w3,
            contract_abi=abi,
            address=address,
            decode_tuples=decode_tuples,
            function_identifier="singleSimpleStruct",
        )
        self.twoMixedStructs = ExampleTwoMixedStructsContractFunction.factory(
            "twoMixedStructs",
            w3=w3,
            contract_abi=abi,
            address=address,
            decode_tuples=decode_tuples,
            function_identifier="twoMixedStructs",
        )
        self.twoSimpleStructs = ExampleTwoSimpleStructsContractFunction.factory(
            "twoSimpleStructs",
            w3=w3,
            contract_abi=abi,
            address=address,
            decode_tuples=decode_tuples,
            function_identifier="twoSimpleStructs",
        )


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

    # @combomethod destroys return types, so we are redefining functions as both class and instance
    # pylint: disable=function-redefined

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

    def get_logs(  # type: ignore
        self: "ExampleFlipContractEvent",
        argument_filters: dict[str, Any] | None = None,
        fromBlock: BlockIdentifier | None = None,
        toBlock: BlockIdentifier | None = None,
        block_hash: HexBytes | None = None,
    ) -> Iterable[EventData]:
        return cast(
            Iterable[EventData],
            super().get_logs(
                argument_filters=argument_filters, fromBlock=fromBlock, toBlock=toBlock, block_hash=block_hash
            ),
        )

    @classmethod
    def get_logs(  # type: ignore
        cls: Type["ExampleFlipContractEvent"],
        argument_filters: dict[str, Any] | None = None,
        fromBlock: BlockIdentifier | None = None,
        toBlock: BlockIdentifier | None = None,
        block_hash: HexBytes | None = None,
    ) -> Iterable[EventData]:
        return cast(
            Iterable[EventData],
            super().get_logs(
                argument_filters=argument_filters, fromBlock=fromBlock, toBlock=toBlock, block_hash=block_hash
            ),
        )

    def create_filter(  # type: ignore
        self: "ExampleFlipContractEvent",
        *,  # PEP 3102
        argument_filters: dict[str, Any] | None = None,
        fromBlock: BlockIdentifier | None = None,
        toBlock: BlockIdentifier = "latest",
        address: ChecksumAddress | None = None,
        topics: Sequence[Any] | None = None,
    ) -> LogFilter:
        return cast(
            LogFilter,
            super().create_filter(
                argument_filters=argument_filters, fromBlock=fromBlock, toBlock=toBlock, address=address, topics=topics
            ),
        )

    @classmethod
    def create_filter(  # type: ignore
        cls: Type["ExampleFlipContractEvent"],
        *,  # PEP 3102
        argument_filters: dict[str, Any] | None = None,
        fromBlock: BlockIdentifier | None = None,
        toBlock: BlockIdentifier = "latest",
        address: ChecksumAddress | None = None,
        topics: Sequence[Any] | None = None,
    ) -> LogFilter:
        return cast(
            LogFilter,
            super().create_filter(
                argument_filters=argument_filters, fromBlock=fromBlock, toBlock=toBlock, 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

    # @combomethod destroys return types, so we are redefining functions as both class and instance
    # pylint: disable=function-redefined

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

    def get_logs(  # type: ignore
        self: "ExampleFlopContractEvent",
        argument_filters: dict[str, Any] | None = None,
        fromBlock: BlockIdentifier | None = None,
        toBlock: BlockIdentifier | None = None,
        block_hash: HexBytes | None = None,
    ) -> Iterable[EventData]:
        return cast(
            Iterable[EventData],
            super().get_logs(
                argument_filters=argument_filters, fromBlock=fromBlock, toBlock=toBlock, block_hash=block_hash
            ),
        )

    @classmethod
    def get_logs(  # type: ignore
        cls: Type["ExampleFlopContractEvent"],
        argument_filters: dict[str, Any] | None = None,
        fromBlock: BlockIdentifier | None = None,
        toBlock: BlockIdentifier | None = None,
        block_hash: HexBytes | None = None,
    ) -> Iterable[EventData]:
        return cast(
            Iterable[EventData],
            super().get_logs(
                argument_filters=argument_filters, fromBlock=fromBlock, toBlock=toBlock, block_hash=block_hash
            ),
        )

    def create_filter(  # type: ignore
        self: "ExampleFlopContractEvent",
        *,  # PEP 3102
        argument_filters: dict[str, Any] | None = None,
        fromBlock: BlockIdentifier | None = None,
        toBlock: BlockIdentifier = "latest",
        address: ChecksumAddress | None = None,
        topics: Sequence[Any] | None = None,
    ) -> LogFilter:
        return cast(
            LogFilter,
            super().create_filter(
                argument_filters=argument_filters, fromBlock=fromBlock, toBlock=toBlock, address=address, topics=topics
            ),
        )

    @classmethod
    def create_filter(  # type: ignore
        cls: Type["ExampleFlopContractEvent"],
        *,  # PEP 3102
        argument_filters: dict[str, Any] | None = None,
        fromBlock: BlockIdentifier | None = None,
        toBlock: BlockIdentifier = "latest",
        address: ChecksumAddress | None = None,
        topics: Sequence[Any] | None = None,
    ) -> LogFilter:
        return cast(
            LogFilter,
            super().create_filter(
                argument_filters=argument_filters, fromBlock=fromBlock, toBlock=toBlock, address=address, topics=topics
            ),
        )


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

    Flip: ExampleFlipContractEvent

    Flop: ExampleFlopContractEvent

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


class ExampleWrongChoiceContractError:
    """ContractError for WrongChoice."""

    # @combomethod destroys return types, so we are redefining functions as both class and instance
    # pylint: disable=function-redefined

    # 4 byte error selector
    selector: str
    # error signature, i.e. CustomError(uint256,bool)
    signature: str

    # pylint: disable=useless-parent-delegation
    def __init__(
        self: "ExampleWrongChoiceContractError",
    ) -> None:
        self.selector = "0xc13b30d4"
        self.signature = "WrongChoice(uint8,string)"

    def decode_error_data(  # type: ignore
        self: "ExampleWrongChoiceContractError",
        data: HexBytes,
        # TODO: instead of returning a tuple, return a dataclass with the input names and types just like we do for functions
    ) -> tuple[Any, ...]:
        """Decodes error data returns from a smart contract."""
        error_abi = cast(
            ABIFunction,
            [item for item in example_abi if item.get("name") == "WrongChoice" and item.get("type") == "error"][0],
        )
        types = get_abi_input_types(error_abi)
        abi_codec = ABICodec(default_registry)
        decoded = abi_codec.decode(types, data)
        return decoded

    @classmethod
    def decode_error_data(  # type: ignore
        cls: Type["ExampleWrongChoiceContractError"],
        data: HexBytes,
    ) -> tuple[Any, ...]:
        """Decodes error data returns from a smart contract."""
        error_abi = cast(
            ABIFunction,
            [item for item in example_abi if item.get("name") == "WrongChoice" and item.get("type") == "error"][0],
        )
        types = get_abi_input_types(error_abi)
        abi_codec = ABICodec(default_registry)
        decoded = abi_codec.decode(types, data)
        return decoded


class ExampleContractErrors:
    """ContractErrors for the Example contract."""

    WrongChoice: ExampleWrongChoiceContractError

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

        self._all = [
            self.WrongChoice,
        ]

    def decode_custom_error(self, data: str) -> tuple[Any, ...]:
        """Decodes a custom contract error."""
        selector = data[:10]
        for err in self._all:
            if err.selector == selector:
                return err.decode_error_data(HexBytes(data[10:]))

        raise ValueError(f"Example does not have a selector matching {selector}")


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


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

    abi: ABI = example_abi
    bytecode: bytes = HexBytes(example_bytecode)

    def __init__(self, address: ChecksumAddress | None = None) -> None:
        try:
            # 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()

        except FallbackNotFound:
            print("Fallback function not found. Continuing...")

    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.

        """

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

    @classmethod
    def deploy(cls, w3: Web3, account: LocalAccount | ChecksumAddress, constructorArgs: ConstructorArgs) -> Self:
        """Deploys and 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(*constructorArgs)

        # 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)
        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]:
        """Deploys and instance of the contract.

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

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

        return contract
