Skip to content

Commit

Permalink
feat: algorand grants integration
Browse files Browse the repository at this point in the history
  • Loading branch information
thelostone-mc committed Jun 21, 2021
1 parent fdec4c3 commit c4e1aa8
Show file tree
Hide file tree
Showing 19 changed files with 527 additions and 10 deletions.
14 changes: 14 additions & 0 deletions app/assets/v2/js/cart.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ Vue.component('grants-cart', {
],
'RSK': [
`${static_url}v2/js/grants/cart/rsk_extension.js`
],
'ALGORAND': [
`${static_url}v2/js/grants/cart/algorand_extension.js`
]
}
};
Expand Down Expand Up @@ -369,6 +372,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 +476,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 +554,9 @@ Vue.component('grants-cart', {
let vm = this;

switch (tenant) {
case 'ALGORAND':
contributeWithAlgorandExtension(grant, vm);
break;
case 'RSK':
contributeWithRskExtension(grant, vm);
break;
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
210 changes: 210 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,210 @@
const contributeWithAlgorandExtension = async(grant, vm, modal) => {
const token_name = grant.grant_donation_currency;
const amount = grant.grant_donation_amount;
const to_address = grant.rsk_payout_address;
const token = vm.getTokenByName(token_name);

// TODO: FIGURE OUT from_address

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

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

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

let is_account_present = false;

accounts.map(account=> {
if (account.address == from_address)
is_account_present = true;
});

if (!is_account_present) {
_alert({ message: `Unable to access address ${from_address} in wallet` }, 'danger');
modal.closeModal();
return;
}

// 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');
modal.closeModal();
return;

}
// ALGO assets
let is_asset_present = false;

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

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

let has_enough_asset_balance = false;

balance.assets.map(asset => {
if (asset['asset-id'] == asset_index && 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');
modal.closeModal();
return;
}


// step4: 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,
to: to_address,
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,
to: to_address,
assetIndex: Number(asset_index.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 => {

// step6: 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 txn. Please try again' }, 'danger');
modal.closeModal();
return;
});

}).catch(e => {
console.log(e);
_alert({ message: 'Unable to sign txn. Please try again' }, 'danger');
modal.closeModal();
return;
});

}).catch(e => {
console.log(e);
_alert({ message: 'Please allow Gitcoin to connect to AlgoSigner extension' }, 'danger');
modal.closeModal();
return;
});
} catch (e) {
modal.closeModal();
_alert({ message: 'Unable to make contribution to grant. Please try again later.' }, 'error');
console.log(error);
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');
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) {
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 make 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 make 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
1 change: 0 additions & 1 deletion 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
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
17 changes: 15 additions & 2 deletions app/grants/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,13 @@ class Meta:
blank=True,
help_text=_('The rsk wallet address where subscription funds will be sent.'),
)
algorand_payout_address = models.CharField(
max_length=255,
default='0x0',
null=True,
blank=True,
help_text=_('The algorand wallet address where subscription funds will be sent.'),
)
# TODO-GRANTS: remove
contract_owner_address = models.CharField(
max_length=255,
Expand Down Expand Up @@ -650,6 +657,8 @@ def tenants(self):
tenants.append('BINANCE')
if self.rsk_payout_address and self.rsk_payout_address != '0x0':
tenants.append('RSK')
if self.algorand_payout_address and self.algorand_payout_address != '0x0':
tenants.append('ALGORAND')

return tenants

Expand Down Expand Up @@ -892,6 +901,7 @@ def cart_payload(self, build_absolute_uri, user=None):
'kusama_payout_address': self.kusama_payout_address,
'harmony_payout_address': self.harmony_payout_address,
'rsk_payout_address': self.rsk_payout_address,
'algorand_payout_address': self.algorand_payout_address,
'is_on_team': is_grant_team_member(self, user.profile) if user and user.is_authenticated else False,
}

Expand Down Expand Up @@ -950,6 +960,7 @@ def repr(self, user, build_absolute_uri):
'harmony_payout_address': self.harmony_payout_address,
'binance_payout_address': self.binance_payout_address,
'rsk_payout_address': self.rsk_payout_address,
'algorand_payout_address': self.algorand_payout_address,
'token_address': self.token_address,
'image_css': self.image_css,
'verified': self.twitter_verified,
Expand Down Expand Up @@ -1007,7 +1018,8 @@ class Subscription(SuperModel):
('KUSAMA', 'KUSAMA'),
('HARMONY', 'HARMONY'),
('BINANCE', 'BINANCE'),
('RSK', 'RSK')
('RSK', 'RSK'),
('ALGORAND', 'ALGORAND')
]

active = models.BooleanField(default=True, db_index=True, help_text=_('Whether or not the Subscription is active.'))
Expand Down Expand Up @@ -1679,7 +1691,8 @@ class Contribution(SuperModel):
('polkadot_std', 'polkadot_std'),
('harmony_std', 'harmony_std'),
('binance_std', 'binance_std'),
('rsk_std', 'rsk_std')
('rsk_std', 'rsk_std'),
('algorand_std', 'algorand_std')
]

success = models.BooleanField(default=True, help_text=_('Whether or not success.'))
Expand Down
Loading

0 comments on commit c4e1aa8

Please sign in to comment.