def transfer_tokens(general_config, actor_options, target_address, value): """Transfer tokens from contract's owner address to another address""" emitter = general_config.emitter ADMINISTRATOR, deployer_address, _, local_registry = actor_options.create_actor( emitter) token_agent = ContractAgency.get_agent( NucypherTokenAgent, registry=local_registry) # type: NucypherTokenAgent tokens = NU.from_nunits(token_agent.get_balance(deployer_address)) emitter.echo( DISPLAY_SENDER_TOKEN_BALANCE_BEFORE_TRANSFER.format( token_balance=tokens)) if not target_address: target_address = click.prompt(PROMPT_RECIPIENT_CHECKSUM_ADDRESS, type=EIP55_CHECKSUM_ADDRESS) if not value: stake_value_range = click.FloatRange(min=0, clamp=False) value = NU.from_tokens( click.prompt(PROMPT_TOKEN_VALUE, type=stake_value_range)) confirmation = CONFIRM_TOKEN_TRANSFER.format( value=value, deployer_address=deployer_address, target_address=target_address) click.confirm(confirmation, abort=True) receipt = token_agent.transfer(amount=int(value), sender_address=deployer_address, target_address=target_address) paint_receipt_summary(emitter=emitter, receipt=receipt)
def commit_to_next_period(general_config, character_options, config_file): """Manually make a commitment to the next period.""" # Setup emitter = setup_emitter(general_config, character_options.config_options.worker_address) _pre_launch_warnings(emitter, dev=character_options.config_options.dev, force=None) _, URSULA = character_options.create_character(emitter, config_file, general_config.json_ipc, load_seednodes=False) committed_period = URSULA.staking_agent.get_current_period() + 1 click.echo( CONFIRMING_ACTIVITY_NOW.format(committed_period=committed_period), color='blue') receipt = URSULA.commit_to_next_period() economics = EconomicsFactory.get_economics(registry=URSULA.registry) date = datetime_at_period(period=committed_period, seconds_per_period=economics.seconds_per_period) # TODO: Double-check dates here message = SUCCESSFUL_CONFIRM_ACTIVITY.format( committed_period=committed_period, date=date) emitter.echo(message, bold=True, color='blue') paint_receipt_summary( emitter=emitter, receipt=receipt, chain_name=URSULA.staking_agent.blockchain.client.chain_name)
def transfer_ownership(general_config, actor_options, target_address, gas): """Transfer ownership of contracts to another address.""" emitter = general_config.emitter ADMINISTRATOR, _, _, _ = actor_options.create_actor(emitter) if not target_address: target_address = click.prompt(PROMPT_NEW_OWNER_ADDRESS, type=EIP55_CHECKSUM_ADDRESS) contract_name = actor_options.contract_name if not contract_name: raise click.MissingParameter(param="--contract-name", message="You need to specify an ownable contract") try: contract_deployer_class = ADMINISTRATOR.deployers[contract_name] except KeyError: message = UNKNOWN_CONTRACT_NAME.format(contract_name=contract_name, contracts=ADMINISTRATOR.ownable_deployer_classes.keys()) emitter.echo(message, color='red', bold=True) raise click.Abort() if contract_deployer_class not in ADMINISTRATOR.ownable_deployer_classes: message = CONTRACT_IS_NOT_OWNABLE.format(contract_name=contract_name) emitter.echo(message, color='red', bold=True) raise click.Abort() contract_deployer = contract_deployer_class(registry=ADMINISTRATOR.registry, deployer_address=ADMINISTRATOR.deployer_address) receipt = contract_deployer.transfer_ownership(new_owner=target_address, transaction_gas_limit=gas) paint_receipt_summary(emitter=emitter, receipt=receipt)
def set_min_rate(general_config, transacting_staker_options, config_file, force, min_rate): """Staker sets the minimum acceptable fee rate for their associated worker.""" # Setup emitter = setup_emitter(general_config) STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file) blockchain = transacting_staker_options.get_blockchain() client_account, staking_address = select_client_account_for_staking( emitter=emitter, stakeholder=STAKEHOLDER, staking_address=transacting_staker_options.staker_options.staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=force) if not min_rate: paint_min_rate(emitter, STAKEHOLDER.registry, STAKEHOLDER.policy_agent, staking_address) # TODO check range min_rate = click.prompt(PROMPT_STAKER_MIN_POLICY_RATE, type=WEI) if not force: click.confirm(CONFIRM_NEW_MIN_POLICY_RATE.format(min_rate=min_rate), abort=True) password = transacting_staker_options.get_password(blockchain, client_account) STAKEHOLDER.assimilate(password=password) receipt = STAKEHOLDER.set_min_fee_rate(min_rate=min_rate) # Report Success message = SUCCESSFUL_SET_MIN_POLICY_RATE.format(min_rate=min_rate, staking_address=staking_address) emitter.echo(message, color='green') paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=blockchain.client.chain_name, transaction_type='set_min_rate')
def enable_claiming(general_config: GroupGeneralConfig, worklock_options: WorkLockOptions, force: bool, hw_wallet: bool, gas_limit: int): """Ensure correctness of WorkLock participants and enable allocation""" emitter, registry, blockchain = worklock_options.setup(general_config=general_config) bidder_address = worklock_options.get_bidder_address(emitter, registry) bidder = worklock_options.create_bidder(registry=registry, hw_wallet=hw_wallet) whales = bidder.get_whales() if whales: headers = ("Participants that require correction", "Current bonus") columns = (whales.keys(), map(prettify_eth_amount, whales.values())) emitter.echo(tabulate.tabulate(dict(zip(headers, columns)), headers=headers, floatfmt="fancy_grid")) if not force: click.confirm(f"Confirm force refund to at least {len(whales)} participants using {bidder_address}?", abort=True) force_refund_receipt = bidder.force_refund() emitter.echo(WHALE_WARNING.format(number=len(whales)), color='green') paint_receipt_summary(receipt=force_refund_receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name, transaction_type=f"force-refund") else: emitter.echo(BIDS_VALID_NO_FORCE_REFUND_INDICATED, color='yellow') if not bidder.worklock_agent.bidders_checked(): confirmation = gas_limit and force while not confirmation: if not gas_limit: min_gas = 180000 gas_limit = click.prompt(PROMPT_BID_VERIFY_GAS_LIMIT.format(min_gas=min_gas), type=click.IntRange(min=min_gas)) bidders_per_transaction = bidder.worklock_agent.estimate_verifying_correctness(gas_limit=gas_limit) if not force: message = CONFIRM_BID_VERIFICATION.format(bidder_address=bidder_address, gas_limit=gas_limit, bidders_per_transaction=bidders_per_transaction) confirmation = click.confirm(message) gas_limit = gas_limit if confirmation else None else: emitter.echo(VERIFICATION_ESTIMATES.format(gas_limit=gas_limit, bidders_per_transaction=bidders_per_transaction)) confirmation = True verification_receipts = bidder.verify_bidding_correctness(gas_limit=gas_limit) emitter.echo(COMPLETED_BID_VERIFICATION, color='green') for iteration, receipt in verification_receipts.items(): paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name, transaction_type=f"verify-correctness[{iteration}]") else: emitter.echo(BIDDERS_ALREADY_VERIFIED, color='yellow')
def cancel_escrow(general_config: GroupGeneralConfig, worklock_options: WorkLockOptions, force: bool, hw_wallet: bool): """Cancel your escrow and receive your ETH back""" emitter, registry, blockchain = worklock_options.setup( general_config=general_config) worklock_agent = ContractAgency.get_agent( WorkLockAgent, registry=registry) # type: WorkLockAgent now = maya.now().epoch if not worklock_agent.start_bidding_date <= now <= worklock_agent.end_cancellation_date: emitter.echo(CANCELLATION_WINDOW_CLOSED, color='red') raise click.Abort() bidder_address = worklock_options.get_bidder_address(emitter, registry) bidder = worklock_options.create_bidder(registry=registry, hw_wallet=hw_wallet) if not force: value = bidder.get_deposited_eth click.confirm( f"Confirm escrow cancellation of {prettify_eth_amount(value)} for {bidder_address}?", abort=True) receipt = bidder.cancel_bid() emitter.echo(SUCCESSFUL_BID_CANCELLATION, color='green') paint_receipt_summary( receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name) return # Exit
def rollback(general_config, actor_options): """Rollback a proxy contract's target.""" emitter = general_config.emitter ADMINISTRATOR, _, _, _ = actor_options.create_actor(emitter) if not actor_options.contract_name: raise click.BadArgumentUsage(message="--contract-name is required when using --rollback") receipt = ADMINISTRATOR.rollback_contract(contract_name=actor_options.contract_name) paint_receipt_summary(emitter=emitter, receipt=receipt)
def prolong(general_config, transacting_staker_options, config_file, force, lock_periods, index): """Prolong an existing stake's duration.""" # Setup emitter = setup_emitter(general_config) STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file) action_period = STAKEHOLDER.staking_agent.get_current_period() blockchain = transacting_staker_options.get_blockchain() economics = STAKEHOLDER.economics # Handle account selection client_account, staking_address = select_client_account_for_staking( emitter=emitter, stakeholder=STAKEHOLDER, staking_address=transacting_staker_options.staker_options.staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=force) # Handle stake update and selection if index is not None: # 0 is valid. current_stake = STAKEHOLDER.stakes[index] else: current_stake = select_stake(staker=STAKEHOLDER, emitter=emitter) # # Prolong # # Interactive if not lock_periods: max_extension = MAX_UINT16 - current_stake.final_locked_period # +1 because current period excluded min_extension = economics.minimum_locked_periods - current_stake.periods_remaining + 1 if min_extension < 1: min_extension = 1 duration_extension_range = click.IntRange(min=min_extension, max=max_extension, clamp=False) lock_periods = click.prompt(PROMPT_PROLONG_VALUE.format(minimum=min_extension, maximum=max_extension), type=duration_extension_range) if not force: click.confirm(CONFIRM_PROLONG.format(lock_periods=lock_periods), abort=True) # Authenticate password = transacting_staker_options.get_password(blockchain, client_account) STAKEHOLDER.assimilate(password=password) # Non-interactive: Consistency check to prevent the above agreement from going stale. last_second_current_period = STAKEHOLDER.staking_agent.get_current_period() if action_period != last_second_current_period: emitter.echo(PERIOD_ADVANCED_WARNING, color='red') raise click.Abort # Execute receipt = STAKEHOLDER.prolong_stake(stake=current_stake, additional_periods=lock_periods) # Report emitter.echo(SUCCESSFUL_STAKE_PROLONG, color='green', verbosity=1) paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=blockchain.client.chain_name) paint_stakes(emitter=emitter, staker=STAKEHOLDER)
def merge(general_config, transacting_staker_options, config_file, force, index_1, index_2): """Merge two stakes into one.""" # Setup emitter = setup_emitter(general_config) STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file) action_period = STAKEHOLDER.staking_agent.get_current_period() blockchain = transacting_staker_options.get_blockchain() client_account, staking_address = select_client_account_for_staking( emitter=emitter, stakeholder=STAKEHOLDER, staking_address=transacting_staker_options.staker_options.staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=force) # Handle stakes selection stake_1, stake_2 = None, None if index_1 is not None and index_2 is not None: stake_1 = STAKEHOLDER.stakes[index_1] stake_2 = STAKEHOLDER.stakes[index_2] elif index_1 is not None: # 0 is valid. stake_1 = STAKEHOLDER.stakes[index_1] elif index_2 is not None: stake_1 = STAKEHOLDER.stakes[index_2] if stake_1 is None: stake_1 = select_stake(staker=STAKEHOLDER, emitter=emitter) if stake_2 is None: emitter.echo(ONLY_DISPLAYING_MERGEABLE_STAKES_NOTE.format(final_period=stake_1.final_locked_period), color='yellow') stake_2 = select_stake(staker=STAKEHOLDER, emitter=emitter, filter_function=lambda s: s.index != stake_1.index and s.final_locked_period == stake_1.final_locked_period) if not force: click.confirm(CONFIRM_MERGE.format(stake_index_1=stake_1.index, stake_index_2=stake_2.index), abort=True) # Authenticate password = transacting_staker_options.get_password(blockchain, client_account) STAKEHOLDER.assimilate(password=password) # Non-interactive: Consistency check to prevent the above agreement from going stale. last_second_current_period = STAKEHOLDER.staking_agent.get_current_period() if action_period != last_second_current_period: emitter.echo(PERIOD_ADVANCED_WARNING, color='red') raise click.Abort # Execute receipt = STAKEHOLDER.merge_stakes(stake_1=stake_1, stake_2=stake_2) # Report emitter.echo(SUCCESSFUL_STAKES_MERGE, color='green', verbosity=1) paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=blockchain.client.chain_name) paint_stakes(emitter=emitter, staker=STAKEHOLDER)
def restake(general_config, transacting_staker_options, config_file, enable, lock_until, force): """Manage re-staking with --enable or --disable.""" # Setup emitter = setup_emitter(general_config) STAKEHOLDER = transacting_staker_options.create_character( emitter, config_file) blockchain = transacting_staker_options.get_blockchain() client_account, staking_address = select_client_account_for_staking( emitter=emitter, stakeholder=STAKEHOLDER, staking_address=transacting_staker_options.staker_options. staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=force) # Authenticate password = transacting_staker_options.get_password(blockchain, client_account) STAKEHOLDER.assimilate(checksum_address=client_account, password=password) # Inner Exclusive Switch if lock_until: if not force: confirm_enable_restaking_lock(emitter, staking_address=staking_address, release_period=lock_until) receipt = STAKEHOLDER.enable_restaking_lock(release_period=lock_until) emitter.echo(SUCCESSFUL_ENABLE_RESTAKE_LOCK.format( staking_address=staking_address, lock_until=lock_until), color='green', verbosity=1) elif enable: if not force: confirm_enable_restaking(emitter, staking_address=staking_address) receipt = STAKEHOLDER.enable_restaking() emitter.echo(SUCCESSFUL_ENABLE_RESTAKING.format( staking_address=staking_address), color='green', verbosity=1) else: if not force: click.confirm(CONFIRM_DISABLE_RESTAKING.format( staking_address=staking_address), abort=True) receipt = STAKEHOLDER.disable_restaking() emitter.echo(SUCCESSFUL_DISABLE_RESTAKING.format( staking_address=staking_address), color='green', verbosity=1) paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=blockchain.client.chain_name)
def escrow(general_config: GroupGeneralConfig, worklock_options: WorkLockOptions, force: bool, hw_wallet: bool, value: Decimal): """Create an ETH escrow, or increase an existing escrow""" emitter, registry, blockchain = worklock_options.setup(general_config=general_config) worklock_agent = ContractAgency.get_agent(WorkLockAgent, registry=registry) # type: WorkLockAgent now = maya.now().epoch if not worklock_agent.start_bidding_date <= now <= worklock_agent.end_bidding_date: emitter.echo(BIDDING_WINDOW_CLOSED, color='red') raise click.Abort() _bidder_address = worklock_options.get_bidder_address(emitter, registry) bidder = worklock_options.create_bidder(registry=registry, hw_wallet=hw_wallet) existing_bid_amount = bidder.get_deposited_eth if not value: if force: raise click.MissingParameter("Missing --value.") if not existing_bid_amount: # It's the first bid minimum_bid = bidder.worklock_agent.minimum_allowed_bid minimum_bid_in_eth = Web3.fromWei(minimum_bid, 'ether') prompt = BID_AMOUNT_PROMPT_WITH_MIN_BID.format(minimum_bid_in_eth=minimum_bid_in_eth) else: # There's an existing bid and the bidder is increasing the amount emitter.message(EXISTING_BID_AMOUNT_NOTICE.format(eth_amount=Web3.fromWei(existing_bid_amount, 'ether'))) minimum_bid_in_eth = Web3.fromWei(1, 'ether') prompt = BID_INCREASE_AMOUNT_PROMPT value = click.prompt(prompt, type=DecimalRange(min=minimum_bid_in_eth)) value = int(Web3.toWei(Decimal(value), 'ether')) if not force: if not existing_bid_amount: paint_bidding_notice(emitter=emitter, bidder=bidder) click.confirm(f"Place WorkLock escrow of {prettify_eth_amount(value)}?", abort=True) else: click.confirm(f"Increase current escrow ({prettify_eth_amount(existing_bid_amount)}) " f"by {prettify_eth_amount(value)}?", abort=True) receipt = bidder.place_bid(value=value) emitter.message("Publishing WorkLock Escrow...") maximum = NU.from_nunits(bidder.economics.maximum_allowed_locked) available_claim = NU.from_nunits(bidder.available_claim) message = f'\nCurrent escrow: {prettify_eth_amount(bidder.get_deposited_eth)} | Allocation: {available_claim}\n' if available_claim > maximum: message += f"\nThis allocation is currently above the allowed max ({maximum}), " \ f"so the escrow may be partially refunded.\n" message += f'Note that the available allocation value may fluctuate until the escrow period closes and ' \ f'allocations are finalized.\n' emitter.echo(message, color='yellow') paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name)
def paint_staking_confirmation(emitter, staker, receipt): emitter.echo("\nStake initialization transaction was successful.", color='green') emitter.echo(f'\nTransaction details:') paint_receipt_summary(emitter=emitter, receipt=receipt, transaction_type="deposit stake") emitter.echo( f'\n{STAKING_ESCROW_CONTRACT_NAME} address: {staker.staking_agent.contract_address}', color='blue') emitter.echo(POST_STAKING_ADVICE, color='green')
def winddown(general_config, transacting_staker_options, config_file, enable, force): """Manage winding down with --enable or --disable.""" # Setup emitter = setup_emitter(general_config) STAKEHOLDER = transacting_staker_options.create_character( emitter, config_file) blockchain = transacting_staker_options.get_blockchain() client_account, staking_address = select_client_account_for_staking( emitter=emitter, stakeholder=STAKEHOLDER, staking_address=transacting_staker_options.staker_options. staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=force) # Inner Exclusive Switch if enable: if not force: confirm_enable_winding_down(emitter, staking_address=staking_address) # Authenticate and Execute password = transacting_staker_options.get_password( blockchain, client_account) STAKEHOLDER.assimilate(password=password) receipt = STAKEHOLDER.enable_winding_down() emitter.echo(SUCCESSFUL_ENABLE_WIND_DOWN.format( staking_address=staking_address), color='green', verbosity=1) else: if not force: click.confirm(CONFIRM_DISABLE_WIND_DOWN.format( staking_address=staking_address), abort=True) # Authenticate and Execute password = transacting_staker_options.get_password( blockchain, client_account) STAKEHOLDER.assimilate(password=password) receipt = STAKEHOLDER.disable_winding_down() emitter.echo(SUCCESSFUL_DISABLE_WIND_DOWN.format( staking_address=staking_address), color='green', verbosity=1) paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=blockchain.client.chain_name)
def unbond(registry_filepath, eth_provider_uri, signer_uri, staking_provider, network, force): """Unbonds an operator from an authorized staking provider.""" # # Setup # emitter = StdoutEmitter() if not signer_uri: emitter.message('--signer is required', color='red') raise click.Abort() if not network: network = select_network(emitter=emitter, network_type=NetworksInventory.ETH) connect_to_blockchain(eth_provider_uri=eth_provider_uri, emitter=emitter) registry = get_registry(network=network, registry_filepath=registry_filepath) agent = ContractAgency.get_agent(PREApplicationAgent, registry=registry) signer = Signer.from_signer_uri(signer_uri) transacting_power = TransactingPower(account=staking_provider, signer=signer) # # Check # bonded, onchain_operator_address = is_bonded( agent=agent, staking_provider=staking_provider, return_address=True) if not bonded: emitter.message(NOT_BONDED.format(provider=staking_provider), color='red') raise click.Abort() check_bonding_requirements(emitter=emitter, agent=agent, staking_provider=staking_provider) # # Unbond # if not force: click.confirm(CONFIRM_UNBONDING.format( provider=staking_provider, operator=onchain_operator_address), abort=True) transacting_power.unlock(password=get_client_password( checksum_address=staking_provider, envvar=NUCYPHER_ENVVAR_STAKING_PROVIDER_ETH_PASSWORD)) emitter.echo(UNBONDING.format(operator=onchain_operator_address)) receipt = agent.bond_operator(operator=NULL_ADDRESS, transacting_power=transacting_power, staking_provider=staking_provider) paint_receipt_summary(receipt=receipt, emitter=emitter)
def bond_worker(general_config, transacting_staker_options, config_file, force, worker_address): """Bond a worker to a staker.""" emitter = setup_emitter(general_config) STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file) blockchain = transacting_staker_options.get_blockchain() economics = STAKEHOLDER.economics client_account, staking_address = select_client_account_for_staking( emitter=emitter, stakeholder=STAKEHOLDER, staking_address=transacting_staker_options.staker_options.staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=force) if not worker_address: worker_address = click.prompt(PROMPT_WORKER_ADDRESS, type=EIP55_CHECKSUM_ADDRESS) if (worker_address == staking_address) and not force: click.confirm(CONFIRM_WORKER_AND_STAKER_ADDRESSES_ARE_EQUAL.format(address=worker_address), abort=True) # TODO: Check preconditions (e.g., minWorkerPeriods, already in use, etc) # TODO: Double-check dates # Calculate release datetime current_period = STAKEHOLDER.staking_agent.get_current_period() bonded_date = datetime_at_period(period=current_period, seconds_per_period=economics.seconds_per_period) min_worker_periods = STAKEHOLDER.economics.minimum_worker_periods release_period = current_period + min_worker_periods release_date = datetime_at_period(period=release_period, seconds_per_period=economics.seconds_per_period, start_of_period=True) if not force: click.confirm(f"Commit to bonding " f"worker {worker_address} to staker {staking_address} " f"for a minimum of {STAKEHOLDER.economics.minimum_worker_periods} periods?", abort=True) password = transacting_staker_options.get_password(blockchain, client_account) STAKEHOLDER.assimilate(password=password) receipt = STAKEHOLDER.bond_worker(worker_address=worker_address) # Report Success message = SUCCESSFUL_WORKER_BONDING.format(worker_address=worker_address, staking_address=staking_address) emitter.echo(message, color='green') paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=blockchain.client.chain_name, transaction_type='bond_worker') emitter.echo(BONDING_DETAILS.format(current_period=current_period, bonded_date=bonded_date), color='green') emitter.echo(BONDING_RELEASE_INFO.format(release_period=release_period, release_date=release_date), color='green')
def refund(general_config: GroupGeneralConfig, worklock_options: WorkLockOptions, force: bool, hw_wallet: bool): """Reclaim ETH unlocked by your work""" emitter, registry, blockchain = worklock_options.setup(general_config=general_config) bidder_address = worklock_options.get_bidder_address(emitter, registry) bidder = worklock_options.create_bidder(registry=registry, hw_wallet=hw_wallet) if not force: click.confirm(CONFIRM_COLLECT_WORKLOCK_REFUND.format(bidder_address=bidder_address), abort=True) emitter.echo(SUBMITTING_WORKLOCK_REFUND_REQUEST) receipt = bidder.refund_deposit() paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name)
def collect_reward(general_config, transacting_staker_options, config_file, staking_reward, policy_fee, withdraw_address, force): """Withdraw staking reward.""" # Setup emitter = setup_emitter(general_config) STAKEHOLDER = transacting_staker_options.create_character( emitter, config_file) blockchain = transacting_staker_options.get_blockchain() if not staking_reward and not policy_fee: raise click.BadArgumentUsage( f"Either --staking-reward or --policy-fee must be True to collect rewards." ) client_account, staking_address = select_client_account_for_staking( emitter=emitter, stakeholder=STAKEHOLDER, staking_address=transacting_staker_options.staker_options. staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=force) password = transacting_staker_options.get_password(blockchain, client_account) STAKEHOLDER.assimilate(checksum_address=client_account, password=password) if staking_reward: # Note: Sending staking / inflation rewards to another account is not allowed. reward_amount = NU.from_nunits(STAKEHOLDER.calculate_staking_reward()) emitter.echo(message=COLLECTING_TOKEN_REWARD.format( reward_amount=reward_amount)) staking_receipt = STAKEHOLDER.collect_staking_reward() paint_receipt_summary( receipt=staking_receipt, chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name, emitter=emitter) if policy_fee: fee_amount = Web3.fromWei(STAKEHOLDER.calculate_policy_fee(), 'ether') emitter.echo(message=COLLECTING_ETH_REWARD.format( reward_amount=fee_amount)) policy_receipt = STAKEHOLDER.collect_policy_fee( collector_address=withdraw_address) paint_receipt_summary( receipt=policy_receipt, chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name, emitter=emitter)
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 = get_registry(network=blockchain_options.network) 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) # FIXME: Unexpected argument!! 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(PROMPT_FOR_RAW_SIGNATURE, type=click.STRING) authorization = Authorization.from_hex(auth_hex) executive_address = trustee.add_authorization(authorization, proposal) emitter.echo(SUCCESSFUL_MULTISIG_AUTHORIZATION.format( executive_address=executive_address), color='green') click.confirm(CONFIRM_EXECUTE_MULTISIG_TRANSACTION, abort=True) receipt = trustee.execute(proposal) paint_receipt_summary(emitter, receipt)
def preallocation(general_config, transacting_staker_options, config_file, action, force): """Claim token rewards collected by a preallocation contract.""" # Setup emitter = setup_emitter(general_config) STAKEHOLDER = transacting_staker_options.create_character( emitter, config_file) blockchain = transacting_staker_options.get_blockchain() # Unauthenticated actions: status if action == 'status': return paint_preallocation_status( emitter=emitter, token_agent=STAKEHOLDER.token_agent, preallocation_agent=STAKEHOLDER.preallocation_escrow_agent) # Authenticated actions: withdraw-tokens client_account, staking_address = select_client_account_for_staking( emitter=emitter, stakeholder=STAKEHOLDER, staking_address=transacting_staker_options.staker_options. staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=force) # Authenticate password = transacting_staker_options.get_password(blockchain, client_account) STAKEHOLDER.assimilate(checksum_address=client_account, password=password) if action == 'withdraw': token_balance = NU.from_nunits( STAKEHOLDER.token_agent.get_balance(staking_address)) locked_tokens = NU.from_nunits( STAKEHOLDER.preallocation_escrow_agent.unvested_tokens) unlocked_tokens = token_balance - locked_tokens emitter.echo(message=COLLECTING_PREALLOCATION_REWARD.format( unlocked_tokens=unlocked_tokens, staking_address=staking_address)) receipt = STAKEHOLDER.withdraw_preallocation_tokens(unlocked_tokens) paint_receipt_summary( receipt=receipt, chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name, emitter=emitter)
def unbond_worker(general_config, transacting_staker_options, config_file, force): """ Unbond worker currently bonded to a staker. """ emitter = setup_emitter(general_config) STAKEHOLDER = transacting_staker_options.create_character( emitter, config_file) blockchain = transacting_staker_options.get_blockchain() economics = STAKEHOLDER.economics client_account, staking_address = select_client_account_for_staking( emitter=emitter, stakeholder=STAKEHOLDER, staking_address=transacting_staker_options.staker_options. staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=force) # TODO: Check preconditions (e.g., minWorkerPeriods) worker_address = STAKEHOLDER.staking_agent.get_worker_from_staker( staking_address) password = transacting_staker_options.get_password(blockchain, client_account) STAKEHOLDER.assimilate(password=password) receipt = STAKEHOLDER.unbond_worker() # TODO: Double-check dates current_period = STAKEHOLDER.staking_agent.get_current_period() bonded_date = datetime_at_period( period=current_period, seconds_per_period=economics.seconds_per_period) message = SUCCESSFUL_DETACH_WORKER.format(worker_address=worker_address, staking_address=staking_address) emitter.echo(message, color='green') paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=blockchain.client.chain_name, transaction_type='unbond_worker') emitter.echo(DETACH_DETAILS.format(current_period=current_period, bonded_date=bonded_date), color='green')
def mint(general_config, transacting_staker_options, config_file, force): """Mint last portion of reward""" # Setup emitter = setup_emitter(general_config) STAKEHOLDER = transacting_staker_options.create_character( emitter, config_file) blockchain = transacting_staker_options.get_blockchain() client_account, staking_address = select_client_account_for_staking( emitter=emitter, stakeholder=STAKEHOLDER, staking_address=transacting_staker_options.staker_options. staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=force) # Nothing to mint mintable_periods = STAKEHOLDER.mintable_periods() if mintable_periods == 0: emitter.echo(NO_MINTABLE_PERIODS, color='red') raise click.Abort # Still locked token if STAKEHOLDER.non_withdrawable_stake() > 0: emitter.echo(STILL_LOCKED_TOKENS, color='yellow') if not force: click.confirm( CONFIRM_MINTING.format(mintable_periods=mintable_periods), abort=True) # Authenticate password = transacting_staker_options.get_password(blockchain, client_account) STAKEHOLDER.assimilate(password=password) receipt = STAKEHOLDER.mint() emitter.echo(SUCCESSFUL_MINTING, color='green', verbosity=1) paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=blockchain.client.chain_name, transaction_type='mint')
def claim(general_config: GroupGeneralConfig, worklock_options: WorkLockOptions, force: bool, hw_wallet: bool): """Claim tokens for your escrow, and start staking them""" emitter, registry, blockchain = worklock_options.setup(general_config=general_config) worklock_agent = ContractAgency.get_agent(WorkLockAgent, registry=registry) # type: WorkLockAgent if not worklock_agent.is_claiming_available(): emitter.echo(CLAIMING_NOT_AVAILABLE, color='red') raise click.Abort() bidder_address = worklock_options.get_bidder_address(emitter, registry) bidder = worklock_options.create_bidder(registry=registry, hw_wallet=hw_wallet) unspent_bid = bidder.available_compensation if unspent_bid: emitter.echo(WORKLOCK_ADDITIONAL_COMPENSATION_AVAILABLE.format(amount=prettify_eth_amount(unspent_bid))) if not force: message = CONFIRM_REQUEST_WORKLOCK_COMPENSATION.format(bidder_address=bidder_address) click.confirm(message, abort=True) emitter.echo(REQUESTING_WORKLOCK_COMPENSATION) receipt = bidder.withdraw_compensation() paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name) has_claimed = bidder.has_claimed if bool(has_claimed): emitter.echo(CLAIM_ALREADY_PLACED.format(bidder_address=bidder.checksum_address), color='red') raise click.Abort() tokens = NU.from_nunits(bidder.available_claim) emitter.echo(AVAILABLE_CLAIM_NOTICE.format(tokens=tokens), color='green', bold=True) if not force: lock_duration = bidder.worklock_agent.worklock_parameters()[-2] emitter.echo(WORKLOCK_CLAIM_ADVISORY.format(lock_duration=lock_duration), color='blue') click.confirm(CONFIRM_WORKLOCK_CLAIM.format(bidder_address=bidder_address), abort=True) emitter.echo(SUBMITTING_WORKLOCK_CLAIM) receipt = bidder.claim() paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name) paint_worklock_claim(emitter=emitter, bidder_address=bidder_address, network=worklock_options.network, provider_uri=worklock_options.provider_uri)
def paint_contract_deployment(emitter, contract_name: str, contract_address: str, receipts: dict, chain_name: str = None, open_in_browser: bool = False): # TODO: switch to using an explicit emitter is_token_contract = contract_name == NUCYPHER_TOKEN_CONTRACT_NAME # Paint heading heading = f'\r{" "*80}\n{contract_name} ({contract_address})' emitter.echo(heading, bold=True) emitter.echo('*' * (42 + 3 + len(contract_name))) try: url = etherscan_url(item=contract_address, network=chain_name, is_token=is_token_contract) except ValueError as e: emitter.log.info("Failed Etherscan URL construction: " + str(e)) else: emitter.echo(f" See {url}\n") # Paint Transactions for tx_name, receipt in receipts.items(): paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=chain_name, transaction_type=tx_name) if open_in_browser: try: url = etherscan_url(item=contract_address, network=chain_name, is_token=is_token_contract) except ValueError as e: emitter.log.info("Failed Etherscan URL construction: " + str(e)) else: webbrowser.open_new_tab(url)
def remove_inactive_substake(emitter, stakeholder: StakeHolder, action_period: int, stake: Stake, chain_name: str, force: bool) -> None: # Non-interactive: Consistency check to prevent the above agreement from going stale. last_second_current_period = stakeholder.staker.staking_agent.get_current_period( ) if action_period != last_second_current_period: emitter.echo(PERIOD_ADVANCED_WARNING, color='red') raise click.Abort if not force: click.confirm(CONFIRM_REMOVE_SUBSTAKE.format(stake_index=stake.index), abort=True) # Execute receipt = stakeholder.staker.remove_inactive_stake(stake=stake) # Report emitter.echo(SUCCESSFUL_STAKE_REMOVAL, color='green', verbosity=1) paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=chain_name) paint_stakes(emitter=emitter, staker=stakeholder.staker)
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(CONFIRM_BUILD_RETARGET_TRANSACTION.format( contract_name=contract_name, target_address=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_eth_balance=False, show_nu_balance=False, show_staking=False) if not actor_options.force: click.confirm( CONFIRM_SELECTED_ACCOUNT.format(address=trustee_address), abort=True) trustee = Trustee(registry=registry, checksum_address=trustee_address) transaction_proposal = trustee.create_transaction_proposal(transaction) message = SUCCESSFUL_RETARGET_TX_BUILT.format( contract_name=contract_name, target_address=target_address) emitter.message(message, 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( SUCCESSFUL_SAVE_MULTISIG_TX_PROPOSAL.format(filepath=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(CONFIRM_RETARGET.format( contract_name=contract_name, target_address=target_address), abort=True) receipt = ADMINISTRATOR.retarget_proxy(contract_name=contract_name, target_address=target_address) message = SUCCESSFUL_RETARGET.format(contract_name=contract_name, target_address=target_address) emitter.message(message, color='green') paint_receipt_summary(emitter=emitter, receipt=receipt) else: if not actor_options.force: click.confirm( CONFIRM_BEGIN_UPGRADE.format(contract_name=contract_name), abort=True) receipts = ADMINISTRATOR.upgrade_contract( contract_name=contract_name, ignore_deployed=ignore_deployed) emitter.message(SUCCESSFUL_UPGRADE.format(contract_name=contract_name), color='green') for name, receipt in receipts.items(): paint_receipt_summary(emitter=emitter, receipt=receipt)
def contracts(general_config, actor_options, mode, activate, gas, ignore_deployed, confirmations, parameters): """Compile and deploy contracts.""" emitter = general_config.emitter ADMINISTRATOR, _, deployer_interface, local_registry = actor_options.create_actor(emitter) chain_name = deployer_interface.client.chain_name deployment_parameters = {} if parameters: with open(parameters) as json_file: deployment_parameters = json.load(json_file) contract_name = actor_options.contract_name deployment_mode = constants.__getattr__(mode.upper()) # TODO: constant sorrow try: contract_deployer_class = ADMINISTRATOR.deployers[contract_name] except KeyError: message = UNKNOWN_CONTRACT_NAME.format(contract_name=contract_name, constants=ADMINISTRATOR.deployers.keys()) emitter.echo(message, color='red', bold=True) raise click.Abort() if activate: # For the moment, only StakingEscrow can be activated staking_escrow_deployer = contract_deployer_class(registry=ADMINISTRATOR.registry, deployer_address=ADMINISTRATOR.deployer_address) if contract_name != STAKING_ESCROW_CONTRACT_NAME or not staking_escrow_deployer.ready_to_activate: raise click.BadOptionUsage(option_name="--activate", message=f"You can only activate an idle instance of {STAKING_ESCROW_CONTRACT_NAME}") escrow_address = staking_escrow_deployer._get_deployed_contract().address prompt = CONFIRM_NETWORK_ACTIVATION.format(staking_escrow_name=STAKING_ESCROW_CONTRACT_NAME, staking_escrow_address=escrow_address) click.confirm(prompt, abort=True) receipts = staking_escrow_deployer.activate(gas_limit=gas, confirmations=confirmations) for tx_name, receipt in receipts.items(): paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=chain_name, transaction_type=tx_name) return # Exit # Stage Deployment paint_staged_deployment(deployer_interface=deployer_interface, administrator=ADMINISTRATOR, emitter=emitter) # Confirm Trigger Deployment if not confirm_deployment(emitter=emitter, deployer_interface=deployer_interface): raise click.Abort() # Deploy emitter.echo(CONTRACT_DEPLOYMENT_SERIES_BEGIN_ADVISORY.format(contract_name=contract_name)) receipts, agent = ADMINISTRATOR.deploy_contract(contract_name=contract_name, gas_limit=gas, deployment_mode=deployment_mode, ignore_deployed=ignore_deployed, confirmations=confirmations, deployment_parameters=deployment_parameters) # Report paint_contract_deployment(contract_name=contract_name, contract_address=agent.contract_address, receipts=receipts, emitter=emitter, chain_name=chain_name, open_in_browser=actor_options.etherscan) # Success registry_outfile = local_registry.filepath emitter.echo(SUCCESSFUL_REGISTRY_CREATION.format(registry_outfile=registry_outfile), bold=True, color='blue')
def upgrade(general_config, actor_options, retarget, target_address, ignore_deployed, multisig, confirmations): """Upgrade NuCypher existing proxy contract deployments.""" # # Setup # emitter = general_config.emitter ADMINISTRATOR, deployer_address, blockchain, local_registry = actor_options.create_actor(emitter, is_multisig=bool(multisig)) # FIXME: Workaround for building MultiSig TXs | NRN # # Pre-flight # contract_name = actor_options.contract_name if not contract_name: raise click.BadArgumentUsage(message="--contract-name is required when using --upgrade") try: # Check contract name exists Deployer = ADMINISTRATOR.deployers[contract_name] except KeyError: message = UNKNOWN_CONTRACT_NAME.format(contract_name=contract_name, constants=ADMINISTRATOR.deployers.keys()) emitter.echo(message, color='red', bold=True) raise click.Abort() deployer = Deployer(registry=local_registry) # Check deployer address is owner if Deployer._ownable and deployer_address != deployer.owner: # blockchain read emitter.echo(DEPLOYER_IS_NOT_OWNER.format(deployer_address=deployer_address, contract_name=contract_name, agent=deployer.make_agent())) raise click.Abort() else: emitter.echo('✓ Verified deployer address as contract owner', color='green') # # Business # if multisig: if not target_address: raise click.BadArgumentUsage(message="--multisig requires using --target-address.") if not actor_options.force: click.confirm(CONFIRM_BUILD_RETARGET_TRANSACTION.format(contract_name=contract_name, target_address=target_address), abort=True) transaction = ADMINISTRATOR.retarget_proxy(contract_name=contract_name, target_address=target_address, just_build_transaction=True, confirmations=confirmations) trustee_address = select_client_account(emitter=emitter, prompt="Select trustee address", provider_uri=actor_options.provider_uri, show_eth_balance=False, show_nu_balance=False, show_staking=False) if not actor_options.force: click.confirm(CONFIRM_SELECTED_ACCOUNT.format(address=trustee_address), abort=True) trustee = Trustee(registry=local_registry, checksum_address=trustee_address) transaction_proposal = trustee.create_transaction_proposal(transaction) message = SUCCESSFUL_RETARGET_TX_BUILT.format(contract_name=contract_name, target_address=target_address) emitter.message(message, 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(SUCCESSFUL_SAVE_MULTISIG_TX_PROPOSAL.format(filepath=filepath), color='blue', bold=True) return # Exit elif retarget: if not target_address: raise click.BadArgumentUsage(message="--target-address is required when using --retarget") if not actor_options.force: click.confirm(CONFIRM_RETARGET.format(contract_name=contract_name, target_address=target_address), abort=True) receipt = ADMINISTRATOR.retarget_proxy(contract_name=contract_name,target_address=target_address, confirmations=confirmations) message = SUCCESSFUL_RETARGET.format(contract_name=contract_name, target_address=target_address) emitter.message(message, color='green') paint_receipt_summary(emitter=emitter, receipt=receipt) return # Exit else: github_registry = establish_deployer_registry(emitter=emitter, download_registry=True, network=actor_options.network) if not actor_options.force: # Check for human verification of versioned upgrade details click.confirm(CONFIRM_BEGIN_UPGRADE.format(contract_name=contract_name), abort=True) if deployer._ownable: # Only ownable + upgradeable contracts apply verify_upgrade_details(blockchain=blockchain, registry=github_registry, deployer=deployer) # Success receipts = ADMINISTRATOR.upgrade_contract(contract_name=contract_name, ignore_deployed=ignore_deployed, confirmations=confirmations) emitter.message(SUCCESSFUL_UPGRADE.format(contract_name=contract_name), color='green') for name, receipt in receipts.items(): paint_receipt_summary(emitter=emitter, receipt=receipt) emitter.echo(REGISTRY_PUBLICATION_HINT.format(contract_name=contract_name, local_registry=local_registry, network=actor_options.network), color='blue') emitter.echo(ETHERSCAN_VERIFY_HINT.format(solc_version=SOLIDITY_COMPILER_VERSION), color='blue') return # Exit
def contracts(general_config, actor_options, mode, activate, gas, ignore_deployed, confirmations, parameters): """Compile and deploy contracts.""" emitter = general_config.emitter ADMINISTRATOR, _, deployer_interface, local_registry = actor_options.create_actor( emitter) chain_name = deployer_interface.client.chain_name deployment_parameters = {} if parameters: with open(parameters) as json_file: deployment_parameters = json.load(json_file) # # Deploy Single Contract (Amend Registry) # contract_name = actor_options.contract_name deployment_mode = constants.__getattr__( mode.upper()) # TODO: constant sorrow if contract_name: # TODO: Remove this conditional, make it the default try: contract_deployer_class = ADMINISTRATOR.deployers[contract_name] except KeyError: message = UNKNOWN_CONTRACT_NAME.format( contract_name=contract_name, constants=ADMINISTRATOR.deployers.keys()) emitter.echo(message, color='red', bold=True) raise click.Abort() if activate: # For the moment, only StakingEscrow can be activated staking_escrow_deployer = contract_deployer_class( registry=ADMINISTRATOR.registry, deployer_address=ADMINISTRATOR.deployer_address) if contract_name != STAKING_ESCROW_CONTRACT_NAME or not staking_escrow_deployer.ready_to_activate: raise click.BadOptionUsage( option_name="--activate", message= f"You can only activate an idle instance of {STAKING_ESCROW_CONTRACT_NAME}" ) escrow_address = staking_escrow_deployer._get_deployed_contract( ).address prompt = CONFIRM_NETWORK_ACTIVATION.format( staking_escrow_name=STAKING_ESCROW_CONTRACT_NAME, staking_escrow_address=escrow_address) click.confirm(prompt, abort=True) receipts = staking_escrow_deployer.activate( gas_limit=gas, confirmations=confirmations) for tx_name, receipt in receipts.items(): paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=chain_name, transaction_type=tx_name) return # Exit # Deploy emitter.echo( CONTRACT_DEPLOYMENT_SERIES_BEGIN_ADVISORY.format( contract_name=contract_name)) receipts, agent = ADMINISTRATOR.deploy_contract( contract_name=contract_name, gas_limit=gas, deployment_mode=deployment_mode, ignore_deployed=ignore_deployed, confirmations=confirmations, deployment_parameters=deployment_parameters) # Report paint_contract_deployment(contract_name=contract_name, contract_address=agent.contract_address, receipts=receipts, emitter=emitter, chain_name=chain_name, open_in_browser=actor_options.etherscan) return # Exit # # Deploy Automated Series (Create Registry) # if deployment_mode is not FULL: raise click.BadOptionUsage( option_name='--mode', message= "Only 'full' mode is supported when deploying all network contracts" ) # Confirm filesystem registry writes. if os.path.isfile(local_registry.filepath): emitter.echo(EXISTING_REGISTRY_FOR_DOMAIN.format( registry_filepath=local_registry.filepath), color='yellow') click.confirm(CONFIRM_LOCAL_REGISTRY_DESTRUCTION, abort=True) os.remove(local_registry.filepath) # Stage Deployment paint_staged_deployment(deployer_interface=deployer_interface, administrator=ADMINISTRATOR, emitter=emitter) # Confirm Trigger Deployment if not confirm_deployment(emitter=emitter, deployer_interface=deployer_interface): raise click.Abort() # Delay - Last chance to abort via KeyboardInterrupt paint_deployment_delay(emitter=emitter) # Execute Deployment deployment_receipts = ADMINISTRATOR.deploy_network_contracts( emitter=emitter, interactive=not actor_options.force, etherscan=actor_options.etherscan, ignore_deployed=ignore_deployed) # Paint outfile paths registry_outfile = local_registry.filepath emitter.echo( SUCCESSFUL_REGISTRY_CREATION.format(registry_outfile=registry_outfile), bold=True, color='blue') # Save transaction metadata receipts_filepath = ADMINISTRATOR.save_deployment_receipts( receipts=deployment_receipts) emitter.echo(SUCCESSFUL_SAVE_DEPLOY_RECEIPTS.format( receipts_filepath=receipts_filepath), color='blue', bold=True)
def divide(general_config, transacting_staker_options, config_file, force, value, lock_periods, index): """Create a new stake from part of an existing one.""" # Setup emitter = setup_emitter(general_config) STAKEHOLDER = transacting_staker_options.create_character( emitter, config_file) blockchain = transacting_staker_options.get_blockchain() economics = STAKEHOLDER.economics action_period = STAKEHOLDER.staking_agent.get_current_period() client_account, staking_address = select_client_account_for_staking( emitter=emitter, stakeholder=STAKEHOLDER, staking_address=transacting_staker_options.staker_options. staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=force) # Dynamic click types (Economics) min_locked = economics.minimum_allowed_locked stake_value_range = click.FloatRange( min=NU.from_nunits(min_locked).to_tokens(), clamp=False) if transacting_staker_options.staker_options.staking_address and index is not None: # 0 is valid. STAKEHOLDER.stakes = StakeList( registry=STAKEHOLDER.registry, checksum_address=transacting_staker_options.staker_options. staking_address) STAKEHOLDER.stakes.refresh() current_stake = STAKEHOLDER.stakes[index] else: current_stake = select_stake(stakeholder=STAKEHOLDER, emitter=emitter, divisible=True, staker_address=client_account) # # Stage Stake # # Value if not value: min_allowed_locked = NU.from_nunits( STAKEHOLDER.economics.minimum_allowed_locked) max_divide_value = max(min_allowed_locked, current_stake.value - min_allowed_locked) prompt = PROMPT_STAKE_DIVIDE_VALUE.format( minimm=min_allowed_locked, maximum=str(max_divide_value)) value = click.prompt(prompt, type=stake_value_range) value = NU(value, 'NU') # Duration if not lock_periods: max_extension = MAX_UINT16 - current_stake.final_locked_period divide_extension_range = click.IntRange(min=1, max=max_extension, clamp=False) extension = click.prompt(PROMPT_STAKE_EXTEND_VALUE, type=divide_extension_range) else: extension = lock_periods if not force: confirm_large_stake(lock_periods=extension, value=value) paint_staged_stake_division(emitter=emitter, stakeholder=STAKEHOLDER, original_stake=current_stake, target_value=value, extension=extension) click.confirm(CONFIRM_BROADCAST_STAKE_DIVIDE, abort=True) # Authenticate password = transacting_staker_options.get_password(blockchain, client_account) # Consistency check to prevent the above agreement from going stale. last_second_current_period = STAKEHOLDER.staking_agent.get_current_period() if action_period != last_second_current_period: emitter.echo(PERIOD_ADVANCED_WARNING, red='red') raise click.Abort # Execute STAKEHOLDER.assimilate(checksum_address=current_stake.staker_address, password=password) modified_stake, new_stake = STAKEHOLDER.divide_stake( stake_index=current_stake.index, target_value=value, additional_periods=extension) emitter.echo(SUCCESSFUL_STAKE_DIVIDE, color='green', verbosity=1) paint_receipt_summary(emitter=emitter, receipt=new_stake.receipt, chain_name=blockchain.client.chain_name) # Show the resulting stake list paint_stakes(emitter=emitter, stakeholder=STAKEHOLDER)
def collect_reward(general_config, transacting_staker_options, config_file, staking_reward, policy_fee, withdraw_address, force): """Withdraw staking reward.""" # Setup emitter = setup_emitter(general_config) STAKEHOLDER = transacting_staker_options.create_character( emitter, config_file) blockchain = transacting_staker_options.get_blockchain() if not staking_reward and not policy_fee: raise click.BadArgumentUsage( f"Either --staking-reward or --policy-fee must be True to collect rewards." ) client_account, staking_address = select_client_account_for_staking( emitter=emitter, stakeholder=STAKEHOLDER, staking_address=transacting_staker_options.staker_options. staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=force) password = None if staking_reward: # Note: Sending staking / inflation rewards to another account is not allowed. reward_amount = NU.from_nunits(STAKEHOLDER.calculate_staking_reward()) if reward_amount == 0: emitter.echo(NO_TOKENS_TO_WITHDRAW, color='red') raise click.Abort emitter.echo(message=COLLECTING_TOKEN_REWARD.format( reward_amount=reward_amount)) withdrawing_last_portion = STAKEHOLDER.non_withdrawable_stake() == 0 if not force and withdrawing_last_portion and STAKEHOLDER.mintable_periods( ) > 0: click.confirm(CONFIRM_COLLECTING_WITHOUT_MINTING, abort=True) # Authenticate and Execute password = transacting_staker_options.get_password( blockchain, client_account) STAKEHOLDER.assimilate(password=password) staking_receipt = STAKEHOLDER.collect_staking_reward() paint_receipt_summary( receipt=staking_receipt, chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name, emitter=emitter) if policy_fee: fee_amount = Web3.fromWei(STAKEHOLDER.calculate_policy_fee(), 'ether') if fee_amount == 0: emitter.echo(NO_FEE_TO_WITHDRAW, color='red') raise click.Abort emitter.echo(message=COLLECTING_ETH_FEE.format(fee_amount=fee_amount)) if password is None: # Authenticate and Execute password = transacting_staker_options.get_password( blockchain, client_account) STAKEHOLDER.assimilate(password=password) policy_receipt = STAKEHOLDER.collect_policy_fee( collector_address=withdraw_address) paint_receipt_summary( receipt=policy_receipt, chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name, emitter=emitter)