Source code for pyetherscan.ethereum

"""
Library for building ethereum objects using the ETherscan API.
"""
import datetime

from . import client, error


[docs]class Transaction(object): """ Represents a generic ethereum transaction. The object is built to lazily parse transactions. Attributes are only evaluated when called for the first time. Public Attributes: - ``nonce`` - ``contract_address`` - ``cumulative_gas_used`` - ``hash`` - ``block_hash`` - ``time_stamp`` - ``gas`` - ``gas_price`` - ``value`` - ``block_number`` - ``block`` - ``to`` - ``from_`` - ``confirmations`` - ``input`` - ``transaction_index`` - ``type`` - ``datetime_executed`` - ``gas_used`` """ def __init__(self, data): """ Initializes the Transaction object. :param data: The dictionary of data that makes up the transaction. :type data: dict """ if not isinstance(data, dict): raise error.EtherscanInitializationError( 'data must be of type dict.' ) self._data = data self._nonce = None self._contract_address = None self._cumulative_gas_used = None self._hash = None self._block_hash = None self._time_stamp = None self._gas = None self._value = None self._block_number = None self._to = None self._confirmations = None self._input = None self._transaction_index = None self._from = None self._gas_price = None self._datetime_executed = None self._gas_used = None self._type = None def _retrieve_gas_price(self): self._gas_price = float(self._data.get('gasPrice')) return self._gas_price @property def gas_price(self): return self._gas_price or self._retrieve_gas_price() def _retrieve_from(self): self._from = self._data.get('from') return self._from @property def from_(self): return self._from or self._retrieve_from() def _retrieve_nonce(self): self._nonce = self._data.get('nonce') return self._nonce @property def nonce(self): return self._nonce or self._retrieve_nonce() def _retrieve_contract_address(self): self._contract_address = self._data.get('contractAddress') return self._contract_address @property def contract_address(self): return self._contract_address or self._retrieve_contract_address() def _retrieve_cumulative_gas_used(self): self._cumulative_gas_used = float(self._data.get('cumulativeGasUsed')) return self._cumulative_gas_used @property def cumulative_gas_used(self): return self._cumulative_gas_used or self._retrieve_cumulative_gas_used() def _retrieve_hash(self): self._hash = self._data.get('hash') return self._hash @property def hash(self): return self._hash or self._retrieve_hash() def _retrieve_block_hash(self): self._block_hash = self._data.get('blockHash') return self._block_hash @property def block_hash(self): return self._block_hash or self._retrieve_block_hash() def _retrieve_time_stamp(self): self._time_stamp = int(self._data.get('timeStamp')) return self._time_stamp @property def time_stamp(self): return self._time_stamp or self._retrieve_time_stamp() def _retrieve_gas(self): self._gas = float(self._data.get('gas')) return self._gas @property def gas(self): return self._gas or self._retrieve_gas() def _retrieve_value(self): self._value = float(self._data.get('value')) return self._value @property def value(self): return self._value or self._retrieve_value() def _retrieve_block_number(self): self._block_number = int(self._data.get('blockNumber')) return self._block_number @property def block_number(self): return self._block_number or self._retrieve_block_number() @property def block(self): return Block(self.block_number) def _retrieve_to(self): self._to = self._data.get('to') return self._to @property def to(self): return self._to or self._retrieve_to() def _retrieve_confirmations(self): self._confirmations = self._data.get('confirmations') return self._confirmations @property def confirmations(self): return self._confirmations or self._retrieve_confirmations() def _retrieve_input(self): self._input = self._data.get('input') return self._input @property def input(self): return self._input or self._retrieve_input() def _retrieve_transaction_index(self): self._transaction_index = int(self._data.get('transactionIndex')) return self._transaction_index @property def transaction_index(self): return self._transaction_index or self._retrieve_transaction_index() def _retrieve_gas_used(self): self._gas_used = float(self._data.get('gasUsed')) return self._gas_used @property def gas_used(self): return self._gas_used or self._retrieve_gas_used() def _retrieve_type(self): self._type = self._data.get('type') return self._type @property def type(self): return self._type or self._retrieve_type() def _convert_time_stamp(self): self._datetime_executed = datetime.datetime.utcfromtimestamp( self.time_stamp ) return self._datetime_executed @property def datetime_executed(self): return self._datetime_executed or self._convert_time_stamp() def __repr__(self): return 'Transaction(hash={hash}, value={value}, ' \ 'datetime_executed={datetime_executed})'.format( hash=self.hash, value=self.value, datetime_executed=self.datetime_executed )
[docs]class TransactionContainer(object): """ Represents a sequence of transactions (normal and internal). """ def __init__(self, transaction_list): if not isinstance(transaction_list, list): raise TypeError('transaction_list must be of type list, ' 'not {type}'.format(type=type(transaction_list))) self.transaction_list = transaction_list def __iter__(self): for transaction in self.transaction_list: yield Transaction(transaction) def __getitem__(self, index): transaction_to_return = self.transaction_list[index] return Transaction(transaction_to_return) def __repr__(self): return 'TransactionContainer(transaction_list=<{n} transactions>)'.format( n=len(self.transaction_list) )
[docs]class Address(object): """ Represents a base address. This uses the :py:class:`Client` object to retrieve information about, and construct, the ``Address``. Public Attributes: - ``address`` - ``balance`` - ``blocks_mined`` Public Methods: - :py:meth:`token_balance` Example Usage: .. code-block:: python In [1]: address = '0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae' In [2]: ethereum_address = Address(address) In [3]: ethereum_address.balance Out[3]: 748997604382925139479303.0 In [4]: for txn in ethereum_address.transactions: ...: print(txn.value) """ def __init__(self, address): """ Initializes an ethereum address object. """ if not isinstance(address, str): raise error.EtherscanInitializationError( "address must be a string." ) self.address = address self.client = client.Client() self._transactions = None self._balance = None self._block_list = None
[docs] def token_balance(self, contract_address): """ Obtains an address's ERC-20 compliant token balance given a token contract address. :param contract_address: The address of the token contract. :type contract_address: str :returns: The balance of the token as a float. """ token = self.client.get_token_balance_by_address( contract_address=contract_address, account_address=self.address ) return token.balance
def _retrieve_balance(self): balance_object = self.client.get_single_balance( self.address ) self._balance = balance_object.balance return self._balance def _retrieve_transactions_for_address(self): normal = self.client.get_transactions_by_address(self.address) internal = self.client.get_transactions_by_address( address=self.address, internal=True ) txns = normal.transactions + internal.transactions self._transactions = txns or [] return self._transactions @property def _raw_transactions(self): return self._transactions or self._retrieve_transactions_for_address() @property def balance(self): """ The balance in ether for this address. """ return self._balance or self._retrieve_balance() @property def transactions(self): """ returns a :py:obj:`ethereum.TransactionContainer` object. This object can be treated as a sequence object containing transactions this address was involved in. """ return TransactionContainer(self._raw_transactions) def _retrieve_block_list(self): response = self.client.get_blocks_mined_by_address(self.address) self._block_list = response.blocks or [] return self._block_list @property def blocks_mined(self): blocks = self._block_list or self._retrieve_block_list() return BlockContainer(blocks) def __repr__(self): return 'Address(address={address})'.format( address=self.address )
[docs]class BlockContainer(object): """ Represents a sequence of blocks. """ def __init__(self, block_list): self.block_list = block_list def __iter__(self): for block in self.block_list: block_number = int(block.get('blockNumber')) yield Block(block_number) def __getitem__(self, index): block_to_return = self.block_list[index] block_number = int(block_to_return.get('blockNumber')) return Block(block_number) def __repr__(self): return 'BlockContainer(block_list=<{n} blocks>)'.format( n=len(self.block_list) )
[docs]class Block(object): """ Represents an ethereum block. This uses the :py:class:`Client` object to retrieve information about, and construct, the ``Block``. Public Attributes: - ``time_stamp`` - ``block_miner`` - ``block_reward`` - ``uncles`` - ``datetime_mined`` - ``uncle_inclusion_reward`` Example Usage: .. code-block:: python In [1]: address = '0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae' In [2]: ethereum_address = Address(address) In [3]: ethereum_address.balance Out[3]: 748997604382925139479303.0 In [4]: for txn in ethereum_address.transactions: ...: print(txn.value) """ def __init__(self, block_number): if not isinstance(block_number, (int, str)): raise error.EtherscanInitializationError( "block_number must be a string or integer." ) self.block_number = block_number self.client = client.Client() self._block_reward_data = None self._time_stamp = None self._block_miner = None self._datetime_mined = None self._block_reward = None self._uncles = None self._uncle_inclusion_reward = None def _retrieve_block_reward_data(self): data = self.client.get_block_and_uncle_rewards_by_block_number( self.block_number ) self._block_reward_data = data.rewards_data return self._block_reward_data @property def _raw_block_data(self): return self._block_reward_data or self._retrieve_block_reward_data() def _retrieve_time_stamp(self): self._time_stamp = int(self._raw_block_data.get('timeStamp')) return self._time_stamp @property def time_stamp(self): return self._time_stamp or self._retrieve_time_stamp() def _retrieve_block_miner(self): self._block_miner = str(self._raw_block_data.get('blockMiner')) return self._block_miner @property def block_miner(self): return self._block_miner or self._retrieve_block_miner() def _convert_time_stamp(self): self._datetime_mined = datetime.datetime.utcfromtimestamp( self.time_stamp ) return self._datetime_mined @property def datetime_mined(self): return self._datetime_mined or self._convert_time_stamp() def _retrieve_block_reward(self): self._block_reward = float(self._raw_block_data.get('blockReward')) return self._block_reward @property def block_reward(self): return self._block_reward or self._retrieve_block_reward() def _retrieve_uncles(self): uncles = self._raw_block_data.get('uncles') parsed_uncles = [ { 'miner': str(u['miner']), 'block_reward': float(u['blockreward']) } for u in uncles ] self._uncles = parsed_uncles return self._uncles @property def uncles(self): return self._uncles or self._retrieve_uncles() def _retrieve_uncle_inclusion_reward(self): self._uncle_inclusion_reward = float( self._raw_block_data.get('uncleInclusionReward') ) return self._uncle_inclusion_reward @property def uncle_inclusion_reward(self): return self._uncle_inclusion_reward or \ self._retrieve_uncle_inclusion_reward() def __repr__(self): return 'Block(block_number={block_number})'.format( block_number=self.block_number )
[docs]class Token(object): """ Represents an ERC-20 compliant token. :param contract_address: The address of the Token contract :type contract_address: str """ def __init__(self, contract_address): if not isinstance(contract_address, str): raise error.EtherscanInitializationError( "contract_address must be a string." ) self.contract_address = contract_address self.client = client.Client() self._supply = None def _retrieve_supply(self): token = self.client.get_token_supply_by_address(self.contract_address) self._supply = token.total_supply return self._supply @property def supply(self): return self._supply or self._retrieve_supply()
[docs] def token_balance(self, address): """ The user balance of this token for a given address. :param address: An ethereum address. :type address: str :returns: The balance as a float. """ token = self.client.get_token_balance_by_address( contract_address=self.contract_address, account_address=address ) return token.balance
def __repr__(self): return 'Token(contract_address={contract_address})'.format( contract_address=self.contract_address )