From c4e1aa87665e072e02b5a01932cd4e695836fb10 Mon Sep 17 00:00:00 2001 From: Aditya Anand M C Date: Mon, 21 Jun 2021 17:38:04 +0530 Subject: [PATCH] feat: algorand grants integration --- app/assets/v2/js/cart.js | 14 ++ app/assets/v2/js/grants/_detail-component.js | 1 + app/assets/v2/js/grants/_new.js | 4 + .../v2/js/grants/cart/algorand_extension.js | 210 ++++++++++++++++++ app/assets/v2/js/grants/funding.js | 3 + .../pages/bounty_detail/algorand_extension.js | 1 - app/assets/v2/js/pages/bounty_details2.js | 2 +- app/grants/admin.py | 2 +- .../commands/sync_pending_contributions.py | 2 +- app/grants/models.py | 17 +- app/grants/sync/algorand.py | 106 +++++++++ app/grants/templates/grants/_new.html | 22 ++ .../templates/grants/bulk_add_to_cart.html | 1 + app/grants/templates/grants/cart-vue.html | 3 + .../grants/cart/extension/algorand.html | 117 ++++++++++ .../templates/grants/components/card.html | 1 + .../grants/detail/template-grant-details.html | 13 ++ app/grants/utils.py | 5 +- app/grants/views.py | 13 +- 19 files changed, 527 insertions(+), 10 deletions(-) create mode 100644 app/assets/v2/js/grants/cart/algorand_extension.js create mode 100644 app/grants/sync/algorand.py create mode 100644 app/grants/templates/grants/cart/extension/algorand.html diff --git a/app/assets/v2/js/cart.js b/app/assets/v2/js/cart.js index f1dab840121..85e2cea4495 100644 --- a/app/assets/v2/js/cart.js +++ b/app/assets/v2/js/cart.js @@ -90,6 +90,9 @@ Vue.component('grants-cart', { ], 'RSK': [ `${static_url}v2/js/grants/cart/rsk_extension.js` + ], + 'ALGORAND': [ + `${static_url}v2/js/grants/cart/algorand_extension.js` ] } }; @@ -369,6 +372,11 @@ Vue.component('grants-cart', { isBinanceExtInstalled() { return window.BinanceChain || false; }, + + isAlgorandExtInstalled() { + return window.AlgoSigner || false; + }, + isRskExtInstalled() { const rskHost = 'https://public-node.rsk.co'; const rskClient = new Web3(); @@ -468,6 +476,9 @@ Vue.component('grants-cart', { case 'RSK': vm.chainId = '30'; break; + case 'ALGORAND': + vm.chainId = '1001'; + break; } }, confirmQRPayment: function(e, grant) { @@ -543,6 +554,9 @@ Vue.component('grants-cart', { let vm = this; switch (tenant) { + case 'ALGORAND': + contributeWithAlgorandExtension(grant, vm); + break; case 'RSK': contributeWithRskExtension(grant, vm); break; diff --git a/app/assets/v2/js/grants/_detail-component.js b/app/assets/v2/js/grants/_detail-component.js index c0c8eef2736..11735236a4b 100644 --- a/app/assets/v2/js/grants/_detail-component.js +++ b/app/assets/v2/js/grants/_detail-component.js @@ -82,6 +82,7 @@ Vue.mixin({ 'polkadot_payout_address': vm.grant.polkadot_payout_address, 'kusama_payout_address': vm.grant.kusama_payout_address, 'rsk_payout_address': vm.grant.rsk_payout_address, + 'algorand_payout_address': vm.grant.algorand_payout_address, 'region': vm.grant.region?.name || undefined, 'has_external_funding': vm.grant.has_external_funding }; diff --git a/app/assets/v2/js/grants/_new.js b/app/assets/v2/js/grants/_new.js index 65ac9c55d57..ae19d88c9f5 100644 --- a/app/assets/v2/js/grants/_new.js +++ b/app/assets/v2/js/grants/_new.js @@ -127,6 +127,8 @@ Vue.mixin({ vm.$set(vm.errors, 'kusama_payout_address', 'Please enter Kusama address'); } else if (vm.chainId == 'rsk' && !vm.form.rsk_payout_address) { vm.$set(vm.errors, 'rsk_payout_address', 'Please enter RSK address'); + } else if (vm.chainId == 'algorand' && !vm.form.algorand_payout_address) { + vm.$set(vm.errors, 'algorand_payout_address', 'Please enter Algorand address'); } if (!vm.form.grant_type) { @@ -180,6 +182,7 @@ Vue.mixin({ 'polkadot_payout_address': form.polkadot_payout_address, 'kusama_payout_address': form.kusama_payout_address, 'rsk_payout_address': form.rsk_payout_address, + 'algorand_payout_address': form.algorand_payout_address, 'grant_type': form.grant_type, 'categories[]': form.grant_categories, 'network': form.network, @@ -363,6 +366,7 @@ if (document.getElementById('gc-new-grant')) { polkadot_payout_address: '', kusama_payout_address: '', rsk_payout_address: '', + algorand_payout_address: '', grant_type: '', grant_categories: [], network: 'mainnet' diff --git a/app/assets/v2/js/grants/cart/algorand_extension.js b/app/assets/v2/js/grants/cart/algorand_extension.js new file mode 100644 index 00000000000..e64a952a1ec --- /dev/null +++ b/app/assets/v2/js/grants/cart/algorand_extension.js @@ -0,0 +1,210 @@ +const contributeWithAlgorandExtension = async(grant, vm, modal) => { + const token_name = grant.grant_donation_currency; + const amount = grant.grant_donation_amount; + const to_address = grant.rsk_payout_address; + const token = vm.getTokenByName(token_name); + + // TODO: FIGURE OUT from_address + + // const NETWORK = 'TestNet'; + const NETWORK = 'MainNet'; + + // 1. check if AlgoSigner is available + if (!AlgoSigner) { + _alert({ message: 'Please download or enable AlgoSigner extension' }, 'danger'); + modal.closeModal(); + return; + } + + try { + AlgoSigner.connect().then(async() => { + // step2: get connected accounts + const accounts = await AlgoSigner.accounts({ ledger: NETWORK }); + + let is_account_present = false; + + accounts.map(account=> { + if (account.address == from_address) + is_account_present = true; + }); + + if (!is_account_present) { + _alert({ message: `Unable to access address ${from_address} in wallet` }, 'danger'); + modal.closeModal(); + return; + } + + // step3: check if enough balance is present + const balance = await AlgoSigner.algod({ + ledger: NETWORK, + path: `/v2/accounts/${from_address}` + }); + + if ( + token_name == 'ALGO' && + Number(balance.amount) <= amount * 10 ** token.decimals + ) { + // ALGO token + _alert({ message: `Insufficent balance in address ${from_address}` }, 'danger'); + modal.closeModal(); + return; + + } + // ALGO assets + let is_asset_present = false; + + if (balance.assets && balance.assets.length > 0) { + balance.assets.map(asset => { + if (asset['asset-id'] == asset_index) + is_asset_present = true; + }); + } + + if (is_asset_present) { + _alert({ message: `Asset ${token_name} is not present in ${from_address}` }, 'danger'); + modal.closeModal(); + return; + } + + let has_enough_asset_balance = false; + + balance.assets.map(asset => { + if (asset['asset-id'] == asset_index && asset['amount'] <= amount * 10 ** token.decimals) + has_enough_asset_balance = true; + }); + + if (has_enough_asset_balance) { + _alert({ message: `Insufficent balance in address ${from_address}` }, 'danger'); + modal.closeModal(); + return; + } + + + // step4: get txnParams + const txParams = await AlgoSigner.algod({ + ledger: NETWORK, + path: '/v2/transactions/params' + }); + + let txn; + // step5: sign transaction + + if (token_name == 'ALGO') { + // ALGO token + txn = { + from: from_address, + to: to_address, + fee: txParams['fee'], + type: 'pay', + amount: amount * 10 ** token.decimals, + firstRound: txParams['last-round'], + lastRound: txParams['last-round'] + 1000, + genesisID: txParams['genesis-id'], + genesisHash: txParams['genesis-hash'], + note: 'contributing to gitcoin grant' + }; + } else { + // ALGO assets + txn = { + from: from_address, + to: to_address, + assetIndex: Number(asset_index.addr), + note: 'contributing to gitcoin grant', + amount: amount * 10 ** token.decimals, + type: 'axfer', + fee: txParams['min-fee'], + firstRound: txParams['last-round'], + lastRound: txParams['last-round'] + 1000, + genesisID: txParams['genesis-id'], + genesisHash: txParams['genesis-hash'] + }; + } + + AlgoSigner.sign(txn).then(signedTx => { + + // step6: broadcast txn + AlgoSigner.send({ + ledger: NETWORK, + tx: signedTx.blob + }) + .then(tx => { + callback(null, from_address, tx.txId); + }) + .catch((e) => { + console.log(e); + _alert({ message: 'Unable to broadcast txn. Please try again' }, 'danger'); + modal.closeModal(); + return; + }); + + }).catch(e => { + console.log(e); + _alert({ message: 'Unable to sign txn. Please try again' }, 'danger'); + modal.closeModal(); + return; + }); + + }).catch(e => { + console.log(e); + _alert({ message: 'Please allow Gitcoin to connect to AlgoSigner extension' }, 'danger'); + modal.closeModal(); + return; + }); + } catch (e) { + modal.closeModal(); + _alert({ message: 'Unable to make contribution to grant. Please try again later.' }, 'error'); + console.log(error); + return; + } + + + function callback(error, from_address, txn) { + if (error) { + vm.updatePaymentStatus(grant.grant_id, 'failed'); + _alert({ message: gettext('Unable to contribute to grant due to ' + error) }, 'danger'); + console.log(error); + } else { + + const payload = { + 'contributions': [{ + 'grant_id': grant.grant_id, + 'contributor_address': from_address, + 'tx_id': txn, + 'token_symbol': grant.grant_donation_currency, + 'tenant': 'ALGORAND', + 'comment': grant.grant_comments, + 'amount_per_period': grant.grant_donation_amount + }] + }; + + const apiUrlBounty = 'v1/api/contribute'; + + fetchData(apiUrlBounty, 'POST', JSON.stringify(payload)).then(response => { + if (200 <= response.status && response.status <= 204) { + MauticEvent.createEvent({ + 'alias': 'products', + 'data': [ + { + 'name': 'product', + 'attributes': { + 'product': 'grants', + 'persona': 'grants-contributor', + 'action': 'contribute' + } + } + ] + }); + vm.updatePaymentStatus(grant.grant_id, 'done', txn); + } else { + vm.updatePaymentStatus(grant.grant_id, 'failed'); + _alert('Unable to make contribute to grant. Please try again later', 'danger'); + console.error(`error: grant contribution failed with status: ${response.status} and message: ${response.message}`); + } + }).catch(function(error) { + vm.updatePaymentStatus(grant.grant_id, 'failed'); + _alert('Unable to make contribute to grant. Please try again later', 'danger'); + console.log(error); + }); + } + } +}; diff --git a/app/assets/v2/js/grants/funding.js b/app/assets/v2/js/grants/funding.js index 178fdfa34ad..938bab1b2dd 100644 --- a/app/assets/v2/js/grants/funding.js +++ b/app/assets/v2/js/grants/funding.js @@ -157,6 +157,9 @@ function tokenOptionsForGrant(grant) { } else if (grant.tenants && grant.tenants.includes('RSK')) { tokenDataList = tokenDataList.filter(token => token.chainId === 30); tokenDefault = 'RBTC'; + } else if (grant.tenants && grant.tenants.includes('ALGORAND')) { + tokenDataList = tokenDataList.filter(token => token.chainId === 1001); + tokenDefault = 'ALGO'; } else { tokenDataList = tokenDataList.filter((token) => token.chainId === 1); } diff --git a/app/assets/v2/js/pages/bounty_detail/algorand_extension.js b/app/assets/v2/js/pages/bounty_detail/algorand_extension.js index cec1b28beda..16ecd9edb1a 100644 --- a/app/assets/v2/js/pages/bounty_detail/algorand_extension.js +++ b/app/assets/v2/js/pages/bounty_detail/algorand_extension.js @@ -16,7 +16,6 @@ const payWithAlgorandExtension = async(fulfillment_id, to_address, vm, modal) => return; } - // const connect = await AlgoSigner.connect(); AlgoSigner.connect().then(async() => { // step2: get connected accounts const accounts = await AlgoSigner.accounts({ ledger: NETWORK }); diff --git a/app/assets/v2/js/pages/bounty_details2.js b/app/assets/v2/js/pages/bounty_details2.js index df33f6a7f3e..6ae407720f0 100644 --- a/app/assets/v2/js/pages/bounty_details2.js +++ b/app/assets/v2/js/pages/bounty_details2.js @@ -229,7 +229,7 @@ Vue.mixin({ case 'ALGO': case 'USDTa': case 'USDCa': - url = `https://algoexplorer.io/tx/${address}`; + url = `https://algoexplorer.io/address/${address}`; break; case 'SC': diff --git a/app/grants/admin.py b/app/grants/admin.py index bd161d82253..2a8aae97681 100644 --- a/app/grants/admin.py +++ b/app/grants/admin.py @@ -113,7 +113,7 @@ class GrantAdmin(GeneralAdmin): 'metadata', 'twitter_handle_1', 'twitter_handle_2', 'view_count', 'in_active_clrs', 'last_update', 'funding_info', 'twitter_verified', 'twitter_verified_by', 'twitter_verified_at', 'stats_history', 'zcash_payout_address', 'celo_payout_address','zil_payout_address', 'harmony_payout_address', 'binance_payout_address', - 'polkadot_payout_address', 'kusama_payout_address', 'rsk_payout_address', 'emails', 'admin_message', 'has_external_funding' + 'polkadot_payout_address', 'kusama_payout_address', 'rsk_payout_address', 'algorand_payout_address', 'emails', 'admin_message', 'has_external_funding' ] readonly_fields = [ 'logo_svg_asset', 'logo_asset', diff --git a/app/grants/management/commands/sync_pending_contributions.py b/app/grants/management/commands/sync_pending_contributions.py index e9c3ee5ac8d..2c453abed57 100644 --- a/app/grants/management/commands/sync_pending_contributions.py +++ b/app/grants/management/commands/sync_pending_contributions.py @@ -42,7 +42,7 @@ def handle(self, *args, **options): # Auto expire pending transactions timeout_period = timezone.now() - timedelta(minutes=60) - tenants = ['ZCASH', 'ZIL', 'CELO', 'POLKADOT', 'HARMONY', 'BINANCE', 'KUSAMA', 'RSK'] + tenants = ['ZCASH', 'ZIL', 'CELO', 'POLKADOT', 'HARMONY', 'BINANCE', 'KUSAMA', 'RSK', 'ALGORAND'] for tenant in tenants: tenant_pending_contributions = pending_contribution.filter(subscription__tenant=tenant) diff --git a/app/grants/models.py b/app/grants/models.py index 6a0abce3ad6..7f3eb5920da 100644 --- a/app/grants/models.py +++ b/app/grants/models.py @@ -414,6 +414,13 @@ class Meta: blank=True, help_text=_('The rsk wallet address where subscription funds will be sent.'), ) + algorand_payout_address = models.CharField( + max_length=255, + default='0x0', + null=True, + blank=True, + help_text=_('The algorand wallet address where subscription funds will be sent.'), + ) # TODO-GRANTS: remove contract_owner_address = models.CharField( max_length=255, @@ -650,6 +657,8 @@ def tenants(self): tenants.append('BINANCE') if self.rsk_payout_address and self.rsk_payout_address != '0x0': tenants.append('RSK') + if self.algorand_payout_address and self.algorand_payout_address != '0x0': + tenants.append('ALGORAND') return tenants @@ -892,6 +901,7 @@ def cart_payload(self, build_absolute_uri, user=None): 'kusama_payout_address': self.kusama_payout_address, 'harmony_payout_address': self.harmony_payout_address, 'rsk_payout_address': self.rsk_payout_address, + 'algorand_payout_address': self.algorand_payout_address, 'is_on_team': is_grant_team_member(self, user.profile) if user and user.is_authenticated else False, } @@ -950,6 +960,7 @@ def repr(self, user, build_absolute_uri): 'harmony_payout_address': self.harmony_payout_address, 'binance_payout_address': self.binance_payout_address, 'rsk_payout_address': self.rsk_payout_address, + 'algorand_payout_address': self.algorand_payout_address, 'token_address': self.token_address, 'image_css': self.image_css, 'verified': self.twitter_verified, @@ -1007,7 +1018,8 @@ class Subscription(SuperModel): ('KUSAMA', 'KUSAMA'), ('HARMONY', 'HARMONY'), ('BINANCE', 'BINANCE'), - ('RSK', 'RSK') + ('RSK', 'RSK'), + ('ALGORAND', 'ALGORAND') ] active = models.BooleanField(default=True, db_index=True, help_text=_('Whether or not the Subscription is active.')) @@ -1679,7 +1691,8 @@ class Contribution(SuperModel): ('polkadot_std', 'polkadot_std'), ('harmony_std', 'harmony_std'), ('binance_std', 'binance_std'), - ('rsk_std', 'rsk_std') + ('rsk_std', 'rsk_std'), + ('algorand_std', 'algorand_std') ] success = models.BooleanField(default=True, help_text=_('Whether or not success.')) diff --git a/app/grants/sync/algorand.py b/app/grants/sync/algorand.py new file mode 100644 index 00000000000..d5621ec62e3 --- /dev/null +++ b/app/grants/sync/algorand.py @@ -0,0 +1,106 @@ +from django.conf import settings + +import requests +from grants.sync.helpers import record_contribution_activity, txn_already_used + +API_KEY = settings.ALGORAND_API_KEY + + +def get_algorand_txn_status_paid_explorer(contribution): + txnid = contribution.tx_id + + if not txnid or txnid == "0x0": + return None + + subscription = contribution.subscription + grant = subscription.grant + token_symbol = subscription.token_symbol + amount = subscription.amount_per_period + + to_address = grant.algorand_payout_address + from_address = subscription.contributor_address + + headers = { + 'accept': 'application/json', + 'x-api-key': API_KEY + } + + url = f'https://mainnet-algorand.api.purestake.io/idx2/v2/transactions/{txnid}' + response = requests.get(url=url, headers=headers).json() + + if response: + if response.get("current-round") and response.get("transaction"): + txn = response["transaction"] + + # asset / algo token + payment_txn = txn['payment-transaction'] if txn.get('payment-transaction') else txn['asset-transfer-transaction'] + + if ( + txn['confirmed-round'] > 0 and + txn['sender'].lower() == from_address.lower() and + payment_txn['receiver'].lower() == to_address.lower() and + float(float(payment_txn['amount'])/ 10**6) == float(amount) and + not txn_already_used(txnid, token_symbol) + ): + return 'success' + + return None + + +def get_algorand_txn_status(contribution): + txnid = contribution.tx_id + + if not txnid or txnid == "0x0": + return None + + subscription = contribution.subscription + grant = subscription.grant + token_symbol = subscription.token_symbol + amount = subscription.amount_per_period + + to_address = grant.algorand_payout_address + from_address = subscription.contributor_address + + url = f'https://api.algoexplorer.io/v2/transactions/pending/{txnid}?format=json' + + response = requests.get(url).json() + + if response.get('confirmed-round') and response.get('txn') and response.get('txn').get('txn'): + txn = response["txn"]['txn'] + if not response["pool-error"] == "": + return None + + # asset / algo token + rcv = txn['rcv'] if txn.get('rcv') else txn['arcv'] + amt = txn['amt'] if txn.get('amt') else txn['aamt'] + + if ( + txn['snd'].lower() == from_address.lower() and + rcv.lower() == to_address.lower() and + float(float(amt)/ 10**6) == float(amount) and + not txn_already_used(txnid, token_symbol) + ): + return 'success' + + elif response.get('message') and API_KEY != '': + # txn is too old and cannot be found on algoexplorer + return get_algorand_txn_status_paid_explorer(contribution) + + return None + + +def sync_algorand_payout(contribution): + + if contribution.tx_id and contribution.tx_id != '0x0': + txn_status = get_algorand_txn_status(contribution) + + if txn_status == 'success': + contribution.success = True + contribution.tx_cleared = True + contribution.checkout_type = 'alogrand_std' + record_contribution_activity(contribution) + contribution.save() + else: + contribution.success = True + contribution.tx_cleared = False + contribution.save() diff --git a/app/grants/templates/grants/_new.html b/app/grants/templates/grants/_new.html index 74baac7a6d5..436e8494de0 100644 --- a/app/grants/templates/grants/_new.html +++ b/app/grants/templates/grants/_new.html @@ -236,6 +236,10 @@
Funding Information
+ + {% endif %} @@ -399,6 +403,24 @@
Funding Information
[[errors.rsk_payout_address]] + + + diff --git a/app/grants/templates/grants/bulk_add_to_cart.html b/app/grants/templates/grants/bulk_add_to_cart.html index fae912a17a0..377b1418f36 100644 --- a/app/grants/templates/grants/bulk_add_to_cart.html +++ b/app/grants/templates/grants/bulk_add_to_cart.html @@ -30,6 +30,7 @@ kusama_payout_address: "{{ grant.obj.kusama_payout_address }}", binance_payout_address: "{{ grant.obj.binance_payout_address }}", rsk_payout_address: "{{ grant.obj.rsk_payout_address }}", + algorand_payout_address: "{{ grant.obj.algorand_payout_address }}", grant_token_address: "{{ grant.obj.token_address }}", grant_logo: {% if grant.logo and grant.logo.url %}"{{ grant.obj.logo.url }}"{% else %}{% with grant_logo='v2/images/grants/logos/' id=grant.id|modulo:3 %} "{% static grant_logo|addstr:id|add:'.png' %}" {% endwith %} {% endif %}, grant_clr_prediction_curve: "{{ grant.obj.clr_prediction_curve }}", diff --git a/app/grants/templates/grants/cart-vue.html b/app/grants/templates/grants/cart-vue.html index 98a648b89a8..922fff3bfe6 100644 --- a/app/grants/templates/grants/cart-vue.html +++ b/app/grants/templates/grants/cart-vue.html @@ -108,6 +108,9 @@

Grants Cart

{% include 'grants/cart/extension/rsk.html' %} + + {% include 'grants/cart/extension/algorand.html' %} + + + + diff --git a/app/grants/utils.py b/app/grants/utils.py index 25f02c82118..25c78ed34b4 100644 --- a/app/grants/utils.py +++ b/app/grants/utils.py @@ -30,10 +30,12 @@ from django.utils import timezone from app import settings +from app.dashboard.sync.algorand import sync_algorand_payout from app.settings import BASE_DIR, BASE_URL, MEDIA_URL, STATIC_HOST, STATIC_URL from avatar.utils import convert_img from economy.utils import ConversionRateNotFoundError, convert_amount from gas.utils import eth_usd_conv_rate +from grants.sync.algorand import sync_algorand_payout from grants.sync.binance import sync_binance_payout from grants.sync.celo import sync_celo_payout from grants.sync.harmony import sync_harmony_payout @@ -58,7 +60,8 @@ 'POLKADOT': sync_polkadot_payout, 'BINANCE': sync_binance_payout, 'KUSAMA': sync_polkadot_payout, - 'RSK': sync_rsk_payout + 'RSK': sync_rsk_payout, + 'ALGORAND': sync_algorand_payout } def get_clr_rounds_metadata(): diff --git a/app/grants/views.py b/app/grants/views.py index a8d1b6d43c9..d987b32c7a0 100644 --- a/app/grants/views.py +++ b/app/grants/views.py @@ -1594,6 +1594,7 @@ def grant_edit(request, grant_id): kusama_payout_address = request.POST.get('kusama_payout_address', '0x0') binance_payout_address = request.POST.get('binance_payout_address', '0x0') rsk_payout_address = request.POST.get('rsk_payout_address', '0x0') + algorand_payout_address = request.POST.get('algorand_payout_address', '0x0') if ( eth_payout_address == '0x0' and @@ -1604,7 +1605,8 @@ def grant_edit(request, grant_id): kusama_payout_address == '0x0' and harmony_payout_address == '0x0' and binance_payout_address == '0x0' and - rsk_payout_address == '0x0' + rsk_payout_address == '0x0' and + algorand_payout_address == '0x0' ): response['message'] = 'error: payout_address is a mandatory parameter' return JsonResponse(response) @@ -1643,6 +1645,9 @@ def grant_edit(request, grant_id): if rsk_payout_address != '0x0': grant.rsk_payout_address = rsk_payout_address + if algorand_payout_address != '0x0': + grant.algorand_payout_address = algorand_payout_address + github_project_url = request.POST.get('github_project_url', None) if github_project_url: grant.github_project_url = github_project_url @@ -1788,13 +1793,14 @@ def grant_new(request): harmony_payout_address = request.POST.get('harmony_payout_address', None) binance_payout_address = request.POST.get('binance_payout_address', None) rsk_payout_address = request.POST.get('rsk_payout_address', None) + algorand_payout_address = request.POST.get('algorand_payout_address', None) if ( not eth_payout_address and not zcash_payout_address and not celo_payout_address and not zil_payout_address and not polkadot_payout_address and not kusama_payout_address and not harmony_payout_address and not binance_payout_address and - not rsk_payout_address + not rsk_payout_address and not algorand_payout_address ): response['message'] = 'error: payout_address is a mandatory parameter' return JsonResponse(response) @@ -1843,6 +1849,7 @@ def grant_new(request): 'harmony_payout_address': harmony_payout_address if harmony_payout_address else '0x0', 'binance_payout_address': binance_payout_address if binance_payout_address else '0x0', 'rsk_payout_address': rsk_payout_address if rsk_payout_address else '0x0', + 'algorand_payout_address': algorand_payout_address if algorand_payout_address else '0x0', 'token_symbol': token_symbol, 'contract_version': contract_version, 'deploy_tx_id': request.POST.get('transaction_hash', '0x0'), @@ -3145,7 +3152,7 @@ def contribute_to_grants_v1(request): }) continue - if not tenant in ['ETH', 'ZCASH', 'ZIL', 'CELO', 'POLKADOT', 'HARMONY', 'KUSAMA', 'BINANCE', 'RSK']: + if not tenant in ['ETH', 'ZCASH', 'ZIL', 'CELO', 'POLKADOT', 'HARMONY', 'KUSAMA', 'BINANCE', 'RSK', 'ALGORAND']: invalid_contributions.append({ 'grant_id': grant_id, 'message': 'error: tenant chain is not supported for grant'