diff --git a/README.md b/README.md index 9bdd05d3aa..814939c4b6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Installation +## Installation Clone the repository: diff --git a/data/networks.json b/data/networks.json index 161c5108b9..7c6cf4fb62 100644 --- a/data/networks.json +++ b/data/networks.json @@ -7,7 +7,7 @@ "api_url": "https://gaia-13006.lunie.io", "bech32_prefix": "cosmos", "testnet": true, - "feature_session": false, + "feature_session": true, "feature_portfolio": true, "feature_validators": true, "feature_proposals": true, diff --git a/lib/cosmosV0-source.js b/lib/cosmosV0-source.js index 3ccb54c2cd..5fd77c870f 100644 --- a/lib/cosmosV0-source.js +++ b/lib/cosmosV0-source.js @@ -3,6 +3,7 @@ const BigNumber = require('bignumber.js') const _ = require('lodash') const { uniqWith, sortBy, reverse } = require('lodash') const { encodeB32, decodeB32, pubkeyToAddress } = require('./tools') +const { UserInputError } = require('apollo-server') class CosmosV0API extends PerBlockCacheDataSource { constructor(network) { @@ -44,7 +45,6 @@ class CosmosV0API extends PerBlockCacheDataSource { const involvedAddresses = transaction.tags.reduce((addresses, tag) => { // temporary in here to identify why this fails if (!tag.value) { - console.log(JSON.stringify(transaction)) return addresses } if (tag.value.startsWith(`cosmos`)) { @@ -58,7 +58,9 @@ class CosmosV0API extends PerBlockCacheDataSource { async getTransactionsByHeight(height) { const txs = await this.get(`txs?tx.height=${height}`) return Array.isArray(txs) - ? txs.map(transaction => this.reducers.transactionReducer(transaction)) + ? txs.map(transaction => + this.reducers.transactionReducer(transaction, this.reducers) + ) : [] } @@ -195,13 +197,18 @@ class CosmosV0API extends PerBlockCacheDataSource { } async getProposalById({ proposalId }) { + const proposal = await this.query(`gov/proposals/${proposalId}`).catch( + () => { + throw new UserInputError( + `There is no proposal in the network with ID '${proposalId}'` + ) + } + ) const [ - proposal, tally, proposer, { bonded_tokens: totalBondedTokens } ] = await Promise.all([ - this.query(`gov/proposals/${proposalId}`), this.query(`/gov/proposals/${proposalId}/tally`), this.query(`gov/proposals/${proposalId}/proposer`).catch(() => { return { proposer: `unknown` } @@ -270,12 +277,21 @@ class CosmosV0API extends PerBlockCacheDataSource { let delegations = (await this.query(`staking/delegators/${address}/delegations`)) || [] - return delegations.map(delegation => - this.reducers.delegationReducer( - delegation, - validatorsDictionary[delegation.validator_address] + return delegations + .filter(delegation => + BigNumber( + this.reducers.calculateTokens( + validatorsDictionary[delegation.validator_address], + delegation.shares + ) + ).isGreaterThanOrEqualTo(1e-6) + ) + .map(delegation => + this.reducers.delegationReducer( + delegation, + validatorsDictionary[delegation.validator_address] + ) ) - ) } async getUndelegationsForDelegatorAddress(address, validatorsDictionary) { @@ -399,7 +415,9 @@ class CosmosV0API extends PerBlockCacheDataSource { const duplicateFreeTxs = uniqWith(txs, (a, b) => a.txhash === b.txhash) const sortedTxs = sortBy(duplicateFreeTxs, ['timestamp']) const reversedTxs = reverse(sortedTxs) - return reversedTxs.map(this.reducers.transactionReducer) + return reversedTxs.map(tx => + this.reducers.transactionReducer(tx, this.reducers) + ) } async awaitUp() { diff --git a/lib/cosmosV2-source.js b/lib/cosmosV2-source.js index ff36127dae..7be37416fb 100644 --- a/lib/cosmosV2-source.js +++ b/lib/cosmosV2-source.js @@ -1,6 +1,4 @@ const CosmosV0API = require('./cosmosV0-source') - -const { transactionReducer } = require('./reducers/cosmosV0-reducers') const { uniqWith, sortBy, reverse } = require('lodash') class CosmosV2API extends CosmosV0API { @@ -33,7 +31,9 @@ class CosmosV2API extends CosmosV0API { const dupFreeTxs = uniqWith(txs, (a, b) => a.txhash === b.txhash) const sortedTxs = sortBy(dupFreeTxs, ['timestamp']) const reversedTxs = reverse(sortedTxs) - return reversedTxs.map(transactionReducer) + return reversedTxs.map(tx => + this.reducers.transactionReducer(tx, this.reducers) + ) } extractInvolvedAddresses(transaction) { @@ -67,7 +67,9 @@ class CosmosV2API extends CosmosV0API { async getTransactionsByHeight(height) { const { txs } = await this.get(`txs?tx.height=${height}`) return Array.isArray(txs) - ? txs.map(transaction => this.reducers.transactionReducer(transaction)) + ? txs.map(transaction => + this.reducers.transactionReducer(transaction, this.reducers) + ) : [] } diff --git a/lib/helpers/tendermint.js b/lib/helpers/tendermint.js index 7dba399538..79086ae9db 100644 --- a/lib/helpers/tendermint.js +++ b/lib/helpers/tendermint.js @@ -60,7 +60,7 @@ function tendermintConnect() { const subscription = this.subscriptions.find(({ id }) => eventId === id) if (subscription) { - if (isSubscriptionEvent) { + if (isSubscriptionEvent && result) { subscription.callback(result.data.value) return } diff --git a/lib/reducers/cosmosV0-reducers.js b/lib/reducers/cosmosV0-reducers.js index 88570ed529..f420ac1d7c 100644 --- a/lib/reducers/cosmosV0-reducers.js +++ b/lib/reducers/cosmosV0-reducers.js @@ -98,8 +98,8 @@ function getTotalVotePercentage(proposal, totalBondedTokens, totalVoted) { if (proposalFinalized(proposal)) return -1 if (BigNumber(totalVoted).eq(0)) return 0 if (!totalBondedTokens) return -1 - return BigNumber(totalBondedTokens) - .div(atoms(totalVoted)) + return BigNumber(totalVoted) + .div(atoms(totalBondedTokens)) .toNumber() } @@ -346,7 +346,17 @@ function getGroupByType(transactionType) { return transactionGroup[transactionType] || 'unknown' } -function transactionReducer(transaction) { +function undelegationEndTimeReducer(transaction) { + if (transaction.tags) { + if (transaction.tags.find(tx => tx.key === `end-time`)) { + return transaction.tags.filter(tx => tx.key === `end-time`)[0].value + } + } else { + return null + } +} + +function transactionReducer(transaction, reducers) { try { let fee = coinReducer(false) if (Array.isArray(transaction.tx.value.fee.amount)) { @@ -371,7 +381,8 @@ function transactionReducer(transaction) { fee, signature: transaction.tx.value.signatures[0].signature, value: JSON.stringify(transaction.tx.value.msg[0].value), - raw: transaction + raw: transaction, + undelegationEndTime: reducers.undelegationEndTimeReducer(transaction) } return result @@ -399,6 +410,8 @@ module.exports = { rewardReducer, overviewReducer, accountInfoReducer, + calculateTokens, + undelegationEndTimeReducer, atoms, proposalBeginTime, @@ -406,5 +419,6 @@ module.exports = { getDeposit, getTotalVotePercentage, getValidatorStatus, - expectedRewardsPerToken + expectedRewardsPerToken, + getGroupByType } diff --git a/lib/reducers/cosmosV2-reducers.js b/lib/reducers/cosmosV2-reducers.js index cd455395a0..c6933bde56 100644 --- a/lib/reducers/cosmosV2-reducers.js +++ b/lib/reducers/cosmosV2-reducers.js @@ -92,9 +92,22 @@ function validatorReducer( } } +function undelegationEndTimeReducer(transaction) { + if ( + transaction.events[1].attributes.find(tx => tx.key === `completion_time`) + ) { + return transaction.events[1].attributes.filter( + tx => tx.key === `completion_time` + )[0].value + } else { + return null + } +} + module.exports = { ...cosmosV0Reducers, proposalReducer, delegationReducer, - validatorReducer + validatorReducer, + undelegationEndTimeReducer } diff --git a/lib/reducers/terraV3-reducers.js b/lib/reducers/terraV3-reducers.js index f2df073e83..c34d77dddc 100644 --- a/lib/reducers/terraV3-reducers.js +++ b/lib/reducers/terraV3-reducers.js @@ -1,5 +1,24 @@ -const cosmosV0Reducers = require('./cosmosV0-reducers') +const cosmosV2Reducers = require('./cosmosV2-reducers') + +// Terra has a slightly different structure and needs its own undelegationEndTimeReducer +function undelegationEndTimeReducer(transaction) { + if (transaction.logs[0].events.length > 2) { + let endTime + const attributes = Object.values(transaction.logs[0].events).map( + event => event.attributes + ) + attributes.forEach(attribute => + attribute.map(tx => { + if (tx.key === `completion_time`) { + endTime = tx.value + } + }) + ) + return endTime ? endTime : null + } +} module.exports = { - ...cosmosV0Reducers + ...cosmosV2Reducers, + undelegationEndTimeReducer } diff --git a/lib/schema.js b/lib/schema.js index c0434cf225..71f3854297 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -139,6 +139,7 @@ const typeDefs = gql` signature: String! value: String! amount: String + undelegationEndTime: String } type GovernanceParameters { diff --git a/lib/terraV3-source.js b/lib/terraV3-source.js index 3d4e371290..c389c4f910 100644 --- a/lib/terraV3-source.js +++ b/lib/terraV3-source.js @@ -57,38 +57,6 @@ class TerraV3API extends CosmosV2API { ) ) } - - // async getTransactions(address) { - // const pagination = `&limit=${1000000000}` - - // const txs = await Promise.all([ - // this.get(`/txs?sender=${address}${pagination}`), - // this.get(`/txs?recipient=${address}${pagination}`), - // this.get(`/txs?action=submit_proposal&proposer=${address}${pagination}`), - // this.get(`/txs?action=deposit&depositor=${address}${pagination}`), - // this.get(`/txs?action=vote&voter=${address}${pagination}`), - // // this.get(`/txs?action=create_validator&destination-validator=${valAddress}`), // TODO - // // this.get(`/txs?action=edit_validator&destination-validator=${valAddress}`), // TODO - // this.get(`/txs?action=delegate&delegator=${address}${pagination}`), - // this.get( - // `/txs?action=begin_redelegate&delegator=${address}${pagination}` - // ), - // this.get(`/txs?action=begin_unbonding&delegator=${address}${pagination}`), - // // this.get(`/txs?action=unjail&source-validator=${address}`), // TODO - // // this.get(`/txs?action=set_withdraw_address&delegator=${address}`), // other - // this.get( - // `/txs?action=withdraw_delegator_reward&delegator=${address}${pagination}` - // ), - // this.get( - // `/txs?action=withdraw_validator_rewards_all&source-validator=${address}${pagination}` - // ) - // ]).then(transactionGroups => [].concat(...transactionGroups)) - - // const duplicateFreeTxs = uniqWith(txs, (a, b) => a.txhash === b.txhash) - // const sortedTxs = sortBy(duplicateFreeTxs, ['timestamp']) - // const reversedTxs = reverse(sortedTxs) - // return reversedTxs.map(this.reducers.transactionReducer) - // } } module.exports = TerraV3API