Пример #1
0
def paint_stakes(emitter: StdoutEmitter,
                 staker: 'Staker',
                 stakes: List[Stake] = None,
                 paint_unlocked: bool = False) -> None:

    stakes = stakes or staker.sorted_stakes()

    fees = staker.policy_agent.get_fee_amount(staker.checksum_address)
    pretty_fees = prettify_eth_amount(fees)
    last_committed = staker.staking_agent.get_last_committed_period(
        staker.checksum_address)
    missing = staker.missing_commitments
    min_fee_rate = prettify_eth_amount(staker.min_fee_rate)

    if missing == -1:
        missing_info = "Never Made a Commitment (New Stake)"
    else:
        missing_info = f'Missing {missing} commitments{"s" if missing > 1 else ""}' if missing else f'Committed #{last_committed}'

    staker_data = [
        missing_info,
        f'{"Yes" if staker.is_restaking else "No"} ({"Locked" if staker.restaking_lock_enabled else "Unlocked"})',
        "Yes" if bool(staker.is_winding_down) else "No",
        "Yes" if bool(staker.is_taking_snapshots) else "No", pretty_fees,
        min_fee_rate
    ]

    line_width = 54
    if staker.registry.source:  # TODO: #1580 - Registry source might be Falsy in tests.
        network_snippet = f"\nNetwork {staker.registry.source.network.capitalize()} "
        snippet_with_line = network_snippet + '═' * (line_width -
                                                     len(network_snippet) + 1)
        emitter.echo(snippet_with_line, bold=True)
    emitter.echo(f"Staker {staker.checksum_address} ════",
                 bold=True,
                 color='red' if missing else 'green')
    worker = staker.worker_address if staker.worker_address != NULL_ADDRESS else 'not bonded'
    emitter.echo(
        f"Worker {worker} ════",
        color='red' if staker.worker_address == NULL_ADDRESS else None)
    emitter.echo(
        tabulate.tabulate(zip(STAKER_TABLE_COLUMNS, staker_data),
                          floatfmt="fancy_grid"))

    rows = list()
    for index, stake in enumerate(stakes):
        if stake.status().is_child(
                Stake.Status.UNLOCKED) and not paint_unlocked:
            # This stake is unlocked.
            continue
        rows.append(list(stake.describe().values()))

    if not rows:
        emitter.echo(f"There are no locked stakes\n")

    emitter.echo(
        tabulate.tabulate(rows,
                          headers=STAKE_TABLE_COLUMNS,
                          tablefmt="fancy_grid"))  # newline
Пример #2
0
def paint_stakers(emitter, stakers: List[str], staking_agent, policy_agent) -> None:
    current_period = staking_agent.get_current_period()
    emitter.echo(f"\nCurrent period: {current_period}")
    emitter.echo("\n| Stakers |\n")
    emitter.echo(f"{'Checksum address':42}  Staker information")
    emitter.echo('=' * (42 + 2 + 53))

    for staker in stakers:
        nickname, pairs = nickname_from_seed(staker)
        symbols = f"{pairs[0][1]}  {pairs[1][1]}"
        emitter.echo(f"{staker}  {'Nickname:':10} {nickname} {symbols}")
        tab = " " * len(staker)

        owned_tokens = staking_agent.owned_tokens(staker)
        last_confirmed_period = staking_agent.get_last_active_period(staker)
        worker = staking_agent.get_worker_from_staker(staker)
        is_restaking = staking_agent.is_restaking(staker)
        is_winding_down = staking_agent.is_winding_down(staker)

        missing_confirmations = current_period - last_confirmed_period
        owned_in_nu = round(NU.from_nunits(owned_tokens), 2)
        locked_tokens = round(NU.from_nunits(staking_agent.get_locked_tokens(staker)), 2)

        emitter.echo(f"{tab}  {'Owned:':10} {owned_in_nu}  (Staked: {locked_tokens})")
        if is_restaking:
            if staking_agent.is_restaking_locked(staker):
                unlock_period = staking_agent.get_restake_unlock_period(staker)
                emitter.echo(f"{tab}  {'Re-staking:':10} Yes  (Locked until period: {unlock_period})")
            else:
                emitter.echo(f"{tab}  {'Re-staking:':10} Yes  (Unlocked)")
        else:
            emitter.echo(f"{tab}  {'Re-staking:':10} No")
        emitter.echo(f"{tab}  {'Winding down:':10} {'Yes' if is_winding_down else 'No'}")
        emitter.echo(f"{tab}  {'Activity:':10} ", nl=False)
        if missing_confirmations == -1:
            emitter.echo(f"Next period confirmed (#{last_confirmed_period})", color='green')
        elif missing_confirmations == 0:
            emitter.echo(f"Current period confirmed (#{last_confirmed_period}). "
                         f"Pending confirmation of next period.", color='yellow')
        elif missing_confirmations == current_period:
            emitter.echo(f"Never confirmed activity", color='red')
        else:
            emitter.echo(f"Missing {missing_confirmations} confirmations "
                         f"(last time for period #{last_confirmed_period})", color='red')

        emitter.echo(f"{tab}  {'Worker:':10} ", nl=False)
        if worker == NULL_ADDRESS:
            emitter.echo(f"Worker not set", color='red')
        else:
            emitter.echo(f"{worker}")

        fees = prettify_eth_amount(policy_agent.get_reward_amount(staker))
        emitter.echo(f"{tab}  Unclaimed fees: {fees}")

        min_rate = prettify_eth_amount(policy_agent.get_min_reward_rate(staker))
        emitter.echo(f"{tab}  Min reward rate: {min_rate}")
Пример #3
0
    def __log_transaction(self, transaction_dict: dict,
                          contract_function: ContractFunction):
        """
        Format and log a transaction dict and return the transaction name string.
        This method *must not* mutate the original transaction dict.
        """
        # Do not mutate the original transaction dict
        tx = dict(transaction_dict).copy()

        # Format
        if tx.get('to'):
            tx['to'] = to_checksum_address(contract_function.address)
        try:
            tx['selector'] = contract_function.selector
        except AttributeError:
            pass
        tx['from'] = to_checksum_address(tx['from'])
        tx.update({
            f: prettify_eth_amount(v)
            for f, v in tx.items() if f in ('gasPrice', 'value')
        })
        payload_pprint = ', '.join("{}: {}".format(k, v)
                                   for k, v in tx.items())

        # Log
        transaction_name = get_transaction_name(
            contract_function=contract_function)
        self.log.debug(f"[TX-{transaction_name}] | {payload_pprint}")
def test_valid_bid(click_runner,
                   mocker,
                   mock_worklock_agent,
                   token_economics,
                   test_registry_source_manager,
                   surrogate_bidder,
                   mock_testerchain):

    now = mock_testerchain.get_blocktime()
    sometime_later = now + 100
    mocker.patch.object(BlockchainInterface, 'get_blocktime', return_value=sometime_later)

    minimum = token_economics.worklock_min_allowed_bid
    bid_value = random.randint(minimum, minimum * 100)
    bid_value_in_eth = Web3.fromWei(bid_value, 'ether')

    # Spy on the corresponding CLI function we are testing
    mock_ensure = mocker.spy(Bidder, 'ensure_bidding_is_open')
    mock_place_bid = mocker.spy(Bidder, 'place_bid')

    # Patch Bidder.get_deposited_eth so it returns what we expect, in the correct sequence
    deposited_eth_sequence = (
        0,  # When deciding if it's a new bid or increasing the existing one
        0,  # When placing the bid, inside Bidder.place_bid
        bid_value,  # When printing the CLI result, after the bid is placed ..
        bid_value,  # .. we use it twice
    )
    mocker.patch.object(Bidder, 'get_deposited_eth', new_callable=PropertyMock, side_effect=deposited_eth_sequence)

    command = ('escrow',
               '--participant-address', surrogate_bidder.checksum_address,
               '--value', bid_value_in_eth,
               '--provider', MOCK_PROVIDER_URI,
               '--signer', MOCK_PROVIDER_URI,
               '--network', TEMPORARY_DOMAIN,
               '--force')

    user_input = INSECURE_DEVELOPMENT_PASSWORD
    result = click_runner.invoke(worklock, command, catch_exceptions=False, input=user_input)
    assert result.exit_code == 0

    # OK - Let's see what happened

    # Bidder
    mock_ensure.assert_called_once()  # checked that the bidding window was open via actors layer
    mock_place_bid.assert_called_once()
    mock_place_bid.assert_called_once_with(surrogate_bidder, value=bid_value)
    assert_successful_transaction_echo(bidder_address=surrogate_bidder.checksum_address, cli_output=result.output)

    # Transactions
    mock_worklock_agent.assert_only_transactions(allowed=[mock_worklock_agent.bid])
    mock_worklock_agent.bid.assert_called_with(checksum_address=surrogate_bidder.checksum_address, value=bid_value)

    # Calls
    expected_calls = (mock_worklock_agent.eth_to_tokens, )
    for expected_call in expected_calls:
        expected_call.assert_called()

    # CLI output
    assert prettify_eth_amount(bid_value) in result.output
Пример #5
0
def paint_bidding_notice(emitter, bidder):
    message = WORKLOCK_AGREEMENT.format(
        refund_rate=prettify_eth_amount(
            bidder.worklock_agent.get_bonus_refund_rate()),
        end_date=maya.MayaDT(
            bidder.economics.bidding_end_date).local_datetime(),
        bidder_address=bidder.checksum_address,
        duration=bidder.economics.worklock_commitment_duration)
    emitter.echo(message)
    return
Пример #6
0
    def build_transaction(self,
                          contract_function: ContractFunction,
                          sender_address: str,
                          payload: dict = None,
                          transaction_gas_limit: int = None,
                          ) -> dict:

        #
        # Build
        #

        if not payload:
            payload = {}

        nonce = self.client.w3.eth.getTransactionCount(sender_address, 'pending')
        payload.update({'chainId': int(self.client.chain_id),
                        'nonce': nonce,
                        'from': sender_address,
                        'gasPrice': self.client.gas_price})

        if transaction_gas_limit:
            payload['gas'] = int(transaction_gas_limit)

        # Get transaction type
        deployment = isinstance(contract_function, ContractConstructor)
        try:
            transaction_name = contract_function.fn_name.upper()
        except AttributeError:
            transaction_name = 'DEPLOY' if deployment else 'UNKNOWN'

        payload_pprint = dict(payload)
        payload_pprint['from'] = to_checksum_address(payload['from'])
        payload_pprint.update({f: prettify_eth_amount(v) for f, v in payload.items() if f in ('gasPrice', 'value')})
        payload_pprint = ', '.join("{}: {}".format(k, v) for k, v in payload_pprint.items())
        self.log.debug(f"[TX-{transaction_name}] | {payload_pprint}")

        # Build transaction payload
        try:
            unsigned_transaction = contract_function.buildTransaction(payload)
        except (ValidationError, ValueError) as e:
            # TODO: #1504 - Handle validation failures for gas limits, invalid fields, etc.
            # Note: Geth raises ValueError in the same condition that pyevm raises ValidationError here.
            # Treat this condition as "Transaction Failed".
            error = str(e).replace("{", "").replace("}", "")  # See #724
            self.log.critical(f"Validation error: {error}")
            raise
        else:
            if deployment:
                self.log.info(f"Deploying contract: {len(unsigned_transaction['data'])} bytes")

        return unsigned_transaction
Пример #7
0
def claim(general_config: GroupGeneralConfig, worklock_options: WorkLockOptions, force: bool, hw_wallet: bool):
    """Claim tokens for your escrow, and start staking them"""
    emitter, registry, blockchain = worklock_options.setup(general_config=general_config)
    worklock_agent = ContractAgency.get_agent(WorkLockAgent, registry=registry)  # type: WorkLockAgent
    if not worklock_agent.is_claiming_available():
        emitter.echo(CLAIMING_NOT_AVAILABLE, color='red')
        raise click.Abort()

    bidder_address = worklock_options.get_bidder_address(emitter, registry)
    bidder = worklock_options.create_bidder(registry=registry, hw_wallet=hw_wallet)

    unspent_bid = bidder.available_compensation
    if unspent_bid:
        emitter.echo(WORKLOCK_ADDITIONAL_COMPENSATION_AVAILABLE.format(amount=prettify_eth_amount(unspent_bid)))
        if not force:
            message = CONFIRM_REQUEST_WORKLOCK_COMPENSATION.format(bidder_address=bidder_address)
            click.confirm(message, abort=True)
        emitter.echo(REQUESTING_WORKLOCK_COMPENSATION)
        receipt = bidder.withdraw_compensation()
        paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name)

    has_claimed = bidder.has_claimed
    if bool(has_claimed):
        emitter.echo(CLAIM_ALREADY_PLACED.format(bidder_address=bidder.checksum_address), color='red')
        raise click.Abort()

    tokens = NU.from_nunits(bidder.available_claim)
    emitter.echo(AVAILABLE_CLAIM_NOTICE.format(tokens=tokens), color='green', bold=True)
    if not force:
        lock_duration = bidder.worklock_agent.worklock_parameters()[-2]
        emitter.echo(WORKLOCK_CLAIM_ADVISORY.format(lock_duration=lock_duration), color='blue')
        click.confirm(CONFIRM_WORKLOCK_CLAIM.format(bidder_address=bidder_address), abort=True)
    emitter.echo(SUBMITTING_WORKLOCK_CLAIM)

    receipt = bidder.claim()
    paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name)
    paint_worklock_claim(emitter=emitter,
                         bidder_address=bidder_address,
                         network=worklock_options.network,
                         provider_uri=worklock_options.provider_uri)
Пример #8
0
def paint_stakes(emitter,
                 stakeholder,
                 paint_inactive: bool = False,
                 staker_address: str = None):
    headers = ('Idx', 'Value', 'Remaining', 'Enactment', 'Termination')
    staker_headers = ('Status', 'Restaking', 'Winding Down', 'Unclaimed Fees',
                      'Min reward rate')

    stakers = stakeholder.get_stakers()
    if not stakers:
        emitter.echo("No staking accounts found.")

    total_stakers = 0
    for staker in stakers:
        if not staker.stakes:
            # This staker has no active stakes.
            # TODO: Something with non-staking accounts?
            continue

        # Filter Target
        if staker_address and staker.checksum_address != staker_address:
            continue

        stakes = sorted(staker.stakes,
                        key=lambda s: s.address_index_ordering_key)
        active_stakes = filter(lambda s: s.is_active, stakes)
        if not active_stakes:
            emitter.echo(f"There are no active stakes\n")

        fees = staker.policy_agent.get_reward_amount(staker.checksum_address)
        pretty_fees = prettify_eth_amount(fees)
        last_confirmed = staker.staking_agent.get_last_active_period(
            staker.checksum_address)
        missing = staker.missing_confirmations
        min_reward_rate = prettify_eth_amount(staker.min_reward_rate)

        if missing == -1:
            missing_info = "Never Confirmed (New Stake)"
        else:
            missing_info = f'Missing {missing} confirmation{"s" if missing > 1 else ""}' if missing else f'Confirmed #{last_confirmed}'

        staker_data = [
            missing_info,
            f'{"Yes" if staker.is_restaking else "No"} ({"Locked" if staker.restaking_lock_enabled else "Unlocked"})',
            "Yes" if bool(staker.is_winding_down) else "No", pretty_fees,
            min_reward_rate
        ]

        line_width = 54
        if staker.registry.source:  # TODO: #1580 - Registry source might be Falsy in tests.
            network_snippet = f"\nNetwork {staker.registry.source.network.capitalize()} "
            snippet_with_line = network_snippet + '═' * (
                line_width - len(network_snippet) + 1)
            emitter.echo(snippet_with_line, bold=True)
        emitter.echo(f"Staker {staker.checksum_address} ════",
                     bold=True,
                     color='red' if missing else 'green')
        emitter.echo(f"Worker {staker.worker_address} ════")
        emitter.echo(
            tabulate.tabulate(zip(staker_headers, staker_data),
                              floatfmt="fancy_grid"))

        rows = list()
        for index, stake in enumerate(stakes):
            if not stake.is_active and not paint_inactive:
                # This stake is inactive.
                continue
            rows.append(list(stake.describe().values()))
        total_stakers += 1
        emitter.echo(
            tabulate.tabulate(rows, headers=headers,
                              tablefmt="fancy_grid"))  # newline

    if not total_stakers:
        emitter.echo("No Stakes found", color='red')
Пример #9
0
def paint_stakes(emitter: StdoutEmitter,
                 staker: 'Staker',
                 stakes: List[Stake] = None,
                 paint_unlocked: bool = False,
                 stakeholder=None) -> None:

    stakes = stakes or staker.sorted_stakes()

    fees = staker.policy_agent.get_fee_amount(staker.checksum_address)
    pretty_fees = prettify_eth_amount(fees)
    last_committed = staker.staking_agent.get_last_committed_period(
        staker.checksum_address)
    missing = staker.missing_commitments
    min_fee_rate = prettify_eth_amount(staker.min_fee_rate)

    if missing == -1:
        missing_info = "Never Made a Commitment (New Stake)"
    else:
        missing_info = f'Missing {missing} commitments{"s" if missing > 1 else ""}' if missing else f'Committed #{last_committed}'

    staker_data = [
        missing_info, "Yes" if staker.is_restaking else "No",
        "Yes" if bool(staker.is_winding_down) else "No",
        "Yes" if bool(staker.is_taking_snapshots) else "No", pretty_fees,
        min_fee_rate
    ]

    line_width = 54
    if staker.registry.source:  # TODO: #1580 - Registry source might be Falsy in tests.
        network_snippet = f"\nNetwork {staker.registry.source.network.capitalize()} "
        snippet_with_line = network_snippet + '═' * (line_width -
                                                     len(network_snippet) + 1)
        emitter.echo(snippet_with_line, bold=True)
    emitter.echo(f"Staker {staker.checksum_address} ════",
                 bold=True,
                 color='red' if missing else 'green')
    worker = staker.worker_address if staker.worker_address != NULL_ADDRESS else 'not bonded'
    emitter.echo(
        f"Worker {worker} ════",
        color='red' if staker.worker_address == NULL_ADDRESS else None)
    if stakeholder and stakeholder.worker_data:
        worker_data = stakeholder.worker_data.get(staker.checksum_address)
        if worker_data:
            emitter.echo(f"\t public address: {worker_data['publicaddress']}")
            if worker_data.get('nucypher version'):
                emitter.echo(
                    f"\t NuCypher Version: {worker_data['nucypher version']}")
            if worker_data.get('blockchain_provider'):
                emitter.echo(
                    f"\t Blockchain Provider: {worker_data['blockchain_provider']}"
                )
    emitter.echo(
        tabulate.tabulate(zip(STAKER_TABLE_COLUMNS, staker_data),
                          floatfmt="fancy_grid"))

    rows, inactive_substakes = list(), list()
    for index, stake in enumerate(stakes):
        is_inactive = False

        if stake.status().is_child(Stake.Status.INACTIVE):
            inactive_substakes.append(index)
            is_inactive = True

        if stake.status().is_child(
                Stake.Status.UNLOCKED) and not paint_unlocked:
            # This stake is unlocked.
            continue

        stake_description = stake.describe()
        if is_inactive:
            # stake is inactive - update display values since they don't make much sense to display
            stake_description['remaining'] = 'N/A'
            stake_description['last_period'] = 'N/A'
            stake_description['boost'] = 'N/A'

        rows.append(list(stake_description.values()))

    if not rows:
        emitter.echo(f"There are no locked stakes\n")

    emitter.echo(
        tabulate.tabulate(rows,
                          headers=STAKE_TABLE_COLUMNS,
                          tablefmt="fancy_grid"))  # newline

    if not paint_unlocked and inactive_substakes:
        emitter.echo(
            f"Note that some sub-stakes are inactive: {inactive_substakes}\n"
            f"Run `nucypher stake list --all` to show all sub-stakes.\n"
            f"Run `nucypher stake remove-inactive --all` to remove inactive sub-stakes; removal of inactive "
            f"sub-stakes will reduce commitment gas costs.",
            color='yellow')
Пример #10
0
def paint_stakers(emitter, stakers: List[str],
                  registry: BaseContractRegistry) -> None:
    staking_agent = ContractAgency.get_agent(StakingEscrowAgent,
                                             registry=registry)
    current_period = staking_agent.get_current_period()
    emitter.echo(f"\nCurrent period: {current_period}")
    emitter.echo("\n| Stakers |\n")
    emitter.echo(f"{'Checksum address':42}  Staker information")
    emitter.echo('=' * (42 + 2 + 53))

    for staker_address in stakers:
        staker = Staker(is_me=False,
                        checksum_address=staker_address,
                        registry=registry)
        nickname = Nickname.from_seed(staker_address)
        emitter.echo(
            f"{staker_address}  {'Nickname:':10} {nickname} {nickname.icon}")
        tab = " " * len(staker_address)

        owned_tokens = staker.owned_tokens()
        last_committed_period = staker.last_committed_period
        worker = staker.worker_address
        is_restaking = staker.is_restaking
        is_winding_down = staker.is_winding_down
        is_taking_snapshots = staker.is_taking_snapshots

        missing_commitments = current_period - last_committed_period
        owned_in_nu = round(owned_tokens, 2)
        current_locked_tokens = round(staker.locked_tokens(periods=0), 2)
        next_locked_tokens = round(staker.locked_tokens(periods=1), 2)

        emitter.echo(f"{tab}  {'Owned:':10} {owned_in_nu}")
        emitter.echo(
            f"{tab}  Staked in current period: {current_locked_tokens}")
        emitter.echo(f"{tab}  Staked in next period: {next_locked_tokens}")
        if is_restaking:
            emitter.echo(f"{tab}  {'Re-staking:':10} Yes")
        else:
            emitter.echo(f"{tab}  {'Re-staking:':10} No")
        emitter.echo(
            f"{tab}  {'Winding down:':10} {'Yes' if is_winding_down else 'No'}"
        )
        emitter.echo(
            f"{tab}  {'Snapshots:':10} {'Yes' if is_taking_snapshots else 'No'}"
        )
        emitter.echo(f"{tab}  {'Activity:':10} ", nl=False)
        if missing_commitments == -1:
            emitter.echo(f"Next period committed (#{last_committed_period})",
                         color='green')
        elif missing_commitments == 0:
            emitter.echo(
                f"Current period committed (#{last_committed_period}). "
                f"Pending commitment to next period.",
                color='yellow')
        elif missing_commitments == current_period:
            emitter.echo(f"Never made a commitment", color='red')
        else:
            emitter.echo(
                f"Missing {missing_commitments} commitments "
                f"(last time for period #{last_committed_period})",
                color='red')

        emitter.echo(f"{tab}  {'Worker:':10} ", nl=False)
        if worker == NULL_ADDRESS:
            emitter.echo(f"Worker not bonded", color='red')
        else:
            emitter.echo(f"{worker}")

        fees = prettify_eth_amount(staker.calculate_policy_fee())
        emitter.echo(f"{tab}  Unclaimed fees: {fees}")

        min_rate = prettify_eth_amount(staker.min_fee_rate)
        emitter.echo(f"{tab}  Min fee rate: {min_rate}")