def upgrade(general_config, actor_options, retarget, target_address, ignore_deployed, multisig): """ Upgrade NuCypher existing proxy contract deployments. """ # Init emitter = general_config.emitter ADMINISTRATOR, _, _, registry = actor_options.create_actor(emitter, is_multisig=bool(multisig)) # FIXME: Workaround for building MultiSig TXs contract_name = actor_options.contract_name if not contract_name: raise click.BadArgumentUsage(message="--contract-name is required when using --upgrade") if multisig: if not target_address: raise click.BadArgumentUsage(message="--multisig requires using --target-address.") if not actor_options.force: click.confirm(f"Confirm building a re-target transaction for {contract_name}'s proxy to {target_address}?", abort=True) transaction = ADMINISTRATOR.retarget_proxy(contract_name=contract_name, target_address=target_address, just_build_transaction=True) trustee_address = select_client_account(emitter=emitter, prompt="Select trustee address", provider_uri=actor_options.provider_uri, show_balances=False) if not actor_options.force: click.confirm(f"Selected {trustee_address} - Continue?", abort=True) trustee = Trustee(registry=registry, checksum_address=trustee_address) transaction_proposal = trustee.create_transaction_proposal(transaction) emitter.message(f"Transaction to retarget {contract_name} proxy to {target_address} was built:", color='green') paint_multisig_proposed_transaction(emitter, transaction_proposal) # TODO: Show decoded function too filepath = f'proposal-{trustee.multisig_agent.contract_address[:8]}-TX-{transaction_proposal.nonce}.json' transaction_proposal.write(filepath=filepath) emitter.echo(f"Saved proposal to {filepath}", color='blue', bold=True) elif retarget: if not target_address: raise click.BadArgumentUsage(message="--target-address is required when using --retarget") if not actor_options.force: click.confirm(f"Confirm re-target {contract_name}'s proxy to {target_address}?", abort=True) receipt = ADMINISTRATOR.retarget_proxy(contract_name=contract_name, target_address=target_address) emitter.message(f"Successfully re-targeted {contract_name} proxy to {target_address}", color='green') paint_receipt_summary(emitter=emitter, receipt=receipt) else: if not actor_options.force: click.confirm(f"Confirm deploy new version of {contract_name} and retarget proxy?", abort=True) receipts = ADMINISTRATOR.upgrade_contract(contract_name=contract_name, ignore_deployed=ignore_deployed) emitter.message(f"Successfully deployed and upgraded {contract_name}", color='green') for name, receipt in receipts.items(): paint_receipt_summary(emitter=emitter, receipt=receipt)
def multisig(general_config, actor_options, action, proposal): """ Perform operations via a MultiSig contract """ # Init emitter = general_config.emitter _ensure_config_root(actor_options.config_root) blockchain = _initialize_blockchain(actor_options.poa, actor_options.provider_uri, emitter) local_registry = establish_deployer_registry( emitter=emitter, use_existing_registry=True, ) # Warnings # _pre_launch_warnings(emitter, etherscan, hw_wallet) multisig_agent = ContractAgency.get_agent( MultiSigAgent, registry=local_registry, provider_uri=actor_options.provider_uri) token_agent = ContractAgency.get_agent(NucypherTokenAgent, registry=local_registry) if action == 'inspect': paint_multisig_contract_info(emitter, multisig_agent, token_agent) elif action == 'sign': if not proposal: raise ValueError("multisig sign requires the use of --proposal") with open(proposal) as json_file: proposal = json.load(json_file) executive_summary = proposal['parameters'] name, version, address, abi = local_registry.search( contract_address=executive_summary['target_address']) # TODO: This assumes that we're always signing proxy retargetting. For the moment is true. proxy_contract = blockchain.client.w3.eth.contract( abi=abi, address=address, version=version, ContractFactoryClass=blockchain._contract_factory) paint_multisig_proposed_transaction(emitter, proposal, proxy_contract) click.confirm("Proceed with signing?", abort=True) # TODO: Blocked by lack of support to EIP191 - #1566 elif action == 'execute': pass # TODO
def execute(general_config, blockchain_options, multisig_options, proposal): """ Collect authorizations from executives and execute transaction through MultiSig contract """ # Init emitter = general_config.emitter #_ensure_config_root(actor_options.config_root) blockchain = blockchain_options.connect_blockchain(emitter, general_config.debug) registry = blockchain_options.get_registry() proposal = Proposal.from_file(proposal) if not multisig_options.checksum_address: multisig_options.checksum_address = select_client_account( emitter=emitter, provider_uri=blockchain_options.provider_uri, poa=blockchain_options.poa, network=blockchain_options.network, registry=registry, show_balances=True) name, version, address, abi = registry.search( contract_address=proposal.target_address) # TODO: This assumes that we're always signing proxy retargetting. For the moment is true. proxy_contract = blockchain.client.w3.eth.contract( abi=abi, address=address, version=version, ContractFactoryClass=blockchain._contract_factory) paint_multisig_proposed_transaction(emitter, proposal, proxy_contract) trustee = multisig_options.create_trustee(registry) threshold = trustee.multisig_agent.threshold while len(trustee.authorizations) < threshold: auth_hex = click.prompt("Signature", type=click.STRING) authorization = Authorization.from_hex(auth_hex) executive_address = trustee.add_authorization(authorization, proposal) emitter.echo(f"Added authorization from executive {executive_address}", color='green') click.confirm( "\nCollected required authorizations. Proceed with execution?", abort=True) receipt = trustee.execute(proposal) paint_receipt_summary(emitter, receipt)
def propose(general_config, blockchain_options, multisig_options): """ Create a proposal of MultiSig transaction """ # TODO: Extend this command to cover this list of proposals # - Add new MultiSig owner # - Remove MultiSig owner # - Change threshold of MultiSig # - Upgrade contract (in particular, retarget to a deployed one) # - Transfer ownership of contract # - Send ETH from MultiSig # - Send tokens from MultiSig # - Change min reward rate range in PolicyManager # - Send raw transaction # Init emitter = general_config.emitter #_ensure_config_root(actor_options.config_root) blockchain = blockchain_options.connect_blockchain(emitter, general_config.debug) registry = blockchain_options.get_registry() if not multisig_options.checksum_address: multisig_options.checksum_address = select_client_account( emitter=emitter, provider_uri=blockchain_options.provider_uri, poa=blockchain_options.poa, network=blockchain_options.network, registry=registry, show_balances=True) trustee = multisig_options.create_transactingless_trustee(registry) # As a PoC, this command only allows to change the threshold # TODO: Think in the UX for choosing between different types of proposals new_threshold = click.prompt("New threshold", type=click.INT) proposal = trustee.propose_changing_threshold(new_threshold) paint_multisig_proposed_transaction(emitter=emitter, proposal=proposal, registry=registry) filepath = f'proposal-changeThreshold-{trustee.multisig_agent.contract_address[:8]}-TX-{proposal.nonce}.json' proposal.write(filepath=filepath) emitter.echo(f"✅ Saved proposal to {filepath}", color='blue', bold=True)
def sign(general_config, blockchain_options, multisig_options, proposal): """ Sign a proposed transaction before being sent to the MultiSig contract for execution """ # Init emitter = general_config.emitter #_ensure_config_root(actor_options.config_root) blockchain = blockchain_options.connect_blockchain(emitter, general_config.debug) registry = blockchain_options.get_registry() proposal = Proposal.from_file(proposal) if not multisig_options.checksum_address: multisig_options.checksum_address = select_client_account( emitter=emitter, provider_uri=blockchain_options.provider_uri, poa=blockchain_options.poa, network=blockchain_options.network, registry=registry, show_balances=True) name, version, address, abi = registry.search( contract_address=proposal.target_address) # TODO: This assumes that we're always signing proxy retargetting. For the moment is true. proxy_contract = blockchain.client.w3.eth.contract( abi=abi, address=address, version=version, ContractFactoryClass=blockchain._contract_factory) paint_multisig_proposed_transaction(emitter, proposal, proxy_contract) click.confirm("Proceed with signing?", abort=True) executive = multisig_options.create_transactingless_executive( registry) # FIXME: Since we use a signer, don't ask for PW authorization = executive.authorize_proposal(proposal) emitter.echo( f"\nSignature received from {authorization.recover_executive_address(proposal)}:\n" ) emitter.echo(f"{authorization.serialize().hex()}\n", bold=True, color='green')
def upgrade(general_config, actor_options, retarget, target_address, ignore_deployed, multisig): """ Upgrade NuCypher existing proxy contract deployments. """ # Init emitter = general_config.emitter ADMINISTRATOR, _, _, registry = actor_options.create_actor(emitter) contract_name = actor_options.contract_name if not contract_name: raise click.BadArgumentUsage( message="--contract-name is required when using --upgrade") existing_secret = click.prompt('Enter existing contract upgrade secret', hide_input=True) new_secret = click.prompt('Enter new contract upgrade secret', hide_input=True, confirmation_prompt=True) if multisig: if not target_address: raise click.BadArgumentUsage( message="--multisig requires using --target-address.") if not actor_options.force: click.confirm( f"Confirm building a re-target transaction for {contract_name}'s proxy to {target_address}?", abort=True) transaction = ADMINISTRATOR.retarget_proxy( contract_name=contract_name, target_address=target_address, existing_plaintext_secret=existing_secret, new_plaintext_secret=new_secret, just_build_transaction=True) trustee = Trustee(registry=registry, checksum_address=ADMINISTRATOR.deployer_address) data_for_multisig_executives = trustee.produce_data_to_sign( transaction) emitter.message( f"Transaction to retarget {contract_name} proxy to {target_address} was built:", color='green') paint_multisig_proposed_transaction(emitter, data_for_multisig_executives) # TODO: Move this logic to a better place nonce = data_for_multisig_executives['parameters']['nonce'] filepath = f'proposal-{nonce}.json' with open(filepath, 'w') as outfile: json.dump(data_for_multisig_executives, outfile) emitter.echo(f"Saved proposal to {filepath}", color='blue', bold=True) elif retarget: if not target_address: raise click.BadArgumentUsage( message="--target-address is required when using --retarget") if not actor_options.force: click.confirm( f"Confirm re-target {contract_name}'s proxy to {target_address}?", abort=True) receipt = ADMINISTRATOR.retarget_proxy( contract_name=contract_name, target_address=target_address, existing_plaintext_secret=existing_secret, new_plaintext_secret=new_secret) emitter.message( f"Successfully re-targeted {contract_name} proxy to {target_address}", color='green') paint_receipt_summary(emitter=emitter, receipt=receipt) else: if not actor_options.force: click.confirm( f"Confirm deploy new version of {contract_name} and retarget proxy?", abort=True) receipts = ADMINISTRATOR.upgrade_contract( contract_name=contract_name, existing_plaintext_secret=existing_secret, new_plaintext_secret=new_secret, ignore_deployed=ignore_deployed) emitter.message(f"Successfully deployed and upgraded {contract_name}", color='green') for name, receipt in receipts.items(): paint_receipt_summary(emitter=emitter, receipt=receipt)