Repository: rotki/rotki

Issue:
Check DSR and vaults for 0x714696C5a872611F76655Bc163D0131cBAc60a70
## Problem Definition

Checking DSR for `0x714696C5a872611F76655Bc163D0131cBAc60a70` seems to give an error when querying logs.

`Did not find corresponding vat.move event for pot join. Skipping ...`

Also vaults work fine, but events are duplicated between vaults. The log query is probably too encompassing.

@isidorosp: Can you post the pictures you posted in discord and also the vault links in oasis along with the duplicated events? 

## Task

Figure out why and see if it can be fixed.


Discussion:
Vault links:
https://oasis.app/borrow/8632
https://oasis.app/borrow/8543

Backend-error for DSR:
https://media.discordapp.net/attachments/683245846070165504/714110019372580905/unknown.png

Image with duplicate/mixed events + timing order discrepancy in vault 8632
https://media.discordapp.net/attachments/683245846070165504/714138190864449606/unknown.png?width=1293&height=368

(note, there are also mixed/duplicate events in vault 8543 as well)
![image](https://user-images.githubusercontent.com/27592/82758506-417def80-9de7-11ea-9a0d-fa14e9d5e29b.png)


Resolving patch:
diff --git a/rotkehlchen/chain/ethereum/makerdao.py b/rotkehlchen/chain/ethereum/makerdao.py
--- a/rotkehlchen/chain/ethereum/makerdao.py
+++ b/rotkehlchen/chain/ethereum/makerdao.py
@@ -142,6 +142,11 @@ def serialize(self) -> Dict[str, Any]:
         del result['urn']
         return result
 
+    @property
+    def ilk(self) -> bytes:
+        """Returns the collateral type string encoded into bytes32, known as ilk in makerdao"""
+        return self.collateral_type.encode('utf-8').ljust(32, b'\x00')
+
 
 class MakerDAOVaultDetails(NamedTuple):
     identifier: int
@@ -220,8 +225,10 @@ def _normalize_amount(asset_symbol: str, amount: int) -> FVal:
 class DSRMovement:
     movement_type: Literal['deposit', 'withdrawal']
     address: ChecksumEthAddress
+    # normalized balance in DSR DAI (RAD precision 10**45)
     normalized_balance: int
     gain_so_far: int = field(init=False)
+    # dai balance in DSR DAI (RAD precision 10**45)
     amount: int
     block_number: int
     timestamp: Timestamp
@@ -256,7 +263,7 @@ def serialize(self) -> Dict[str, Any]:
 
 def _dsrdai_to_dai(value: Union[int, FVal]) -> FVal:
     """Turns a big integer that is the value of DAI in DSR into a proper DAI decimal FVal"""
-    return FVal(value / FVal(1e27) / FVal(1e18))
+    return FVal(value / FVal(RAD))
 
 
 class MakerDAO(EthereumModule):
@@ -453,6 +460,24 @@ def _query_vault_details(
             )
         creation_ts = self.ethereum.get_event_timestamp(events[0])
 
+        # get vat frob events for cross-checking
+        argument_filters = {
+            'sig': '0x76088703',  # frob
+            'arg1': '0x' + vault.ilk.hex(),  # ilk
+            'arg2': address_to_bytes32(urn),  # urn
+            # arg3 can be urn for the 1st deposit, and proxy/owner for the next ones
+            # so don't filter for it
+            # 'arg3': address_to_bytes32(proxy),  # proxy - owner
+        }
+        frob_events = self.ethereum.get_logs(
+            contract_address=MAKERDAO_VAT.address,
+            abi=MAKERDAO_VAT.abi,
+            event_name='LogNote',
+            argument_filters=argument_filters,
+            from_block=MAKERDAO_VAT.deployed_block,
+        )
+        frob_event_tx_hashes = [x['transactionHash'] for x in frob_events]
+
         gemjoin = GEMJOIN_MAPPING[vault.collateral_asset.identifier]
         vault_events = []
         # Get the collateral deposit events
@@ -492,6 +517,11 @@ def _query_vault_details(
             if tx_hash in deposit_tx_hashes:
                 # Skip duplicate deposit that would be detected in non migrated CDP case
                 continue
+
+            if tx_hash not in frob_event_tx_hashes:
+                # If there is no corresponding frob event then skip
+                continue
+
             deposit_tx_hashes.add(tx_hash)
             amount = _normalize_amount(
                 asset_symbol=asset_symbol,
@@ -517,6 +547,10 @@ def _query_vault_details(
             from_block=gemjoin.deployed_block,
         )
         for event in events:
+            tx_hash = event['transactionHash']
+            if tx_hash not in frob_event_tx_hashes:
+                # If there is no corresponding frob event then skip
+                continue
             amount = _normalize_amount(
                 asset_symbol=asset_symbol,
                 amount=hex_or_bytes_to_int(event['topics'][3]),
@@ -782,19 +816,23 @@ def get_current_dsr(self) -> DSRCurrentBalances:
 
             # Calculation is from here:
             # https://docs.makerdao.com/smart-contract-modules/rates-module#a-note-on-setting-rates
-            current_dsr_percentage = ((FVal(current_dsr / 1e27) ** 31622400) % 1) * 100
+            current_dsr_percentage = ((FVal(current_dsr / RAY) ** 31622400) % 1) * 100
             result = DSRCurrentBalances(balances=balances, current_dsr=current_dsr_percentage)
 
         return result
 
-    def _get_vat_move_event_value(
+    def _get_vat_join_exit_at_transaction(
             self,
-            from_address: ChecksumEthAddress,
-            to_address: ChecksumEthAddress,
+            movement_type: Literal['join', 'exit'],
+            proxy_address: ChecksumEthAddress,
             block_number: int,
             transaction_index: int,
     ) -> Optional[int]:
-        """Returns values in DAI that were moved from to address at a block number and tx index
+        """Returns values in DSR DAI that were deposited/withdrawn at a block number and tx index
+
+        DSR DAI means they need they have a lot more digits than normal DAI and they
+        need to be divided by RAD (10**45) in order to get real DAI value. Keeping
+        it like that since most calculations deal with RAD precision in DSR.
 
         Returns None if no value was found of if there was an error with conversion.
 
@@ -804,16 +842,13 @@ def _get_vat_move_event_value(
         - BlockchainQueryError if an ethereum node is used and the contract call
         queries fail for some reason
         """
-        arg1 = address_to_bytes32(from_address)
-        arg2 = address_to_bytes32(to_address)
         argument_filters = {
-            'sig': '0xbb35783b',  # move
-            'arg1': arg1,  # src
-            'arg2': arg2,  # dst
+            'sig': '0x3b4da69f' if movement_type == 'join' else '0xef693bed',
+            'usr': proxy_address,
         }
         events = self.ethereum.get_logs(
-            contract_address=MAKERDAO_VAT.address,
-            abi=MAKERDAO_VAT.abi,
+            contract_address=MAKERDAO_DAI_JOIN.address,
+            abi=MAKERDAO_DAI_JOIN.abi,
             event_name='LogNote',
             argument_filters=argument_filters,
             from_block=block_number,
@@ -832,7 +867,7 @@ def _get_vat_move_event_value(
                     break
                 except ConversionError:
                     value = None
-        return value
+        return value * RAY  # turn it from DAI to RAD
 
     def _historical_dsr_for_account(
             self,
@@ -877,13 +912,13 @@ def _historical_dsr_for_account(
                 msg = f'Error at reading DSR join event block number. {str(e)}. Skipping event...'
                 self.msg_aggregator.add_error(msg)
                 continue
-            dai_value = self._get_vat_move_event_value(
-                from_address=proxy,
-                to_address=MAKERDAO_POT.address,
+            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 not dai_value:
+            if dai_value is None:
                 self.msg_aggregator.add_error(
                     'Did not find corresponding vat.move event for pot join. Skipping ...',
                 )
@@ -928,13 +963,13 @@ def _historical_dsr_for_account(
                 continue
 
             # and now get the withdrawal amount
-            dai_value = self._get_vat_move_event_value(
-                from_address=MAKERDAO_POT.address,
-                to_address=proxy,
+            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 not dai_value:
+            if dai_value is None:
                 self.msg_aggregator.add_error(
                     'Did not find corresponding vat.move event for pot exit. Skipping ...',
                 )
@@ -955,7 +990,15 @@ def _historical_dsr_for_account(
         amount_in_dsr = 0
         movements.sort(key=lambda x: x.block_number)
 
-        for m in movements:
+        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
+                else:
+                    m.gain_so_far = movements[idx - 1].gain_so_far
+                continue
+
             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)
@@ -1116,20 +1159,13 @@ def _try_get_chi_close_to(self, time: Timestamp) -> FVal:
         if isinstance(first_topic, bytes):
             first_topic = first_topic.hex()
 
-        if first_topic.startswith('0x049878f3'):  # join
-            from_address = hex_or_bytes_to_address(found_event['topics'][1])
-            to_address = MAKERDAO_POT.address
-        else:
-            from_address = MAKERDAO_POT.address
-            to_address = hex_or_bytes_to_address(found_event['topics'][1])
-
-        amount = self._get_vat_move_event_value(
-            from_address=from_address,
-            to_address=to_address,
+        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 not amount:
+        if amount is None:
             raise ChiRetrievalError(
                 f'Found no VAT.move events around timestamp {time}. Cant query chi.',
             )
@@ -1223,7 +1259,7 @@ def on_startup(self) -> None:
     def on_account_addition(self, address: ChecksumEthAddress) -> None:
         # Get the proxy of the account
         proxy_result = self._get_account_proxy(address)
-        if not proxy_result:
+        if proxy_result is None:
             return
 
         # add it to the mapping


Test patch:
diff --git a/rotkehlchen/tests/api/test_makerdao_dsr.py b/rotkehlchen/tests/api/test_makerdao_dsr.py
--- a/rotkehlchen/tests/api/test_makerdao_dsr.py
+++ b/rotkehlchen/tests/api/test_makerdao_dsr.py
@@ -7,7 +7,13 @@
 import requests
 
 from rotkehlchen.chain.ethereum.makerdao import _dsrdai_to_dai
-from rotkehlchen.constants.ethereum import MAKERDAO_POT, MAKERDAO_PROXY_REGISTRY, MAKERDAO_VAT
+from rotkehlchen.constants.ethereum import (
+    MAKERDAO_DAI_JOIN,
+    MAKERDAO_POT,
+    MAKERDAO_PROXY_REGISTRY,
+    MAKERDAO_VAT,
+)
+from rotkehlchen.constants.misc import ZERO
 from rotkehlchen.externalapis.etherscan import Etherscan
 from rotkehlchen.fval import FVal
 from rotkehlchen.tests.utils.api import (
@@ -17,7 +23,9 @@
     assert_proper_response,
     wait_for_async_task,
 )
+from rotkehlchen.tests.utils.checks import assert_serialized_lists_equal
 from rotkehlchen.tests.utils.factories import make_ethereum_address
+from rotkehlchen.tests.utils.makerdao import mock_proxies
 from rotkehlchen.tests.utils.mock import MockResponse
 from rotkehlchen.typing import ChecksumEthAddress
 
@@ -77,20 +85,20 @@ def mock_etherscan_for_dsr(
     account1_join1_event = f"""{{"address": "{MAKERDAO_POT.address}", "topics": ["0x049878f300000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy1)}", "{int_to_32byteshexstr(params.account1_join1_normalized_balance)}", "0x0000000000000000000000000000000000000000000000000000000000000000"], "data": "0xwedontcare", "blockNumber": "{hex(params.account1_join1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_join1_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontacre", "transactionIndex": "0x79"}}"""  # noqa: E501
 
     account1_join1_deposit = params.account1_join1_normalized_balance * params.account1_join1_chi
-    account1_join1_move_event = f"""{{"address": "{MAKERDAO_VAT.address}", "topics": ["0xbb35783b00000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy1)}", "{MAKERDAO_POT.address}", "{int_to_32byteshexstr(account1_join1_deposit)}"], "data": "0xwedontcare", "blockNumber": "{hex(params.account1_join1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_join1_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontcare", "transactionIndex": "0x79"}}"""  # noqa: E501
+    account1_join1_move_event = f"""{{"address": "{MAKERDAO_VAT.address}", "topics": ["0xbb35783b00000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy1)}", "{MAKERDAO_POT.address}", "{int_to_32byteshexstr(account1_join1_deposit // 10 ** 27)}"], "data": "0xwedontcare", "blockNumber": "{hex(params.account1_join1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_join1_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontcare", "transactionIndex": "0x79"}}"""  # noqa: E501
     account1_join2_event = f"""{{"address": "{MAKERDAO_POT.address}", "topics": ["0x049878f300000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy1)}", "{int_to_32byteshexstr(params.account1_join2_normalized_balance)}", "0x0000000000000000000000000000000000000000000000000000000000000000"], "data": "0xwedontcare", "blockNumber": "{hex(params.account1_join2_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_join2_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontacre", "transactionIndex": "0x79"}}"""  # noqa: E501
     account1_join2_deposit = params.account1_join2_normalized_balance * params.account1_join2_chi
-    account1_join2_move_event = f"""{{"address": "{MAKERDAO_VAT.address}", "topics": ["0xbb35783b00000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy1)}", "{MAKERDAO_POT.address}", "{int_to_32byteshexstr(account1_join2_deposit)}"], "data": "0xwedontcare", "blockNumber": "{hex(params.account1_join1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_join2_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontcare", "transactionIndex": "0x79"}}"""  # noqa: E501
+    account1_join2_move_event = f"""{{"address": "{MAKERDAO_VAT.address}", "topics": ["0xbb35783b00000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy1)}", "{MAKERDAO_POT.address}", "{int_to_32byteshexstr(account1_join2_deposit // 10 ** 27)}"], "data": "0xwedontcare", "blockNumber": "{hex(params.account1_join1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_join2_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontcare", "transactionIndex": "0x79"}}"""  # noqa: E501
 
     account1_exit1_event = f"""{{"address": "{MAKERDAO_POT.address}", "topics": ["0x7f8661a100000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy1)}", "{int_to_32byteshexstr(params.account1_exit1_normalized_balance)}", "0x0000000000000000000000000000000000000000000000000000000000000000"], "data": "0xwedontcare", "blockNumber": "{hex(params.account1_exit1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_exit1_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontacre", "transactionIndex": "0x79"}}"""  # noqa: E501
     account1_exit1_withdrawal = (
         params.account1_exit1_normalized_balance * params.account1_exit1_chi
     )
-    account1_exit1_move_event = f"""{{"address": "{MAKERDAO_VAT.address}", "topics": ["0xbb35783b00000000000000000000000000000000000000000000000000000000", "{MAKERDAO_POT.address}", "{address_to_32byteshexstr(proxy1)}", "{int_to_32byteshexstr(account1_exit1_withdrawal)}"], "data": "0xwedontcare", "blockNumber": "{hex(params.account1_exit1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_exit1_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontcare", "transactionIndex": "0x79"}}"""  # noqa: E501
+    account1_exit1_move_event = f"""{{"address": "{MAKERDAO_VAT.address}", "topics": ["0xbb35783b00000000000000000000000000000000000000000000000000000000", "{MAKERDAO_POT.address}", "{address_to_32byteshexstr(proxy1)}", "{int_to_32byteshexstr(account1_exit1_withdrawal // 10 ** 27)}"], "data": "0xwedontcare", "blockNumber": "{hex(params.account1_exit1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_exit1_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontcare", "transactionIndex": "0x79"}}"""  # noqa: E501
     account2_join1_event = f"""{{"address": "{MAKERDAO_POT.address}", "topics": ["0x049878f300000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy2)}", "{int_to_32byteshexstr(params.account2_join1_normalized_balance)}", "0x0000000000000000000000000000000000000000000000000000000000000000"], "data": "0xwedontcare", "blockNumber": "{hex(params.account2_join1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account2_join1_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontacre", "transactionIndex": "0x79"}}"""  # noqa: E501
 
     account2_join1_deposit = params.account2_join1_normalized_balance * params.account2_join1_chi
-    account2_join1_move_event = f"""{{"address": "{MAKERDAO_VAT.address}", "topics": ["0xbb35783b00000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy2)}", "{MAKERDAO_POT.address}", "{int_to_32byteshexstr(account2_join1_deposit)}"], "data": "0xwedontcare", "blockNumber": "{hex(params.account2_join1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account2_join1_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontcare", "transactionIndex": "0x79"}}"""  # noqa: E501
+    account2_join1_move_event = f"""{{"address": "{MAKERDAO_VAT.address}", "topics": ["0xbb35783b00000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy2)}", "{MAKERDAO_POT.address}", "{int_to_32byteshexstr(account2_join1_deposit // 10 ** 27)}"], "data": "0xwedontcare", "blockNumber": "{hex(params.account2_join1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account2_join1_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontcare", "transactionIndex": "0x79"}}"""  # noqa: E501
 
     def mock_requests_get(url, *args, **kwargs):
         if 'etherscan.io/api?module=proxy&action=eth_blockNumber' in url:
@@ -197,6 +205,24 @@ def mock_requests_get(url, *args, **kwargs):
                     response = f'{{"status":"1","message":"OK","result":[{",".join(events)}]}}'
                 else:
                     raise AssertionError('Etherscan unknown log query to makerdao VAT contract')
+            elif contract_address == MAKERDAO_DAI_JOIN.address:
+                events = []
+                if topic0.startswith('0x3b4da69f'):  # join
+                    if proxy1[2:].lower() in topic1:  # deposit from acc1
+                        if from_block <= params.account1_join1_blocknumber <= to_block:
+                            events.append(account1_join1_move_event)
+                        if from_block <= params.account1_join2_blocknumber <= to_block:
+                            events.append(account1_join2_move_event)
+                    elif proxy2[2:].lower() in topic1:  # deposit from acc2
+                        if from_block <= params.account2_join1_blocknumber <= to_block:
+                            events.append(account2_join1_move_event)
+                elif topic0.startswith('0xef693bed'):  # exit
+                    if from_block <= params.account1_exit1_blocknumber <= to_block:
+                        events.append(account1_exit1_move_event)
+                else:
+                    raise AssertionError('Etherscan unknown call to makerdao DAIJOIN contract')
+                response = f'{{"status":"1","message":"OK","result":[{",".join(events)}]}}'
+
             else:
                 raise AssertionError(
                     f'Etherscan getLogs call to unknown contract {contract_address} during tests',
@@ -424,7 +450,7 @@ def assert_dsr_history_result_is_correct(result: Dict[str, Any], setup: DSRTestS
                             ), max_diff='1e-8')
 
             else:
-                assert FVal(result[account][key]) == FVal(val)
+                assert FVal(result[account][key]).is_close(FVal(val))
 
 
 @pytest.mark.parametrize('number_of_eth_accounts', [3])
@@ -434,6 +460,14 @@ def test_query_historical_dsr(
         rotkehlchen_api_server,
         ethereum_accounts,
 ):
+    """Test DSR history is correctly queried
+
+    This (and the async version) is a very hard to maintain test due to mocking
+    everything.
+
+    TODO: Perhaps change it to querying etherscan/chain until a given block for a
+    given DSR account and check that until then all data match.
+    """
     rotki = rotkehlchen_api_server.rest_api.rotkehlchen
     account1 = ethereum_accounts[0]
     account2 = ethereum_accounts[2]
@@ -483,3 +517,85 @@ def test_query_historical_dsr_async(
     assert outcome['message'] == ''
     result = outcome['result']
     assert_dsr_history_result_is_correct(result, setup)
+
+
+@pytest.mark.parametrize('number_of_eth_accounts', [1])
+@pytest.mark.parametrize('ethereum_modules', [['makerdao']])
+@pytest.mark.parametrize('start_with_valid_premium', [True])
+def test_query_historical_dsr_with_a_zero_withdrawal(
+        rotkehlchen_api_server,
+        ethereum_accounts,
+):
+    """Test DSR for an account that was opened while DSR is 0 and made a 0 DAI withdrawal
+
+    Essentially reproduce DSR problem reported here: https://github.com/rotki/rotki/issues/1032
+
+    The account in question operates in a zero DSR environment but the reported
+    problem seems to be just because he tried a zero DAI withdrawal
+    """
+    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
+    original_get_logs = rotki.chain_manager.ethereum.get_logs
+    proxies_mapping = {
+        # proxy for 0x714696C5a872611F76655Bc163D0131cBAc60a70
+        ethereum_accounts[0]: '0xAe9996b76bdAa003ace6D66328A6942565f5768d',
+    }
+    mock_proxies(rotki, proxies_mapping)
+
+    # Query only until a block we know DSR is 0 and we know the number
+    # of DSR events
+    def mock_get_logs(
+            contract_address,
+            abi,
+            event_name,
+            argument_filters,
+            from_block,
+            to_block='latest',  # pylint: disable=unused-argument
+    ):
+        return original_get_logs(
+            contract_address,
+            abi,
+            event_name,
+            argument_filters,
+            from_block,
+            to_block=10149816,  # A block at which DSR is still zero
+        )
+
+    patched_get_logs = patch.object(
+        rotki.chain_manager.ethereum,
+        'get_logs',
+        side_effect=mock_get_logs,
+    )
+
+    with patched_get_logs:
+        response = requests.get(api_url_for(
+            rotkehlchen_api_server,
+            "makerdaodsrhistoryresource",
+        ))
+    assert_proper_response(response)
+    json_data = response.json()
+    assert json_data['message'] == ''
+    result = json_data['result'][ethereum_accounts[0]]
+    assert FVal(result['gain_so_far']) == ZERO
+    movements = result['movements']
+    expected_movements = [{
+        'movement_type': 'deposit',
+        'gain_so_far': ZERO,
+        'amount': FVal('79'),
+        'block_number': 9953028,
+        'timestamp': 1587970286,
+    }, {
+        'movement_type': 'withdrawal',
+        'gain_so_far': ZERO,
+        'amount': FVal('79'),
+        'block_number': 9968906,
+        'timestamp': 1588182567,
+    }, {
+        'movement_type': 'withdrawal',
+        'gain_so_far': ZERO,
+        'amount': ZERO,
+        'block_number': 9968906,
+        'timestamp': 1588182567,
+    }]
+    assert_serialized_lists_equal(movements, expected_movements)
+    errors = rotki.msg_aggregator.consume_errors()
+    assert len(errors) == 0
diff --git a/rotkehlchen/tests/api/test_makerdao_vaults.py b/rotkehlchen/tests/api/test_makerdao_vaults.py
--- a/rotkehlchen/tests/api/test_makerdao_vaults.py
+++ b/rotkehlchen/tests/api/test_makerdao_vaults.py
@@ -1,5 +1,4 @@
 from http import HTTPStatus
-from typing import Dict
 
 import pytest
 import requests
@@ -19,14 +18,7 @@
     assert_serialized_lists_equal,
 )
 from rotkehlchen.tests.utils.constants import A_USDC, A_WBTC
-from rotkehlchen.typing import ChecksumEthAddress
-
-
-def mock_proxies(rotki, mapping) -> None:
-    def mock_get_proxies() -> Dict[ChecksumEthAddress, ChecksumEthAddress]:
-        return mapping
-    rotki.chain_manager.makerdao._get_accounts_having_maker_proxy = mock_get_proxies
-
+from rotkehlchen.tests.utils.makerdao import mock_proxies
 
 VAULT_8015 = {
     'identifier': 8015,  # owner is missing and is filled in by the test function
@@ -460,6 +452,137 @@ def test_query_vaults_usdc(rotkehlchen_api_server, ethereum_accounts):
     assert_serialized_lists_equal(expected_details, details)
 
 
+@pytest.mark.parametrize('number_of_eth_accounts', [1])
+@pytest.mark.parametrize('ethereum_modules', [['makerdao']])
+@pytest.mark.parametrize('start_with_valid_premium', [True])
+def test_two_vaults_same_account_same_collateral(rotkehlchen_api_server, ethereum_accounts):
+    """Check that no events are duplicated between vaults for same collateral by same account
+
+    Test for vaults side of https://github.com/rotki/rotki/issues/1032
+    """
+    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
+    proxies_mapping = {
+        # proxy for 8632 and 8543
+        ethereum_accounts[0]: '0xAe9996b76bdAa003ace6D66328A6942565f5768d',
+    }
+    mock_proxies(rotki, proxies_mapping)
+
+    response = requests.get(api_url_for(
+        rotkehlchen_api_server,
+        "makerdaovaultsresource",
+    ))
+    vaults = assert_proper_response_with_result(response)
+    vault_8543 = {
+        'identifier': 8543,
+        'owner': ethereum_accounts[0],
+        'collateral_type': 'ETH-A',
+        'collateral_asset': 'ETH',
+        'collateral_amount': ZERO,
+        'collateral_usd_value': ZERO,
+        'debt_value': ZERO,
+        'collateralization_ratio': None,
+        'liquidation_ratio': '150.00%',
+        'liquidation_price': None,
+        'stability_fee': '0.00%',
+    }
+    vault_8632 = {
+        'identifier': 8632,
+        'owner': ethereum_accounts[0],
+        'collateral_type': 'ETH-A',
+        'collateral_asset': 'ETH',
+        'collateral_amount': '0.0',
+        'collateral_usd_value': '0.0',
+        'debt_value': '0.0',
+        'collateralization_ratio': None,
+        'liquidation_ratio': '150.00%',
+        'liquidation_price': None,
+        'stability_fee': '0.00%',
+    }
+    assert len(vaults) == 2
+    assert_serialized_dicts_equal(vaults[0], vault_8543)
+    assert_serialized_dicts_equal(vaults[1], vault_8632, ignore_keys=VAULT_IGNORE_KEYS)
+    response = requests.get(api_url_for(
+        rotkehlchen_api_server,
+        "makerdaovaultdetailsresource",
+    ))
+    vault_8543_details = {
+        'identifier': 8543,
+        'creation_ts': 1587910979,
+        'total_interest_owed': ZERO,
+        'total_liquidated_amount': ZERO,
+        'total_liquidated_usd': ZERO,
+        'events': [{
+            'event_type': 'deposit',
+            'amount': FVal('1'),
+            'timestamp': 1587910979,
+            'tx_hash': '0xf59858df4e42cdc2aecfebdcf38e1df841866c6a9eb3adb6bde9a844564a3bb6',
+        }, {
+            'event_type': 'generate',
+            'amount': FVal('80'),
+            'timestamp': 1587910979,
+            'tx_hash': '0xf59858df4e42cdc2aecfebdcf38e1df841866c6a9eb3adb6bde9a844564a3bb6',
+        }, {
+            'event_type': 'payback',
+            'amount': FVal('80'),
+            'timestamp': 1589989097,
+            'tx_hash': '0x52396f7d20db54e2e9e716698b643a39815ff149a6cccbe9c7597dc9e06bb9d3',
+        }, {
+            'event_type': 'deposit',
+            'amount': FVal('3.5'),
+            'timestamp': 1589993538,
+            'tx_hash': '0x3c3942dc40fe68303098d91e765ceecaed4664bba0ef8f8e684b6f0e61968c6c',
+        }, {
+            'event_type': 'withdraw',
+            'amount': FVal('4.5'),
+            'timestamp': 1590043499,
+            'tx_hash': '0xbcd4158f0089404f6ab5378517762cddc13d21c9d2fcf3fd45cf1cf4b656242c',
+        }],
+    }
+    vault_8632_details = {
+        'identifier': 8632,
+        'creation_ts': 1588174425,
+        'total_interest_owed': ZERO,
+        'total_liquidated_amount': ZERO,
+        'total_liquidated_usd': ZERO,
+        'events': [{
+            'event_type': 'deposit',
+            'amount': FVal('2.4'),
+            'timestamp': 1588174425,
+            'tx_hash': '0xdb677a4257b5bdb305c278102d7b2460408bb7a3981414b994f4dd80a737ac2a',
+        }, {
+            'event_type': 'generate',
+            'amount': FVal('192'),
+            'timestamp': 1588174425,
+            'tx_hash': '0xdb677a4257b5bdb305c278102d7b2460408bb7a3981414b994f4dd80a737ac2a',
+        }, {
+            'event_type': 'payback',
+            'amount': FVal('192'),
+            'timestamp': 1590042891,
+            'tx_hash': '0x488a937677030cc810d0062001c08c944ecf6329b24a45ae9480bada8147bf75',
+        }, {
+            'event_type': 'deposit',
+            'amount': FVal('4.4'),
+            'timestamp': 1590043699,
+            'tx_hash': '0x712ddb654b878bcb30c5344d7c18f7f796fe94abd6e5b8a22b2da0a6c99bb425',
+        }, {
+            'event_type': 'generate',
+            'amount': FVal('429.79'),
+            'timestamp': 1590044118,
+            'tx_hash': '0x36bfa27e157c03393a8816f6c1e3e990474f8f7473413810d87e2f4981d58044',
+        }],
+    }
+    details = assert_proper_response_with_result(response)
+    assert len(details) == 2
+    assert_serialized_dicts_equal(details[0], vault_8543_details)
+    assert_serialized_dicts_equal(
+        details[1],
+        vault_8632_details,
+        ignore_keys=['total_interest_owed', 'total_liquidated_amount', 'total_liquidated_usd'],
+        # Checking only the first 5 events, since that's how many we had when the test was written
+        length_list_keymap={'events': 5},
+    )
+
+
 @pytest.mark.skip('This vault is special and does not work. needs investigation')
 @pytest.mark.parametrize('number_of_eth_accounts', [1])
 @pytest.mark.parametrize('ethereum_modules', [['makerdao']])
diff --git a/rotkehlchen/tests/utils/makerdao.py b/rotkehlchen/tests/utils/makerdao.py
--- a/rotkehlchen/tests/utils/makerdao.py
+++ b/rotkehlchen/tests/utils/makerdao.py
@@ -144,3 +144,9 @@ def mock_contract(address, abi):  # pylint: disable=unused-argument
         'contract',
         side_effect=mock_contract,
     )
+
+
+def mock_proxies(rotki, mapping) -> None:
+    def mock_get_proxies() -> Dict[ChecksumEthAddress, ChecksumEthAddress]:
+        return mapping
+    rotki.chain_manager.makerdao._get_accounts_having_maker_proxy = mock_get_proxies