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

xinfin: bounties integration #8421

Merged
merged 4 commits into from
Mar 24, 2021
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
1 change: 1 addition & 0 deletions app/app/local.env
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ VIEW_BLOCK_API_KEY=
ETHERSCAN_API_KEY=
FORTMATIC_LIVE_KEY=
FORTMATIC_TEST_KEY=
XINFIN_API_KEY=

PYPL_CLIENT_ID=

Expand Down
1 change: 1 addition & 0 deletions app/app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
FORTMATIC_LIVE_KEY = env('FORTMATIC_LIVE_KEY', default='YOUR-SupEr-SecRet-LiVe-FoRtMaTiC-KeY')
FORTMATIC_TEST_KEY = env('FORTMATIC_TEST_KEY', default='YOUR-SupEr-SecRet-TeSt-FoRtMaTiC-KeY')
PYPL_CLIENT_ID = env('PYPL_CLIENT_ID', default='')
XINFIN_API_KEY = env('XINFIN_API_KEY', default='')

# Ratelimit

Expand Down
1 change: 1 addition & 0 deletions app/assets/v2/images/chains/xinfin.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
58 changes: 58 additions & 0 deletions app/assets/v2/js/lib/xinfin/xdc3.min.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions app/assets/v2/js/lib/xinfin/xdc3.min.js.map

Large diffs are not rendered by default.

88 changes: 88 additions & 0 deletions app/assets/v2/js/pages/bounty_detail/xinfin_extension.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
const payWithXinfinExtension = async (fulfillment_id, to_address, vm, modal) => {

const amount = vm.fulfillment_context.amount;
const token_name = vm.bounty.token_name;

try {
web3.version.getNetwork(async (err, providerNetworkId) => {
// 1. init provider
await ethereum.enable();
xdc3Client = await new xdc3(web3.currentProvider);

// 2. check if default account is selected
if (web3.eth.defaultAccount == null) {
modal.closeModal();
_alert({ message: 'Please unlock XinPay Wallet extension' }, 'error');
return;
}

// 3. check if token is XDC
if (token_name == 'XDC') {

const xdcBalance = await xdc3Client.eth.getBalance(web3.eth.defaultAccount);

if (Number(xdcBalance) < Number(amount * 10 ** 18)) {
_alert({ message: `Insufficent balance in address ${web3.eth.defaultAccount}` }, 'error');
return;
}

let txArgs = {
to: to_address.toLowerCase(),
from: web3.eth.defaultAccount,
value: (amount * 10 ** 18).toString(),
};

xdc3Client.eth.sendTransaction(
txArgs,
(error, result) => callback(error, web3.eth.defaultAccount, result)
);

} else {
modal.closeModal();
_alert({ message: 'XinPay supports payouts in XDC only.' }, 'error');
return;
}
});

} catch (e) {
modal.closeModal();
_alert({ message: 'Please download or enable XinPay Wallet extension' }, 'error');
return;
}

function callback(error, from_address, txn) {
if (error) {
_alert({ message: gettext('Unable to payout bounty due to: ' + error) }, 'error');
console.log(error);
} else {

const payload = {
payout_type: 'xinfin_ext',
tenant: 'XINFIN',
amount: amount,
token_name: token_name,
funder_address: from_address.replace(/^(0x)/,"xdc"),
payout_tx_id: txn
};

modal.closeModal();
const apiUrlBounty = `/api/v1/bounty/payout/${fulfillment_id}`;

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

vm.fetchBounty();
_alert('Payment Successful');

} else {
_alert('Unable to make payout bounty. Please try again later', 'error');
console.error(`error: bounty payment failed with status: ${response.status} and message: ${response.message}`);
}
}).catch(function(error) {
_alert('Unable to make payout bounty. Please try again later', 'error');
console.log(error);
});
}
}
};
22 changes: 18 additions & 4 deletions app/assets/v2/js/pages/bounty_details2.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ Vue.mixin({
url = `https://explorer.rsk.co/tx/${txn}`;
break;

case 'XDC':
url = `https://explorer.xinfin.network/tx/${txn}`;
break;

default:
url = `https://etherscan.io/tx/${txn}`;

Expand Down Expand Up @@ -200,6 +204,10 @@ Vue.mixin({
url = `https://explorer.rsk.co/address/${address}`;
break;

case 'XDC':
url = `https://explorer.xinfin.network/addr/${address}`;
break;

default:
url = `https://etherscan.io/address/${address}`;
}
Expand Down Expand Up @@ -423,6 +431,10 @@ Vue.mixin({
tenant = 'RSK';
break;

case 'XDC':
tenant = 'XINFIN';
break;

default:
tenant = 'ETH';
}
Expand Down Expand Up @@ -509,6 +521,10 @@ Vue.mixin({
case 'rsk_ext':
payWithRSKExtension(fulfillment_id, fulfiller_address, vm, modal);
break;

case 'xinfin_ext':
payWithXinfinExtension(fulfillment_id, fulfiller_address, vm, modal);
break;
}
},
closeBounty: function() {
Expand Down Expand Up @@ -714,18 +730,16 @@ Vue.mixin({
let vm = this;

switch (fulfillment.payout_type) {
case 'fiat':
vm.fulfillment_context.active_step = 'payout_amount';
break;

case 'qr':
case 'manual':
vm.fulfillment_context.active_step = 'check_wallet_owner';
break;

case 'fiat':
case 'web3_modal':
case 'polkadot_ext':
case 'rsk_ext':
case 'xinfin_ext':
vm.fulfillment_context.active_step = 'payout_amount';
break;
}
Expand Down
4 changes: 4 additions & 0 deletions app/assets/v2/js/pages/hackathon_new_bounty.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ Vue.mixin({
// rsk
type = 'rsk_ext';
break;
case '50':
// xinfin
type = 'xinfin_ext';
break;
case '58':
// polkadot
type = 'polkadot_ext';
Expand Down
4 changes: 4 additions & 0 deletions app/assets/v2/js/pages/new_bounty.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,10 @@ Vue.mixin({
// rsk
type = 'rsk_ext';
break;
case '50':
// xinfin
type = 'xinfin_ext';
break;
case '59':
case '58':
// 58 - polkadot, 59 - kusama
Expand Down
2 changes: 1 addition & 1 deletion app/assets/v2/js/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const WalletConnectProvider = window.WalletConnectProvider.default;
const eventWalletReady = new Event('walletReady', {bubbles: true});
const eventDataWalletReady = new Event('dataWalletReady', {bubbles: true});

let web3;
var web3 = typeof (web3) != 'undefined' ? web3 : null;
let web3Modal;
let provider;
let selectedAccount;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def handle(self, *args, **options):
)

# Extensions
ext_payout_types= ['web3_modal', 'polkadot_ext', 'harmony_ext', 'binance_ext', 'rsk_ext']
ext_payout_types= ['web3_modal', 'polkadot_ext', 'harmony_ext', 'binance_ext', 'rsk_ext', 'xinfin_ext']
for ext_payout_type in ext_payout_types:
ext_pending_fulfillments = pending_fulfillments.filter(payout_type=ext_payout_type)
for fulfillment in ext_pending_fulfillments.all():
Expand Down
28 changes: 28 additions & 0 deletions app/dashboard/migrations/0175_auto_20210324_1140.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 2.2.4 on 2021-03-24 11:40

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('dashboard', '0174_profile_google_user_id'),
]

operations = [
migrations.AlterField(
model_name='bounty',
name='web3_type',
field=models.CharField(choices=[('legacy_gitcoin', 'Legacy Bounty'), ('bounties_network', 'Bounties Network'), ('qr', 'QR Code'), ('web3_modal', 'Web3 Modal'), ('polkadot_ext', 'Polkadot Ext'), ('binance_ext', 'Binance Ext'), ('harmony_ext', 'Harmony Ext'), ('rsk_ext', 'RSK Ext'), ('xinfin_ext', 'Xinfin Ext'), ('fiat', 'Fiat'), ('manual', 'Manual')], default='bounties_network', max_length=50),
),
migrations.AlterField(
model_name='bountyfulfillment',
name='payout_type',
field=models.CharField(blank=True, choices=[('bounties_network', 'bounties_network'), ('qr', 'qr'), ('fiat', 'fiat'), ('web3_modal', 'web3_modal'), ('polkadot_ext', 'polkadot_ext'), ('binance_ext', 'binance_ext'), ('harmony_ext', 'harmony_ext'), ('rsk_ext', 'rsk_ext'), ('xinfin_ext', 'xinfin_ext'), ('manual', 'manual')], help_text='payment type used to make the payment', max_length=20, null=True),
),
migrations.AlterField(
model_name='bountyfulfillment',
name='tenant',
field=models.CharField(blank=True, choices=[('BTC', 'BTC'), ('ETH', 'ETH'), ('ETC', 'ETC'), ('ZIL', 'ZIL'), ('CELO', 'CELO'), ('PYPL', 'PYPL'), ('POLKADOT', 'POLKADOT'), ('BINANCE', 'BINANCE'), ('HARMONY', 'HARMONY'), ('FILECOIN', 'FILECOIN'), ('RSK', 'RSK'), ('XINFIN', 'XINFIN'), ('OTHERS', 'OTHERS')], help_text='specific tenant type under the payout_type', max_length=10, null=True),
),
]
3 changes: 3 additions & 0 deletions app/dashboard/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ class Bounty(SuperModel):
('binance_ext', 'Binance Ext'),
('harmony_ext', 'Harmony Ext'),
('rsk_ext', 'RSK Ext'),
('xinfin_ext', 'Xinfin Ext'),
('fiat', 'Fiat'),
('manual', 'Manual')
)
Expand Down Expand Up @@ -1412,6 +1413,7 @@ class BountyFulfillment(SuperModel):
('binance_ext', 'binance_ext'),
('harmony_ext', 'harmony_ext'),
('rsk_ext', 'rsk_ext'),
('xinfin_ext', 'xinfin_ext'),
('manual', 'manual')
]

Expand All @@ -1427,6 +1429,7 @@ class BountyFulfillment(SuperModel):
('HARMONY', 'HARMONY'),
('FILECOIN', 'FILECOIN'),
('RSK', 'RSK'),
('XINFIN', 'XINFIN'),
('OTHERS', 'OTHERS')
]

Expand Down
93 changes: 93 additions & 0 deletions app/dashboard/sync/xinfin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import json

from django.conf import settings
from django.utils import timezone

import requests
from dashboard.sync.helpers import record_payout_activity, txn_already_used

API_KEY = settings.XINFIN_API_KEY

def find_txn_on_xinfin_explorer(fulfillment):
token_name = fulfillment.token_name

funderAddress = fulfillment.bounty.bounty_owner_address
amount = fulfillment.payout_amount
payeeAddress = fulfillment.fulfiller_address

if token_name not in ['XDC']:
return None

url = f'https://xdc.network/publicAPI?module=account&action=txlist&address={funderAddress}&page=0&pageSize=10&apikey={API_KEY}'
response = requests.get(url).json()

if response['message'] and response['result']:
for txn in response['result']:
to_address_match = txn['to'].lower() == payeeAddress.lower() if token_name == 'XDC' else True
if (
txn['from'].lower() == funderAddress.lower() and
to_address_match and
float(txn['value']) == float(amount * 10 ** 18) and
not txn_already_used(txn['hash'], token_name)
):
return txn
return None


def get_xinfin_txn_status(fulfillment):

txnid = fulfillment.payout_tx_id
token_name = fulfillment.token_name
funderAddress = fulfillment.bounty.bounty_owner_address
payeeAddress = fulfillment.fulfiller_address

amount = fulfillment.payout_amount


if token_name not in ['XDC']:
return None

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

url = f'https://explorer.xinfin.network/publicAPI?module=transaction&action=gettxdetails&txhash={txnid}&apikey={API_KEY}'
response = requests.get(url).json()

if response['status'] == '0':
return 'expired'
elif response['result']:
txn = response['result']

to_address_match = txn['to'].lower() == payeeAddress.lower() if token_name == 'XDC' else True

if (
txn['from'].lower() == funderAddress.lower() and
to_address_match and
float(float(txn['value'])/ 10**18) == float(amount) and
not txn_already_used(txn['hash'], token_name)
):
return 'success'

return None


def sync_xinfin_payout(fulfillment):
if not fulfillment.payout_tx_id or fulfillment.payout_tx_id == "0x0":
txn = find_txn_on_xinfin_explorer(fulfillment)
if txn:
fulfillment.payout_tx_id = txn['hash']
fulfillment.save()

if fulfillment.payout_tx_id and fulfillment.payout_tx_id != "0x0":
txn_status = get_xinfin_txn_status(fulfillment)

if txn_status == 'success':
fulfillment.payout_status = 'done'
fulfillment.accepted_on = timezone.now()
fulfillment.accepted = True
record_payout_activity(fulfillment)

elif txn_status == 'expired':
fulfillment.payout_status = 'expired'

fulfillment.save()
9 changes: 7 additions & 2 deletions app/dashboard/templates/bounty/details2.html
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ <h5 class="font-body font-weight-semibold">{% trans "SUBMISSIONS" %}</h5>
</div>

<!-- WEB3 MODAL FLOW -->
<div v-else-if="fulfillment.payout_type == 'rsk_ext' || fulfillment.payout_type == 'web3_modal' || fulfillment.payout_type == 'polkadot_ext' || fulfillment.payout_type == 'harmony_ext' || fulfillment.payout_type == 'binance_ext'" class="px-sm-4">
<div v-else-if="fulfillment.payout_type == 'xinfin_ext' || fulfillment.payout_type == 'rsk_ext' || fulfillment.payout_type == 'web3_modal' || fulfillment.payout_type == 'polkadot_ext' || fulfillment.payout_type == 'harmony_ext' || fulfillment.payout_type == 'binance_ext'" class="px-sm-4">

<div class="text-center pb-3">
<p class="mb-3 font-subheader font-weight-bold">Payout</p>
Expand Down Expand Up @@ -1125,10 +1125,15 @@ <h3>{{ noscript.keywords }}</h3>
<script src="{% static "v2/js/pages/bounty_detail/harmony_extension.js" %}"></script>

{% elif web3_type == 'rsk_ext' %}

<script src="{% static "v2/js/pages/bounty_detail/rsk_extension.js" %}"></script>

{% elif web3_type == 'xinfin_ext' %}

<script src="{% static "v2/js/lib/xinfin/xdc3.min.js" %}"></script>
<script src="{% static "v2/js/pages/bounty_detail/xinfin_extension.js" %}"></script>

{% elif web3_type == 'fiat' %}

{% if PYPL_CLIENT_ID %}
<script src="https://www.paypal.com/sdk/js?client-id={{PYPL_CLIENT_ID}}"></script>
{% endif %}
Expand Down
4 changes: 4 additions & 0 deletions app/dashboard/templates/bounty/new_bounty.html
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ <h1 class="text-center">Fund Issue</h1>
</label>

{% if is_staff %}
<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 === '50'}">
<input type="radio" name="bounty_chain" id="50_chain" value="50" v-model="chainId"><img class="mr-2" src="{% static 'v2/images/chains/xinfin.svg' %}" alt="" width="16"> Xinfin
</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 === '717171'}">
<input type="radio" name="bounty_chain" id="717171_chain" value="717171" v-model="chainId"> Other
</label>
Expand Down
Loading