diff --git a/deployment/confirm.py b/deployment/confirm.py index 4f9adaa1..aae74c5c 100644 --- a/deployment/confirm.py +++ b/deployment/confirm.py @@ -11,6 +11,14 @@ def _confirm_deployment(contract_name: str) -> None: exit(-1) +def _continue() -> None: + """Asks the user to continue.""" + answer = input(f"Continue Y/N? ") + if answer.lower().strip() == "n": + print("Aborting deployment!") + exit(-1) + + def _confirm_zero_address() -> None: answer = input("Zero Address detected for deployment parameter; Continue? Y/N? ") if answer.lower().strip() == "n": diff --git a/deployment/params.py b/deployment/params.py index c89a4b88..4f57fe2b 100644 --- a/deployment/params.py +++ b/deployment/params.py @@ -7,9 +7,20 @@ from ape import chain, project from ape.api import AccountAPI, ReceiptAPI from ape.cli import get_user_selected_account -from ape.contracts.base import ContractContainer, ContractInstance +from ape.contracts.base import ( + ContractContainer, + ContractInstance, + ContractTransactionHandler +) from ape.utils import ZERO_ADDRESS -from deployment.confirm import _confirm_resolution +from eth_typing import ChecksumAddress +from ethpm_types import MethodABI +from web3.auto.gethdev import w3 + +from deployment.confirm import ( + _confirm_resolution, + _continue +) from deployment.constants import ( BYTES_PREFIX, DEPLOYER_INDICATOR, @@ -18,8 +29,6 @@ SPECIAL_VARIABLE_DELIMITER, VARIABLE_PREFIX, ) -from eth_typing import ChecksumAddress -from web3.auto.gethdev import w3 def _is_variable(param: Any) -> bool: @@ -237,6 +246,27 @@ def get_contract_container(contract: str) -> ContractContainer: return contract_container +def _get_function_abi(method: ContractTransactionHandler, args) -> MethodABI: + """Returns the function ABI for a contract function with a given number of arguments.""" + for abi in method.abis: + if len(abi.inputs) == len(args): + return abi + else: + raise ValueError(f"Could not find ABI for {method} with {len(args)} args") + + +def _validate_transaction_args(args: typing.Tuple[Any, ...], abi) -> typing.Dict[str, Any]: + """Validates the transaction arguments against the function ABI.""" + named_args = dict() + for arg, abi_input in zip(args, abi.inputs): + if not w3.is_encodable(abi_input.type, arg): + raise ValueError( + f"Argument '{arg}' of type '{type(arg)}' is not encodable as '{abi_input.type}'" + ) + named_args[abi_input.name] = arg + return named_args + + def validate_constructor_parameters(config: typing.OrderedDict[str, Any]) -> None: """Validates the constructor parameters for all contracts in a single config.""" available_contracts = list(config.keys()) @@ -323,12 +353,18 @@ def deploy(self, container: ContractContainer) -> ContractInstance: instance = deployer_account.deploy(*args, **kwargs) return instance - def transact(self, contract: ContractInstance, method: str, *args) -> ReceiptAPI: - print( - f"Transacting '{method}' on {contract.contract_type.name} " - f"({contract.address}) with args\n\t{args}" - ) - result = contract.invoke_transaction(method, *args, sender=self.get_account()) + def transact(self, method: ContractTransactionHandler, *args) -> ReceiptAPI: + abi = _get_function_abi(method=method, args=args) + named_args = _validate_transaction_args(args=args, abi=abi) + base_message = f"Transacting {method.contract.contract_type.name}[{method.contract.address[:10]}].{method}" + if named_args: + pretty_args = "\n\t".join(f"{k}={v}" for k, v in named_args.items()) + message = f"{base_message} with arguments:\n\t{pretty_args}" + else: + message = f"{base_message} with no arguments" + print(message) + _continue() + result = method(*args, sender=self.get_account()) return result @staticmethod diff --git a/deployment/registry.py b/deployment/registry.py index 813219a5..399e3687 100644 --- a/deployment/registry.py +++ b/deployment/registry.py @@ -4,11 +4,12 @@ from typing import Dict, List, NamedTuple, Optional from ape.contracts import ContractInstance -from deployment.params import get_contract_container -from deployment.utils import check_registry_filepath from eth_typing import ChecksumAddress from eth_utils import to_checksum_address +from deployment.params import get_contract_container +from deployment.utils import check_registry_filepath + class RegistryEntry(NamedTuple): """Represents a single entry in a nucypher-style contract registry.""" diff --git a/deployment/utils.py b/deployment/utils.py index 5696e05e..2552dc50 100644 --- a/deployment/utils.py +++ b/deployment/utils.py @@ -3,7 +3,6 @@ from typing import List from ape import networks -from ape.cli import get_user_selected_account from ape.contracts import ContractInstance from ape_etherscan.utils import API_KEY_ENV_KEY_MAP @@ -11,10 +10,6 @@ CURRENT_NETWORK, LOCAL_BLOCKCHAIN_ENVIRONMENTS ) -from deployment.params import ( - Deployer, - ConstructorParameters -) def check_registry_filepath(registry_filepath: Path) -> None: diff --git a/scripts/lynx/deploy_child.py b/scripts/lynx/deploy_child.py index 7412ce8a..115794b0 100644 --- a/scripts/lynx/deploy_child.py +++ b/scripts/lynx/deploy_child.py @@ -1,6 +1,7 @@ #!/usr/bin/python3 from ape import project + from deployment.constants import ( ARTIFACTS_DIR, CONSTRUCTOR_PARAMS_DIR, @@ -43,13 +44,13 @@ def main(): proxy = deployer.deploy(OZ_DEPENDENCY.TransparentUpgradeableProxy) taco_child_application = deployer.proxy(project.TACoChildApplication, proxy) - deployer.transact(mock_polygon_child, "setChildApplication", taco_child_application.address) + deployer.transact(mock_polygon_child.setChildApplication, taco_child_application.address) ritual_token = deployer.deploy(project.LynxRitualToken) coordinator = deployer.deploy(project.Coordinator) - deployer.transact(taco_child_application, "initialize", coordinator.address) + deployer.transact(taco_child_application.initialize, coordinator.address) global_allow_list = deployer.deploy(project.GlobalAllowList) diff --git a/scripts/lynx/deploy_root.py b/scripts/lynx/deploy_root.py index eb030a41..4cd514a6 100644 --- a/scripts/lynx/deploy_root.py +++ b/scripts/lynx/deploy_root.py @@ -1,6 +1,7 @@ #!/usr/bin/python3 from ape import project + from deployment.constants import ( ARTIFACTS_DIR, CONSTRUCTOR_PARAMS_DIR, @@ -44,12 +45,12 @@ def main(): proxy = deployer.deploy(OZ_DEPENDENCY.TransparentUpgradeableProxy) taco_application = deployer.proxy(project.TACoApplication, proxy) - deployer.transact(mock_threshold_staking, "setApplication", taco_application.address) + deployer.transact(mock_threshold_staking.setApplication, taco_application.address) - deployer.transact(taco_application, "initialize") + deployer.transact(taco_application.initialize) mock_polygon_root = deployer.deploy(project.MockPolygonRoot) - deployer.transact(taco_application, "setChildApplication", mock_polygon_root.address) + deployer.transact(taco_application.setChildApplication, mock_polygon_root.address) deployments = [ reward_token,