예제 #1
0
    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
예제 #2
0
    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,
        )