Exemple #1
0
def select_client_account(emitter,
                          eth_provider_uri: str = None,
                          signer: Signer = None,
                          signer_uri: str = None,
                          prompt: str = None,
                          default: int = 0,
                          registry: BaseContractRegistry = None,
                          show_eth_balance: bool = False,
                          show_nu_balance: bool = False,
                          show_staking: bool = False,
                          network: str = None,
                          poa: bool = None) -> str:
    """
    Interactively select an ethereum wallet account from a table of nucypher account metadata.

    Note: Showing ETH and/or NU balances, causes an eager blockchain connection.
    """

    if signer and signer_uri:
        raise ValueError('Pass either signer or signer_uri but not both.')

    if not any((eth_provider_uri, signer_uri, signer)):
        raise ValueError(
            "At least a provider URI, signer URI or signer must be provided to select an account"
        )

    if eth_provider_uri:
        # Connect to the blockchain in order to select an account
        if not BlockchainInterfaceFactory.is_interface_initialized(
                eth_provider_uri=eth_provider_uri):
            BlockchainInterfaceFactory.initialize_interface(
                eth_provider_uri=eth_provider_uri, poa=poa, emitter=emitter)
        if not signer_uri:
            signer_uri = eth_provider_uri

    blockchain = BlockchainInterfaceFactory.get_interface(
        eth_provider_uri=eth_provider_uri)

    if signer_uri and not signer:
        testnet = network != NetworksInventory.MAINNET
        signer = Signer.from_signer_uri(signer_uri, testnet=testnet)

    # Display accounts info
    if show_nu_balance or show_staking:  # Lazy registry fetching
        if not registry:
            if not network:
                raise ValueError("Pass network name or registry; Got neither.")
            registry = InMemoryContractRegistry.from_latest_publication(
                network=network)

    enumerated_accounts = dict(enumerate(signer.accounts))
    if len(enumerated_accounts) < 1:
        emitter.echo(NO_ETH_ACCOUNTS, color='red', bold=True)
        raise click.Abort()
    elif len(enumerated_accounts) == 1:
        # There are no choices if there is only one available address.
        return enumerated_accounts[0]

    # Display account info
    headers = ['Account']
    if show_staking:
        headers.append('Staking')
    if show_eth_balance:
        headers.append('ETH')
    if show_nu_balance:
        headers.append('NU')

    rows = list()
    for index, account in enumerated_accounts.items():
        row = [account]
        if show_staking:
            staker = Staker(domain=network,
                            checksum_address=account,
                            registry=registry)
            staker.refresh_stakes()
            is_staking = 'Yes' if bool(staker.stakes) else 'No'
            row.append(is_staking)
        if show_eth_balance:
            ether_balance = Web3.fromWei(
                blockchain.client.get_balance(account), 'ether')
            row.append(f'{ether_balance} ETH')
        if show_nu_balance:
            token_agent = ContractAgency.get_agent(NucypherTokenAgent,
                                                   registry=registry)
            token_balance = NU.from_units(
                token_agent.get_balance(account, registry))
            row.append(token_balance)
        rows.append(row)
    emitter.echo(tabulate(rows, headers=headers, showindex='always'))

    # Prompt the user for selection, and return
    prompt = prompt or GENERIC_SELECT_ACCOUNT
    account_range = click.IntRange(min=0, max=len(enumerated_accounts) - 1)
    choice = click.prompt(prompt, type=account_range, default=default)
    chosen_account = enumerated_accounts[choice]

    emitter.echo(SELECTED_ACCOUNT.format(choice=choice,
                                         chosen_account=chosen_account),
                 color='blue')
    return chosen_account
Exemple #2
0
def test_select_client_account_with_balance_display(
        mock_stdin, test_emitter, mock_testerchain, capsys,
        test_registry_source_manager, mock_staking_agent, mock_token_agent,
        selection, show_staking, show_eth, show_tokens, stake_info):

    # Setup
    mock_staking_agent.get_all_stakes.return_value = stake_info

    # Missing network kwarg with balance display active
    blockchain_read_required = any((show_staking, show_eth, show_tokens))
    if blockchain_read_required:
        with pytest.raises(
                ValueError,
                match='Pass network name or registry; Got neither.'):
            select_client_account(emitter=test_emitter,
                                  show_eth_balance=show_eth,
                                  show_nu_balance=show_tokens,
                                  show_staking=show_staking,
                                  eth_provider_uri=MOCK_ETH_PROVIDER_URI)

    # Good selection
    mock_stdin.line(str(selection))
    selected_account = select_client_account(
        emitter=test_emitter,
        network=TEMPORARY_DOMAIN,
        show_eth_balance=show_eth,
        show_nu_balance=show_tokens,
        show_staking=show_staking,
        eth_provider_uri=MOCK_ETH_PROVIDER_URI)

    # check for accurate selection consistency with client index
    assert selected_account == mock_testerchain.client.accounts[selection]
    assert mock_stdin.empty()

    # Display account info
    headers = ['Account']
    if show_staking:
        headers.append('Staking')
    if show_eth:
        headers.append('ETH')
    if show_tokens:
        headers.append('NU')

    captured = capsys.readouterr()
    for column_name in headers:
        assert column_name in captured.out, f'"{column_name}" column was not displayed'

    for account in mock_testerchain.client.accounts:
        assert account in captured.out

        if show_tokens:
            balance = mock_token_agent.get_balance(address=account)
            assert str(NU.from_units(balance)) in captured.out

        if show_eth:
            balance = mock_testerchain.client.get_balance(account=account)
            assert str(Web3.fromWei(balance, 'ether')) in captured.out

        if show_staking:
            if len(stake_info) == 0:
                assert "No" in captured.out
            else:
                assert 'Yes' in captured.out