def receive_tip_v2(request, pk, txid, network): """Handle the receiving of a tip (the POST). TODO: Deprecate after v3 has been live for a month. Returns: TemplateResponse: the UI with the tip confirmed """ tip = Tip.objects.get(web3_type='v2', metadata__priv_key=pk, txid=txid, network=network) if tip.receive_txid: messages.info(request, 'This tip has already been received') not_mined_yet = get_web3(tip.network).eth.getBalance( Web3.toChecksumAddress(tip.metadata['address'])) == 0 if not_mined_yet: messages.info( request, f'This tx {tip.txid}, is still mining. Please wait a moment before submitting the receive form.' ) """Receive a tip.""" if request.POST and not tip.receive_txid: params = request.POST # db mutations try: address = params['forwarding_address'] tip.receive_txid = tip.payout_to(address) tip.receive_address = address tip.received_on = timezone.now() tip.save() record_user_action(tip.from_username, 'receive_tip', tip) record_tip_activity(tip, tip.from_username, 'receive_tip') messages.success(request, 'This tip has been received') except Exception as e: messages.error(request, str(e)) logger.exception(e) params = { 'issueURL': request.GET.get('source'), 'class': 'receive', 'title': _('Receive Tip'), 'gas_price': round( recommend_min_gas_price_to_confirm_in_time( confirm_time_minutes_target), 1), 'tip': tip, 'disable_inputs': tip.receive_txid or not_mined_yet, } return TemplateResponse(request, 'onepager/receive_v2.html', params)
def getBountyContract(network): web3 = get_web3(network) standardbounties_abi = '[{"constant":false,"inputs":[{"name":"_bountyId","type":"uint256"}],"name":"killBounty","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_bountyId","type":"uint256"}],"name":"getBountyToken","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_bountyId","type":"uint256"},{"name":"_data","type":"string"}],"name":"fulfillBounty","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_bountyId","type":"uint256"},{"name":"_newDeadline","type":"uint256"}],"name":"extendDeadline","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getNumBounties","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_bountyId","type":"uint256"},{"name":"_fulfillmentId","type":"uint256"},{"name":"_data","type":"string"}],"name":"updateFulfillment","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_bountyId","type":"uint256"},{"name":"_newFulfillmentAmount","type":"uint256"},{"name":"_value","type":"uint256"}],"name":"increasePayout","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_bountyId","type":"uint256"},{"name":"_newFulfillmentAmount","type":"uint256"}],"name":"changeBountyFulfillmentAmount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_bountyId","type":"uint256"},{"name":"_newIssuer","type":"address"}],"name":"transferIssuer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_bountyId","type":"uint256"},{"name":"_value","type":"uint256"}],"name":"activateBounty","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_issuer","type":"address"},{"name":"_deadline","type":"uint256"},{"name":"_data","type":"string"},{"name":"_fulfillmentAmount","type":"uint256"},{"name":"_arbiter","type":"address"},{"name":"_paysTokens","type":"bool"},{"name":"_tokenContract","type":"address"}],"name":"issueBounty","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_issuer","type":"address"},{"name":"_deadline","type":"uint256"},{"name":"_data","type":"string"},{"name":"_fulfillmentAmount","type":"uint256"},{"name":"_arbiter","type":"address"},{"name":"_paysTokens","type":"bool"},{"name":"_tokenContract","type":"address"},{"name":"_value","type":"uint256"}],"name":"issueAndActivateBounty","outputs":[{"name":"","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"_bountyId","type":"uint256"}],"name":"getBountyArbiter","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_bountyId","type":"uint256"},{"name":"_value","type":"uint256"}],"name":"contribute","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_bountyId","type":"uint256"},{"name":"_newPaysTokens","type":"bool"},{"name":"_newTokenContract","type":"address"}],"name":"changeBountyPaysTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_bountyId","type":"uint256"}],"name":"getBountyData","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_bountyId","type":"uint256"},{"name":"_fulfillmentId","type":"uint256"}],"name":"getFulfillment","outputs":[{"name":"","type":"bool"},{"name":"","type":"address"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_bountyId","type":"uint256"},{"name":"_newArbiter","type":"address"}],"name":"changeBountyArbiter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_bountyId","type":"uint256"},{"name":"_newDeadline","type":"uint256"}],"name":"changeBountyDeadline","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_bountyId","type":"uint256"},{"name":"_fulfillmentId","type":"uint256"}],"name":"acceptFulfillment","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"bounties","outputs":[{"name":"issuer","type":"address"},{"name":"deadline","type":"uint256"},{"name":"data","type":"string"},{"name":"fulfillmentAmount","type":"uint256"},{"name":"arbiter","type":"address"},{"name":"paysTokens","type":"bool"},{"name":"bountyStage","type":"uint8"},{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_bountyId","type":"uint256"}],"name":"getBounty","outputs":[{"name":"","type":"address"},{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"bool"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_bountyId","type":"uint256"},{"name":"_newData","type":"string"}],"name":"changeBountyData","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_bountyId","type":"uint256"}],"name":"getNumFulfillments","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_owner","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"bountyId","type":"uint256"}],"name":"BountyIssued","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"bountyId","type":"uint256"},{"indexed":false,"name":"issuer","type":"address"}],"name":"BountyActivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"bountyId","type":"uint256"},{"indexed":true,"name":"fulfiller","type":"address"},{"indexed":true,"name":"_fulfillmentId","type":"uint256"}],"name":"BountyFulfilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_bountyId","type":"uint256"},{"indexed":false,"name":"_fulfillmentId","type":"uint256"}],"name":"FulfillmentUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"bountyId","type":"uint256"},{"indexed":true,"name":"fulfiller","type":"address"},{"indexed":true,"name":"_fulfillmentId","type":"uint256"}],"name":"FulfillmentAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"bountyId","type":"uint256"},{"indexed":true,"name":"issuer","type":"address"}],"name":"BountyKilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"bountyId","type":"uint256"},{"indexed":true,"name":"contributor","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"ContributionAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"bountyId","type":"uint256"},{"indexed":false,"name":"newDeadline","type":"uint256"}],"name":"DeadlineExtended","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"bountyId","type":"uint256"}],"name":"BountyChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_bountyId","type":"uint256"},{"indexed":true,"name":"_newIssuer","type":"address"}],"name":"IssuerTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_bountyId","type":"uint256"},{"indexed":false,"name":"_newFulfillmentAmount","type":"uint256"}],"name":"PayoutIncreased","type":"event"}]' standardbounties_addr = getStandardBountiesContractAddresss(network) bounty_abi = json.loads(standardbounties_abi) getBountyContract = web3.eth.contract(standardbounties_addr, abi=bounty_abi) return getBountyContract
def contract(self): """Return grants contract.""" from dashboard.utils import get_web3 web3 = get_web3(self.network) grant_contract = web3.eth.contract(Web3.toChecksumAddress( self.contract_address), abi=self.abi) return grant_contract
def update_user_balance(self, holder_profile, holder_address): if PTOKEN_ABI and self.token_address: web3 = get_web3(self.network) contract = web3.eth.contract(Web3.toChecksumAddress(self.token_address), abi=PTOKEN_ABI) decimals = 10 ** contract.functions.decimals().call() balance = contract.functions.balanceOf(holder_address).call() // decimals self.cached_balances[str(holder_profile.id)] = balance self.save()
def index(request): # setup player = request.GET.get('coinbase') network = request.GET.get('network') if not request.user.is_authenticated: return JsonResponse({'status': 'error', 'msg': 'You must login'}) # setup web3 interactions w3 = get_web3(network) player = w3.toChecksumAddress(player) contract_address_rinkeby = w3.toChecksumAddress('0xcEFBf0A9Ada7A03056dD08B470AA843ef8ca5D79') contract_address_mainnet = w3.toChecksumAddress('0xb4e903dc14dfe994fe291fc5b385c4718413366d') if network not in ['rinkeby', 'mainnet']: return JsonResponse({'status': 'error', 'msg': 'Unsupported network'}) # get contract contract_address = contract_address_mainnet if network == 'mainnet' else contract_address_rinkeby abi = '[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"tokenURI","type":"string"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint256","name":"_nonce","type":"uint256"}],"name":"createPassport","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_messageHash","type":"bytes32"}],"name":"getEthSignedMessageHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"string","name":"_message","type":"string"},{"internalType":"uint256","name":"_nonce","type":"uint256"}],"name":"getMessageHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_ethSignedMessageHash","type":"bytes32"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"recoverSigner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"sig","type":"bytes"}],"name":"splitSignature","outputs":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"string","name":"_message","type":"string"},{"internalType":"uint256","name":"_nonce","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"verify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"}]' abi = json.loads(abi) contract = w3.eth.contract(contract_address, abi=abi) # validation balance = contract.functions.balanceOf(player).call() if balance > 0: return JsonResponse({'status': 'error', 'msg': 'You already have a passport. You must burn your passport before you can generate another one!'}) # validation passed nonce = contract.functions.totalSupply().call() + 5 _uuid = uuid.uuid4() tokenURI = f'{_uuid}' if request.user.profile.passport_requests.filter(network=network).exists(): return JsonResponse({'status': 'error', 'msg': 'You already have a passport. You must burn your passport before you can generate another one!'}) ppr = PassportRequest.objects.create( profile=request.user.profile, nonce=nonce, address=player, uuid=_uuid, network=network, uri=tokenURI, ) # sign message message = contract.functions.getMessageHash(player, 0, tokenURI, nonce).call() message_hash = defunct_hash_message(primitive=message) private_key = settings.PASSPORT_PK_RINKEBY if network == 'rinkeby' else settings.PASSPORT_PK_MAINNET signed_message = w3.eth.account.signHash(message_hash, private_key=private_key).signature.hex() context = { 'status': 'success', 'contract_address': contract_address, 'contract_abi': abi, 'nonce': nonce, 'tokenURI': tokenURI, 'hash': signed_message, } return JsonResponse(context)
def test_get_web3(): """Test the dashboard utility get_web3.""" networks = ['mainnet', 'rinkeby', 'ropsten'] for network in networks: web3_provider = get_web3(network) assert isinstance(web3_provider, Web3) assert len(web3_provider.providers) == 1 assert isinstance(web3_provider.providers[0], HTTPProvider) assert web3_provider.providers[0].endpoint_uri == f'https://{network}.infura.io'
def has_tx_mined(txid, network): web3 = get_web3(network) try: transaction = web3.eth.getTransaction(txid) if not transaction: return False return transaction.blockHash != HexBytes('0x0000000000000000000000000000000000000000000000000000000000000000') except Exception: return False
def new_matching_partner(request): tx_hash = request.POST.get('hash') tx_amount = request.POST.get('amount') profile = get_profile(request) def get_json_response(message, status): return JsonResponse({ 'status': status, 'message': message }, status=status) def is_verified(tx_details, tx_hash, tx_amount, network): gitcoin_account = '0x00De4B13153673BCAE2616b67bf822500d325Fc3' return has_tx_mined(tx_hash, network) and\ tx_details.to.lower() == gitcoin_account.lower() if not request.user.is_authenticated: return get_json_response("Not Authorized", 403) if not profile: return get_json_response("Profile not found.", 404) if request.POST and tx_hash: network = 'mainnet' web3 = get_web3(network) tx = web3.eth.getTransaction(tx_hash) if not tx: raise Http404 match_pledge = MatchPledge.objects.create(profile=profile, amount=convert_amount( tx.value / 10**18, 'ETH', 'USDT'), data=json.dumps({ 'tx_hash': tx_hash, 'network': network, 'from': tx['from'], 'to': tx.to, 'tx_amount': tx.value })) match_pledge.active = is_verified(tx, tx_hash, tx_amount, network) match_pledge.save() return get_json_response( """Thank you for volunteering to match on Gitcoin Grants. You are supporting open source, and we thank you.""", 201) return get_json_response("Wrong request.", 400)
def test_get_web3(): """Test the dashboard utility get_web3.""" networks = ['mainnet', 'rinkeby', 'ropsten'] for network in networks: web3_provider = get_web3(network) assert isinstance(web3_provider, Web3) assert len(web3_provider.providers) == 1 assert isinstance(web3_provider.providers[0], HTTPProvider) if settings.INFURA_USE_V3: assert web3_provider.providers[0].endpoint_uri == f'https://{network}.infura.io/v3/{settings.INFURA_V3_PROJECT_ID}' else: assert web3_provider.providers[0].endpoint_uri == f'https://{network}.infura.io'
def handle(self, *args, **options): # Parse inputs / setup network = options['network'] w3 = get_web3(network) if network != 'mainnet' and network != 'rinkeby': raise Exception('Invalid network: Must be mainnet or rinkeby') # Get array of subscriptions with N/A contributor address bad_subscriptions = Subscription.objects.filter(contributor_address="N/A") # For each one, find the from address and use that to replace the N/A for subscription in bad_subscriptions: try: tx_hash = subscription.split_tx_id if tx_hash[0:8].lower() == 'sync-tx:': # zkSync transaction, so use zkSync's API: https://zksync.io/api/v0.1.html#transaction-details tx_hash = tx_hash.replace('sync-tx:', '0x') base_url = 'https://rinkeby-api.zksync.io/api/v0.1' if network == 'rinkeby' else 'https://api.zksync.io/api/v0.1' r = requests.get(f"{base_url}/transactions_all/{tx_hash}") r.raise_for_status() tx_data = r.json() # zkSync transaction data if not tx_data: print(f'Skipping, zkSync receipt not found for transaction {subscription.split_tx_id}') continue from_address = tx_data['from'] elif len(tx_hash) == 66: # Standard L1 transaction receipt = w3.eth.getTransactionReceipt(tx_hash) if not receipt: print(f'Skipping, L1 receipt not found for transaction {subscription.split_tx_id}') continue from_address = receipt['from'] else: print(f'Skipping unknown transaction hash format, could not parse {subscription.split_tx_id}') continue if not from_address: print(f'Skipping invalid from address {from_address} for transaction hash {subscription.split_tx_id}') # Note: This approach does not guarantee the correct contributor address. Because we are using the sender # of the transaction as the contributor, we get the wrong address for users with wallet's that use # a relayer or meta-transactions, such as Argent. In those cases, the relayer address is incorrectly # listed as the sender. A more robust approach would take a non-trivial amount of work since it # requires recognizing relayed transaction and parsing them to find the wallet address, and there's no # universal standard for relayed transaction format from_address = Web3.toChecksumAddress(from_address) subscription.contributor_address = from_address subscription.save() except Exception as e: print(f'Skipping: Error when fetching from_address for transaction hash {subscription.split_tx_id}') print(e) print("\n")
def handle(self, *args, **options): w3 = get_web3('mainnet') monitored_accounts = [settings.KUDOS_OWNER_ACCOUNT] for account in monitored_accounts: balance_eth_threshold = 0.1 if account == settings.KUDOS_OWNER_ACCOUNT: balance_eth_threshold = 0.4 balance_wei = w3.eth.getBalance(account) balance_eth = balance_wei / 10**18 if balance_eth < balance_eth_threshold: warn_account_out_of_eth(account, balance_eth, 'ETH')
def get_debug_info(self): """Return grants contract.""" from dashboard.utils import get_web3 from dashboard.abi import erc20_abi from dashboard.tokens import addr_to_token try: web3 = get_web3(self.network) if not self.token_address: return "This subscription has no token_address" token_contract = web3.eth.contract(Web3.toChecksumAddress( self.token_address), abi=erc20_abi) balance = token_contract.functions.balanceOf( Web3.toChecksumAddress(self.contributor_address)).call() allowance = token_contract.functions.allowance( Web3.toChecksumAddress(self.contributor_address), Web3.toChecksumAddress(self.grant.contract_address)).call() gasPrice = self.gas_price is_active = self.get_is_active_from_web3() token = addr_to_token(self.token_address, self.network) next_valid_timestamp = self.get_next_valid_timestamp() decimals = token.get('decimals', 0) balance = balance / 10**decimals allowance = allowance / 10**decimals error_reason = "unknown" if not is_active: error_reason = 'not_active' if timezone.now().timestamp() < next_valid_timestamp: error_reason = 'before_next_valid_timestamp' if (float(balance) + float(gasPrice)) < float( self.amount_per_period): error_reason = "insufficient_balance" if allowance < self.amount_per_period: error_reason = "insufficient_allowance" debug_info = f""" error_reason: {error_reason} ============================== is_active: {is_active} decimals: {decimals} balance: {balance} allowance: {allowance} amount_per_period: {self.amount_per_period} next_valid_timestamp: {next_valid_timestamp} """ except Exception as e: return str(e) return debug_info
def handle(self, *args, **options): networks = ['xdai', 'mainnet'] for network in networks: denomination = 'ETH' if network == 'mainnet' else 'xdai' w3 = get_web3(network) monitored_accounts = [settings.KUDOS_OWNER_ACCOUNT] for account in monitored_accounts: balance_eth_threshold = 0.1 if account == settings.KUDOS_OWNER_ACCOUNT: balance_eth_threshold = 0.4 balance_wei = w3.eth.getBalance(account) balance_eth = balance_wei / 10**18 if balance_eth < balance_eth_threshold: warn_account_out_of_eth(account, balance_eth, 'ETH')
def get_tx_status_and_details(txid, network, created_on): from django.utils import timezone from dashboard.utils import get_web3 import pytz DROPPED_DAYS = 4 # get status tx = {} status = None if txid == 'override': return 'success', None #overridden by admin try: web3 = get_web3(network) tx = web3.eth.getTransactionReceipt(txid) if not tx: drop_dead_date = created_on + timezone.timedelta(days=DROPPED_DAYS) if timezone.now() > drop_dead_date: status = 'dropped' else: status = 'pending' elif tx and 'status' not in tx.keys(): if bool(tx['blockNumber']) and bool(tx['blockHash']): status = 'success' else: raise Exception("got a tx but no blockNumber or blockHash") elif tx.status == 1: status = 'success' elif tx.status == 0: status = 'error' else: status = 'unknown' except Exception as e: logger.debug(f'Failure in get_tx_status for {txid} - ({e})') status = 'unknown' # get timestamp timestamp = None try: if tx: block = web3.eth.getBlock(tx['blockNumber']) timestamp = block.timestamp timestamp = timezone.datetime.fromtimestamp(timestamp).replace( tzinfo=pytz.UTC) except: pass return status, timestamp, tx
def __init__(self, network='localhost', sockets=False): """Initialize the KudosContract. Args: network (str, optional): The blockchain network (localhost, rinkeby, ropsten, mainnet) sockets (bool, optional): Use web socket provider if set to True, otherwise use Http provider. """ network = 'localhost' if network == 'custom network' else network self.network = network self._w3 = get_web3(self.network, sockets=sockets) ipfsConnectionString = f'/dns/{settings.IPFS_HOST}/tcp/{settings.IPFS_API_PORT}/{settings.IPFS_API_SCHEME}' self._ipfs = ipfshttpclient.connect(ipfsConnectionString) self._contract = self._get_contract() self.address = self._get_contract_address()
def _do_helper_via_web3(self, fn, minutes_to_confirm_within=1): """Call the specified function fn""" from dashboard.utils import get_web3 args = self.get_subscription_hash_arguments() tx = fn( args['from'], args['to'], args['tokenAddress'], args['tokenAmount'], args['periodSeconds'], args['gasPrice'], args['nonce'], args['signature'], ).buildTransaction(self.helper_tx_dict(minutes_to_confirm_within)) web3 = get_web3(self.grant.network) signed_txn = web3.eth.account.signTransaction( tx, private_key=settings.GRANTS_PRIVATE_KEY) return web3.eth.sendRawTransaction(signed_txn.rawTransaction).hex()
def handle(self, *args, **options): # config block = 'latest' # setup network = options['network'] web3 = get_web3(network) contract_address = getStandardBountiesContractAddresss(network) contract = getBountyContract(network) last_block_hash = None while True: # wait for a new block block = web3.eth.getBlock('latest') block_hash = block['hash'] if last_block_hash == block_hash: time.sleep(1) continue print('got new block %s' % web3.toHex(block_hash)) # get txs transactions = block['transactions'] for tx in transactions: tx = web3.eth.getTransaction(tx) if not tx or tx['to'] != contract_address: continue print('found a stdbounties tx') data = tx['input'] method_id = data[:10] if method_id == '0x7e9e511d': # issueAndActivateBounty bounty_id = contract.functions.getNumBounties().call() - 1 else: # any other method bounty_id = int(data[10:74], 16) print('process_bounty %d' % bounty_id) process_bounty(bounty_id, network) print('done process_bounty %d' % bounty_id) last_block_hash = block_hash
def update_tx_status(self): self.tx_status, self.tx_time = get_tx_status(self.txid, self.network, self.created_on) # Exit if transaction not mined, otherwise continue if (self.tx_status != 'success'): return bool(self.tx_status) # Get token address from event logs if (self.token_address == "0x0"): web3 = get_web3(self.network) receipt = web3.eth.getTransactionReceipt(self.txid) contract = web3.eth.contract(Web3.toChecksumAddress(FACTORY_ADDRESS), abi=ptoken_factory_abi) logs = contract.events.NewPToken().processReceipt(receipt) self.token_address = logs[0].args.token record_ptoken_activity('create_ptoken', self, self.token_owner_profile) send_personal_token_created(self.token_owner_profile, self) self.update_token_status()
def do_execute_subscription_via_web3(self, minutes_to_confirm_within = 5): """.Executes the subscription on the blockchain""" from dashboard.utils import get_web3 args = self.get_subscription_hash_arguments() tx = self.grant.contract.functions.executeSubscription( args['from'], args['to'], args['tokenAddress'], args['tokenAmount'], args['periodSeconds'], args['gasPrice'], args['nonce'], args['signature'], ).buildTransaction( self.helper_tx_dict(minutes_to_confirm_within) ) web3 = get_web3(self.grant.network) signed_txn = web3.eth.account.signTransaction(tx, private_key=settings.GRANTS_PRIVATE_KEY) return web3.eth.sendRawTransaction(signed_txn.rawTransaction).hex()
def update_token_status(self): if PTOKEN_ABI and self.token_address: web3 = get_web3(self.network, sockets=True) contract = web3.eth.contract(Web3.toChecksumAddress( self.token_address), abi=PTOKEN_ABI) decimals = 10**contract.functions.decimals().call() self.total_minted = contract.functions.totalSupply().call( ) // decimals self.value = contract.functions.price().call() // decimals if self.tx_status == 'success' and self.txid: latest = web3.eth.blockNumber tx = web3.eth.getTransaction(self.txid) if self.last_block == 0: self.last_block = tx['blockNumber'] redeem_filter = contract.events.Redeemed.createFilter( fromBlock=self.last_block, toBlock=latest) purchase_filter = contract.events.Purchased.createFilter( fromBlock=self.last_block, toBlock=latest) redeemed = 0 purchased = 0 for redeem in redeem_filter.get_all_entries(): redeemed += redeem['args']['amountRedeemed'] for purchase in purchase_filter.get_all_entries(): purchased += purchase['args']['amountReceived'] redeemed = redeemed // decimals purchased = purchased // decimals self.total_purchased = purchased self.total_redeemed = redeemed self.total_available = self.total_minted - ( self.total_purchased - self.total_redeemed) self.redemptions = len(redeem_filter.get_all_entries()) self.purchases = len(purchase_filter.get_all_entries()) print(f'REDEEMED: {self.total_redeemed}') print(f'PURCHASED: {self.total_purchased}') print(f'AVAILABLE: {self.total_available}') self.save()
def redeem_bulk_kudos(self, kt_id, signed_rawTransaction, retry=False): """ :param self: :param kt_id: :param signed_rawTransaction: :return: """ with redis.lock("tasks:redeem_bulk_kudos:%s" % kt_id, timeout=LOCK_TIMEOUT): try: obj = KudosTransfer.objects.get(pk=kt_id) w3 = get_web3(obj.network) obj.txid = w3.eth.sendRawTransaction( HexBytes(signed_rawTransaction)).hex() obj.receive_txid = obj.txid obj.save() pass except Exception as e: self.retry(30)
def forwards_func(apps, schema_editor): return #run me manually try: Profile = apps.get_model('dashboard', 'Profile') print('bounty') Bounty = apps.get_model('dashboard', 'Bounty') for bounty in Bounty.objects.filter(network='mainnet', current_bounty=True): try_to_link_address_to_profile(Profile, bounty.bounty_owner_github_username, bounty.bounty_owner_address) for fulfillment in bounty.fulfillments.all(): try_to_link_address_to_profile(Profile, fulfillment.fulfiller_address, fulfillment.fulfiller_address) print('fr') FaucetRequest = apps.get_model('faucet', 'FaucetRequest') for faucet in FaucetRequest.objects.all(): try_to_link_address_to_profile(Profile, faucet.github_username, faucet.address) print('tip') Tip = apps.get_model('dashboard', 'Tip') for tip in Tip.objects.filter(network='mainnet').all(): try: try_to_link_address_to_profile(Profile, tip.from_username, tip.from_address) w3 = get_web3(tip.network) tx = w3.eth.getTransaction(tip.receive_txid) if tx: to = tx['to'] try_to_link_address_to_profile(Profile, tip.username, to) except: pass print('ens') ENSSubdomainRegistration = apps.get_model('enssubdomain', 'ENSSubdomainRegistration') for ens in ENSSubdomainRegistration.objects.all(): if ens.profile: try_to_link_address_to_profile(Profile, ens.profile.handle, ens.subdomain_wallet_address) except Exception as e: print(e)
def redeem_bulk_kudos(self, kt_id, signed_rawTransaction, retry=False): """ :param self: :param kt_id: :param signed_rawTransaction: :return: """ with redis.lock("tasks:all_redeem_bulk_kudos", timeout=LOCK_TIMEOUT): with redis.lock("tasks:redeem_bulk_kudos:%s" % kt_id, timeout=LOCK_TIMEOUT): from dashboard.utils import has_tx_mined try: obj = KudosTransfer.objects.get(pk=kt_id) w3 = get_web3(obj.network) obj.txid = w3.eth.sendRawTransaction(HexBytes(signed_rawTransaction)).hex() obj.receive_txid = obj.txid obj.save() while not has_tx_mined(obj.txid, obj.network): time.sleep(1) pass except Exception as e: self.retry(30)
def get_token_recipient_senders(network, recipient_address, token_address): w3 = get_web3(network) contract = w3.eth.contract( address=token_address, abi=erc20_abi, ) # TODO: This can be made less brittle/opaque # see usage of contract.events.Transfer.getLogs in # commit 99a44cd3036ace8fcd886ed1e96747528f105d10 # after migrating to web3 >= 5. filter_params = _construct_transfer_filter_params( recipient_address, token_address, ) logs = w3.eth.getLogs(filter_params) process_log = compose(_trim_null_address, _extract_sender_address_from_log) return [process_log(log) for log in logs]
def handle(self, *args, **options): # config network = options['network'] _filter = options['kudos_filter'] live = options['live'] multiplier = options['multiplier'] tokens = Token.objects.filter( owner_address=settings.KUDOS_OWNER_ACCOUNT, contract__network=network) if _filter: tokens = tokens.filter(name__contains=_filter) kudos_contract = KudosContract(network=network)._get_contract() w3 = get_web3(network) for token in tokens: if token.gen != 1: continue print(token) if live: _tokenId = token.token_id _newPriceFinney = token.price_finney * multiplier tx = kudos_contract.functions.setPrice( _tokenId, _newPriceFinney).buildTransaction({ 'nonce': get_nonce(network, settings.KUDOS_OWNER_ACCOUNT), 'gas': 47000, 'gasPrice': int( recommend_min_gas_price_to_confirm_in_time(5) * 10**9), 'value': 0, }) signed = w3.eth.account.signTransaction( tx, settings.KUDOS_PRIVATE_KEY) txid = w3.eth.sendRawTransaction(signed.rawTransaction).hex() print(txid)
def handle(self, *args, **kwargs): api = twitter.Api( consumer_key=settings.TWITTER_CONSUMER_KEY, consumer_secret=settings.TWITTER_CONSUMER_SECRET, access_token_key=settings.TWITTER_ACCESS_TOKEN, access_token_secret=settings.TWITTER_ACCESS_SECRET, ) for grant in Grant.objects.all(): try: if grant.twitter_handle_1: user = api.GetUser( screen_name=grant.twitter_handle_1.replace('@', '')) grant.twitter_handle_1_follower_count = user.followers_count grant.save() except Exception as e: print(e) try: if grant.twitter_handle_2: user = api.GetUser( screen_name=grant.twitter_handle_2.replace('@', '')) grant.twitter_handle_2_follower_count = user.followers_count grant.save() except Exception as e: print(e) try: if not grant.contract_owner_address: w3 = get_web3('mainnet') grant.contract_owner_address = w3.eth.getTransaction( grant.deploy_tx_id)['from'] grant.save() except Exception as e: print(e)
def handle(self, *args, **options): # setup payment_threshold_usd = 0 KYC_THRESHOLD = settings.GRANTS_PAYOUT_CLR_KYC_THRESHOLD network = 'mainnet' if not settings.DEBUG else 'rinkeby' from_address = settings.GRANTS_PAYOUT_ADDRESS from_pk = settings.GRANTS_PAYOUT_PRIVATE_KEY DECIMALS = 18 what = options['what'] clr_round = options['clr_round'] DAI_ADDRESS = '0x6b175474e89094c44da98b954eedeac495271d0f' if network=='mainnet' else '0x6a6e8b58dee0ca4b4ee147ad72d3ddd2ef1bf6f7' CLR_TOKEN_ADDRESS = '0xe4101d014443af2b7f6f9f603e904adc9faf0de5' if network=='mainnet' else '0xc19b694ebd4309d7a2adcd9970f8d7f424a1528b' # get data clr_pks = options['clr_pks'].split(',') gclrs = GrantCLR.objects.filter(pk__in=clr_pks) pks = [] for gclr in gclrs: pks += gclr.grants.values_list('pk', flat=True) scheduled_matches = CLRMatch.objects.filter(round_number=clr_round) grants = Grant.objects.filter(active=True, network='mainnet', link_to_new_grant__isnull=True, pk__in=pks) print(f"got {grants.count()} grants") # finalize rankings if what == 'finalize': total_owed_grants = 0 for grant in grants: try: for gclr in grant.clr_calculations.filter(grantclr__in=gclrs, latest=True): total_owed_grants += gclr.clr_prediction_curve[0][1] except: pass total_owed_matches = sum(sm.amount for sm in scheduled_matches) print(f"there are {grants.count()} grants to finalize worth ${round(total_owed_grants,2)}") print(f"there are {scheduled_matches.count()} Match Payments already created worth ${round(total_owed_matches,2)}") print('------------------------------') user_input = input("continue? (y/n) ") if user_input != 'y': return for grant in grants: amount = sum(ele.clr_prediction_curve[0][1] for ele in grant.clr_calculations.filter(grantclr__in=gclrs, latest=True)) has_already_kyc = grant.clr_matches.filter(has_passed_kyc=True).exists() if not amount: continue already_exists = scheduled_matches.filter(grant=grant).exists() if already_exists: continue needs_kyc = amount > KYC_THRESHOLD and not has_already_kyc comments = "" if not needs_kyc else "Needs KYC" ready_for_test_payout = not needs_kyc match = CLRMatch.objects.create( round_number=clr_round, amount=amount, grant=grant, comments=comments, ready_for_test_payout=ready_for_test_payout, ) if needs_kyc: grant_match_distribution_kyc(match) # payout rankings (round must be finalized first) if what in ['prepare_final_payout']: payout_matches = scheduled_matches.exclude(test_payout_tx='').filter(ready_for_payout=False) payout_matches_amount = sum(sm.amount for sm in payout_matches) print(f"there are {payout_matches.count()} UNPAID Match Payments already created worth ${round(payout_matches_amount,2)} {network} DAI") print('------------------------------') user_input = input("continue? (y/n) ") if user_input != 'y': return for match in payout_matches: match.ready_for_payout=True match.save() print('promoted') # payout rankings (round must be finalized first) if what in ['payout_test', 'payout_dai']: is_real_payout = what == 'payout_dai' TOKEN_ADDRESS = DAI_ADDRESS if is_real_payout else CLR_TOKEN_ADDRESS kwargs = {} token_name = f'CLR{clr_round}' if not is_real_payout else 'DAI' key = 'ready_for_test_payout' if not is_real_payout else 'ready_for_payout' kwargs[key] = False not_ready_scheduled_matches = scheduled_matches.filter(**kwargs) kwargs[key] = True kwargs2 = {} key2 = 'test_payout_tx' if not is_real_payout else 'payout_tx' kwargs2[key2] = '' unpaid_scheduled_matches = scheduled_matches.filter(**kwargs).filter(**kwargs2) paid_scheduled_matches = scheduled_matches.filter(**kwargs).exclude(**kwargs2) total_not_ready_matches = sum(sm.amount for sm in not_ready_scheduled_matches) total_owed_matches = sum(sm.amount for sm in unpaid_scheduled_matches) total_paid_matches = sum(sm.amount for sm in paid_scheduled_matches) print(f"there are {not_ready_scheduled_matches.count()} NOT READY Match Payments already created worth ${round(total_not_ready_matches,2)} {network} {token_name}") print(f"there are {unpaid_scheduled_matches.count()} UNPAID Match Payments already created worth ${round(total_owed_matches,2)} {network} {token_name}") print(f"there are {paid_scheduled_matches.count()} PAID Match Payments already created worth ${round(total_paid_matches,2)} {network} {token_name}") print('------------------------------') user_input = input("continue? (y/n) ") if user_input != 'y': return print(f"continuing with {unpaid_scheduled_matches.count()} unpaid scheduled payouts") if is_real_payout: user_input = input(F"THIS IS A REAL PAYOUT FOR {network} {token_name}. ARE YOU DOUBLE SECRET SUPER SURE? (y/n) ") if user_input != 'y': return for match in unpaid_scheduled_matches.order_by('amount'): # issue payment print(f"- issuing payout {match.pk} worth {match.amount} {token_name}") address = match.grant.admin_address amount_owed = match.amount w3 = get_web3(network) contract = w3.eth.contract(Web3.toChecksumAddress(TOKEN_ADDRESS), abi=abi) address = Web3.toChecksumAddress(address) amount = int(amount_owed * 10**DECIMALS) tx_args = { 'nonce': w3.eth.getTransactionCount(from_address), 'gas': 100000, 'gasPrice': int(float(recommend_min_gas_price_to_confirm_in_time(1)) * 10**9 * 1.4) } tx = contract.functions.transfer(address, amount).buildTransaction(tx_args) signed = w3.eth.account.signTransaction(tx, from_pk) tx_id = None success = False counter = 0 while not success: try: tx_id = w3.eth.sendRawTransaction(signed.rawTransaction).hex() success = True except Exception as e: counter +=1 if 'replacement transaction underpriced' in str(e): print(f'replacement transaction underpriced. retrying {counter}') time.sleep(WAIT_TIME_BETWEEN_PAYOUTS) elif 'nonce too low' in str(e): print(f'nonce too low. retrying {counter}') time.sleep(WAIT_TIME_BETWEEN_PAYOUTS) # rebuild txn tx_args['nonce'] = w3.eth.getTransactionCount(from_address) tx = contract.functions.transfer(address, amount).buildTransaction(tx_args) signed = w3.eth.account.signTransaction(tx, from_pk) else: raise e if not tx_id: print("cannot pay advance, did not get a txid") continue print("paid via", tx_id) # make save state to DB if is_real_payout: match.payout_tx = tx_id else: match.test_payout_tx = tx_id match.save() # wait for tx to clear while not has_tx_mined(tx_id, network): time.sleep(1) # make save state to DB if is_real_payout: match.payout_tx_date = timezone.now() grant_match_distribution_final_txn(match) else: match.test_payout_tx_date = timezone.now() grant_match_distribution_test_txn(match) match.save() # create payout obj artifacts profile = Profile.objects.get(handle__iexact='gitcoinbot') validator_comment = f"created by ingest payout_round_script" subscription = Subscription() subscription.is_postive_vote = True subscription.active = False subscription.error = True subscription.contributor_address = 'N/A' subscription.amount_per_period = match.amount subscription.real_period_seconds = 2592000 subscription.frequency = 30 subscription.frequency_unit = 'N/A' subscription.token_address = TOKEN_ADDRESS subscription.token_symbol = token_name subscription.gas_price = 0 subscription.new_approve_tx_id = '0x0' subscription.num_tx_approved = 1 subscription.network = network subscription.contributor_profile = profile subscription.grant = match.grant subscription.comments = validator_comment subscription.amount_per_period_usdt = match.amount if is_real_payout else 0 subscription.save() contrib = Contribution.objects.create( success=True, tx_cleared=True, tx_override=True, tx_id=tx_id, subscription=subscription, validator_passed=True, validator_comment=validator_comment, ) print(f"ingested {subscription.pk} / {contrib.pk}") if is_real_payout: match.payout_contribution = contrib else: match.test_payout_contribution = contrib match.save() metadata = { 'id': subscription.id, 'value_in_token': str(subscription.amount_per_period), 'value_in_usdt_now': str(round(subscription.amount_per_period_usdt,2)), 'token_name': subscription.token_symbol, 'title': subscription.grant.title, 'grant_url': subscription.grant.url, 'num_tx_approved': subscription.num_tx_approved, 'category': 'grant', } kwargs = { 'profile': profile, 'subscription': subscription, 'grant': subscription.grant, 'activity_type': 'new_grant_contribution', 'metadata': metadata, } activity = Activity.objects.create(**kwargs) if is_real_payout: comment = f"CLR Round {clr_round} Payout" comment = Comment.objects.create(profile=profile, activity=activity, comment=comment) print("SLEEPING") time.sleep(WAIT_TIME_BETWEEN_PAYOUTS) print("DONE SLEEPING")
def receive_tip_v3(request, key, txid, network): """Handle the receiving of a tip (the POST). Returns: TemplateResponse: the UI with the tip confirmed. """ these_tips = Tip.objects.filter(web3_type='v3', txid=txid, network=network) tips = these_tips.filter(metadata__reference_hash_for_receipient=key) | these_tips.filter(metadata__reference_hash_for_funder=key) tip = tips.first() is_authed = request.user.username.lower() == tip.username.lower() or request.user.username.lower() == tip.from_username.lower() or not tip.username not_mined_yet = get_web3(tip.network).eth.getBalance(Web3.toChecksumAddress(tip.metadata['address'])) == 0 did_fail = False if not_mined_yet: tip.update_tx_status() did_fail = tip.tx_status in ['dropped', 'unknown', 'na', 'error'] if not request.user.is_authenticated or request.user.is_authenticated and not getattr( request.user, 'profile', None ): login_redirect = redirect('/login/github?next=' + request.get_full_path()) return login_redirect num_redemptions = tip.metadata.get("num_redemptions", 0) max_redemptions = tip.metadata.get("max_redemptions", 0) is_redeemable = not (tip.receive_txid and (num_redemptions >= max_redemptions)) and is_authed has_this_user_redeemed = request.user.profile.tip_payouts.filter(tip=tip).count() if has_this_user_redeemed: is_redeemable = False if not is_redeemable: messages.info(request, 'This tip has been received already') elif not is_authed: messages.error(request, f'This tip is for @{tip.username} but you are logged in as @{request.user.username}. Please logout and log back in as {tip.username}.') elif did_fail: messages.info(request, f'This tx {tip.txid}, failed. Please contact the sender and ask them to send the tx again.') elif not_mined_yet: messages.info(request, f'This tx {tip.txid}, is still mining. Please wait a moment before submitting the receive form.') elif request.POST.get('receive_txid') and is_redeemable: params = request.POST # db mutations try: profile = get_profile(tip.username) eth_address = params['forwarding_address'] if not is_valid_eth_address(eth_address): eth_address = profile.preferred_payout_address if params['save_addr']: if profile: profile.preferred_payout_address = eth_address profile.save() tip.receive_txid = params['receive_txid'] tip.receive_tx_status = 'pending' tip.receive_address = eth_address tip.received_on = timezone.now() num_redemptions = tip.metadata.get("num_redemptions", 0) # note to future self: to create a tip like this in the future set # tip.username # tip.metadata.max_redemptions # tip.metadata.override_send_amount # tip.amount to the amount you want to send # ,"override_send_amount":1,"max_redemptions":29 num_redemptions += 1 tip.metadata["num_redemptions"] = num_redemptions tip.save() record_user_action(tip.from_username, 'receive_tip', tip) record_tip_activity(tip, tip.username, 'receive_tip') TipPayout.objects.create( txid=tip.receive_txid, profile=request.user.profile, tip=tip, ) messages.success(request, 'This tip has been received') is_redeemable = False has_this_user_redeemed = True except Exception as e: messages.error(request, str(e)) logger.exception(e) params = { 'issueURL': request.GET.get('source'), 'class': 'receive', 'title': _('Receive Tip'), 'gas_price': round(recommend_min_gas_price_to_confirm_in_time(120), 1), 'tip': tip, 'has_this_user_redeemed': has_this_user_redeemed, 'key': key, 'is_redeemable': is_redeemable, 'is_authed': is_authed, 'disable_inputs': not is_redeemable or not is_authed, } return TemplateResponse(request, 'onepager/receive.html', params)
def receive_bulk(request, secret): coupons = BulkTransferCoupon.objects.filter(secret=secret) if not coupons.exists(): raise Http404 coupon = coupons.first() if coupon.num_uses_remaining <= 0: messages.info(request, f'Sorry but the coupon for a free kudos has has expired. Contact the person who sent you the coupon link, or you can still purchase one on this page.') return redirect(coupon.token.url) kudos_transfer = None if request.user.is_authenticated: redemptions = BulkTransferRedemption.objects.filter(redeemed_by=request.user.profile, coupon=coupon) if redemptions.exists(): kudos_transfer = redemptions.first().kudostransfer error = False if request.POST: try: address = Web3.toChecksumAddress(request.POST.get('forwarding_address')) except: error = "You must enter a valid Ethereum address (so we know where to send your Kudos). Please try again." if request.user.is_anonymous: error = "You must login." if not error: user = request.user profile = user.profile save_addr = request.POST.get('save_addr') ip_address = get_ip(request) # handle form submission if save_addr: profile.preferred_payout_address = address profile.save() kudos_contract_address = Web3.toChecksumAddress(settings.KUDOS_CONTRACT_MAINNET) kudos_owner_address = Web3.toChecksumAddress(settings.KUDOS_OWNER_ACCOUNT) w3 = get_web3(coupon.token.contract.network) contract = w3.eth.contract(Web3.toChecksumAddress(kudos_contract_address), abi=kudos_abi()) nonce = w3.eth.getTransactionCount(kudos_owner_address) tx = contract.functions.clone(address, coupon.token.token_id, 1).buildTransaction({ 'nonce': nonce, 'gas': 500000, 'gasPrice': int(recommend_min_gas_price_to_confirm_in_time(2) * 10**9), 'value': int(coupon.token.price_finney / 1000.0 * 10**18), }) if not profile.trust_profile and profile.github_created_on > (timezone.now() - timezone.timedelta(days=7)): messages.error(request, f'Your github profile is too new. Cannot receive kudos.') else: signed = w3.eth.account.signTransaction(tx, settings.KUDOS_PRIVATE_KEY) txid = w3.eth.sendRawTransaction(signed.rawTransaction).hex() with transaction.atomic(): kudos_transfer = KudosTransfer.objects.create( emails=[request.user.email], # For kudos, `token` is a kudos.models.Token instance. kudos_token_cloned_from=coupon.token, amount=0, comments_public=coupon.comments_to_put_in_kudos_transfer, ip=ip_address, github_url='', from_name=coupon.sender_profile.handle, from_email='', from_username=coupon.sender_profile.handle, username=profile.handle, network=coupon.token.contract.network, from_address=settings.KUDOS_OWNER_ACCOUNT, is_for_bounty_fulfiller=False, metadata={'coupon_redemption': True, 'nonce': nonce}, recipient_profile=profile, sender_profile=coupon.sender_profile, txid=txid, receive_txid=txid, tx_status='pending', receive_tx_status='pending', ) # save to DB BulkTransferRedemption.objects.create( coupon=coupon, redeemed_by=profile, ip_address=ip_address, kudostransfer=kudos_transfer, ) coupon.num_uses_remaining -= 1 coupon.current_uses += 1 coupon.save() # send email maybe_market_kudos_to_email(kudos_transfer) title = f"Redeem {coupon.token.humanized_name} Kudos from @{coupon.sender_profile.handle}" desc = f"This Kudos has been AirDropped to you. About this Kudos: {coupon.token.description}" params = { 'title': title, 'card_title': title, 'card_desc': desc, 'error': error, 'avatar_url': coupon.token.img_url, 'coupon': coupon, 'user': request.user, 'is_authed': request.user.is_authenticated, 'kudos_transfer': kudos_transfer, 'tweet_text': urllib.parse.quote_plus(f"I just got a {coupon.token.humanized_name} Kudos on @GetGitcoin. ") } return TemplateResponse(request, 'transaction/receive_bulk.html', params)
def receive(request, key, txid, network): """Handle the receiving of a kudos (the POST). Returns: TemplateResponse: the UI with the kudos confirmed """ these_kudos_transfers = KudosTransfer.objects.filter(web3_type='v3', txid=txid, network=network) kudos_transfers = these_kudos_transfers.filter( Q(metadata__reference_hash_for_receipient=key) | Q(metadata__reference_hash_for_funder=key) ) kudos_transfer = kudos_transfers.first() if not kudos_transfer: raise Http404 is_authed = kudos_transfer.trust_url or request.user.username.replace('@', '') in [ kudos_transfer.username.replace('@', ''), kudos_transfer.from_username.replace('@', '') ] not_mined_yet = get_web3(kudos_transfer.network).eth.getBalance( Web3.toChecksumAddress(kudos_transfer.metadata['address'])) == 0 did_fail = False if not_mined_yet: kudos_transfer.update_tx_status() did_fail = kudos_transfer.tx_status in ['dropped', 'unknown', 'na', 'error'] if not kudos_transfer.trust_url: if not request.user.is_authenticated or request.user.is_authenticated and not getattr( request.user, 'profile', None ): login_redirect = redirect('/login/github?next=' + request.get_full_path()) return login_redirect if kudos_transfer.receive_txid: messages.info(request, _('This kudos has been received')) elif not is_authed: messages.error( request, f'This kudos is for {kudos_transfer.username} but you are logged in as {request.user.username}. Please logout and log back in as {kudos_transfer.username}.') elif did_fail: messages.info(request, f'This tx {kudos_transfer.txid}, failed. Please contact the sender and ask them to send the tx again.') elif not_mined_yet and not request.GET.get('receive_txid'): message = mark_safe( f'The <a href="https://etherscan.io/tx/{txid}">transaction</a> is still mining. ' 'Please wait a moment before submitting the receive form.' ) messages.info(request, message) elif request.GET.get('receive_txid') and not kudos_transfer.receive_txid: params = request.GET # db mutations try: if params['save_addr']: profile = get_profile(kudos_transfer.username.replace('@', '')) if profile: # TODO: Does this mean that the address the user enters in the receive form # Will overwrite an already existing preferred_payout_address? Should we # ask the user to confirm this? profile.preferred_payout_address = params['forwarding_address'] profile.save() kudos_transfer.receive_txid = params['receive_txid'] kudos_transfer.receive_address = params['forwarding_address'] kudos_transfer.received_on = timezone.now() if request.user.is_authenticated: kudos_transfer.recipient_profile = request.user.profile kudos_transfer.save() record_user_action(kudos_transfer.from_username, 'receive_kudos', kudos_transfer) record_kudos_email_activity(kudos_transfer, kudos_transfer.username, 'receive_kudos') record_kudos_activity( kudos_transfer, kudos_transfer.from_username, 'new_kudos' if kudos_transfer.username else 'new_crowdfund' ) messages.success(request, _('This kudos has been received')) except Exception as e: messages.error(request, str(e)) logger.exception(e) params = { 'issueURL': request.GET.get('source'), 'class': 'receive', 'gas_price': round(recommend_min_gas_price_to_confirm_in_time(120), 1), 'kudos_transfer': kudos_transfer, 'title': f"Receive {kudos_transfer.kudos_token_cloned_from.humanized_name} Kudos" if kudos_transfer and kudos_transfer.kudos_token_cloned_from else _('Receive Kudos'), 'avatar_url': kudos_transfer.kudos_token_cloned_from.img_url if kudos_transfer and kudos_transfer.kudos_token_cloned_from else None, 'card_desc': f"You've received a {kudos_transfer.kudos_token_cloned_from.humanized_name} kudos!" if kudos_transfer and kudos_transfer.kudos_token_cloned_from else _('You\'ve received a kudos'), 'key': key, 'is_authed': is_authed, 'disable_inputs': kudos_transfer.receive_txid or not_mined_yet or not is_authed, 'tweet_text': urllib.parse.quote_plus(f"I just got a {kudos_transfer.kudos_token_cloned_from.humanized_name} Kudos on @GetGitcoin. ") } return TemplateResponse(request, 'transaction/receive.html', params)