# Code generated by moonworm : https://github.com/bugout-dev/moonworm
# Moonworm version : 0.1.15

import argparse
import json
import os
from pathlib import Path
from typing import Any, Dict, List, Optional, Union

from brownie import Contract, network, project
from brownie.network.contract import ContractContainer
from eth_typing.evm import ChecksumAddress


PROJECT_DIRECTORY = os.path.abspath(os.path.dirname(__file__))
BUILD_DIRECTORY = os.path.join(PROJECT_DIRECTORY, "build", "contracts")


def boolean_argument_type(raw_value: str) -> bool:
    TRUE_VALUES = ["1", "t", "y", "true", "yes"]
    FALSE_VALUES = ["0", "f", "n", "false", "no"]

    if raw_value.lower() in TRUE_VALUES:
        return True
    elif raw_value.lower() in FALSE_VALUES:
        return False

    raise ValueError(
        f"Invalid boolean argument: {raw_value}. Value must be one of: {','.join(TRUE_VALUES + FALSE_VALUES)}"
    )


def bytes_argument_type(raw_value: str) -> bytes:
    return raw_value.encode()


def get_abi_json(abi_name: str) -> List[Dict[str, Any]]:
    abi_full_path = os.path.join(BUILD_DIRECTORY, f"{abi_name}.json")
    if not os.path.isfile(abi_full_path):
        raise IOError(
            f"File does not exist: {abi_full_path}. Maybe you have to compile the smart contracts?"
        )

    with open(abi_full_path, "r") as ifp:
        build = json.load(ifp)

    abi_json = build.get("abi")
    if abi_json is None:
        raise ValueError(f"Could not find ABI definition in: {abi_full_path}")

    return abi_json


def contract_from_build(abi_name: str) -> ContractContainer:
    # This is workaround because brownie currently doesn't support loading the same project multiple
    # times. This causes problems when using multiple contracts from the same project in the same
    # python project.
    PROJECT = project.main.Project("moonworm", Path(PROJECT_DIRECTORY))

    abi_full_path = os.path.join(BUILD_DIRECTORY, f"{abi_name}.json")
    if not os.path.isfile(abi_full_path):
        raise IOError(
            f"File does not exist: {abi_full_path}. Maybe you have to compile the smart contracts?"
        )

    with open(abi_full_path, "r") as ifp:
        build = json.load(ifp)

    return ContractContainer(PROJECT, build)


class DarkForest:
    def __init__(self, contract_address: Optional[ChecksumAddress]):
        self.contract_name = "DarkForest"
        self.address = contract_address
        self.contract = None
        self.abi = get_abi_json("DarkForest")
        if self.address is not None:
            self.contract: Optional[Contract] = Contract.from_abi(
                self.contract_name, self.address, self.abi
            )

    def deploy(
        self,
        _crypto_unicorns_address: ChecksumAddress,
        _unicorn_milk_token_address: ChecksumAddress,
        _terminus_address: ChecksumAddress,
        _unicorn_milk_staking_reward: int,
        _stake_period_seconds: int,
        _vrf_coordinator_address: ChecksumAddress,
        _link_token_address: ChecksumAddress,
        _chainlink_vrf_fee: int,
        _chainlink_vrf_keyhash: bytes,
        transaction_config,
    ):
        contract_class = contract_from_build(self.contract_name)
        deployed_contract = contract_class.deploy(
            _crypto_unicorns_address,
            _unicorn_milk_token_address,
            _terminus_address,
            _unicorn_milk_staking_reward,
            _stake_period_seconds,
            _vrf_coordinator_address,
            _link_token_address,
            _chainlink_vrf_fee,
            _chainlink_vrf_keyhash,
            transaction_config,
        )
        self.address = deployed_contract.address
        self.contract = deployed_contract

    def assert_contract_is_instantiated(self) -> None:
        if self.contract is None:
            raise Exception("contract has not been instantiated")

    def verify_contract(self):
        self.assert_contract_is_instantiated()
        contract_class = contract_from_build(self.contract_name)
        contract_class.publish_source(self.contract)

    def chainlink_vrf_fee(self) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.chainlinkVRFFee.call()

    def chainlink_vrf_keyhash(self) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.chainlinkVRFKeyhash.call()

    def complete_sacrifice(self, transaction_config) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.completeSacrifice(transaction_config)

    def contract_is_initialized(self) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.contractIsInitialized.call()

    def crypto_unicorns_address(self) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.cryptoUnicornsAddress.call()

    def current_sacrifice_id(self, sacrificer: ChecksumAddress) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.currentSacrificeId.call(sacrificer)

    def drain_erc20(self, token_address: ChecksumAddress, transaction_config) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.drainERC20(token_address, transaction_config)

    def exit_forest(self, token_id: int, transaction_config) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.exitForest(token_id, transaction_config)

    def lootbox_pool_ids(self) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.lootboxPoolIds.call()

    def num_staked(self, _staker: ChecksumAddress) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.numStaked.call(_staker)

    def offer_sacrifice(self, amount: int, transaction_config) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.offerSacrifice(amount, transaction_config)

    def on_erc721_received(
        self,
        operator: ChecksumAddress,
        from_: ChecksumAddress,
        token_id: int,
        data: bytes,
        transaction_config,
    ) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.onERC721Received(
            operator, from_, token_id, data, transaction_config
        )

    def owner(self) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.owner.call()

    def raw_fulfill_randomness(
        self, request_id: bytes, randomness: int, transaction_config
    ) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.rawFulfillRandomness(
            request_id, randomness, transaction_config
        )

    def remaining_shadowcorn_eggs(self) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.remainingShadowcornEggs.call()

    def renounce_ownership(self, transaction_config) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.renounceOwnership(transaction_config)

    def rescue_unicorn(self, token_id: int, transaction_config) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.rescueUnicorn(token_id, transaction_config)

    def reset_sacrifice_for_sacrificer(
        self, sacrificer: ChecksumAddress, transaction_config
    ) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.resetSacrificeForSacrificer(sacrificer, transaction_config)

    def set_chainlink_vrf_fee(self, _fee: int, transaction_config) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.setChainlinkVRFFee(_fee, transaction_config)

    def set_chainlink_vrf_keyhash(self, _keyhash: bytes, transaction_config) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.setChainlinkVRFKeyhash(_keyhash, transaction_config)

    def set_crypto_unicorns_address(
        self, _crypto_unicorns_address: ChecksumAddress, transaction_config
    ) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.setCryptoUnicornsAddress(
            _crypto_unicorns_address, transaction_config
        )

    def set_lootbox_pool_ids(self, _lootbox_pool_ids: List, transaction_config) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.setLootboxPoolIds(_lootbox_pool_ids, transaction_config)

    def set_shadowcorn_egg_bonuses_to_unicorn_milk(
        self, _bonuses: List, transaction_config
    ) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.setShadowcornEggBonusesToUnicornMilk(
            _bonuses, transaction_config
        )

    def set_shadowcorn_pool_ids(
        self, _shadowcorn_pool_ids: List, transaction_config
    ) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.setShadowcornPoolIds(
            _shadowcorn_pool_ids, transaction_config
        )

    def set_stake_period_seconds(
        self, _stake_period_seconds: int, transaction_config
    ) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.setStakePeriodSeconds(
            _stake_period_seconds, transaction_config
        )

    def set_terminus_address(
        self, _terminus_address: ChecksumAddress, transaction_config
    ) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.setTerminusAddress(_terminus_address, transaction_config)

    def set_unicorn_milk_staking_reward(
        self, _unicorn_milk_staking_reward: int, transaction_config
    ) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.setUnicornMilkStakingReward(
            _unicorn_milk_staking_reward, transaction_config
        )

    def set_unicorn_milk_thresholds(
        self, _unicorn_milk_thresholds: List, transaction_config
    ) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.setUnicornMilkThresholds(
            _unicorn_milk_thresholds, transaction_config
        )

    def set_unicorn_milk_token_address(
        self, _unicorn_milk_token_address: ChecksumAddress, transaction_config
    ) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.setUnicornMilkTokenAddress(
            _unicorn_milk_token_address, transaction_config
        )

    def shadowcorn_egg_bonuses_to_unicorn_milk(self) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.shadowcornEggBonusesToUnicornMilk.call()

    def shadowcorn_pool_ids(self) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.shadowcornPoolIds.call()

    def stake_period_seconds(self) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.stakePeriodSeconds.call()

    def staker(self, token_id: int) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.staker.call(token_id)

    def surrender_terminus_pools(self, transaction_config) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.surrenderTerminusPools(transaction_config)

    def terminus_address(self) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.terminusAddress.call()

    def token_of_staker_by_index(self, _staker: ChecksumAddress, index: int) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.tokenOfStakerByIndex.call(_staker, index)

    def transfer_ownership(self, new_owner: ChecksumAddress, transaction_config) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.transferOwnership(new_owner, transaction_config)

    def unicorn_milk_staking_reward(self) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.unicornMilkStakingReward.call()

    def unicorn_milk_thresholds(self) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.unicornMilkThresholds.call()

    def unicorn_milk_token_address(self) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.unicornMilkTokenAddress.call()

    def unstakes_at(self, token_id: int) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.unstakesAt.call(token_id)

    def view_sacrifice(self, _request_id: bytes) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.viewSacrifice.call(_request_id)

    def withdraw_erc20(
        self, token_address: ChecksumAddress, amount: int, transaction_config
    ) -> Any:
        self.assert_contract_is_instantiated()
        return self.contract.withdrawERC20(token_address, amount, transaction_config)


def get_transaction_config(args: argparse.Namespace) -> Dict[str, Any]:
    signer = network.accounts.load(args.sender, args.password)
    transaction_config: Dict[str, Any] = {"from": signer}
    if args.gas_price is not None:
        transaction_config["gas_price"] = args.gas_price
    if args.max_fee_per_gas is not None:
        transaction_config["max_fee"] = args.max_fee_per_gas
    if args.max_priority_fee_per_gas is not None:
        transaction_config["priority_fee"] = args.max_priority_fee_per_gas
    if args.confirmations is not None:
        transaction_config["required_confs"] = args.confirmations
    if args.nonce is not None:
        transaction_config["nonce"] = args.nonce
    return transaction_config


def add_default_arguments(parser: argparse.ArgumentParser, transact: bool) -> None:
    parser.add_argument(
        "--network", required=True, help="Name of brownie network to connect to"
    )
    parser.add_argument(
        "--address", required=False, help="Address of deployed contract to connect to"
    )
    if not transact:
        return
    parser.add_argument(
        "--sender", required=True, help="Path to keystore file for transaction sender"
    )
    parser.add_argument(
        "--password",
        required=False,
        help="Password to keystore file (if you do not provide it, you will be prompted for it)",
    )
    parser.add_argument(
        "--gas-price", default=None, help="Gas price at which to submit transaction"
    )
    parser.add_argument(
        "--max-fee-per-gas",
        default=None,
        help="Max fee per gas for EIP1559 transactions",
    )
    parser.add_argument(
        "--max-priority-fee-per-gas",
        default=None,
        help="Max priority fee per gas for EIP1559 transactions",
    )
    parser.add_argument(
        "--confirmations",
        type=int,
        default=None,
        help="Number of confirmations to await before considering a transaction completed",
    )
    parser.add_argument(
        "--nonce", type=int, default=None, help="Nonce for the transaction (optional)"
    )
    parser.add_argument(
        "--value", default=None, help="Value of the transaction in wei(optional)"
    )


def handle_deploy(args: argparse.Namespace) -> None:
    network.connect(args.network)
    transaction_config = get_transaction_config(args)
    contract = DarkForest(None)
    result = contract.deploy(
        _crypto_unicorns_address=args.crypto_unicorns_address_arg,
        _unicorn_milk_token_address=args.unicorn_milk_token_address_arg,
        _terminus_address=args.terminus_address_arg,
        _unicorn_milk_staking_reward=args.unicorn_milk_staking_reward_arg,
        _stake_period_seconds=args.stake_period_seconds_arg,
        _vrf_coordinator_address=args.vrf_coordinator_address_arg,
        _link_token_address=args.link_token_address_arg,
        _chainlink_vrf_fee=args.chainlink_vrf_fee_arg,
        _chainlink_vrf_keyhash=args.chainlink_vrf_keyhash_arg,
        transaction_config=transaction_config,
    )
    print(result)


def handle_verify_contract(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.verify_contract()
    print(result)


def handle_chainlink_vrf_fee(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.chainlink_vrf_fee()
    print(result)


def handle_chainlink_vrf_keyhash(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.chainlink_vrf_keyhash()
    print(result)


def handle_complete_sacrifice(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.complete_sacrifice(transaction_config=transaction_config)
    print(result)


def handle_contract_is_initialized(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.contract_is_initialized()
    print(result)


def handle_crypto_unicorns_address(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.crypto_unicorns_address()
    print(result)


def handle_current_sacrifice_id(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.current_sacrifice_id(sacrificer=args.sacrificer)
    print(result)


def handle_drain_erc20(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.drain_erc20(
        token_address=args.token_address, transaction_config=transaction_config
    )
    print(result)


def handle_exit_forest(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.exit_forest(
        token_id=args.token_id, transaction_config=transaction_config
    )
    print(result)


def handle_lootbox_pool_ids(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.lootbox_pool_ids()
    print(result)


def handle_num_staked(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.num_staked(_staker=args.staker_arg)
    print(result)


def handle_offer_sacrifice(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.offer_sacrifice(
        amount=args.amount, transaction_config=transaction_config
    )
    print(result)


def handle_on_erc721_received(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.on_erc721_received(
        operator=args.operator,
        from_=args.from_arg,
        token_id=args.token_id,
        data=args.data,
        transaction_config=transaction_config,
    )
    print(result)


def handle_owner(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.owner()
    print(result)


def handle_raw_fulfill_randomness(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.raw_fulfill_randomness(
        request_id=args.request_id,
        randomness=args.randomness,
        transaction_config=transaction_config,
    )
    print(result)


def handle_remaining_shadowcorn_eggs(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.remaining_shadowcorn_eggs()
    print(result)


def handle_renounce_ownership(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.renounce_ownership(transaction_config=transaction_config)
    print(result)


def handle_rescue_unicorn(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.rescue_unicorn(
        token_id=args.token_id, transaction_config=transaction_config
    )
    print(result)


def handle_reset_sacrifice_for_sacrificer(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.reset_sacrifice_for_sacrificer(
        sacrificer=args.sacrificer, transaction_config=transaction_config
    )
    print(result)


def handle_set_chainlink_vrf_fee(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.set_chainlink_vrf_fee(
        _fee=args.fee_arg, transaction_config=transaction_config
    )
    print(result)


def handle_set_chainlink_vrf_keyhash(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.set_chainlink_vrf_keyhash(
        _keyhash=args.keyhash_arg, transaction_config=transaction_config
    )
    print(result)


def handle_set_crypto_unicorns_address(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.set_crypto_unicorns_address(
        _crypto_unicorns_address=args.crypto_unicorns_address_arg,
        transaction_config=transaction_config,
    )
    print(result)


def handle_set_lootbox_pool_ids(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.set_lootbox_pool_ids(
        _lootbox_pool_ids=args.lootbox_pool_ids_arg,
        transaction_config=transaction_config,
    )
    print(result)


def handle_set_shadowcorn_egg_bonuses_to_unicorn_milk(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.set_shadowcorn_egg_bonuses_to_unicorn_milk(
        _bonuses=args.bonuses_arg, transaction_config=transaction_config
    )
    print(result)


def handle_set_shadowcorn_pool_ids(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.set_shadowcorn_pool_ids(
        _shadowcorn_pool_ids=args.shadowcorn_pool_ids_arg,
        transaction_config=transaction_config,
    )
    print(result)


def handle_set_stake_period_seconds(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.set_stake_period_seconds(
        _stake_period_seconds=args.stake_period_seconds_arg,
        transaction_config=transaction_config,
    )
    print(result)


def handle_set_terminus_address(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.set_terminus_address(
        _terminus_address=args.terminus_address_arg,
        transaction_config=transaction_config,
    )
    print(result)


def handle_set_unicorn_milk_staking_reward(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.set_unicorn_milk_staking_reward(
        _unicorn_milk_staking_reward=args.unicorn_milk_staking_reward_arg,
        transaction_config=transaction_config,
    )
    print(result)


def handle_set_unicorn_milk_thresholds(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.set_unicorn_milk_thresholds(
        _unicorn_milk_thresholds=args.unicorn_milk_thresholds_arg,
        transaction_config=transaction_config,
    )
    print(result)


def handle_set_unicorn_milk_token_address(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.set_unicorn_milk_token_address(
        _unicorn_milk_token_address=args.unicorn_milk_token_address_arg,
        transaction_config=transaction_config,
    )
    print(result)


def handle_shadowcorn_egg_bonuses_to_unicorn_milk(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.shadowcorn_egg_bonuses_to_unicorn_milk()
    print(result)


def handle_shadowcorn_pool_ids(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.shadowcorn_pool_ids()
    print(result)


def handle_stake_period_seconds(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.stake_period_seconds()
    print(result)


def handle_staker(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.staker(token_id=args.token_id)
    print(result)


def handle_surrender_terminus_pools(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.surrender_terminus_pools(transaction_config=transaction_config)
    print(result)


def handle_terminus_address(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.terminus_address()
    print(result)


def handle_token_of_staker_by_index(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.token_of_staker_by_index(
        _staker=args.staker_arg, index=args.index
    )
    print(result)


def handle_transfer_ownership(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.transfer_ownership(
        new_owner=args.new_owner, transaction_config=transaction_config
    )
    print(result)


def handle_unicorn_milk_staking_reward(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.unicorn_milk_staking_reward()
    print(result)


def handle_unicorn_milk_thresholds(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.unicorn_milk_thresholds()
    print(result)


def handle_unicorn_milk_token_address(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.unicorn_milk_token_address()
    print(result)


def handle_unstakes_at(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.unstakes_at(token_id=args.token_id)
    print(result)


def handle_view_sacrifice(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    result = contract.view_sacrifice(_request_id=args.request_id_arg)
    print(result)


def handle_withdraw_erc20(args: argparse.Namespace) -> None:
    network.connect(args.network)
    contract = DarkForest(args.address)
    transaction_config = get_transaction_config(args)
    result = contract.withdraw_erc20(
        token_address=args.token_address,
        amount=args.amount,
        transaction_config=transaction_config,
    )
    print(result)


def generate_cli() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(description="CLI for DarkForest")
    parser.set_defaults(func=lambda _: parser.print_help())
    subcommands = parser.add_subparsers()

    deploy_parser = subcommands.add_parser("deploy")
    add_default_arguments(deploy_parser, True)
    deploy_parser.add_argument(
        "--crypto-unicorns-address-arg", required=True, help="Type: address"
    )
    deploy_parser.add_argument(
        "--unicorn-milk-token-address-arg", required=True, help="Type: address"
    )
    deploy_parser.add_argument(
        "--terminus-address-arg", required=True, help="Type: address"
    )
    deploy_parser.add_argument(
        "--unicorn-milk-staking-reward-arg",
        required=True,
        help="Type: uint256",
        type=int,
    )
    deploy_parser.add_argument(
        "--stake-period-seconds-arg", required=True, help="Type: uint256", type=int
    )
    deploy_parser.add_argument(
        "--vrf-coordinator-address-arg", required=True, help="Type: address"
    )
    deploy_parser.add_argument(
        "--link-token-address-arg", required=True, help="Type: address"
    )
    deploy_parser.add_argument(
        "--chainlink-vrf-fee-arg", required=True, help="Type: uint256", type=int
    )
    deploy_parser.add_argument(
        "--chainlink-vrf-keyhash-arg",
        required=True,
        help="Type: bytes32",
        type=bytes_argument_type,
    )
    deploy_parser.set_defaults(func=handle_deploy)

    verify_contract_parser = subcommands.add_parser("verify-contract")
    add_default_arguments(verify_contract_parser, False)
    verify_contract_parser.set_defaults(func=handle_verify_contract)

    chainlink_vrf_fee_parser = subcommands.add_parser("chainlink-vrf-fee")
    add_default_arguments(chainlink_vrf_fee_parser, False)
    chainlink_vrf_fee_parser.set_defaults(func=handle_chainlink_vrf_fee)

    chainlink_vrf_keyhash_parser = subcommands.add_parser("chainlink-vrf-keyhash")
    add_default_arguments(chainlink_vrf_keyhash_parser, False)
    chainlink_vrf_keyhash_parser.set_defaults(func=handle_chainlink_vrf_keyhash)

    complete_sacrifice_parser = subcommands.add_parser("complete-sacrifice")
    add_default_arguments(complete_sacrifice_parser, True)
    complete_sacrifice_parser.set_defaults(func=handle_complete_sacrifice)

    contract_is_initialized_parser = subcommands.add_parser("contract-is-initialized")
    add_default_arguments(contract_is_initialized_parser, False)
    contract_is_initialized_parser.set_defaults(func=handle_contract_is_initialized)

    crypto_unicorns_address_parser = subcommands.add_parser("crypto-unicorns-address")
    add_default_arguments(crypto_unicorns_address_parser, False)
    crypto_unicorns_address_parser.set_defaults(func=handle_crypto_unicorns_address)

    current_sacrifice_id_parser = subcommands.add_parser("current-sacrifice-id")
    add_default_arguments(current_sacrifice_id_parser, False)
    current_sacrifice_id_parser.add_argument(
        "--sacrificer", required=True, help="Type: address"
    )
    current_sacrifice_id_parser.set_defaults(func=handle_current_sacrifice_id)

    drain_erc20_parser = subcommands.add_parser("drain-erc20")
    add_default_arguments(drain_erc20_parser, True)
    drain_erc20_parser.add_argument(
        "--token-address", required=True, help="Type: address"
    )
    drain_erc20_parser.set_defaults(func=handle_drain_erc20)

    exit_forest_parser = subcommands.add_parser("exit-forest")
    add_default_arguments(exit_forest_parser, True)
    exit_forest_parser.add_argument(
        "--token-id", required=True, help="Type: uint256", type=int
    )
    exit_forest_parser.set_defaults(func=handle_exit_forest)

    lootbox_pool_ids_parser = subcommands.add_parser("lootbox-pool-ids")
    add_default_arguments(lootbox_pool_ids_parser, False)
    lootbox_pool_ids_parser.set_defaults(func=handle_lootbox_pool_ids)

    num_staked_parser = subcommands.add_parser("num-staked")
    add_default_arguments(num_staked_parser, False)
    num_staked_parser.add_argument("--staker-arg", required=True, help="Type: address")
    num_staked_parser.set_defaults(func=handle_num_staked)

    offer_sacrifice_parser = subcommands.add_parser("offer-sacrifice")
    add_default_arguments(offer_sacrifice_parser, True)
    offer_sacrifice_parser.add_argument(
        "--amount", required=True, help="Type: uint256", type=int
    )
    offer_sacrifice_parser.set_defaults(func=handle_offer_sacrifice)

    on_erc721_received_parser = subcommands.add_parser("on-erc721-received")
    add_default_arguments(on_erc721_received_parser, True)
    on_erc721_received_parser.add_argument(
        "--operator", required=True, help="Type: address"
    )
    on_erc721_received_parser.add_argument(
        "--from-arg", required=True, help="Type: address"
    )
    on_erc721_received_parser.add_argument(
        "--token-id", required=True, help="Type: uint256", type=int
    )
    on_erc721_received_parser.add_argument(
        "--data", required=True, help="Type: bytes", type=bytes_argument_type
    )
    on_erc721_received_parser.set_defaults(func=handle_on_erc721_received)

    owner_parser = subcommands.add_parser("owner")
    add_default_arguments(owner_parser, False)
    owner_parser.set_defaults(func=handle_owner)

    raw_fulfill_randomness_parser = subcommands.add_parser("raw-fulfill-randomness")
    add_default_arguments(raw_fulfill_randomness_parser, True)
    raw_fulfill_randomness_parser.add_argument(
        "--request-id", required=True, help="Type: bytes32", type=bytes_argument_type
    )
    raw_fulfill_randomness_parser.add_argument(
        "--randomness", required=True, help="Type: uint256", type=int
    )
    raw_fulfill_randomness_parser.set_defaults(func=handle_raw_fulfill_randomness)

    remaining_shadowcorn_eggs_parser = subcommands.add_parser(
        "remaining-shadowcorn-eggs"
    )
    add_default_arguments(remaining_shadowcorn_eggs_parser, False)
    remaining_shadowcorn_eggs_parser.set_defaults(func=handle_remaining_shadowcorn_eggs)

    renounce_ownership_parser = subcommands.add_parser("renounce-ownership")
    add_default_arguments(renounce_ownership_parser, True)
    renounce_ownership_parser.set_defaults(func=handle_renounce_ownership)

    rescue_unicorn_parser = subcommands.add_parser("rescue-unicorn")
    add_default_arguments(rescue_unicorn_parser, True)
    rescue_unicorn_parser.add_argument(
        "--token-id", required=True, help="Type: uint256", type=int
    )
    rescue_unicorn_parser.set_defaults(func=handle_rescue_unicorn)

    reset_sacrifice_for_sacrificer_parser = subcommands.add_parser(
        "reset-sacrifice-for-sacrificer"
    )
    add_default_arguments(reset_sacrifice_for_sacrificer_parser, True)
    reset_sacrifice_for_sacrificer_parser.add_argument(
        "--sacrificer", required=True, help="Type: address"
    )
    reset_sacrifice_for_sacrificer_parser.set_defaults(
        func=handle_reset_sacrifice_for_sacrificer
    )

    set_chainlink_vrf_fee_parser = subcommands.add_parser("set-chainlink-vrf-fee")
    add_default_arguments(set_chainlink_vrf_fee_parser, True)
    set_chainlink_vrf_fee_parser.add_argument(
        "--fee-arg", required=True, help="Type: uint256", type=int
    )
    set_chainlink_vrf_fee_parser.set_defaults(func=handle_set_chainlink_vrf_fee)

    set_chainlink_vrf_keyhash_parser = subcommands.add_parser(
        "set-chainlink-vrf-keyhash"
    )
    add_default_arguments(set_chainlink_vrf_keyhash_parser, True)
    set_chainlink_vrf_keyhash_parser.add_argument(
        "--keyhash-arg", required=True, help="Type: bytes32", type=bytes_argument_type
    )
    set_chainlink_vrf_keyhash_parser.set_defaults(func=handle_set_chainlink_vrf_keyhash)

    set_crypto_unicorns_address_parser = subcommands.add_parser(
        "set-crypto-unicorns-address"
    )
    add_default_arguments(set_crypto_unicorns_address_parser, True)
    set_crypto_unicorns_address_parser.add_argument(
        "--crypto-unicorns-address-arg", required=True, help="Type: address"
    )
    set_crypto_unicorns_address_parser.set_defaults(
        func=handle_set_crypto_unicorns_address
    )

    set_lootbox_pool_ids_parser = subcommands.add_parser("set-lootbox-pool-ids")
    add_default_arguments(set_lootbox_pool_ids_parser, True)
    set_lootbox_pool_ids_parser.add_argument(
        "--lootbox-pool-ids-arg", required=True, help="Type: uint256[3]", nargs="+"
    )
    set_lootbox_pool_ids_parser.set_defaults(func=handle_set_lootbox_pool_ids)

    set_shadowcorn_egg_bonuses_to_unicorn_milk_parser = subcommands.add_parser(
        "set-shadowcorn-egg-bonuses-to-unicorn-milk"
    )
    add_default_arguments(set_shadowcorn_egg_bonuses_to_unicorn_milk_parser, True)
    set_shadowcorn_egg_bonuses_to_unicorn_milk_parser.add_argument(
        "--bonuses-arg", required=True, help="Type: uint256[3]", nargs="+"
    )
    set_shadowcorn_egg_bonuses_to_unicorn_milk_parser.set_defaults(
        func=handle_set_shadowcorn_egg_bonuses_to_unicorn_milk
    )

    set_shadowcorn_pool_ids_parser = subcommands.add_parser("set-shadowcorn-pool-ids")
    add_default_arguments(set_shadowcorn_pool_ids_parser, True)
    set_shadowcorn_pool_ids_parser.add_argument(
        "--shadowcorn-pool-ids-arg", required=True, help="Type: uint256[3]", nargs="+"
    )
    set_shadowcorn_pool_ids_parser.set_defaults(func=handle_set_shadowcorn_pool_ids)

    set_stake_period_seconds_parser = subcommands.add_parser("set-stake-period-seconds")
    add_default_arguments(set_stake_period_seconds_parser, True)
    set_stake_period_seconds_parser.add_argument(
        "--stake-period-seconds-arg", required=True, help="Type: uint256", type=int
    )
    set_stake_period_seconds_parser.set_defaults(func=handle_set_stake_period_seconds)

    set_terminus_address_parser = subcommands.add_parser("set-terminus-address")
    add_default_arguments(set_terminus_address_parser, True)
    set_terminus_address_parser.add_argument(
        "--terminus-address-arg", required=True, help="Type: address"
    )
    set_terminus_address_parser.set_defaults(func=handle_set_terminus_address)

    set_unicorn_milk_staking_reward_parser = subcommands.add_parser(
        "set-unicorn-milk-staking-reward"
    )
    add_default_arguments(set_unicorn_milk_staking_reward_parser, True)
    set_unicorn_milk_staking_reward_parser.add_argument(
        "--unicorn-milk-staking-reward-arg",
        required=True,
        help="Type: uint256",
        type=int,
    )
    set_unicorn_milk_staking_reward_parser.set_defaults(
        func=handle_set_unicorn_milk_staking_reward
    )

    set_unicorn_milk_thresholds_parser = subcommands.add_parser(
        "set-unicorn-milk-thresholds"
    )
    add_default_arguments(set_unicorn_milk_thresholds_parser, True)
    set_unicorn_milk_thresholds_parser.add_argument(
        "--unicorn-milk-thresholds-arg",
        required=True,
        help="Type: uint256[3]",
        nargs="+",
    )
    set_unicorn_milk_thresholds_parser.set_defaults(
        func=handle_set_unicorn_milk_thresholds
    )

    set_unicorn_milk_token_address_parser = subcommands.add_parser(
        "set-unicorn-milk-token-address"
    )
    add_default_arguments(set_unicorn_milk_token_address_parser, True)
    set_unicorn_milk_token_address_parser.add_argument(
        "--unicorn-milk-token-address-arg", required=True, help="Type: address"
    )
    set_unicorn_milk_token_address_parser.set_defaults(
        func=handle_set_unicorn_milk_token_address
    )

    shadowcorn_egg_bonuses_to_unicorn_milk_parser = subcommands.add_parser(
        "shadowcorn-egg-bonuses-to-unicorn-milk"
    )
    add_default_arguments(shadowcorn_egg_bonuses_to_unicorn_milk_parser, False)
    shadowcorn_egg_bonuses_to_unicorn_milk_parser.set_defaults(
        func=handle_shadowcorn_egg_bonuses_to_unicorn_milk
    )

    shadowcorn_pool_ids_parser = subcommands.add_parser("shadowcorn-pool-ids")
    add_default_arguments(shadowcorn_pool_ids_parser, False)
    shadowcorn_pool_ids_parser.set_defaults(func=handle_shadowcorn_pool_ids)

    stake_period_seconds_parser = subcommands.add_parser("stake-period-seconds")
    add_default_arguments(stake_period_seconds_parser, False)
    stake_period_seconds_parser.set_defaults(func=handle_stake_period_seconds)

    staker_parser = subcommands.add_parser("staker")
    add_default_arguments(staker_parser, False)
    staker_parser.add_argument(
        "--token-id", required=True, help="Type: uint256", type=int
    )
    staker_parser.set_defaults(func=handle_staker)

    surrender_terminus_pools_parser = subcommands.add_parser("surrender-terminus-pools")
    add_default_arguments(surrender_terminus_pools_parser, True)
    surrender_terminus_pools_parser.set_defaults(func=handle_surrender_terminus_pools)

    terminus_address_parser = subcommands.add_parser("terminus-address")
    add_default_arguments(terminus_address_parser, False)
    terminus_address_parser.set_defaults(func=handle_terminus_address)

    token_of_staker_by_index_parser = subcommands.add_parser("token-of-staker-by-index")
    add_default_arguments(token_of_staker_by_index_parser, False)
    token_of_staker_by_index_parser.add_argument(
        "--staker-arg", required=True, help="Type: address"
    )
    token_of_staker_by_index_parser.add_argument(
        "--index", required=True, help="Type: uint256", type=int
    )
    token_of_staker_by_index_parser.set_defaults(func=handle_token_of_staker_by_index)

    transfer_ownership_parser = subcommands.add_parser("transfer-ownership")
    add_default_arguments(transfer_ownership_parser, True)
    transfer_ownership_parser.add_argument(
        "--new-owner", required=True, help="Type: address"
    )
    transfer_ownership_parser.set_defaults(func=handle_transfer_ownership)

    unicorn_milk_staking_reward_parser = subcommands.add_parser(
        "unicorn-milk-staking-reward"
    )
    add_default_arguments(unicorn_milk_staking_reward_parser, False)
    unicorn_milk_staking_reward_parser.set_defaults(
        func=handle_unicorn_milk_staking_reward
    )

    unicorn_milk_thresholds_parser = subcommands.add_parser("unicorn-milk-thresholds")
    add_default_arguments(unicorn_milk_thresholds_parser, False)
    unicorn_milk_thresholds_parser.set_defaults(func=handle_unicorn_milk_thresholds)

    unicorn_milk_token_address_parser = subcommands.add_parser(
        "unicorn-milk-token-address"
    )
    add_default_arguments(unicorn_milk_token_address_parser, False)
    unicorn_milk_token_address_parser.set_defaults(
        func=handle_unicorn_milk_token_address
    )

    unstakes_at_parser = subcommands.add_parser("unstakes-at")
    add_default_arguments(unstakes_at_parser, False)
    unstakes_at_parser.add_argument(
        "--token-id", required=True, help="Type: uint256", type=int
    )
    unstakes_at_parser.set_defaults(func=handle_unstakes_at)

    view_sacrifice_parser = subcommands.add_parser("view-sacrifice")
    add_default_arguments(view_sacrifice_parser, False)
    view_sacrifice_parser.add_argument(
        "--request-id-arg",
        required=True,
        help="Type: bytes32",
        type=bytes_argument_type,
    )
    view_sacrifice_parser.set_defaults(func=handle_view_sacrifice)

    withdraw_erc20_parser = subcommands.add_parser("withdraw-erc20")
    add_default_arguments(withdraw_erc20_parser, True)
    withdraw_erc20_parser.add_argument(
        "--token-address", required=True, help="Type: address"
    )
    withdraw_erc20_parser.add_argument(
        "--amount", required=True, help="Type: uint256", type=int
    )
    withdraw_erc20_parser.set_defaults(func=handle_withdraw_erc20)

    return parser


def main() -> None:
    parser = generate_cli()
    args = parser.parse_args()
    args.func(args)


if __name__ == "__main__":
    main()
