From bea6a6300b493164e4f7b08f011e238a4c6d36d9 Mon Sep 17 00:00:00 2001 From: Graham Dixon Date: Mon, 5 Apr 2021 12:20:28 +0100 Subject: [PATCH] Adds xDai to POAP verification (#8677) * Updates POAPs abi, from_blocks and adds xDai verification * Tidies up logic --- app/dashboard/utils.py | 65 ++++++++++++++++++++++++++---------------- app/dashboard/views.py | 31 +++++++++++++------- 2 files changed, 62 insertions(+), 34 deletions(-) diff --git a/app/dashboard/utils.py b/app/dashboard/utils.py index bf0a763c078..b4e9a28619c 100644 --- a/app/dashboard/utils.py +++ b/app/dashboard/utils.py @@ -21,6 +21,7 @@ import json import logging import re +import time from json.decoder import JSONDecodeError from django.conf import settings @@ -309,7 +310,11 @@ def get_web3(network, sockets=False): w3.middleware_stack.inject(geth_poa_middleware, layer=0) return w3 elif network == 'xdai': - return Web3(HTTPProvider(f'https://dai.poa.network/')) + if sockets: + provider = WebsocketProvider(f'wss://rpc.xdaichain.com/wss') + else: + provider = HTTPProvider(f'https://dai.poa.network/') + return Web3(provider) elif network == 'localhost' or 'custom network': return Web3(Web3.HTTPProvider("http://testrpc:8545", request_kwargs={'timeout': 60})) @@ -391,41 +396,53 @@ def getBountyContract(network): def get_poap_contract_addresss(network): if network == 'mainnet': return to_checksum_address('0x22C1f6050E56d2876009903609a2cC3fEf83B415') + elif network == 'xdai': + return to_checksum_address('0x22C1f6050E56d2876009903609a2cC3fEf83B415') elif network == 'ropsten': return to_checksum_address('0x50C5CA3e7f5566dA3Aa64eC687D283fdBEC2A2F2') raise UnsupportedNetworkException(network) -def get_poap_contract(network): - web3 = get_web3(network) - poap_abi = '[{"constant":true,"inputs":[{"name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"eventId","type":"uint256"}],"name":"renounceEventMinter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"eventId","type":"uint256"},{"name":"account","type":"address"}],"name":"removeEventMinter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"isAdmin","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"eventId","type":"uint256"},{"name":"account","type":"address"}],"name":"isEventMinter","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"account","type":"address"}],"name":"addAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"renounceAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"eventId","type":"uint256"},{"name":"account","type":"address"}],"name":"addEventMinter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"tokenId","type":"uint256"},{"name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"eventId","type":"uint256"},{"indexed":false,"name":"tokenId","type":"uint256"}],"name":"EventToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"account","type":"address"}],"name":"AdminAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"account","type":"address"}],"name":"AdminRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"eventId","type":"uint256"},{"indexed":true,"name":"account","type":"address"}],"name":"EventMinterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"eventId","type":"uint256"},{"indexed":true,"name":"account","type":"address"}],"name":"EventMinterRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":true,"name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"approved","type":"address"},{"indexed":true,"name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"operator","type":"address"},{"indexed":false,"name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"tokenId","type":"uint256"}],"name":"tokenEvent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"index","type":"uint256"}],"name":"tokenDetailsOfOwnerByIndex","outputs":[{"name":"tokenId","type":"uint256"},{"name":"eventId","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"baseURI","type":"string"}],"name":"setBaseURI","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"eventId","type":"uint256"},{"name":"to","type":"address"}],"name":"mintToken","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"eventId","type":"uint256"},{"name":"to","type":"address[]"}],"name":"mintEventToManyUsers","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"eventIds","type":"uint256[]"},{"name":"to","type":"address"}],"name":"mintUserToManyEvents","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"__name","type":"string"},{"name":"__symbol","type":"string"},{"name":"__baseURI","type":"string"},{"name":"admins","type":"address[]"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]' +def get_poap_contract(network, sockets): + web3 = get_web3(network, sockets) + poap_abi = '[{"constant":true,"inputs":[{"name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"eventId","type":"uint256"}],"name":"renounceEventMinter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"tokenId","type":"uint256"}],"name":"tokenEvent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"eventId","type":"uint256"},{"name":"account","type":"address"}],"name":"removeEventMinter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"account","type":"address"}],"name":"removeAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"isAdmin","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"eventId","type":"uint256"},{"name":"to","type":"address[]"}],"name":"mintEventToManyUsers","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"eventId","type":"uint256"},{"name":"account","type":"address"}],"name":"isEventMinter","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"baseURI","type":"string"}],"name":"setBaseURI","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"index","type":"uint256"}],"name":"tokenDetailsOfOwnerByIndex","outputs":[{"name":"tokenId","type":"uint256"},{"name":"eventId","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"account","type":"address"}],"name":"addAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"renounceAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"__name","type":"string"},{"name":"__symbol","type":"string"},{"name":"__baseURI","type":"string"},{"name":"admins","type":"address[]"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"eventId","type":"uint256"},{"name":"account","type":"address"}],"name":"addEventMinter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"eventId","type":"uint256"},{"name":"to","type":"address"}],"name":"mintToken","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"tokenId","type":"uint256"},{"name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"eventId","type":"uint256"},{"name":"tokenId","type":"uint256"},{"name":"to","type":"address"}],"name":"mintToken","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"eventIds","type":"uint256[]"},{"name":"to","type":"address"}],"name":"mintUserToManyEvents","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"eventId","type":"uint256"},{"indexed":false,"name":"tokenId","type":"uint256"}],"name":"EventToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"account","type":"address"}],"name":"AdminAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"account","type":"address"}],"name":"AdminRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"eventId","type":"uint256"},{"indexed":true,"name":"account","type":"address"}],"name":"EventMinterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"eventId","type":"uint256"},{"indexed":true,"name":"account","type":"address"}],"name":"EventMinterRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":true,"name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"approved","type":"address"},{"indexed":true,"name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"operator","type":"address"},{"indexed":false,"name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"}]' poap_addr = get_poap_contract_addresss(network) poap_abi = json.loads(poap_abi) poap_contract = web3.eth.contract(poap_addr, abi=poap_abi) return poap_contract -def get_poap_earliest_owned_token_timestamp(network, address): - poap_contract = get_poap_contract(network) - from_block = 7844308 - if network == "ropsten": - from_block = 5592255 - # Filter the contract events by owner address - transfer_filter = poap_contract.events.Transfer.createFilter(argument_filters={'to': address}, fromBlock=from_block, toBlock='latest') - log_entries = transfer_filter.get_all_entries() - if len(log_entries) == 0: - # We find no event for this address +def get_poap_earliest_owned_token_timestamp(network, sockets, address): + web3 = get_web3(network, sockets) + poap_contract = get_poap_contract(network, sockets) + from_block = 7844214 + if network == "xdai": + from_block = 12188423 + elif network == "ropsten": + from_block = 5592255 + + # Check that the address holds a balance + if poap_contract.functions.balanceOf(address).call() == 0: + # No balance for this address return None else: - # get block number of the earliest tokenId that still owned by owner - for entry in log_entries: - token_id = entry.args.tokenId - block_number = entry.blockNumber - owner = poap_contract.functions.ownerOf(token_id).call() - if address.lower() == owner.lower(): - # Gotcha - web3 = get_web3(network) - return web3.eth.getBlock(block_number).timestamp - + # Get all Transfer events that were sent to this Address + transfer_filter = poap_contract.events.Transfer.createFilter(argument_filters={'to': address}, fromBlock=from_block, toBlock='latest') + # Make sure the filter is registered before claiming entries + time.sleep(1) + log_entries = transfer_filter.get_all_entries() + # If no entries are returned then we should return 0 to denote a failure (there should be at least some entries if we have a balance present) + if len(log_entries) == 0: + # No Transfer events we're retrieved for this address + return 0 + else: + # Get block number of the earliest tokenId that still owned by owner + for entry in log_entries: + token_id = entry.args.tokenId + block_number = entry.blockNumber + owner = poap_contract.functions.ownerOf(token_id).call() + if address.lower() == owner.lower(): + # Gotcha + return web3.eth.getBlock(block_number).timestamp def get_bounty(bounty_enum, network): diff --git a/app/dashboard/views.py b/app/dashboard/views.py index e0c1a4a0a84..6e534b68b16 100644 --- a/app/dashboard/views.py +++ b/app/dashboard/views.py @@ -6472,27 +6472,38 @@ def verify_user_poap(request, handle): 'msg': 'Invalid signature', }) - # commented out because network = get_default_network() results in dashboard.utils.UnsupportedNetworkException: rinkeby - # network = get_default_network() - network = "mainnet" + # POAP verification is only valid if the ethereum address has held the badge > 15 days fifteen_days_ago = datetime.now()-timedelta(days=15) + fitteen_days_ago_ts = fifteen_days_ago.timestamp() - timestamp = get_poap_earliest_owned_token_timestamp(network, eth_address) - if timestamp is None or timestamp > fifteen_days_ago.timestamp(): - # We couldn't find any POAP badge for this ethereum address + timestamp = None + + for network in ['mainnet', 'xdai']: + timestamp = get_poap_earliest_owned_token_timestamp(network, True, eth_address) + # only break if we find a token that has been held for longer than 15 days + if timestamp and timestamp <= fitteen_days_ago_ts: + break + + # fail cases (no qualifying tokens / network failure) + if timestamp is None or timestamp > fitteen_days_ago_ts: return JsonResponse({ 'ok': False, 'msg': 'No qualifying POAP badges (ERC721 NFTs held for at least 15 days) found for this account.', }) + elif timestamp == 0: + return JsonResponse({ + 'ok': False, + 'msg': 'An error occured, please try again later.', + }) profile = profile_helper(handle, True) profile.is_poap_verified = True profile.save() + # Success response return JsonResponse({ - 'ok': True, - 'msg': 'Found a POAP badge that has been sitting in this wallet more than 15 days' - } - ) + 'ok': True, + 'msg': 'Found a POAP badge that has been sitting in this wallet more than 15 days' + }) @csrf_protect