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

feat: algorand grants integration #9168

Merged
merged 5 commits into from
Jun 28, 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
21 changes: 21 additions & 0 deletions app/assets/v2/js/cart.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ Vue.component('grants-cart', {
],
'RSK': [
`${static_url}v2/js/grants/cart/rsk_extension.js`
],
'ALGORAND': [
`${static_url}v2/js/tokens.js`,
`${static_url}v2/js/grants/cart/algorand_extension.js`
]
}
};
Expand Down Expand Up @@ -369,6 +373,11 @@ Vue.component('grants-cart', {
isBinanceExtInstalled() {
return window.BinanceChain || false;
},

isAlgorandExtInstalled() {
return window.AlgoSigner || false;
},

isRskExtInstalled() {
const rskHost = 'https://public-node.rsk.co';
const rskClient = new Web3();
Expand Down Expand Up @@ -468,6 +477,9 @@ Vue.component('grants-cart', {
case 'RSK':
vm.chainId = '30';
break;
case 'ALGORAND':
vm.chainId = '1001';
break;
}
},
confirmQRPayment: function(e, grant) {
Expand Down Expand Up @@ -543,6 +555,13 @@ Vue.component('grants-cart', {
let vm = this;

switch (tenant) {
case 'ALGORAND':
if (data) {
contributeWithAlgorandExtension(grant, vm, data);
} else {
initAlgorandConnection(grant, vm);
}
break;
case 'RSK':
contributeWithRskExtension(grant, vm);
break;
Expand Down Expand Up @@ -612,6 +631,8 @@ Vue.component('grants-cart', {
}
}
});

vm.$forceUpdate();
},

/**
Expand Down
1 change: 1 addition & 0 deletions app/assets/v2/js/grants/_detail-component.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Vue.mixin({
'polkadot_payout_address': vm.grant.polkadot_payout_address,
'kusama_payout_address': vm.grant.kusama_payout_address,
'rsk_payout_address': vm.grant.rsk_payout_address,
'algorand_payout_address': vm.grant.algorand_payout_address,
'region': vm.grant.region?.name || undefined,
'has_external_funding': vm.grant.has_external_funding
};
Expand Down
4 changes: 4 additions & 0 deletions app/assets/v2/js/grants/_new.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ Vue.mixin({
vm.$set(vm.errors, 'kusama_payout_address', 'Please enter Kusama address');
} else if (vm.chainId == 'rsk' && !vm.form.rsk_payout_address) {
vm.$set(vm.errors, 'rsk_payout_address', 'Please enter RSK address');
} else if (vm.chainId == 'algorand' && !vm.form.algorand_payout_address) {
vm.$set(vm.errors, 'algorand_payout_address', 'Please enter Algorand address');
}

if (!vm.form.grant_type) {
Expand Down Expand Up @@ -180,6 +182,7 @@ Vue.mixin({
'polkadot_payout_address': form.polkadot_payout_address,
'kusama_payout_address': form.kusama_payout_address,
'rsk_payout_address': form.rsk_payout_address,
'algorand_payout_address': form.algorand_payout_address,
'grant_type': form.grant_type,
'categories[]': form.grant_categories,
'network': form.network,
Expand Down Expand Up @@ -363,6 +366,7 @@ if (document.getElementById('gc-new-grant')) {
polkadot_payout_address: '',
kusama_payout_address: '',
rsk_payout_address: '',
algorand_payout_address: '',
grant_type: '',
grant_categories: [],
network: 'mainnet'
Expand Down
208 changes: 208 additions & 0 deletions app/assets/v2/js/grants/cart/algorand_extension.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@

const initAlgorandConnection = async(grant, vm) => {

// 1. check if AlgoSigner is available
if (!AlgoSigner) {
_alert({ message: 'Please download or enable AlgoSigner extension' }, 'danger');
return;
}

// const NETWORK = 'TestNet';
const NETWORK = 'MainNet';

// step2: get connected accounts
AlgoSigner.connect().then(async() => {
const addresses = await AlgoSigner.accounts({ ledger: NETWORK });

if (addresses.length == 0) {
_alert({ message: 'No wallet addresses detected on AlgoSigner extension' }, 'danger');
return;
}
vm.updatePaymentStatus(grant.grant_id, 'waiting-on-user-input', null, {addresses: addresses});
});

};

const contributeWithAlgorandExtension = async(grant, vm, from_address) => {
const token_name = grant.grant_donation_currency;
const amount = grant.grant_donation_amount;
const to_address = grant.algorand_payout_address;
const token = vm.getTokenByName(token_name);

// const NETWORK = 'TestNet';
const NETWORK = 'MainNet';

try {
AlgoSigner.connect().then(async() => {

// step3: check if enough balance is present
const balance = await AlgoSigner.algod({
ledger: NETWORK,
path: `/v2/accounts/${from_address}`
});

if (
token_name == 'ALGO' &&
Number(balance.amount) <= amount * 10 ** token.decimals
) {
// ALGO token
_alert({ message: `Insufficent balance in address ${from_address}` }, 'danger');
return;
}
// ALGO assets
let is_asset_present = false;

if (balance.assets && balance.assets.length > 0) {
balance.assets.map(asset => {
if (asset['asset-id'] == token.addr)
is_asset_present = true;
});
}

if (!is_asset_present) {
_alert({ message: `Asset ${token_name} is not present in ${from_address}` }, 'danger');
return;
}

let has_enough_asset_balance = false;

balance.assets.map(asset => {
if (asset['asset-id'] == token.addr && asset['amount'] <= amount * 10 ** token.decimals)
has_enough_asset_balance = true;
});

if (has_enough_asset_balance) {
_alert({ message: `Insufficent balance in address ${from_address}` }, 'danger');
return;
}

// step4: set modal to waiting state
vm.updatePaymentStatus(grant.grant_id, 'waiting');

// step5: get txnParams
const txParams = await AlgoSigner.algod({
ledger: NETWORK,
path: '/v2/transactions/params'
});

let txn;
// step5: sign transaction

if (token_name == 'ALGO') {
// ALGO token
txn = {
from: from_address.toUpperCase(),
to: to_address.toUpperCase(),
fee: txParams['fee'],
type: 'pay',
amount: amount * 10 ** token.decimals,
firstRound: txParams['last-round'],
lastRound: txParams['last-round'] + 1000,
genesisID: txParams['genesis-id'],
genesisHash: txParams['genesis-hash'],
note: 'contributing to gitcoin grant'
};
} else {
// ALGO assets
txn = {
from: from_address.toUpperCase(),
to: to_address.toUpperCase(),
assetIndex: Number(token.addr),
note: 'contributing to gitcoin grant',
amount: amount * 10 ** token.decimals,
type: 'axfer',
fee: txParams['min-fee'],
firstRound: txParams['last-round'],
lastRound: txParams['last-round'] + 1000,
genesisID: txParams['genesis-id'],
genesisHash: txParams['genesis-hash']
};
}

AlgoSigner.sign(txn).then(signedTx => {
// step7: broadcast txn
AlgoSigner.send({
ledger: NETWORK,
tx: signedTx.blob
}).then(tx => {
callback(null, from_address, tx.txId);
}).catch((e) => {
console.log(e);
_alert({ message: 'Unable to broadcast transaction. Please try again' }, 'danger');
vm.updatePaymentStatus(grant.grant_id, 'failed');
return;
});

}).catch(e => {
console.log(e);
_alert({ message: 'Unable to sign transaction. Please try again' }, 'danger');
vm.updatePaymentStatus(grant.grant_id, 'failed');
return;
});

}).catch(e => {
console.log(e);
_alert({ message: 'Please allow Gitcoin to connect to AlgoSigner extension' }, 'danger');
vm.updatePaymentStatus(grant.grant_id, 'failed');
return;
});
} catch (e) {
callback(err);
return;
}


function callback(error, from_address, txn) {
if (error) {
vm.updatePaymentStatus(grant.grant_id, 'failed');
_alert({ message: gettext('Unable to contribute to grant due to ' + error) }, 'danger');
chibie marked this conversation as resolved.
Show resolved Hide resolved
console.log(error);
} else {

const payload = {
'contributions': [{
'grant_id': grant.grant_id,
'contributor_address': from_address,
'tx_id': txn,
'token_symbol': grant.grant_donation_currency,
'tenant': 'ALGORAND',
'comment': grant.grant_comments,
'amount_per_period': grant.grant_donation_amount
}]
};

const apiUrlBounty = 'v1/api/contribute';

fetchData(apiUrlBounty, 'POST', JSON.stringify(payload)).then(response => {

if (200 <= response.status && response.status <= 204) {
console.log('success', response);
MauticEvent.createEvent({
'alias': 'products',
'data': [
{
'name': 'product',
'attributes': {
'product': 'grants',
'persona': 'grants-contributor',
'action': 'contribute'
}
}
]
});

vm.updatePaymentStatus(grant.grant_id, 'done', txn);

} else {
vm.updatePaymentStatus(grant.grant_id, 'failed');
_alert('Unable to contribute to grant. Please try again later', 'danger');
console.error(`error: grant contribution failed with status: ${response.status} and message: ${response.message}`);
}
}).catch(function(error) {
vm.updatePaymentStatus(grant.grant_id, 'failed');
_alert('Unable to contribute to grant. Please try again later', 'danger');
console.log(error);
});
}
}
};
3 changes: 3 additions & 0 deletions app/assets/v2/js/grants/funding.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ function tokenOptionsForGrant(grant) {
} else if (grant.tenants && grant.tenants.includes('RSK')) {
tokenDataList = tokenDataList.filter(token => token.chainId === 30);
tokenDefault = 'RBTC';
} else if (grant.tenants && grant.tenants.includes('ALGORAND')) {
tokenDataList = tokenDataList.filter(token => token.chainId === 1001);
tokenDefault = 'ALGO';
} else {
tokenDataList = tokenDataList.filter((token) => token.chainId === 1);
}
Expand Down
9 changes: 4 additions & 5 deletions app/assets/v2/js/pages/bounty_detail/algorand_extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ const payWithAlgorandExtension = async(fulfillment_id, to_address, vm, modal) =>
return;
}

// const connect = await AlgoSigner.connect();
AlgoSigner.connect().then(async() => {
// step2: get connected accounts
const accounts = await AlgoSigner.accounts({ ledger: NETWORK });
Expand Down Expand Up @@ -54,12 +53,12 @@ const payWithAlgorandExtension = async(fulfillment_id, to_address, vm, modal) =>

if (balance.assets && balance.assets.length > 0) {
balance.assets.map(asset => {
if (asset['asset-id'] == asset_index)
if (asset['asset-id'] == asset_index.addr)
is_asset_present = true;
});
}

if (is_asset_present) {
if (!is_asset_present) {
_alert({ message: `Asset ${token_name} is not present in ${from_address}` }, 'danger');
modal.closeModal();
return;
Expand All @@ -68,7 +67,7 @@ const payWithAlgorandExtension = async(fulfillment_id, to_address, vm, modal) =>
let has_enough_asset_balance = false;

balance.assets.map(asset => {
if (asset['asset-id'] == asset_index && asset['amount'] <= amount * 10 ** vm.decimals)
if (asset['asset-id'] == asset_index.addr && asset['amount'] <= amount * 10 ** vm.decimals)
has_enough_asset_balance = true;
});

Expand Down Expand Up @@ -138,7 +137,7 @@ const payWithAlgorandExtension = async(fulfillment_id, to_address, vm, modal) =>

}).catch(e => {
console.log(e);
_alert({ message: 'Unable to sign txn. Please try again' }, 'danger');
_alert({ message: 'Unable to sign transaction. Please try again' }, 'danger');
modal.closeModal();
return;
});
Expand Down
2 changes: 1 addition & 1 deletion app/assets/v2/js/pages/bounty_details2.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ Vue.mixin({
case 'ALGO':
case 'USDTa':
case 'USDCa':
url = `https://algoexplorer.io/tx/${address}`;
url = `https://algoexplorer.io/address/${address}`;
break;

case 'SC':
Expand Down
2 changes: 1 addition & 1 deletion app/grants/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ class GrantAdmin(GeneralAdmin):
'metadata', 'twitter_handle_1', 'twitter_handle_2', 'view_count', 'in_active_clrs',
'last_update', 'funding_info', 'twitter_verified', 'twitter_verified_by', 'twitter_verified_at', 'stats_history',
'zcash_payout_address', 'celo_payout_address','zil_payout_address', 'harmony_payout_address', 'binance_payout_address',
'polkadot_payout_address', 'kusama_payout_address', 'rsk_payout_address', 'emails', 'admin_message', 'has_external_funding'
'polkadot_payout_address', 'kusama_payout_address', 'rsk_payout_address', 'algorand_payout_address', 'emails', 'admin_message', 'has_external_funding'
]
readonly_fields = [
'logo_svg_asset', 'logo_asset',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def handle(self, *args, **options):
# Auto expire pending transactions
timeout_period = timezone.now() - timedelta(minutes=60)

tenants = ['ZCASH', 'ZIL', 'CELO', 'POLKADOT', 'HARMONY', 'BINANCE', 'KUSAMA', 'RSK']
tenants = ['ZCASH', 'ZIL', 'CELO', 'POLKADOT', 'HARMONY', 'BINANCE', 'KUSAMA', 'RSK', 'ALGORAND']

for tenant in tenants:
tenant_pending_contributions = pending_contribution.filter(subscription__tenant=tenant)
Expand Down
Loading