Skip to content

Commit

Permalink
feat: sia crosschain | bounties/hackathons (#8899)
Browse files Browse the repository at this point in the history
* backend sync functions

* finish backend work

* frontend work

- add logo
- create bounty flow
- add sia extension
- allow change of address

* remove unnecessary use of extension

* little fix

* bump funder address validation

* bug fix

* remove sia validation placeholder
  • Loading branch information
chibie authored Jun 7, 2021
1 parent c248542 commit ee6260c
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 24 deletions.
1 change: 1 addition & 0 deletions app/assets/v2/images/chains/sia.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 31 additions & 1 deletion app/assets/v2/js/pages/bounty_details2.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ Vue.mixin({
url = `https://algoexplorer.io/tx/${txn}`;
break;

case 'SC':
url = `https://siastats.info/navigator?search=${txn}`;
break;

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

Expand Down Expand Up @@ -220,6 +224,10 @@ Vue.mixin({
url = `https://algoexplorer.io/tx/${address}`;
break;

case 'SC':
url = `https://siastats.info/navigator?search=${address}`;
break;

default:
url = `https://etherscan.io/address/${address}`;
}
Expand Down Expand Up @@ -392,6 +400,7 @@ Vue.mixin({
},
getTenant: function(token_name, web3_type) {
let tenant;
let vm = this;

if (web3_type == 'manual') {
tenant = 'OTHERS';
Expand Down Expand Up @@ -453,6 +462,11 @@ Vue.mixin({
tenant = 'ALGORAND';
break;

case 'SC':
tenant = 'SIA';
vm.canChangeFunderAddress = true;
break;

default:
tenant = 'ETH';
}
Expand Down Expand Up @@ -754,7 +768,9 @@ Vue.mixin({
switch (fulfillment.payout_type) {
case 'qr':
case 'manual':
case 'sia_ext':
vm.fulfillment_context.active_step = 'check_wallet_owner';
vm.getTenant(vm.bounty.token_name, fulfillment.payout_type);
break;

case 'fiat':
Expand Down Expand Up @@ -803,6 +819,18 @@ Vue.mixin({
vm.fulfillment_context.active_step = 'payout_amount';
break;
}
},
validateFunderAddress: function(token_name) {
let vm = this;
let hasError = false;

vm.errors = {};

// include validation for tokens here - switch statement

if (hasError) {
vm.$set(vm.errors, 'funderAddress', `Please enter a valid ${token_name} address`);
}
}
},
computed: {
Expand Down Expand Up @@ -844,6 +872,7 @@ if (document.getElementById('gc-bounty-detail')) {
el: '#gc-bounty-detail',
data() {
return {
errors: {},
loadingState: loadingState['loading'],
bounty: bounty,
url: url,
Expand All @@ -859,7 +888,8 @@ if (document.getElementById('gc-bounty-detail')) {
inputBountyOwnerAddress: bounty.bounty_owner_address,
contxt: document.contxt,
quickLinks: [],
pollInterval: null
pollInterval: null,
canChangeFunderAddress: false
};
},
mounted() {
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 @@ -174,6 +174,10 @@ Vue.mixin({
// algorand
type = 'algorand_ext';
break;
case '1935':
// sia
type = 'sia_ext';
break;
case '666':
// paypal
type = 'fiat';
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 @@ -214,6 +214,10 @@ Vue.mixin({
// algorand
type = 'algorand_ext';
break;
case '1935':
// sia
type = 'sia_ext';
break;
case '666':
// paypal
type = 'fiat';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,12 @@ def handle(self, *args, **options):
)

# Extensions
ext_payout_types= ['web3_modal', 'polkadot_ext', 'harmony_ext', 'binance_ext', 'rsk_ext', 'xinfin_ext', 'algorand_ext']
ext_payout_types= ['web3_modal', 'polkadot_ext', 'harmony_ext', 'binance_ext', 'rsk_ext', 'xinfin_ext', 'algorand_ext', 'sia_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():
sync_payout(fulfillment)


# QR
qr_pending_fulfillments = pending_fulfillments.filter(payout_type='qr')
if qr_pending_fulfillments:
Expand Down
3 changes: 3 additions & 0 deletions app/dashboard/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ class Bounty(SuperModel):
('rsk_ext', 'RSK Ext'),
('xinfin_ext', 'Xinfin Ext'),
('algorand_ext', 'Algorand Ext'),
('sia_ext', 'Sia Ext'),
('fiat', 'Fiat'),
('manual', 'Manual')
)
Expand Down Expand Up @@ -1416,6 +1417,7 @@ class BountyFulfillment(SuperModel):
('rsk_ext', 'rsk_ext'),
('xinfin_ext', 'xinfin_ext'),
('algorand_ext', 'algorand_ext'),
('sia_ext', 'sia_ext'),
('manual', 'manual')
]

Expand All @@ -1433,6 +1435,7 @@ class BountyFulfillment(SuperModel):
('RSK', 'RSK'),
('XINFIN', 'XINFIN'),
('ALGORAND', 'ALGORAND'),
('SIA', 'SIA'),
('OTHERS', 'OTHERS')
]

Expand Down
74 changes: 74 additions & 0 deletions app/dashboard/sync/sia.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from django.utils import timezone

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

BASE_URL = 'https://siastats.info:3500/navigator-api'


def find_txn_on_sia_explorer(fulfillment):
token_name = fulfillment.token_name

funderAddress = fulfillment.funder_address
amount = fulfillment.payout_amount

if token_name != 'SC':
return None

url = f'{BASE_URL}/hash/{funderAddress}'

response = requests.get(url).json()

if response:
last100_txns = response[1]['last100Transactions']
if response[0]['Type'] == 'address' and last100_txns:
for txn in last100_txns:
if (
txn['TxType'] == 'ScTx'
and txn['ScChange'] < 0
and abs(txn['ScChange']) / 10 ** 24 == amount / 10 ** 24
and not txn_already_used(txn['MasterHash'], token_name)
):
return txn
return None


def get_sia_txn_status(txnid, network='mainnet'):
if not txnid:
return None

tx_url = f'{BASE_URL}/hash/{txnid}'
stats_url = f'{BASE_URL}/status'

tx_response = requests.get(tx_url).json()

if tx_response:
last_block = requests.get(stats_url).json()[0]['lastblock'] # or consensusblock ?

confirmations = last_block - tx_response[1]['Height']

if (
tx_response[0]['Type'] == 'ScTx'
and tx_response[0]['MasterHash'].strip() == txnid
and confirmations > 0
):
return True
else:
return False


def sync_sia_payout(fulfillment):
if not fulfillment.payout_tx_id or fulfillment.payout_tx_id == "0x0":
txn = find_txn_on_sia_explorer(fulfillment)
if txn:
fulfillment.payout_tx_id = txn['MasterHash']
fulfillment.save()

if fulfillment.payout_tx_id and fulfillment.payout_tx_id != "0x0":
txn_status = get_sia_txn_status(fulfillment.payout_tx_id)
if txn_status:
fulfillment.payout_status = 'done'
fulfillment.accepted_on = timezone.now()
fulfillment.accepted = True
record_payout_activity(fulfillment)
fulfillment.save()
50 changes: 33 additions & 17 deletions app/dashboard/templates/bounty/details2.html
Original file line number Diff line number Diff line change
Expand Up @@ -530,14 +530,14 @@ <h5 class="font-body font-weight-semibold">{% trans "SUBMISSIONS" %}</h5>
<p>
This bounty can only be paid out from the same wallet address that funded it.
</p>
<br>
<p class="mb-4 font-weight-bold">
Make sure you’re paying out from this address

<span v-if="fulfillment.payout_type == 'manual'">[[ bounty.bounty_owner_address | truncateHash ]]</span>
<a v-else class="font-weight-bold" :href="getAddressURL(fulfillment.token_name, bounty.bounty_owner_address)" target="_blank">
[[ bounty.bounty_owner_address ]]
</a>

</p>
<button class="btn btn-primary btn-block py-3 btn-lg mt-4"
v-on:click="goToStep('payout_amount', 'check_wallet_owner')"
Expand Down Expand Up @@ -613,7 +613,7 @@ <h5 class="font-body font-weight-semibold">{% trans "SUBMISSIONS" %}</h5>
<button class="btn btn-primary btn-block py-3 btn-lg mt-3"
@click="fulfillmentComplete(fulfillment.payout_type, fulfillment.pk, $event)"
>
I have Paid
I have paid
</button>

<p class="font-subheader mt-4 mb-0 text-center">
Expand Down Expand Up @@ -645,29 +645,45 @@ <h5 class="font-body font-weight-semibold">{% trans "SUBMISSIONS" %}</h5>

<!-- STEP : WALLET OWNER CHECK -->
<div id="check_wallet_owner" v-if="fulfillment_context.active_step == 'check_wallet_owner'">

<p>
<div class="funder-address-container mt-4" v-if="canChangeFunderAddress">
<label class="font-caption letter-spacing text-black-60 text-uppercase" for="funderAddress">Funder Address</label>
<input v-model="bounty.bounty_owner_address" @input="validateFunderAddress(bounty.token_name)" name="funderAddress" id="funderAddress" class="form__input" type="text" placeholder="Address with which the bounty will be paid out">
<div class="text-danger" v-if="errors.funderAddress">
[[ errors.funderAddress ]]
</div>
</div>
<p v-else>
This bounty can only be paid out from the same wallet address that funded it.
</p>
<p class="mb-4">
<br>
<p class="mb-4 font-weight-bold">
Make sure you’re paying out from this address

<span v-if="fulfillment.payout_type == 'manual'">[[ bounty.bounty_owner_address | truncateHash ]]</span>
<span v-if="fulfillment.payout_type == 'manual' && !canChangeFunderAddress">[[ bounty.bounty_owner_address | truncateHash ]]</span>
<a v-else class="font-weight-bold" :href="getAddressURL(fulfillment.token_name, bounty.bounty_owner_address)" target="_blank">
[[ bounty.bounty_owner_address ]]
</a>

</p>
<button class="btn btn-primary btn-block py-3 btn-lg mt-4"
v-on:click="goToStep('payout_amount', 'check_wallet_owner', 'access_to_wallet')"
>
Yes, I understand
</button>
<p class="font-subheader mt-4 mb-0 text-center">
<a href="#" v-on:click="goToStep('payout_amount', 'check_wallet_owner', 'no_access_to_wallet')">
I don’t have access to this address
</a>
</p>
<div v-if="canChangeFunderAddress">
<button :disabled="errors.funderAddress" class="btn btn-primary btn-block py-3 btn-lg mt-4"
v-on:click="goToStep('payout_amount', 'check_wallet_owner')"
>
Yes, I understand
</button>
</div>
<div v-else>
<button class="btn btn-primary btn-block py-3 btn-lg mt-4"
v-on:click="goToStep('payout_amount', 'check_wallet_owner', 'access_to_wallet')"
>
Yes, I understand
</button>
<p class="font-subheader mt-4 mb-0 text-center">
<a href="#" v-on:click="goToStep('payout_amount', 'check_wallet_owner', 'no_access_to_wallet')">
I don’t have access to this address
</a>
</p>
</div>

</div>

Expand Down Expand Up @@ -801,7 +817,7 @@ <h5 class="font-body font-weight-semibold">{% trans "SUBMISSIONS" %}</h5>
<button class="btn btn-primary btn-block py-3 btn-lg mt-3"
@click="fulfillmentComplete(fulfillment.payout_type, fulfillment.pk, $event)"
>
I have Paid
I have paid
</button>

<p class="font-subheader mt-4 mb-0 text-center">
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 @@ -127,6 +127,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 === '1935'}">
<input type="radio" name="bounty_chain" id="1935_chain" value="1935" v-model="chainId"><img class="mr-2" src="{% static 'v2/images/chains/sia.svg' %}" alt="" width="16"> Sia
</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
4 changes: 4 additions & 0 deletions app/dashboard/templates/dashboard/hackathon/new_bounty.html
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ <h1 class="text-center">Fund Prize</h1>
<input type="radio" name="bounty_chain" id="1001_chain" value="1001" v-model="chainId"><img class="mr-2" src="{% static 'v2/images/chains/algorand.svg' %}" alt="" width="16"> Algorand
</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 === '1935'}">
<input type="radio" name="bounty_chain" id="1935_chain" value="1935" v-model="chainId"><img class="mr-2" src="{% static 'v2/images/chains/sia.svg' %}" alt="" width="16"> Sia
</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
4 changes: 4 additions & 0 deletions app/dashboard/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
from dashboard.sync.harmony import sync_harmony_payout
from dashboard.sync.polkadot import sync_polkadot_payout
from dashboard.sync.rsk import sync_rsk_payout
from dashboard.sync.sia import sync_sia_payout
from dashboard.sync.xinfin import sync_xinfin_payout
from dashboard.sync.zil import sync_zil_payout
from ens.auto import ns
Expand Down Expand Up @@ -672,6 +673,9 @@ def sync_payout(fulfillment):
elif fulfillment.payout_type == 'algorand_ext':
sync_algorand_payout(fulfillment)

elif fulfillment.payout_type == 'sia_ext':
sync_sia_payout(fulfillment)


def get_bounty_id(issue_url, network):
issue_url = normalize_url(issue_url)
Expand Down
8 changes: 4 additions & 4 deletions app/dashboard/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6153,7 +6153,7 @@ def fulfill_bounty_v1(request):
if payout_type == 'fiat' and not fulfiller_identifier:
response['message'] = 'error: missing fulfiller_identifier'
return JsonResponse(response)
elif payout_type in ['qr', 'polkadot_ext', 'harmony_ext', 'binance_ext', 'rsk_ext', 'xinfin_ext', 'algorand_ext'] and not fulfiller_address:
elif payout_type in ['qr', 'polkadot_ext', 'harmony_ext', 'binance_ext', 'rsk_ext', 'xinfin_ext', 'algorand_ext', 'sia_ext'] and not fulfiller_address:
response['message'] = 'error: missing fulfiller_address'
return JsonResponse(response)

Expand Down Expand Up @@ -6270,8 +6270,8 @@ def payout_bounty_v1(request, fulfillment_id):
if not payout_type:
response['message'] = 'error: missing parameter payout_type'
return JsonResponse(response)
if payout_type not in ['fiat', 'qr', 'web3_modal', 'polkadot_ext', 'harmony_ext' , 'binance_ext', 'rsk_ext', 'xinfin_ext', 'algorand_ext', 'manual']:
response['message'] = 'error: parameter payout_type must be fiat / qr / web_modal / polkadot_ext / harmony_ext / binance_ext / rsk_ext / xinfin_ext / algorand_ext / manual'
if payout_type not in ['fiat', 'qr', 'web3_modal', 'polkadot_ext', 'harmony_ext' , 'binance_ext', 'rsk_ext', 'xinfin_ext', 'algorand_ext', 'sia_ext', 'manual']:
response['message'] = 'error: parameter payout_type must be fiat / qr / web_modal / polkadot_ext / harmony_ext / binance_ext / rsk_ext / xinfin_ext / algorand_ext / sia_ext / manual'
return JsonResponse(response)
if payout_type == 'manual' and not bounty.event:
response['message'] = 'error: payout_type manual is eligible only for hackathons'
Expand Down Expand Up @@ -6337,7 +6337,7 @@ def payout_bounty_v1(request, fulfillment_id):
fulfillment.save()
record_bounty_activity(bounty, user, 'worker_paid', None, fulfillment)

elif payout_type in ['qr', 'web3_modal', 'polkadot_ext', 'harmony_ext', 'binance_ext', 'rsk_ext', 'xinfin_ext', 'algorand_ext']:
elif payout_type in ['qr', 'web3_modal', 'polkadot_ext', 'harmony_ext', 'binance_ext', 'rsk_ext', 'xinfin_ext', 'algorand_ext', 'sia_ext']:
fulfillment.payout_status = 'pending'
fulfillment.save()
sync_payout(fulfillment)
Expand Down

0 comments on commit ee6260c

Please sign in to comment.