def winddown(general_config, transacting_staker_options, config_file, enable, lock_until, force): """ Manage winding down with --enable or --disable. """ emitter = _setup_emitter(general_config) STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file) blockchain = transacting_staker_options.get_blockchain() client_account, staking_address = handle_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 enable: if not force: confirm_enable_winding_down(emitter, staking_address=staking_address) receipt = STAKEHOLDER.enable_winding_down() emitter.echo(f'Successfully enabled winding down for {staking_address}', color='green', verbosity=1) else: if not force: click.confirm(f"Confirm disable winding down for staker {staking_address}?", abort=True) receipt = STAKEHOLDER.disable_winding_down() emitter.echo(f'Successfully disabled winding down for {staking_address}', color='green', verbosity=1) paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=blockchain.client.chain_name)
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 = handle_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 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) # # 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(f"Enter number of periods to extend ({min_extension}-{max_extension})", type=duration_extension_range) if not force: click.confirm(f"Publish stake extension of {lock_periods} period(s) to the blockchain?", abort=True) password = transacting_staker_options.get_password(blockchain, client_account) # 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("Current period advanced before transaction was broadcasted. Please try again.", red='red') raise click.Abort # Authenticate and Execute STAKEHOLDER.assimilate(checksum_address=current_stake.staker_address, password=password) receipt = STAKEHOLDER.prolong_stake(stake_index=current_stake.index, additional_periods=lock_periods) # Report emitter.echo('Successfully Prolonged Stake', color='green', verbosity=1) paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=blockchain.client.chain_name) painting.paint_stakes(emitter=emitter, stakeholder=STAKEHOLDER) return # Exit
def events(general_config, staker_options, config_file, event_name): """ See blockchain events associated to a staker """ ### Setup ### emitter = _setup_emitter(general_config) STAKEHOLDER = staker_options.create_character(emitter, config_file) blockchain = staker_options.get_blockchain() _client_account, staking_address = handle_client_account_for_staking( emitter=emitter, stakeholder=STAKEHOLDER, staking_address=staker_options.staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=True) title = f" {STAKEHOLDER.staking_agent.registry_contract_name} Events ".center(40, "-") emitter.echo(f"\n{title}\n", bold=True, color='green') if event_name: events = [STAKEHOLDER.staking_agent.contract.events[event_name]] else: raise click.BadOptionUsage(message="You must specify an event name with --event-name") # TODO: Doesn't work for the moment # event_names = STAKEHOLDER.staking_agent.events.names # events = [STAKEHOLDER.staking_agent.contract.events[e] for e in event_names] # events = [e for e in events if 'staker' in e.argument_names] for event in events: emitter.echo(f"{event.event_name}:", bold=True, color='yellow') event_filter = event.createFilter(fromBlock=0, toBlock='latest', argument_filters={'staker': staking_address}) entries = event_filter.get_all_entries() for event_record in entries: emitter.echo(f" - {EventRecord(event_record)}")
def detach_worker(click_config, # Worker Options poa, light, registry_filepath, config_file, provider_uri, staking_address, hw_wallet, beneficiary_address, allocation_filepath, worker_address, # Other options force): """ Detach worker currently bonded to a staker. """ ### Setup ### emitter = _setup_emitter(click_config) STAKEHOLDER, blockchain = _create_stakeholder(config_file, provider_uri, poa, light, registry_filepath, staking_address, beneficiary_address=beneficiary_address, allocation_filepath=allocation_filepath) ############# economics = STAKEHOLDER.economics client_account, staking_address = handle_client_account_for_staking(emitter=emitter, stakeholder=STAKEHOLDER, staking_address=staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=force) if worker_address: raise click.BadOptionUsage(message="detach-worker cannot be used together with --worker-address", option_name='--worker-address') # TODO: Check preconditions (e.g., minWorkerPeriods) worker_address = STAKEHOLDER.staking_agent.get_worker_from_staker(staking_address) password = None if not hw_wallet and not blockchain.client.is_local: password = get_client_password(checksum_address=client_account) STAKEHOLDER.assimilate(checksum_address=client_account, password=password) receipt = STAKEHOLDER.detach_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) emitter.echo(f"Successfully detached worker {worker_address} from staker {staking_address}", color='green') paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=blockchain.client.chain_name, transaction_type='detach_worker') emitter.echo(f"Detached at period #{current_period} ({bonded_date})", color='green')
def preallocation(click_config, # Stake Options poa, light, registry_filepath, config_file, provider_uri, staking_address, hw_wallet, beneficiary_address, allocation_filepath, # Preallocation subcommands, action, # Other force): """ Claim token rewards collected by a preallocation contract. """ ### Setup ### emitter = _setup_emitter(click_config) STAKEHOLDER, blockchain = _create_stakeholder(config_file, provider_uri, poa, light, registry_filepath, staking_address, beneficiary_address=beneficiary_address, allocation_filepath=allocation_filepath) ############# # Unauthenticated actions: status if action == 'status': paint_preallocation_status(emitter=emitter, token_agent=STAKEHOLDER.token_agent, preallocation_agent=STAKEHOLDER.preallocation_escrow_agent) return # Authenticated actions: withdraw-tokens client_account, staking_address = handle_client_account_for_staking(emitter=emitter, stakeholder=STAKEHOLDER, staking_address=staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=force) password = None if not hw_wallet and not blockchain.client.is_local: password = get_client_password(checksum_address=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=f'Collecting {unlocked_tokens} from PreallocationEscrow contract {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 set_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 = handle_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("Enter worker address", type=EIP55_CHECKSUM_ADDRESS) if (worker_address == staking_address) and not force: click.confirm("The worker address provided is the same as the staking account. " "It is *highly recommended* to use a different accounts for staker and worker roles.\n" "Continue?", abort=True) # TODO: Check preconditions (e.g., minWorkerPeriods, already in use, etc) password = transacting_staker_options.get_password(blockchain, client_account) # 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) STAKEHOLDER.assimilate(checksum_address=client_account, password=password) receipt = STAKEHOLDER.set_worker(worker_address=worker_address) # Report Success emitter.echo(f"\nWorker {worker_address} successfully bonded to staker {staking_address}", color='green') paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=blockchain.client.chain_name, transaction_type='set_worker') emitter.echo(f"Bonded at period #{current_period} ({bonded_date})", color='green') emitter.echo(f"This worker can be replaced or detached after period " f"#{release_period} ({release_date})", color='green')
def restake(click_config, # Stake Options poa, light, registry_filepath, config_file, provider_uri, staking_address, hw_wallet, beneficiary_address, allocation_filepath, # Other enable, lock_until, force): """ Manage re-staking with --enable or --disable. """ ### Setup ### emitter = _setup_emitter(click_config) STAKEHOLDER, blockchain = _create_stakeholder(config_file, provider_uri, poa, light, registry_filepath, staking_address, beneficiary_address=beneficiary_address, allocation_filepath=allocation_filepath) ############# client_account, staking_address = handle_client_account_for_staking(emitter=emitter, stakeholder=STAKEHOLDER, staking_address=staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=force) # Authenticate password = None if not hw_wallet and not blockchain.client.is_local: password = get_client_password(checksum_address=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(f'Successfully enabled re-staking lock for {staking_address} 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(f'Successfully enabled re-staking for {staking_address}', color='green', verbosity=1) else: if not force: click.confirm(f"Confirm disable re-staking for staker {staking_address}?", abort=True) receipt = STAKEHOLDER.disable_restaking() emitter.echo(f'Successfully disabled re-staking for {staking_address}', color='green', verbosity=1) paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=blockchain.client.chain_name)
def collect_reward(click_config, # Stake Options poa, light, registry_filepath, config_file, provider_uri, staking_address, hw_wallet, beneficiary_address, allocation_filepath, # Other staking_reward, policy_reward, withdraw_address, force): """ Withdraw staking reward. """ ### Setup ### emitter = _setup_emitter(click_config) STAKEHOLDER, blockchain = _create_stakeholder(config_file, provider_uri, poa, light, registry_filepath, staking_address, beneficiary_address=beneficiary_address, allocation_filepath=allocation_filepath) ############# client_account, staking_address = handle_client_account_for_staking(emitter=emitter, stakeholder=STAKEHOLDER, staking_address=staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=force) password = None if not hw_wallet and not blockchain.client.is_local: password = get_client_password(checksum_address=client_account) if not staking_reward and not policy_reward: raise click.BadArgumentUsage(f"Either --staking-reward or --policy-reward must be True to collect rewards.") 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=f'Collecting {reward_amount} from staking rewards...') staking_receipt = STAKEHOLDER.collect_staking_reward() paint_receipt_summary(receipt=staking_receipt, chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name, emitter=emitter) if policy_reward: reward_amount = Web3.fromWei(STAKEHOLDER.calculate_policy_reward(), 'ether') emitter.echo(message=f'Collecting {reward_amount} ETH from policy rewards...') policy_receipt = STAKEHOLDER.collect_policy_reward(collector_address=withdraw_address) paint_receipt_summary(receipt=policy_receipt, chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name, emitter=emitter)
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': painting.paint_preallocation_status( emitter=emitter, token_agent=STAKEHOLDER.token_agent, preallocation_agent=STAKEHOLDER.preallocation_escrow_agent) return # Authenticated actions: withdraw-tokens client_account, staking_address = handle_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 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= f'Collecting {unlocked_tokens} from PreallocationEscrow contract {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 collect_reward(general_config, transacting_staker_options, config_file, staking_reward, policy_reward, 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_reward: raise click.BadArgumentUsage( f"Either --staking-reward or --policy-reward must be True to collect rewards." ) client_account, staking_address = handle_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=f'Collecting {reward_amount} from staking rewards...') staking_receipt = STAKEHOLDER.collect_staking_reward() paint_receipt_summary( receipt=staking_receipt, chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name, emitter=emitter) if policy_reward: reward_amount = Web3.fromWei(STAKEHOLDER.calculate_policy_reward(), 'ether') emitter.echo( message=f'Collecting {reward_amount} ETH from policy rewards...') policy_receipt = STAKEHOLDER.collect_policy_reward( collector_address=withdraw_address) paint_receipt_summary( receipt=policy_receipt, chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name, emitter=emitter)
def detach_worker(general_config, transacting_staker_options, config_file, force): """ Detach 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 = handle_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(checksum_address=client_account, password=password) receipt = STAKEHOLDER.detach_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) emitter.echo( f"Successfully detached worker {worker_address} from staker {staking_address}", color='green') paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=blockchain.client.chain_name, transaction_type='detach_worker') emitter.echo(f"Detached at period #{current_period} ({bonded_date})", color='green')
def set_min_rate(general_config, transacting_staker_options, config_file, force, min_rate): """ Set minimum acceptable value for the reward rate. """ emitter = _setup_emitter(general_config) STAKEHOLDER = transacting_staker_options.create_character( emitter, config_file) blockchain = transacting_staker_options.get_blockchain() client_account, staking_address = handle_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: painting.paint_min_rate(emitter, STAKEHOLDER.registry, STAKEHOLDER.policy_agent, staking_address) # TODO check range min_rate = click.prompt( "Enter new value for min reward rate within range", type=WEI) password = transacting_staker_options.get_password(blockchain, client_account) if not force: click.confirm( f"Commit new value {min_rate} for " f"minimum acceptable reward rate?", abort=True) STAKEHOLDER.assimilate(checksum_address=client_account, password=password) receipt = STAKEHOLDER.set_min_reward_rate(min_rate=min_rate) # Report Success emitter.echo( f"\nMinimum reward rate {min_rate} successfully set by staker {staking_address}", color='green') paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=blockchain.client.chain_name, transaction_type='set_min_rate')
def divide( click_config, # Stake Options poa, light, registry_filepath, config_file, provider_uri, staking_address, hw_wallet, beneficiary_address, allocation_filepath, # Other force, value, lock_periods, index): """ Create a new stake from part of an existing one. """ ### Setup ### emitter = _setup_emitter(click_config) STAKEHOLDER, blockchain = _create_stakeholder( config_file, provider_uri, poa, light, registry_filepath, staking_address, beneficiary_address=beneficiary_address, allocation_filepath=allocation_filepath) ############# client_account, staking_address = handle_client_account_for_staking( emitter=emitter, stakeholder=STAKEHOLDER, staking_address=staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=force) economics = STAKEHOLDER.economics # 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) stake_extension_range = click.IntRange( min=1, max=economics.maximum_allowed_locked, clamp=False) if staking_address and index is not None: # 0 is valid. STAKEHOLDER.stakes = StakeList(registry=STAKEHOLDER.registry, checksum_address=staking_address) STAKEHOLDER.stakes.refresh() current_stake = STAKEHOLDER.stakes[index] else: current_stake = select_stake(stakeholder=STAKEHOLDER, emitter=emitter) # # Stage Stake # # Value if not value: value = click.prompt( f"Enter target value (must be less than or equal to {str(current_stake.value)})", type=stake_value_range) value = NU(value, 'NU') # Duration if not lock_periods: extension = click.prompt("Enter number of periods to extend", type=stake_extension_range) else: extension = lock_periods if not force: painting.paint_staged_stake_division(emitter=emitter, stakeholder=STAKEHOLDER, original_stake=current_stake, target_value=value, extension=extension) click.confirm("Is this correct?", abort=True) # Execute password = None if not hw_wallet and not blockchain.client.is_local: password = get_client_password( checksum_address=current_stake.staker_address) 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('Successfully divided stake', color='green', verbosity=1) paint_receipt_summary(emitter=emitter, receipt=new_stake.receipt, chain_name=blockchain.client.chain_name) # Show the resulting stake list painting.paint_stakes(emitter=emitter, stakes=STAKEHOLDER.stakes)
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 = handle_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) value = click.prompt( f"Enter target value ({min_allowed_locked} - {str(max_divide_value)})", 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(f"Enter number of periods to extend", type=divide_extension_range) else: extension = lock_periods if not force: issue_stake_suggestions(lock_periods=extension, value=value) painting.paint_staged_stake_division(emitter=emitter, stakeholder=STAKEHOLDER, original_stake=current_stake, target_value=value, extension=extension) click.confirm("Publish stake division to the blockchain?", 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( "Current period advanced before stake division was broadcasted. Please try again.", 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('Successfully divided stake', color='green', verbosity=1) paint_receipt_summary(emitter=emitter, receipt=new_stake.receipt, chain_name=blockchain.client.chain_name) # Show the resulting stake list painting.paint_stakes(emitter=emitter, stakeholder=STAKEHOLDER)
def create(general_config, transacting_staker_options, config_file, force, value, lock_periods): """ Initialize a new stake. """ 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 = handle_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) stake_duration_range = click.IntRange(min=economics.minimum_locked_periods, clamp=False) # # Stage Stake # if not value: token_balance = NU.from_nunits( STAKEHOLDER.token_agent.get_balance(staking_address)) lower_limit = NU.from_nunits( STAKEHOLDER.economics.minimum_allowed_locked) upper_limit = min( token_balance, NU.from_nunits(STAKEHOLDER.economics.maximum_allowed_locked)) value = click.prompt( f"Enter stake value in NU " f"({lower_limit} - {upper_limit})", type=stake_value_range, default=upper_limit.to_tokens()) value = NU.from_tokens(value) if not lock_periods: min_locktime = STAKEHOLDER.economics.minimum_locked_periods default_locktime = STAKEHOLDER.economics.maximum_rewarded_periods max_locktime = MAX_UINT16 - STAKEHOLDER.staking_agent.get_current_period( ) prompt = f"Enter stake duration ({min_locktime} - {max_locktime})" lock_periods = click.prompt(prompt, type=stake_duration_range, default=default_locktime) start_period = STAKEHOLDER.staking_agent.get_current_period() + 1 unlock_period = start_period + lock_periods # # Review and Publish # if not force: issue_stake_suggestions(value=value, lock_periods=lock_periods) painting.paint_staged_stake(emitter=emitter, stakeholder=STAKEHOLDER, staking_address=staking_address, stake_value=value, lock_periods=lock_periods, start_period=start_period, unlock_period=unlock_period) confirm_staged_stake(staker_address=staking_address, value=value, lock_periods=lock_periods) # Last chance to bail click.confirm("Publish staged stake to the blockchain?", 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 start_period != last_second_current_period + 1: emitter.echo( "Current period advanced before stake was broadcasted. Please try again.", color='red') raise click.Abort # Authenticate and Execute STAKEHOLDER.assimilate(checksum_address=client_account, password=password) new_stake = STAKEHOLDER.initialize_stake(amount=value, lock_periods=lock_periods) painting.paint_staking_confirmation(emitter=emitter, staker=STAKEHOLDER, new_stake=new_stake)
def set_worker( click_config, # Worker Options poa, light, registry_filepath, config_file, provider_uri, staking_address, hw_wallet, beneficiary_address, allocation_filepath, worker_address, # Other options force): """ Bond a worker to a staker. """ ### Setup ### emitter = _setup_emitter(click_config) STAKEHOLDER, blockchain = _create_stakeholder( config_file, provider_uri, poa, light, registry_filepath, staking_address, beneficiary_address=beneficiary_address, allocation_filepath=allocation_filepath) ############# economics = STAKEHOLDER.economics client_account, staking_address = handle_client_account_for_staking( emitter=emitter, stakeholder=STAKEHOLDER, staking_address=staking_address, individual_allocation=STAKEHOLDER.individual_allocation, force=force) if not worker_address: worker_address = click.prompt("Enter worker address", type=EIP55_CHECKSUM_ADDRESS) # TODO: Check preconditions (e.g., minWorkerPeriods, already in use, etc) password = None if not hw_wallet and not blockchain.client.is_local: password = get_client_password(checksum_address=client_account) STAKEHOLDER.assimilate(checksum_address=client_account, password=password) receipt = STAKEHOLDER.set_worker(worker_address=worker_address) # 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) min_worker_periods = STAKEHOLDER.staking_agent.staking_parameters()[7] 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) emitter.echo( f"\nWorker {worker_address} successfully bonded to staker {staking_address}", color='green') paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=blockchain.client.chain_name, transaction_type='set_worker') emitter.echo(f"Bonded at period #{current_period} ({bonded_date})", color='green') emitter.echo( f"This worker can be replaced or detached after period " f"#{release_period} ({release_date})", color='green')
def create( click_config, # Stake Options poa, light, registry_filepath, config_file, provider_uri, staking_address, hw_wallet, beneficiary_address, allocation_filepath, # Other force, value, lock_periods): """ Initialize a new stake. """ ### Setup ### emitter = _setup_emitter(click_config) STAKEHOLDER, blockchain = _create_stakeholder( config_file, provider_uri, poa, light, registry_filepath, staking_address, beneficiary_address=beneficiary_address, allocation_filepath=allocation_filepath) ############# economics = STAKEHOLDER.economics client_account, staking_address = handle_client_account_for_staking( emitter=emitter, stakeholder=STAKEHOLDER, staking_address=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) stake_duration_range = click.IntRange(min=economics.minimum_locked_periods, clamp=False) password = None if not hw_wallet and not blockchain.client.is_local: password = get_client_password(checksum_address=client_account) # # Stage Stake # if not value: value = click.prompt(f"Enter stake value in NU", type=stake_value_range, default=NU.from_nunits(min_locked).to_tokens()) value = NU.from_tokens(value) if not lock_periods: prompt = f"Enter stake duration ({STAKEHOLDER.economics.minimum_locked_periods} periods minimum)" lock_periods = click.prompt(prompt, type=stake_duration_range) start_period = STAKEHOLDER.staking_agent.get_current_period() + 1 unlock_period = start_period + lock_periods # # Review # if not force: painting.paint_staged_stake(emitter=emitter, stakeholder=STAKEHOLDER, staking_address=staking_address, stake_value=value, lock_periods=lock_periods, start_period=start_period, unlock_period=unlock_period) confirm_staged_stake(staker_address=staking_address, value=value, lock_periods=lock_periods) # Last chance to bail click.confirm("Publish staged stake to the blockchain?", abort=True) # Execute STAKEHOLDER.assimilate(checksum_address=client_account, password=password) new_stake = STAKEHOLDER.initialize_stake(amount=value, lock_periods=lock_periods) painting.paint_staking_confirmation(emitter=emitter, ursula=STAKEHOLDER, transactions=new_stake.transactions)