Source code for pyetherscan.response

"""
A module used to define API-specific response objects. All Etherscan API requests return an instance of :py:class:`EtherscanResponse`, extended to meet the endpoint's specific needs.

See :doc:`/response` for an overview.
"""
import json

from . import error

try:
    from json.decoder import JSONDecodeError
except (AttributeError, ImportError):
    # In python 2.x there is no JSONDecodeError, the json module
    # simply throws a ValueError
    JSONDecodeError = ValueError


[docs]class EtherscanResponse(object): """ This is the parent class for all Etherscan API responses. All child classes must define :py:meth:`parse_response` Upon initialization, the class sets the following attributes: - `etherscan_response`: The response json from Etherscan. - `response_object`: The `requests.Response <http://docs.python-requests.org/en/master/api/#requests.Response>`_ returned by the API call. - `status`: The Etherscan response status (independent of the `requests.Response` status). - `message`: The Etherscan response message. - Class-specific attributes are then set via the call to :py:meth:`parse_response`. If a `403` error is received it will raise an :py:class:`EtherscanRequestError`. This typically means the rate limit has been reached. By default, :py:meth:`get_request` and :py:meth:`post_request` will handle this and automatically retry the request. """ def __init__(self, resp): # Check for rate limit errors if resp.status_code == 403: raise error.EtherscanRequestError( 'Rate limit reached.' ) # Ensure a valid response code was received if resp.status_code not in [200, 201]: raise error.EtherscanRequestError( 'reason: {reason}'.format(reason=resp.reason) ) # Attempt to parse response body try: self.etherscan_response = json.loads(resp.text) except (AttributeError, JSONDecodeError): raise error.EtherscanRequestError( 'Invalid request: \n{request}'.format( request=resp ) ) else: self.message = self.etherscan_response.get('message') self.status = self.etherscan_response.get('status') # Parse API message to check for API errors result = self.etherscan_response.get('result') bad_data = self.message == 'NOTOK' or result == 'Error!' if bad_data: raise error.EtherscanDataError( '{message}. result={result}'.format( message=self.message, result=result ) ) # Finish parsing response object self.response_object = resp self.response_status_code = resp.status_code self.parse_response()
[docs] def parse_response(self): """ The method that will parse the response object and store all attributes within the specific API response object. """ raise NotImplementedError
def __repr__(self): """ Build a response representation like: `EtherscanResponse(resp=<Response 200>)` """ return '{_class}(resp={resp})'.format( _class=self.__class__.__name__, resp=self.response_object )
[docs]class SingleAddressBalanceResponse(EtherscanResponse): """ Represents a response object for a single address account balance call within the Etherscan `Accounts` endpoint. Available attributes: - `balance`: The balance of the address returned as a float. Example: .. code-block:: python In [1]: response = SingleAddressBalanceResponse(resp) In [2]: response.etherscan_response Out[2]: { "status":"1", "message":"OK", "result":"40807168564070000000000" } In [3]: response.balance Out[3]: 40807168564070000000000.0 """
[docs] def parse_response(self): """ Parses a single balance request response. Example API response output: .. code-block:: python { "status":"1", "message":"OK", "result":"40807168564070000000000" } """ self.balance = float(self.etherscan_response.get('result'))
[docs]class MultiAddressBalanceResponse(EtherscanResponse): """ Represents a response object for a multi address account balance call within the Etherscan `Accounts` endpoint. Available attributes: - `balances`: The balances of the addresses returned as a dict. Example: .. code-block:: python In [1]: response = MultiAddressBalanceResponse(resp) In [2]: response.etherscan_response Out[2]: { "status":"1", "message":"OK", "result":[ { "account":"0xddbd2b932c763ba5b1b7ae3b362eac3e8d40121a", "balance":"40807168564070000000000" }, { "account":"0x63a9975ba31b0b9626b34300f7f627147df1f526", "balance":"332567136222827062478" }, { "account":"0x198ef1ec325a96cc354c7266a038be8b5c558f67", "balance":"12005264493462223951724" } ] } In [3]: response.balances Out[3]: { '0xddbd2b932c763ba5b1b7ae3b362eac3e8d40121a': 40807168564070000000000.0, '0x63a9975ba31b0b9626b34300f7f627147df1f526': 332567136222827062478.0, '0x198ef1ec325a96cc354c7266a038be8b5c558f67': 12005264493462223951724.0, } """
[docs] def parse_response(self): """ Parses a multi balance request response. Example API response output: .. code-block:: python { "status":"1", "message":"OK", "result":[ { "account":"0xddbd2b932c763ba5b1b7ae3b362eac3e8d40121a", "balance":"40807168564070000000000" }, { "account":"0x63a9975ba31b0b9626b34300f7f627147df1f526", "balance":"332567136222827062478" }, { "account":"0x198ef1ec325a96cc354c7266a038be8b5c558f67", "balance":"12005264493462223951724" } ] } """ address_balance_mapping_list = self.etherscan_response.get('result') self.balances = { mapping.get('account'): float(mapping.get('balance')) for mapping in address_balance_mapping_list }
[docs]class TransactionsByAddressResponse(EtherscanResponse):
[docs] def parse_response(self): """ Parses a transactions by address request response. Example API response output: .. code-block:: python { "status":"1", "message":"OK", "result":[ { "blockNumber":"54092", "timeStamp":"1439048640", "hash":"0x9c81f44c29ff0226f83...", "nonce":"0", "blockHash":"0xd3cabad6adab0b5...", "transactionIndex":"0", "from":"0x5abfec25f74cd88437631a7731906932776356f9", "to":"", "value":"11901464239480000000000000", "gas":"2000000", "gasPrice":"10000000000000", "isError":"0", "input":"0x6060b91f525b5ae7a03d...", "contractAddress":"0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae", "cumulativeGasUsed":"1436963", "gasUsed":"1436963", "confirmations":"3921024" }, { ... } ] } """ self.transactions = self.etherscan_response.get('result')
[docs]class TransactionsByHashResponse(EtherscanResponse):
[docs] def parse_response(self): """ Parses a transactions by hash request response. Example API response output: .. code-block:: python { "status":"1", "message":"OK", "result":[ { "blockNumber":"1743059", "timeStamp":"1466489498", "from":"0x2cac6e4b11d6b58f6d3c1c9d5fe8faa89f60e5a2", "to":"0x66a1c3eaf0f1ffc28d209c0763ed0ca614f3b002", "value":"7106740000000000", "contractAddress":"", "input":"", "type":"call", "gas":"2300", "gasUsed":"0", "isError":"0", "errCode":"" } ] } """ self.transaction = self.etherscan_response.get('result')[0]
[docs]class BlocksMinedByAddressResponse(EtherscanResponse):
[docs] def parse_response(self): """ Parses a blocks mined by address request response. Example API response output: .. code-block:: python { "status":"1", "message":"OK", "result":[ { "blockNumber":"3462296", "timeStamp":"1491118514", "blockReward":"5194770940000000000" }, { ... } ] } """ self.blocks = self.etherscan_response.get('result')
[docs]class ContractABIByAddressResponse(EtherscanResponse):
[docs] def parse_response(self): """ Parses a contract abi by address request response. Example API response output: .. code-block:: python { "status":"1", "message":"OK", "result":[ { 'constant': True, 'inputs': [ { 'name': '', 'type': 'uint256' } ], 'name': 'proposals', 'outputs': [ {'name': 'recipient', 'type': 'address'}, {'name': 'amount', 'type': 'uint256'}, {'name': 'description', 'type': 'string'}, {'name': 'votingDeadline', 'type': 'uint256'}, {'name': 'open', 'type': 'bool'}, {'name': 'proposalPassed', 'type': 'bool'}, {'name': 'proposalHash', 'type': 'bytes32'}, {'name': 'proposalDeposit', 'type': 'uint256'}, {'name': 'newCurator', 'type': 'bool'}, {'name': 'yea', 'type': 'uint256'}, {'name': 'nay', 'type': 'uint256'}, {'name': 'creator', 'type': 'address'} ], 'type': 'function' }, { ... } ] } """ self.contract_abi = self.etherscan_response.get('result')
[docs]class ContractStatusResponse(EtherscanResponse): """ Represents a response object for a contract status call within the Etherscan `Contracts` endpoint. Available attributes: - `contract_status`: The status of the contract returned as a json object. Example: .. code-block:: python In [1]: response = ContractStatusResponse(resp) In [2]: response.contract_status Out[2]: { "status":"1", "message":"OK", "result":{ "isError":"1", "errDescription":"Bad jump destination" } } """
[docs] def parse_response(self): """ Parses a transaction status by hash request response. Example API response output: .. code-block:: python { "status":"1", "message":"OK", "result":{ "isError":"1", "errDescription":"Bad jump destination" } } """ self.contract_status = self.etherscan_response.get('result')
[docs]class TokenSupplyResponse(EtherscanResponse): """ Represents a response object for a token supply call within the Etherscan `Tokens` endpoint. Available attributes: - `total_supply`: The total supply of the token returned as a float. Example: .. code-block:: python In [1]: response = TokenSupplyResponse(resp) In [2]: response.etherscan_response Out[2]: { "status":"1", "message":"OK", "result":"21265524714464" } In [3]: response.total_supply Out[3]: 21265524714464.0 """
[docs] def parse_response(self): """ Parses a token supply by address request response. Example API response output: .. code-block:: python { "status":"1", "message":"OK", "result":"21265524714464" } """ self.total_supply = float(self.etherscan_response.get('result'))
[docs]class TokenAccountBalanceResponse(EtherscanResponse): """ Represents a response object for a token account balance call within the Etherscan `Tokens` endpoint. Available attributes: - `balance`: The account balance of a token (by contract address) returned as a float. Example: .. code-block:: python In [1]: response = TokenSupplyResponse(resp) In [2]: response.etherscan_response Out[2]: { "status":"1", "message":"OK", "result":"135499" } In [3]: response.balance Out[3]: 135499.0 """
[docs] def parse_response(self): """ Parses a token account balance request response. Example API response output: .. code-block:: python { "status":"1", "message":"OK", "result":"135499" } """ self.balance = float(self.etherscan_response.get('result'))
[docs]class BlockRewardsResponse(EtherscanResponse): """ Represents a response object for a block / uncle rewards API call to the Etherscan `Blocks` endpoint. Available attributes: - `rewards_data`: A dict of the rewards for the block and any uncles mined. Example: .. code-block:: python In [1]: response = BlockRewardsResponse(resp) In [2]: response.rewards_data Out[2]: { "blockNumber": "2165403", "timeStamp": "1472533979", "blockMiner": "0x13a06d3dfe21e0db5c016c03ea7d2509f7f8d1e3", "blockReward": "5314181600000000000", "uncles": [ { "miner": "0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1", "unclePosition": "0", "blockreward": "3750000000000000000" }, { "miner": "0x0d0c9855c722ff0c78f21e43aa275a5b8ea60dce", "unclePosition": "1", "blockreward": "3750000000000000000" } ], "uncleInclusionReward": "312500000000000000" } """
[docs] def parse_response(self): """ Parses a token account balance request response. Example API response output: .. code-block:: python { "status": "1", "message": "OK", "result": { "blockNumber": "2165403", "timeStamp": "1472533979", "blockMiner": "0x13a06d3dfe21e0db5c016c03ea7d2509f7f8d1e3", "blockReward": "5314181600000000000", "uncles": [ { "miner": "0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1", "unclePosition": "0", "blockreward": "3750000000000000000" }, { "miner": "0x0d0c9855c722ff0c78f21e43aa275a5b8ea60dce", "unclePosition": "1", "blockreward": "3750000000000000000" } ], "uncleInclusionReward": "312500000000000000" } } """ self.rewards_data = self.etherscan_response.get('result')