def get_current_dsr(self) -> DSRCurrentBalances: """Gets the current DSR balance for all accounts that have DAI in DSR and the current DSR percentage May raise: - RemoteError if etherscan is used and there is a problem with reaching it or with the returned result. - BlockchainQueryError if an ethereum node is used and the contract call queries fail for some reason """ with self.lock: proxy_mappings = self._get_accounts_having_maker_proxy() balances = {} try: current_dai_price = Inquirer().find_usd_price( EthereumToken('DAI')) except RemoteError: current_dai_price = Price(FVal(1)) for account, proxy in proxy_mappings.items(): guy_slice = MAKERDAO_POT.call(self.ethereum, 'pie', arguments=[proxy]) if guy_slice == 0: # no current DSR balance for this proxy continue chi = MAKERDAO_POT.call(self.ethereum, 'chi') dai_balance = _dsrdai_to_dai(guy_slice * chi) balances[account] = Balance( amount=dai_balance, usd_value=current_dai_price * dai_balance, ) current_dsr = MAKERDAO_POT.call(self.ethereum, 'dsr') # Calculation is from here: # https://docs.makerdao.com/smart-contract-modules/rates-module#a-note-on-setting-rates current_dsr_percentage = ( (FVal(current_dsr / RAY)**31622400) % 1) * 100 result = DSRCurrentBalances(balances=balances, current_dsr=current_dsr_percentage) return result
def _historical_dsr_for_account( self, account: ChecksumEthAddress, proxy: ChecksumEthAddress, ) -> DSRAccountReport: """Creates a historical DSR report for a single account May raise: - RemoteError if etherscan is used and there is a problem with reaching it or with the returned result. - BlockchainQueryError if an ethereum node is used and the contract call queries fail for some reason """ movements = [] join_normalized_balances = [] exit_normalized_balances = [] argument_filters = { 'sig': '0x049878f3', # join 'usr': proxy, } join_events = self.ethereum.get_logs( contract_address=MAKERDAO_POT.address, abi=MAKERDAO_POT.abi, event_name='LogNote', argument_filters=argument_filters, from_block=MAKERDAO_POT.deployed_block, ) for join_event in join_events: try: wad_val = hexstr_to_int(join_event['topics'][2]) except DeserializationError as e: msg = f'Error at reading DSR join event topics. {str(e)}. Skipping event...' self.msg_aggregator.add_error(msg) continue join_normalized_balances.append(wad_val) # and now get the deposit amount block_number = join_event['blockNumber'] dai_value = self._get_vat_join_exit_at_transaction( movement_type='join', proxy_address=proxy, block_number=block_number, transaction_index=join_event['transactionIndex'], ) if dai_value is None: self.msg_aggregator.add_error( 'Did not find corresponding vat.move event for pot join. Skipping ...', ) continue timestamp = self.ethereum.get_event_timestamp(join_event) usd_price = query_usd_price_or_use_default( asset=A_DAI, time=timestamp, default_value=FVal(1), location='DSR deposit', ) movements.append( DSRMovement( movement_type='deposit', address=account, normalized_balance=wad_val, amount=dai_value, amount_usd_value=_dsrdai_to_dai(dai_value) * usd_price, block_number=join_event['blockNumber'], timestamp=timestamp, tx_hash=join_event['transactionHash'], ), ) argument_filters = { 'sig': '0x7f8661a1', # exit 'usr': proxy, } exit_events = self.ethereum.get_logs( contract_address=MAKERDAO_POT.address, abi=MAKERDAO_POT.abi, event_name='LogNote', argument_filters=argument_filters, from_block=MAKERDAO_POT.deployed_block, ) for exit_event in exit_events: try: wad_val = hexstr_to_int(exit_event['topics'][2]) except DeserializationError as e: msg = f'Error at reading DSR exit event topics. {str(e)}. Skipping event...' self.msg_aggregator.add_error(msg) continue exit_normalized_balances.append(wad_val) block_number = exit_event['blockNumber'] # and now get the withdrawal amount dai_value = self._get_vat_join_exit_at_transaction( movement_type='exit', proxy_address=proxy, block_number=block_number, transaction_index=exit_event['transactionIndex'], ) if dai_value is None: self.msg_aggregator.add_error( 'Did not find corresponding vat.move event for pot exit. Skipping ...', ) continue timestamp = self.ethereum.get_event_timestamp(exit_event) usd_price = query_usd_price_or_use_default( asset=A_DAI, time=timestamp, default_value=FVal(1), location='DSR withdrawal', ) movements.append( DSRMovement( movement_type='withdrawal', address=account, normalized_balance=wad_val, amount=dai_value, amount_usd_value=_dsrdai_to_dai(dai_value) * usd_price, block_number=exit_event['blockNumber'], timestamp=timestamp, tx_hash=exit_event['transactionHash'], ), ) normalized_balance = 0 amount_in_dsr = 0 movements.sort(key=lambda x: x.block_number) for idx, m in enumerate(movements): if m.normalized_balance == 0: # skip 0 amount/balance movements. Consider last gain as last gain so far. if idx == 0: m.gain_so_far = 0 m.gain_so_far_usd_value = ZERO else: m.gain_so_far = movements[idx - 1].gain_so_far m.gain_so_far_usd_value = movements[ idx - 1].gain_so_far_usd_value continue if normalized_balance == m.normalized_balance: m.gain_so_far = m.amount - amount_in_dsr else: current_chi = FVal(m.amount) / FVal(m.normalized_balance) gain_so_far = normalized_balance * current_chi - amount_in_dsr m.gain_so_far = gain_so_far.to_int(exact=False) usd_price = query_usd_price_or_use_default( asset=A_DAI, time=m.timestamp, default_value=FVal(1), location='DSR movement', ) m.gain_so_far_usd_value = _dsrdai_to_dai(m.gain_so_far) * usd_price if m.movement_type == 'deposit': normalized_balance += m.normalized_balance amount_in_dsr += m.amount else: # withdrawal amount_in_dsr -= m.amount normalized_balance -= m.normalized_balance chi = MAKERDAO_POT.call(self.ethereum, 'chi') normalized_balance = normalized_balance * chi gain = normalized_balance - amount_in_dsr try: current_dai_price = Inquirer().find_usd_price(A_DAI) except RemoteError: current_dai_price = Price(FVal(1)) # Calculate the total gain so far in USD unaccounted_gain = _dsrdai_to_dai(gain) last_usd_value = ZERO last_dai_gain = 0 if len(movements) != 0: last_usd_value = movements[-1].gain_so_far_usd_value last_dai_gain = movements[-1].gain_so_far unaccounted_gain = _dsrdai_to_dai(gain - last_dai_gain) gain_so_far_usd_value = unaccounted_gain * current_dai_price + last_usd_value return DSRAccountReport( movements=movements, gain_so_far=gain, gain_so_far_usd_value=gain_so_far_usd_value, )