Repository: rotki/rotki

Issue:
Augment our airdrops list from Claimable
## Abstract

Claimable have made a claimable airdrops webpage much like we have in our airdrops section: https://github.com/dawsbot/claimable

We can see what lists of claimable airdrops they have which we don't and add those airdrops also to our list.

## Task

Check out all airdrops by claimable above and if we are missing something, then just take it and add it to our list.


Discussion:

Resolving patch:
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,14 +1,25 @@
 import csv
 from collections import defaultdict
+from json.decoder import JSONDecodeError
 from pathlib import Path
-from typing import Dict, Iterator, List, TextIO, Tuple
+from typing import Any, 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.constants.assets import (
+    A_1INCH,
+    A_COMBO,
+    A_CORN,
+    A_CRV,
+    A_GRAIN,
+    A_LDO,
+    A_TORN,
+    A_UNI,
+)
 from rotkehlchen.errors import RemoteError
 from rotkehlchen.typing import ChecksumEthAddress
+from rotkehlchen.utils.serialization import rlk_jsondumps, rlk_jsonloads_dict
 
 AIRDROPS = {
     'uniswap': (
@@ -53,6 +64,111 @@
         A_LDO,
         'https://lido.fi/',
     ),
+    'curve': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/9a37e5342ddb6219a805a82bcd3d63fe/raw/71e89f0e95ea8ef5503fb1ac569447fea63f1ede/curve_airdrop.csv',  # noqa: E501
+        A_CRV,
+        'https://www.curve.fi/',
+    ),
+}
+
+POAP_AIRDROPS = {
+    'aave_v2_pioneers': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/569992ba1536474f97f7c74104e66354/raw/a8c5bfb91c8328f8a9d2b6f853a0a55328458ed7/poap_aave_v2_pioneers.json',  # noqa: E501
+        'https://poap.delivery/aave-v2-pioneers',
+        'AAVE V2 Pioneers',
+    ),
+    'beacon_chain_first_1024': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/73469098efe0b12965e5752899be00fe/raw/2ee02c22b68b90333359e2f1d24ff5d460dba092/poap_beacon_chain_first_1024.json',  # noqa: E501
+        'https://poap.delivery/beacon-chain-first-1024',
+        'Beacon Chain First 32,769 Block Validators',
+    ),
+    'beacon_chain_first_32769': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/6302f4e6da6c1488427fbb8b6207222e/raw/1e2e6ebc8c29ba75c2189a5780c132da4ed8530c/poap_beacon_chain_first_32769.json',  # noqa: E501
+        'https://poap.delivery/beacon-chain-first-32769',
+        'Beacon Chain First 1024 Depositors and Proposers',
+    ),
+    'coingecko_yield_farming': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/58d23332afc6e9fe701ecc80fcc864f6/raw/25ee4153498e9a4c709542d6f541cc9ab76997d8/poap_coingecko_yield_farming.json',  # noqa: E501
+        'https://poap.delivery/coin-gecko',
+        'Coin Gecko Yield Farming',
+    ),
+    'eth2_genesis': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/7ce2c343de427c9fe6f54dc9bd6d1a0c/raw/e55baebe6657c11c73b6b808cb269fab31c02da8/poap_eth2_genesis.json',  # noqa: E501
+        'https://poap.delivery/eth2-genesis/',
+        'Beacon Chain Genesis Depositor',
+    ),
+    'half_rekt': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/429e9c9b3948499cfe793cea75a3b0d6/raw/a0a09372d5bf01285490108661a7c223c1a7de8d/poap_half_rekt.json',  # noqa: E501
+        'https://poap.delivery/half-rekt',
+        'Half Rekt',
+    ),
+    'keep_stakers': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/b794526fb996cb85dfb825ee5f814e4f/raw/69ed0700a9f7432d783148c89872b86f1d0ee3dd/poap_keep_stakers.json',  # noqa: E501
+        'https://poap.delivery/keep-stakers',
+        'KEEP Network Mainnet Stakers',
+    ),
+    'lumberjackers': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/802359b6825472ee0081580dbe1a1c82/raw/a456fcd1eb1ddedac1cf4b6dd4bbb2d57371f028/poap_lumberjackers.json',  # noqa: E501
+        'https://poap.delivery/lumberjackers',
+        'False Start Lumberjackers',
+    ),
+    'medalla': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/1a293bf46b388f709df84ff98c5c5cc6/raw/196ccb50451d908d71cca4bc43731d6547b2276b/poap_medalla.json',  # noqa: E501
+        'https://poap.delivery/medalla',
+        'Medalla Validator',
+    ),
+    'muir_glacier': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/d03954e7e6e48a340cb3830bf0ddb0f4/raw/d339cfd94885874cb83d829e815212cd29cbb66f/poap_muir_glacier.json',  # noqa: E501
+        'https://poap.delivery/muir-glacier',
+        'Muir Glacier',
+    ),
+    'proof_of_gucci': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/43b2c4bb73923d7bb3eaf3b329f7f7a1/raw/e5adb753a3a86f0a50bf93137e2c4adc61548293/poap_proof_of_gucci.json',  # noqa: E501
+        'https://poap.delivery/proof-of-gucci',
+        'YFI - Proof of Gucci',
+    ),
+    'proof_of_gucci_design_competition': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/40c6b2a94d4b8d7f442522b099e5e258/raw/a0851ce92ca2f668e17a97c9df982bc3e12f9bb3/poap_proof_of_gucci_design_competition.json',  # noqa: E501
+        'https://poap.delivery/proof-of-gucci-design',
+        'YFI - Proof of Gucci Design Competition',
+    ),
+    'resucitators': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/0ac0216f82f16453b74a40529384a152/raw/f003083090efd834bcee1d0d1fe4e218380aa0cf/poap_resucitators.json',  # noqa: E501
+        'https://poap.delivery/medalla-resuscitator',
+        'Medalla Resuscitators',
+    ),
+    'yam': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/d676e1f3db8df96928c2501c6be434ac/raw/fa0a603117f6e3e807a49491924aaaa2bc89179a/poap_yam.json',  # noqa: E501
+        'https://poap.delivery/yam',
+        'Yam Heroes',
+    ),
+    'ycover': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/727c05f3a9cab79059258528595c102e/raw/59ff367bd532f632ce94e69900709155a55de82a/poap_ycover.json',  # noqa: E501
+        'https://poap.delivery/ycover',
+        'A New Face For yCover',
+    ),
+    'yfi_og': (
+        # is checksummed
+        'https://gist.githubusercontent.com/LefterisJP/58862ec2b398c770d60c24e50e18e50c/raw/3292995f530baedcdacde02526c6b2bd1de0e110/poap_yfi_og.json',  # noqa: E501
+        'https://poap.delivery/yfi-og',
+        'I Played 4 YFI',
+    ),
 }
 
 
@@ -76,6 +192,30 @@ def get_airdrop_data(name: str, data_dir: Path) -> Tuple[Iterator, TextIO]:
     return iterator, csvfile
 
 
+def get_poap_airdrop_data(name: str, data_dir: Path) -> Dict[str, Any]:
+    airdrops_dir = data_dir / 'airdrops_poap'
+    airdrops_dir.mkdir(parents=True, exist_ok=True)
+    filename = airdrops_dir / f'{name}.json'
+    if not filename.is_file():
+        # if not cached, get it from the gist
+        try:
+            request = requests.get(POAP_AIRDROPS[name][0])
+        except requests.exceptions.RequestException as e:
+            raise RemoteError(f'POAP airdrops Gist request failed due to {str(e)}') from e
+
+        try:
+            json_data = rlk_jsonloads_dict(request.content.decode('utf-8'))
+        except JSONDecodeError as e:
+            raise RemoteError(f'POAP airdrops Gist contains an invalid JSON {str(e)}') from e
+
+        with open(filename, 'w') as outfile:
+            outfile.write(rlk_jsondumps(json_data))
+
+    infile = open(filename, 'r')
+    data_dict = rlk_jsonloads_dict(infile.read())
+    return data_dict
+
+
 def check_airdrops(
         addresses: List[ChecksumEthAddress],
         data_dir: Path,
@@ -101,4 +241,21 @@ def check_airdrops(
                 }
         csvfile.close()
 
+    # TODO: fix next line annotation
+    for protocol_name, airdrop_data in POAP_AIRDROPS.items():  # type: ignore
+        data_dict = get_poap_airdrop_data(protocol_name, data_dir)
+        for addr, assets in data_dict.items():
+            # not doing to_checksum_address() here since the file addresses are checksummed
+            # and doing to_checksum_address() so many times hits performance
+            if addr in addresses:
+                if 'poap' not in found_data[addr]:
+                    found_data[addr]['poap'] = []
+
+                found_data[addr]['poap'].append({
+                    'event': protocol_name,
+                    'assets': assets,
+                    'link': airdrop_data[1],
+                    'name': airdrop_data[2],
+                })
+
     return dict(found_data)


Test patch: