from .rpc.request import ( rpc_request ) from .rpc.exceptions import ( RPCError, RequestsError, RequestsTimeoutError ) from .exceptions import ( InvalidRPCReplyError ) from .blockchain import ( get_sharding_structure ) from .bech32.bech32 import ( bech32_decode ) _default_endpoint = 'http://localhost:9500' _default_timeout = 30 _address_length = 42 def is_valid_address(address) -> bool: """ Check if given string is valid one address NOTE: This function is NOT thread safe due to the C function used by the bech32 library. Parameters ---------- address: str String to check if valid one address Returns ------- bool Is valid address """ if not address.startswith('one1'): return False hrp, _ = bech32_decode(address) if not hrp: 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 API Reference ------------- https://api.hmny.io/#da8901d2-d237-4c3b-9d7d-10af9def05c4 """ method = 'hmyv2_getBalance' params = [ address ] try: balance = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] return int(balance) # v2 returns the result as it is except TypeError as e: # check will work if rpc returns None 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 API Reference ------------- https://api.hmny.io/#9aeae4b8-1a09-4ed2-956b-d7c96266dd33 https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/blockchain.go#L92 """ method = 'hmyv2_getBalanceByBlockNumber' params = [ address, block_num ] try: balance = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] return int(balance) except TypeError as e: raise InvalidRPCReplyError(method, endpoint) from e def get_account_nonce(address, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get the account nonce Parameters ---------- address: str Address to get transaction count for block_num: :obj:`int` or 'latest' Block to get nonce at 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 API Reference ------------- https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L51 """ method = 'hmyv2_getAccountNonce' params = [ address, block_num ] try: nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] return int(nonce) except TypeError as e: raise InvalidRPCReplyError(method, endpoint) from e def get_transaction_count(address, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get the number of transactions the given address has sent for the given block number Legacy for apiv1. For apiv2, please use get_account_nonce/get_transactions_count/get_staking_transactions_count apis for more granular transaction counts queries Parameters ---------- address: str Address to get transaction count for block_num: :obj:`int` or 'latest' Block to get nonce at endpoint: :obj:`str`, optional Endpoint to send request to timeout: :obj:`int`, optional Timeout in seconds Returns ------- int The number of transactions the given address has sent for the given block number Raises ------ InvalidRPCReplyError If received unknown result from endpoint API Reference ------------- https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L69 """ method = 'hmyv2_getTransactionCount' params = [ address, block_num ] try: nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] return int(nonce) except TypeError as e: raise InvalidRPCReplyError(method, endpoint) from e def get_transactions_count(address, tx_type, endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get the number of regular transactions from genesis of input type Parameters ---------- address: str Address to get transaction count for tx_type: str Type of transactions to include in the count currently supported are 'SENT', 'RECEIVED', 'ALL' endpoint: :obj:`str`, optional Endpoint to send request to timeout: :obj:`int`, optional Timeout in seconds Returns ------- int Count of transactions of type tx_type Raises ------ InvalidRPCReplyError If received unknown result from endpoint API Reference ------------- https://api.hmny.io/#fc97aed2-e65e-4cf4-bc01-8dadb76732c0 https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L114 """ method = 'hmyv2_getTransactionsCount' params = [ address, tx_type ] try: tx_count = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] return int(tx_count) except TypeError as e: raise InvalidRPCReplyError(method, endpoint) from e def get_staking_transactions_count(address, tx_type, endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get the number of staking transactions from genesis of input type ("SENT", "RECEIVED", "ALL") Parameters ---------- address: str Address to get staking transaction count for tx_type: str Type of staking transactions to include in the count currently supported are 'SENT', 'RECEIVED', 'ALL' endpoint: :obj:`str`, optional Endpoint to send request to timeout: :obj:`int`, optional Timeout in seconds Returns ------- int Count of staking transactions of type tx_type Raises ------ InvalidRPCReplyError If received unknown result from endpoint API Reference ------------- https://api.hmny.io/#ddc1b029-f341-4c4d-ba19-74b528d6e5e5 https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L134 """ method = 'hmyv2_getStakingTransactionsCount' params = [ address, tx_type ] try: tx_count = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] return int(tx_count) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e 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 (oldest first) 'DESC' to sort transactions in descending order based on timestamp (newest first) endpoint: :obj:`str`, optional Endpoint to send request to timeout: :obj:`int`, optional Timeout in seconds Returns ------- list of transactions if include_full_tx is True, each transaction is a dictionary with the following keys see transaction/get_transaction_by_hash for a description if include_full_tx is False, each element represents the transaction hash Raises ------ InvalidRPCReplyError If received unknown result from endpoint API Reference ------------- https://api.hmny.io/#2200a088-81b5-4420-a291-312a7c6d880e https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L255 """ params = [ { 'address': address, 'pageIndex': page, 'pageSize': page_size, 'fullTx': include_full_tx, 'txType': tx_type, 'order': order } ] method = 'hmyv2_getTransactionsHistory' try: tx_history = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout) 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 of transactions if include_full_tx is True, each transaction is a dictionary with the following kets blockHash: :obj:`str` Block hash that transaction was finalized; "0x0000000000000000000000000000000000000000000000000000000000000000" if tx is pending blockNumber: :obj:`int` Block number that transaction was finalized; None if tx is pending from: :obj:`str` Wallet address timestamp: :obj:`int` Timestamp in Unix time when transaction was finalized gas: :obj:`int` Gas limit in Atto gasPrice :obj:`int` Gas price in Atto hash: :obj:`str` Transaction hash nonce: :obj:`int` Wallet nonce for the transaction transactionIndex: :obj:`int` Index of transaction in block; None if tx is pending type: :obj:`str` Type of staking transaction, for example, "CollectRewards", "Delegate", "Undelegate" msg: :obj:`dict` Message attached to the staking transaction r: :obj:`str` First 32 bytes of the transaction signature s: :obj:`str` Next 32 bytes of the transaction signature v: :obj:`str` Recovery value + 27, as hex string if include_full_tx is False, each element represents the transaction hash Raises ------ InvalidRPCReplyError If received unknown result from endpoint API Reference ------------- https://api.hmny.io/#c5d25b36-57be-4e43-a23b-17ace350e322 https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L303 """ 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' try: stx_history = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] 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 of dictionaries, each dictionary to contain shard number and balance of that 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 See also ------ get_balance_on_all_shards """ 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