Exemple #1
0
def test_stakeholder_configuration(test_emitter, test_registry,
                                   mock_testerchain, mock_staking_agent):

    stakeholder_config_options = StakeHolderConfigOptions(
        provider_uri=MOCK_PROVIDER_URI,
        poa=None,
        light=None,
        registry_filepath=None,
        network=TEMPORARY_DOMAIN,
        signer_uri=None)

    mock_staking_agent.get_all_stakes.return_value = [SubStakeInfo(1, 2, 3)]
    force = False
    selected_index = 0
    selected_account = mock_testerchain.client.accounts[selected_index]
    expected_stakeholder = StakeHolder(registry=test_registry,
                                       domains={TEMPORARY_DOMAIN},
                                       initial_address=selected_account)
    expected_stakeholder.refresh_stakes()

    staker_options = StakerOptions(config_options=stakeholder_config_options,
                                   staking_address=selected_account)
    transacting_staker_options = TransactingStakerOptions(
        staker_options=staker_options,
        hw_wallet=None,
        beneficiary_address=None,
        allocation_filepath=None)
    stakeholder_from_configuration = transacting_staker_options.create_character(
        emitter=test_emitter, config_file=None)
    client_account, staking_address = select_client_account_for_staking(
        emitter=test_emitter,
        stakeholder=stakeholder_from_configuration,
        staking_address=selected_account,
        individual_allocation=None,
        force=force)
    assert client_account == staking_address == selected_account
    assert stakeholder_from_configuration.stakes == expected_stakeholder.stakes
    assert stakeholder_from_configuration.checksum_address == client_account

    staker_options = StakerOptions(config_options=stakeholder_config_options,
                                   staking_address=None)
    transacting_staker_options = TransactingStakerOptions(
        staker_options=staker_options,
        hw_wallet=None,
        beneficiary_address=None,
        allocation_filepath=None)
    stakeholder_from_configuration = transacting_staker_options.create_character(
        emitter=None, config_file=None)
    client_account, staking_address = select_client_account_for_staking(
        emitter=test_emitter,
        stakeholder=stakeholder_from_configuration,
        staking_address=selected_account,
        individual_allocation=None,
        force=force)
    assert client_account == staking_address == selected_account
    assert stakeholder_from_configuration.stakes == expected_stakeholder.stakes
    assert stakeholder_from_configuration.checksum_address == client_account
Exemple #2
0
def test_select_client_account_for_staking_cli_action(
        test_emitter, test_registry, test_registry_source_manager, mock_stdin,
        mock_testerchain, capsys, mocker, mock_staking_agent):
    """Fine-grained assertions about the return value of interactive client account selection"""
    force = False
    mock_staking_agent.get_all_stakes.return_value = []

    selected_index = 0
    selected_account = mock_testerchain.client.accounts[selected_index]

    stakeholder = StakeHolder(registry=test_registry,
                              domains={TEMPORARY_DOMAIN})

    client_account, staking_address = select_client_account_for_staking(
        emitter=test_emitter,
        stakeholder=stakeholder,
        staking_address=selected_account,
        individual_allocation=None,
        force=force)
    assert client_account == staking_address == selected_account

    mock_stdin.line(str(selected_index))
    client_account, staking_address = select_client_account_for_staking(
        emitter=test_emitter,
        stakeholder=stakeholder,
        staking_address=None,
        individual_allocation=None,
        force=force)
    assert client_account == staking_address == selected_account
    assert mock_stdin.empty()

    staking_contract_address = '0xFABADA'
    mock_individual_allocation = mocker.Mock(
        beneficiary_address=selected_account,
        contract_address=staking_contract_address)
    mock_stdin.line(YES)
    client_account, staking_address = select_client_account_for_staking(
        emitter=test_emitter,
        stakeholder=stakeholder,
        individual_allocation=mock_individual_allocation,
        staking_address=None,
        force=force)

    assert client_account == selected_account
    assert staking_address == staking_contract_address
    assert mock_stdin.empty()

    captured = capsys.readouterr()
    message = PREALLOCATION_STAKE_ADVISORY.format(
        client_account=selected_account,
        staking_address=staking_contract_address)
    assert message in captured.out
Exemple #3
0
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)

    _client_account, staking_address = select_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.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)}")
Exemple #4
0
def set_min_rate(general_config, transacting_staker_options, config_file, force, min_rate):
    """Staker sets the minimum acceptable fee rate for their associated worker."""

    # Setup
    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    if not min_rate:
        paint_min_rate(emitter, STAKEHOLDER.registry, STAKEHOLDER.policy_agent, staking_address)
        # TODO check range
        min_rate = click.prompt(PROMPT_STAKER_MIN_POLICY_RATE, type=WEI)
    if not force:
        click.confirm(CONFIRM_NEW_MIN_POLICY_RATE.format(min_rate=min_rate), abort=True)
    password = transacting_staker_options.get_password(blockchain, client_account)
    STAKEHOLDER.assimilate(password=password)
    receipt = STAKEHOLDER.set_min_fee_rate(min_rate=min_rate)

    # Report Success
    message = SUCCESSFUL_SET_MIN_POLICY_RATE.format(min_rate=min_rate, staking_address=staking_address)
    emitter.echo(message, color='green')
    paint_receipt_summary(emitter=emitter,
                          receipt=receipt,
                          chain_name=blockchain.client.chain_name,
                          transaction_type='set_min_rate')
Exemple #5
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 = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    # Handle stake update and selection
    if index is not None:  # 0 is valid.
        current_stake = STAKEHOLDER.stakes[index]
    else:
        current_stake = select_stake(staker=STAKEHOLDER, emitter=emitter)

    #
    # Prolong
    #

    # Interactive
    if not lock_periods:
        max_extension = MAX_UINT16 - current_stake.final_locked_period
        # +1 because current period excluded
        min_extension = economics.minimum_locked_periods - current_stake.periods_remaining + 1
        if min_extension < 1:
            min_extension = 1
        duration_extension_range = click.IntRange(min=min_extension, max=max_extension, clamp=False)
        lock_periods = click.prompt(PROMPT_PROLONG_VALUE.format(minimum=min_extension, maximum=max_extension),
                                    type=duration_extension_range)
    if not force:
        click.confirm(CONFIRM_PROLONG.format(lock_periods=lock_periods), abort=True)

    # Authenticate
    password = transacting_staker_options.get_password(blockchain, client_account)
    STAKEHOLDER.assimilate(password=password)

    # Non-interactive: Consistency check to prevent the above agreement from going stale.
    last_second_current_period = STAKEHOLDER.staking_agent.get_current_period()
    if action_period != last_second_current_period:
        emitter.echo(PERIOD_ADVANCED_WARNING, color='red')
        raise click.Abort

    # Execute
    receipt = STAKEHOLDER.prolong_stake(stake=current_stake, additional_periods=lock_periods)

    # Report
    emitter.echo(SUCCESSFUL_STAKE_PROLONG, color='green', verbosity=1)
    paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=blockchain.client.chain_name)
    paint_stakes(emitter=emitter, staker=STAKEHOLDER)
Exemple #6
0
def merge(general_config, transacting_staker_options, config_file, force, index_1, index_2):
    """Merge two stakes into one."""

    # Setup
    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file)
    action_period = STAKEHOLDER.staking_agent.get_current_period()
    blockchain = transacting_staker_options.get_blockchain()

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    # Handle stakes selection
    stake_1, stake_2 = None, None

    if index_1 is not None and index_2 is not None:
        stake_1 = STAKEHOLDER.stakes[index_1]
        stake_2 = STAKEHOLDER.stakes[index_2]
    elif index_1 is not None:  # 0 is valid.
        stake_1 = STAKEHOLDER.stakes[index_1]
    elif index_2 is not None:
        stake_1 = STAKEHOLDER.stakes[index_2]

    if stake_1 is None:
        stake_1 = select_stake(staker=STAKEHOLDER, emitter=emitter)
    if stake_2 is None:
        emitter.echo(ONLY_DISPLAYING_MERGEABLE_STAKES_NOTE.format(final_period=stake_1.final_locked_period),
                     color='yellow')
        stake_2 = select_stake(staker=STAKEHOLDER,
                               emitter=emitter,
                               filter_function=lambda s: s.index != stake_1.index and
                                                         s.final_locked_period == stake_1.final_locked_period)

    if not force:
        click.confirm(CONFIRM_MERGE.format(stake_index_1=stake_1.index, stake_index_2=stake_2.index), abort=True)

    # Authenticate
    password = transacting_staker_options.get_password(blockchain, client_account)
    STAKEHOLDER.assimilate(password=password)

    # Non-interactive: Consistency check to prevent the above agreement from going stale.
    last_second_current_period = STAKEHOLDER.staking_agent.get_current_period()
    if action_period != last_second_current_period:
        emitter.echo(PERIOD_ADVANCED_WARNING, color='red')
        raise click.Abort

    # Execute
    receipt = STAKEHOLDER.merge_stakes(stake_1=stake_1, stake_2=stake_2)

    # Report
    emitter.echo(SUCCESSFUL_STAKES_MERGE, color='green', verbosity=1)
    paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=blockchain.client.chain_name)
    paint_stakes(emitter=emitter, staker=STAKEHOLDER)
Exemple #7
0
def restake(general_config, transacting_staker_options, config_file, enable,
            lock_until, force):
    """Manage re-staking with --enable or --disable."""

    # Setup
    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(
        emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.
        staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    # Authenticate
    password = transacting_staker_options.get_password(blockchain,
                                                       client_account)
    STAKEHOLDER.assimilate(checksum_address=client_account, password=password)

    # Inner Exclusive Switch
    if lock_until:
        if not force:
            confirm_enable_restaking_lock(emitter,
                                          staking_address=staking_address,
                                          release_period=lock_until)
        receipt = STAKEHOLDER.enable_restaking_lock(release_period=lock_until)
        emitter.echo(SUCCESSFUL_ENABLE_RESTAKE_LOCK.format(
            staking_address=staking_address, lock_until=lock_until),
                     color='green',
                     verbosity=1)
    elif enable:
        if not force:
            confirm_enable_restaking(emitter, staking_address=staking_address)
        receipt = STAKEHOLDER.enable_restaking()
        emitter.echo(SUCCESSFUL_ENABLE_RESTAKING.format(
            staking_address=staking_address),
                     color='green',
                     verbosity=1)
    else:
        if not force:
            click.confirm(CONFIRM_DISABLE_RESTAKING.format(
                staking_address=staking_address),
                          abort=True)
        receipt = STAKEHOLDER.disable_restaking()
        emitter.echo(SUCCESSFUL_DISABLE_RESTAKING.format(
            staking_address=staking_address),
                     color='green',
                     verbosity=1)

    paint_receipt_summary(receipt=receipt,
                          emitter=emitter,
                          chain_name=blockchain.client.chain_name)
Exemple #8
0
def winddown(general_config, transacting_staker_options, config_file, enable,
             force):
    """Manage winding down with --enable or --disable."""

    # Setup
    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(
        emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.
        staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    # Inner Exclusive Switch
    if enable:
        if not force:
            confirm_enable_winding_down(emitter,
                                        staking_address=staking_address)

        # Authenticate and Execute
        password = transacting_staker_options.get_password(
            blockchain, client_account)
        STAKEHOLDER.assimilate(password=password)

        receipt = STAKEHOLDER.enable_winding_down()
        emitter.echo(SUCCESSFUL_ENABLE_WIND_DOWN.format(
            staking_address=staking_address),
                     color='green',
                     verbosity=1)
    else:
        if not force:
            click.confirm(CONFIRM_DISABLE_WIND_DOWN.format(
                staking_address=staking_address),
                          abort=True)

        # Authenticate and Execute
        password = transacting_staker_options.get_password(
            blockchain, client_account)
        STAKEHOLDER.assimilate(password=password)

        receipt = STAKEHOLDER.disable_winding_down()
        emitter.echo(SUCCESSFUL_DISABLE_WIND_DOWN.format(
            staking_address=staking_address),
                     color='green',
                     verbosity=1)

    paint_receipt_summary(receipt=receipt,
                          emitter=emitter,
                          chain_name=blockchain.client.chain_name)
Exemple #9
0
def bond_worker(general_config, transacting_staker_options, config_file, force, worker_address):
    """Bond a worker to a staker."""

    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()
    economics = STAKEHOLDER.economics

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    if not worker_address:
        worker_address = click.prompt(PROMPT_WORKER_ADDRESS, type=EIP55_CHECKSUM_ADDRESS)

    if (worker_address == staking_address) and not force:
        click.confirm(CONFIRM_WORKER_AND_STAKER_ADDRESSES_ARE_EQUAL.format(address=worker_address), abort=True)

    # TODO: Check preconditions (e.g., minWorkerPeriods, already in use, etc)

    # TODO: Double-check dates
    # Calculate release datetime
    current_period = STAKEHOLDER.staking_agent.get_current_period()
    bonded_date = datetime_at_period(period=current_period, seconds_per_period=economics.seconds_per_period)
    min_worker_periods = STAKEHOLDER.economics.minimum_worker_periods

    release_period = current_period + min_worker_periods
    release_date = datetime_at_period(period=release_period,
                                      seconds_per_period=economics.seconds_per_period,
                                      start_of_period=True)

    if not force:
        click.confirm(f"Commit to bonding "
                      f"worker {worker_address} to staker {staking_address} "
                      f"for a minimum of {STAKEHOLDER.economics.minimum_worker_periods} periods?", abort=True)

    password = transacting_staker_options.get_password(blockchain, client_account)
    STAKEHOLDER.assimilate(password=password)
    receipt = STAKEHOLDER.bond_worker(worker_address=worker_address)

    # Report Success
    message = SUCCESSFUL_WORKER_BONDING.format(worker_address=worker_address, staking_address=staking_address)
    emitter.echo(message, color='green')
    paint_receipt_summary(emitter=emitter,
                          receipt=receipt,
                          chain_name=blockchain.client.chain_name,
                          transaction_type='bond_worker')
    emitter.echo(BONDING_DETAILS.format(current_period=current_period, bonded_date=bonded_date), color='green')
    emitter.echo(BONDING_RELEASE_INFO.format(release_period=release_period, release_date=release_date), color='green')
def test_select_client_account_for_staking_cli_action(
        test_emitter, test_registry, test_registry_source_manager, mock_stdin,
        mock_testerchain, capsys, mock_staking_agent):
    """Fine-grained assertions about the return value of interactive client account selection"""
    mock_staking_agent.get_all_stakes.return_value = []

    selected_index = 0
    selected_account = mock_testerchain.client.accounts[selected_index]

    stakeholder = StakeHolder(registry=test_registry,
                              domain=TEMPORARY_DOMAIN,
                              signer=Web3Signer(mock_testerchain.client))

    client_account, staking_address = select_client_account_for_staking(
        emitter=test_emitter,
        stakeholder=stakeholder,
        staking_address=selected_account)
    assert client_account == staking_address == selected_account

    mock_stdin.line(str(selected_index))
    client_account, staking_address = select_client_account_for_staking(
        emitter=test_emitter, stakeholder=stakeholder, staking_address=None)
    assert client_account == staking_address == selected_account
    assert mock_stdin.empty()
Exemple #11
0
def collect_reward(general_config, transacting_staker_options, config_file,
                   staking_reward, policy_fee, withdraw_address, force):
    """Withdraw staking reward."""

    # Setup
    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(
        emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()

    if not staking_reward and not policy_fee:
        raise click.BadArgumentUsage(
            f"Either --staking-reward or --policy-fee must be True to collect rewards."
        )

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.
        staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    password = transacting_staker_options.get_password(blockchain,
                                                       client_account)

    STAKEHOLDER.assimilate(checksum_address=client_account, password=password)
    if staking_reward:
        # Note: Sending staking / inflation rewards to another account is not allowed.
        reward_amount = NU.from_nunits(STAKEHOLDER.calculate_staking_reward())
        emitter.echo(message=COLLECTING_TOKEN_REWARD.format(
            reward_amount=reward_amount))
        staking_receipt = STAKEHOLDER.collect_staking_reward()
        paint_receipt_summary(
            receipt=staking_receipt,
            chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name,
            emitter=emitter)

    if policy_fee:
        fee_amount = Web3.fromWei(STAKEHOLDER.calculate_policy_fee(), 'ether')
        emitter.echo(message=COLLECTING_ETH_REWARD.format(
            reward_amount=fee_amount))
        policy_receipt = STAKEHOLDER.collect_policy_fee(
            collector_address=withdraw_address)
        paint_receipt_summary(
            receipt=policy_receipt,
            chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name,
            emitter=emitter)
Exemple #12
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':
        return paint_preallocation_status(
            emitter=emitter,
            token_agent=STAKEHOLDER.token_agent,
            preallocation_agent=STAKEHOLDER.preallocation_escrow_agent)

    # Authenticated actions: withdraw-tokens
    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.
        staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    # Authenticate
    password = transacting_staker_options.get_password(blockchain,
                                                       client_account)
    STAKEHOLDER.assimilate(checksum_address=client_account, password=password)

    if action == 'withdraw':
        token_balance = NU.from_nunits(
            STAKEHOLDER.token_agent.get_balance(staking_address))
        locked_tokens = NU.from_nunits(
            STAKEHOLDER.preallocation_escrow_agent.unvested_tokens)
        unlocked_tokens = token_balance - locked_tokens

        emitter.echo(message=COLLECTING_PREALLOCATION_REWARD.format(
            unlocked_tokens=unlocked_tokens, staking_address=staking_address))
        receipt = STAKEHOLDER.withdraw_preallocation_tokens(unlocked_tokens)
        paint_receipt_summary(
            receipt=receipt,
            chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name,
            emitter=emitter)
Exemple #13
0
def unbond_worker(general_config, transacting_staker_options, config_file,
                  force):
    """
    Unbond worker currently bonded to a staker.
    """
    emitter = setup_emitter(general_config)

    STAKEHOLDER = transacting_staker_options.create_character(
        emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()

    economics = STAKEHOLDER.economics

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.
        staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    # TODO: Check preconditions (e.g., minWorkerPeriods)
    worker_address = STAKEHOLDER.staking_agent.get_worker_from_staker(
        staking_address)

    password = transacting_staker_options.get_password(blockchain,
                                                       client_account)
    STAKEHOLDER.assimilate(password=password)
    receipt = STAKEHOLDER.unbond_worker()

    # TODO: Double-check dates
    current_period = STAKEHOLDER.staking_agent.get_current_period()
    bonded_date = datetime_at_period(
        period=current_period, seconds_per_period=economics.seconds_per_period)

    message = SUCCESSFUL_DETACH_WORKER.format(worker_address=worker_address,
                                              staking_address=staking_address)
    emitter.echo(message, color='green')
    paint_receipt_summary(emitter=emitter,
                          receipt=receipt,
                          chain_name=blockchain.client.chain_name,
                          transaction_type='unbond_worker')
    emitter.echo(DETACH_DETAILS.format(current_period=current_period,
                                       bonded_date=bonded_date),
                 color='green')
Exemple #14
0
def mint(general_config, transacting_staker_options, config_file, force):
    """Mint last portion of reward"""

    # Setup
    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(
        emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.
        staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    # Nothing to mint
    mintable_periods = STAKEHOLDER.mintable_periods()
    if mintable_periods == 0:
        emitter.echo(NO_MINTABLE_PERIODS, color='red')
        raise click.Abort

    # Still locked token
    if STAKEHOLDER.non_withdrawable_stake() > 0:
        emitter.echo(STILL_LOCKED_TOKENS, color='yellow')

    if not force:
        click.confirm(
            CONFIRM_MINTING.format(mintable_periods=mintable_periods),
            abort=True)

    # Authenticate
    password = transacting_staker_options.get_password(blockchain,
                                                       client_account)
    STAKEHOLDER.assimilate(password=password)
    receipt = STAKEHOLDER.mint()
    emitter.echo(SUCCESSFUL_MINTING, color='green', verbosity=1)

    paint_receipt_summary(receipt=receipt,
                          emitter=emitter,
                          chain_name=blockchain.client.chain_name,
                          transaction_type='mint')
Exemple #15
0
def divide(general_config, transacting_staker_options, config_file, force,
           value, lock_periods, index):
    """Create a new stake from part of an existing one."""

    # Setup
    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(
        emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()
    economics = STAKEHOLDER.economics
    action_period = STAKEHOLDER.staking_agent.get_current_period()

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.
        staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    # Dynamic click types (Economics)
    min_locked = economics.minimum_allowed_locked
    stake_value_range = click.FloatRange(
        min=NU.from_nunits(min_locked).to_tokens(), clamp=False)

    if transacting_staker_options.staker_options.staking_address and index is not None:  # 0 is valid.
        STAKEHOLDER.stakes = StakeList(
            registry=STAKEHOLDER.registry,
            checksum_address=transacting_staker_options.staker_options.
            staking_address)
        STAKEHOLDER.stakes.refresh()
        current_stake = STAKEHOLDER.stakes[index]
    else:
        current_stake = select_stake(stakeholder=STAKEHOLDER,
                                     emitter=emitter,
                                     divisible=True,
                                     staker_address=client_account)

    #
    # Stage Stake
    #

    # Value
    if not value:
        min_allowed_locked = NU.from_nunits(
            STAKEHOLDER.economics.minimum_allowed_locked)
        max_divide_value = max(min_allowed_locked,
                               current_stake.value - min_allowed_locked)
        prompt = PROMPT_STAKE_DIVIDE_VALUE.format(
            minimm=min_allowed_locked, maximum=str(max_divide_value))
        value = click.prompt(prompt, type=stake_value_range)
    value = NU(value, 'NU')

    # Duration
    if not lock_periods:
        max_extension = MAX_UINT16 - current_stake.final_locked_period
        divide_extension_range = click.IntRange(min=1,
                                                max=max_extension,
                                                clamp=False)
        extension = click.prompt(PROMPT_STAKE_EXTEND_VALUE,
                                 type=divide_extension_range)
    else:
        extension = lock_periods

    if not force:
        confirm_large_stake(lock_periods=extension, value=value)
        paint_staged_stake_division(emitter=emitter,
                                    stakeholder=STAKEHOLDER,
                                    original_stake=current_stake,
                                    target_value=value,
                                    extension=extension)
        click.confirm(CONFIRM_BROADCAST_STAKE_DIVIDE, abort=True)

    # Authenticate
    password = transacting_staker_options.get_password(blockchain,
                                                       client_account)

    # Consistency check to prevent the above agreement from going stale.
    last_second_current_period = STAKEHOLDER.staking_agent.get_current_period()
    if action_period != last_second_current_period:
        emitter.echo(PERIOD_ADVANCED_WARNING, red='red')
        raise click.Abort

    # Execute
    STAKEHOLDER.assimilate(checksum_address=current_stake.staker_address,
                           password=password)
    modified_stake, new_stake = STAKEHOLDER.divide_stake(
        stake_index=current_stake.index,
        target_value=value,
        additional_periods=extension)
    emitter.echo(SUCCESSFUL_STAKE_DIVIDE, color='green', verbosity=1)
    paint_receipt_summary(emitter=emitter,
                          receipt=new_stake.receipt,
                          chain_name=blockchain.client.chain_name)

    # Show the resulting stake list
    paint_stakes(emitter=emitter, stakeholder=STAKEHOLDER)
Exemple #16
0
def create(general_config, transacting_staker_options, config_file, force,
           value, lock_periods):
    """Initialize a new stake."""

    # Setup
    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(
        emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()
    economics = STAKEHOLDER.economics

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.
        staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    # 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:
        confirm_large_stake(value=value, lock_periods=lock_periods)
        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(CONFIRM_BROADCAST_CREATE_STAKE, 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(PERIOD_ADVANCED_WARNING, 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)
    paint_staking_confirmation(emitter=emitter,
                               staker=STAKEHOLDER,
                               new_stake=new_stake)
Exemple #17
0
def collect_reward(general_config, transacting_staker_options, config_file,
                   staking_reward, policy_fee, withdraw_address, force):
    """Withdraw staking reward."""

    # Setup
    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(
        emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()

    if not staking_reward and not policy_fee:
        raise click.BadArgumentUsage(
            f"Either --staking-reward or --policy-fee must be True to collect rewards."
        )

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.
        staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    password = None

    if staking_reward:
        # Note: Sending staking / inflation rewards to another account is not allowed.
        reward_amount = NU.from_nunits(STAKEHOLDER.calculate_staking_reward())
        if reward_amount == 0:
            emitter.echo(NO_TOKENS_TO_WITHDRAW, color='red')
            raise click.Abort

        emitter.echo(message=COLLECTING_TOKEN_REWARD.format(
            reward_amount=reward_amount))

        withdrawing_last_portion = STAKEHOLDER.non_withdrawable_stake() == 0
        if not force and withdrawing_last_portion and STAKEHOLDER.mintable_periods(
        ) > 0:
            click.confirm(CONFIRM_COLLECTING_WITHOUT_MINTING, abort=True)

        # Authenticate and Execute
        password = transacting_staker_options.get_password(
            blockchain, client_account)
        STAKEHOLDER.assimilate(password=password)

        staking_receipt = STAKEHOLDER.collect_staking_reward()
        paint_receipt_summary(
            receipt=staking_receipt,
            chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name,
            emitter=emitter)

    if policy_fee:
        fee_amount = Web3.fromWei(STAKEHOLDER.calculate_policy_fee(), 'ether')
        if fee_amount == 0:
            emitter.echo(NO_FEE_TO_WITHDRAW, color='red')
            raise click.Abort

        emitter.echo(message=COLLECTING_ETH_FEE.format(fee_amount=fee_amount))

        if password is None:
            # Authenticate and Execute
            password = transacting_staker_options.get_password(
                blockchain, client_account)
            STAKEHOLDER.assimilate(password=password)

        policy_receipt = STAKEHOLDER.collect_policy_fee(
            collector_address=withdraw_address)
        paint_receipt_summary(
            receipt=policy_receipt,
            chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name,
            emitter=emitter)
Exemple #18
0
def increase(general_config, transacting_staker_options, config_file, force, value, index, only_lock):
    """Increase an existing stake."""

    # Setup
    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    # Handle stake update and selection
    if index is not None:  # 0 is valid.
        current_stake = STAKEHOLDER.stakes[index]
    else:
        current_stake = select_stake(staker=STAKEHOLDER, emitter=emitter)

    #
    # Stage Stake
    #

    if not value:
        if not only_lock:
            only_lock = not click.prompt(PROMPT_DEPOSIT_OR_LOCK, type=click.BOOL, default=True)

        token_balance = STAKEHOLDER.token_balance if not only_lock else STAKEHOLDER.calculate_staking_reward()
        locked_tokens = STAKEHOLDER.locked_tokens(periods=1).to_nunits()
        upper_limit = min(token_balance, NU.from_nunits(STAKEHOLDER.economics.maximum_allowed_locked - locked_tokens))

        if token_balance == 0:
            emitter.echo(INSUFFICIENT_BALANCE_TO_INCREASE, color='red')
            raise click.Abort
        if upper_limit == 0:
            emitter.echo(MAXIMUM_STAKE_REACHED, color='red')
            raise click.Abort

        stake_value_range = click.FloatRange(min=0, max=upper_limit.to_tokens(), clamp=False)
        value = click.prompt(PROMPT_STAKE_INCREASE_VALUE.format(upper_limit=upper_limit),
                             type=stake_value_range)
    value = NU.from_tokens(value)

    #
    # Review and Publish
    #

    if not force:
        lock_periods = current_stake.periods_remaining - 1
        current_period = STAKEHOLDER.staking_agent.get_current_period()
        unlock_period = current_stake.final_locked_period + 1

        confirm_large_stake(value=value, lock_periods=lock_periods)
        paint_staged_stake(emitter=emitter,
                           stakeholder=STAKEHOLDER,
                           staking_address=staking_address,
                           stake_value=value,
                           lock_periods=lock_periods,
                           start_period=current_period + 1,
                           unlock_period=unlock_period)
        click.confirm(CONFIRM_INCREASING_STAKE.format(stake_index=current_stake.index, value=value), abort=True)

    # Authenticate
    password = transacting_staker_options.get_password(blockchain, client_account)
    STAKEHOLDER.assimilate(password=password)

    # Execute
    receipt = STAKEHOLDER.increase_stake(stake=current_stake, amount=value, only_lock=only_lock)

    # Report
    emitter.echo(SUCCESSFUL_STAKE_INCREASE, color='green', verbosity=1)
    paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=blockchain.client.chain_name)
    paint_stakes(emitter=emitter, staker=STAKEHOLDER)
Exemple #19
0
def create(general_config, transacting_staker_options, config_file, force, value, lock_periods, only_lock):
    """Initialize a new stake."""

    # Setup
    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()
    economics = STAKEHOLDER.economics

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    # 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:
        if not only_lock:
            only_lock = not click.prompt(PROMPT_DEPOSIT_OR_LOCK, type=click.BOOL, default=True)

        token_balance = STAKEHOLDER.token_balance if not only_lock else STAKEHOLDER.calculate_staking_reward()
        lower_limit = NU.from_nunits(STAKEHOLDER.economics.minimum_allowed_locked)
        locked_tokens = STAKEHOLDER.locked_tokens(periods=1).to_nunits()
        upper_limit = min(token_balance, NU.from_nunits(STAKEHOLDER.economics.maximum_allowed_locked - locked_tokens))

        if token_balance <= lower_limit:
            emitter.echo(INSUFFICIENT_BALANCE_TO_CREATE, color='red')
            raise click.Abort
        if upper_limit <= lower_limit:
            emitter.echo(MAXIMUM_STAKE_REACHED, color='red')
            raise click.Abort

        value = click.prompt(PROMPT_STAKE_CREATE_VALUE.format(lower_limit=lower_limit, upper_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()
        lock_periods = click.prompt(PROMPT_STAKE_CREATE_LOCK_PERIODS.format(min_locktime=min_locktime,
                                                                            max_locktime=max_locktime),
                                    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:
        confirm_large_stake(value=value, lock_periods=lock_periods)
        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(CONFIRM_BROADCAST_CREATE_STAKE, abort=True)

    # Authenticate
    password = transacting_staker_options.get_password(blockchain, client_account)
    STAKEHOLDER.assimilate(password=password)

    # 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(PERIOD_ADVANCED_WARNING, color='red')
        raise click.Abort

    # Execute
    receipt = STAKEHOLDER.initialize_stake(amount=value, lock_periods=lock_periods, only_lock=only_lock)
    paint_staking_confirmation(emitter=emitter, staker=STAKEHOLDER, receipt=receipt)