diff --git a/app/assets/v2/images/chains/sia.svg b/app/assets/v2/images/chains/sia.svg new file mode 100644 index 00000000000..706198cce68 --- /dev/null +++ b/app/assets/v2/images/chains/sia.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/v2/js/pages/bounty_details2.js b/app/assets/v2/js/pages/bounty_details2.js index 5131771e039..a311001458a 100644 --- a/app/assets/v2/js/pages/bounty_details2.js +++ b/app/assets/v2/js/pages/bounty_details2.js @@ -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}`; @@ -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}`; } @@ -392,6 +400,7 @@ Vue.mixin({ }, getTenant: function(token_name, web3_type) { let tenant; + let vm = this; if (web3_type == 'manual') { tenant = 'OTHERS'; @@ -453,6 +462,11 @@ Vue.mixin({ tenant = 'ALGORAND'; break; + case 'SC': + tenant = 'SIA'; + vm.canChangeFunderAddress = true; + break; + default: tenant = 'ETH'; } @@ -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': @@ -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: { @@ -844,6 +872,7 @@ if (document.getElementById('gc-bounty-detail')) { el: '#gc-bounty-detail', data() { return { + errors: {}, loadingState: loadingState['loading'], bounty: bounty, url: url, @@ -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() { diff --git a/app/assets/v2/js/pages/hackathon_new_bounty.js b/app/assets/v2/js/pages/hackathon_new_bounty.js index 3c1e6b5205e..bcb4396d016 100644 --- a/app/assets/v2/js/pages/hackathon_new_bounty.js +++ b/app/assets/v2/js/pages/hackathon_new_bounty.js @@ -174,6 +174,10 @@ Vue.mixin({ // algorand type = 'algorand_ext'; break; + case '1935': + // sia + type = 'sia_ext'; + break; case '666': // paypal type = 'fiat'; diff --git a/app/assets/v2/js/pages/new_bounty.js b/app/assets/v2/js/pages/new_bounty.js index a20a430274d..bd9650ec00d 100644 --- a/app/assets/v2/js/pages/new_bounty.js +++ b/app/assets/v2/js/pages/new_bounty.js @@ -214,6 +214,10 @@ Vue.mixin({ // algorand type = 'algorand_ext'; break; + case '1935': + // sia + type = 'sia_ext'; + break; case '666': // paypal type = 'fiat'; diff --git a/app/dashboard/management/commands/sync_pending_fulfillments.py b/app/dashboard/management/commands/sync_pending_fulfillments.py index 39b3dc63009..b7ad91668a1 100644 --- a/app/dashboard/management/commands/sync_pending_fulfillments.py +++ b/app/dashboard/management/commands/sync_pending_fulfillments.py @@ -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: diff --git a/app/dashboard/models.py b/app/dashboard/models.py index 015d06cce83..bea7355c39d 100644 --- a/app/dashboard/models.py +++ b/app/dashboard/models.py @@ -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') ) @@ -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') ] @@ -1433,6 +1435,7 @@ class BountyFulfillment(SuperModel): ('RSK', 'RSK'), ('XINFIN', 'XINFIN'), ('ALGORAND', 'ALGORAND'), + ('SIA', 'SIA'), ('OTHERS', 'OTHERS') ] diff --git a/app/dashboard/sync/sia.py b/app/dashboard/sync/sia.py new file mode 100644 index 00000000000..96d4d422aa0 --- /dev/null +++ b/app/dashboard/sync/sia.py @@ -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() diff --git a/app/dashboard/templates/bounty/details2.html b/app/dashboard/templates/bounty/details2.html index 76eb47bcb0f..12eeb736438 100644 --- a/app/dashboard/templates/bounty/details2.html +++ b/app/dashboard/templates/bounty/details2.html @@ -530,6 +530,7 @@
This bounty can only be paid out from the same wallet address that funded it.
+Make sure you’re paying out from this address @@ -537,7 +538,6 @@