def get_code(self, account: ChecksumEthAddress) -> str: """Gets the deployment bytecode at the given address May raise: - RemoteError if Covalent is used and there is a problem querying it or parsing its response """ return hex_or_bytes_to_str(self.w3.eth.getCode(account))
def _get_code(self, web3: Optional[Web3], account: ChecksumEthAddress) -> str: """Gets the deployment bytecode at the given address May raise: - RemoteError if Etherscan is used and there is a problem querying it or parsing its response """ if web3 is None: return self.etherscan.get_code(account) return hex_or_bytes_to_str(web3.eth.getCode(account))
def get_block_by_number(self, num: int) -> Dict[str, Any]: """Returns the block object corresponding to the given block number May raise: - RemoteError if an external service such as Covalent is queried and there is a problem with its query. - BlockNotFound if number used to lookup the block can't be found. Raised by web3.eth.get_block(). """ block_data: MutableAttributeDict = MutableAttributeDict(self.w3.eth.get_block(num)) # type: ignore # pylint: disable=no-member # noqa: E501 block_data['hash'] = hex_or_bytes_to_str(block_data['hash']) return dict(block_data)
def _get_block_by_number(self, web3: Optional[Web3], num: int) -> Dict[str, Any]: """Returns the block object corresponding to the given block number May raise: - RemoteError if an external service such as Etherscan is queried and there is a problem with its query. """ if web3 is None: return self.etherscan.get_block_by_number(num) block_data: MutableAttributeDict = MutableAttributeDict(web3.eth.get_block(num)) # type: ignore # pylint: disable=no-member # noqa: E501 block_data['hash'] = hex_or_bytes_to_str(block_data['hash']) return dict(block_data)
def _try_get_chi_close_to(self, time: Timestamp) -> FVal: """Best effort attempt to get a chi value close to the given timestamp It can't be 100% accurate since we use the logs of join() or exit() in order to find the closest time chi was changed. It also may not work if for some reason there is no logs in the block range we are looking for. Better solution would have been an archive node's query. May raise: - RemoteError if there are problems with querying etherscan - ChiRetrievalError if we are unable to query chi at the given timestamp - BlockchainQueryError if an ethereum node is used and the contract call queries fail for some reason """ if time > 1584386100: # If the time is after 16/03/2020 19:15 GMT we know that # makerdao DSR was set to 0% we know chi has not changed # https://twitter.com/MakerDAO/status/1239270910810411008 return FVal('1018008449363110619399951035') block_number = self.ethereum.etherscan.get_blocknumber_by_time(time) if self.ethereum.web3 is not None: latest_block = self.ethereum.web3.eth.blockNumber else: latest_block = self.ethereum.query_eth_highest_block() blocks_queried = 0 counter = 1 # Keep trying to find events that could reveal the chi to us. Go back # as far as MAX_BLOCKS_TO_QUERY and only then give up while blocks_queried < MAX_BLOCKS_TO_QUERY: back_from_block = max( MAKERDAO_POT.deployed_block, block_number - counter * CHI_BLOCKS_SEARCH_DISTANCE, ) back_to_block = block_number - (counter - 1) * CHI_BLOCKS_SEARCH_DISTANCE forward_from_block = min( latest_block, block_number + (counter - 1) * CHI_BLOCKS_SEARCH_DISTANCE, ) forward_to_block = min( latest_block, block_number + CHI_BLOCKS_SEARCH_DISTANCE, ) back_joins, back_exits = self._get_join_exit_events(back_from_block, back_to_block) forward_joins, forward_exits = self._get_join_exit_events( from_block=forward_from_block, to_block=forward_to_block, ) no_results = all( len(x) == 0 for x in (back_joins, back_exits, forward_joins, forward_exits) ) if latest_block == forward_to_block and no_results: # if our forward querying got us to the latest block and there is # still no other results, then take current chi return self.ethereum.call_contract( contract_address=MAKERDAO_POT.address, abi=MAKERDAO_POT.abi, method_name='chi', ) if not no_results: # got results! break blocks_queried += 2 * CHI_BLOCKS_SEARCH_DISTANCE counter += 1 if no_results: raise ChiRetrievalError( f'Found no DSR events around timestamp {time}. Cant query chi.', ) # Find the closest event to the to_block number, looking both at events # in the blocks before and in the blocks after block_number found_event = None back_event = _find_closest_event(back_joins, back_exits, -1, operator.gt) forward_event = _find_closest_event(forward_joins, forward_exits, 0, operator.lt) if back_event and not forward_event: found_event = back_event elif forward_event and not back_event: found_event = forward_event else: # We have both backward and forward events, get the one closer to block number try: back_block_number = deserialize_blocknumber(back_event['blockNumber']) # type: ignore # noqa: E501 forward_block_number = deserialize_blocknumber(forward_event['blockNumber']) # type: ignore # noqa: E501 except DeserializationError as e: msg = f'Error at reading DSR drip event block number. {str(e)}' raise ChiRetrievalError(msg) if block_number - back_block_number <= forward_block_number - block_number: found_event = back_event else: found_event = forward_event assert found_event, 'at this point found_event should be populated' # helps mypy event_block_number = deserialize_blocknumber(found_event['blockNumber']) first_topic = hex_or_bytes_to_str(found_event['topics'][0]) amount = self._get_vat_join_exit_at_transaction( movement_type='join' if first_topic.startswith('0x049878f3') else 'exit', proxy_address=hex_or_bytes_to_address(found_event['topics'][1]), block_number=event_block_number, transaction_index=found_event['transactionIndex'], ) if amount is None: raise ChiRetrievalError( f'Found no VAT.move events around timestamp {time}. Cant query chi.', ) wad_val = hex_or_bytes_to_int(found_event['topics'][2]) chi = FVal(amount) / FVal(wad_val) return chi