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
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