from .rpc.request import ( rpc_request ) from .rpc.exceptions import ( RPCError, RequestsError, RequestsTimeoutError ) from .exceptions import ( InvalidRPCReplyError ) from .blockchain import ( get_sharding_structure ) _default_endpoint = 'http://localhost:9500' _default_timeout = 30 _address_length = 42 # TODO: Implement real address validity check def is_valid_address(address) -> bool: """ Check if given string is valid one address Parameters ---------- address: str String to check if valid one address Returns ------- bool Is valid address """ if not address.startswith('one1') and len(address) != _address_length: return False return True def get_balance(address, endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get current account balance Parameters ---------- address: str Address to get balance for endpoint: :obj:`str`, optional Endpoint to send request to timeout: :obj:`int`, optional Timeout in seconds Returns ------- int Account balance in ATTO Raises ------ InvalidRPCReplyError If received unknown result from endpoint """ method = 'hmy_getBalance' params = [ address, 'latest' ] balance = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] try: return int(balance, 16) except TypeError as e: raise InvalidRPCReplyError(method, endpoint) from e def get_balance_by_block(address, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get account balance for address at a given block number Parameters ---------- address: str Address to get balance for block_num: int Block to get balance at endpoint: :obj:`str`, optional Endpoint to send request to timeout: :obj:`int`, optional Timeout in seconds Returns ------- int Account balance in ATTO Raises ------ InvalidRPCReplyError If received unknown result from endpoint """ method = 'hmy_getBalanceByBlockNumber' params = [ address, str(hex(block_num)) ] balance = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] try: return int(balance, 16) except TypeError as e: raise InvalidRPCReplyError(method, endpoint) from e def get_account_nonce(address, true_nonce=False, endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get the account nonce Parameters ---------- address: str Address to get transaction count for true_nonce: :obj:`bool`, optional True to get on-chain nonce False to get nonce based on pending transaction pool endpoint: :obj:`str`, optional Endpoint to send request to timeout: :obj:`int`, optional Timeout in seconds Returns ------- int Account nonce Raises ------ InvalidRPCReplyError If received unknown result from endpoint """ method = 'hmy_getTransactionCount' params = [ address, 'latest' if true_nonce else 'pending' ] nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] try: return int(nonce, 16) except TypeError as e: raise InvalidRPCReplyError(method, endpoint) from e def get_transaction_count(address, endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get number of transactions & staking transactions sent by an account Parameters ---------- address: str Address to get transaction count for endpoint: :obj:`str`, optional Endpoint to send request to timeout: :obj:`int`, optional Timeout in seconds Returns ------- int Number of transactions sent by the account See also -------- get_account_nonce """ return get_account_nonce(address, true_nonce=True, endpoint=endpoint, timeout=timeout) def get_transaction_history(address, page=0, page_size=1000, include_full_tx=False, tx_type='ALL', order='ASC', endpoint=_default_endpoint, timeout=_default_timeout ) -> list: """ Get list of transactions sent and/or received by the account Parameters ---------- address: str Address to get transaction history for page: :obj:`int`, optional Page to request for pagination page_size: :obj:`int`, optional Size of page for pagination include_full_tx: :obj:`bool`, optional True to include full transaction data False to just get the transaction hash tx_type: :obj:`str`, optional 'ALL' to get all transactions send & received by the address 'SENT' to get all transactions sent by the address 'RECEIVED' to get all transactions received by the address order: :obj:`str`, optional 'ASC' to sort transactions in ascending order based on timestamp 'DESC' to sort transactions in descending order based on timestamp endpoint: :obj:`str`, optional Endpoint to send request to timeout: :obj:`int`, optional Timeout in seconds Returns ------- list # TODO: Add link to reference RPC documentation Raises ------ InvalidRPCReplyError If received unknown result from endpoint """ params = [ { 'address': address, 'pageIndex': page, 'pageSize': page_size, 'fullTx': include_full_tx, 'txType': tx_type, 'order': order } ] method = 'hmy_getTransactionsHistory' tx_history = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout) try: return tx_history['result']['transactions'] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e def get_staking_transaction_history(address, page=0, page_size=1000, include_full_tx=False, tx_type='ALL', order='ASC', endpoint=_default_endpoint, timeout=_default_timeout ) -> list: """ Get list of staking transactions sent by the account Parameters ---------- address: str Address to get staking transaction history for page: :obj:`int`, optional Page to request for pagination page-size: :obj:`int`, optional Size of page for pagination include_full_tx: :obj:`bool`, optional True to include full staking transaction data False to just get the staking transaction hash tx_type: :obj:`str`, optional 'ALL' to get all staking transactions order: :obj:`str`, optional 'ASC' to sort transactions in ascending order based on timestamp 'DESC' to sort transactions in descending order based on timestamp endpoint: :obj:`str`, optional Endpoint to send request to timeout: :obj:`int`, optional Timeout in seconds Returns ------- list # TODO: Add link to reference RPC documentation Raises ------ InvalidRPCReplyError If received unknown result from endpoint """ params = [ { 'address': address, 'pageIndex': page, 'pageSize': page_size, 'fullTx': include_full_tx, 'txType': tx_type, 'order': order } ] # Using v2 API, because getStakingTransactionHistory not implemented in v1 method = 'hmyv2_getStakingTransactionsHistory' stx_history = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] try: return stx_history['staking_transactions'] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e def get_balance_on_all_shards(address, skip_error=True, endpoint=_default_endpoint, timeout=_default_timeout) -> list: """ Get current account balance in all shards & optionally report errors getting account balance for a shard Parameters ---------- address: str Address to get balance for skip_error: :obj:`bool`, optional True to ignore errors getting balance for shard False to include errors when getting balance for shard endpoint: :obj:`str`, optional Endpoint to send request to timeout: :obj:`int`, optional Timeout in seconds per request Returns ------- list Account balance per shard in ATTO Example reply: [ { 'shard': 0, 'balance': 0, }, ... ] """ balances = [] sharding_structure = get_sharding_structure(endpoint=endpoint, timeout=timeout) for shard in sharding_structure: try: balances.append({ 'shard': shard['shardID'], 'balance': get_balance(address, endpoint=shard['http'], timeout=timeout) }) except (KeyError, RPCError, RequestsError, RequestsTimeoutError): if not skip_error: balances.append({ 'shard': shard['shardID'], 'balance': None }) return balances def get_total_balance(address, endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get total account balance on all shards Parameters ---------- address: str Address to get balance for endpoint: :obj:`str`, optional Endpoint to send request to timeout: :obj:`int`, optional Timeout in seconds per request Returns ------- int Total account balance in ATTO Raises ------ RuntimeError If error occurred getting account balance for a shard """ try: balances = get_balance_on_all_shards(address, skip_error=False, endpoint=endpoint, timeout=timeout) return sum(b['balance'] for b in balances) except TypeError as e: raise RuntimeError from e