We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add the COMBO token to the airdrops
## Abstract

Furucombo has launched a token and airdropped to their users and donors on January 15th. 
These tokens can be claimed periodically over a vesting period of 8 weeks.

## Motivation

The airdrop ranges with either 350 or 900 COMBO which at the time of writing it worth 560 and 1450 usd. It would be nice have a easy way to track these tokes so the user can claim them.
<!-- Why do you think this feature should be addressed. What is the value added to the users of Rotki and why would they want to have it implemented? -->

## Specification
Add the COMBO token to the airdrop section and update the balance of the COMBO tokens in each vesting cycle.
Each vesting cycle is at every friday for 8 weeks afaik.   
Idk how much will be distributed each cycle but those who had 350 where able to unlock 87.5 today.   

A list of eligible addresses can be found here:
https://docs.google.com/spreadsheets/d/113AiPrGJ-yp7g-Kdo_IofMWiftc7msEoBMmKNKxJ4fo/edit#gid=0
The announcement of the token airdrop can be found here: 
https://medium.com/furucombo/first-furucombo-grant-7b1e48175c99
<!-- If the feature is techical in nature please write as detailed as possible a specification of what needs to be built. -->


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `rotkehlchen/constants/assets.py`
Content:
```
1 from rotkehlchen.assets.asset import Asset, EthereumToken
2 
3 A_USD = Asset('USD')
4 A_EUR = Asset('EUR')
5 A_GBP = Asset('GBP')
6 A_JPY = Asset('JPY')
7 A_CNY = Asset('CNY')
8 A_CAD = Asset('CAD')
9 A_KRW = Asset('KRW')
10 A_RUB = Asset('RUB')
11 A_CHF = Asset('CHF')
12 A_TRY = Asset('TRY')
13 A_ZAR = Asset('ZAR')
14 A_AUD = Asset('AUD')
15 A_NZD = Asset('NZD')
16 A_BRL = Asset('BRL')
17 FIAT_CURRENCIES = (
18     A_USD,
19     A_EUR,
20     A_GBP,
21     A_JPY,
22     A_CNY,
23     A_CAD,
24     A_KRW,
25     A_RUB,
26     A_CHF,
27     A_TRY,
28     A_ZAR,
29     A_AUD,
30     A_NZD,
31     A_BRL,
32 )
33 
34 S_BTC = 'BTC'
35 S_ETH = 'ETH'
36 S_KSM = 'KSM'
37 
38 A_BTC = Asset(S_BTC)
39 A_BCH = Asset('BCH')
40 A_BAL = Asset('BAL')
41 A_BSV = Asset('BSV')
42 A_ETH = Asset(S_ETH)
43 A_ETH2 = Asset('ETH2')
44 A_ETC = Asset('ETC')
45 A_KSM = Asset(S_KSM)
46 A_BAT = EthereumToken('BAT')
47 A_UNI = EthereumToken('UNI')
48 A_1INCH = EthereumToken('1INCH')
49 A_DAI = EthereumToken('DAI')
50 A_SAI = EthereumToken('SAI')
51 A_YFI = EthereumToken('YFI')
52 A_USDT = EthereumToken('USDT')
53 A_USDC = EthereumToken('USDC')
54 A_TUSD = EthereumToken('TUSD')
55 A_ALINK = EthereumToken('aLINK')
56 A_GUSD = EthereumToken('GUSD')
57 A_CRV = EthereumToken('CRV')
58 A_KNC = EthereumToken('KNC')
59 A_WBTC = EthereumToken('WBTC')
60 A_WETH = EthereumToken('WETH')
61 A_ZRX = EthereumToken('ZRX')
62 A_MANA = EthereumToken('MANA')
63 A_PAX = EthereumToken('PAX')
64 A_COMP = EthereumToken('COMP')
65 A_LRC = EthereumToken('LRC')
66 A_LINK = EthereumToken('LINK')
67 A_ADX = EthereumToken('ADX')
68 A_TORN = EthereumToken('TORN')
69 A_CORN = EthereumToken('CORN-2')
70 A_GRAIN = EthereumToken('GRAIN')
71
```

Path: `rotkehlchen/chain/ethereum/airdrops.py`
Content:
```
1 from rotkehlchen.typing import ChecksumEthAddress
2 from typing import List, Dict, TextIO, Iterator, Tuple
3 from rotkehlchen.constants.assets import A_UNI, A_1INCH, A_TORN, A_CORN, A_GRAIN
4 import csv
5 import requests
6 from pathlib import Path
7 from collections import defaultdict
8 from rotkehlchen.errors import RemoteError
9 from rotkehlchen.chain.ethereum.utils import token_normalized_value_decimals
10 
11 AIRDROPS = {
12     'uniswap': (
13         # is checksummed
14         'https://gist.githubusercontent.com/LefterisJP/d883cb7187a7c4fcf98c7a62f45568e7/raw/3718c95d572a29b9c3906d7c64726d3bd7524bfd/uniswap.csv',  # noqa: E501
15         A_UNI,
16         'https://app.uniswap.org/',
17     ),
18     '1inch': (
19         # is checksummed
20         'https://gist.githubusercontent.com/LefterisJP/8f41d1511bf354d7e56810188116a410/raw/87d967e86e1435aa3a9ddb97ce20531e4e52dbad/1inch.csv',  # noqa: E501
21         A_1INCH,
22         'https://1inch.exchange/',
23     ),
24     'tornado': (
25         # is checksummed
26         'https://raw.githubusercontent.com/tornadocash/airdrop/master/airdrop.csv',
27         A_TORN,  # Don't have TORN token yet?
28         'https://tornado.cash/',
29     ),
30     'cornichon': (
31         # is checksummed
32         'https://gist.githubusercontent.com/LefterisJP/5199d8bc6caa3253c343cd5084489088/raw/7e9ca4c4772fc50780bfe9997e1c43525e1b7445/cornichon_airdrop.csv',  # noqa: E501
33         A_CORN,
34         'https://cornichon.ape.tax/',
35     ),
36     'grain': (
37         # is checksummed
38         'https://gist.githubusercontent.com/LefterisJP/08d7a5b28876741b300c944650c89280/raw/987ab4a92d5363fdbe262f639565732bd1fd3921/grain_iou.csv',  # noqa: E501
39         A_GRAIN,
40         'https://claim.harvest.finance/',
41     ),
42 }
43 
44 
45 def get_airdrop_data(name: str, data_dir: Path) -> Tuple[Iterator, TextIO]:
46     airdrops_dir = data_dir / 'airdrops'
47     airdrops_dir.mkdir(parents=True, exist_ok=True)
48     filename = airdrops_dir / f'{name}.csv'
49     if not filename.is_file():
50         # if not cached, get it from the gist
51         try:
52             request = requests.get(AIRDROPS[name][0])
53         except requests.exceptions.RequestException as e:
54             raise RemoteError(f'Airdrops Gist request failed due to {str(e)}') from e
55 
56         with open(filename, 'w') as f:
57             f.write(request.content.decode('utf-8'))
58 
59     csvfile = open(filename, 'r')
60     iterator = csv.reader(csvfile)
61     next(iterator)  # skip header
62     return iterator, csvfile
63 
64 
65 def check_airdrops(
66         addresses: List[ChecksumEthAddress],
67         data_dir: Path,
68 ) -> Dict[ChecksumEthAddress, Dict]:
69     """Checks airdrop data for the given list of ethereum addresses
70 
71     May raise:
72         - RemoteError if the remote request fails
73     """
74     found_data: Dict[ChecksumEthAddress, Dict] = defaultdict(lambda: defaultdict(dict))
75     for protocol_name, airdrop_data in AIRDROPS.items():
76         data, csvfile = get_airdrop_data(protocol_name, data_dir)
77         for addr, amount, *_ in data:
78             # not doing to_checksum_address() here since the file addresses are checksummed
79             # and doing to_checksum_address() so many times hits performance
80             if protocol_name in ('cornichon', 'tornado', 'grain'):
81                 amount = token_normalized_value_decimals(int(amount), 18)
82             if addr in addresses:
83                 found_data[addr][protocol_name] = {
84                     'amount': str(amount),
85                     'asset': airdrop_data[1],
86                     'link': airdrop_data[2],
87                 }
88         csvfile.close()
89 
90     return dict(found_data)
91
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/rotkehlchen/chain/ethereum/airdrops.py b/rotkehlchen/chain/ethereum/airdrops.py
--- a/rotkehlchen/chain/ethereum/airdrops.py
+++ b/rotkehlchen/chain/ethereum/airdrops.py
@@ -1,12 +1,14 @@
-from rotkehlchen.typing import ChecksumEthAddress
-from typing import List, Dict, TextIO, Iterator, Tuple
-from rotkehlchen.constants.assets import A_UNI, A_1INCH, A_TORN, A_CORN, A_GRAIN
 import csv
-import requests
-from pathlib import Path
 from collections import defaultdict
-from rotkehlchen.errors import RemoteError
+from pathlib import Path
+from typing import Dict, Iterator, List, TextIO, Tuple
+
+import requests
+
 from rotkehlchen.chain.ethereum.utils import token_normalized_value_decimals
+from rotkehlchen.constants.assets import A_1INCH, A_COMBO, A_CORN, A_GRAIN, A_LDO, A_TORN, A_UNI
+from rotkehlchen.errors import RemoteError
+from rotkehlchen.typing import ChecksumEthAddress
 
 AIRDROPS = {
     'uniswap': (
@@ -39,6 +41,18 @@
         A_GRAIN,
         'https://claim.harvest.finance/',
     ),
+    'furucombo': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/69612e155e8063fd6b3422d4efbf22a3/raw/b9023960ab1c478ee2620c456e208e5124115c19/furucombo_airdrop.csv',  # noqa: E501
+        A_COMBO,
+        'https://furucombo.app/',
+    ),
+    'lido': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/57a8d65280a482fed6f3e2cc00c0e540/raw/e6ebac56c438cc8a882585c5f5bfba64eb57c424/lido_airdrop.csv',  # noqa: E501
+        A_LDO,
+        'https://lido.fi/',
+    ),
 }
 
 
@@ -77,7 +91,7 @@
         for addr, amount, *_ in data:
             # not doing to_checksum_address() here since the file addresses are checksummed
             # and doing to_checksum_address() so many times hits performance
-            if protocol_name in ('cornichon', 'tornado', 'grain'):
+            if protocol_name in ('cornichon', 'tornado', 'grain', 'lido'):
                 amount = token_normalized_value_decimals(int(amount), 18)
             if addr in addresses:
                 found_data[addr][protocol_name] = {
diff --git a/rotkehlchen/constants/assets.py b/rotkehlchen/constants/assets.py
--- a/rotkehlchen/constants/assets.py
+++ b/rotkehlchen/constants/assets.py
@@ -68,3 +68,5 @@
 A_TORN = EthereumToken('TORN')
 A_CORN = EthereumToken('CORN-2')
 A_GRAIN = EthereumToken('GRAIN')
+A_COMBO = EthereumToken('COMBO')
+A_LDO = EthereumToken('LDO')

{"golden_diff": "diff --git a/rotkehlchen/chain/ethereum/airdrops.py b/rotkehlchen/chain/ethereum/airdrops.py\n--- a/rotkehlchen/chain/ethereum/airdrops.py\n+++ b/rotkehlchen/chain/ethereum/airdrops.py\n@@ -1,12 +1,14 @@\n-from rotkehlchen.typing import ChecksumEthAddress\n-from typing import List, Dict, TextIO, Iterator, Tuple\n-from rotkehlchen.constants.assets import A_UNI, A_1INCH, A_TORN, A_CORN, A_GRAIN\n import csv\n-import requests\n-from pathlib import Path\n from collections import defaultdict\n-from rotkehlchen.errors import RemoteError\n+from pathlib import Path\n+from typing import Dict, Iterator, List, TextIO, Tuple\n+\n+import requests\n+\n from rotkehlchen.chain.ethereum.utils import token_normalized_value_decimals\n+from rotkehlchen.constants.assets import A_1INCH, A_COMBO, A_CORN, A_GRAIN, A_LDO, A_TORN, A_UNI\n+from rotkehlchen.errors import RemoteError\n+from rotkehlchen.typing import ChecksumEthAddress\n \n AIRDROPS = {\n     'uniswap': (\n@@ -39,6 +41,18 @@\n         A_GRAIN,\n         'https://claim.harvest.finance/',\n     ),\n+    'furucombo': (\n+        # is checksummed\n+        'https://gist.githubusercontent.com/LefterisJP/69612e155e8063fd6b3422d4efbf22a3/raw/b9023960ab1c478ee2620c456e208e5124115c19/furucombo_airdrop.csv',  # noqa: E501\n+        A_COMBO,\n+        'https://furucombo.app/',\n+    ),\n+    'lido': (\n+        # is checksummed\n+        'https://gist.githubusercontent.com/LefterisJP/57a8d65280a482fed6f3e2cc00c0e540/raw/e6ebac56c438cc8a882585c5f5bfba64eb57c424/lido_airdrop.csv',  # noqa: E501\n+        A_LDO,\n+        'https://lido.fi/',\n+    ),\n }\n \n \n@@ -77,7 +91,7 @@\n         for addr, amount, *_ in data:\n             # not doing to_checksum_address() here since the file addresses are checksummed\n             # and doing to_checksum_address() so many times hits performance\n-            if protocol_name in ('cornichon', 'tornado', 'grain'):\n+            if protocol_name in ('cornichon', 'tornado', 'grain', 'lido'):\n                 amount = token_normalized_value_decimals(int(amount), 18)\n             if addr in addresses:\n                 found_data[addr][protocol_name] = {\ndiff --git a/rotkehlchen/constants/assets.py b/rotkehlchen/constants/assets.py\n--- a/rotkehlchen/constants/assets.py\n+++ b/rotkehlchen/constants/assets.py\n@@ -68,3 +68,5 @@\n A_TORN = EthereumToken('TORN')\n A_CORN = EthereumToken('CORN-2')\n A_GRAIN = EthereumToken('GRAIN')\n+A_COMBO = EthereumToken('COMBO')\n+A_LDO = EthereumToken('LDO')\n", "issue": "Add the COMBO token to the airdrops\n## Abstract\r\n\r\nFurucombo has launched a token and airdropped to their users and donors on January 15th. \r\nThese tokens can be claimed periodically over a vesting period of 8 weeks.\r\n\r\n## Motivation\r\n\r\nThe airdrop ranges with either 350 or 900 COMBO which at the time of writing it worth 560 and 1450 usd. It would be nice have a easy way to track these tokes so the user can claim them.\r\n<!-- Why do you think this feature should be addressed. What is the value added to the users of Rotki and why would they want to have it implemented? -->\r\n\r\n## Specification\r\nAdd the COMBO token to the airdrop section and update the balance of the COMBO tokens in each vesting cycle.\r\nEach vesting cycle is at every friday for 8 weeks afaik.   \r\nIdk how much will be distributed each cycle but those who had 350 where able to unlock 87.5 today.   \r\n\r\nA list of eligible addresses can be found here:\r\nhttps://docs.google.com/spreadsheets/d/113AiPrGJ-yp7g-Kdo_IofMWiftc7msEoBMmKNKxJ4fo/edit#gid=0\r\nThe announcement of the token airdrop can be found here: \r\nhttps://medium.com/furucombo/first-furucombo-grant-7b1e48175c99\r\n<!-- If the feature is techical in nature please write as detailed as possible a specification of what needs to be built. -->\r\n\n", "before_files": [{"content": "from rotkehlchen.assets.asset import Asset, EthereumToken\n\nA_USD = Asset('USD')\nA_EUR = Asset('EUR')\nA_GBP = Asset('GBP')\nA_JPY = Asset('JPY')\nA_CNY = Asset('CNY')\nA_CAD = Asset('CAD')\nA_KRW = Asset('KRW')\nA_RUB = Asset('RUB')\nA_CHF = Asset('CHF')\nA_TRY = Asset('TRY')\nA_ZAR = Asset('ZAR')\nA_AUD = Asset('AUD')\nA_NZD = Asset('NZD')\nA_BRL = Asset('BRL')\nFIAT_CURRENCIES = (\n    A_USD,\n    A_EUR,\n    A_GBP,\n    A_JPY,\n    A_CNY,\n    A_CAD,\n    A_KRW,\n    A_RUB,\n    A_CHF,\n    A_TRY,\n    A_ZAR,\n    A_AUD,\n    A_NZD,\n    A_BRL,\n)\n\nS_BTC = 'BTC'\nS_ETH = 'ETH'\nS_KSM = 'KSM'\n\nA_BTC = Asset(S_BTC)\nA_BCH = Asset('BCH')\nA_BAL = Asset('BAL')\nA_BSV = Asset('BSV')\nA_ETH = Asset(S_ETH)\nA_ETH2 = Asset('ETH2')\nA_ETC = Asset('ETC')\nA_KSM = Asset(S_KSM)\nA_BAT = EthereumToken('BAT')\nA_UNI = EthereumToken('UNI')\nA_1INCH = EthereumToken('1INCH')\nA_DAI = EthereumToken('DAI')\nA_SAI = EthereumToken('SAI')\nA_YFI = EthereumToken('YFI')\nA_USDT = EthereumToken('USDT')\nA_USDC = EthereumToken('USDC')\nA_TUSD = EthereumToken('TUSD')\nA_ALINK = EthereumToken('aLINK')\nA_GUSD = EthereumToken('GUSD')\nA_CRV = EthereumToken('CRV')\nA_KNC = EthereumToken('KNC')\nA_WBTC = EthereumToken('WBTC')\nA_WETH = EthereumToken('WETH')\nA_ZRX = EthereumToken('ZRX')\nA_MANA = EthereumToken('MANA')\nA_PAX = EthereumToken('PAX')\nA_COMP = EthereumToken('COMP')\nA_LRC = EthereumToken('LRC')\nA_LINK = EthereumToken('LINK')\nA_ADX = EthereumToken('ADX')\nA_TORN = EthereumToken('TORN')\nA_CORN = EthereumToken('CORN-2')\nA_GRAIN = EthereumToken('GRAIN')\n", "path": "rotkehlchen/constants/assets.py"}, {"content": "from rotkehlchen.typing import ChecksumEthAddress\nfrom typing import List, Dict, TextIO, Iterator, Tuple\nfrom rotkehlchen.constants.assets import A_UNI, A_1INCH, A_TORN, A_CORN, A_GRAIN\nimport csv\nimport requests\nfrom pathlib import Path\nfrom collections import defaultdict\nfrom rotkehlchen.errors import RemoteError\nfrom rotkehlchen.chain.ethereum.utils import token_normalized_value_decimals\n\nAIRDROPS = {\n    'uniswap': (\n        # is checksummed\n        'https://gist.githubusercontent.com/LefterisJP/d883cb7187a7c4fcf98c7a62f45568e7/raw/3718c95d572a29b9c3906d7c64726d3bd7524bfd/uniswap.csv',  # noqa: E501\n        A_UNI,\n        'https://app.uniswap.org/',\n    ),\n    '1inch': (\n        # is checksummed\n        'https://gist.githubusercontent.com/LefterisJP/8f41d1511bf354d7e56810188116a410/raw/87d967e86e1435aa3a9ddb97ce20531e4e52dbad/1inch.csv',  # noqa: E501\n        A_1INCH,\n        'https://1inch.exchange/',\n    ),\n    'tornado': (\n        # is checksummed\n        'https://raw.githubusercontent.com/tornadocash/airdrop/master/airdrop.csv',\n        A_TORN,  # Don't have TORN token yet?\n        'https://tornado.cash/',\n    ),\n    'cornichon': (\n        # is checksummed\n        'https://gist.githubusercontent.com/LefterisJP/5199d8bc6caa3253c343cd5084489088/raw/7e9ca4c4772fc50780bfe9997e1c43525e1b7445/cornichon_airdrop.csv',  # noqa: E501\n        A_CORN,\n        'https://cornichon.ape.tax/',\n    ),\n    'grain': (\n        # is checksummed\n        'https://gist.githubusercontent.com/LefterisJP/08d7a5b28876741b300c944650c89280/raw/987ab4a92d5363fdbe262f639565732bd1fd3921/grain_iou.csv',  # noqa: E501\n        A_GRAIN,\n        'https://claim.harvest.finance/',\n    ),\n}\n\n\ndef get_airdrop_data(name: str, data_dir: Path) -> Tuple[Iterator, TextIO]:\n    airdrops_dir = data_dir / 'airdrops'\n    airdrops_dir.mkdir(parents=True, exist_ok=True)\n    filename = airdrops_dir / f'{name}.csv'\n    if not filename.is_file():\n        # if not cached, get it from the gist\n        try:\n            request = requests.get(AIRDROPS[name][0])\n        except requests.exceptions.RequestException as e:\n            raise RemoteError(f'Airdrops Gist request failed due to {str(e)}') from e\n\n        with open(filename, 'w') as f:\n            f.write(request.content.decode('utf-8'))\n\n    csvfile = open(filename, 'r')\n    iterator = csv.reader(csvfile)\n    next(iterator)  # skip header\n    return iterator, csvfile\n\n\ndef check_airdrops(\n        addresses: List[ChecksumEthAddress],\n        data_dir: Path,\n) -> Dict[ChecksumEthAddress, Dict]:\n    \"\"\"Checks airdrop data for the given list of ethereum addresses\n\n    May raise:\n        - RemoteError if the remote request fails\n    \"\"\"\n    found_data: Dict[ChecksumEthAddress, Dict] = defaultdict(lambda: defaultdict(dict))\n    for protocol_name, airdrop_data in AIRDROPS.items():\n        data, csvfile = get_airdrop_data(protocol_name, data_dir)\n        for addr, amount, *_ in data:\n            # not doing to_checksum_address() here since the file addresses are checksummed\n            # and doing to_checksum_address() so many times hits performance\n            if protocol_name in ('cornichon', 'tornado', 'grain'):\n                amount = token_normalized_value_decimals(int(amount), 18)\n            if addr in addresses:\n                found_data[addr][protocol_name] = {\n                    'amount': str(amount),\n                    'asset': airdrop_data[1],\n                    'link': airdrop_data[2],\n                }\n        csvfile.close()\n\n    return dict(found_data)\n", "path": "rotkehlchen/chain/ethereum/airdrops.py"}], "after_files": [{"content": "from rotkehlchen.assets.asset import Asset, EthereumToken\n\nA_USD = Asset('USD')\nA_EUR = Asset('EUR')\nA_GBP = Asset('GBP')\nA_JPY = Asset('JPY')\nA_CNY = Asset('CNY')\nA_CAD = Asset('CAD')\nA_KRW = Asset('KRW')\nA_RUB = Asset('RUB')\nA_CHF = Asset('CHF')\nA_TRY = Asset('TRY')\nA_ZAR = Asset('ZAR')\nA_AUD = Asset('AUD')\nA_NZD = Asset('NZD')\nA_BRL = Asset('BRL')\nFIAT_CURRENCIES = (\n    A_USD,\n    A_EUR,\n    A_GBP,\n    A_JPY,\n    A_CNY,\n    A_CAD,\n    A_KRW,\n    A_RUB,\n    A_CHF,\n    A_TRY,\n    A_ZAR,\n    A_AUD,\n    A_NZD,\n    A_BRL,\n)\n\nS_BTC = 'BTC'\nS_ETH = 'ETH'\nS_KSM = 'KSM'\n\nA_BTC = Asset(S_BTC)\nA_BCH = Asset('BCH')\nA_BAL = Asset('BAL')\nA_BSV = Asset('BSV')\nA_ETH = Asset(S_ETH)\nA_ETH2 = Asset('ETH2')\nA_ETC = Asset('ETC')\nA_KSM = Asset(S_KSM)\nA_BAT = EthereumToken('BAT')\nA_UNI = EthereumToken('UNI')\nA_1INCH = EthereumToken('1INCH')\nA_DAI = EthereumToken('DAI')\nA_SAI = EthereumToken('SAI')\nA_YFI = EthereumToken('YFI')\nA_USDT = EthereumToken('USDT')\nA_USDC = EthereumToken('USDC')\nA_TUSD = EthereumToken('TUSD')\nA_ALINK = EthereumToken('aLINK')\nA_GUSD = EthereumToken('GUSD')\nA_CRV = EthereumToken('CRV')\nA_KNC = EthereumToken('KNC')\nA_WBTC = EthereumToken('WBTC')\nA_WETH = EthereumToken('WETH')\nA_ZRX = EthereumToken('ZRX')\nA_MANA = EthereumToken('MANA')\nA_PAX = EthereumToken('PAX')\nA_COMP = EthereumToken('COMP')\nA_LRC = EthereumToken('LRC')\nA_LINK = EthereumToken('LINK')\nA_ADX = EthereumToken('ADX')\nA_TORN = EthereumToken('TORN')\nA_CORN = EthereumToken('CORN-2')\nA_GRAIN = EthereumToken('GRAIN')\nA_COMBO = EthereumToken('COMBO')\nA_LDO = EthereumToken('LDO')\n", "path": "rotkehlchen/constants/assets.py"}, {"content": "import csv\nfrom collections import defaultdict\nfrom pathlib import Path\nfrom typing import Dict, Iterator, List, TextIO, Tuple\n\nimport requests\n\nfrom rotkehlchen.chain.ethereum.utils import token_normalized_value_decimals\nfrom rotkehlchen.constants.assets import A_1INCH, A_COMBO, A_CORN, A_GRAIN, A_LDO, A_TORN, A_UNI\nfrom rotkehlchen.errors import RemoteError\nfrom rotkehlchen.typing import ChecksumEthAddress\n\nAIRDROPS = {\n    'uniswap': (\n        # is checksummed\n        'https://gist.githubusercontent.com/LefterisJP/d883cb7187a7c4fcf98c7a62f45568e7/raw/3718c95d572a29b9c3906d7c64726d3bd7524bfd/uniswap.csv',  # noqa: E501\n        A_UNI,\n        'https://app.uniswap.org/',\n    ),\n    '1inch': (\n        # is checksummed\n        'https://gist.githubusercontent.com/LefterisJP/8f41d1511bf354d7e56810188116a410/raw/87d967e86e1435aa3a9ddb97ce20531e4e52dbad/1inch.csv',  # noqa: E501\n        A_1INCH,\n        'https://1inch.exchange/',\n    ),\n    'tornado': (\n        # is checksummed\n        'https://raw.githubusercontent.com/tornadocash/airdrop/master/airdrop.csv',\n        A_TORN,  # Don't have TORN token yet?\n        'https://tornado.cash/',\n    ),\n    'cornichon': (\n        # is checksummed\n        'https://gist.githubusercontent.com/LefterisJP/5199d8bc6caa3253c343cd5084489088/raw/7e9ca4c4772fc50780bfe9997e1c43525e1b7445/cornichon_airdrop.csv',  # noqa: E501\n        A_CORN,\n        'https://cornichon.ape.tax/',\n    ),\n    'grain': (\n        # is checksummed\n        'https://gist.githubusercontent.com/LefterisJP/08d7a5b28876741b300c944650c89280/raw/987ab4a92d5363fdbe262f639565732bd1fd3921/grain_iou.csv',  # noqa: E501\n        A_GRAIN,\n        'https://claim.harvest.finance/',\n    ),\n    'furucombo': (\n        # is checksummed\n        'https://gist.githubusercontent.com/LefterisJP/69612e155e8063fd6b3422d4efbf22a3/raw/b9023960ab1c478ee2620c456e208e5124115c19/furucombo_airdrop.csv',  # noqa: E501\n        A_COMBO,\n        'https://furucombo.app/',\n    ),\n    'lido': (\n        # is checksummed\n        'https://gist.githubusercontent.com/LefterisJP/57a8d65280a482fed6f3e2cc00c0e540/raw/e6ebac56c438cc8a882585c5f5bfba64eb57c424/lido_airdrop.csv',  # noqa: E501\n        A_LDO,\n        'https://lido.fi/',\n    ),\n}\n\n\ndef get_airdrop_data(name: str, data_dir: Path) -> Tuple[Iterator, TextIO]:\n    airdrops_dir = data_dir / 'airdrops'\n    airdrops_dir.mkdir(parents=True, exist_ok=True)\n    filename = airdrops_dir / f'{name}.csv'\n    if not filename.is_file():\n        # if not cached, get it from the gist\n        try:\n            request = requests.get(AIRDROPS[name][0])\n        except requests.exceptions.RequestException as e:\n            raise RemoteError(f'Airdrops Gist request failed due to {str(e)}') from e\n\n        with open(filename, 'w') as f:\n            f.write(request.content.decode('utf-8'))\n\n    csvfile = open(filename, 'r')\n    iterator = csv.reader(csvfile)\n    next(iterator)  # skip header\n    return iterator, csvfile\n\n\ndef check_airdrops(\n        addresses: List[ChecksumEthAddress],\n        data_dir: Path,\n) -> Dict[ChecksumEthAddress, Dict]:\n    \"\"\"Checks airdrop data for the given list of ethereum addresses\n\n    May raise:\n        - RemoteError if the remote request fails\n    \"\"\"\n    found_data: Dict[ChecksumEthAddress, Dict] = defaultdict(lambda: defaultdict(dict))\n    for protocol_name, airdrop_data in AIRDROPS.items():\n        data, csvfile = get_airdrop_data(protocol_name, data_dir)\n        for addr, amount, *_ in data:\n            # not doing to_checksum_address() here since the file addresses are checksummed\n            # and doing to_checksum_address() so many times hits performance\n            if protocol_name in ('cornichon', 'tornado', 'grain', 'lido'):\n                amount = token_normalized_value_decimals(int(amount), 18)\n            if addr in addresses:\n                found_data[addr][protocol_name] = {\n                    'amount': str(amount),\n                    'asset': airdrop_data[1],\n                    'link': airdrop_data[2],\n                }\n        csvfile.close()\n\n    return dict(found_data)\n", "path": "rotkehlchen/chain/ethereum/airdrops.py"}]}