Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement twitter grant verification #7377

Merged
merged 4 commits into from
Sep 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion app/assets/v2/css/grants/profile.css
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,20 @@ h2.title {
.title {
padding-left: 10px;
}
}

.verification__warning {
background-color: #f8f8f0;
border: 2px solid #ffce08;
color: #b88b16;
font-weight: bold;
}

}
.verification__warning__icon {
font-size: 1.8rem;
margin-top: 7px;
}

.error {
color: var(--gc-pink);
}
2 changes: 1 addition & 1 deletion app/grants/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class GrantAdmin(GeneralAdmin):
'subscriptions_links', 'contributions_links', 'logo', 'logo_svg', 'image_css',
'link', 'clr_prediction_curve', 'hidden', 'grant_type', 'next_clr_calc_date', 'last_clr_calc_date',
'metadata', 'categories', 'twitter_handle_1', 'twitter_handle_2', 'view_count', 'is_clr_eligible', 'in_active_clrs',
'last_update', 'funding_info'
'last_update', 'funding_info', 'twitter_verified', 'twitter_verified_by', 'twitter_verified_at'
]
readonly_fields = [
'logo_svg_asset', 'logo_asset',
Expand Down
30 changes: 30 additions & 0 deletions app/grants/migrations/0080_auto_20200914_2146.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 2.2.4 on 2020-09-14 21:46

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('dashboard', '0148_add_brightid_status'),
('grants', '0079_auto_20200914_2031'),
]

operations = [
migrations.AddField(
model_name='grant',
name='twitter_verified',
field=models.BooleanField(default=False, help_text='The owner grant has verified the twitter account'),
),
migrations.AddField(
model_name='grant',
name='twitter_verified_at',
field=models.DateTimeField(blank=True, help_text='At what time and date what verified this grant', null=True),
),
migrations.AddField(
model_name='grant',
name='twitter_verified_by',
field=models.ForeignKey(blank=True, help_text='Team member who verified this grant', null=True, on_delete=django.db.models.deletion.SET_NULL, to='dashboard.Profile'),
),
]
20 changes: 12 additions & 8 deletions app/grants/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,10 @@ class Meta:
is_clr_active = models.BooleanField(default=False, help_text=_('CLR Round active or not? (auto computed)'))
clr_round_num = models.CharField(default='', max_length=255, help_text=_('the CLR round number thats active'), blank=True)

twitter_verified = models.BooleanField(default=False, help_text='The owner grant has verified the twitter account')
twitter_verified_by = models.ForeignKey('dashboard.Profile', null=True, blank=True, on_delete=models.SET_NULL, help_text='Team member who verified this grant')
twitter_verified_at = models.DateTimeField(blank=True, null=True, help_text='At what time and date what verified this grant')

# Grant Query Set used as manager.
objects = GrantQuerySet.as_manager()

Expand Down Expand Up @@ -1259,38 +1263,38 @@ def update_tx_status(self):

# We use the transaction hashes of this object to help identify zkSync checkouts. This
# works as follows:
#
#
# self.split_tx_id holds one of:
# Case 1: The tx hash of an L1 transaction to the BulkCheckout contract for an
# Case 1: The tx hash of an L1 transaction to the BulkCheckout contract for an
# ordinary checkout
# Case 2: The tx hash of an L1 transaction that deposits funds into zkSync. This
# occurs when a user did not have existing funds in zkSync
# Case 3: The address of the Gitcoin zkSync wallet that executed the donations. This
# occurs when a user already had funds in zkSync
#
#
# Case 1 has already been handled by everything above. For Case 2, we mark a
# contribution as cleared once both of the below conditions are met:
# 1. The L1 deposit transaction has been confirmed, and
# 2. The L2 transfers have been completed
#
# For case 3, we mark a contribution as cleared once the L2 transfers are completed.

# Prepare web3 provider
network = self.subscription.network
PROVIDER = "wss://" + network + ".infura.io/ws/v3/" + settings.INFURA_V3_PROJECT_ID
w3 = Web3(Web3.WebsocketProvider(PROVIDER))

# Get case number
is_split_tx_id_address = len(self.split_tx_id) == 42 and self.split_tx_id[0:2] == '0x'
if is_split_tx_id_address:
case_number = 3

else:
# Figure out if we are in Case 1 or Case 2
# handle replace of split_tx_id
if not self.split_tx_id:
return

split_tx_status, _ = get_tx_status(self.split_tx_id, self.subscription.network, self.created_on)
if split_tx_status in ['pending', 'dropped', 'unknown', '']:
new_tx = getReplacedTX(self.split_tx_id)
Expand All @@ -1311,7 +1315,7 @@ def update_tx_status(self):
batch_zksync_deposit_contract_addr = '0x9D37F793E5eD4EbD66d62D505684CD9f756504F6'.lower()
zkSync_recipients = [zksync_contract_addr.lower(), batch_zksync_deposit_contract_addr.lower()]
case_number = 2 if recipient_L1 in zkSync_recipients else 1

# If case 1, proceed as normal
if case_number == 1:
# actually validate token transfers
Expand Down
12 changes: 12 additions & 0 deletions app/grants/templates/grants/components/card.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@
<div class="grant-item__content col-md-8 px-3">
<h2 class="grant-item__title font-subheader">
<a :href="grant.details_url">[[ grant.title.slice(0,60) ]][[ grant.title.length > 60 ? '...' : '']]</a>
<button v-if="grant.verified" class="btn btn-sm animate-verify p-0" data-container="body" data-toggle="popover" data-html="true" data-placement="bottom" data-trigger="click" data-content='
<p class="h6 my-2 text-left">Verified Ownership <img width="18" src="{% static "v2/images/badge-verify.svg" %}"></p>
<p>Grant owner has verified ownership of their twitter account.</p>
<a href="#">Learn more.</p>'
><img width="14" src="{% static 'v2/images/badge-verify.svg' %}" alt="">
</button>
</h2>
<div>
<p class="text-muted font-smaller-5 mb-0" style="height:2rem;" data-toggle="tooltip" data-placement="top" title="The last time the grant admin updated the grant." >
Expand Down Expand Up @@ -121,6 +127,12 @@ <h2 class="grant-item__title font-subheader">
<div class="grant-item__content px-3">
<h2 class="grant-item__title font-subheader">
<a :href="grant.details_url">[[ grant.title.slice(0,60) ]][[ grant.title.length > 60 ? '...' : '']]</a>
<button v-if="grant.verified" class="btn btn-sm animate-verify p-0" data-container="body" data-toggle="popover" data-html="true" data-placement="bottom" data-trigger="hover click" data-content='
<p class="h6 my-2 text-left">Verified Ownership <img width="18" src="{% static "v2/images/badge-verify.svg" %}"></p>
<p>Grant owner has verified ownership of their twitter account.</p>
<a href="#">Learn more.</p>'
><img width="16" src="{% static 'v2/images/badge-verify.svg' %}" alt="">
</button>
</h2>
<p class="grant-item__pitch font-caption mb-2">[[ grant.description.slice(0, 145) ]][[ grant.description.length > 145 ? '...' : '']]</p>
<p class="grant-item__pitch font-caption mb-0" style="height:2rem;" data-toggle="tooltip" data-placement="top" title="The last time the grant admin updated the grant." >
Expand Down
17 changes: 16 additions & 1 deletion app/grants/templates/grants/detail/funding.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,22 @@

{% load static humanize i18n grants_extra %}

<div class="px-0 pr-md-3">
<div class="px-0 pr-md-3 my-0 mx-0 mt-md-5 mr-md-5">
{% if not grant.twitter_verified %}
<div class="my-0 mx-0 mt-md-5 mr-md-5 py-2">
<div class="verification__warning py-2 px-2 d-flex">
<i class="fas fa-exclamation-triangle verification__warning__icon mr-2"></i>
<div>
<div class="uppercase">Warning: This grant has not verified their ownership of the property listed</div>
{% if is_team_member %}
<div class="text-right mt-3">
<button class="btn btn-sm btn-gc-blue" data-toggle="modal" data-target="#startVerification">Verify your grant</button>
</div>
{% endif %}
</div>
</div>
</div>
{% endif %}
<div id="funding-card" class="card my-0 mx-0 mt-md-5 mr-md-5 shadow-sm">
<div class="card-body">
<div class="row progress-container {% if clr_active %} mb-2 {% endif %} {% if not is_team_member %} mt-4 {% endif %}">
Expand Down
71 changes: 71 additions & 0 deletions app/grants/templates/grants/detail/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,48 @@
<div id="side-cart" class="grant-side-cart px-5 px-md-4 pb-5 pb-md-0 pt-md-0" style="display: none;">
{% include 'grants/detail/side-cart.html' %}
</div>
</div>
<div class="modal fade" id="startVerification" tabindex="-1" role="dialog" aria-labelledby="startVerification" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content text-center">
<div class="modal-header" style="border-bottom: none">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body px-5">
<img height="45" class="mb-4" src="{% static "v2/images/badge-verify.svg" %}" >
<h5 class="font-weight-bold mb-3">Verify Grant Ownership</h5>
<p class="mb-4">Verify your grant ownership to ensure that your supporters are contributing to the correct grant.</p>
<button id="triggerTwitter" class="btn btn-lg btn-gc-blue" data-toggle="modal" data-target="#startTwitterVerification">Verify with twitter</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="startTwitterVerification" tabindex="-1" role="dialog" aria-labelledby="startTwitterVerification" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content text-center">
<div class="modal-header" style="border-bottom: none">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body px-5">
<img height="45" class="mb-4" src="{% static "v2/images/badge-verify.svg" %}" >
<h5 class="font-weight-bold mb-3">Verify Grant Ownership with Twitter</h5>
<p class="mb-4 text-left"><b>Step 1:</b> Send a tweet from your account (<b>{{ grant.twitter_handle_1 }}</b>) saying <b>
{% with verification_tweet|add:" "|add:user_code as tweet %}
{{ tweet }} <a class="font-caption btn btn-outline-gc-blue btn-sm" href="https://twitter.com/intent/tweet?text={{ tweet|urlencode}}" target="_blank" ><i class="fab fa-twitter"></i> Tweet it</a></b> <br>
{% endwith %}
</p>
<p class="mb-4 text-left"><b>Step 2:</b>Click the "Verify" button below</p>
<button class="btn btn-lg btn-gc-blue mb-4" id="twitterVerification">Verify</button>
<br>
<span class="error" id="validation-errors"></span>

</div>
</div>
</div>
</div>
<input type="hidden" id="contract_version" name="contract_version" value="{{ grant.contract_version }}"/>
{% include 'shared/current_profile.html' %}
Expand Down Expand Up @@ -121,6 +163,35 @@
<script type="text/javascript">
$(document).ready(function() {
$("img").unveil();

$('#twitterVerification').on('click', async () => {
const response = await fetchData('/grants/v1/api/{{grant.id}}/verify');

if (!response.ok) {
_alert(response.msg, 'error');
return;
}
if (response.verified) {
_alert('Congratulations, your grant is now verified!', 'success')
$('.verification__warning').remove();
$('#startTwitterVerification .close').click()
}

if (!response.has_text) {
$('#validation-errors').text(`Don't remove the intent "{{ verification_tweet }}"`);
return;
}

if (!response.has_code) {
$('#validation-errors').text(`Missing emoji code "{{ user_code }}", please don't remove this unique code before validate your grant.`);
return;
}
});


$('#triggerTwitter').on('click', async () => {
$("#startVerification .close").click();
});
});

</script>
Expand Down
6 changes: 6 additions & 0 deletions app/grants/templates/grants/detail/info.html
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ <h1 class="font-title-xl my-4 font-weight-bold">
<i style="width: 14px;" class="fab fa-twitter-square mr-2 text-center"></i>
<p class="d-inline-block mb-0">
<a href="https://twitter.com/{{grant.twitter_handle_1}}" target="new" rel="nofollow" data-toggle="tooltip" data-html="true" data-placement="top" title="Project Twitter Account">{{grant.twitter_handle_1}}</a>
<button class="btn btn-sm animate-verify {% if not grant.twitter_verified %}d-none{% endif %}" data-container="body" data-toggle="popover" data-html="true" data-placement="bottom" data-trigger="hover click" data-content='
<p class="h6 my-2 text-left">Verified Ownership <img width="18" src="{% static "v2/images/badge-verify.svg" %}"></p>
<p>Grant owner has verified ownership of their twitter account.</p>
<a href="#">Learn more.</p>'
><img width="18" src="{% static 'v2/images/badge-verify.svg' %}" alt="">
</button>
</p>
</div>
{% endif %}
Expand Down
4 changes: 2 additions & 2 deletions app/grants/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
bulk_fund, flag, get_grants, get_replaced_tx, grant_activity, grant_categories, grant_details, grant_fund,
grant_new, grant_new_whitelabel, grants, grants_addr_as_json, grants_bulk_add, grants_by_grant_type,
grants_cart_view, grants_clr, grants_stats_view, invoice, leaderboard, new_matching_partner, profile, quickstart,
subscription_cancel, toggle_grant_favorite, zksync_get_interrupt_status, zksync_set_interrupt_status,
subscription_cancel, toggle_grant_favorite, zksync_get_interrupt_status, zksync_set_interrupt_status, verify_grant,
)

app_name = 'grants'
Expand Down Expand Up @@ -65,5 +65,5 @@
path('<slug:grant_type>', grants_by_grant_type, name='grants_by_category2'),
path('<slug:grant_type>/', grants_by_grant_type, name='grants_by_category'),
path('v1/api/clr', grants_clr, name='grants_clr'),

path('v1/api/<int:grant_id>/verify', verify_grant, name='verify_grant')
]
16 changes: 14 additions & 2 deletions app/grants/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import logging
import os
from decimal import Decimal
from random import randint, seed
from secrets import token_hex

from economy.utils import ConversionRateNotFoundError, convert_amount
Expand All @@ -28,6 +29,10 @@

logger = logging.getLogger(__name__)

block_codes = ('▖', '▗', '▘', '▙', '▚', '▛', '▜', '▝', '▞', '▟')
emoji_codes = ('🎉', '🎈', '🎁', '🎊', '🙌', '🥂', '🎆', '🔥', '⚡', '👍')


def get_upload_filename(instance, filename):
salt = token_hex(16)
file_path = os.path.basename(filename)
Expand Down Expand Up @@ -115,10 +120,10 @@ def which_clr_round(timestamp):

if round_start < timestamp < round_end:
return round

return None

def get_converted_amount(amount, token_symbol):
def get_converted_amount(amount, token_symbol):
try:
if token_symbol == "ETH" or token_symbol == "WETH":
return Decimal(float(amount) * float(eth_usd_conv_rate()))
Expand All @@ -142,3 +147,10 @@ def get_converted_amount(amount, token_symbol):
except ConversionRateNotFoundError as no_conversion_e:
logger.info(no_conversion_e)
return None


def get_user_code(user_id, grant, coding_set=block_codes, length=6):
seed(user_id ** grant.id)
coding_id = [coding_set[randint(0, 9)] for _ in range(length)]

return ''.join(coding_id)
Loading