Example #1
0
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
Example #2
0
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)
Example #3
0
 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
Example #4
0
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)
Example #5
0
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)
Example #6
0
    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)
Example #7
0
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)
Example #8
0
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)
Example #9
0
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)
Example #10
0
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)
Example #11
0
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)
Example #12
0
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)
Example #13
0
        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())
Example #14
0
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)
Example #15
0
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)
Example #16
0
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)
Example #17
0
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)
Example #18
0
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
Example #19
0
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)
Example #20
0
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)
Example #21
0
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)
Example #22
0
    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")
Example #23
0
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
Example #24
0
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)
Example #25
0
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!")
Example #26
0
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)
Example #27
0
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)
Example #28
0
File: views.py Project: bakaoh/web
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
Example #29
0
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)
Example #30
0
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)