Skip to content

Commit

Permalink
feat(grants): binance crosschain integration (#8306)
Browse files Browse the repository at this point in the history
* feat: add binance chain sync + new migration

* defer makemigration

* feat: sync payout/pending tx flow

* update admin +  repr + cart_payload

* update contribute view

* update grant new + edit view

* fix redundancy

* add binance to grant creation flow + edit flow

* add contribution with binance extension

* binance cart flow

* handle BUSD contribution

* fix isBinanceExtInstalled

* new migration

* fix chain selection

* include binance html + handle decimals
  • Loading branch information
chibie authored Feb 10, 2021
1 parent f610c34 commit b757dfe
Show file tree
Hide file tree
Showing 16 changed files with 405 additions and 19 deletions.
10 changes: 7 additions & 3 deletions app/assets/v2/js/cart-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,23 +106,27 @@ class CartData {
grantData.grant_donation_currency = 'ZIL';
}
} else if (grantData.tenants.includes('HARMONY')) {

if (!grantData.grant_donation_amount) {
grantData.grant_donation_amount = 1;
}
if (!grantData.grant_donation_currency) {
grantData.grant_donation_currency = 'ONE';
}
} else if (grantData.tenants.includes('BINANCE')) {
if (!grantData.grant_donation_amount) {
grantData.grant_donation_amount = 1;
}
if (!grantData.grant_donation_currency) {
grantData.grant_donation_currency = 'BNB';
}
} else if (grantData.tenants.includes('POLKADOT')) {

if (!grantData.grant_donation_amount) {
grantData.grant_donation_amount = 1;
}
if (!grantData.grant_donation_currency) {
grantData.grant_donation_currency = 'DOT';
}
} else if (grantData.tenants.includes('KUSAMA')) {

if (!grantData.grant_donation_amount) {
grantData.grant_donation_amount = 1;
}
Expand Down
10 changes: 10 additions & 0 deletions app/assets/v2/js/cart.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,10 @@ Vue.component('grants-cart', {
return window.onewallet && window.onewallet.isOneWallet;
},

isBinanceExtInstalled() {
return window.BinanceChain || false;
},

isPolkadotExtInstalled() {
return polkadot_extension_dapp.isWeb3Injected;
}
Expand Down Expand Up @@ -380,6 +384,9 @@ Vue.component('grants-cart', {
case 'HARMONY':
vm.chainId = '1000';
break;
case 'BINANCE':
vm.chainId = '56';
break;
case 'KUSAMA':
vm.chainId = '59';
break;
Expand Down Expand Up @@ -452,6 +459,9 @@ Vue.component('grants-cart', {
case 'HARMONY':
contributeWithHarmonyExtension(grant, vm);
break;
case 'BINANCE':
contributeWithBinanceExtension(grant, vm);
break;
case 'POLKADOT':
case 'KUSAMA':
if (data) {
Expand Down
4 changes: 4 additions & 0 deletions app/assets/v2/js/grants/_new.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ Vue.mixin({
vm.$set(vm.errors, 'zil_payout_address', 'Please enter Zilliqa address');
} else if (vm.chainId == 'harmony' && !vm.form.harmony_payout_address) {
vm.$set(vm.errors, 'harmony_payout_address', 'Please enter Harmony address');
} else if (vm.chainId == 'binance' && !vm.form.binance_payout_address) {
vm.$set(vm.errors, 'binance_payout_address', 'Please enter Binance address');
} else if (vm.chainId == 'polkadot' && !vm.form.polkadot_payout_address) {
vm.$set(vm.errors, 'polkadot_payout_address', 'Please enter Polkadot address');
} else if (vm.chainId == 'kusama' && !vm.form.kusama_payout_address) {
Expand Down Expand Up @@ -169,6 +171,7 @@ Vue.mixin({
'celo_payout_address': form.celo_payout_address,
'zil_payout_address': form.zil_payout_address,
'harmony_payout_address': form.harmony_payout_address,
'binance_payout_address': form.binance_payout_address,
'polkadot_payout_address': form.polkadot_payout_address,
'kusama_payout_address': form.kusama_payout_address,
'grant_type': form.grant_type,
Expand Down Expand Up @@ -330,6 +333,7 @@ if (document.getElementById('gc-new-grant')) {
celo_payout_address: '',
zil_payout_address: '',
harmony_payout_address: '',
binance_payout_address: '',
polkadot_payout_address: '',
kusama_payout_address: '',
grant_type: '',
Expand Down
92 changes: 92 additions & 0 deletions app/assets/v2/js/grants/cart/binance_extension.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
const contributeWithBinanceExtension = async (grant, vm) => {
let token_name = grant.grant_donation_currency;
let decimals = vm.filterByChainId.filter(token => {
return token.name == token_name })[0].decimals
let amount = grant.grant_donation_amount * 10 ** decimals;
let to_address = grant.binance_payout_address;
let from_address;

try {
from_address = await binance_utils.getSelectedAccount();
} catch (error) {
_alert({ message: `Please ensure your Binance Chain Extension wallet is installed and enabled`}, 'error');
return;
}

if (!token_name) {
token_name = 'BNB';
}

if (token_name === 'BNB') {
const account_balance = await binance_utils.getAddressBalance(from_address);

if (Number(account_balance) < amount) {
_alert({ message: `Account needs to have more than ${amount / 10 ** decimals} BNB for payout` }, 'error');
return;
}
} else if (token_name === 'BUSD') {
const busd_contract_address = '0xe9e7cea3dedca5984780bafc599bd69add087d56'

const account_balance = await binance_utils.getAddressTokenBalance(from_address, busd_contract_address);

if (Number(account_balance) < amount ) {
_alert({ message: `Account needs to have more than ${amount / 10 ** decimals} BUSD for payout` }, 'error');
return;
}
}

binance_utils.transferViaExtension(
amount,
to_address,
from_address,
token_name
).then(txn => {
if (txn) {
callback(null, from_address, txn);
} else {
callback('error in signing transaction');
}
}).catch(err => {
callback(err);
});

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) }, 'error');
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': 'BINANCE',
'comment': grant.grant_comments,
'amount_per_period': grant.grant_donation_amount
}]
};

const apiUrlGrant = `v1/api/contribute`;

fetchData(apiUrlGrant, 'POST', JSON.stringify(payload)).then(response => {
if (200 <= response.status && response.status <= 204) {
console.log('success', response);

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', 'error');
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', 'error');
console.log(error);
});
}
}
};
3 changes: 3 additions & 0 deletions app/assets/v2/js/grants/funding.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ function tokenOptionsForGrant(grant) {
} else if (grant.tenants && grant.tenants.includes('HARMONY')) {
tokenDataList = tokenDataList.filter(token => token.chainId === 1000);
tokenDefault = 'ONE';
} else if (grant.tenants && grant.tenants.includes('BINANCE')) {
tokenDataList = tokenDataList.filter(token => token.chainId === 56);
tokenDefault = 'BNB';
} else if (grant.tenants && grant.tenants.includes('POLKADOT')) {
tokenDataList = tokenDataList.filter(token => token.chainId === 58);
tokenDefault = 'DOT';
Expand Down
4 changes: 2 additions & 2 deletions app/grants/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ class GrantAdmin(GeneralAdmin):
'link', 'clr_prediction_curve', 'hidden', 'next_clr_calc_date', 'last_clr_calc_date',
'metadata', 'twitter_handle_1', 'twitter_handle_2', 'view_count', 'is_clr_eligible', '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', 'polkadot_payout_address',
'kusama_payout_address', 'emails'
'zcash_payout_address', 'celo_payout_address','zil_payout_address', 'harmony_payout_address', 'binance_payout_address',
'polkadot_payout_address', 'kusama_payout_address', 'emails'
]
readonly_fields = [
'logo_svg_asset', 'logo_asset',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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', 'KUSAMA']
tenants = ['ZCASH', 'ZIL', 'CELO', 'POLKADOT', 'HARMONY', 'BINANCE', 'KUSAMA']

for tenant in tenants:
tenant_pending_contributions = pending_contribution.filter(subscription__tenant=tenant)
Expand Down
28 changes: 28 additions & 0 deletions app/grants/migrations/0110_auto_20210208_1839.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 2.2.4 on 2021-02-08 18:39

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('grants', '0109_auto_20210203_1419'),
]

operations = [
migrations.AddField(
model_name='grant',
name='binance_payout_address',
field=models.CharField(blank=True, default='0x0', help_text='The binance wallet address where subscription funds will be sent.', max_length=255, null=True),
),
migrations.AlterField(
model_name='contribution',
name='checkout_type',
field=models.CharField(blank=True, choices=[('eth_std', 'eth_std'), ('eth_zksync', 'eth_zksync'), ('zcash_std', 'zcash_std'), ('celo_std', 'celo_std'), ('zil_std', 'zil_std'), ('polkadot_std', 'polkadot_std'), ('harmony_std', 'harmony_std'), ('binance_std', 'binance_std')], help_text='The checkout method used while making the contribution', max_length=30, null=True),
),
migrations.AlterField(
model_name='subscription',
name='tenant',
field=models.CharField(blank=True, choices=[('ETH', 'ETH'), ('ZCASH', 'ZCASH'), ('CELO', 'CELO'), ('ZIL', 'ZIL'), ('POLKADOT', 'POLKADOT'), ('KUSAMA', 'KUSAMA'), ('HARMONY', 'HARMONY'), ('BINANCE', 'BINANCE')], default='ETH', help_text='specific tenant in which contribution is made', max_length=10, null=True),
),
]
18 changes: 16 additions & 2 deletions app/grants/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,13 @@ class Meta:
blank=True,
help_text=_('The harmony wallet address where subscription funds will be sent.'),
)
binance_payout_address = models.CharField(
max_length=255,
default='0x0',
null=True,
blank=True,
help_text=_('The binance wallet address where subscription funds will be sent.'),
)
# TODO-GRANTS: remove
contract_owner_address = models.CharField(
max_length=255,
Expand Down Expand Up @@ -572,6 +579,8 @@ def tenants(self):
tenants.append('KUSAMA')
if self.harmony_payout_address and self.harmony_payout_address != '0x0':
tenants.append('HARMONY')
if self.binance_payout_address and self.binance_payout_address != '0x0':
tenants.append('BINANCE')

return tenants

Expand Down Expand Up @@ -804,6 +813,8 @@ def cart_payload(self, build_absolute_uri):
'celo_payout_address': self.celo_payout_address,
'zil_payout_address': self.zil_payout_address,
'polkadot_payout_address': self.polkadot_payout_address,
'harmony_payout_address': self.harmony_payout_address,
'binance_payout_address': self.binance_payout_address,
'kusama_payout_address': self.kusama_payout_address,
'harmony_payout_address': self.harmony_payout_address
}
Expand Down Expand Up @@ -861,6 +872,7 @@ def repr(self, user, build_absolute_uri):
'polkadot_payout_address': self.polkadot_payout_address,
'kusama_payout_address': self.kusama_payout_address,
'harmony_payout_address': self.harmony_payout_address,
'binance_payout_address': self.binance_payout_address,
'token_address': self.token_address,
'image_css': self.image_css,
'verified': self.twitter_verified,
Expand Down Expand Up @@ -914,7 +926,8 @@ class Subscription(SuperModel):
('ZIL', 'ZIL'),
('POLKADOT', 'POLKADOT'),
('KUSAMA', 'KUSAMA'),
('HARMONY', 'HARMONY')
('HARMONY', 'HARMONY'),
('BINANCE', 'BINANCE'),
]

active = models.BooleanField(default=True, db_index=True, help_text=_('Whether or not the Subscription is active.'))
Expand Down Expand Up @@ -1575,7 +1588,8 @@ class Contribution(SuperModel):
('celo_std', 'celo_std'),
('zil_std', 'zil_std'),
('polkadot_std', 'polkadot_std'),
('harmony_std', 'harmony_std')
('harmony_std', 'harmony_std'),
('binance_std', 'binance_std')
]

success = models.BooleanField(default=True, help_text=_('Whether or not success.'))
Expand Down
64 changes: 64 additions & 0 deletions app/grants/sync/binance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import requests
from grants.sync.helpers import record_contribution_activity


def get_binance_txn_status(contribution):
txnid = contribution.tx_id

if not txnid or txnid == "0x0":
return None

response = { 'status': 'pending' }

try:
binance_url = f'https://bsc-dataseed.binance.org'

data = {
'id': 0,
'jsonrpc': '2.0',
'method': 'eth_getTransactionReceipt',
'params': [ txnid ]
}

headers = {
'Host': 'gitcoin.co'
}

binance_response = requests.post(binance_url, json=data).json()

result = binance_response['result']

response = { 'status': 'pending' }

if result:
tx_status = int(result.get('status'), 16) # convert hex to decimal

if tx_status == 1:
response = { 'status': 'done' }
elif tx_status == 0:
response = { 'status': 'expired' }

except Exception as e:
logger.error(f'error: get_binance_txn_status - {e}')

finally:
return response


def sync_binance_payout(contribution):
if contribution.tx_id or contribution.tx_id == "0x0":
txn_status = get_binance_txn_status(contribution)

if txn_status:
status_description = txn_status.get('status')

if status_description == 'done':
contribution.success = True
contribution.tx_cleared = True
contribution.checkout_type = 'binance_std'
record_contribution_activity(contribution)
contribution.save()
elif status_description == 'expired':
contribution.success = True
contribution.tx_cleared = False
contribution.save()
21 changes: 21 additions & 0 deletions app/grants/templates/grants/_new.html
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ <h5 class="mt-4">Funding Information</h5>
<input type="radio" name="grant_chain" id="harmony_chain" value="harmony" v-model="chainId"><img class="mr-2" src="{% static 'v2/images/chains/harmony.svg' %}" alt="" width="20"> harmony
</label>

<label class="btn btn-radio chain-btn d-flex align-items-center mr-2 mb-2 font-weight-bold py-2 px-4" :class="{'active': chainId === 'binance'}">
<input type="radio" name="grant_chain" id="binance_chain" value="binance" v-model="chainId"><img class="mr-2" src="{% static 'v2/images/chains/binance.svg' %}" alt="" width="20"> binance
</label>

<label class="btn btn-radio chain-btn d-flex align-items-center mr-2 mb-2 font-weight-bold py-2 px-4" :class="{'active': chainId === 'polkadot'}">
<input type="radio" name="grant_chain" id="polkadot_chain" value="polkadot" v-model="chainId"><img class="mr-2" src="{% static 'v2/images/chains/polkadot.svg' %}" alt="" width="16"> polkadot
</label>
Expand Down Expand Up @@ -324,6 +328,23 @@ <h5 class="mt-4">Funding Information</h5>
</div>
</template>

<template v-if="chainId == 'binance'">
<label class="font-caption letter-spacing text-black-60 text-uppercase" for="binance_payout_address">
Binance Recipient Wallet Address
</label>
<span class="font-smaller-6 badge badge-greylight text-capitalize ml-2">Required</span>

<p class="text-black-60 font-caption mb-2">
This is where funds from contributors to this grant will be sent
</p>

<input id="binance_payout_address" v-model="form.binance_payout_address" name="binance_payout_address" class="form__input form__input-lg" maxlength="50" placeholder="0x0" required/>

<div class="text-danger" v-if="errors.binance_payout_address">
[[errors.binance_payout_address]]
</div>
</template>

<template v-if="chainId == 'polkadot'">
<label class="font-caption letter-spacing text-black-60 text-uppercase" for="polkadot_payout_address">
Polkadot Recipient Wallet Address
Expand Down
Loading

0 comments on commit b757dfe

Please sign in to comment.