diff --git a/dao/contracts/Diamond.py b/dao/contracts/Diamond.py new file mode 100644 index 0000000..bc4a055 --- /dev/null +++ b/dao/contracts/Diamond.py @@ -0,0 +1,176 @@ +# Code generated by moonworm : https://github.com/bugout-dev/moonworm +# Moonworm version : 0.1.6 + +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.join(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 Diamond: + def __init__(self, contract_address: Optional[ChecksumAddress]): + self.contract_name = "Diamond" + self.address = contract_address + self.contract = None + self.abi = get_abi_json("Diamond") + if self.address is not None: + self.contract: Optional[Contract] = Contract.from_abi( + self.contract_name, self.address, self.abi + ) + + def deploy( + self, + _contract_owner: ChecksumAddress, + _diamond_cut_facet: ChecksumAddress, + transaction_config, + ): + contract_class = contract_from_build(self.contract_name) + deployed_contract = contract_class.deploy( + _contract_owner, _diamond_cut_facet, 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 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.confirmations is not None: + transaction_config["required_confs"] = args.confirmations + 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( + "--confirmations", + type=int, + default=None, + help="Number of confirmations to await before considering a transaction completed", + ) + + +def handle_deploy(args: argparse.Namespace) -> None: + network.connect(args.network) + transaction_config = get_transaction_config(args) + contract = Diamond(None) + result = contract.deploy( + _contract_owner=args.contract_owner_arg, + _diamond_cut_facet=args.diamond_cut_facet_arg, + transaction_config=transaction_config, + ) + print(result) + + +def generate_cli() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="CLI for Diamond") + 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( + "--contract-owner-arg", required=True, help="Type: address" + ) + deploy_parser.add_argument( + "--diamond-cut-facet-arg", required=True, help="Type: address" + ) + deploy_parser.set_defaults(func=handle_deploy) + + return parser + + +def main() -> None: + parser = generate_cli() + args = parser.parse_args() + args.func(args) + + +if __name__ == "__main__": + main() diff --git a/dao/DiamondCutFacet.py b/dao/contracts/DiamondCutFacet.py similarity index 100% rename from dao/DiamondCutFacet.py rename to dao/contracts/DiamondCutFacet.py diff --git a/dao/DiamondLoupeFacet.py b/dao/contracts/DiamondLoupeFacet.py similarity index 100% rename from dao/DiamondLoupeFacet.py rename to dao/contracts/DiamondLoupeFacet.py diff --git a/dao/OwnershipFacet.py b/dao/contracts/OwnershipFacet.py similarity index 100% rename from dao/OwnershipFacet.py rename to dao/contracts/OwnershipFacet.py