Repository: rotki/rotki

Issue:
foreign key error for NFTs
## Problem Definition

While adding multiple ethereum addresses (with the form to past multiple to be imported) from the frontend I saw a FOREIGN KEY error 

## Logs

```
[08/06/2022 11:15:44 CEST] DEBUG rotkehlchen.externalapis.opensea Greenlet-8: Querying opensea: https://api.opensea.io/api/v1/assets
[08/06/2022 11:15:44 CEST] ERROR rotkehlchen.greenlets Greenlet with id 139784676040016: Periodically update snapshot balances died with exception: FOREIGN KEY constraint failed.
Exception Name: <class 'pysqlcipher3.dbapi2.IntegrityError'>
Exception Info: FOREIGN KEY constraint failed
Traceback:
   File "src/gevent/greenlet.py", line 906, in gevent._gevent_cgreenlet.Greenlet.run
  File "/home/yabirgb/work/rotki/rotkehlchen/rotkehlchen.py", line 740, in query_balances
    nft_mapping = nfts.get_balances(
  File "/home/yabirgb/work/rotki/rotkehlchen/chain/ethereum/modules/nfts.py", line 187, in get_balances
    cursor.execute(

```


### System Description

Operating system: Linux
Rotki version: bugfixes branch



Discussion:
Alexey has reproduced this with a user's data. We should fix.
So after trying to reproduce the error what I saw was that the reason is this insert failing https://github.com/rotki/rotki/blob/7c560a371c03300b534c6c98bdde1611da975075/rotkehlchen/chain/ethereum/modules/nfts.py#L189 because the address is not yet in the database. The address is added at this point https://github.com/rotki/rotki/blob/7c560a371c03300b534c6c98bdde1611da975075/rotkehlchen/rotkehlchen.py#L539 so the way to reproduce the error is adding an account and before the address is added to the database call the NFT endpoint and do reach the insert endpoint before the address is in the database. I managed to do it once but is very complicated to reproduce
Isn't the easiest and elegant solution here to just use ` db.get_blockchain_accounts` here: https://github.com/rotki/rotki/blob/7c560a371c03300b534c6c98bdde1611da975075/rotkehlchen/chain/ethereum/modules/nfts.py#L131 to determine the subset of addresses to query that are also in the DB?

And only query the addresses that are already in the DB with a comment that this limitation is due to foreign key relations and the edge case described in this issue?

Resolving patch:
diff --git a/rotkehlchen/chain/ethereum/modules/nfts.py b/rotkehlchen/chain/ethereum/modules/nfts.py
--- a/rotkehlchen/chain/ethereum/modules/nfts.py
+++ b/rotkehlchen/chain/ethereum/modules/nfts.py
@@ -128,8 +128,12 @@ def get_balances(
         May raise:
         - RemoteError
         """
+        with self.db.conn.read_ctx() as cursor:
+            accounts = self.db.get_blockchain_accounts(cursor=cursor)
+        # Be sure that the only addresses queried already exist in the database. Fix for #4456
+        queried_addresses = list(set(accounts.eth) & set(addresses))
         result: DefaultDict[ChecksumEthAddress, List[Dict[str, Any]]] = defaultdict(list)
-        nft_results, _ = self._get_all_nft_data(addresses, ignore_cache=ignore_cache)
+        nft_results, _ = self._get_all_nft_data(queried_addresses, ignore_cache=ignore_cache)
         cached_db_result = self.get_nfts_with_price()
         cached_db_prices = {x['asset']: x for x in cached_db_result}
         db_data: List[Tuple[str, str, Optional[str], Optional[str], int, ChecksumEthAddress]] = []


Test patch:
diff --git a/rotkehlchen/tests/unit/test_nfts.py b/rotkehlchen/tests/unit/test_nfts.py
new file mode 100644
--- /dev/null
+++ b/rotkehlchen/tests/unit/test_nfts.py
@@ -0,0 +1,21 @@
+import pytest
+
+TEST_ACC1 = '0xc37b40ABdB939635068d3c5f13E7faF686F03B65'  # yabir.eth
+TEST_ACC2 = '0x2B888954421b424C5D3D9Ce9bB67c9bD47537d12'  # lefteris.eth
+
+
+@pytest.mark.parametrize('ethereum_accounts', [[TEST_ACC1]])
+@pytest.mark.parametrize('start_with_valid_premium', [True])
+@pytest.mark.parametrize('ethereum_modules', [['nfts']])
+def test_addresses_queried_for_nfts(blockchain):
+    """Tests that nfts are only queried for addresses stored in the database preventing the
+    IntegrityError described in https://github.com/rotki/rotki/issues/4456
+    """
+    nft_module = blockchain.get_module('nfts')
+    balances = nft_module.get_balances(
+        addresses=[TEST_ACC1, TEST_ACC2],
+        return_zero_values=False,
+        ignore_cache=True,
+    )
+    assert TEST_ACC1 in balances
+    assert TEST_ACC2 not in balances