def bootstrap_network(cls) -> Tuple['TesterBlockchain', Dict[str, EthereumContractAgent]]: testerchain = cls.connect() origin = testerchain.interface.w3.eth.accounts[0] deployer = Deployer(blockchain=testerchain, deployer_address=origin, bare=True) random_deployment_secret = partial(os.urandom, DISPATCHER_SECRET_LENGTH) _txhashes, agents = deployer.deploy_network_contracts(miner_secret=random_deployment_secret(), policy_secret=random_deployment_secret()) return testerchain, agents
def test_rapid_deployment(): compiler = SolidityCompiler() registry = InMemoryEthereumContractRegistry() allocation_registry = InMemoryAllocationRegistry() interface = BlockchainDeployerInterface(compiler=compiler, registry=registry, provider_uri='tester://pyevm') blockchain = TesterBlockchain(interface=interface, airdrop=False, test_accounts=4) blockchain.ether_airdrop(amount=1000000000) origin, *everyone = blockchain.interface.w3.eth.accounts deployer = Deployer(blockchain=blockchain, deployer_address=origin) deployer_address, *all_yall = deployer.blockchain.interface.w3.eth.accounts # The Big Three (+ Dispatchers) deployer.deploy_network_contracts(miner_secret=os.urandom(32), policy_secret=os.urandom(32)) # User Escrow Proxy deployer.deploy_escrow_proxy(secret=os.urandom(32)) # Deploy User Escrow total_allocations = 100 # Start with some hard-coded cases... allocation_data = [{ 'address': all_yall[1], 'amount': MAX_ALLOWED_LOCKED, 'duration': ONE_YEAR_IN_SECONDS }, { 'address': all_yall[2], 'amount': MIN_ALLOWED_LOCKED, 'duration': ONE_YEAR_IN_SECONDS * 2 }, { 'address': all_yall[3], 'amount': MIN_ALLOWED_LOCKED * 100, 'duration': ONE_YEAR_IN_SECONDS * 3 }] # Pile on the rest for _ in range(total_allocations - len(allocation_data)): random_password = ''.join( random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(16)) acct = w3.eth.account.create(random_password) beneficiary_address = acct.address amount = random.randint(MIN_ALLOWED_LOCKED, MAX_ALLOWED_LOCKED) duration = random.randint(MIN_LOCKED_PERIODS, MAX_MINTING_PERIODS * 3) random_allocation = { 'address': beneficiary_address, 'amount': amount, 'duration': duration } allocation_data.append(random_allocation) deployer.deploy_beneficiary_contracts( allocations=allocation_data, allocation_registry=allocation_registry)
def bootstrap_network(cls) -> Tuple['TesterBlockchain', Dict[str, EthereumContractAgent]]: """For use with metric testing scripts""" testerchain = cls(compiler=SolidityCompiler()) power = BlockchainPower(blockchain=testerchain, account=testerchain.client.etherbase) power.unlock_account(password=INSECURE_DEVELOPMENT_PASSWORD) testerchain.transacting_power = power origin = testerchain.client.etherbase deployer = Deployer(blockchain=testerchain, deployer_address=origin, bare=True) _txhashes, agents = deployer.deploy_network_contracts(staker_secret=STAKING_ESCROW_DEPLOYMENT_SECRET, policy_secret=POLICY_MANAGER_DEPLOYMENT_SECRET, adjudicator_secret=ADJUDICATOR_DEPLOYMENT_SECRET, user_escrow_proxy_secret=USER_ESCROW_PROXY_DEPLOYMENT_SECRET) return testerchain, agents
def bootstrap_network( cls ) -> Tuple['TesterBlockchain', Dict[str, EthereumContractAgent]]: testerchain = cls.connect() origin = testerchain.interface.w3.eth.accounts[0] deployer = Deployer(blockchain=testerchain, deployer_address=origin, bare=True) _txhashes, agents = deployer.deploy_network_contracts( miner_secret=MINERS_ESCROW_DEPLOYMENT_SECRET, policy_secret=POLICY_MANAGER_DEPLOYMENT_SECRET, adjudicator_secret=MINING_ADJUDICATOR_DEPLOYMENT_SECRET, user_escrow_proxy_secret=USER_ESCROW_PROXY_DEPLOYMENT_SECRET) return testerchain, agents
def test_rapid_deployment(token_economics): compiler = SolidityCompiler() registry = InMemoryEthereumContractRegistry() allocation_registry = InMemoryAllocationRegistry() interface = BlockchainDeployerInterface(compiler=compiler, registry=registry, provider_uri='tester://pyevm') blockchain = TesterBlockchain(interface=interface, airdrop=False, test_accounts=4) deployer_address = blockchain.etherbase_account deployer = Deployer(blockchain=blockchain, deployer_address=deployer_address) # The Big Three (+ Dispatchers) deployer.deploy_network_contracts(miner_secret=MINERS_ESCROW_DEPLOYMENT_SECRET, policy_secret=POLICY_MANAGER_DEPLOYMENT_SECRET, adjudicator_secret=MINING_ADJUDICATOR_DEPLOYMENT_SECRET) # Deploy User Escrow, too (+ Linker) deployer.deploy_escrow_proxy(secret=USER_ESCROW_PROXY_DEPLOYMENT_SECRET) total_allocations = NUMBER_OF_ALLOCATIONS_IN_TESTS all_yall = blockchain.unassigned_accounts # Start with some hard-coded cases... allocation_data = [{'address': all_yall[1], 'amount': token_economics.maximum_allowed_locked, 'duration': ONE_YEAR_IN_SECONDS}, {'address': all_yall[2], 'amount': token_economics.minimum_allowed_locked, 'duration': ONE_YEAR_IN_SECONDS*2}, {'address': all_yall[3], 'amount': token_economics.minimum_allowed_locked*100, 'duration': ONE_YEAR_IN_SECONDS*3} ] # Pile on the rest for _ in range(total_allocations - len(allocation_data)): random_password = ''.join(random.SystemRandom().choice(string.ascii_uppercase+string.digits) for _ in range(16)) acct = w3.eth.account.create(random_password) beneficiary_address = acct.address amount = random.randint(token_economics.minimum_allowed_locked, token_economics.maximum_allowed_locked) duration = random.randint(token_economics.minimum_locked_periods*ONE_YEAR_IN_SECONDS, (token_economics.maximum_locked_periods*ONE_YEAR_IN_SECONDS)*3) random_allocation = {'address': beneficiary_address, 'amount': amount, 'duration': duration} allocation_data.append(random_allocation) deployer.deploy_beneficiary_contracts(allocations=allocation_data, allocation_registry=allocation_registry)
def test_rapid_deployment(token_economics): compiler = SolidityCompiler() allocation_registry = InMemoryAllocationRegistry() blockchain = _TesterBlockchain(eth_airdrop=False, test_accounts=4, compiler=compiler) # TODO: #1092 - TransactingPower blockchain.transacting_power = BlockchainPower( blockchain=blockchain, account=blockchain.etherbase_account) deployer_address = blockchain.etherbase_account deployer = Deployer(blockchain=blockchain, deployer_address=deployer_address) deployer.deploy_network_contracts( staker_secret=STAKING_ESCROW_DEPLOYMENT_SECRET, policy_secret=POLICY_MANAGER_DEPLOYMENT_SECRET, adjudicator_secret=ADJUDICATOR_DEPLOYMENT_SECRET, user_escrow_proxy_secret=USER_ESCROW_PROXY_DEPLOYMENT_SECRET) all_yall = blockchain.unassigned_accounts # Start with some hard-coded cases... allocation_data = [{ 'address': all_yall[1], 'amount': token_economics.maximum_allowed_locked, 'duration': ONE_YEAR_IN_SECONDS }, { 'address': all_yall[2], 'amount': token_economics.minimum_allowed_locked, 'duration': ONE_YEAR_IN_SECONDS * 2 }, { 'address': all_yall[3], 'amount': token_economics.minimum_allowed_locked * 100, 'duration': ONE_YEAR_IN_SECONDS * 3 }] # Pile on the rest for _ in range(NUMBER_OF_ALLOCATIONS_IN_TESTS - len(allocation_data)): random_password = ''.join( random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(16)) acct = w3.eth.account.create(random_password) beneficiary_address = acct.address amount = random.randint(token_economics.minimum_allowed_locked, token_economics.maximum_allowed_locked) duration = random.randint( token_economics.minimum_locked_periods * ONE_YEAR_IN_SECONDS, (token_economics.maximum_locked_periods * ONE_YEAR_IN_SECONDS) * 3) random_allocation = { 'address': beneficiary_address, 'amount': amount, 'duration': duration } allocation_data.append(random_allocation) deployer.deploy_beneficiary_contracts( allocations=allocation_data, allocation_registry=allocation_registry)
def deployed_blockchain(): # # Interface # compiler = SolidityCompiler() registry = InMemoryEthereumContractRegistry() allocation_registry = InMemoryAllocationRegistry() interface = BlockchainDeployerInterface(compiler=compiler, registry=registry, provider_uri=TEST_PROVIDER_URI) # # Blockchain # blockchain = TesterBlockchain(interface=interface, airdrop=False, test_accounts=5, poa=True) blockchain.ether_airdrop(amount=TESTING_ETH_AIRDROP_AMOUNT) origin, *everyone = blockchain.interface.w3.eth.accounts # # Delpoyer # deployer = Deployer(blockchain=blockchain, deployer_address=origin) deployer_address, *all_yall = deployer.blockchain.interface.w3.eth.accounts # The Big Three (+ Dispatchers) deployer.deploy_network_contracts(miner_secret=os.urandom(32), policy_secret=os.urandom(32)) # User Escrow Proxy deployer.deploy_escrow_proxy(secret=os.urandom(32)) # Start with some hard-coded cases... allocation_data = [{ 'address': all_yall[1], 'amount': MAX_ALLOWED_LOCKED, 'duration': ONE_YEAR_IN_SECONDS }] deployer.deploy_beneficiary_contracts( allocations=allocation_data, allocation_registry=allocation_registry) yield blockchain, deployer_address, registry
def deployed_blockchain(token_economics): # Interface compiler = SolidityCompiler() registry = InMemoryEthereumContractRegistry() allocation_registry = InMemoryAllocationRegistry() interface = BlockchainDeployerInterface(compiler=compiler, registry=registry, provider_uri=TEST_PROVIDER_URI) # Blockchain blockchain = TesterBlockchain(interface=interface, airdrop=True, test_accounts=5, poa=True) deployer_address = blockchain.etherbase_account # Deployer deployer = Deployer(blockchain=blockchain, deployer_address=deployer_address) # The Big Three (+ Dispatchers) deployer.deploy_network_contracts(miner_secret=os.urandom(32), policy_secret=os.urandom(32), adjudicator_secret=os.urandom(32)) # User Escrow Proxy deployer.deploy_escrow_proxy(secret=os.urandom(32)) # Start with some hard-coded cases... all_yall = blockchain.unassigned_accounts allocation_data = [{ 'address': all_yall[1], 'amount': token_economics.maximum_allowed_locked, 'duration': ONE_YEAR_IN_SECONDS }] deployer.deploy_beneficiary_contracts( allocations=allocation_data, allocation_registry=allocation_registry) yield blockchain, deployer_address, registry
def __connect(deployer_address=None): # Ensure config root exists if not os.path.exists(DEFAULT_CONFIG_ROOT): os.makedirs(DEFAULT_CONFIG_ROOT) # Connect to Blockchain blockchain = Blockchain.connect(provider_uri=provider_uri, deployer=True, compile=not no_compile, poa=poa) if not deployer_address: etherbase = blockchain.interface.w3.eth.accounts[0] deployer_address = etherbase click.confirm( "Deployer Address is {} - Continue?".format(deployer_address), abort=True) deployer = Deployer(blockchain=blockchain, deployer_address=deployer_address) return deployer
def deploy(click_config, action, poa, provider_uri, geth, enode, deployer_address, contract_name, allocation_infile, allocation_outfile, registry_infile, registry_outfile, no_compile, amount, recipient_address, config_root, sync, force): """Manage contract and registry deployment""" ETH_NODE = None # # Validate # # Ensure config root exists, because we need a default place to put output files. config_root = config_root or DEFAULT_CONFIG_ROOT if not os.path.exists(config_root): os.makedirs(config_root) # # Connect to Blockchain # # Establish a contract registry from disk if specified registry, registry_filepath = None, (registry_outfile or registry_infile) if registry_filepath is not None: registry = EthereumContractRegistry( registry_filepath=registry_filepath) if geth: # Spawn geth child process ETH_NODE = NuCypherGethDevnetProcess(config_root=config_root) ETH_NODE.ensure_account_exists(password=click_config.get_password( confirm=True)) if not ETH_NODE.initialized: ETH_NODE.initialize_blockchain() ETH_NODE.start() # TODO: Graceful shutdown provider_uri = ETH_NODE.provider_uri # Deployment-tuned blockchain connection blockchain = Blockchain.connect(provider_uri=provider_uri, poa=poa, registry=registry, compile=not no_compile, deployer=True, fetch_registry=False, sync=sync) # # Deployment Actor # if not deployer_address: for index, address in enumerate(blockchain.interface.w3.eth.accounts): click.secho(f"{index} --- {address}") choices = click.IntRange(0, len(blockchain.interface.w3.eth.accounts)) deployer_address_index = click.prompt("Select deployer address", default=0, type=choices) deployer_address = blockchain.interface.w3.eth.accounts[ deployer_address_index] # Verify Address if not force: click.confirm("Selected {} - Continue?".format(deployer_address), abort=True) deployer = Deployer(blockchain=blockchain, deployer_address=deployer_address) # Verify ETH Balance click.secho(f"\n\nDeployer ETH balance: {deployer.eth_balance}") if deployer.eth_balance == 0: click.secho("Deployer address has no ETH.", fg='red', bold=True) raise click.Abort() if not blockchain.interface.is_local: # (~ dev mode; Assume accounts are already unlocked) password = click.prompt("Enter ETH node password", hide_input=True) blockchain.interface.w3.geth.personal.unlockAccount( deployer_address, password) # Add ETH Bootnode or Peer if enode: if geth: blockchain.interface.w3.geth.admin.addPeer(enode) click.secho(f"Added ethereum peer {enode}") else: raise NotImplemented # TODO: other backends # # Action switch # if action == 'upgrade': 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) deployer.upgrade_contract(contract_name=contract_name, existing_plaintext_secret=existing_secret, new_plaintext_secret=new_secret) elif action == 'rollback': 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) deployer.rollback_contract(contract_name=contract_name, existing_plaintext_secret=existing_secret, new_plaintext_secret=new_secret) elif action == "contracts": registry_filepath = deployer.blockchain.interface.registry.filepath if os.path.isfile(registry_filepath): click.secho( f"\nThere is an existing contract registry at {registry_filepath}.\n" f"Did you mean 'nucypher-deploy upgrade'?\n", fg='yellow') click.confirm( "Optionally, destroy existing local registry and continue?", abort=True) click.confirm( f"Confirm deletion of contract registry '{registry_filepath}'?", abort=True) os.remove(registry_filepath) # # Deploy Single Contract # if contract_name: # TODO: Handle secret collection for single contract deployment try: deployer_func = deployer.deployers[contract_name] except KeyError: message = f"No such contract {contract_name}. Available contracts are {deployer.deployers.keys()}" click.secho(message, fg='red', bold=True) raise click.Abort() else: # Deploy single contract _txs, _agent = deployer_func() # TODO: Painting for single contract deployment if ETH_NODE: ETH_NODE.stop() return # # Stage Deployment # # Track tx hashes, and new agents __deployment_transactions = dict() __deployment_agents = dict() secrets = click_config.collect_deployment_secrets() click.clear() click.secho(NU_BANNER) w3 = deployer.blockchain.interface.w3 click.secho(f"Current Time ........ {maya.now().iso8601()}") click.secho( f"Web3 Provider ....... {deployer.blockchain.interface.provider_uri}" ) click.secho(f"Block ............... {w3.eth.blockNumber}") click.secho(f"Gas Price ........... {w3.eth.gasPrice}") click.secho(f"Deployer Address .... {deployer.checksum_address}") click.secho(f"ETH ................. {deployer.eth_balance}") click.secho( f"CHAIN ID............. {deployer.blockchain.interface.chain_id}") click.secho( f"CHAIN................ {deployer.blockchain.interface.chain_name}" ) # Ask - Last chance to gracefully abort if not force: click.secho( "\nDeployment successfully staged. Take a deep breath. \n", fg='green') if click.prompt("Type 'DEPLOY' to continue") != 'DEPLOY': raise click.Abort() # Delay - Last chance to crash and abort click.secho(f"Starting deployment in 3 seconds...", fg='red') time.sleep(1) click.secho(f"2...", fg='yellow') time.sleep(1) click.secho(f"1...", fg='green') time.sleep(1) click.secho(f"Deploying...", bold=True) # # DEPLOY < ------- # txhashes, deployers = deployer.deploy_network_contracts( miner_secret=secrets.miner_secret, policy_secret=secrets.policy_secret, adjudicator_secret=secrets.mining_adjudicator_secret, user_escrow_proxy_secret=secrets.escrow_proxy_secret) # Success __deployment_transactions.update(txhashes) # # Paint # total_gas_used = 0 # TODO: may be faulty for contract_name, transactions in __deployment_transactions.items(): # Paint heading heading = '\n{} ({})'.format( contract_name, deployers[contract_name].contract_address) click.secho(heading, bold=True) click.echo('*' * (42 + 3 + len(contract_name))) for tx_name, txhash in transactions.items(): # Wait for inclusion in the blockchain try: receipt = deployer.blockchain.wait_for_receipt( txhash=txhash) except TimeExhausted: raise # TODO: Option to wait longer or retry # Examine Receipt # TODO: This currently cannot receive failed transactions if receipt['status'] == 1: click.secho("OK", fg='green', nl=False, bold=True) else: click.secho("Failed", fg='red', nl=False, bold=True) # Accumulate gas total_gas_used += int(receipt['gasUsed']) # Paint click.secho(" | {}".format(tx_name), fg='yellow', nl=False) click.secho(" | {}".format(txhash.hex()), fg='yellow', nl=False) click.secho(" ({} gas)".format(receipt['cumulativeGasUsed'])) click.secho("Block #{} | {}\n".format( receipt['blockNumber'], receipt['blockHash'].hex())) # Paint outfile paths click.secho( "Cumulative Gas Consumption: {} gas".format(total_gas_used), bold=True, fg='blue') registry_outfile = deployer.blockchain.interface.registry.filepath click.secho('Generated registry {}'.format(registry_outfile), bold=True, fg='blue') # Save transaction metadata receipts_filepath = deployer.save_deployment_receipts( transactions=__deployment_transactions) click.secho(f"Saved deployment receipts to {receipts_filepath}", fg='blue', bold=True) # # Publish Contract Registry # if not deployer.blockchain.interface.is_local: if click.confirm("Publish new contract registry?"): try: response = registry.publish( ) # TODO: Handle non-200 response and dehydrate except EthereumContractRegistry.RegistryError as e: click.secho("Registry publication failed.", fg='red', bold=True) click.secho(str(e)) raise click.Abort() click.secho(f"Published new contract registry.", fg='green') elif action == "allocations": if not allocation_infile: allocation_infile = click.prompt("Enter allocation data filepath") click.confirm("Continue deploying and allocating?", abort=True) deployer.deploy_beneficiaries_from_file( allocation_data_filepath=allocation_infile, allocation_outfile=allocation_outfile) elif action == "transfer": token_agent = NucypherTokenAgent(blockchain=blockchain) click.confirm( f"Transfer {amount} from {token_agent.contract_address} to {recipient_address}?", abort=True) txhash = token_agent.transfer( amount=amount, sender_address=token_agent.contract_address, target_address=recipient_address) click.secho(f"OK | {txhash}") elif action == "publish-registry": registry = deployer.blockchain.interface.registry click.confirm( f"Publish {registry.filepath} to GitHub (Authentication Required)?", abort=True) try: response = registry.publish( ) # TODO: Handle non-200 response and dehydrate except EthereumContractRegistry.RegistryError as e: click.secho(str(e)) raise click.Abort() click.secho(f"Published new contract registry.", fg='green') elif action == "destroy-registry": registry_filepath = deployer.blockchain.interface.registry.filepath click.confirm( f"Are you absolutely sure you want to destroy the contract registry at {registry_filepath}?", abort=True) os.remove(registry_filepath) click.secho(f"Successfully destroyed {registry_filepath}", fg='red') else: raise click.BadArgumentUsage(message=f"Unknown action '{action}'") if ETH_NODE: ETH_NODE.stop()
def deploy(click_config, action, poa, provider_uri, deployer_address, contract_name, allocation_infile, allocation_outfile, registry_infile, registry_outfile, no_compile, amount, recipient_address, config_root, force): """Manage contract and registry deployment""" # Ensure config root exists, because we need a default place to put outfiles. config_root = config_root or DEFAULT_CONFIG_ROOT if not os.path.exists(config_root): os.makedirs(config_root) # Establish a contract Registry registry, registry_filepath = None, (registry_outfile or registry_infile) if registry_filepath is not None: registry = EthereumContractRegistry( registry_filepath=registry_filepath) # Connect to Blockchain blockchain = Blockchain.connect(provider_uri=provider_uri, registry=registry, deployer=True, compile=not no_compile, poa=poa) # OK - Let's init a Deployment actor if not deployer_address: etherbase = blockchain.interface.w3.eth.accounts[0] deployer_address = etherbase # TODO: Make this required instead, perhaps interactive click.confirm( "Deployer Address is {} - Continue?".format(deployer_address), abort=True) deployer = Deployer(blockchain=blockchain, deployer_address=deployer_address) # The Big Three if action == "contracts": secrets = click_config.collect_deployment_secrets() # Track tx hashes, and new agents __deployment_transactions = dict() __deployment_agents = dict() if force: deployer.blockchain.interface.registry._destroy() try: txhashes, agents = deployer.deploy_network_contracts( miner_secret=bytes(secrets.miner_secret, encoding='utf-8'), policy_secret=bytes(secrets.policy_secret, encoding='utf-8'), adjudicator_secret=bytes(secrets.mining_adjudicator_secret, encoding='utf-8')) except BlockchainInterface.InterfaceError: raise # TODO: Handle registry management here (contract may already exist) else: __deployment_transactions.update(txhashes) # User Escrow Proxy deployer.deploy_escrow_proxy( secret=bytes(secrets.escrow_proxy_secret, encoding='utf-8')) click.secho("Deployed!", fg='green', bold=True) # # Deploy Single Contract # if contract_name: try: deployer_func = deployer.deployers[contract_name] except KeyError: message = "No such contract {}. Available contracts are {}".format( contract_name, deployer.deployers.keys()) click.secho(message, fg='red', bold=True) raise click.Abort() else: _txs, _agent = deployer_func() registry_outfile = deployer.blockchain.interface.registry.filepath click.secho( '\nDeployment Transaction Hashes for {}'.format(registry_outfile), bold=True, fg='blue') for contract_name, transactions in __deployment_transactions.items(): heading = '\n{} ({})'.format( contract_name, agents[contract_name].contract_address) click.secho(heading, bold=True) click.echo('*' * (42 + 3 + len(contract_name))) total_gas_used = 0 for tx_name, txhash in transactions.items(): receipt = deployer.blockchain.wait_for_receipt(txhash=txhash) total_gas_used += int(receipt['gasUsed']) if receipt['status'] == 1: click.secho("OK", fg='green', nl=False, bold=True) else: click.secho("Failed", fg='red', nl=False, bold=True) click.secho(" | {}".format(tx_name), fg='yellow', nl=False) click.secho(" | {}".format(txhash.hex()), fg='yellow', nl=False) click.secho(" ({} gas)".format(receipt['cumulativeGasUsed'])) click.secho("Block #{} | {}\n".format( receipt['blockNumber'], receipt['blockHash'].hex())) click.secho( "Cumulative Gas Consumption: {} gas\n".format(total_gas_used), bold=True, fg='blue') elif action == "allocations": if not allocation_infile: allocation_infile = click.prompt("Enter allocation data filepath") click.confirm("Continue deploying and allocating?", abort=True) deployer.deploy_beneficiaries_from_file( allocation_data_filepath=allocation_infile, allocation_outfile=allocation_outfile) elif action == "transfer": token_agent = NucypherTokenAgent(blockchain=blockchain) click.confirm( f"Transfer {amount} from {token_agent.contract_address} to {recipient_address}?", abort=True) txhash = token_agent.transfer( amount=amount, sender_address=token_agent.contract_address, target_address=recipient_address) click.secho(f"OK | {txhash}") return elif action == "destroy-registry": registry_filepath = deployer.blockchain.interface.registry.filepath click.confirm( f"Are you absolutely sure you want to destroy the contract registry at {registry_filepath}?", abort=True) os.remove(registry_filepath) click.secho(f"Successfully destroyed {registry_filepath}", fg='red') else: raise click.BadArgumentUsage(message=f"Unknown action '{action}'")