diff --git a/app/app/local.env b/app/app/local.env index 2382fd52c55..618dfa04e95 100644 --- a/app/app/local.env +++ b/app/app/local.env @@ -35,6 +35,9 @@ ETHERSCAN_API_KEY= INFURA_USE_V3=True +SUPRESS_DEBUG_TOOLBAR=1 + + SENDGRID_API_KEY= CONTACT_EMAIL= diff --git a/app/app/services.py b/app/app/services.py index ee1453f07ef..fa0675a6299 100644 --- a/app/app/services.py +++ b/app/app/services.py @@ -2,7 +2,7 @@ from django.conf import settings -from app.settings import account_sid, auth_token +from app.settings import account_sid, auth_token, verify_service from redis import Redis from twilio.rest import Client @@ -39,10 +39,7 @@ def __create_connection(self): if not TwilioService._client: TwilioService._client = Client(account_sid, auth_token) - friendly_names = settings.TWILIO_FRIENDLY_NAMES - friendly_name = random.choice(friendly_names) - TwilioService._service = TwilioService._client.verify.services.create(friendly_name=friendly_name) - redis.set(f"validation:twilio:sid", TwilioService._service.sid) + TwilioService._service = TwilioService._client.verify.services(verify_service) def __init__(self): self.__create_connection() @@ -54,5 +51,4 @@ def lookups(self): @property def verify(self): redis = RedisService().redis - sid = redis.get(f"validation:twilio:sid") - return TwilioService._client.verify.services(sid.decode('utf-8')) + return TwilioService._client.verify.services(verify_service) diff --git a/app/app/settings.py b/app/app/settings.py index 1f970914f5a..7755fbdc96e 100644 --- a/app/app/settings.py +++ b/app/app/settings.py @@ -836,6 +836,7 @@ def callback(request): account_sid = env('TWILIO_ACCOUNT_SID', default='') auth_token = env('TWILIO_AUTH_TOKEN', default='') +verify_service = env('TWILIO_VERIFY_SERVICE', default='') SMS_MAX_VERIFICATION_ATTEMPTS = env('SMS_MAX_VERIFICATION_ATTEMPTS', default=4) SMS_COOLDOWN_IN_MINUTES = env('SMS_COOLDOWN_IN_MINUTES', default=1) diff --git a/app/app/urls.py b/app/app/urls.py index 94632f365e4..11365ed8b79 100644 --- a/app/app/urls.py +++ b/app/app/urls.py @@ -681,6 +681,7 @@ url(settings.GITHUB_EVENT_HOOK_URL, gitcoinbot.views.payload, name='payload'), url(r'^impersonate/', include('impersonate.urls')), url(r'^api/v0.1/hackathon_project/set_winner/', dashboard.views.set_project_winner, name='project_winner'), + url(r'^api/v0.1/hackathon_project/set_winner/', dashboard.views.set_project_winner, name='project_winner'), # users url(r'^api/v0.1/user_bounties/', dashboard.views.get_user_bounties, name='get_user_bounties'), diff --git a/app/assets/onepager/js/receive.js b/app/assets/onepager/js/receive.js index d25e07f3628..a770133ee43 100644 --- a/app/assets/onepager/js/receive.js +++ b/app/assets/onepager/js/receive.js @@ -190,7 +190,8 @@ $(document).ready(function() { } else { // send ERC20 - var data = token_contract.methods.transfer(forwarding_address, amount_in_wei.toString()).encodeABI(); + var encoded_amount = new web3.utils.BN(BigInt(document.tip['amount_in_wei'])).toString(); + var data = token_contract.methods.transfer(forwarding_address, encoded_amount).encodeABI(); rawTx = { nonce: web3.utils.toHex(nonce), diff --git a/app/assets/onepager/js/request.js b/app/assets/onepager/js/request.js index 4fa67f5c874..5265d23ef1e 100644 --- a/app/assets/onepager/js/request.js +++ b/app/assets/onepager/js/request.js @@ -1,10 +1,4 @@ $(document).ready(function() { - waitforWeb3(function() { - if (!$('#address').val() && web3 && web3.eth.coinbase) { - $('#address').val(web3.eth.coinbase); - } - }); - $('#network').change(function(e) { if ($(this).val() !== 'ETH') { @@ -17,13 +11,13 @@ $(document).ready(function() { $('#request').on('click', function(e) { e.preventDefault(); if ($(this).hasClass('disabled')) - return; + return false; if (!$('#tos').is(':checked')) { _alert('Please accept the terms and conditions before submit.', 'warning'); - return; + return false; } - loading_button($(this)); + // get form data const username = $('.username-search').select2('data')[0] ? $('.username-search').select2('data')[0].text : ''; const amount = parseFloat($('#amount').val()); @@ -33,16 +27,30 @@ $(document).ready(function() { if (!document.contxt['github_handle']) { _alert('You must be logged in to use this form', 'warning'); - return; + return false; + } + + if (!provider) { + return onConnect().then(() => { + return false; + }); } + + if (!username || !amount || !address) { + _alert('Please fill all the fields.', 'warning'); + return false; + } + if (username == document.contxt['github_handle']) { _alert('You cannot request money from yourself.', 'warning'); - return; + return false; } if (!comments || comments.length < 5) { _alert('Please leave a comment describing why this user should send you money.', 'warning'); - return; + return false; } + loading_button($(this)); + const tokenAddress = ( ($('#token').val() == '0x0') ? '0x0000000000000000000000000000000000000000' @@ -118,7 +126,14 @@ function requestFunds(username, amount, comments, tokenAddress, tokenName, netwo }); } -window.addEventListener('load', function() { - setInterval(listen_for_web3_changes, 5000); - listen_for_web3_changes(); -}); \ No newline at end of file +window.addEventListener('load', async() => { + if (!provider && !web3Modal.cachedProvider || provider === 'undefined') { + onConnect().then(() => { + $('#address').val(selectedAccount); + }); + } else { + web3Modal.on('connect', async(data) => { + $('#address').val(data.selectedAddress); + }); + } +}); diff --git a/app/assets/onepager/js/send.js b/app/assets/onepager/js/send.js index a958c0dd829..e6cc9394695 100644 --- a/app/assets/onepager/js/send.js +++ b/app/assets/onepager/js/send.js @@ -1,4 +1,19 @@ /* eslint-disable no-console */ + +function changeTokens() { + if (document.fundRequest && document.fundRequest.token_name) { + const is_token_selected = document.fundRequest.token_name; + + $(`#token option:contains(${is_token_selected})`).attr('selected', 'selected'); + $('#amount').val(document.fundRequest.amount); + } + $('#token').select2().trigger('change'); +} + +window.addEventListener('tokensReady', function(e) { + changeTokens(); +}, false); + var get_gas_price = function() { if (typeof defaultGasPrice != 'undefined') { return defaultGasPrice; @@ -68,18 +83,21 @@ $(document).ready(function() { if (typeof userSearch != 'undefined') { userSearch('.username-search', true); } - set_metadata(); // jquery bindings $('#advanced_toggle').on('click', function() { advancedToggle(); }); $('#amount').on('keyup blur change', updateEstimate); $('#token').on('change', updateEstimate); - $('#send').on('click', function(e) { + $('#send').on('click', async function(e) { e.preventDefault(); if ($(this).hasClass('disabled')) return; loading_button($(this)); + + if (!provider) { + await onConnect(); + } // get form data var email = $('#email').val(); var github_url = $('#issueURL').val(); @@ -139,22 +157,6 @@ $(document).ready(function() { }); - waitforWeb3(function() { - tokens(document.web3network).forEach(function(ele) { - if (ele && ele.addr) { - const is_token_selected = $('#token').data('token') === ele.name ? ' selected' : ' '; - const html = ''; - - $('#token').append(html); - } - }); - let addr = tokenNameToDetails(document.web3network, document.default_token)['addr']; - - console.log(addr); - $('#token').val(addr).select2(); - jQuery('#token').select2(); - }); - }); function advancedToggle() { @@ -175,6 +177,7 @@ function isNumeric(n) { function sendTip(email, github_url, from_name, username, amount, comments_public, comments_priv, from_email, accept_tos, tokenAddress, expires, success_callback, failure_callback, is_for_bounty_fulfiller, noAvailableUser) { + set_metadata(); if (typeof web3 == 'undefined') { _alert({ message: gettext('You must have a web3 enabled browser to do this. Please download Metamask.') }, 'warning'); failure_callback(); @@ -329,7 +332,7 @@ function sendTip(email, github_url, from_name, username, amount, comments_public var send_erc20 = function() { var token_contract = new web3.eth.Contract(token_abi, tokenAddress); - token_contract.methods.transfer(destinationAccount, web3.utils.toHex(amountInDenom)).send({from: fromAccount}, post_send_callback); + token_contract.methods.transfer(destinationAccount, new web3.utils.BN(BigInt(amountInDenom)).toString()).send({from: fromAccount}, post_send_callback); }; var send_gas_money_and_erc20 = function() { _alert({ message: gettext('You will now be asked to confirm two transactions. The first is gas money, so your receipient doesnt have to pay it. The second is the actual token transfer. (note: check Metamask extension, sometimes the 2nd confirmation window doesnt popup)') }, 'info'); diff --git a/app/assets/v2/css/gitcoin.css b/app/assets/v2/css/gitcoin.css index 6a7579f1b70..e9a5558c6f4 100644 --- a/app/assets/v2/css/gitcoin.css +++ b/app/assets/v2/css/gitcoin.css @@ -121,6 +121,10 @@ h4, color: #0FCE7C; } +.gc-text-yellow { + color: #FFCE08; +} + .text-black-60 { color: rgba(0, 0, 0, 0.6)!important; } diff --git a/app/assets/v2/css/submit_bounty.css b/app/assets/v2/css/submit_bounty.css index 1c2d3a7b055..75537f53b4f 100644 --- a/app/assets/v2/css/submit_bounty.css +++ b/app/assets/v2/css/submit_bounty.css @@ -236,7 +236,7 @@ input:read-only { } .release-after-form-group-required { - border: 1px solid red; + border: none; padding: 5px; } diff --git a/app/assets/v2/js/base.js b/app/assets/v2/js/base.js index 80b547f0766..9881ff6b7f3 100644 --- a/app/assets/v2/js/base.js +++ b/app/assets/v2/js/base.js @@ -17,7 +17,10 @@ $(document).ready(function() { }); } - applyCartMenuStyles(); + // TODO: MOVE TO GRANTS shared + if (typeof CartData != 'undefined') { + applyCartMenuStyles(); + } $('body').on('click', '.copy_me', function() { $(this).focus(); diff --git a/app/assets/v2/js/cart-data.js b/app/assets/v2/js/cart-data.js index 1017e2f72c9..61a0328cd96 100644 --- a/app/assets/v2/js/cart-data.js +++ b/app/assets/v2/js/cart-data.js @@ -13,6 +13,27 @@ class CartData { return idList.includes(grantId); } + static share_url(title) { + const donations = this.loadCart(); + let bulk_add_cart = 'https://gitcoin.co/grants/cart/bulk-add/'; + + for (let i = 0; i < donations.length; i += 1) { + const donation = donations[i]; + + bulk_add_cart += String(donation['grant_id']) + ','; + } + + if (document.contxt['github_handle']) { + bulk_add_cart += ':' + document.contxt['github_handle']; + } + + if (title && typeof title != 'undefined') { + bulk_add_cart += ':' + encodeURI(title); + } + + return bulk_add_cart; + } + static addToCart(grantData) { if (this.cartContainsGrantWithId(grantData.grant_id)) { return; @@ -25,7 +46,15 @@ class CartData { network = 'mainnet'; } const acceptsAllTokens = (grantData.grant_token_address === '0x0000000000000000000000000000000000000000'); - const accptedTokenName = tokenAddressToDetailsByNetwork(grantData.grant_token_address, network).name; + + let accptedTokenName; + + try { + accptedTokenName = tokenAddressToDetailsByNetwork(grantData.grant_token_address, network).name; + } catch (e) { + // When numbers are too small toWei fails because there's too many decimal places + accptedTokenName = 'DAI'; + } if (acceptsAllTokens || 'DAI' == accptedTokenName) { grantData.grant_donation_amount = 5; diff --git a/app/assets/v2/js/cart.js b/app/assets/v2/js/cart.js index 02b387ebd8e..5e7fedbbd2a 100644 --- a/app/assets/v2/js/cart.js +++ b/app/assets/v2/js/cart.js @@ -247,7 +247,8 @@ Vue.component('grants-cart', { if (vm.code) { const verificationRequest = fetchData('/sms/validate/', 'POST', { - code: vm.code + code: vm.code, + phone: vm.phone }, {'X-CSRFToken': vm.csrf}); $.when(verificationRequest).then(response => { @@ -337,11 +338,21 @@ Vue.component('grants-cart', { window.location.href = `${window.location.origin}/login/github/?next=/grants/cart`; }, + confirmClearCart() { + if (confirm('are you sure')) { + this.clearCart(); + } + }, + clearCart() { CartData.setCart([]); this.grantData = []; update_cart_title(); }, + shareCart() { + _alert('Cart URL copied to clipboard', 'success', 1000); + copyToClipboard(CartData.share_url()); + }, removeGrantFromCart(id) { CartData.removeIdFromCart(id); @@ -629,14 +640,15 @@ Vue.component('grants-cart', { // Clear cart, redirect back to grants page, and show success alert localStorage.setItem('contributions_were_successful', 'true'); localStorage.setItem('contributions_count', String(this.grantData.length)); - this.clearCart(); var network = document.web3network; - if (network === 'rinkeby') { - window.location.href = `${window.location.origin}/grants/?network=rinkeby&category=`; - } else { - window.location.href = `${window.location.origin}/grants`; - } + setTimeout(function() { + if (network === 'rinkeby') { + window.location.href = `${window.location.origin}/grants/?network=rinkeby&category=`; + } else { + window.location.href = `${window.location.origin}/grants`; + } + }, 1500); }) .on('error', (error, receipt) => { // If the transaction was rejected by the network with a receipt, the second parameter will be the receipt. diff --git a/app/assets/v2/js/grants/fund.js b/app/assets/v2/js/grants/fund.js new file mode 100644 index 00000000000..5efaf7aa75e --- /dev/null +++ b/app/assets/v2/js/grants/fund.js @@ -0,0 +1,883 @@ +/* eslint-disable no-console */ +let deployedToken; +let deployedSubscription; +let tokenAddress; +let redirectURL; +let realPeriodSeconds = 0; +let selected_token; +let splitterAddress; +let gitcoinDonationAddress; + +document.suppress_faucet_solicitation = 1; + +var set_form_disabled = function(is_disabled) { + if (is_disabled) { + $('body').append('
 
'); + } else { + $('#intercept_overlay').remove(); + } +}; + + +$(document).ready(function() { + + // set defaults + var set_defaults = function() { + var lookups = { + 'frequency_unit': '#frequency_unit', + 'token_address': 'select[name=denomination]', + 'recurring_or_not': '#recurring_or_not', + 'real_period_seconds': '#real_period_seconds', + 'amount_per_period': 'input#amount', + 'comment': 'textarea[name=comment]', + 'num_periods': 'input[name=num_periods]', + 'gitcoin-grant-input-amount': '#gitcoin-grant-input-amount' + + }; + + for (key in lookups) { + if (key) { + const selector = lookups[key]; + const ls = localStorage.getItem('grants' + key); + + if (ls) { + $(selector).val(ls); + $(selector + ' option:eq("' + ls + '")').prop('selected', true); + } + } + } + }; + + + predictPhantomCLRMatch(); + predictCLRMatch(); + + $('#amount').on('input', () => { + predictCLRMatch(); + }); + + gitcoinDonationAddress = $('#gitcoin_donation_address').val(); + splitterAddress = $('#splitter_contract_address').val(); + + updateSummary(); + + $('#grants_form .nav-item').click(function(e) { + $('.nav-item a').removeClass('active'); + $(this).find('a').addClass('active'); + var targetid = $(this).find('a').data('target'); + var target = $('#' + targetid); + + $('.tab_target').addClass('hidden'); + target.removeClass('hidden'); + + e.preventDefault(); + }); + + $('#adjust').click(function(e) { + $(this).remove(); + $('.unhide_if_expanded').removeClass('hidden'); + e.preventDefault(); + }); + + $('#frequency_unit, #js-token').on('select2:select', event => { + updateSummary(); + }); + + $('input#frequency_count, input#amount, input#period').on('input', () => { + updateSummary(); + }); + + $('#gitcoin-grant-input-amount').on('input', () => { + $('.bot-heart').hide(); + updateSummary(); + + if ($('#gitcoin-grant-input-amount').val() == 0) { + $('#bot-heartbroken').show(); + } else if ($('#gitcoin-grant-input-amount').val() >= 20) { + $('#bot-heart-20').show(); + } else if ($('#gitcoin-grant-input-amount').val() >= 15) { + $('#bot-heart-15').show(); + } else if ($('#gitcoin-grant-input-amount').val() >= 10) { + $('#bot-heart-10').show(); + } else if ($('#gitcoin-grant-input-amount').val() > 0) { + $('#bot-heart-5').show(); + } + }); + + $('#gitcoin-grant-input-amount').on('focus', () => { + $('#gitcoin-grant-input-amount').removeClass('inactive'); + $('#gitcoin-grant-section .badge').addClass('inactive'); + }); + + $('#gitcoin-grant-section .badge').on('click', event => { + + $('#gitcoin-grant-section .badge').removeClass('inactive'); + $('#gitcoin-grant-input-amount').addClass('inactive'); + + const percentage = Number(event.currentTarget.getAttribute('data-percent')); + + $('#gitcoin-grant-input-amount').val(percentage); + $('.gitcoin-grant-percent').val(percentage); + + $('#gitcoin-grant-input-amount').trigger('input'); + + $('#gitcoin-grant-section .badge').removeClass('badge-active'); + $('#gitcoin-grant-section .badge').addClass('badge-inactive'); + + $(event.currentTarget).removeClass('badge-inactive'); + $(event.currentTarget).addClass('badge-active'); + }); + + $('input[name=match_direction]').change(function(e) { + let direction = $(this).val(); + + if (direction == '+') { + $('.est_direction').text('increase').css('background-color', 'yellow'); + setTimeout(function() { + $('.est_direction').css('background-color', 'white'); + }, 500); + $('.hide_wallet_address_container').removeClass('hidden'); + } else { + $('.est_direction').text('decrease').css('background-color', 'yellow'); + setTimeout(function() { + $('.est_direction').css('background-color', 'white'); + }, 500); + $('.hide_wallet_address_container').addClass('hidden'); + } + }); + + $('#js-token').change(function(e) { + const val = $(this).val(); + const is_eth = val == '0x0000000000000000000000000000000000000000'; + + if (val == '0xdac17f958d2ee523a2206206994597c13d831ec7') { + _alert('WARNING: USDT is not well supported, it is recommended to use $USDC or $DAI instead. More info here', 'error', 2000); + } + + if (is_eth && $('#recurring_or_not').val() == 'recurring') { + _alert('Sorry but this token is not supported for recurring donations', 'error', 1000); + } + if (is_eth) { + $('option[value=recurring]').attr('disabled', 'disabled'); + $('.contribution_type select').val('once').trigger('change'); + } else { + $('option[value=recurring]').attr('disabled', null); + } + }); + + + $('.contribution_type select').change(function(e) { + if ($('.contribution_type select').val() == 'once') { + $('.frequency').addClass('hidden'); + $('.num_recurring').addClass('hidden'); + $('.hide_if_onetime').addClass('hidden'); + $('.hide_if_recurring').removeClass('hidden'); + $('#period').val(4); + updateSummary(); + $('#amount_label').text('Amount'); + $('#negative').prop('disabled', ''); + $('label[for=negative]').css('color', 'black'); + $('#period').val(1); + } else { + $('.frequency').removeClass('hidden'); + $('.num_recurring').removeClass('hidden'); + $('#amount_label').text('Amount Per Period'); + $('.hide_if_onetime').removeClass('hidden'); + $('.hide_if_recurring').addClass('hidden'); + $('#positive').click(); + $('#negative').prop('disabled', 'disabled'); + $('label[for=negative]').css('color', 'grey'); + } + }); + $('.contribution_type select').trigger('change'); + + $('#js-fundGrant').submit(function(e) { + e.preventDefault(); + + if (!provider) { + return onConnect(); + } + + var data = {}; + var form = $(this).serializeArray(); + + + $.each(form, function() { + data[this.name] = this.value; + }); + + for (key in data) { + if (key) { + const val = data[key]; + var ls_key = 'grants' + key; + + localStorage.setItem(ls_key, val); + } + } + localStorage.setItem('grantsrecurring_or_not', $('#recurring_or_not').val()); + localStorage.setItem('grantstoken_address', $('#js-token').val()); + localStorage.setItem('grantsgitcoin-grant-input-amount', $('#gitcoin-grant-input-amount').val()); + + data.is_postive_vote = (data.match_direction == '-') ? 0 : 1; + + if (data.frequency_unit) { + + // translate timeAmount&timeType to requiredPeriodSeconds + let periodSeconds = 1; + + if (data.frequency_unit == 'days') { + periodSeconds *= 86400; + } else if (data.frequency_unit == 'hours') { + periodSeconds *= 3600; + } else if (data.frequency_unit == 'minutes') { + periodSeconds *= 60; + } else if (data.frequency_unit == 'months') { + periodSeconds *= 2592000; + } else if (data.frequency_unit == 'rounds') { + periodSeconds *= 2592001; + } + if (periodSeconds) { + realPeriodSeconds = periodSeconds; + } + } + + if (data.contract_version == 0) { + deployedSubscription = new web3.eth.Contract(compiledSubscription0.abi, data.contract_address); + } else if (data.contract_version == 1) { + deployedSubscription = new web3.eth.Contract(compiledSubscription1.abi, data.contract_address); + } + + if (data.token_address != '0x0000000000000000000000000000000000000000') { + selected_token = data.token_address; + deployedToken = new web3.eth.Contract(compiledToken.abi, data.token_address); + } else { + selected_token = data.denomination; + deployedToken = new web3.eth.Contract(compiledToken.abi, data.denomination); + $('#token_symbol').val($('#js-token option:selected').text()); + $('#token_address').val(selected_token); + data.token_address = selected_token; + } + + if (!selected_token) { + _alert('Please select a token', 'error'); + return; + } + + // eth payments + const is_eth = $('#js-token').val() == '0x0000000000000000000000000000000000000000'; + + if (is_eth && $('#recurring_or_not').val() == 'recurring') { + _alert('Sorry but ETH is not supported for recurring donations', 'error', 1000); + return; + } + + if (is_eth) { + const percent = $('#gitcoin-grant-input-amount').val(); + const to_addr_amount = parseInt((100 - percent) * 0.01 * data.amount_per_period * 10 ** 18); + const gitcoin_amount = parseInt((percent) * 0.01 * data.amount_per_period * 10 ** 18); + + web3.eth.getAccounts(function(err, accounts) { + indicateMetamaskPopup(); + var to_address = data.match_direction == '+' ? data.admin_address : gitcoinDonationAddress; + + set_form_disabled(true); + web3.eth.sendTransaction({ + from: accounts[0], + to: to_address, + value: to_addr_amount + }, function(err, txid) { + indicateMetamaskPopup(1); + if (err) { + console.log(err); + _alert('There was an error', 'error'); + set_form_disabled(false); + return; + } + $('#gas_price').val(1); + $('#sub_new_approve_tx_id').val(txid); + + var data = {}; + + $.each($('#js-fundGrant').serializeArray(), function() { + data[this.name] = this.value; + }); + saveSubscription(data, true); + var success_callback = function(err, new_txid) { + indicateMetamaskPopup(1); + if (err) { + console.log(err); + _alert('There was an error', 'error'); + set_form_disabled(false); + return; + } + data = { + 'subscription_hash': 'onetime', + 'signature': 'onetime', + 'csrfmiddlewaretoken': $("#js-fundGrant input[name='csrfmiddlewaretoken']").val(), + 'sub_new_approve_tx_id': txid + }; + saveSplitTx(data, new_txid, true); + + waitforData(() => { + document.suppress_loading_leave_code = true; + window.location = window.location.href.replace('/fund', ''); + }); + + const linkURL = get_etherscan_url(new_txid); + + document.issueURL = linkURL; + + $('#transaction_url').attr('href', linkURL); + enableWaitState('#grants_form'); + set_form_disabled(false); + $('#tweetModal').css('display', 'block'); + + }; + + if (!gitcoin_amount) { + success_callback(null, txid); + } else { + indicateMetamaskPopup(); + web3.eth.sendTransaction({ + from: accounts[0], + to: gitcoinDonationAddress, + value: gitcoin_amount + }, success_callback); + } + }); + }); + return; + } + + // erc20 + tokenAddress = data.token_address; + + deployedToken.methods.decimals().call(function(err, decimals) { + if (err) { + console.log(err); + _alert('The token you selected is not a valid ERC20 token', 'error'); + return; + } + + let realTokenAmount = Number(data.amount_per_period * Math.pow(10, decimals)); + let realApproval; + const approve_buffer = 100000; + + if (data.contract_version == 1 || data.num_periods == 1) { + + realApproval = Number(((grant_amount + gitcoin_grant_amount) * data.num_periods * Math.pow(10, decimals)) + approve_buffer); + } else if (data.contract_version == 0) { + console.log('grant amount: ' + grant_amount); + console.log('gitcoin grant amount: ' + gitcoin_grant_amount); + // don't need to approve for gitcoin_grant_amount since we will directly transfer it + realApproval = Number(((grant_amount * data.num_periods)) * Math.pow(10, decimals) + approve_buffer); + } + + const realGasPrice = Number(gitcoin_grant_amount * Math.pow(10, decimals)); // optional grants fee + + + $('#gas_price').val(realGasPrice); + + let approvalSTR = realApproval.toLocaleString('fullwide', { useGrouping: false }); + + web3.eth.getAccounts(function(err, accounts) { + + $('#contributor_address').val(accounts[0]); + + var approvalAddress; + + if (data.num_periods == 1) { + approvalAddress = splitterAddress; + } else { + approvalAddress = data.contract_address; + } + + // ERC20 + deployedToken.methods.balanceOf( + accounts[0] + ).call().then(function(result) { + if (result < realTokenAmount) { + _alert({ message: gettext('You do not have enough tokens to make this transaction.')}, 'error'); + } else { + set_form_disabled(true); + indicateMetamaskPopup(); + deployedToken.methods.approve( + approvalAddress, + web3.utils.toTwosComplement(approvalSTR) + ).send({ + from: accounts[0], + gas: web3.utils.toHex(gas_amount(document.location.href)), + gasLimit: web3.utils.toHex(gas_amount(document.location.href)) + }).on('error', function(error) { + indicateMetamaskPopup(true); + set_form_disabled(false); + console.log('1', error); + _alert({ message: gettext('Your approval transaction failed. Please try again.')}, 'error'); + }).on('transactionHash', function(transactionHash) { + indicateMetamaskPopup(true); + $('#sub_new_approve_tx_id').val(transactionHash); + if (data.num_periods == 1) { + // call splitter after approval + var to_address = data.match_direction == '+' ? data.admin_address : gitcoinDonationAddress; + + var first = Number(grant_amount * Math.pow(10, decimals)).toLocaleString('fullwide', {useGrouping: false}); + var second = Number(gitcoin_grant_amount * Math.pow(10, decimals)).toLocaleString('fullwide', {useGrouping: false}); + + splitPayment(accounts[0], to_address, gitcoinDonationAddress, first, second); + } else { + if (data.contract_version == 0 && gitcoin_grant_amount > 0) { + donationPayment(deployedToken, accounts[0], Number(gitcoin_grant_amount * Math.pow(10, decimals)).toLocaleString('fullwide', {useGrouping: false})); + } + subscribeToGrant(transactionHash); + } + }).on('confirmation', function(confirmationNumber, receipt) { + waitforData(() => { + document.suppress_loading_leave_code = true; + window.location = redirectURL; + }); // waitforData + }); // approve on confirmation + } // if (result < realTokenAmount) + }); // check token balance + }); // getAccounts + }); // decimals + }); // validate + + waitforWeb3(function() { + if (!provider) { + needWalletConnection(); + } + if (document.web3network != $('#network').val()) { + $('#js-fundGrant-button').prop('disabled', true); + let network = $('#network').val(); + + _alert({ message: gettext('This Grant is on the ' + network + ' network. Please, switch to ' + network + ' to contribute to this grant.') }, 'error'); + } + + tokens(document.web3network).forEach(function(ele) { + let option = document.createElement('option'); + + option.text = ele.name; + option.value = ele.addr; + + $('#js-token').append($(' + `; } else { options += ` diff --git a/app/assets/v2/js/grants/new.js b/app/assets/v2/js/grants/new.js index 09db5b3a064..9b483ade59f 100644 --- a/app/assets/v2/js/grants/new.js +++ b/app/assets/v2/js/grants/new.js @@ -68,9 +68,12 @@ $('#new_button').on('click', function(e) { }); const init = () => { + /* + KO - commenting out during grants deploy, double check with Octavio if (!provider) { return onConnect(); } + */ if (localStorage['grants_quickstart_disable'] !== 'true') { window.location = document.location.origin + '/grants/quickstart'; @@ -106,7 +109,8 @@ const init = () => { validator.showErrors({ 'admin_address': 'Please check your address!' }); - return _alert({ message: gettext('Please check your address and try again.') }, 'error'); + _alert({ message: gettext('Please check your address and try again.') }, 'error'); + return false; }); } }); @@ -121,7 +125,7 @@ const init = () => { var msg = 'You have specified ' + recipient_addr + ' as the grant funding recipient address. Please TRIPLE CHECK that this is the correct address to receive funds for contributions to this grant. If access to this address is lost, you will not be able to access funds from contributors to this grant.'; if (!confirm(msg)) { - return; + return false; } $(form).find(':input:disabled').removeAttr('disabled'); @@ -201,3 +205,15 @@ const init = () => { $(this).removeAttr('title'); }); }; + +window.addEventListener('load', async() => { + if (!provider && !web3Modal.cachedProvider || provider === 'undefined') { + onConnect().then(() => { + init(); + }); + } else { + web3Modal.on('connect', async(data) => { + init(); + }); + } +}); diff --git a/app/assets/v2/js/pages/create_bounty/ETH.js b/app/assets/v2/js/pages/create_bounty/ETH.js index 39b89a5d191..92ea0ae01d8 100644 --- a/app/assets/v2/js/pages/create_bounty/ETH.js +++ b/app/assets/v2/js/pages/create_bounty/ETH.js @@ -113,8 +113,10 @@ const ethCreateBounty = async (data) => { const account = selectedAccount; console.log(selectedAccount) const amountNoDecimal = amount; - amount = amount * decimalDivisor; + + const bigAmount = new web3.utils.BN(BigInt(amount)).toString(); + // Create the bounty object. // This function instantiates a contract from the existing deployed Standard Bounties Contract. // bounty_abi is a giant object containing the different network options @@ -211,29 +213,21 @@ const ethCreateBounty = async (data) => { // The Ethereum network requires using ether to do stuff on it // issueAndActivateBounty is a method defined in the StandardBounties solidity contract. - const eth_amount = isETH ? amount : 0; + const eth_amount = isETH ? bigAmount : 0; const _paysTokens = !isETH; - console.log( - account, - mock_expire_date, - result, - String(amount), - '0xf209d2b723b6417cbf04c07e733bee776105a073', - _paysTokens, - tokenAddress, - String(amount)) + bounty.methods.issueAndActivateBounty( account, // _issuer mock_expire_date, // _deadline result, // _data (ipfs hash) - String(amount), // _fulfillmentAmount + bigAmount, // _fulfillmentAmount '0xf209d2b723b6417cbf04c07e733bee776105a073', // _arbiter _paysTokens, // _paysTokens tokenAddress, // _tokenContract - String(amount) // _value + bigAmount, // _value ).send({ from: account, - value: String(eth_amount), + value: eth_amount, gas: web3.utils.toHex(318730), gasLimit: web3.utils.toHex(318730) }).then((result) => {web3Callback(result)}).catch(err => { @@ -242,10 +236,26 @@ const ethCreateBounty = async (data) => { }); } + const checkTokenAllowance = async () => { + let currentAllowance = await getAllowance(bounty_address(), tokenAddress); + + if (currentAllowance < bigAmount) { + let approvedAllowance; + + approvedAllowance = await approveAllowance( + bounty_address(), + tokenAddress + ); + } + } + var do_bounty = function(callback) { - handleTokenAuth().then(() => { + handleTokenAuth().then(async () => { const fee = Number((Number(data.amount) * FEE_PERCENTAGE).toFixed(4)); const to_address = '0x00De4B13153673BCAE2616b67bf822500d325Fc3'; + if (!isETH) { + await checkTokenAllowance(); + } console.log(fee) indicateMetamaskPopup(); if (FEE_PERCENTAGE == 0) { @@ -276,9 +286,10 @@ const ethCreateBounty = async (data) => { }); } else { const amountInWei = fee * 1.0 * Math.pow(10, token.decimals); + const amountAsString = new web3.utils.BN(BigInt(amountInWei)).toString(); const token_contract = new web3.eth.Contract(token_abi, tokenAddress); - token_contract.methods.transfer(to_address, web3.utils.toHex(amountInWei)).send({from: selectedAccount}, + token_contract.methods.transfer(to_address, web3.utils.toHex(amountAsString)).send({from: selectedAccount}, function(error, txnId) { indicateMetamaskPopup(true); if (error) { diff --git a/app/assets/v2/js/pages/kudos_send.js b/app/assets/v2/js/pages/kudos_send.js index 4f7ea01f50d..884d689c7e2 100644 --- a/app/assets/v2/js/pages/kudos_send.js +++ b/app/assets/v2/js/pages/kudos_send.js @@ -167,7 +167,6 @@ $(document).ready(function() { }); }, 100); - set_metadata(); // jquery bindings $('#advanced_toggle').on('click', function(e) { e.preventDefault(); @@ -212,6 +211,7 @@ $(document).ready(function() { $('#send').on('click', function(e) { e.preventDefault(); + set_metadata(); if (typeof web3 == 'undefined') { _alert({ message: gettext('You must have a web3 enabled browser to do this. Please download Metamask.') }, 'warning'); diff --git a/app/assets/v2/js/pages/new_bounty.js b/app/assets/v2/js/pages/new_bounty.js index 2b29a481fac..9802d25290b 100644 --- a/app/assets/v2/js/pages/new_bounty.js +++ b/app/assets/v2/js/pages/new_bounty.js @@ -60,7 +60,7 @@ const updateOnNetworkOrTokenChange = () => { function changeUi() { $('.eth-chain').show(); FEE_PERCENTAGE = document.FEE_PERCENTAGE / 100.0; - + $('#navbar-network-banner').show(); $('.navbar-network').show(); @@ -185,7 +185,7 @@ const getSuggestions = () => { } obj.children.forEach((children, childIndex) => { - children.text = children.fulfiller_github_username || children.user__profile__handle || children.handle; + children.text = children.fulfiller_github_username || children.user__profile__handle || children.profile__handle || children.handle; children.id = generalIndex; if (obj.text == 'Invites') { children.selected = true; diff --git a/app/assets/v2/js/pages/process_bounty.js b/app/assets/v2/js/pages/process_bounty.js index 35214252697..976ebe2d60a 100644 --- a/app/assets/v2/js/pages/process_bounty.js +++ b/app/assets/v2/js/pages/process_bounty.js @@ -215,7 +215,7 @@ window.onload = function() { errormsg = gettext('No claimee found for this bounty.'); } - if (fromAddress != selectedAccount) { + if (fromAddress.toLowerCase() != selectedAccount.toLowerCase()) { errormsg = gettext('You can only process a funded issue if you submitted it initially.'); } diff --git a/app/assets/v2/js/search.js b/app/assets/v2/js/search.js index 5aa287d02f9..f4edf39b7e6 100644 --- a/app/assets/v2/js/search.js +++ b/app/assets/v2/js/search.js @@ -40,9 +40,11 @@ if (document.getElementById('gc-search')) { }, search: async function() { let vm = this; + let thisDate = new Date(); if (vm.term.length >= 2) { vm.isLoading = true; + document.current_search = thisDate; const response = await fetchData( `/api/v0.1/search/?term=${vm.term}`, @@ -50,7 +52,9 @@ if (document.getElementById('gc-search')) { ); // let response = [{"title": "bounty event test cooperative", "description": "https://github.com/danlipert/gitcoin-test/issues/38https://github.com/danlipert/gitcoin-test/issues/38https://github.com/danlipert/gitcoin-test/issues/38https://github.com/danlipert/gitcoin-test/issues/38https://github.com/danlipert/gitcoin-test/issues/38", "full_search": "bounty event test cooperativehttps://github.com/danlipert/gitcoin-test/issues/38https://github.com/danlipert/gitcoin-test/issues/38https://github.com/danlipert/gitcoin-test/issues/38https://github.com/danlipert/gitcoin-test/issues/38https://github.com/danlipert/gitcoin-test/issues/38Bounty", "url": "https://gitcoin.co/issue/danlipert/gitcoin-test/39/2533", "pk": 15138, "img_url": "https://gitcoin.co/dynamic/avatar/danlipert/1", "timestamp": "2020-04-11T10:26:33.327518+00:00", "source_type": "Bounty"}, {"title": "Gitcoin Ambassador", "description": "This Kudos can be awarded only by Gitcoin team, and signifies that one was a Gitcoin ambassador during Fall 2018 and beyond.", "full_search": "Gitcoin AmbassadorThis Kudos can be awarded only by Gitcoin team, and signifies that one was a Gitcoin ambassador during Fall 2018 and beyond.Kudos", "url": "https://gitcoin.co/kudos/486/gitcoin_ambassador", "pk": 94898, "img_url": "https://gitcoin.co/dynamic/kudos/486/gitcoin_ambassador", "timestamp": "2020-04-11T11:32:06.394147+00:00", "source_type": "Kudos"}, {"title": "Gitcoin Genesis", "description": "The Gitcoin Genesis Badge is the rarest of the Gitcoin team badges. Owners of this badge contributed to Gitcoin in a meaningful way in the way-back-when.", "full_search": "Gitcoin GenesisThe Gitcoin Genesis Badge is the rarest of the Gitcoin team badges. Owners of this badge contributed to Gitcoin in a meaningful way in the way-back-when.Kudos", "url": "https://gitcoin.co/kudos/3/gitcoin_genesis", "pk": 94470, "img_url": "https://gitcoin.co/dynamic/kudos/3/gitcoin_genesis", "timestamp": "2020-04-10T17:03:20.171063+00:00", "source_type": "Kudos"}, {"title": "Gitcoin Torchbearer", "description": "For the Gitcoin Grants TorchBearers", "full_search": "Gitcoin TorchbearerFor the Gitcoin Grants TorchBearersKudos", "url": "https://gitcoin.co/kudos/1904/gitcoin_torchbearer", "pk": 94775, "img_url": "https://gitcoin.co/dynamic/kudos/1904/gitcoin_torchbearer", "timestamp": "2020-04-11T11:26:46.881200+00:00", "source_type": "Kudos"}, {"title": "Gitcoin Genesis", "description": "The Gitcoin Genesis Badge is the rarest of the Gitcoin team badges. Owners of this badge contributed to Gitcoin in a meaningful way in the way-back-when.", "full_search": "Gitcoin GenesisThe Gitcoin Genesis Badge is the rarest of the Gitcoin team badges. Owners of this badge contributed to Gitcoin in a meaningful way in the way-back-when.Kudos", "url": "https://gitcoin.co/kudos/53/gitcoin_genesis", "pk": 94915, "img_url": "https://gitcoin.co/dynamic/kudos/53/gitcoin_genesis", "timestamp": "2020-04-11T10:26:48.271323+00:00", "source_type": "Kudos"}, {"title": "Gitcoin Quests 101", "description": "This Quest is an intro to Gitcoin Quests (how meta!)", "full_search": "Gitcoin Quests 101This Quest is an intro to Gitcoin Quests (how meta!)Quest", "url": "https://gitcoin.co/quests/43/gitcoin-quests-101", "pk": 17139, "img_url": "https://gitcoin.co/dynamic/kudos/4550/grants_round_3_contributor_-_futurism_bot", "timestamp": "2020-04-11T15:25:54.813955+00:00", "source_type": "Quest"}, {"title": "gitcoin mock issue", "description": "####gitcoin mock issue\r\n", "full_search": "gitcoin mock issue####gitcoin mock issue\r\nBounty", "url": "https://gitcoin.co/issue/Korridzy/range/1/566", "pk": 12982, "img_url": "https://gitcoin.co/dynamic/avatar/Korridzy/1", "timestamp": "2020-04-11T02:26:36.194393+00:00", "source_type": "Bounty"}, {"title": "Gitcoin Testing", "description": "Testing Gitcoin Functionality with this issue.", "full_search": "Gitcoin TestingTesting Gitcoin Functionality with this issue.Bounty", "url": "https://gitcoin.co/issue/UniBitProject/wallet/18/1476", "pk": 14619, "img_url": "https://gitcoin.co/dynamic/avatar/UniBitProject/1", "timestamp": "2020-04-10T19:26:57.564235+00:00", "source_type": "Bounty"}, {"title": "Gitcoin and StandardBounties 101", "description": "Whats the difference between Gitcoin and StandardBounties?", "full_search": "Gitcoin and StandardBounties 101Whats the difference between Gitcoin and StandardBounties?Quest", "url": "https://gitcoin.co/quests/32/gitcoin-and-standardbounties-101", "pk": 17140, "img_url": "https://gitcoin.co/dynamic/kudos/105/bee_of_all_trades", "timestamp": "2020-04-11T15:25:54.483253+00:00", "source_type": "Quest"}, {"title": "Gitcoin Robot Friend", "description": "This kudos for those who wanna see a gitcoin robot friend", "full_search": "Gitcoin Robot FriendThis kudos for those who wanna see a gitcoin robot friendKudos", "url": "https://gitcoin.co/kudos/12477/gitcoin_robot_friend", "pk": 191834, "img_url": "https://gitcoin.co/dynamic/kudos/12477/gitcoin_robot_friend", "timestamp": "2020-04-08T18:24:39.350902+00:00", "source_type": "Kudos"}]; - vm.results = groupBySource(response); + if (document.current_search == thisDate) { + vm.results = groupBySource(response); + } vm.isLoading = false; } else { vm.results = {}; @@ -60,6 +64,7 @@ if (document.getElementById('gc-search')) { } }); } +document.current_search = new Date(); const groupBySource = results => { let grouped_result = {}; diff --git a/app/assets/v2/js/shared.js b/app/assets/v2/js/shared.js index f1b4f2b0167..348537d0a08 100644 --- a/app/assets/v2/js/shared.js +++ b/app/assets/v2/js/shared.js @@ -1026,8 +1026,17 @@ const updateMultipleParams = (newParams) => { for (const [ key, value ] of newParams) { params.set(key, value); } + let category_str = ''; - window.location.href = '/grants/?' + decodeURIComponent(params.toString()); + if (params.get('type')) { + category_str = params.get('type') + '/'; + params.delete('type'); + } + if (!params.get('category')) { + params.delete('category'); + } + + window.location.href = '/grants/' + category_str + '?' + decodeURIComponent(params.toString()); }; diff --git a/app/assets/v2/js/theme_switcher.js b/app/assets/v2/js/theme_switcher.js index fd7ea97ce5e..ba03596a754 100644 --- a/app/assets/v2/js/theme_switcher.js +++ b/app/assets/v2/js/theme_switcher.js @@ -12,6 +12,9 @@ if (theme === 'dark' || (!theme && window.matchMedia('(prefers-color-scheme: dar document.addEventListener('DOMContentLoaded', subscribe, false); function subscribe() { + if (!document.contxt['github_handle']) { + return; + } // Handle preferred user theme mode document.getElementById('theme-switcher').addEventListener('click', onToggleMode); diff --git a/app/assets/v2/js/wallet.js b/app/assets/v2/js/wallet.js index 2374da4ff22..628cc0bbb43 100644 --- a/app/assets/v2/js/wallet.js +++ b/app/assets/v2/js/wallet.js @@ -308,6 +308,10 @@ $('.selected-account').click(function(e) { }); window.addEventListener('load', async() => { + if (!document.contxt['github_handle']) { + return; + } + initWallet(); if (web3Modal.cachedProvider) { try { @@ -407,6 +411,25 @@ let minABI = [ }], 'payable': false, 'type': 'function' + }, + // approve allowance + { + 'constant': false, + 'inputs': [{ + 'name': '_spender', + 'type': 'address' + }, + { + 'name': '_value', + 'type': 'uint256' + }], + 'name': 'approve', + 'outputs': [{ + 'name': 'success', + 'type': 'bool' + }], + 'payable': false, + 'type': 'function' } ]; @@ -449,7 +472,7 @@ async function getTokenBalances(tokenAddress) { /** * * Check the allowance remaining on a contract address. * * @param {string} address - the contract address - * * @param {string} tokenAddress - the token address + * * @param {string} tokenAddress - the token address contract * */ async function getAllowance(address, tokenAddress) { let allowance; @@ -459,3 +482,19 @@ async function getAllowance(address, tokenAddress) { console.log(await allowance); return await allowance; } + +/** + * * Approve allowance for a contract address. + * * @param {string} address - the contract address + * * @param {string} tokenAddress - the token address contract + * * @param {string} weiamount (optional)- the token address + * */ +async function approveAllowance(address, tokenAddress, weiamount) { + let defaultAmount = new web3.utils.BN(BigInt(10 * 18 * 9999999999999999999999999999999999999999999999999999)).toString(); + let amount = weiamount || defaultAmount; // uint256 + let approved; + let tokensContract = new web3.eth.Contract(minABI, tokenAddress); + + approved = tokensContract.methods.approve(address, amount).send({from: selectedAccount}); + return await approved; +} diff --git a/app/dashboard/admin.py b/app/dashboard/admin.py index 6df795dedc5..32a798cab44 100644 --- a/app/dashboard/admin.py +++ b/app/dashboard/admin.py @@ -489,7 +489,7 @@ class AnswersAdmin(admin.ModelAdmin): class ProfileVerificationAdmin(admin.ModelAdmin): - list_display = ['id', 'caller_type', 'mobile_network_code', 'country_code', 'carrier_name', 'carrier_type', + list_display = ['id', 'profile', 'success', 'validation_passed', 'caller_type', 'mobile_network_code', 'country_code', 'carrier_name', 'carrier_type', 'phone_number', 'carrier_error_code'] raw_id_fields = ['profile'] diff --git a/app/dashboard/migrations/0121_merge_20200612_0200.py b/app/dashboard/migrations/0121_merge_20200612_0200.py deleted file mode 100644 index f35965b25de..00000000000 --- a/app/dashboard/migrations/0121_merge_20200612_0200.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 2.2.4 on 2020-06-12 02:00 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('dashboard', '0119_auto_20200605_2036'), - ('dashboard', '0120_auto_20200612_0207'), - ] - - operations = [ - ] diff --git a/app/dashboard/migrations/0122_merge_20200612_2059.py b/app/dashboard/migrations/0121_merge_20200615_1236.py similarity index 65% rename from app/dashboard/migrations/0122_merge_20200612_2059.py rename to app/dashboard/migrations/0121_merge_20200615_1236.py index 15e533ecbd5..4e7a3e0e1dc 100644 --- a/app/dashboard/migrations/0122_merge_20200612_2059.py +++ b/app/dashboard/migrations/0121_merge_20200615_1236.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.4 on 2020-06-12 20:59 +# Generated by Django 2.2.4 on 2020-06-15 12:36 from django.db import migrations @@ -6,8 +6,8 @@ class Migration(migrations.Migration): dependencies = [ - ('dashboard', '0121_merge_20200612_0200'), ('dashboard', '0119_profile_pref_do_not_track'), + ('dashboard', '0120_auto_20200612_0207'), ] operations = [ diff --git a/app/dashboard/migrations/0122_auto_20200615_1510.py b/app/dashboard/migrations/0122_auto_20200615_1510.py new file mode 100644 index 00000000000..c0024b8f99e --- /dev/null +++ b/app/dashboard/migrations/0122_auto_20200615_1510.py @@ -0,0 +1,28 @@ +# Generated by Django 2.2.4 on 2020-06-15 15:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dashboard', '0121_merge_20200615_1236'), + ] + + operations = [ + migrations.AddField( + model_name='profileverification', + name='success', + field=models.BooleanField(default=False, help_text='Was a successful transaction verification?'), + ), + migrations.AddField( + model_name='profileverification', + name='validation_comment', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='profileverification', + name='validation_passed', + field=models.BooleanField(default=False, help_text='Did the initial validation pass?'), + ), + ] diff --git a/app/dashboard/models.py b/app/dashboard/models.py index bf1aed8d7d0..68569ae4874 100644 --- a/app/dashboard/models.py +++ b/app/dashboard/models.py @@ -5165,6 +5165,10 @@ class ProfileVerification(SuperModel): country_code = models.CharField(max_length=5, null=True, blank=True) phone_number = models.CharField(max_length=150, null=True, blank=True) delivery_method = models.CharField(max_length=255, null=True, blank=True) + validation_passed = models.BooleanField(help_text=_('Did the initial validation pass?'), default=False) + validation_comment = models.CharField(max_length=255, null=True, blank=True) + success = models.BooleanField(help_text=_('Was a successful transaction verification?'), default=False) + def __str__(self): return f'{self.phone_number} ({self.caller_type}) from {self.country_code} request ${self.delivery_method} code at {self.created_on}' diff --git a/app/dashboard/tasks.py b/app/dashboard/tasks.py index 618a2e665cd..fdb7b4c3e1e 100644 --- a/app/dashboard/tasks.py +++ b/app/dashboard/tasks.py @@ -158,10 +158,13 @@ def increment_view_count(self, pks, content_type, user_id, view_type, retry: boo key = f"{content_type}_{pk}" print(key) result = redis.incr(key) - if pk: - ObjectView.objects.create( - viewer=user, - target_id=pk, - target_type=ContentType.objects.filter(model=content_type).first(), - view_type=view_type, - ) + if pk and view_type == 'individual': + try: + ObjectView.objects.create( + viewer=user, + target_id=pk, + target_type=ContentType.objects.filter(model=content_type).first(), + view_type=view_type, + ) + except: + pass # fix for https://sentry.io/organizations/gitcoin/issues/1715509732/ diff --git a/app/dashboard/templates/bounty/fund.html b/app/dashboard/templates/bounty/fund.html new file mode 100644 index 00000000000..55dcb39130c --- /dev/null +++ b/app/dashboard/templates/bounty/fund.html @@ -0,0 +1,362 @@ +{% comment %} + Copyright (C) 2020 Gitcoin Core + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +{% endcomment %} +{% load i18n static %} + + + + + {% include 'shared/head.html' %} + {% include 'shared/cards.html' %} + + + + + + + + {% include 'shared/tag_manager_2.html' %} +
+ {% include 'shared/top_nav.html' with class='d-md-flex' %} + {% include 'shared/nav.html' %} +
+
+ +
+
+ +
+

{% trans "Fund Issue" %}

+

Fund your GitHub issue and work with talented developers!

+ + + +
+

{% trans "Last Synced " %}

+ +
+
+ +
+
+
+ + +
+
+ + +
+
+ + + {% include 'shared/pricing.html' %} + +
+
+ + + + + +
+ {% include 'shared/bounty_details.html' %} +
+ +
+ {% include 'shared/bounty_categories.html' %} +
+ +
+ {% include 'shared/bounty_keywords.html' %} +
+ +
+ +
+ +
+ {% include 'shared/issue_details.html' %} + {% include 'shared/issue_deadline.html' %} +
+ + +
+
+
+ + + +
+ +
+
+ +
+ + +
+ +
+ {% if events %} +
+ +
+ + +
+
+ +
+ +
+
+
+ {% endif %} +
+
+ +
+ +
+
+ {% include 'shared/notification_email.html' with show_information_publicly_checkbox=1 %} + {% include 'shared/github_username.html' %} +
+
+
+ + + +
+ + {% if FEE_PERCENTAGE != 0 %} +
+
+ {% trans "No Surprises" %} +
+
+
+

+ Simply pay the bounty amount ( plus a {% if FEE_PERCENTAGE == 10 %} standard {% else %} discounted {% endif %} + 10% Gitcoin platform fee ). + This covers our costs for finding quality contributors to join our platform so that you get the best work. + If your business requires additional assistance, please contact us founders@gitcoin.co +

+
+
+
+

+

+ (10%) +

+
+
+
+
+ {% endif %} + +
+
+ + +
+
+ + +
+
+ +
+ {% include 'shared/bounty_actions_hidden_vars.html' %} +
+
+ +
+
+
+
{% trans "Total"%}
+
+
+

{% trans "Payment Due" %}

+ + +

+ Bounty 0 + ETH + + + + {% if FEE_PERCENTAGE != 0 %} + + + 0 + ETH Gitcoin Platform Fee + {% else %} + + No Fees + {% endif %} + +

+

+ You will have to approve + + {% if FEE_PERCENTAGE == 0 %} 1 {% else %} 2 {% endif %} + web3 wallet confirmations. + +

+
+
+
+ +
+ +

+ Your transaction is secured by the audited StandardBounties contract on the Ethereum blockchain. + Learn more here. +

+
+
+
+ +
+ + + + + {% include 'shared/bottom_notification.html' %} + {% include 'shared/analytics.html' %} + {% include 'shared/footer_scripts_lite.html' %} + {% include 'shared/footer.html' %} + + + +{% include 'shared/current_profile.html' %} + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/dashboard/templates/dashboard/index-vue.html b/app/dashboard/templates/dashboard/index-vue.html index f448dd9a270..71de648bd0a 100644 --- a/app/dashboard/templates/dashboard/index-vue.html +++ b/app/dashboard/templates/dashboard/index-vue.html @@ -94,7 +94,7 @@

-
+
-
+