Skip to content

Commit

Permalink
casper crosschain | bounties/hackathons (#9291)
Browse files Browse the repository at this point in the history
* update models

* update views

* update utils/mgmt cmd

* sync function

* update new bounty pages

* fetch browserify casper + svg logo

* fix casper chain id

* start casper extension

* + usable casper js sdk

* extension wip

* validate cspr addresses

* add address + tx urls

* finish casper extension

* minify svg & js sdk

* serialize deploy to / fro

* add payout forwarder endpoint

* handle rpc error

* final fixes

* review fixes

* less complexity

* restore tx expiry in tezos

* review update
  • Loading branch information
chibie authored Jul 26, 2021
1 parent 426c051 commit f8ede14
Show file tree
Hide file tree
Showing 17 changed files with 247 additions and 13 deletions.
1 change: 1 addition & 0 deletions app/app/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
url('^api/v1/bounty/fulfill', dashboard.views.fulfill_bounty_v1, name='fulfill_bounty_v1'),
path('api/v1/bounty/<int:bounty_id>/close', dashboard.views.close_bounty_v1, name='close_bounty_v1'),
path('api/v1/bounty/payout/<int:fulfillment_id>', dashboard.views.payout_bounty_v1, name='payout_bounty_v1'),
path('api/v1/reverse-proxy/<str:tenant>', dashboard.views.reverse_proxy_rpc_v1, name='payout_tx_forwarder_v1'),
re_path(r'.*api/v0.1/video/presence$', townsquare.views.video_presence, name='video_presence'),

# inbox
Expand Down
1 change: 1 addition & 0 deletions app/assets/v2/images/chains/casper.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/casper/casper_js_sdk.js

Large diffs are not rendered by default.

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

const amount = vm.fulfillment_context.amount;
const token_name = vm.bounty.token_name;
const tenant = vm.getTenant(token_name, vm.bounty.web3_type);

const { CasperClient, DeployUtil, PublicKey, Signer } = casper;
const casperClient = new CasperClient(`/api/v1/reverse-proxy/${tenant}`);

const isConnected = await Signer.isConnected();

if (!isConnected) {
try {
Signer.sendConnectionRequest();
} catch (e) {
_alert({ message: gettext('Please download or enable CasperLabs Signer extension.') }, 'danger');
return;
}
}

const selectedAddress = await Signer.getActivePublicKey();

const paymentAmount = 10000; // for native-transfers the payment price is fixed
const id = fulfillment_id;
const gasPrice = 1; // gasPrice for native transfers can be set to 1

// time that the deploy will remain valid for, in milliseconds
// the default value is 1800000 ms (30 minutes)
const ttl = 1800000;

const fromPublicKey = PublicKey.fromHex(selectedAddress);
const toPublicKey = PublicKey.fromHex(to_address);

let deployParams = new DeployUtil.DeployParams(fromPublicKey, 'casper', gasPrice, ttl);

const session = DeployUtil.ExecutableDeployItem.newTransfer(
amount * 10 ** vm.decimals,
toPublicKey,
null,
id
);

const payment = DeployUtil.standardPayment(paymentAmount);
const deploy = DeployUtil.makeDeploy(deployParams, session, payment);
const deployJson = DeployUtil.deployToJson(deploy);

const signedDeployJson = await Signer.sign(deployJson, selectedAddress, to_address);
const signedDeploy = DeployUtil.deployFromJson(signedDeployJson);

try {
const deployHash = await casperClient.putDeploy(signedDeploy);

callback(null, selectedAddress, deployHash);
} catch (e) {
modal.closeModal();
callback(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: 'casper_ext',
tenant: 'CASPER',
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);
});
}
}
};

17 changes: 17 additions & 0 deletions app/assets/v2/js/pages/bounty_details2.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ Vue.mixin({
url = `https://tzkt.io/${txn}`;
break;

case 'CSPR':
url = `https://casperstats.io/tx/${txn}`;
break;

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

Expand Down Expand Up @@ -244,6 +248,10 @@ Vue.mixin({
url = `https://tzkt.io/${address}/operations/`;
break;

case 'CSPR':
url = `https://casperstats.io/address/${address}`;
break;

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

case 'CSPR':
tenant = 'CASPER';
break;

default:
tenant = 'ETH';
}
Expand Down Expand Up @@ -589,6 +601,10 @@ Vue.mixin({
case 'tezos_ext':
payWithTezosExtension(fulfillment_id, fulfiller_address, vm, modal);
break;

case 'casper_ext':
payWithCasperExtension(fulfillment_id, fulfiller_address, vm, modal);
break;
}
},
closeBounty: function() {
Expand Down Expand Up @@ -809,6 +825,7 @@ Vue.mixin({
case 'xinfin_ext':
case 'algorand_ext':
case 'tezos_ext':
case 'casper_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 @@ -186,6 +186,10 @@ Vue.mixin({
// tezos
type = 'tezos_ext';
break;
case '270895':
// casper
type = 'casper_ext';
break;
case '666':
// paypal
type = 'fiat';
Expand Down
14 changes: 14 additions & 0 deletions app/assets/v2/js/pages/new_bounty.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,16 @@ Vue.mixin({
break;
}

case '270895': {
// casper
let addr = vm.form.funderAddress;

if (!addr.toLowerCase().startsWith('01') && !addr.toLowerCase().startsWith('02')) {
isValid = false;
}
break;
}

// include validation for other chains here
}

Expand Down Expand Up @@ -255,6 +265,10 @@ Vue.mixin({
// tezos
type = 'tezos_ext';
break;
case '270895':
// casper
type = 'casper_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', 'tezos_ext']
ext_payout_types= ['web3_modal', 'polkadot_ext', 'harmony_ext', 'binance_ext', 'rsk_ext', 'xinfin_ext', 'nervos_ext', 'algorand_ext', 'sia_ext', 'tezos_ext', 'casper_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
3 changes: 3 additions & 0 deletions app/dashboard/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ class Bounty(SuperModel):
('algorand_ext', 'Algorand Ext'),
('sia_ext', 'Sia Ext'),
('tezos_ext', 'Tezos Ext'),
('casper_ext', 'Casper Ext'),
('fiat', 'Fiat'),
('manual', 'Manual')
)
Expand Down Expand Up @@ -1410,6 +1411,7 @@ class BountyFulfillment(SuperModel):
('algorand_ext', 'algorand_ext'),
('sia_ext', 'sia_ext'),
('tezos_ext', 'tezos_ext'),
('casper_ext', 'casper_ext'),
('manual', 'manual')
]

Expand All @@ -1430,6 +1432,7 @@ class BountyFulfillment(SuperModel):
('ALGORAND', 'ALGORAND'),
('SIA', 'SIA'),
('TEZOS', 'TEZOS'),
('CASPER', 'CASPER'),
('OTHERS', 'OTHERS')
]

Expand Down
4 changes: 0 additions & 4 deletions app/dashboard/sync/binance.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ def get_binance_txn_status(fulfillment):
'params': [ txnid ]
}

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

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

result = binance_response['result']
Expand Down
63 changes: 63 additions & 0 deletions app/dashboard/sync/casper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import logging

from django.utils import timezone

import requests
from dashboard.sync.helpers import record_payout_activity

logger = logging.getLogger(__name__)


def get_casper_txn_status(fulfillment):
txnid = fulfillment.payout_tx_id
token_name = fulfillment.token_name
funderAddress = fulfillment.funder_address
amount = fulfillment.payout_amount

if token_name != 'CSPR' or not txnid:
return None

try:
data = {
'id': 0,
'jsonrpc': '2.0',
'method': 'info_get_deploy',
'params': [ txnid ]
}
casper_rpc_url = 'http://3.142.224.108:7777/rpc'
casper_response = requests.post(casper_rpc_url, json=data).json()

result = casper_response['result']

if result:
transfer_args = result["deploy"]["session"]["Transfer"]["args"]
if (
result["deploy"]["hash"] == txnid
and result["deploy"]["header"]["account"] == funderAddress
and float([
arg for arg in transfer_args if arg[0] == 'amount'
][0][1]['parsed']) == float(amount) * 10 ** 9
):
if result["execution_results"][0]["result"].get("Success", False) != False:
return 'success'
return 'expired'

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

return None


def sync_casper_payout(fulfillment):
if fulfillment.payout_tx_id:
txn_status = get_casper_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()
4 changes: 1 addition & 3 deletions app/dashboard/sync/tezos.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,8 @@ def get_tezos_txn_status(fulfillment):
and tx_response['sender']['address'] == funderAddress
and tx_response['target']['address'] == payeeAddress
and tx_response['amount'] == float(amount) * 10 ** 6
and tx_response['status'] == 'applied'
and confirmations > 0
):
if tx_response['status'] == 'applied':
if tx_response['status'] == 'applied' and confirmations > 0:
return 'success'
return 'expired'

Expand Down
7 changes: 6 additions & 1 deletion app/dashboard/templates/bounty/details2.html
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ <h5 class="font-body font-weight-semibold">{% trans "SUBMISSIONS" %}</h5>
</div>

<!-- WEB3 MODAL FLOW -->
<div v-else-if="fulfillment.payout_type == 'algorand_ext' || 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' || fulfillment.payout_type == 'tezos_ext'" class="px-sm-4">
<div v-else-if="fulfillment.payout_type == 'algorand_ext' || 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' || fulfillment.payout_type == 'tezos_ext' || fulfillment.payout_type == 'casper_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 @@ -1163,6 +1163,11 @@ <h3>{{ noscript.keywords }}</h3>
<script src="{% static "v2/js/lib/tezos/taquito-beacon-wallet.min.js" %}"></script>
<script src="{% static "v2/js/pages/bounty_detail/tezos_extension.js" %}"></script>

{% elif web3_type == 'casper_ext' %}

<script src="{% static "v2/js/lib/casper/casper_js_sdk.js" %}"></script>
<script src="{% static "v2/js/pages/bounty_detail/casper_extension.js" %}"></script>

{% elif web3_type == 'fiat' %}

{% if PYPL_CLIENT_ID %}
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 @@ -139,6 +139,10 @@ <h1 class="text-center">Fund Issue</h1>
<input type="radio" name="bounty_chain" id="50797_chain" value="50797" v-model="chainId"><img class="mr-2" src="{% static 'v2/images/chains/tezos.svg' %}" alt="" width="16"> Tezos
</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 === '270895'}">
<input type="radio" name="bounty_chain" id="270895_chain" value="270895" v-model="chainId"><img class="mr-2" src="{% static 'v2/images/chains/casper.svg' %}" alt="" width="16"> Casper
</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 @@ -203,6 +203,10 @@ <h1 class="text-center">Fund Prize</h1>
<input type="radio" name="bounty_chain" id="50797_chain" value="50797" v-model="chainId"><img class="mr-2" src="{% static 'v2/images/chains/tezos.svg' %}" alt="" width="16"> Tezos
</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 === '270895'}">
<input type="radio" name="bounty_chain" id="270895_chain" value="270895" v-model="chainId"><img class="mr-2" src="{% static 'v2/images/chains/casper.svg' %}" alt="" width="16"> Casper
</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 @@ -39,6 +39,7 @@
from dashboard.sync.algorand import sync_algorand_payout
from dashboard.sync.binance import sync_binance_payout
from dashboard.sync.btc import sync_btc_payout
from dashboard.sync.casper import sync_casper_payout
from dashboard.sync.celo import sync_celo_payout
from dashboard.sync.etc import sync_etc_payout
from dashboard.sync.eth import sync_eth_payout
Expand Down Expand Up @@ -671,6 +672,9 @@ def sync_payout(fulfillment):
elif fulfillment.payout_type == 'tezos_ext':
sync_tezos_payout(fulfillment)

elif fulfillment.payout_type == 'casper_ext':
sync_casper_payout(fulfillment)


def get_bounty_id(issue_url, network):
issue_url = normalize_url(issue_url)
Expand Down
Loading

0 comments on commit f8ede14

Please sign in to comment.