Beispiel #1
0
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
Beispiel #2
0
def refund(general_config, worklock_options, force, hw_wallet):
    """Reclaim ETH unlocked by your work"""
    emitter, registry, blockchain = worklock_options.setup(
        general_config=general_config)
    if not worklock_options.bidder_address:
        worklock_options.bidder_address = select_client_account(
            emitter=emitter,
            provider_uri=worklock_options.provider_uri,
            poa=worklock_options.poa,
            network=worklock_options.network,
            registry=registry,
            show_balances=True)
    if not force:
        click.confirm(
            f"Collect ETH refund for bidder {worklock_options.bidder_address}?",
            abort=True)
    emitter.echo("Submitting WorkLock refund request...")

    bidder = worklock_options.create_bidder(registry=registry,
                                            hw_wallet=hw_wallet)
    receipt = bidder.refund_deposit()
    paint_receipt_summary(
        receipt=receipt,
        emitter=emitter,
        chain_name=bidder.staking_agent.blockchain.client.chain_name)
    return  # Exit
Beispiel #3
0
def cancel_bid(general_config, worklock_options, force, hw_wallet):
    """Cancel your bid 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:
        raise click.Abort(
            f"You can't cancel your bid. The cancellation window is closed.")

    if not worklock_options.bidder_address:  # TODO: Consider bundle this in worklock_options
        worklock_options.bidder_address = select_client_account(
            emitter=emitter,
            provider_uri=worklock_options.provider_uri,
            poa=worklock_options.poa,
            network=worklock_options.network,
            show_balances=True,
            registry=registry)

    bidder = worklock_options.create_bidder(registry=registry,
                                            hw_wallet=hw_wallet)
    if not force:
        value = bidder.get_deposited_eth
        click.confirm(
            f"Confirm bid cancellation of {prettify_eth_amount(value)}"
            f" for {worklock_options.bidder_address}?",
            abort=True)
    receipt = bidder.cancel_bid()
    emitter.echo("Bid canceled\n", color='green')
    paint_receipt_summary(
        receipt=receipt,
        emitter=emitter,
        chain_name=bidder.staking_agent.blockchain.client.chain_name)
    return  # Exit
Beispiel #4
0
def claim(general_config, worklock_options, registry_options, force,
          hw_wallet):
    """Claim tokens for a successful bid, and start staking them"""
    emitter = _setup_emitter(general_config)
    if not worklock_options.bidder_address:
        worklock_options.bidder_address = select_client_account(
            emitter=emitter,
            provider_uri=registry_options.provider_uri,
            network=registry_options.network,
            show_balances=True)

    # TODO: Show amount of tokens to claim

    if not force:
        emitter.echo(
            "Note: Claiming WorkLock NU tokens will initialize a new stake.",
            color='blue')
        click.confirm(
            f"Continue worklock claim for bidder {worklock_options.bidder_address}?",
            abort=True)
    emitter.message("Submitting Claim...")
    registry = registry_options.get_registry(emitter, general_config.debug)
    bidder = worklock_options.create_bidder(registry=registry,
                                            hw_wallet=hw_wallet)
    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=worklock_options.bidder_address,
                         network=registry_options.network,
                         provider_uri=registry_options.provider_uri)
    return  # Exit
Beispiel #5
0
def cancel_bid(general_config, registry_options, worklock_options, force,
               hw_wallet):
    """Cancel your bid"""
    emitter = _setup_emitter(general_config)
    if not worklock_options.bidder_address:  # TODO: Consider bundle this in worklock_options
        worklock_options.bidder_address = select_client_account(
            emitter=emitter,
            provider_uri=registry_options.provider_uri,
            network=registry_options.network,
            show_balances=True)
    registry = registry_options.get_registry(emitter, general_config.debug)
    bidder = worklock_options.create_bidder(registry=registry,
                                            hw_wallet=hw_wallet)
    if not force:
        value = bidder.get_deposited_eth
        click.confirm(
            f"Confirm bid cancellation of {prettify_eth_amount(value)}"
            f" for {worklock_options.bidder_address}?",
            abort=True)
    receipt = bidder.cancel_bid()
    emitter.echo("Bid canceled\n", color='green')
    paint_receipt_summary(
        receipt=receipt,
        emitter=emitter,
        chain_name=bidder.staking_agent.blockchain.client.chain_name)
    return  # Exit
Beispiel #6
0
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)
    if not target_address:
        target_address = click.prompt("Enter recipient's checksum address",
                                      type=EIP55_CHECKSUM_ADDRESS)
    if not value:
        stake_value_range = click.FloatRange(min=0, clamp=False)
        value = NU.from_tokens(
            click.prompt(f"Enter value in NU", type=stake_value_range))

    click.confirm(
        f"Transfer {value} from {deployer_address} to {target_address}?",
        abort=True)
    receipt = token_agent.transfer(amount=int(value),
                                   sender_address=deployer_address,
                                   target_address=target_address)
    paint_receipt_summary(emitter=emitter, receipt=receipt)
Beispiel #7
0
def confirm_activity(click_config,

                     # API Options
                     geth, provider_uri, network, registry_filepath, staker_address, worker_address, federated_only,
                     rest_host, rest_port, db_filepath, poa, light, config_file, dev, lonely, teacher_uri, min_stake):
    """
    Manually confirm-activity for the current period.
    """

    ### Setup ###
    _validate_args(geth, federated_only, staker_address, registry_filepath)

    emitter = _setup_emitter(click_config, worker_address)

    _pre_launch_warnings(emitter, dev=dev, force=None)

    ursula_config, provider_uri = _get_ursula_config(emitter, geth, provider_uri, network, registry_filepath, dev,
                                                     config_file, staker_address, worker_address, federated_only,
                                                     rest_host, rest_port, db_filepath, poa, light)
    #############

    URSULA = _create_ursula(ursula_config, click_config, dev, emitter, lonely, teacher_uri, min_stake)

    receipt = URSULA.confirm_activity()

    confirmed_period = URSULA.staking_agent.get_current_period() + 1
    date = datetime_at_period(period=confirmed_period,
                              seconds_per_period=URSULA.economics.seconds_per_period)

    # TODO: Double-check dates here
    emitter.echo(f'\nActivity confirmed for period #{confirmed_period} '
                 f'(starting at {date})', bold=True, color='blue')
    painting.paint_receipt_summary(emitter=emitter,
                                   receipt=receipt,
                                   chain_name=URSULA.staking_agent.blockchain.client.chain_name)
Beispiel #8
0
def confirm_activity(general_config, character_options, config_file):
    """
    Manually confirm-activity for the current period.
    """
    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)

    confirmed_period = URSULA.staking_agent.get_current_period() + 1
    click.echo(f"Confirming activity for period {confirmed_period}",
               color='blue')
    receipt = URSULA.confirm_activity()

    economics = TokenEconomicsFactory.get_economics(registry=URSULA.registry)
    date = datetime_at_period(period=confirmed_period,
                              seconds_per_period=economics.seconds_per_period)

    # TODO: Double-check dates here
    emitter.echo(
        f'\nActivity confirmed for period #{confirmed_period} '
        f'(starting at {date})',
        bold=True,
        color='blue')
    painting.paint_receipt_summary(
        emitter=emitter,
        receipt=receipt,
        chain_name=URSULA.staking_agent.blockchain.client.chain_name)
Beispiel #9
0
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)
Beispiel #10
0
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("Enter new owner's checksum address", type=EIP55_CHECKSUM_ADDRESS)

    contract_name = actor_options.contract_name
    if contract_name:
        try:
            contract_deployer_class = ADMINISTRATOR.deployers[contract_name]
        except KeyError:
            message = f"No such contract {contract_name}. Available contracts are {ADMINISTRATOR.deployers.keys()}"
            emitter.echo(message, color='red', bold=True)
            raise click.Abort()
        else:
            contract_deployer = contract_deployer_class(registry=ADMINISTRATOR.registry,
                                                        deployer_address=ADMINISTRATOR.deployer_address)
            receipts = contract_deployer.transfer_ownership(new_owner=target_address, transaction_gas_limit=gas)
    else:
        click.confirm(f"You are about to relinquish ownership of all ownable contracts in favor of {target_address}.\n"
                      f"Are you sure you want to continue?", abort=True)
        receipts = ADMINISTRATOR.relinquish_ownership(new_owner=target_address, transaction_gas_limit=gas)

    for tx_type, receipt in receipts.items():
        paint_receipt_summary(emitter=emitter, receipt=receipt, transaction_type=tx_type)
Beispiel #11
0
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')
Beispiel #12
0
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)
Beispiel #13
0
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')
Beispiel #14
0
def upgrade(general_config, actor_options, retarget, target_address, ignore_deployed, multisig):
    """
    Upgrade NuCypher existing proxy contract deployments.
    """
    # Init
    emitter = general_config.emitter

    ADMINISTRATOR, _, _, registry = actor_options.create_actor(emitter, is_multisig=bool(multisig))  # FIXME: Workaround for building MultiSig TXs

    contract_name = actor_options.contract_name
    if not contract_name:
        raise click.BadArgumentUsage(message="--contract-name is required when using --upgrade")

    if multisig:
        if not target_address:
            raise click.BadArgumentUsage(message="--multisig requires using --target-address.")
        if not actor_options.force:
            click.confirm(f"Confirm building a re-target transaction for {contract_name}'s proxy to {target_address}?",
                          abort=True)
        transaction = ADMINISTRATOR.retarget_proxy(contract_name=contract_name,
                                                   target_address=target_address,
                                                   just_build_transaction=True)

        trustee_address = select_client_account(emitter=emitter,
                                                prompt="Select trustee address",
                                                provider_uri=actor_options.provider_uri,
                                                show_balances=False)

        if not actor_options.force:
            click.confirm(f"Selected {trustee_address} - Continue?", abort=True)

        trustee = Trustee(registry=registry, checksum_address=trustee_address)
        transaction_proposal = trustee.create_transaction_proposal(transaction)

        emitter.message(f"Transaction to retarget {contract_name} proxy to {target_address} was built:", color='green')
        paint_multisig_proposed_transaction(emitter, transaction_proposal)  # TODO: Show decoded function too

        filepath = f'proposal-{trustee.multisig_agent.contract_address[:8]}-TX-{transaction_proposal.nonce}.json'
        transaction_proposal.write(filepath=filepath)
        emitter.echo(f"Saved proposal to {filepath}", color='blue', bold=True)

    elif retarget:
        if not target_address:
            raise click.BadArgumentUsage(message="--target-address is required when using --retarget")
        if not actor_options.force:
            click.confirm(f"Confirm re-target {contract_name}'s proxy to {target_address}?", abort=True)
        receipt = ADMINISTRATOR.retarget_proxy(contract_name=contract_name,
                                               target_address=target_address)
        emitter.message(f"Successfully re-targeted {contract_name} proxy to {target_address}", color='green')
        paint_receipt_summary(emitter=emitter, receipt=receipt)
    else:
        if not actor_options.force:
            click.confirm(f"Confirm deploy new version of {contract_name} and retarget proxy?", abort=True)
        receipts = ADMINISTRATOR.upgrade_contract(contract_name=contract_name,
                                                  ignore_deployed=ignore_deployed)
        emitter.message(f"Successfully deployed and upgraded {contract_name}", color='green')
        for name, receipt in receipts.items():
            paint_receipt_summary(emitter=emitter, receipt=receipt)
Beispiel #15
0
def burn_unclaimed_tokens(general_config, registry_options, checksum_address):
    emitter = _setup_emitter(general_config)
    registry = registry_options.get_registry(emitter, general_config.debug)
    worklock_agent = ContractAgency.get_agent(WorkLockAgent, registry=registry)
    if not checksum_address:
        checksum_address = select_client_account(emitter=emitter, provider_uri=general_config.provider_uri)
    receipt = worklock_agent.burn_unclaimed(sender_address=checksum_address)
    paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=worklock_agent.blockchain.client.chain_name)
    return  # Exit
Beispiel #16
0
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)
Beispiel #17
0
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)
Beispiel #18
0
def enable_claiming(general_config, registry_options, worklock_options, force, hw_wallet, gas_limit):
    """Ensure correctness of bidding and enable claiming"""
    emitter = _setup_emitter(general_config)
    registry = registry_options.get_registry(emitter, general_config.debug)
    if not worklock_options.bidder_address:  # TODO: Consider bundle this in worklock_options
        worklock_options.bidder_address = select_client_account(emitter=emitter,
                                                                provider_uri=registry_options.provider_uri,
                                                                network=registry_options.network,
                                                                registry=registry,
                                                                show_balances=True)
    bidder = worklock_options.create_bidder(registry=registry, hw_wallet=hw_wallet)

    whales = bidder.get_whales()
    if whales:
        headers = ("Bidders that require correction", "Current bid 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)} bidders"
                          f" using {worklock_options.bidder_address}?", abort=True)

        force_refund_receipt = bidder.force_refund()
        emitter.echo(f"At least {len(whales)} bidders got a force refund\n", 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(f"All bids are correct, force refund is not needed\n", color='yellow')

    if not bidder.worklock_agent.bidders_checked():
        if not gas_limit:
            # TODO print gas estimations
            min_gas = 180000
            gas_limit = click.prompt(f"Enter gas limit per each verification transaction (at least {min_gas})",
                                     type=click.IntRange(min=min_gas))

        if not force:
            click.confirm(f"Confirm verifying of bidding from {worklock_options.bidder_address} "
                          f"using {gas_limit} gas per each transaction?", abort=True)

        verification_receipts = bidder.verify_bidding_correctness(gas_limit=gas_limit)
        emitter.echo("Bidding has been checked\n", 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(f"Bidders have already been checked\n", color='yellow')

    return  # Exit
Beispiel #19
0
def bid(general_config, worklock_options, registry_options, force, hw_wallet, value):
    """Place a bid, or increase an existing bid"""
    emitter = _setup_emitter(general_config)
    registry = registry_options.get_registry(emitter, general_config.debug)

    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:
        raise click.Abort(f"You can't bid, the bidding window is closed.")

    if not worklock_options.bidder_address:
        worklock_options.bidder_address = select_client_account(emitter=emitter,
                                                                provider_uri=registry_options.provider_uri,
                                                                poa=registry_options.poa,
                                                                network=registry_options.network,
                                                                registry=registry,
                                                                show_balances=True)

    bidder = worklock_options.create_bidder(registry=registry, hw_wallet=hw_wallet)

    if not value:
        if force:
            raise click.MissingParameter("Missing --value.")

        existing_bid_amount = bidder.get_deposited_eth
        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 = f"Enter bid amount in ETH (at least {minimum_bid_in_eth} ETH)"
        else:  # There's an existing bid and the bidder is increasing the amount
            emitter.message(f"You have an existing bid of {Web3.fromWei(existing_bid_amount, 'ether')} ETH")
            minimum_bid_in_eth = Web3.fromWei(1, 'ether')
            prompt = f"Enter the amount in ETH that you want to increase your bid"
        value = click.prompt(prompt, type=DecimalRange(min=minimum_bid_in_eth))

    value = int(Web3.toWei(Decimal(value), 'ether'))

    if not force:
        paint_bidding_notice(emitter=emitter, bidder=bidder)
        click.confirm(f"Place WorkLock bid of {prettify_eth_amount(value)}?", abort=True)

    receipt = bidder.place_bid(value=value)
    emitter.message("Publishing WorkLock Bid...")

    maximum = NU.from_nunits(bidder.economics.maximum_allowed_locked)
    available_claim = NU.from_nunits(bidder.available_claim)
    message = f'Current bid: {prettify_eth_amount(bidder.get_deposited_eth)} | Claim: {available_claim}\n'
    if available_claim > maximum:
        message += f"This claim is currently above the allowed max ({maximum}), so the bid may be partially refunded.\n"
    message += f'Note that available claim value may fluctuate until bidding closes and claims are finalized.\n'
    emitter.echo(message, color='yellow')

    paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name)
    return  # Exit
Beispiel #20
0
def refund(general_config, worklock_options, registry_options, force):
    emitter = _setup_emitter(general_config)
    if not worklock_options.bidder_address:
        worklock_options.bidder_address = select_client_account(emitter=emitter, provider_uri=general_config.provider_uri)
    if not force:
        click.confirm(f"Collect ETH refund for bidder {worklock_options.bidder_address}?", abort=True)
    emitter.message("Submitting WorkLock refund request...")
    registry = registry_options.get_registry(emitter, general_config.debug)
    bidder = worklock_options.create_bidder(registry=registry)
    receipt = bidder.refund_deposit()
    paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name)
    return  # Exit
Beispiel #21
0
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)
Beispiel #22
0
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)
Beispiel #23
0
def upgrade(general_config, actor_options, retarget, target_address,
            ignore_deployed):
    """
    Upgrade NuCypher existing proxy contract deployments.
    """
    # Init
    emitter = general_config.emitter
    ADMINISTRATOR, _, _, _ = actor_options.create_actor(emitter)

    contract_name = actor_options.contract_name
    if not contract_name:
        raise click.BadArgumentUsage(
            message="--contract-name is required when using --upgrade")

    existing_secret = click.prompt('Enter existing contract upgrade secret',
                                   hide_input=True)
    new_secret = click.prompt('Enter new contract upgrade secret',
                              hide_input=True,
                              confirmation_prompt=True)

    if retarget:
        if not target_address:
            raise click.BadArgumentUsage(
                message="--target-address is required when using --retarget")
        if not actor_options.force:
            click.confirm(
                f"Confirm re-target {contract_name}'s proxy to {target_address}?",
                abort=True)
        receipt = ADMINISTRATOR.retarget_proxy(
            contract_name=contract_name,
            target_address=target_address,
            existing_plaintext_secret=existing_secret,
            new_plaintext_secret=new_secret)
        emitter.message(
            f"Successfully re-targeted {contract_name} proxy to {target_address}",
            color='green')
        paint_receipt_summary(emitter=emitter, receipt=receipt)
    else:
        if not actor_options.force:
            click.confirm(
                f"Confirm deploy new version of {contract_name} and retarget proxy?",
                abort=True)
        receipts = ADMINISTRATOR.upgrade_contract(
            contract_name=contract_name,
            existing_plaintext_secret=existing_secret,
            new_plaintext_secret=new_secret,
            ignore_deployed=ignore_deployed)
        emitter.message(f"Successfully deployed and upgraded {contract_name}",
                        color='green')
        for name, receipt in receipts.items():
            paint_receipt_summary(emitter=emitter, receipt=receipt)
Beispiel #24
0
def cancel_bid(general_config, registry_options, worklock_options, force):
    emitter = _setup_emitter(general_config)
    if not worklock_options.bidder_address:
        worklock_options.bidder_address = select_client_account(emitter=emitter,
                                                                provider_uri=general_config.provider_uri)
    registry = registry_options.get_registry(emitter, general_config.debug)
    bidder = worklock_options.create_bidder(registry=registry)
    if not force:
        value = bidder.get_deposited_eth
        click.confirm(f"Confirm bid cancellation of {Web3.fromWei(value, 'ether')} ETH"
                      f" for {worklock_options.bidder_address}?", abort=True)
    receipt = bidder.cancel_bid()
    paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name)
    return  # Exit
Beispiel #25
0
def claim(general_config, worklock_options, registry_options, force, hw_wallet):
    """Claim tokens for your bid, and start staking them"""
    emitter = _setup_emitter(general_config)
    registry = registry_options.get_registry(emitter, general_config.debug)

    worklock_agent = ContractAgency.get_agent(WorkLockAgent, registry=registry)  # type: WorkLockAgent
    if not worklock_agent.is_claiming_available():
        raise click.Abort(f"You can't claim tokens. Claiming is not currently available.")

    if not worklock_options.bidder_address:
        worklock_options.bidder_address = select_client_account(emitter=emitter,
                                                                provider_uri=registry_options.provider_uri,
                                                                poa=registry_options.poa,
                                                                network=registry_options.network,
                                                                registry=registry,
                                                                show_balances=True)

    bidder = worklock_options.create_bidder(registry=registry, hw_wallet=hw_wallet)

    unspent_bid = bidder.available_compensation
    if unspent_bid:
        emitter.echo(f"Note that WorkLock did not use your entire bid due to a maximum claim limit.\n"
                     f"Therefore, an unspent amount of {prettify_eth_amount(unspent_bid)} is available for refund.")
        if not force:
            click.confirm(f"Before claiming your NU tokens for {worklock_options.bidder_address}, you will need to be refunded your unspent bid amount. Would you like to proceed?", abort=True)
        emitter.echo("Requesting refund of unspent bid amount...")
        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 has_claimed:
        emitter.echo(f"Claim was already done for {bidder.checksum_address}", color='red')
        return

    tokens = NU.from_nunits(bidder.available_claim)
    emitter.echo(f"\nYou have an available claim of {tokens} 🎉 \n", color='green', bold=True)
    if not force:
        lock_duration = bidder.worklock_agent.worklock_parameters()[-2]
        emitter.echo(f"Note: Claiming WorkLock NU tokens will initialize a new stake to be locked for {lock_duration} periods.",
                     color='blue')
        click.confirm(f"Continue WorkLock claim for bidder {worklock_options.bidder_address}?", abort=True)
    emitter.echo("Submitting 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=worklock_options.bidder_address,
                         network=registry_options.network,
                         provider_uri=registry_options.provider_uri)
    return  # Exit
Beispiel #26
0
def claim(general_config, worklock_options, registry_options, force):
    emitter = _setup_emitter(general_config)
    if not worklock_options.bidder_address:
        worklock_options.bidder_address = select_client_account(emitter=emitter,
                                                                provider_uri=general_config.provider_uri)
    if not force:
        emitter.message("Note: Claiming WorkLock NU tokens will initialize a new stake.", color='blue')
        click.confirm(f"Continue worklock claim for bidder {worklock_options.bidder_address}?", abort=True)
    emitter.message("Submitting Claim...")
    registry = registry_options.get_registry(emitter, general_config.debug)
    bidder = worklock_options.create_bidder(registry=registry)
    receipt = bidder.claim()
    paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name)
    paint_worklock_claim(emitter, bidder_address=worklock_options.bidder_address)
    return  # Exit
Beispiel #27
0
def execute(general_config, blockchain_options, multisig_options, proposal):
    """
    Collect authorizations from executives and execute transaction through MultiSig contract
    """
    # Init
    emitter = general_config.emitter
    #_ensure_config_root(actor_options.config_root)
    blockchain = blockchain_options.connect_blockchain(emitter,
                                                       general_config.debug)
    registry = blockchain_options.get_registry()

    proposal = Proposal.from_file(proposal)

    if not multisig_options.checksum_address:
        multisig_options.checksum_address = select_client_account(
            emitter=emitter,
            provider_uri=blockchain_options.provider_uri,
            poa=blockchain_options.poa,
            network=blockchain_options.network,
            registry=registry,
            show_balances=True)

    name, version, address, abi = registry.search(
        contract_address=proposal.target_address)
    # TODO: This assumes that we're always signing proxy retargetting. For the moment is true.
    proxy_contract = blockchain.client.w3.eth.contract(
        abi=abi,
        address=address,
        version=version,
        ContractFactoryClass=blockchain._contract_factory)
    paint_multisig_proposed_transaction(emitter, proposal, proxy_contract)

    trustee = multisig_options.create_trustee(registry)
    threshold = trustee.multisig_agent.threshold

    while len(trustee.authorizations) < threshold:
        auth_hex = click.prompt("Signature", type=click.STRING)
        authorization = Authorization.from_hex(auth_hex)
        executive_address = trustee.add_authorization(authorization, proposal)
        emitter.echo(f"Added authorization from executive {executive_address}",
                     color='green')

    click.confirm(
        "\nCollected required authorizations. Proceed with execution?",
        abort=True)

    receipt = trustee.execute(proposal)
    paint_receipt_summary(emitter, receipt)
Beispiel #28
0
def bid(general_config, worklock_options, registry_options, force, hw_wallet,
        value):
    """Place a bid"""
    emitter = _setup_emitter(general_config)

    if not worklock_options.bidder_address:
        worklock_options.bidder_address = select_client_account(
            emitter=emitter,
            provider_uri=registry_options.provider_uri,
            network=registry_options.network,
            show_balances=True)
    if not value:
        if force:
            raise click.MissingParameter("Missing --value.")
        value = click.prompt("Enter bid amount in ETH", type=click.STRING)
    value = int(Web3.toWei(Decimal(value), 'ether'))

    registry = registry_options.get_registry(emitter, general_config.debug)
    bidder = worklock_options.create_bidder(registry=registry,
                                            hw_wallet=hw_wallet)

    if not force:
        paint_bidding_notice(emitter=emitter, bidder=bidder)
        click.confirm(f"Place WorkLock bid of {prettify_eth_amount(value)}?",
                      abort=True)

    receipt = bidder.place_bid(value=value)
    emitter.message("Publishing WorkLock Bid...")

    # Ensure the total bid value is worth a claim that is at
    # least large enough for the minimum stake.
    minimum = bidder.economics.minimum_allowed_locked
    available_claim = NU.from_nunits(bidder.available_claim)
    if available_claim < minimum:
        warning = f"Total bid ({available_claim}) is too small for a claim, please bid more or cancel.\n" \
                  f"Total must be worth at least {NU.from_nunits(minimum)})."
        emitter.echo(warning, color='yellow')
    else:
        message = f'Current bid: {prettify_eth_amount(bidder.get_deposited_eth)} | ' \
                  f'Available claim: {available_claim}\n' \
                  f'Note that available claim value may fluctuate until bidding closes and claims are finalized.\n'
        emitter.echo(message, color='yellow')

    paint_receipt_summary(
        receipt=receipt,
        emitter=emitter,
        chain_name=bidder.staking_agent.blockchain.client.chain_name)
    return  # Exit
Beispiel #29
0
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')
Beispiel #30
0
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')