Skip to content

Commit

Permalink
GITC-112: tezos crosschain | bounties/hackathons (#9182)
Browse files Browse the repository at this point in the history
* tezos backend init

* finish sync

* svg + add to chain list

* add tenant

* + browser script

* start tezos extension

* + taquito, beacon wallet

* - finish extension
- switch to mainnet
- count confirmations

* minify taquito beacon wallet

* add validation for tezos addresses

* fix address validation

* show web3 modal only for eth in issue/fulfill

* make migrations

* address comments

* compress tezos.svg

* review fix

Co-authored-by: Octavio Amuchástegui <[email protected]>
Co-authored-by: Aditya Anand M C <[email protected]>
  • Loading branch information
3 people authored Jul 1, 2021
1 parent 3bb4d64 commit 8a534c4
Show file tree
Hide file tree
Showing 19 changed files with 297 additions and 22 deletions.
1 change: 1 addition & 0 deletions app/assets/v2/images/chains/tezos.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/assets/v2/js/lib/tezos/taquito-beacon-wallet.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions app/assets/v2/js/lib/tezos/taquito.min.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions app/assets/v2/js/lib/tezos/walletbeacon.min.js

Large diffs are not rendered by default.

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

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

let selectedAddress;

const Tezos = new taquito.TezosToolkit('https://mainnet-tezos.giganode.io');
const wallet = new taquitoBeaconWallet.BeaconWallet({ name: 'Gitcoin' });

Tezos.setWalletProvider(wallet);

const activeAccount = await wallet.client.getActiveAccount();

if (activeAccount) {
console.log('Already connected:', activeAccount.address);
selectedAddress = activeAccount.address;
} else {
try {
await wallet.requestPermissions();
selectedAddress = await wallet.getPKH();
console.log('New connection:', selectedAddress);
} catch (e) {
console.log(e);
}
}

if (token_name == 'XTZ') {
try {
const txHash = await wallet.sendOperations([
{
kind: beacon.TezosOperationType.TRANSACTION,
destination: to_address,
amount: amount * 10 ** vm.decimals
}
]);

callback(null, selectedAddress, txHash);
} catch (e) {
modal.closeModal();
_alert({ message: `${e.title} - ${e.description}` }, 'danger');
console.log(e);
}
}

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

const payload = {
payout_type: 'tezos_ext',
tenant: 'TEZOS',
amount: amount,
token_name: token_name,
funder_address: from_address,
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) {
vm.fetchBounty();
_alert('Payment Successful', 'success');

} else {
_alert('Unable to make payout bounty. Please try again later', 'danger');
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', 'danger');
console.log(error);
});
}
}
};

19 changes: 18 additions & 1 deletion app/assets/v2/js/pages/bounty_details2.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ Vue.mixin({
url = `https://siastats.info/navigator?search=${txn}`;
break;

case 'XTZ':
url = `https://tzkt.io/${txn}`;
break;

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

Expand Down Expand Up @@ -236,6 +240,10 @@ Vue.mixin({
url = `https://siastats.info/navigator?search=${address}`;
break;

case 'XTZ':
url = `https://tzkt.io/${address}/operations/`;
break;

default:
url = `https://etherscan.io/address/${address}`;
}
Expand Down Expand Up @@ -480,6 +488,10 @@ Vue.mixin({
vm.canChangeFunderAddress = true;
break;

case 'XTZ':
tenant = 'TEZOS';
break;

default:
tenant = 'ETH';
}
Expand Down Expand Up @@ -574,6 +586,10 @@ Vue.mixin({
case 'algorand_ext':
payWithAlgorandExtension(fulfillment_id, fulfiller_address, vm, modal);
break;

case 'tezos_ext':
payWithTezosExtension(fulfillment_id, fulfiller_address, vm, modal);
break;
}
},
closeBounty: function() {
Expand Down Expand Up @@ -793,6 +809,7 @@ Vue.mixin({
case 'rsk_ext':
case 'xinfin_ext':
case 'algorand_ext':
case 'tezos_ext':
vm.fulfillment_context.active_step = 'payout_amount';
break;
}
Expand Down Expand Up @@ -845,7 +862,7 @@ Vue.mixin({
const ADDRESS_REGEX = new RegExp('^(ckb){1}[0-9a-zA-Z]{43,92}$');
const isNervosValid = ADDRESS_REGEX.test(vm.bounty.bounty_owner_address);

if (!isNervosValid && !address.toLowerCase().startsWith('0x')) {
if (!isNervosValid && !vm.bounty.bounty_owner_address.toLowerCase().startsWith('0x')) {
hasError = true;
}
}
Expand Down
2 changes: 1 addition & 1 deletion app/assets/v2/js/pages/fulfill_bounty/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable no-console */
if (bountyChainId !== '58') {
if (web3_type === 'web3_modal') {
needWalletConnection();

const fetchFromWeb3Wallet = () => {
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 @@ -182,6 +182,10 @@ Vue.mixin({
// sia
type = 'sia_ext';
break;
case '50797':
// tezos
type = 'tezos_ext';
break;
case '666':
// paypal
type = 'fiat';
Expand Down
57 changes: 45 additions & 12 deletions app/assets/v2/js/pages/new_bounty.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,27 +121,57 @@ Vue.mixin({

vm.form.keywords.push(item);
},
validateBtc: function() {
validateFunderAddress: function() {
let vm = this;
let isValid = true;

const ADDRESS_REGEX = new RegExp('^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$');
const BECH32_REGEX = new RegExp('^bc1[ac-hj-np-zAC-HJ-NP-Z02-9]{11,71}$');
const valid_legacy = ADDRESS_REGEX.test(vm.form.funderAddress);
const valid_segwit = BECH32_REGEX.test(vm.form.funderAddress);
switch (vm.chainId) {
case '1995': {
// nervos
const ADDRESS_REGEX = new RegExp('^(ckb){1}[0-9a-zA-Z]{43,92}$');
const isNervosValid = ADDRESS_REGEX.test(vm.form.funderAddress);

if (!isNervosValid && !vm.form.funderAddress.toLowerCase().startsWith('0x')) {
isValid = false;
}
break;
}

case '50797': {
// tezos
const ADDRESS_REGEX = new RegExp('^(tz1|tz2|tz3)[0-9a-zA-Z]{33}$');
const isTezosValid = ADDRESS_REGEX.test(vm.form.funderAddress);

if (!isTezosValid) {
isValid = false;
}
break;
}

case '0': {
// btc
const ADDRESS_REGEX = new RegExp('^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$');
const BECH32_REGEX = new RegExp('^bc1[ac-hj-np-zAC-HJ-NP-Z02-9]{11,71}$');
const valid_legacy = ADDRESS_REGEX.test(vm.form.funderAddress);
const valid_segwit = BECH32_REGEX.test(vm.form.funderAddress);

if (!valid_legacy && !valid_segwit) {
isValid = false;
}
break;
}

if (valid_legacy || valid_segwit) {
return true;
// include validation for other chains here
}

return false;
return isValid;
},
checkForm: async function(e) {
let vm = this;

vm.submitted = true;
vm.errors = {};


if (!vm.form.keywords.length) {
vm.$set(vm.errors, 'keywords', 'Please select the prize keywords');
}
Expand All @@ -160,9 +190,8 @@ Vue.mixin({
if (!vm.form.funderAddress) {
vm.$set(vm.errors, 'funderAddress', 'Fill the owner wallet address');
}
// validate BTC address
if (vm.chainId == 0 && !vm.validateBtc()) {
vm.$set(vm.errors, 'funderAddress', 'Please enter a valid BTC address');
if (!vm.validateFunderAddress()) {
vm.$set(vm.errors, 'funderAddress', `Please enter a valid ${vm.form.token.symbol} address`);
}
if (!vm.form.project_type) {
vm.$set(vm.errors, 'project_type', 'Select the project type');
Expand Down Expand Up @@ -222,6 +251,10 @@ Vue.mixin({
// sia
type = 'sia_ext';
break;
case '50797':
// tezos
type = 'tezos_ext';
break;
case '666':
// paypal
type = 'fiat';
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', 'xinfin_ext', 'nervos_ext', 'algorand_ext', 'sia_ext']
ext_payout_types= ['web3_modal', 'polkadot_ext', 'harmony_ext', 'binance_ext', 'rsk_ext', 'xinfin_ext', 'nervos_ext', 'algorand_ext', 'sia_ext', 'tezos_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/0181_auto_20210623_1536.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 2.2.20 on 2021-06-23 15:36

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('dashboard', '0180_auto_20210503_0431'),
]

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'), ('nervos_ext', 'Nervos Ext'), ('algorand_ext', 'Algorand Ext'), ('sia_ext', 'Sia Ext'), ('tezos_ext', 'Tezos 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'), ('nervos_ext', 'nervos_ext'), ('algorand_ext', 'algorand_ext'), ('sia_ext', 'sia_ext'), ('tezos_ext', 'tezos_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'), ('NERVOS', 'NERVOS'), ('ALGORAND', 'ALGORAND'), ('SIA', 'SIA'), ('TEZOS', 'TEZOS'), ('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 @@ -300,6 +300,7 @@ class Bounty(SuperModel):
('nervos_ext', 'Nervos Ext'),
('algorand_ext', 'Algorand Ext'),
('sia_ext', 'Sia Ext'),
('tezos_ext', 'Tezos Ext'),
('fiat', 'Fiat'),
('manual', 'Manual')
)
Expand Down Expand Up @@ -1421,6 +1422,7 @@ class BountyFulfillment(SuperModel):
('nervos_ext', 'nervos_ext'),
('algorand_ext', 'algorand_ext'),
('sia_ext', 'sia_ext'),
('tezos_ext', 'tezos_ext'),
('manual', 'manual')
]

Expand All @@ -1440,6 +1442,7 @@ class BountyFulfillment(SuperModel):
('NERVOS', 'NERVOS'),
('ALGORAND', 'ALGORAND'),
('SIA', 'SIA'),
('TEZOS', 'TEZOS'),
('OTHERS', 'OTHERS')
]

Expand Down
1 change: 0 additions & 1 deletion app/dashboard/sync/rsk.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ def get_rsk_txn_status(fulfillment):
if txn['success']:
return 'success'
return 'expired'


return None

Expand Down
Loading

0 comments on commit 8a534c4

Please sign in to comment.