def set_resolver(signer, github_handle, nonce): ns = ENS.fromWeb3(w3) resolver_addr = ns.address('resolver.eth') signer = Web3.toChecksumAddress(signer) txn_hash = None gasPrice = recommend_min_gas_price_to_confirm_in_time( 1) * 10**9 if not settings.DEBUG else 15 * 10**9 subdomain = f"{github_handle}.{settings.ENS_TLD}" transaction = { 'from': Web3.toChecksumAddress(settings.ENS_OWNER_ACCOUNT), 'value': 0, 'nonce': nonce, 'gas': 100000, 'gasPrice': gasPrice } ens_contract = w3.eth.contract( address=ENS_MAINNET_ADDR, abi=ens_abi, ) txn = ens_contract.functions.setResolver( dot_eth_namehash(subdomain), resolver_addr, ).buildTransaction(transaction) signed_txn = w3.eth.account.signTransaction( txn, private_key=settings.ENS_PRIVATE_KEY) txn_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction) # hack to convert # "b'7bce7e4bcd2fea4d26f3d254bb8cf52b9ee8dd7353b19bfbc86803c27d9bbf39'" # to "0x7bce7e4bcd2fea4d26f3d254bb8cf52b9ee8dd7353b19bfbc86803c27d9bbf39" txn_hash = str(binascii.b2a_hex(txn_hash)).replace("b'", "0x").replace("'", "") return txn_hash
def new_bounty(request): """Create a new bounty.""" issue_url = request.GET.get('source') or request.GET.get('url', '') params = { 'issueURL': issue_url, 'active': 'submit_bounty', 'title': 'Create Funded Issue', 'recommend_gas_price': recommend_min_gas_price_to_confirm_in_time( confirm_time_minutes_target), 'eth_usd_conv_rate': eth_usd_conv_rate(), 'conf_time_spread': conf_time_spread(), 'from_email': request.session.get('email', ''), 'from_handle': request.session.get('handle', '') } return TemplateResponse(request, 'submit_bounty.html', params)
def mint(self, gas_price_gwei=None): """Approve / mint this token.""" from kudos.management.commands.mint_all_kudos import mint_kudos # avoid circular import from kudos.utils import KudosContract # avoid circular import account = settings.KUDOS_OWNER_ACCOUNT private_key = settings.KUDOS_PRIVATE_KEY kudos = { 'name': self.name, 'description': self.description, 'priceFinney': self.priceFinney, 'artist': self.artist, 'platform': self.name, 'platform': self.platform, 'numClonesAllowed': self.numClonesAllowed, 'tags': self.tags, 'artwork_url': self.artwork_url, } kudos_contract = KudosContract(network=self.network) gas_price_gwei = recommend_min_gas_price_to_confirm_in_time(1) * 2 if not gas_price_gwei else None tx_id = mint_kudos(kudos_contract, kudos, account, private_key, gas_price_gwei, mint_to=None, live=True, dont_wait_for_kudos_id_return_tx_hash_instead=True) self.processed = True self.approved = True self.save() return tx_id
def mint_token_request(self, token_req_id, retry=False): """ :param self: :param token_req_id: :return: """ with redis.lock("tasks:all_token_mint_requests", timeout=LOCK_TIMEOUT): with redis.lock("tasks:token_req_id:%s" % token_req_id, timeout=LOCK_TIMEOUT): from kudos.management.commands.mint_all_kudos import sync_latest from gas.utils import recommend_min_gas_price_to_confirm_in_time from dashboard.utils import has_tx_mined obj = TokenRequest.objects.get(pk=token_req_id) multiplier = 1 if not retry else (mint_token_request.request.retries + 1) gas_price = int(recommend_min_gas_price_to_confirm_in_time(1) * multiplier) tx_id = obj.mint(gas_price) if tx_id: while not has_tx_mined(tx_id, obj.network): time.sleep(1) sync_latest(0) sync_latest(1) sync_latest(2) sync_latest(3) else: self.retry(30)
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 == tip.username or request.user.username == tip.from_username not_mined_yet = get_web3(tip.network).eth.getBalance( Web3.toChecksumAddress(tip.metadata['address'])) == 0 if not request.user.is_authenticated or request.user.is_authenticated and not getattr( request.user, 'profile'): login_redirect = redirect('/login/github?next=' + request.get_full_path()) return login_redirect elif tip.receive_txid: messages.info(request, 'This tip has been received') 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 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.GET.get('receive_txid') and not tip.receive_txid: params = request.GET # db mutations try: if params['save_addr']: profile = get_profile(tip.username) if profile: profile.preferred_payout_address = params[ 'forwarding_address'] profile.save() tip.receive_txid = params['receive_txid'] tip.receive_address = params['forwarding_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(120), 1), 'tip': tip, 'key': key, 'is_authed': is_authed, 'disable_inputs': tip.receive_txid or not_mined_yet or not is_authed, } return TemplateResponse(request, 'onepager/receive.html', params)
def handle(self, *args, **options): # setup payment_threshold_usd = 1 network = 'mainnet' if not settings.DEBUG else 'rinkeby' from_address = settings.MINICLR_ADDRESS from_pk = settings.MINICLR_PRIVATE_KEY DAI_ADDRESS = '0x6b175474e89094c44da98b954eedeac495271d0f' if network == 'mainnet' else '0x8f2e097e79b1c51be9cba42658862f0192c3e487' # find a round that has recently expired minutes_ago = options['minutes_ago'] cursor_time = timezone.now() - timezone.timedelta(minutes=minutes_ago) mr = MatchRound.objects.filter(valid_from__lt=cursor_time, valid_to__gt=cursor_time, valid_to__lt=timezone.now()).first() if options['round_number']: mr = MatchRound.objects.get(number=options['round_number']) if not mr: print( f'No Match Round Found that ended between {cursor_time} <> {timezone.now()}' ) return print(mr) # finalize rankings if options['what'] == 'finalize': rankings = mr.ranking.filter(final=False, paid=False).order_by('-match_total') print(rankings.count(), "to finalize") for ranking in rankings: ranking.final = True ranking.save() print(rankings.count(), " finalied") # payout rankings (round must be finalized first) if options['what'] == 'payout': rankings = mr.ranking.filter(final=True, paid=False).order_by('-match_total') print(rankings.count(), " to pay") w3 = get_web3(network) for ranking in rankings: # figure out amount_owed profile = ranking.profile owed_rankings = profile.match_rankings.filter(final=True, paid=False) amount_owed = sum( owed_rankings.values_list('match_total', flat=True)) print( f"paying {ranking.profile.handle} who is owed {amount_owed} ({ranking.match_total} from this round)" ) # validate error = None if amount_owed < payment_threshold_usd: error = ("- less than amount owed; continue") address = profile.preferred_payout_address if not address: error = ("- address not on file") if error: print(error) ranking.payout_tx_status = error ranking.save() continue # issue payment contract = w3.eth.contract(Web3.toChecksumAddress(DAI_ADDRESS), abi=abi) address = Web3.toChecksumAddress(address) amount = int(amount_owed * 10**18) tx = contract.functions.transfer( address, amount ).buildTransaction({ 'nonce': w3.eth.getTransactionCount(from_address), 'gas': 100000, 'gasPrice': int( float(recommend_min_gas_price_to_confirm_in_time(1)) * 10**9 * 1.4) }) signed = w3.eth.account.signTransaction(tx, from_pk) tx_id = w3.eth.sendRawTransaction(signed.rawTransaction).hex() print("paid via", tx_id) # wait for tx to clear while not has_tx_mined(tx_id, network): time.sleep(1) ranking.payout_tx_status, ranking.payout_tx_issued = get_tx_status( tx_id, network, timezone.now()) ranking.paid = True ranking.payout_txid = tx_id ranking.save() for other_ranking in owed_rankings: other_ranking.paid = True other_ranking.payout_txid = ranking.payout_txid other_ranking.payout_tx_issued = ranking.payout_tx_issued other_ranking.payout_tx_status = ranking.payout_tx_status other_ranking.save() # create earning object from_profile = Profile.objects.get(handle='gitcoinbot') Earning.objects.update_or_create( source_type=ContentType.objects.get(app_label='townsquare', model='matchranking'), source_id=ranking.pk, defaults={ "created_on": ranking.created_on, "org_profile": None, "from_profile": from_profile, "to_profile": ranking.profile, "value_usd": amount_owed, "url": 'https://gitcoin.co/#clr', "network": network, }) Activity.objects.create(created_on=timezone.now(), profile=ranking.profile, activity_type='mini_clr_payout', metadata={ "amount": float(amount_owed), "number": int(mr.number), "mr_pk": int(mr.pk), "round_description": f"Mini CLR Round {mr.number}" }) from marketing.mails import match_distribution match_distribution(ranking) print("paid ", ranking) time.sleep(30) # announce finalists (round must be finalized first) from_profile = Profile.objects.get(handle='gitcoinbot') if options['what'] == 'announce': copy = f"Mini CLR Round {mr.number} Winners:<BR>" rankings = mr.ranking.filter( final=True).order_by('-match_total')[0:10] print(rankings.count(), " to announce") for ranking in rankings: profile_link = f"<a href=/{ranking.profile}>@{ranking.profile}</a>" copy += f" - {profile_link} was ranked <strong>#{ranking.number}</strong>. <BR>" metadata = { 'copy': copy, } Activity.objects.create( created_on=timezone.now(), profile=from_profile, activity_type='consolidated_mini_clr_payout', metadata=metadata)
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: raise PermissionDenied 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 if request.POST: address = Web3.toChecksumAddress( request.POST.get('forwarding_address')) 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()) tx = contract.functions.clone( address, coupon.token.token_id, 1).buildTransaction({ 'nonce': get_nonce(coupon.token.contract.network, kudos_owner_address), 'gas': 500000, 'gasPrice': int(recommend_min_gas_price_to_confirm_in_time(5) * 10**9), 'value': int(coupon.token.price_finney / 1000.0 * 10**18), }) 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}, recipient_profile=profile, sender_profile=coupon.sender_profile, txid=txid, receive_txid=txid, ) # 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 title = f"Redeem AirDropped *{coupon.token.humanized_name}* Kudos" desc = f"This Kudos has been AirDropped to you. About this Kudos: {coupon.token.description}" params = { 'title': title, 'card_title': title, 'card_desc': desc, 'avatar_url': coupon.token.img_url, 'coupon': coupon, 'user': request.user, 'is_authed': request.user.is_authenticated, 'kudos_transfer': kudos_transfer, } return TemplateResponse(request, 'transaction/receive_bulk.html', params)
def gas_calculator(request): recommended_gas_price = recommend_min_gas_price_to_confirm_in_time(confirm_time_minutes_target) _cts = conf_time_spread() actions = [{ 'name': _('New Bounty'), 'target': '/new', 'persona': 'funder', 'product': 'bounties', }, { 'name': _('Fulfill Bounty'), 'target': 'issue/fulfill', 'persona': 'developer', 'product': 'bounties', }, { 'name': _('Increase Funding'), 'target': 'issue/increase', 'persona': 'funder', 'product': 'bounties', }, { 'name': _('Accept Submission'), 'target': 'issue/accept', 'persona': 'funder', 'product': 'bounties', }, { 'name': _('Cancel Funding'), 'target': 'issue/cancel', 'persona': 'funder', 'product': 'bounties', }, { 'name': _('Send tip'), 'target': 'tip/send/2/', 'persona': 'funder', 'product': 'tips', }, { 'name': _('Receive tip'), 'target': 'tip/receive', 'persona': 'developer', 'product': 'tips', }, { 'name': _('Create Grant'), 'target': 'grants/new', 'persona': 'developer', 'product': 'grants', }, { 'name': _('Fund Grant'), 'target': 'grants/fund', 'persona': 'funder', 'product': 'grants', }, { 'name': _('Cancel Grant Funding'), 'target': 'grants/cancel', 'persona': 'funder', 'product': 'grants', }] context = { 'title': _('Live Ethereum (ETH) Gas Calculator'), 'card_desc': _('See what popular Gitcoin methods cost at different Gas Prices'), 'actions': actions, 'conf_time_spread': _cts, 'eth_to_usd': round(convert_amount(1, 'ETH', 'USDT'), 0), 'start_gas_cost': recommended_gas_price, 'hide_send_tip': True, } return TemplateResponse(request, 'gas_calculator.html', context)
def send_tip_2(request): """Handle the second stage of sending a tip. TODO: * Convert this view-based logic to a django form. Returns: JsonResponse: If submitting tip, return response with success state. TemplateResponse: Render the submission form. """ from_username = request.session.get('handle', '') primary_from_email = request.session.get('email', '') access_token = request.session.get('access_token') to_emails = [] if request.body: # http response response = { 'status': 'OK', 'message': 'Notification has been sent', } params = json.loads(request.body) to_username = params['username'].lstrip('@') try: to_profile = Profile.objects.get(handle__iexact=to_username) if to_profile.email: to_emails.append(to_profile.email) if to_profile.github_access_token: to_emails = get_github_emails(to_profile.github_access_token) except Profile.DoesNotExist: pass if params.get('email'): to_emails.append(params['email']) # If no primary email in session, try the POST data. If none, fetch from GH. if params.get('fromEmail'): primary_from_email = params['fromEmail'] elif access_token and not primary_from_email: primary_from_email = get_github_primary_email(access_token) to_emails = list(set(to_emails)) expires_date = timezone.now() + timezone.timedelta( seconds=params['expires_date']) # db mutations tip = Tip.objects.create( emails=to_emails, url=params['url'], tokenName=params['tokenName'], amount=params['amount'], comments_priv=params['comments_priv'], comments_public=params['comments_public'], ip=get_ip(request), expires_date=expires_date, github_url=params['github_url'], from_name=params['from_name'], from_email=params['from_email'], from_username=from_username, username=params['username'], network=params['network'], tokenAddress=params['tokenAddress'], txid=params['txid'], from_address=params['from_address'], ) # notifications maybe_market_tip_to_github(tip) maybe_market_tip_to_slack(tip, 'new_tip') maybe_market_tip_to_email(tip, to_emails) if not to_emails: response['status'] = 'error' response[ 'message'] = 'Uh oh! No email addresses for this user were found via Github API. Youll have to let the tipee know manually about their tip.' return JsonResponse(response) params = { 'issueURL': request.GET.get('source'), 'class': 'send2', 'title': 'Send Tip', 'recommend_gas_price': recommend_min_gas_price_to_confirm_in_time( confirm_time_minutes_target), 'from_email': primary_from_email, 'from_handle': from_username, } return TemplateResponse(request, 'yge/send2.html', params)
def send_2(request): """Handle the first start of the Kudos email send. This form is filled out before the 'send' button is clicked. """ if not request.user.is_authenticated or request.user.is_authenticated and not getattr( request.user, 'profile', None): return redirect('/login/github/?next=' + request.get_full_path()) _id = request.GET.get('id') if _id and not str(_id).isdigit(): raise Http404 username = request.GET.get('username', None) user = {} if username: profiles = Profile.objects.filter(handle=username.lower()) if profiles.exists(): profile = profiles.first() user['id'] = profile.id user['text'] = profile.handle if profile.avatar_baseavatar_related.exists(): user['avatar_id'] = profile.avatar_baseavatar_related.first( ).pk user['avatar_url'] = profile.avatar_baseavatar_related.first( ).avatar_url user[ 'preferred_payout_address'] = profile.preferred_payout_address kudos = Token.objects.filter(pk=_id).first() if kudos and not kudos.send_enabled_for(request.user): messages.error(request, f'This kudos is not available to be sent.') return redirect(kudos.url) params = { 'active': 'send', 'issueURL': request.GET.get('source'), 'avatar_url': request.build_absolute_uri( static('v2/images/twitter_cards/tw_cards-06.png')), 'class': 'send2', 'recommend_gas_price': recommend_min_gas_price_to_confirm_in_time( confirm_time_minutes_target), 'from_email': getattr(request.user, 'email', ''), 'from_handle': request.user.username, 'title': _('Send Kudos | Gitcoin'), 'card_desc': _('Send a Kudos to any github user at the click of a button.'), 'numbers': range(1, 100), 'kudos': kudos, 'username': username, } if user: params['user_json'] = user return TemplateResponse(request, 'transaction/send.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 subscription_cancel(request, grant_id, grant_slug, subscription_id): """Handle the cancellation of a grant subscription.""" subscription = Subscription.objects.select_related('grant').get( pk=subscription_id) grant = getattr(subscription, 'grant', None) now = datetime.datetime.now() profile = get_profile(request) if not subscription.active: params = { 'active': 'grant_error', 'title': _('Grant Subscription Cancelled'), 'grant': grant } if grant.active: params['text'] = _( 'This Grant subscription has already been cancelled.') else: params['text'] = _( 'This Subscription is already cancelled as the grant is not longer active.' ) return TemplateResponse(request, 'grants/shared/error.html', params) if request.method == 'POST' and ( profile == subscription.contributor_profile or request.user.has_perm('grants.change_subscription')): subscription.end_approve_tx_id = request.POST.get( 'sub_end_approve_tx_id', '') subscription.cancel_tx_id = request.POST.get('sub_cancel_tx_id', '') subscription.active = False subscription.save() support_cancellation(grant, subscription) messages.info( request, _('Your subscription has been canceled. We hope you continue to support other open source projects!' )) return redirect(reverse('grants:details', args=(grant.pk, grant.slug))) params = { 'active': 'cancel_grant', 'title': _('Cancel Grant Subscription'), 'card_desc': _('Provide sustainable funding for Open Source with Gitcoin Grants'), 'subscription': subscription, 'grant': grant, 'now': now, 'keywords': get_keywords(), 'recommend_gas_price': recommend_min_gas_price_to_confirm_in_time(4), 'recommend_gas_price_slow': recommend_min_gas_price_to_confirm_in_time(120), 'recommend_gas_price_avg': recommend_min_gas_price_to_confirm_in_time(15), 'recommend_gas_price_fast': recommend_min_gas_price_to_confirm_in_time(1), 'eth_usd_conv_rate': eth_usd_conv_rate(), 'conf_time_spread': conf_time_spread(), 'gas_advisories': gas_advisories(), } return TemplateResponse(request, 'grants/cancel.html', params)
def sell_token(self, exchangeAddress, tokenSymbol): """Swap total balance of ERC-20 token associated with Uniswap exchange address to ETH. Args: exchangeAddress (str): The address of the Uniswap exchange contract assocciated with the ERC-20 token to convert to ETH tokenSymbol (str): The symbol for the ERC-20 token to be converted to ETH. Note: This currently only works with the BAT Uniswap exchange on Rinkeby Testnet """ if (self.network == 'rinkeby'): chain = 4 else: chain = 1 # Check for previous failed transactions in database failure_count = CurrencyConversion.objects.filter(from_token_symbol=tokenSymbol).exclude(transaction_result='success').count() if failure_count == 0: exchangeContract = self.web3.eth.contract(address = exchangeAddress, abi = self.exchangeABI) tokenAddress = exchangeContract.functions.tokenAddress().call() # Follows Uniswap doc guidance on calculating conversion tokenContract = self.web3.eth.contract(address = tokenAddress, abi = self.tokenABI) walletBalance = tokenContract.functions.balanceOf(settings.FEE_ADDRESS).call() outputReserve = self.web3.eth.getBalance(exchangeAddress) inputReserve = tokenContract.functions.balanceOf(exchangeAddress).call() numerator = walletBalance*outputReserve * (1-settings.UNISWAP_LIQUIDITY_FEE)*1000 denominator = inputReserve*1000 + walletBalance*(1-settings.UNISWAP_LIQUIDITY_FEE)*1000 outputAmount = numerator / denominator deadline = self.web3.eth.getBlock('latest')['timestamp'] + settings.UNISWAP_TRADE_DEADLINE logger.info('Current token balance is : ' + str(self.web3.fromWei(walletBalance,'ether'))) logger.info('Amount of ETH to be bought is :' + str(self.web3.fromWei(outputAmount,'ether'))) logger.info('Exchange rate is : ' + str(outputAmount/walletBalance)+ ' ETH/token') # Call contract function to give exchange approval to spend ERC-20 token balance. # Required to have exchange perform ERC-20 token transactions on behalf of FEE_ADDRESS nonce = self.web3.eth.getTransactionCount(settings.FEE_ADDRESS) txn_dict = exchangeContract.functions.approve(settings.FEE_ADDRESS,self.web3.toWei(walletBalance,'wei')).buildTransaction({ 'chainId': chain, 'gas': 300000, 'gasPrice': self.web3.toWei(recommend_min_gas_price_to_confirm_in_time(settings.UNISWAP_TRADE_DEADLINE/60),'gwei'), 'nonce':nonce, }) logger.info(txn_dict) signed_txn = self.web3.eth.account.signTransaction(txn_dict,private_key=settings.FEE_ADDRESS_PRIVATE_KEY) result = self.web3.eth.sendRawTransaction(signed_txn.rawTransaction) tx_receipt = self.web3.eth.getTransactionReceipt(result) count = 0 while tx_receipt is None and (count < 30): time.sleep(10) tx_receipt = self.web3.eth.getTransactionReceipt(result) logger.info(str(tx_receipt)) # Submit token -> ETH exchange trade to Uniswap. Transaction only works for BAT exchange on Rinkeby. nonce = self.web3.eth.getTransactionCount(settings.FEE_ADDRESS) txn_dict = exchangeContract.functions.tokenToEthSwapInput(self.web3.toWei(walletBalance,'wei'),self.web3.toWei(outputAmount*(1-settings.SLIPPAGE),'wei'),deadline=deadline).buildTransaction({ 'chainId': chain, 'gas': 300000, 'gasPrice': self.web3.toWei(recommend_min_gas_price_to_confirm_in_time(settings.UNISWAP_TRADE_DEADLINE/60),'gwei'), 'nonce':nonce, }) signed_txn = self.web3.eth.account.signTransaction(txn_dict,private_key=settings.FEE_ADDRESS_PRIVATE_KEY) result = self.web3.eth.sendRawTransaction(signed_txn.rawTransaction) tx_receipt = self.web3.eth.getTransactionReceipt(result) count = 0 while tx_receipt is None and (count < 30): time.sleep(10) tx_receipt = self.web3.eth.getTransactionReceipt(result) logger.info(str(tx_receipt)) if tx_receipt['status'] == 1: # Post transaction record to database if transaction succeeded transaction_record = CurrencyConversion.objects.create(transaction_date=now(),from_amount=walletBalance, to_amount=outputAmount,conversion_rate=outputAmount/walletBalance,txid=self.web3.toHex(tx_receipt['transactionHash']),from_token_addr=tokenAddress,from_token_symbol=tokenSymbol,to_token_symbol='ETH',transaction_result='success') else: # Post failed transaction record to database if transaction failed transaction_record = CurrencyConversion.objects.create(transaction_date=now(),from_amount=walletBalance, to_amount=outputAmount,conversion_rate=outputAmount/walletBalance,txid=self.web3.toHex(tx_receipt['transactionHash']),from_token_addr=tokenAddress,from_token_symbol=tokenSymbol,to_token_symbol='ETH',transaction_result='failure') # Email Gitcoin staff if transaction failed mail = Mail(Email(settings.CONTACT_EMAIL),'Failed fee conversion', Email(settings.CONTACT_EMAIL),Content('text/plain', tokenSymbol+' conversion to ETH failed')) response = self.sg.client.mail.send.post(request_body=mail.get()) else: # Email Gitcoin staff if token balance exists in wallet where previous attempt convert to ETH failed mail = Mail(Email('*****@*****.**'),'Token in Fee Wallet with previous failed fee conversion', Email(settings.CONTACT_EMAIL),Content('text/plain', tokenSymbol+' conversion to ETH failed previously so no conversion was attempted.')) response = self.sg.client.mail.send.post(request_body=mail.get())
def grant_fund(request, grant_id, grant_slug): """Handle grant funding.""" try: grant = Grant.objects.get(pk=grant_id, slug=grant_slug) except Grant.DoesNotExist: raise Http404 profile = get_profile(request) if not grant.active: params = { 'active': 'grant_error', 'title': _('Grant Ended'), 'grant': grant, 'text': _('This Grant is not longer active.') } return TemplateResponse(request, 'grants/shared/error.html', params) if grant.admin_profile == profile: params = { 'active': 'grant_error', 'title': _('Invalid Grant Subscription'), 'grant': grant, 'text': _('You cannot fund your own Grant.') } return TemplateResponse(request, 'grants/shared/error.html', params) active_subscription = Subscription.objects.select_related('grant').filter( grant=grant_id, active=True, contributor_profile=request.user.profile) if active_subscription: params = { 'active': 'grant_error', 'title': _('Subscription Exists'), 'grant': grant, 'text': _('You already have an active subscription for this grant.') } return TemplateResponse(request, 'grants/shared/error.html', params) if request.method == 'POST': subscription = Subscription() subscription.subscription_hash = request.POST.get( 'subscription_hash', '') subscription.contributor_signature = request.POST.get('signature', '') subscription.contributor_address = request.POST.get( 'contributor_address', '') subscription.amount_per_period = request.POST.get( 'amount_per_period', 0) subscription.real_period_seconds = request.POST.get( 'real_period_seconds', 2592000) subscription.frequency = request.POST.get('frequency', 30) subscription.frequency_unit = request.POST.get('frequency_unit', 'days') subscription.token_address = request.POST.get('denomination', '') subscription.token_symbol = request.POST.get('token_symbol', '') subscription.gas_price = request.POST.get('gas_price', 0) subscription.new_approve_tx_id = request.POST.get( 'sub_new_approve_tx_id', '') subscription.network = request.POST.get('network', '') subscription.contributor_profile = profile subscription.grant = grant subscription.save() new_supporter(grant, subscription) thank_you_for_supporting(grant, subscription) return redirect(reverse('grants:details', args=(grant.pk, grant.slug))) params = { 'active': 'fund_grant', 'title': _('Fund Grant'), 'card_desc': _('Provide sustainable funding for Open Source with Gitcoin Grants'), 'subscription': {}, 'grant_has_no_token': True if grant.token_address == '0x0000000000000000000000000000000000000000' else False, 'grant': grant, 'keywords': get_keywords(), 'recommend_gas_price': recommend_min_gas_price_to_confirm_in_time(4), 'recommend_gas_price_slow': recommend_min_gas_price_to_confirm_in_time(120), 'recommend_gas_price_avg': recommend_min_gas_price_to_confirm_in_time(15), 'recommend_gas_price_fast': recommend_min_gas_price_to_confirm_in_time(1), 'eth_usd_conv_rate': eth_usd_conv_rate(), 'conf_time_spread': conf_time_spread(), 'gas_advisories': gas_advisories(), } return TemplateResponse(request, 'grants/fund.html', params)
def grant_new(request): """Handle new grant.""" if not request.user.has_perm('grants.add_grant'): messages.info( request, _('Grants is still in beta. To create a Grant ping us at [email protected]' )) return redirect(reverse('grants:grants')) profile = get_profile(request) if request.method == 'POST': logo = request.FILES.get('input_image', None) receipt = json.loads(request.POST.get('receipt', '{}')) team_members = request.POST.getlist('team_members[]') grant_kwargs = { 'title': request.POST.get('input_title', ''), 'description': request.POST.get('description', ''), 'reference_url': request.POST.get('reference_url', ''), 'admin_address': request.POST.get('admin_address', ''), 'contract_owner_address': request.POST.get('contract_owner_address', ''), 'token_address': request.POST.get('denomination', ''), 'token_symbol': request.POST.get('token_symbol', ''), 'amount_goal': request.POST.get('amount_goal', 1), 'contract_version': request.POST.get('contract_version', ''), 'deploy_tx_id': request.POST.get('transaction_hash', ''), 'contract_address': request.POST.get('contract_address', ''), 'network': request.POST.get('network', 'mainnet'), 'metadata': receipt, 'admin_profile': profile, 'logo': logo, } grant = Grant.objects.create(**grant_kwargs) new_grant(grant, profile) team_members.append(profile.id) grant.team_members.add(*list( filter(lambda member_id: member_id > 0, map(int, team_members)))) return redirect(reverse('grants:details', args=(grant.pk, grant.slug))) params = { 'active': 'new_grant', 'title': _('New Grant'), 'card_desc': _('Provide sustainable funding for Open Source with Gitcoin Grants'), 'profile': profile, 'grant': {}, 'keywords': get_keywords(), 'recommend_gas_price': recommend_min_gas_price_to_confirm_in_time(4), 'recommend_gas_price_slow': recommend_min_gas_price_to_confirm_in_time(120), 'recommend_gas_price_avg': recommend_min_gas_price_to_confirm_in_time(15), 'recommend_gas_price_fast': recommend_min_gas_price_to_confirm_in_time(1), 'eth_usd_conv_rate': eth_usd_conv_rate(), 'conf_time_spread': conf_time_spread(), 'gas_advisories': gas_advisories(), } return TemplateResponse(request, 'grants/new.html', params)
def grant_details(request, grant_id, grant_slug): """Display the Grant details page.""" profile = get_profile(request) try: grant = Grant.objects.prefetch_related('subscriptions', 'milestones', 'updates').get(pk=grant_id, slug=grant_slug) milestones = grant.milestones.order_by('due_date') updates = grant.updates.order_by('-created_on') subscriptions = grant.subscriptions.filter(active=True, error=False) user_subscription = grant.subscriptions.filter( contributor_profile=profile, active=True).first() except Grant.DoesNotExist: raise Http404 if request.method == 'POST' and (profile == grant.admin_profile or request.user.is_staff): if 'contract_address' in request.POST: grant.cancel_tx_id = request.POST.get('grant_cancel_tx_id', '') grant.active = False grant.save() grant_cancellation(grant, user_subscription) for sub in subscriptions: subscription_terminated(grant, sub) elif 'input-title' in request.POST: update_kwargs = { 'title': request.POST.get('input-title', ''), 'description': request.POST.get('description', ''), 'grant': grant } Update.objects.create(**update_kwargs) elif 'contract_owner_address' in request.POST: grant.contract_owner_address = request.POST.get( 'contract_owner_address') grant.save() return redirect( reverse('grants:details', args=(grant.pk, grant.slug))) elif 'edit-title' in request.POST: grant.title = request.POST.get('edit-title') grant.reference_url = request.POST.get('edit-reference_url') form_profile = request.POST.get('edit-admin_profile') admin_profile = Profile.objects.get(handle=form_profile) grant.admin_profile = admin_profile grant.description = request.POST.get('edit-description') team_members = request.POST.getlist('edit-grant_members[]') team_members.append(str(admin_profile.id)) grant.team_members.set(team_members) grant.save() return redirect( reverse('grants:details', args=(grant.pk, grant.slug))) params = { 'active': 'grant_details', 'grant': grant, 'title': grant.title, 'card_desc': grant.description, 'avatar_url': grant.logo.url if grant.logo else None, 'subscriptions': subscriptions, 'user_subscription': user_subscription, 'is_admin': (grant.admin_profile.id == profile.id) if profile and grant.admin_profile else False, 'grant_is_inactive': not grant.active, 'updates': updates, 'milestones': milestones, 'keywords': get_keywords(), 'recommend_gas_price': recommend_min_gas_price_to_confirm_in_time(4), 'recommend_gas_price_slow': recommend_min_gas_price_to_confirm_in_time(120), 'recommend_gas_price_avg': recommend_min_gas_price_to_confirm_in_time(15), 'recommend_gas_price_fast': recommend_min_gas_price_to_confirm_in_time(1), 'eth_usd_conv_rate': eth_usd_conv_rate(), 'conf_time_spread': conf_time_spread(), 'gas_advisories': gas_advisories(), } return TemplateResponse(request, 'grants/detail.html', params)
def send_tip_2(request): if request.body != '': status = 'OK' message = 'Notification has been sent' params = json.loads(request.body) emails = [] #basic validation username = params['username'] #get emails if params['email']: emails.append(params['email']) gh_user = get_github_user(username) user_full_name = gh_user['name'] if gh_user.get('email', False): emails.append(gh_user['email']) gh_user_events = get_github_user(username, '/events/public') for event in gh_user_events: commits = event.get('payload', {}).get('commits', []) for commit in commits: email = commit.get('author', {}).get('email', None) #print(event['actor']['display_login'].lower() == username.lower()) #print(commit['author']['name'].lower() == user_full_name.lower()) #print('========') if email and \ event['actor']['display_login'].lower() == username.lower() and \ commit['author']['name'].lower() == user_full_name.lower() and \ 'noreply.github.com' not in email and \ email not in emails: emails.append(email) expires_date = timezone.now() + timezone.timedelta( seconds=params['expires_date']) #db mutations tip = Tip.objects.create( emails=emails, url=params['url'], tokenName=params['tokenName'], amount=params['amount'], comments_priv=params['comments_priv'], comments_public=params['comments_public'], ip=get_ip(request), expires_date=expires_date, github_url=params['github_url'], from_name=params['from_name'], from_email=params['from_email'], username=params['username'], network=params['network'], tokenAddress=params['tokenAddress'], txid=params['txid'], ) #notifications did_post_to_github = maybe_market_tip_to_github(tip) maybe_market_tip_to_slack(tip, 'new_tip', tip.txid) maybe_market_tip_to_email(tip, emails) if len(emails) == 0: status = 'error' message = 'Uh oh! No email addresses for this user were found via Github API. Youll have to let the tipee know manually about their tip.' #http response response = { 'status': status, 'message': message, } return JsonResponse(response) params = { 'issueURL': request.GET.get('source'), 'class': 'send2', 'title': 'Send Tip', 'recommend_gas_price': recommend_min_gas_price_to_confirm_in_time( confirm_time_minutes_target), } return TemplateResponse(request, 'yge/send2.html', params)
def redeem_bulk_coupon(coupon, profile, address, ip_address, save_addr=False, submit_later=False, exit_after_sending_tx=False, max_gas_price_we_are_willing_to_pay_gwei=15): try: address = Web3.toChecksumAddress(address) except: error = "You must enter a valid Ethereum address (so we know where to send your Kudos). Please try again." return None, error, None # handle form submission kudos_transfer = None if save_addr: profile.preferred_payout_address = address profile.save() private_key = settings.KUDOS_PRIVATE_KEY if not coupon.sender_pk else coupon.sender_pk kudos_owner_address = settings.KUDOS_OWNER_ACCOUNT if not coupon.sender_address else coupon.sender_address gas_price_confirmation_time = 1 if not coupon.sender_address else 60 gas_price_multiplier = 1.3 if not coupon.sender_address else 1 kudos_contract_address = Web3.toChecksumAddress( settings.KUDOS_CONTRACT_MAINNET) kudos_owner_address = Web3.toChecksumAddress(kudos_owner_address) 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) gas_price = int( int( recommend_min_gas_price_to_confirm_in_time( gas_price_confirmation_time) * 10**9) * gas_price_multiplier) tx = contract.functions.clone( address, coupon.token.token_id, 1).buildTransaction({ 'nonce': nonce, 'gas': 500000, 'gasPrice': gas_price, 'value': int(coupon.token.price_finney / 1000.0 * 10**18), }) if not profile.is_brightid_verified and not profile.is_twitter_verified and not profile.trust_profile and profile.github_created_on > ( timezone.now() - timezone.timedelta(days=7)): error = f'Your github profile is too new, so you cannot receive kudos. Please verifiy your profile on BrightID and/or Twitter to proceed.' return None, error, None else: if profile.bulk_transfer_redemptions.filter(coupon=coupon).exists(): error = f'You have already redeemed this kudos.' return None, error, None signed = w3.eth.account.signTransaction(tx, private_key) retry_later = False tx_status = 'pending' if submit_later: txid = '' tx_status = 'not_subed' else: try: # TODO - in the future, override this if the user pays for expediated processing if recommend_min_gas_price_to_confirm_in_time( 1) > max_gas_price_we_are_willing_to_pay_gwei: if coupon.token.contract.network == 'mainnet': raise Exception( "gas price is too high. try again when its not pls" ) txid = w3.eth.sendRawTransaction(signed.rawTransaction).hex() except Exception as e: txid = "pending_celery" retry_later = True if exit_after_sending_tx: return txid, None, None with transaction.atomic(): kudos_transfer = KudosTransfer.objects.create( emails=[profile.email], # For kudos, `token` is a kudos.models.Token instance. kudos_token_cloned_from=coupon.token, amount=coupon.token.price_in_eth, 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=kudos_owner_address, 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=tx_status, receive_tx_status=tx_status, receive_address=address, ) # 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() # user actions record_user_action(kudos_transfer.username, 'new_kudos', kudos_transfer) record_user_action(kudos_transfer.from_username, 'receive_kudos', kudos_transfer) record_kudos_activity(kudos_transfer, kudos_transfer.username, 'receive_kudos') # send email maybe_market_kudos_to_email(kudos_transfer) if retry_later: redeem_bulk_kudos.delay(kudos_transfer.id) return True, None, kudos_transfer
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)
def receive_bulk(request, secret): coupons = BulkTransferCoupon.objects.filter(secret=secret) if not coupons.exists(): raise Http404 coupon = coupons.first() _class = request.GET.get('class', '') if coupon.num_uses_remaining <= 0: messages.info( request, f'Sorry but the coupon for a free kudos has been used already. Contact the person who sent you the coupon link, or you can still purchase one on this page.' ) return redirect(coupon.token.url) error = False if request.POST: if request.user.is_anonymous: error = "You must login." if not error: submit_later = (recommend_min_gas_price_to_confirm_in_time(1) ) > 10 and not coupon.is_paid_right_now submit_later = False success, error, _ = redeem_bulk_coupon( coupon, request.user.profile, request.POST.get('forwarding_address'), get_ip(request), request.POST.get('save_addr'), submit_later=submit_later) if error: messages.error(request, error) 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 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}" tweet_text = f"I just got a {coupon.token.humanized_name} Kudos on @gitcoin. " if not request.GET.get( 'tweet', None) else request.GET.get('tweet') gas_amount = round( 0.00035 * 1.3 * float(recommend_min_gas_price_to_confirm_in_time(1)), 4) params = { 'title': title, 'card_title': title, 'card_desc': desc, 'error': error, 'avatar_url': coupon.token.img_url, 'coupon': coupon, 'user': request.user, 'class': _class, 'gas_amount': gas_amount, 'is_authed': request.user.is_authenticated, 'kudos_transfer': kudos_transfer, 'tweet_text': urllib.parse.quote_plus(tweet_text), 'tweet_url': coupon.token.url if not request.GET.get('tweet_url') else request.GET.get('tweet_url'), } return TemplateResponse(request, 'transaction/receive_bulk.html', params)
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 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 get_gas_price(gas_multiplier=1.101): """Get the recommended minimum gas price.""" return recommend_min_gas_price_to_confirm_in_time(1) * 10**9 if not settings.DEBUG else 15 * 10**9 * gas_multiplier
def grant_new_v0(request): """Create a v0 version of a grant contract.""" profile = get_profile(request) if request.method == 'POST': if 'title' in request.POST: logo = request.FILES.get('input_image', None) receipt = json.loads(request.POST.get('receipt', '{}')) team_members = request.POST.getlist('team_members[]') grant_kwargs = { 'title': request.POST.get('title', ''), 'description': request.POST.get('description', ''), 'reference_url': request.POST.get('reference_url', ''), 'admin_address': request.POST.get('admin_address', ''), 'contract_owner_address': request.POST.get('contract_owner_address', ''), 'token_address': request.POST.get('token_address', ''), 'token_symbol': request.POST.get('token_symbol', ''), 'amount_goal': request.POST.get('amount_goal', 1), 'contract_version': request.POST.get('contract_version', ''), 'deploy_tx_id': request.POST.get('transaction_hash', ''), 'network': request.POST.get('network', 'mainnet'), 'metadata': receipt, 'admin_profile': profile, 'logo': logo, } grant = Grant.objects.create(**grant_kwargs) team_members = (team_members[0].split(',')) team_members.append(profile.id) team_members = list(set(team_members)) for i in range(0, len(team_members)): team_members[i] = int(team_members[i]) grant.team_members.add(*team_members) grant.save() return JsonResponse({ 'success': True, }) if 'contract_address' in request.POST: tx_hash = request.POST.get('transaction_hash', '') if not tx_hash: return JsonResponse({ 'success': False, 'info': 'no tx hash', 'url': None, }) grant = Grant.objects.filter(deploy_tx_id=tx_hash).first() grant.contract_address = request.POST.get('contract_address', '') print(tx_hash, grant.contract_address) grant.save() record_grant_activity_helper('new_grant', grant, profile) new_grant(grant, profile) return JsonResponse({ 'success': True, 'url': reverse('grants:details', args=(grant.pk, grant.slug)) }) params = { 'active': 'new_grant', 'title': _('New Grant'), 'card_desc': _('Provide sustainable funding for Open Source with Gitcoin Grants'), 'profile': profile, 'grant': {}, 'keywords': get_keywords(), 'recommend_gas_price': recommend_min_gas_price_to_confirm_in_time(4), 'recommend_gas_price_slow': recommend_min_gas_price_to_confirm_in_time(120), 'recommend_gas_price_avg': recommend_min_gas_price_to_confirm_in_time(15), 'recommend_gas_price_fast': recommend_min_gas_price_to_confirm_in_time(1), 'eth_usd_conv_rate': eth_usd_conv_rate(), 'conf_time_spread': conf_time_spread(), 'gas_advisories': gas_advisories(), 'trusted_relayer': settings.GRANTS_OWNER_ACCOUNT } return TemplateResponse(request, 'grants/newv0.html', params)
def redeem_bulk_kudos(self, kt_id, retry=False): """ :param self: :param kt_id: :return: """ try: with redis.lock("tasks:redeem_bulk_kudos:%s" % kt_id, timeout=LOCK_TIMEOUT): multiplier = 1 # high gas prices, 5 hour gas limit - DL gas_price = int( float(recommend_min_gas_price_to_confirm_in_time(300)) * multiplier) if gas_price > delay_if_gas_prices_gt_redeem: # do not retry is gas prices are too high # TODO: revisit this when gas prices go down # self.retry(countdown=60*10) return obj = KudosTransfer.objects.get(pk=kt_id) w3 = get_web3(obj.network) token = obj.kudos_token_cloned_from if token.owner_address.lower( ) != '0x6239FF1040E412491557a7a02b2CBcC5aE85dc8F'.lower(): raise Exception( "kudos isnt owned by Gitcoin; cowardly refusing to spend Gitcoin's ETH minting it" ) kudos_owner_address = settings.KUDOS_OWNER_ACCOUNT kudos_owner_address = Web3.toChecksumAddress(kudos_owner_address) kudos_contract_address = Web3.toChecksumAddress( settings.KUDOS_CONTRACT_MAINNET) contract = w3.eth.contract( Web3.toChecksumAddress(kudos_contract_address), abi=kudos_abi()) nonce = w3.eth.getTransactionCount(kudos_owner_address) tx = contract.functions.clone( Web3.toChecksumAddress(obj.receive_address), token.token_id, 1).buildTransaction({ 'nonce': nonce, 'gas': 500000, 'gasPrice': gas_price, 'value': int(token.price_finney / 1000.0 * 10**18), }) private_key = settings.KUDOS_PRIVATE_KEY signed = w3.eth.account.signTransaction(tx, private_key) obj.txid = w3.eth.sendRawTransaction(signed.rawTransaction).hex() obj.receive_txid = obj.txid obj.save() while not has_tx_mined(obj.txid, obj.network): time.sleep(1) pass except (SoftTimeLimitExceeded, TimeLimitExceeded): print('max timeout for bulk kudos redeem exceeded ... giving up!') except Exception as e: print(e) if self.request.retries < self.max_retries: self.retry(countdown=(30 * (self.request.retries + 1))) else: print("max retries for bulk kudos redeem exceeded ... giving up!")
def grant_details(request, grant_id, grant_slug): """Display the Grant details page.""" profile = get_profile(request) add_cancel_params = False try: grant = Grant.objects.prefetch_related('subscriptions', 'milestones', 'updates').get(pk=grant_id, slug=grant_slug) milestones = grant.milestones.order_by('due_date') updates = grant.updates.order_by('-created_on') subscriptions = grant.subscriptions.filter( active=True, error=False).order_by('-created_on') cancelled_subscriptions = grant.subscriptions.filter( active=False, error=False).order_by('-created_on') _contributions = Contribution.objects.filter( subscription__in=grant.subscriptions.all()) phantom_funds = grant.phantom_funding.filter(round_number=3) contributions = list(_contributions.order_by('-created_on')) + [ ele.to_mock_contribution() for ele in phantom_funds.order_by('-created_on') ] contributors = list( _contributions.distinct('subscription__contributor_profile') ) + list(phantom_funds.distinct('profile')) activity_count = len(cancelled_subscriptions) + len(contributions) user_subscription = grant.subscriptions.filter( contributor_profile=profile, active=True).first() user_non_errored_subscription = grant.subscriptions.filter( contributor_profile=profile, active=True, error=False).first() add_cancel_params = user_subscription except Grant.DoesNotExist: raise Http404 is_admin = (grant.admin_profile.id == profile.id) if profile and grant.admin_profile else False if is_admin: add_cancel_params = True is_team_member = False if profile: for team_member in grant.team_members.all(): if team_member.id == profile.id: is_team_member = True break if request.method == 'POST' and (is_team_member or request.user.is_staff): if request.FILES.get('input_image'): logo = request.FILES.get('input_image', None) grant.logo = logo grant.save() record_grant_activity_helper('update_grant', grant, profile) return redirect( reverse('grants:details', args=(grant.pk, grant.slug))) if 'contract_address' in request.POST: grant.cancel_tx_id = request.POST.get('grant_cancel_tx_id', '') grant.active = False grant.save() grant_cancellation(grant, user_subscription) for sub in subscriptions: subscription_terminated(grant, sub) record_grant_activity_helper('killed_grant', grant, profile) elif 'input-title' in request.POST: update_kwargs = { 'title': request.POST.get('input-title', ''), 'description': request.POST.get('description', ''), 'grant': grant } Update.objects.create(**update_kwargs) record_grant_activity_helper('update_grant', grant, profile) elif 'edit-title' in request.POST: grant.title = request.POST.get('edit-title') grant.reference_url = request.POST.get('edit-reference_url') grant.amount_goal = Decimal(request.POST.get('edit-amount_goal')) team_members = request.POST.getlist('edit-grant_members[]') team_members.append(str(grant.admin_profile.id)) grant.team_members.set(team_members) if 'edit-description' in request.POST: grant.description = request.POST.get('edit-description') grant.description_rich = request.POST.get( 'edit-description_rich') grant.save() record_grant_activity_helper('update_grant', grant, profile) return redirect( reverse('grants:details', args=(grant.pk, grant.slug))) params = { 'active': 'grant_details', 'clr_matching_banners_style': clr_matching_banners_style, 'grant': grant, 'tab': request.GET.get('tab', 'description'), 'title': matching_live + grant.title, 'card_desc': grant.description, 'avatar_url': grant.logo.url if grant.logo else None, 'subscriptions': subscriptions, 'cancelled_subscriptions': cancelled_subscriptions, 'contributions': contributions, 'user_subscription': user_subscription, 'user_non_errored_subscription': user_non_errored_subscription, 'is_admin': is_admin, 'grant_is_inactive': not grant.active, 'updates': updates, 'milestones': milestones, 'keywords': get_keywords(), 'activity_count': activity_count, 'contributors': contributors, 'clr_active': clr_active, 'is_team_member': is_team_member } if add_cancel_params: add_in_params = { 'recommend_gas_price': recommend_min_gas_price_to_confirm_in_time(4), 'recommend_gas_price_slow': recommend_min_gas_price_to_confirm_in_time(120), 'recommend_gas_price_avg': recommend_min_gas_price_to_confirm_in_time(15), 'recommend_gas_price_fast': recommend_min_gas_price_to_confirm_in_time(1), 'eth_usd_conv_rate': eth_usd_conv_rate(), 'conf_time_spread': conf_time_spread(), 'gas_advisories': gas_advisories(), } for key, value in add_in_params.items(): params[key] = value return TemplateResponse(request, 'grants/detail/index.html', params)
def grant_details(request, grant_id, grant_slug): """Display the Grant details page.""" profile = get_profile(request) add_cancel_params = False try: grant = Grant.objects.prefetch_related('subscriptions', 'milestones', 'updates').get( pk=grant_id, slug=grant_slug ) milestones = grant.milestones.order_by('due_date') updates = grant.updates.order_by('-created_on') subscriptions = grant.subscriptions.filter(active=True, error=False).order_by('-created_on') cancelled_subscriptions = grant.subscriptions.filter(active=False, error=False).order_by('-created_on') _contributions = Contribution.objects.filter(subscription__in=grant.subscriptions.all()) phantom_funds = grant.phantom_funding.all() contributions = list(_contributions.order_by('-created_on')) voucher_fundings = [ele.to_mock_contribution() for ele in phantom_funds.order_by('-created_on')] contributors = list(_contributions.distinct('subscription__contributor_profile')) + list(phantom_funds.distinct('profile')) activity_count = len(cancelled_subscriptions) + len(contributions) user_subscription = grant.subscriptions.filter(contributor_profile=profile, active=True).first() user_non_errored_subscription = grant.subscriptions.filter(contributor_profile=profile, active=True, error=False).first() add_cancel_params = user_subscription except Grant.DoesNotExist: raise Http404 is_admin = (grant.admin_profile.id == profile.id) if profile and grant.admin_profile else False if is_admin: add_cancel_params = True is_team_member = is_grant_team_member(grant, profile) if request.method == 'POST' and (is_team_member or request.user.is_staff): if request.FILES.get('input_image'): logo = request.FILES.get('input_image', None) grant.logo = logo grant.save() record_grant_activity_helper('update_grant', grant, profile) return redirect(reverse('grants:details', args=(grant.pk, grant.slug))) if 'contract_address' in request.POST: grant.cancel_tx_id = request.POST.get('grant_cancel_tx_id', '') grant.active = False grant.save() grant_cancellation(grant, user_subscription) for sub in subscriptions: subscription_terminated(grant, sub) record_grant_activity_helper('killed_grant', grant, profile) elif 'input-title' in request.POST: update_kwargs = { 'title': request.POST.get('input-title', ''), 'description': request.POST.get('description', ''), 'grant': grant } Update.objects.create(**update_kwargs) record_grant_activity_helper('update_grant', grant, profile) elif 'edit-title' in request.POST: grant.title = request.POST.get('edit-title') grant.reference_url = request.POST.get('edit-reference_url') grant.amount_goal = Decimal(request.POST.get('edit-amount_goal')) team_members = request.POST.getlist('edit-grant_members[]') team_members.append(str(grant.admin_profile.id)) grant.team_members.set(team_members) if 'edit-description' in request.POST: grant.description = request.POST.get('edit-description') grant.description_rich = request.POST.get('edit-description_rich') grant.save() record_grant_activity_helper('update_grant', grant, profile) return redirect(reverse('grants:details', args=(grant.pk, grant.slug))) # handle grant updates unsubscribe key = 'unsubscribed_profiles' is_unsubscribed_from_updates_from_this_grant = request.user.is_authenticated and request.user.profile.pk in grant.metadata.get(key, []) if request.GET.get('unsubscribe') and request.user.is_authenticated: ups = grant.metadata.get(key, []) ups.append(request.user.profile.pk) grant.metadata[key] = ups grant.save() messages.info( request, _('You have been unsubscribed from the updates from this grant.') ) is_unsubscribed_from_updates_from_this_grant = True tab = request.GET.get('tab', 'activity') params = { 'active': 'grant_details', 'clr_matching_banners_style': clr_matching_banners_style, 'grant': grant, 'tab': tab, 'title': matching_live + grant.title, 'card_desc': grant.description, 'avatar_url': grant.logo.url if grant.logo else None, 'subscriptions': subscriptions, 'cancelled_subscriptions': cancelled_subscriptions, 'contributions': contributions, 'user_subscription': user_subscription, 'user_non_errored_subscription': user_non_errored_subscription, 'is_admin': is_admin, 'grant_is_inactive': not grant.active, 'updates': updates, 'milestones': milestones, 'keywords': get_keywords(), 'target': f'/activity?what=grant:{grant.pk}', 'activity_count': activity_count, 'contributors': contributors, 'clr_active': clr_active, 'show_clr_card': show_clr_card, 'is_team_member': is_team_member, 'voucher_fundings': voucher_fundings, 'is_unsubscribed_from_updates_from_this_grant': is_unsubscribed_from_updates_from_this_grant, 'tags': [(f'Email Grant Funders ({len(contributors)})', 'bullhorn')] if is_team_member else [], } if tab == 'stats': params['max_graph'] = grant.history_by_month_max params['history'] = json.dumps(grant.history_by_month) if add_cancel_params: add_in_params = { 'recommend_gas_price': recommend_min_gas_price_to_confirm_in_time(4), 'recommend_gas_price_slow': recommend_min_gas_price_to_confirm_in_time(120), 'recommend_gas_price_avg': recommend_min_gas_price_to_confirm_in_time(15), 'recommend_gas_price_fast': recommend_min_gas_price_to_confirm_in_time(1), 'eth_usd_conv_rate': eth_usd_conv_rate(), 'conf_time_spread': conf_time_spread(), 'gas_advisories': gas_advisories(), } for key, value in add_in_params.items(): params[key] = value return TemplateResponse(request, 'grants/detail/index.html', params)
def redeem_coin(request, shortcode): if request.body: status = 'OK' body_unicode = request.body.decode('utf-8') body = json.loads(body_unicode) address = body['address'] try: coin = CoinRedemption.objects.get(shortcode=shortcode) address = Web3.toChecksumAddress(address) if hasattr(coin, 'coinredemptionrequest'): status = 'error' message = 'Bad request' else: abi = json.loads( '[{"constant":true,"inputs":[],"name":"mintingFinished","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"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":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"mint","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"version","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_subtractedValue","type":"uint256"}],"name":"decreaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"finishMinting","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_addedValue","type":"uint256"}],"name":"increaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"payable":false,"stateMutability":"nonpayable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[],"name":"MintFinished","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]' ) # Instantiate Colorado Coin contract contract = w3.eth.contract(coin.contract_address, abi=abi) tx = contract.functions.transfer( address, coin.amount * 10**18).buildTransaction({ 'nonce': w3.eth.getTransactionCount( settings.COLO_ACCOUNT_ADDRESS), 'gas': 100000, 'gasPrice': recommend_min_gas_price_to_confirm_in_time(5) * 10**9 }) signed = w3.eth.account.signTransaction( tx, settings.COLO_ACCOUNT_PRIVATE_KEY) transaction_id = w3.eth.sendRawTransaction( signed.rawTransaction).hex() CoinRedemptionRequest.objects.create(coin_redemption=coin, ip=get_ip(request), sent_on=timezone.now(), txid=transaction_id, txaddress=address) message = transaction_id except CoinRedemption.DoesNotExist: status = 'error' message = 'Bad request' except Exception as e: status = 'error' message = str(e) # http response response = { 'status': status, 'message': message, } return JsonResponse(response) try: coin = CoinRedemption.objects.get(shortcode=shortcode) params = { 'class': 'redeem', 'title': 'Coin Redemption', 'coin_status': 'PENDING' } try: coin_redeem_request = CoinRedemptionRequest.objects.get( coin_redemption=coin) params['colo_txid'] = coin_redeem_request.txid except CoinRedemptionRequest.DoesNotExist: params['coin_status'] = 'INITIAL' return TemplateResponse(request, 'yge/redeem_coin.html', params) except CoinRedemption.DoesNotExist: raise Http404
def grant_fund(request, grant_id, grant_slug): """Handle grant funding.""" try: grant = Grant.objects.get(pk=grant_id, slug=grant_slug) except Grant.DoesNotExist: raise Http404 profile = get_profile(request) if not grant.active: params = { 'active': 'grant_error', 'title': _('Fund - Grant Ended'), 'grant': grant, 'text': _('This Grant has ended.'), 'subtext': _('Contributions can no longer be made this grant') } return TemplateResponse(request, 'grants/shared/error.html', params) if is_grant_team_member(grant, profile): params = { 'active': 'grant_error', 'title': _('Fund - Grant funding blocked'), 'grant': grant, 'text': _('This Grant cannot be funded'), 'subtext': _('Grant team members cannot contribute to their own grant.') } return TemplateResponse(request, 'grants/shared/error.html', params) if grant.link_to_new_grant: params = { 'active': 'grant_error', 'title': _('Fund - Grant Migrated'), 'grant': grant.link_to_new_grant, 'text': f'This Grant has ended', 'subtext': 'Contributions can no longer be made to this grant. <br> Visit the new grant to contribute.', 'button_txt': 'View New Grant' } return TemplateResponse(request, 'grants/shared/error.html', params) active_subscription = Subscription.objects.select_related('grant').filter( grant=grant_id, active=True, error=False, contributor_profile=request.user.profile ) if active_subscription: params = { 'active': 'grant_error', 'title': _('Subscription Exists'), 'grant': grant, 'text': _('You already have an active subscription for this grant.') } return TemplateResponse(request, 'grants/shared/error.html', params) if grant.contract_address == '0x0': messages.info( request, _('This grant is not configured to accept funding at this time. Please contact [email protected] if you believe this message is in error!') ) logger.error(f"Grant {grant.pk} is not properly configured for funding. Please set grant.contract_address on this grant") return redirect(reverse('grants:details', args=(grant.pk, grant.slug))) if request.method == 'POST': if 'contributor_address' in request.POST: subscription = Subscription() subscription.active = False subscription.contributor_address = request.POST.get('contributor_address', '') subscription.amount_per_period = request.POST.get('amount_per_period', 0) subscription.real_period_seconds = request.POST.get('real_period_seconds', 2592000) subscription.frequency = request.POST.get('frequency', 30) subscription.frequency_unit = request.POST.get('frequency_unit', 'days') subscription.token_address = request.POST.get('token_address', '') subscription.token_symbol = request.POST.get('token_symbol', '') subscription.gas_price = request.POST.get('gas_price', 0) subscription.new_approve_tx_id = request.POST.get('sub_new_approve_tx_id', '0x0') subscription.num_tx_approved = request.POST.get('num_tx_approved', 1) subscription.network = request.POST.get('network', '') subscription.contributor_profile = profile subscription.grant = grant subscription.comments = request.POST.get('comment', '') subscription.save() # one time payments activity = None if int(subscription.num_tx_approved) == 1: subscription.successful_contribution(subscription.new_approve_tx_id); subscription.error = True #cancel subs so it doesnt try to bill again subscription.subminer_comments = "skipping subminer bc this is a 1 and done subscription, and tokens were alredy sent" subscription.save() activity = record_subscription_activity_helper('new_grant_contribution', subscription, profile) else: activity = record_subscription_activity_helper('new_grant_subscription', subscription, profile) if 'comment' in request.POST: comment = request.POST.get('comment') if comment and activity: comment = Comment.objects.create( profile=request.user.profile, activity=activity, comment=comment) # TODO - how do we attach the tweet modal WITH BULK TRANSFER COUPON next pageload?? messages.info( request, _('Your subscription has been created. It will bill within the next 5 minutes or so. Thank you for supporting Open Source !') ) return JsonResponse({ 'success': True, }) if 'hide_wallet_address' in request.POST: profile.hide_wallet_address = bool(request.POST.get('hide_wallet_address', False)) profile.save() if 'signature' in request.POST: sub_new_approve_tx_id = request.POST.get('sub_new_approve_tx_id', '') subscription = Subscription.objects.filter(new_approve_tx_id=sub_new_approve_tx_id).first() subscription.active = True subscription.subscription_hash = request.POST.get('subscription_hash', '') subscription.contributor_signature = request.POST.get('signature', '') if 'split_tx_id' in request.POST: subscription.split_tx_id = request.POST.get('split_tx_id', '') subscription.save_split_tx_to_contribution() if 'split_tx_confirmed' in request.POST: subscription.split_tx_confirmed = bool(request.POST.get('split_tx_confirmed', False)) subscription.save_split_tx_to_contribution() subscription.save() value_usdt = subscription.get_converted_amount() if value_usdt: grant.monthly_amount_subscribed += subscription.get_converted_monthly_amount() grant.save() new_supporter(grant, subscription) thank_you_for_supporting(grant, subscription) return JsonResponse({ 'success': True, 'url': reverse('grants:details', args=(grant.pk, grant.slug)) }) splitter_contract_address = settings.SPLITTER_CONTRACT_ADDRESS # handle phantom funding active_tab = 'normal' fund_reward = None round_number = 4 can_phantom_fund = request.user.is_authenticated and request.user.groups.filter(name='phantom_funders').exists() and clr_active phantom_funds = PhantomFunding.objects.filter(profile=request.user.profile, round_number=round_number).order_by('created_on').nocache() if request.user.is_authenticated else PhantomFunding.objects.none() is_phantom_funding_this_grant = can_phantom_fund and phantom_funds.filter(grant=grant).exists() show_tweet_modal = False if can_phantom_fund: active_tab = 'phantom' if can_phantom_fund and request.POST.get('toggle_phantom_fund'): if is_phantom_funding_this_grant: msg = "You are no longer signaling for this grant." phantom_funds.filter(grant=grant).delete() else: msg = "You are now signaling for this grant." show_tweet_modal = True name_search = 'grants_round_4_contributor' if not settings.DEBUG else 'pogs_eth' fund_reward = BulkTransferCoupon.objects.filter(token__name__contains=name_search).order_by('?').first() PhantomFunding.objects.create(grant=grant, profile=request.user.profile, round_number=round_number) record_grant_activity_helper('new_grant_contribution', grant, request.user.profile) messages.info( request, msg ) is_phantom_funding_this_grant = not is_phantom_funding_this_grant params = { 'profile': profile, 'active': 'fund_grant', 'title': _('Fund Grant'), 'card_desc': _('Provide sustainable funding for Open Source with Gitcoin Grants'), 'subscription': {}, 'show_tweet_modal': show_tweet_modal, 'grant_has_no_token': True if grant.token_address == '0x0000000000000000000000000000000000000000' else False, 'grant': grant, 'clr_prediction_curve': [c[1] for c in grant.clr_prediction_curve] if grant.clr_prediction_curve and len(grant.clr_prediction_curve[0]) > 1 else [0, 0, 0, 0, 0, 0], 'keywords': get_keywords(), 'recommend_gas_price': recommend_min_gas_price_to_confirm_in_time(4), 'recommend_gas_price_slow': recommend_min_gas_price_to_confirm_in_time(120), 'recommend_gas_price_avg': recommend_min_gas_price_to_confirm_in_time(15), 'recommend_gas_price_fast': recommend_min_gas_price_to_confirm_in_time(1), 'eth_usd_conv_rate': eth_usd_conv_rate(), 'conf_time_spread': conf_time_spread(), 'gas_advisories': gas_advisories(), 'splitter_contract_address': settings.SPLITTER_CONTRACT_ADDRESS, 'gitcoin_donation_address': settings.GITCOIN_DONATION_ADDRESS, 'can_phantom_fund': can_phantom_fund, 'is_phantom_funding_this_grant': is_phantom_funding_this_grant, 'active_tab': active_tab, 'fund_reward': fund_reward, 'phantom_funds': phantom_funds, 'clr_round': clr_round, 'clr_active': clr_active, 'total_clr_pot': total_clr_pot, } return TemplateResponse(request, 'grants/fund.html', params)
def send_tip_2(request): """Handle the second stage of sending a tip. TODO: * Convert this view-based logic to a django form. Returns: JsonResponse: If submitting tip, return response with success state. TemplateResponse: Render the submission form. """ profile = None fund_request = None username = request.GET.get('username', None) pk_fund_request = request.GET.get('request', None) is_user_authenticated = request.user.is_authenticated from_username = request.user.username if is_user_authenticated else '' primary_from_email = request.user.email if is_user_authenticated else '' user = {} if username: profiles = Profile.objects.filter(handle=username.lower()) if profiles.exists(): profile = profiles.first() if pk_fund_request: requests = FundRequest.objects.filter(pk=int(pk_fund_request)) if requests.exists(): fund_request = requests.first() profile = fund_request.requester else: messages.error( f'Failed to retrieve the fund request {fund_request}') if profile: user['id'] = profile.id user['text'] = profile.handle user['avatar_url'] = profile.avatar_url if profile.avatar_baseavatar_related.exists(): user['avatar_id'] = profile.avatar_baseavatar_related.filter( active=True).first().pk user['avatar_url'] = profile.avatar_baseavatar_related.filter( active=True).first().avatar_url user['preferred_payout_address'] = profile.preferred_payout_address params = { 'issueURL': request.GET.get('source'), 'class': 'send2', 'recommend_gas_price': recommend_min_gas_price_to_confirm_in_time( confirm_time_minutes_target), 'from_email': primary_from_email, 'from_handle': from_username, 'title': 'Send Tip | Gitcoin', 'card_desc': 'Send a tip to any github user at the click of a button.', 'fund_request': fund_request } if user: params['user_json'] = user return TemplateResponse(request, 'onepager/send2.html', params)