From d2d009c5a2ea9949f967d86ca7f3205612347140 Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Sun, 20 Oct 2019 19:13:44 -0400 Subject: [PATCH 01/19] validators proposals working --- data/networks.js | 7 +- index.js | 14 +- lib/gaia-source.js | 179 ------------------ lib/{cosmos-source.js => gaiav0-source.js} | 148 ++++++++------- lib/gaiav2-source.js | 41 ++++ lib/reducers/gaia-reducers.js | 165 ---------------- ...{cosmos-reducers.js => gaiav0-reducers.js} | 13 +- lib/reducers/gaiav2-reducers.js | 78 ++++++++ lib/resolvers.js | 4 +- 9 files changed, 226 insertions(+), 423 deletions(-) delete mode 100644 lib/gaia-source.js rename lib/{cosmos-source.js => gaiav0-source.js} (74%) create mode 100644 lib/gaiav2-source.js delete mode 100644 lib/reducers/gaia-reducers.js rename lib/reducers/{cosmos-reducers.js => gaiav0-reducers.js} (97%) create mode 100644 lib/reducers/gaiav2-reducers.js diff --git a/data/networks.js b/data/networks.js index 0ba414105f..f496ee78f8 100644 --- a/data/networks.js +++ b/data/networks.js @@ -8,8 +8,11 @@ let networks = { cosmoshub: { api_url: 'https://lcd.nylira.net', rpc_url: 'wss://rpc.nylira.net/websocket' - }, - testnet: { + } +} + +if (config.enableTestnet) { + networks.testnet = { api_url: 'http://localhost:9070', rpc_url: 'ws://localhost:26657/websocket' } diff --git a/index.js b/index.js index 62a31fac00..59c486f48a 100644 --- a/index.js +++ b/index.js @@ -1,23 +1,23 @@ const { ApolloServer } = require('apollo-server') const typeDefs = require('./lib/schema') const resolvers = require('./lib/resolvers') -const CosmosAPI = require('./lib/cosmos-source') -const GaiaAPI = require('./lib/gaia-source') +const CosmosHubAPI = require('./lib/gaiav0-source') +const GaiaTestnetAPI = require('./lib/gaiav2-source') const LunieDBAPI = require('./lib/luniedb-source') const { networks } = require('./data/networks') const config = require('./config') -const cosmosApi = new CosmosAPI(networks['cosmoshub']) -const gaiaApi = new GaiaAPI(networks['gaia-testnet']) +const cosmosHubApi = new CosmosHubAPI(networks['cosmoshub']) +const gaiaTestnetApi = new GaiaTestnetAPI(networks['gaia-testnet']) const lunieDBAPI = new LunieDBAPI() const dataSources = { - CosmosAPI: cosmosApi, - GaiaAPI: gaiaApi, + CosmosAPI: cosmosHubApi, + GaiaAPI: gaiaTestnetApi, LunieDBAPI: lunieDBAPI } if (config.enableTestnet) { - const testnetAPI = new CosmosAPI(networks['testnet']) + const testnetAPI = new CosmosHubAPI(networks['testnet']) dataSources.TestnetAPI = testnetAPI } diff --git a/lib/gaia-source.js b/lib/gaia-source.js deleted file mode 100644 index 034e2aa760..0000000000 --- a/lib/gaia-source.js +++ /dev/null @@ -1,179 +0,0 @@ -const { RESTDataSource } = require('apollo-datasource-rest') -const chainpubsub = require('./chain-pubsub') -const BigNumber = require('bignumber.js') -const _ = require('lodash') -const { - proposalReducer, - validatorReducer, - blockReducer, - balanceReducer, - delegationReducer, - undelegationReducer -} = require('./reducers/gaia-reducers') -const { expectedReturns, calculateTokens, pubkeyToAddress } = require('./tools') - -class GaiaAPI extends RESTDataSource { - constructor(network) { - super() - this.baseURL = network.api_url - this.initialize({}) - this.subscribeToBlocks(network) - } - - // subscribe to blocks via Tendermint - async subscribeToBlocks(network) { - this.wsClient = await chainpubsub.client(network.rpc_url) - this.wsClient.subscribe({ query: "tm.event='NewBlock'" }, async event => { - const block = await this.getBlockByHeight({ - blockHeight: event.block.header.height - }) - chainpubsub.publishBlockAdded(network.id, block) - }) - } - - async getAllProposals() { - const response = await this.get('gov/proposals') - const { - result: { bonded_tokens: totalBondedTokens } - } = await this.get('/staking/pool') - if (!Array.isArray(response.result)) return [] - const proposals = await Promise.all( - response.result.map(async proposal => { - return proposalReducer(proposal, totalBondedTokens) - }) - ) - return _.orderBy(proposals, 'id', 'desc') - } - - async getAllValidatorSets() { - const response = await this.get(`validatorsets/latest`) - return response.result - } - - async getValidatorSigningInfos() { - const response = await this.get(`slashing/signing_infos`) - return response.result - } - - async getAnnualProvision() { - const response = await this.get(`minting/annual-provisions`) - return response.result - } - - async getAllValidators() { - const [validators, annualProvision, validatorSet] = await Promise.all([ - Promise.all([ - this.get(`staking/validators?status=unbonding`), - this.get(`staking/validators?status=bonded`), - this.get(`staking/validators?status=unbonded`) - ]) - .then(responses => responses.map(({ result }) => result)) - .then(validatorGroups => [].concat(...validatorGroups)), - this.getAnnualProvision(), - this.getAllValidatorSets() - ]) - - // create a dictionary to reduce array lookups - const consensusValidators = _.keyBy(validatorSet.validators, 'address') - const totalVotingPower = validatorSet.validators.reduce( - (sum, { voting_power }) => sum.plus(voting_power), - BigNumber(0) - ) - - // query for signing info - const signingInfos = _.keyBy( - await this.getValidatorSigningInfos(), - 'address' - ) - - validators.forEach(validator => { - const consensusAddress = pubkeyToAddress(validator.consensus_pubkey) - validator.voting_power = consensusValidators[consensusAddress] - ? BigNumber(consensusValidators[consensusAddress].voting_power).div( - totalVotingPower - ) - : 0 - validator.expected_returns = expectedReturns(validator, annualProvision) - validator.signing_info = signingInfos[consensusAddress] - }) - - return validators.map(validator => validatorReducer(validator)) - } - - async getValidatorByAddress(operatorAddress) { - const response = await this.get(`staking/validators/${operatorAddress}`) - return validatorReducer(response.result) - } - - async getProposalById({ proposalId }) { - const response = await this.get(`gov/proposals/${proposalId}`) - return proposalReducer(response.result) - } - - async getGovernanceParameters() { - const { result: depositParameters } = await this.get( - `gov/parameters/deposit` - ) - const { result: tallyingParamers } = await this.get( - `gov/parameters/tallying` - ) - return { - votingThreshold: tallyingParamers.threshold, - vetoThreshold: tallyingParamers.veto, - // for now assuming one deposit denom - depositDenom: 'STAKE', - depositThreshold: BigNumber(depositParameters.min_deposit[0].amount).div( - 1000000 - ) - } - } - - async getDelegatorVote({ proposalId, address }) { - const response = await this.get(`gov/proposals/${proposalId}/votes`) - const votes = response.result || [] - const vote = votes.find(({ voter }) => voter === address) || {} - return { - option: vote.option - } - } - - async getBlockByHeight({ blockHeight }) { - let response - if (blockHeight) { - response = await this.get(`blocks/${blockHeight}`) - } else { - response = await this.get(`blocks/latest`) - } - return blockReducer(response) - } - - async getBalanceFromAddress(address) { - const response = await this.get(`bank/balances/${address}`) - return response.result.map(balanceReducer) - } - - async getDelegationsForDelegatorAddress(address) { - const response = await this.get(`staking/delegators/${address}/delegations`) - return Array.isArray(response.result) - ? response.result.map(proposal => delegationReducer(proposal)) - : [] - } - - async getUndelegationsForDelegatorAddress(address) { - const response = await this.get( - `staking/delegators/${address}/unbonding_delegations` - ) - return Array.isArray(response) - ? response.map(undelegation => undelegationReducer(undelegation)) - : [] - } - - async getDelegationForValidator(delegatorAddress, validatorAddress) { - const response = await this.get( - `staking/delegators/${delegatorAddress}/delegations/${validatorAddress}` - ) - return delegationReducer(response.result) - } -} - -module.exports = GaiaAPI diff --git a/lib/cosmos-source.js b/lib/gaiav0-source.js similarity index 74% rename from lib/cosmos-source.js rename to lib/gaiav0-source.js index b6782410ba..0559b6a7e9 100644 --- a/lib/cosmos-source.js +++ b/lib/gaiav0-source.js @@ -2,17 +2,6 @@ const { RESTDataSource } = require('apollo-datasource-rest') const BigNumber = require('bignumber.js') const _ = require('lodash') const chainpubsub = require('./chain-pubsub') -const { - proposalReducer, - validatorReducer, - blockReducer, - delegationReducer, - coinReducer, - undelegationReducer, - overviewReducer, - transactionReducer, - rewardReducer -} = require('./reducers/cosmos-reducers') const { uniqWith, sortBy, reverse } = require('lodash') const { encodeB32, @@ -26,6 +15,8 @@ class CosmosAPI extends RESTDataSource { super() this.baseURL = network.api_url this.initialize({}) + + this.setReducers() this.subscribeToBlocks(network) this.loadStaticData() @@ -33,9 +24,17 @@ class CosmosAPI extends RESTDataSource { this.getAllValidators() } + setReducers() { + this.reducers = require('./reducers/gaiav0-reducers') + } + + async query(url) { + return this.get(url) + } + async loadStaticData() { - this.stakingDenom = (await this.get('/staking/parameters')).bond_denom - // this.depositDenom = (await this.get("/gov/parameters/deposit")).min_deposit[0].denom + this.stakingDenom = (await this.query('/staking/parameters')).bond_denom + // this.depositDenom = (await this.query("/gov/parameters/deposit")).min_deposit[0].denom } // subscribe to blocks via Tendermint @@ -46,29 +45,35 @@ class CosmosAPI extends RESTDataSource { blockHeight: event.block.header.height }) chainpubsub.publishBlockAdded(network.id, block) - const txs = await await this.get( - `txs?tx.height=${event.block.header.height}` - ) - txs.forEach(tx => { - tx.tags.forEach(tag => { - if (tag.value.startsWith(`cosmos`)) { - const fmtTx = transactionReducer(tx) - chainpubsub.publishUserTransactionAdded( - network.id, - tag.value, - fmtTx - ) - } - }) + this.reactToNewTransactions(network, event.block.header.height) + }) + } + + async reactToNewTransactions(network, height) { + const txs = await this.getTransactionsByHeight(height) + txs.forEach(tx => { + this.extractInvolvedAddresses(tx.raw).forEach(address => { + chainpubsub.publishUserTransactionAdded(network.id, address, tx) }) }) } + extractInvolvedAddresses(transaction) { + const involvedAddresses = transaction.tags.reduce((addresses, tag) => { + if (tag.value.startsWith(`cosmos`)) { + addresses.push(tag.value) + } + return addresses + }, []) + return involvedAddresses + } + async getTransactionsByHeight(height) { const response = await this.get(`txs?tx.height=${height}`) - console.log() return Array.isArray(response) - ? response.map(transaction => transactionReducer(transaction)) + ? response.map(transaction => + this.reducers.transactionReducer(transaction) + ) : [] } @@ -92,7 +97,7 @@ class CosmosAPI extends RESTDataSource { console.log(`Ignore Validator ${validatorConsensusPubKey}`) throw Error() } - const response = await this.get( + const response = await this.query( `slashing/validators/${validatorConsensusPubKey}/signing_info`, { cacheOptions: { ttl: 60 } } ) @@ -110,16 +115,16 @@ class CosmosAPI extends RESTDataSource { } async getAllValidatorSets() { - const response = await this.get(`validatorsets/latest`) + const response = await this.query(`validatorsets/latest`) return response } async getAllValidators() { let [validators, annualProvision, validatorSet] = await Promise.all([ Promise.all([ - this.get(`staking/validators?status=unbonding`), - this.get(`staking/validators?status=bonded`), - this.get(`staking/validators?status=unbonded`) + this.query(`staking/validators?status=unbonding`), + this.query(`staking/validators?status=bonded`), + this.query(`staking/validators?status=unbonded`) ]).then(validatorGroups => [].concat(...validatorGroups)), this.getAnnualProvision(), this.getAllValidatorSets() @@ -149,7 +154,9 @@ class CosmosAPI extends RESTDataSource { validator.signing_info = signingInfos[consensusAddress] }) - return validators.map(validator => validatorReducer(validator)) + return validators.map(validator => + this.reducers.validatorReducer(validator) + ) } async getValidatorByAddress(wantedOperatorAddress) { @@ -161,7 +168,7 @@ class CosmosAPI extends RESTDataSource { const [validators, selfDelegation] = await Promise.all([ this.getAllValidators(), - this.get( + this.query( `staking/delegators/${delegatorAddressFromOperator}/delegations/${wantedOperatorAddress}` ) ]) @@ -169,32 +176,37 @@ class CosmosAPI extends RESTDataSource { ({ operatorAddress }) => operatorAddress === wantedOperatorAddress ) - validator.selfStake = delegationReducer(selfDelegation, validator).amount + validator.selfStake = this.reducers.delegationReducer( + selfDelegation, + validator + ).amount return validator } async getAllProposals() { - const response = await this.get('gov/proposals') - const { bonded_tokens: totalBondedTokens } = await this.get('/staking/pool') - if (!Array.isArray(response)) return [] - const proposals = await Promise.all( - response.map(async proposal => { - return proposalReducer(proposal, totalBondedTokens) - }) + const response = await this.query('gov/proposals') + const { bonded_tokens: totalBondedTokens } = await this.query( + '/staking/pool' ) + if (!Array.isArray(response)) return [] + const proposals = response.map(async proposal => { + return this.reducers.proposalReducer(proposal, totalBondedTokens) + }) return _.orderBy(proposals, 'id', 'desc') } async getProposalById({ proposalId }) { - const response = await this.get(`gov/proposals/${proposalId}`) - const { bonded_tokens: totalBondedTokens } = await this.get('/staking/pool') - return proposalReducer(response, totalBondedTokens) + const response = await this.query(`gov/proposals/${proposalId}`) + const { bonded_tokens: totalBondedTokens } = await this.query( + '/staking/pool' + ) + return this.reducers.proposalReducer(response, totalBondedTokens) } async getGovernanceParameters() { - const depositParameters = await this.get(`gov/parameters/deposit`) - const tallyingParamers = await this.get(`gov/parameters/tallying`) + const depositParameters = await this.query(`gov/parameters/deposit`) + const tallyingParamers = await this.query(`gov/parameters/tallying`) return { votingThreshold: tallyingParamers.threshold, vetoThreshold: tallyingParamers.veto, @@ -207,7 +219,7 @@ class CosmosAPI extends RESTDataSource { } async getDelegatorVote({ proposalId, address }) { - const response = await this.get(`gov/proposals/${proposalId}/votes`) + const response = await this.query(`gov/proposals/${proposalId}/votes`) const votes = response || [] const vote = votes.find(({ voter }) => voter === address) || {} return { @@ -222,22 +234,22 @@ class CosmosAPI extends RESTDataSource { } else { response = await this.get(`blocks/latest`) } - return blockReducer(response) + return this.reducers.blockReducer(response) } async getBalanceFromAddress(address) { - const response = await this.get(`bank/balances/${address}`) - return response.map(coinReducer) + const response = await this.query(`bank/balances/${address}`) + return response.map(this.reducers.coinReducer) } async getDelegationsForDelegatorAddress(address) { let delegations = - (await this.get(`staking/delegators/${address}/delegations`)) || [] + (await this.query(`staking/delegators/${address}/delegations`)) || [] const validators = await this.getAllValidators() const validatorsDictionary = _.keyBy(validators, 'operatorAddress') return delegations.map(delegation => - delegationReducer( + this.reducers.delegationReducer( delegation, validatorsDictionary[delegation.validator_address] ) @@ -246,8 +258,9 @@ class CosmosAPI extends RESTDataSource { async getUndelegationsForDelegatorAddress(address) { let undelegations = - (await this.get(`staking/delegators/${address}/unbonding_delegations`)) || - [] + (await this.query( + `staking/delegators/${address}/unbonding_delegations` + )) || [] const validators = await this.getAllValidators() const validatorsDictionary = _.keyBy(validators, 'operatorAddress') @@ -268,7 +281,7 @@ class CosmosAPI extends RESTDataSource { [] ) return flattenedUndelegations.map(undelegation => - undelegationReducer( + this.reducers.undelegationReducer( undelegation, validatorsDictionary[undelegation.validator_address] ) @@ -277,7 +290,7 @@ class CosmosAPI extends RESTDataSource { async getDelegationForValidator(delegatorAddress, operatorAddress) { const [delegation, validator] = await Promise.all([ - this.get( + this.query( `staking/delegators/${delegatorAddress}/delegations/${operatorAddress}` ).catch(() => ({ validator_address: operatorAddress, @@ -286,11 +299,11 @@ class CosmosAPI extends RESTDataSource { })), this.getValidatorByAddress(operatorAddress) ]) - return delegationReducer(delegation, validator) + return this.reducers.delegationReducer(delegation, validator) } async getAnnualProvision() { - const response = await this.get(`minting/annual-provisions`) + const response = await this.query(`minting/annual-provisions`) return response } @@ -303,13 +316,13 @@ class CosmosAPI extends RESTDataSource { const rewards = await Promise.all( delegations.map(async ({ validatorAddress, validator }) => ({ validator, - rewards: await this.get( + rewards: await this.query( `distribution/delegators/${delegatorAddress}/rewards/${validatorAddress}` ) })) ) return rewards.map(({ rewards, validator }) => - rewardReducer(rewards[0], validator) + this.reducers.rewardReducer(rewards[0], validator) ) } @@ -319,7 +332,12 @@ class CosmosAPI extends RESTDataSource { this.getDelegationsForDelegatorAddress(delegatorAddress) ]) const rewards = await this.getRewards(delegatorAddress, delegations) - return overviewReducer(balances, delegations, rewards, this.stakingDenom) + return this.reducers.overviewReducer( + balances, + delegations, + rewards, + this.stakingDenom + ) } async getTransactions(address) { @@ -347,7 +365,7 @@ class CosmosAPI extends RESTDataSource { 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(this.reducers.transactionReducer) } } diff --git a/lib/gaiav2-source.js b/lib/gaiav2-source.js new file mode 100644 index 0000000000..15455a1a94 --- /dev/null +++ b/lib/gaiav2-source.js @@ -0,0 +1,41 @@ +const GaiaV0API = require('./gaiav0-source') + +const { transactionReducer } = require('./reducers/gaiav0-reducers') +const { uniqWith, sortBy, reverse } = require('lodash') + +class GaiaV2API extends GaiaV0API { + setReducers() { + this.reducers = require('./reducers/gaiav2-reducers') + } + + async query(url) { + const response = await this.get(url) + return response.result + } + + async getValidatorSigningInfos() { + const signingInfos = await this.query(`slashing/signing_infos`) + return signingInfos + } + + async getTransactions(address) { + const pagination = `&page=${1000000000}` + + const txs = await Promise.all([ + this.get(`/txs?message.sender=${address}${pagination}`), + this.get(`/txs?message.recipient=${address}${pagination}`) + ]).then(([senderTxs, recipientTxs]) => [].concat(senderTxs, recipientTxs)) + + const dupFreeTxs = uniqWith(txs, (a, b) => a.txhash === b.txhash) + const sortedTxs = sortBy(dupFreeTxs, ['timestamp']) + const reversedTxs = reverse(sortedTxs) + return reversedTxs.map(transactionReducer) + } + + extractInvolvedAddresses(transaction) { + console.log('TODO extractInvolvedAddresses', transaction) + return [] + } +} + +module.exports = GaiaV2API diff --git a/lib/reducers/gaia-reducers.js b/lib/reducers/gaia-reducers.js deleted file mode 100644 index 3ac7c3221e..0000000000 --- a/lib/reducers/gaia-reducers.js +++ /dev/null @@ -1,165 +0,0 @@ -const BigNumber = require('bignumber.js') - -const NETWORK_ID = 'gaia-testnet' - -function proposalBeginTime(proposal) { - switch (proposal.proposal_status.toLowerCase()) { - case 'depositperiod': - return proposal.submit_time - case 'votingperiod': - return proposal.voting_start_time - case 'passed': - case 'rejected': - return proposal.voting_end_time - } -} - -function proposalEndTime(proposal) { - switch (proposal.proposal_status.toLowerCase()) { - case 'depositperiod': - return proposal.voting_start_time - case 'votingperiod': - // the end time lives in the past already if the proposal is finalized - case 'passed': - case 'rejected': - return proposal.voting_end_time - } -} - -function atoms(nanoAtoms) { - return BigNumber(nanoAtoms).div(1000000) -} - -// reduce deposits to one number and filter by required denom -function getDeposit(proposal, bondDenom) { - return atoms( - proposal.total_deposit.reduce( - (sum, cur) => sum.plus(cur.denom === bondDenom ? cur.amount : 0), - BigNumber(0) - ) - ) -} - -function getTotalVotedPercentage(proposal, totalBondedTokens, totalVoted) { - // for passed proposals we can't calculate the total voted percentage, as we don't know the totalBondedTokens in the past - if (['Passed', 'Rejected'].indexOf(proposal.proposal_status) !== -1) return -1 - if (totalVoted.eq(0)) return 0 - if (!totalBondedTokens) return -1 - return BigNumber(totalBondedTokens) - .div(atoms(totalVoted)) - .toNumber() -} - -function tallyReducer(proposal, totalBondedTokens) { - const totalVoted = atoms( - BigNumber(proposal.final_tally_result.yes) - .plus(proposal.final_tally_result.no) - .plus(proposal.final_tally_result.abstain) - .plus(proposal.final_tally_result.no_with_veto) - ) - - return { - yes: atoms(proposal.final_tally_result.yes), - no: atoms(proposal.final_tally_result.no), - abstain: atoms(proposal.final_tally_result.abstain), - veto: atoms(proposal.final_tally_result.no_with_veto), - total: totalVoted, - totalVotedPercentage: getTotalVotedPercentage( - proposal, - totalBondedTokens, - totalVoted - ) - } -} - -function proposalReducer(proposal, totalBondedTokens) { - return { - networkId: NETWORK_ID, - id: Number(proposal.id), - type: proposal.content.type, - title: proposal.content.value.title, - description: proposal.content.value.description, - creationTime: proposal.submit_time, - status: proposal.proposal_status, - statusBeginTime: proposalBeginTime(proposal), - statusEndTime: proposalEndTime(proposal), - tally: tallyReducer(proposal, totalBondedTokens), - deposit: getDeposit(proposal, 'stake') - } -} - -function validatorReducer(validator) { - return { - networkId: NETWORK_ID, - operator_address: validator.operator_address, - consensus_pubkey: validator.consensus_pubkey, - jailed: validator.jailed, - status: validator.status, - tokens: validator.tokens, - moniker: validator.description.moniker, - votingPower: validator.voting_power, - startHeight: validator.signing_info - ? validator.signing_info.start_height - : undefined, - identity: validator.description.identity, - website: validator.description.website, - details: validator.description.details, - bond_height: validator.bond_height, - bond_intra_tx_counter: validator.bond_intra_tx_counter, - unbonding_height: validator.unbonding_height, - unbonding_time: validator.unbonding_time, - rate: validator.rate, - max_rate: validator.max_rate, - max_change_rate: validator.max_change_rate, - update_time: validator.update_time, - delegatorShares: validator.delegator_shares, // needed to calculate delegation token amounts from shares - expectedReturns: validator.expected_returns - } -} - -function blockReducer(block) { - return { - networkId: NETWORK_ID, - height: block.block_meta.header.height, - chainId: block.block_meta.header.chain_id, - hash: block.block_meta.block_id.hash, - time: block.block_meta.header.time, - numTxs: block.block_meta.header.num_txs, - proposer_address: block.block_meta.header.proposer_address - } -} - -function balanceReducer(balance) { - return { - denom: balance.denom.toUpperCase(), - amount: BigNumber(balance.amount).div(1000000) - } -} - -function delegationReducer(delegation) { - return { - delegatorAddress: delegation.delegator_address, - validatorAddress: delegation.validator_address, - amount: delegation.balance - } -} - -function undelegationReducer(undelegation) { - return { - delegatorAddress: undelegation.delegator_address, - validatorAddress: undelegation.validator_address, - amount: undelegation.balance, - startHeight: undelegation.creation_height, - endTime: undelegation.min_time - } -} - -module.exports = { - proposalReducer, - tallyReducer, - validatorReducer, - blockReducer, - balanceReducer, - delegationReducer, - undelegationReducer -} diff --git a/lib/reducers/cosmos-reducers.js b/lib/reducers/gaiav0-reducers.js similarity index 97% rename from lib/reducers/cosmos-reducers.js rename to lib/reducers/gaiav0-reducers.js index 5eb206969c..762ed43c96 100644 --- a/lib/reducers/cosmos-reducers.js +++ b/lib/reducers/gaiav0-reducers.js @@ -139,7 +139,7 @@ function validatorReducer(validator) { ? validator.signing_info.start_height : undefined, uptimePercentage: 1, // TODO - tokens: validator.tokens, + tokens: atoms(validator.tokens), commissionUpdateTime: validator.commission.update_time, commission: validator.commission.rate, maxCommission: validator.commission.max_rate, @@ -283,7 +283,8 @@ function transactionReducer(transaction) { memo: transaction.tx.value.memo, fee, signature: transaction.tx.value.signatures[0].signature, - value: JSON.stringify(transaction.tx.value.msg[0].value) + value: JSON.stringify(transaction.tx.value.msg[0].value), + raw: transaction } return result @@ -300,5 +301,11 @@ module.exports = { undelegationReducer, rewardReducer, overviewReducer, - atoms + + atoms, + proposalBeginTime, + proposalEndTime, + getDeposit, + getTotalVotedPercentage, + getValidatorStatus } diff --git a/lib/reducers/gaiav2-reducers.js b/lib/reducers/gaiav2-reducers.js new file mode 100644 index 0000000000..1d7bc52ee0 --- /dev/null +++ b/lib/reducers/gaiav2-reducers.js @@ -0,0 +1,78 @@ +const gaiav0Reducers = require('./gaiav0-reducers') +const { + proposalBeginTime, + proposalEndTime, + getDeposit, + tallyReducer, + atoms, + getValidatorStatus +} = gaiav0Reducers + +const NETWORK_ID = 'gaia-testnet' // TODO needs to be taken from network config + +function proposalReducer(proposal, totalBondedTokens) { + return { + networkId: NETWORK_ID, + id: Number(proposal.id), + type: proposal.content.type, + title: proposal.content.value.title, + description: proposal.content.value.description, + creationTime: proposal.submit_time, + status: proposal.proposal_status, + statusBeginTime: proposalBeginTime(proposal), + statusEndTime: proposalEndTime(proposal), + tally: tallyReducer(proposal, totalBondedTokens), + deposit: getDeposit(proposal, 'stake') // TODO use denom lookup + use network config + } +} + +function delegationReducer(delegation, validator) { + return { + validatorAddress: delegation.validator_address, + delegatorAddress: delegation.delegator_address, + validator, + amount: atoms(delegation.balance) + } +} + +function validatorReducer(validator) { + const statusInfo = getValidatorStatus(validator) + let websiteURL = validator.description.website + if (!websiteURL || websiteURL === '[do-not-modify]') { + websiteURL = '' + } else if (!websiteURL.match(/http[s]?/)) { + websiteURL = `https://` + websiteURL + } + + return { + networkId: NETWORK_ID, + operatorAddress: validator.operator_address, + consensusPubkey: validator.consensus_pubkey, + jailed: validator.jailed, + details: validator.description.details, + website: websiteURL, + identity: validator.description.identity, + name: validator.description.moniker, + votingPower: validator.voting_power, + startHeight: validator.signing_info + ? validator.signing_info.start_height + : undefined, + uptimePercentage: 1, // TODO + tokens: atoms(validator.tokens), + commissionUpdateTime: validator.commission.update_time, + commission: validator.commission.commission_rates.rate, + maxCommission: validator.commission.commission_rates.max_rate, + maxChangeCommission: validator.commission.commission_rates.max_change_rate, + status: statusInfo.status, + statusDetailed: statusInfo.status_detailed, + delegatorShares: validator.delegator_shares, // needed to calculate delegation token amounts from shares + expectedReturns: validator.expected_returns + } +} + +module.exports = { + ...gaiav0Reducers, + proposalReducer, + delegationReducer, + validatorReducer +} diff --git a/lib/resolvers.js b/lib/resolvers.js index a865fd7c39..f98b786d5c 100644 --- a/lib/resolvers.js +++ b/lib/resolvers.js @@ -37,8 +37,8 @@ async function validators(_, { networkId, all, query }, { dataSources }) { validators = validators.filter(({ status }) => status === 'ACTIVE') } if (query) { - validators = validators.filter(({ moniker, operatorAddress }) => { - return moniker.indexOf(query) !== -1 || operatorAddress.indexOf(query) + validators = validators.filter(({ name, operatorAddress }) => { + return name.indexOf(query) !== -1 || operatorAddress.indexOf(query) }) } From 5529900ee6a3df1e4df956c44069bf7be69467b7 Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Sun, 20 Oct 2019 19:23:38 -0400 Subject: [PATCH 02/19] working activity --- lib/gaiav0-source.js | 10 +++++----- lib/gaiav2-source.js | 10 +++++++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/gaiav0-source.js b/lib/gaiav0-source.js index 0559b6a7e9..3d8ece2cdc 100644 --- a/lib/gaiav0-source.js +++ b/lib/gaiav0-source.js @@ -72,8 +72,8 @@ class CosmosAPI extends RESTDataSource { const response = await this.get(`txs?tx.height=${height}`) return Array.isArray(response) ? response.map(transaction => - this.reducers.transactionReducer(transaction) - ) + this.reducers.transactionReducer(transaction) + ) : [] } @@ -147,8 +147,8 @@ class CosmosAPI extends RESTDataSource { const consensusAddress = pubkeyToAddress(validator.consensus_pubkey) validator.voting_power = consensusValidators[consensusAddress] ? BigNumber(consensusValidators[consensusAddress].voting_power).div( - totalVotingPower - ) + totalVotingPower + ) : 0 validator.expected_returns = expectedReturns(validator, annualProvision) validator.signing_info = signingInfos[consensusAddress] @@ -341,7 +341,7 @@ class CosmosAPI extends RESTDataSource { } async getTransactions(address) { - const pagination = `&page=${1000000000}` + const pagination = `&limit=${1000000000}` const txs = await Promise.all([ this.get(`/txs?sender=${address}${pagination}`), diff --git a/lib/gaiav2-source.js b/lib/gaiav2-source.js index 15455a1a94..e0ad6b431b 100644 --- a/lib/gaiav2-source.js +++ b/lib/gaiav2-source.js @@ -19,11 +19,15 @@ class GaiaV2API extends GaiaV0API { } async getTransactions(address) { - const pagination = `&page=${1000000000}` + const pagination = `&limit=${1000000000}` const txs = await Promise.all([ - this.get(`/txs?message.sender=${address}${pagination}`), - this.get(`/txs?message.recipient=${address}${pagination}`) + this.get(`/txs?message.sender=${address}${pagination}`).then( + ({ txs }) => txs + ), + this.get(`/txs?message.recipient=${address}${pagination}`).then( + ({ txs }) => txs + ) ]).then(([senderTxs, recipientTxs]) => [].concat(senderTxs, recipientTxs)) const dupFreeTxs = uniqWith(txs, (a, b) => a.txhash === b.txhash) From b27be0a50f9b4c23ef0ab8dffe52b93803b732d5 Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Sun, 20 Oct 2019 19:41:41 -0400 Subject: [PATCH 03/19] working --- lib/gaiav0-source.js | 15 ++++++++------- lib/gaiav2-source.js | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/lib/gaiav0-source.js b/lib/gaiav0-source.js index 3d8ece2cdc..474a957535 100644 --- a/lib/gaiav0-source.js +++ b/lib/gaiav0-source.js @@ -72,8 +72,8 @@ class CosmosAPI extends RESTDataSource { const response = await this.get(`txs?tx.height=${height}`) return Array.isArray(response) ? response.map(transaction => - this.reducers.transactionReducer(transaction) - ) + this.reducers.transactionReducer(transaction) + ) : [] } @@ -147,8 +147,8 @@ class CosmosAPI extends RESTDataSource { const consensusAddress = pubkeyToAddress(validator.consensus_pubkey) validator.voting_power = consensusValidators[consensusAddress] ? BigNumber(consensusValidators[consensusAddress].voting_power).div( - totalVotingPower - ) + totalVotingPower + ) : 0 validator.expected_returns = expectedReturns(validator, annualProvision) validator.signing_info = signingInfos[consensusAddress] @@ -316,9 +316,10 @@ class CosmosAPI extends RESTDataSource { const rewards = await Promise.all( delegations.map(async ({ validatorAddress, validator }) => ({ validator, - rewards: await this.query( - `distribution/delegators/${delegatorAddress}/rewards/${validatorAddress}` - ) + rewards: + (await this.query( + `distribution/delegators/${delegatorAddress}/rewards/${validatorAddress}` + )) || [] })) ) return rewards.map(({ rewards, validator }) => diff --git a/lib/gaiav2-source.js b/lib/gaiav2-source.js index e0ad6b431b..fde0eb518a 100644 --- a/lib/gaiav2-source.js +++ b/lib/gaiav2-source.js @@ -40,6 +40,23 @@ class GaiaV2API extends GaiaV0API { console.log('TODO extractInvolvedAddresses', transaction) return [] } + + async getRewards(delegatorAddress) { + const { rewards } = await this.query( + `distribution/delegators/${delegatorAddress}/rewards` + ) + const validators = await this.getAllValidators() + return rewards + .filter(({ rewards }) => !!rewards) + .map(({ rewards, validator_address }) => + this.reducers.rewardReducer( + rewards[0], + validators.find( + ({ operatorAddress }) => validator_address === operatorAddress + ) + ) + ) + } } module.exports = GaiaV2API From a86cd1dc8efdc3ea2fffa26958220e948ddb8980 Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Sun, 20 Oct 2019 20:19:32 -0400 Subject: [PATCH 04/19] removed hard coded network id --- .DS_Store | Bin 0 -> 6148 bytes data/networks.js | 3 +++ lib/gaiav0-source.js | 11 ++++++++--- lib/reducers/gaiav0-reducers.js | 14 ++++++-------- lib/reducers/gaiav2-reducers.js | 10 ++++------ 5 files changed, 21 insertions(+), 17 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 - this.reducers.validatorReducer(validator) + this.reducers.validatorReducer(this.networkId, validator) ) } @@ -191,7 +192,11 @@ class CosmosAPI extends RESTDataSource { ) if (!Array.isArray(response)) return [] const proposals = response.map(async proposal => { - return this.reducers.proposalReducer(proposal, totalBondedTokens) + return this.reducers.proposalReducer( + this.networkId, + proposal, + totalBondedTokens + ) }) return _.orderBy(proposals, 'id', 'desc') } @@ -234,7 +239,7 @@ class CosmosAPI extends RESTDataSource { } else { response = await this.get(`blocks/latest`) } - return this.reducers.blockReducer(response) + return this.reducers.blockReducer(this.networkId, response) } async getBalanceFromAddress(address) { diff --git a/lib/reducers/gaiav0-reducers.js b/lib/reducers/gaiav0-reducers.js index 762ed43c96..f036031419 100644 --- a/lib/reducers/gaiav0-reducers.js +++ b/lib/reducers/gaiav0-reducers.js @@ -3,8 +3,6 @@ const { cosmosMessageType } = require('../message-types') const BigNumber = require('bignumber.js') const _ = require('lodash') -const NETWORK_ID = 'cosmoshub' - function proposalBeginTime(proposal) { switch (proposal.proposal_status.toLowerCase()) { case 'depositperiod': @@ -77,9 +75,9 @@ function tallyReducer(proposal, totalBondedTokens) { } } -function proposalReducer(proposal, totalBondedTokens) { +function proposalReducer(networkId, proposal, totalBondedTokens) { return { - networkId: NETWORK_ID, + networkId, id: Number(proposal.proposal_id), type: proposal.proposal_content.type, title: proposal.proposal_content.value.title, @@ -116,7 +114,7 @@ function getValidatorStatus(validator) { } } -function validatorReducer(validator) { +function validatorReducer(networkId, validator) { const statusInfo = getValidatorStatus(validator) let websiteURL = validator.description.website if (!websiteURL || websiteURL === '[do-not-modify]') { @@ -126,7 +124,7 @@ function validatorReducer(validator) { } return { - networkId: NETWORK_ID, + networkId, operatorAddress: validator.operator_address, consensusPubkey: validator.consensus_pubkey, jailed: validator.jailed, @@ -151,9 +149,9 @@ function validatorReducer(validator) { } } -function blockReducer(block) { +function blockReducer(networkId, block) { return { - networkId: NETWORK_ID, + networkId, height: block.block_meta.header.height, chainId: block.block_meta.header.chain_id, hash: block.block_meta.block_id.hash, diff --git a/lib/reducers/gaiav2-reducers.js b/lib/reducers/gaiav2-reducers.js index 1d7bc52ee0..e7fc3d9216 100644 --- a/lib/reducers/gaiav2-reducers.js +++ b/lib/reducers/gaiav2-reducers.js @@ -8,11 +8,9 @@ const { getValidatorStatus } = gaiav0Reducers -const NETWORK_ID = 'gaia-testnet' // TODO needs to be taken from network config - -function proposalReducer(proposal, totalBondedTokens) { +function proposalReducer(networkId, proposal, totalBondedTokens) { return { - networkId: NETWORK_ID, + networkId, id: Number(proposal.id), type: proposal.content.type, title: proposal.content.value.title, @@ -35,7 +33,7 @@ function delegationReducer(delegation, validator) { } } -function validatorReducer(validator) { +function validatorReducer(networkId, validator) { const statusInfo = getValidatorStatus(validator) let websiteURL = validator.description.website if (!websiteURL || websiteURL === '[do-not-modify]') { @@ -45,7 +43,7 @@ function validatorReducer(validator) { } return { - networkId: NETWORK_ID, + networkId, operatorAddress: validator.operator_address, consensusPubkey: validator.consensus_pubkey, jailed: validator.jailed, From 8073718aecbefdecdabe0a605b73c1c112685051 Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Sun, 20 Oct 2019 20:29:36 -0400 Subject: [PATCH 05/19] added uptime --- lib/gaiav0-source.js | 9 ++++++++- lib/reducers/gaiav0-reducers.js | 7 +++++-- lib/reducers/gaiav2-reducers.js | 7 +++++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/gaiav0-source.js b/lib/gaiav0-source.js index 4683de27b6..5cc1fe6dcf 100644 --- a/lib/gaiav0-source.js +++ b/lib/gaiav0-source.js @@ -35,6 +35,9 @@ class CosmosAPI extends RESTDataSource { async loadStaticData() { this.stakingDenom = (await this.query('/staking/parameters')).bond_denom + this.signedBlocksWindow = (await this.query( + '/slashing/parameters' + )).signed_blocks_window // this.depositDenom = (await this.query("/gov/parameters/deposit")).min_deposit[0].denom } @@ -156,7 +159,11 @@ class CosmosAPI extends RESTDataSource { }) return validators.map(validator => - this.reducers.validatorReducer(this.networkId, validator) + this.reducers.validatorReducer( + this.networkId, + this.signedBlocksWindow, + validator + ) ) } diff --git a/lib/reducers/gaiav0-reducers.js b/lib/reducers/gaiav0-reducers.js index f036031419..311e57b99b 100644 --- a/lib/reducers/gaiav0-reducers.js +++ b/lib/reducers/gaiav0-reducers.js @@ -114,7 +114,7 @@ function getValidatorStatus(validator) { } } -function validatorReducer(networkId, validator) { +function validatorReducer(networkId, signedBlocksWindow, validator) { const statusInfo = getValidatorStatus(validator) let websiteURL = validator.description.website if (!websiteURL || websiteURL === '[do-not-modify]') { @@ -136,7 +136,10 @@ function validatorReducer(networkId, validator) { startHeight: validator.signing_info ? validator.signing_info.start_height : undefined, - uptimePercentage: 1, // TODO + uptimePercentage: + 1 - + Number(validator.signing_info.missed_blocks_counter) / + Number(signedBlocksWindow), tokens: atoms(validator.tokens), commissionUpdateTime: validator.commission.update_time, commission: validator.commission.rate, diff --git a/lib/reducers/gaiav2-reducers.js b/lib/reducers/gaiav2-reducers.js index e7fc3d9216..afc0431c0d 100644 --- a/lib/reducers/gaiav2-reducers.js +++ b/lib/reducers/gaiav2-reducers.js @@ -33,7 +33,7 @@ function delegationReducer(delegation, validator) { } } -function validatorReducer(networkId, validator) { +function validatorReducer(networkId, signedBlocksWindow, validator) { const statusInfo = getValidatorStatus(validator) let websiteURL = validator.description.website if (!websiteURL || websiteURL === '[do-not-modify]') { @@ -55,7 +55,10 @@ function validatorReducer(networkId, validator) { startHeight: validator.signing_info ? validator.signing_info.start_height : undefined, - uptimePercentage: 1, // TODO + uptimePercentage: + 1 - + Number(validator.signing_info.missed_blocks_counter) / + Number(signedBlocksWindow), tokens: atoms(validator.tokens), commissionUpdateTime: validator.commission.update_time, commission: validator.commission.commission_rates.rate, From 3ed3560f71be947ed8a90786d04d5000d37b244c Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Sun, 20 Oct 2019 20:32:20 -0400 Subject: [PATCH 06/19] fixes --- lib/gaiav0-source.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/gaiav0-source.js b/lib/gaiav0-source.js index 5cc1fe6dcf..b90eb58a5a 100644 --- a/lib/gaiav0-source.js +++ b/lib/gaiav0-source.js @@ -76,8 +76,8 @@ class CosmosAPI extends RESTDataSource { const response = await this.get(`txs?tx.height=${height}`) return Array.isArray(response) ? response.map(transaction => - this.reducers.transactionReducer(transaction) - ) + this.reducers.transactionReducer(transaction) + ) : [] } @@ -151,8 +151,8 @@ class CosmosAPI extends RESTDataSource { const consensusAddress = pubkeyToAddress(validator.consensus_pubkey) validator.voting_power = consensusValidators[consensusAddress] ? BigNumber(consensusValidators[consensusAddress].voting_power).div( - totalVotingPower - ) + totalVotingPower + ) : 0 validator.expected_returns = expectedReturns(validator, annualProvision) validator.signing_info = signingInfos[consensusAddress] @@ -213,7 +213,11 @@ class CosmosAPI extends RESTDataSource { const { bonded_tokens: totalBondedTokens } = await this.query( '/staking/pool' ) - return this.reducers.proposalReducer(response, totalBondedTokens) + return this.reducers.proposalReducer( + this.networkId, + response, + totalBondedTokens + ) } async getGovernanceParameters() { From 3ec3e9a09faff0c8c64a6155e1d518c51333ac84 Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Sun, 20 Oct 2019 21:17:35 -0400 Subject: [PATCH 07/19] added transactions to block --- lib/gaiav0-source.js | 28 +++++++++++++++++----------- lib/reducers/gaiav0-reducers.js | 6 +++--- lib/reducers/gaiav2-reducers.js | 6 +++++- lib/schema.js | 2 +- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/lib/gaiav0-source.js b/lib/gaiav0-source.js index b90eb58a5a..856c3abd36 100644 --- a/lib/gaiav0-source.js +++ b/lib/gaiav0-source.js @@ -73,11 +73,9 @@ class CosmosAPI extends RESTDataSource { } async getTransactionsByHeight(height) { - const response = await this.get(`txs?tx.height=${height}`) - return Array.isArray(response) - ? response.map(transaction => - this.reducers.transactionReducer(transaction) - ) + const { txs } = await this.get(`txs?tx.height=${height}`) + return Array.isArray(txs) + ? txs.map(transaction => this.reducers.transactionReducer(transaction)) : [] } @@ -151,8 +149,8 @@ class CosmosAPI extends RESTDataSource { const consensusAddress = pubkeyToAddress(validator.consensus_pubkey) validator.voting_power = consensusValidators[consensusAddress] ? BigNumber(consensusValidators[consensusAddress].voting_power).div( - totalVotingPower - ) + totalVotingPower + ) : 0 validator.expected_returns = expectedReturns(validator, annualProvision) validator.signing_info = signingInfos[consensusAddress] @@ -244,13 +242,21 @@ class CosmosAPI extends RESTDataSource { } async getBlockByHeight({ blockHeight }) { - let response + let block, transactions if (blockHeight) { - response = await this.get(`blocks/${blockHeight}`) + const response = await Promise.all([ + this.get(`blocks/${blockHeight}`), + this.getTransactionsByHeight(blockHeight) + ]) + block = response[0] + transactions = response[1] } else { - response = await this.get(`blocks/latest`) + block = await this.get(`blocks/latest`) + transactions = await this.getTransactionsByHeight( + block.block_meta.header.height + ) } - return this.reducers.blockReducer(this.networkId, response) + return this.reducers.blockReducer(this.networkId, block, transactions) } async getBalanceFromAddress(address) { diff --git a/lib/reducers/gaiav0-reducers.js b/lib/reducers/gaiav0-reducers.js index 311e57b99b..92f59c1efc 100644 --- a/lib/reducers/gaiav0-reducers.js +++ b/lib/reducers/gaiav0-reducers.js @@ -139,7 +139,7 @@ function validatorReducer(networkId, signedBlocksWindow, validator) { uptimePercentage: 1 - Number(validator.signing_info.missed_blocks_counter) / - Number(signedBlocksWindow), + Number(signedBlocksWindow), tokens: atoms(validator.tokens), commissionUpdateTime: validator.commission.update_time, commission: validator.commission.rate, @@ -152,14 +152,14 @@ function validatorReducer(networkId, signedBlocksWindow, validator) { } } -function blockReducer(networkId, block) { +function blockReducer(networkId, block, transactions) { return { networkId, height: block.block_meta.header.height, chainId: block.block_meta.header.chain_id, hash: block.block_meta.block_id.hash, time: block.block_meta.header.time, - numTxs: block.block_meta.header.num_txs, + transactions, proposer_address: block.block_meta.header.proposer_address } } diff --git a/lib/reducers/gaiav2-reducers.js b/lib/reducers/gaiav2-reducers.js index afc0431c0d..08fe4aa061 100644 --- a/lib/reducers/gaiav2-reducers.js +++ b/lib/reducers/gaiav2-reducers.js @@ -57,7 +57,11 @@ function validatorReducer(networkId, signedBlocksWindow, validator) { : undefined, uptimePercentage: 1 - - Number(validator.signing_info.missed_blocks_counter) / + Number( + validator.signing_info + ? validator.signing_info.missed_blocks_counter + : 0 + ) / Number(signedBlocksWindow), tokens: atoms(validator.tokens), commissionUpdateTime: validator.commission.update_time, diff --git a/lib/schema.js b/lib/schema.js index 0d591990ec..6c4e7941b5 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -71,7 +71,7 @@ const typeDefs = gql` hash: String chainId: String time: String - numTxs: Int + transactions: [Transaction] proposer_address: String } From 83be1145fb94d5894b062c777d3aeee76e453141 Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Mon, 21 Oct 2019 10:46:33 -0400 Subject: [PATCH 08/19] renamed gaia to cosmos --- lib/{gaiav0-source.js => cosmosV0-source.js} | 0 lib/{gaiav2-source.js => cosmosV2-source.js} | 10 +++++----- .../{gaiav0-reducers.js => cosmosV0-reducers.js} | 4 ++-- .../{gaiav2-reducers.js => cosmosV2-reducers.js} | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) rename lib/{gaiav0-source.js => cosmosV0-source.js} (100%) rename lib/{gaiav2-source.js => cosmosV2-source.js} (86%) rename lib/reducers/{gaiav0-reducers.js => cosmosV0-reducers.js} (98%) rename lib/reducers/{gaiav2-reducers.js => cosmosV2-reducers.js} (96%) diff --git a/lib/gaiav0-source.js b/lib/cosmosV0-source.js similarity index 100% rename from lib/gaiav0-source.js rename to lib/cosmosV0-source.js diff --git a/lib/gaiav2-source.js b/lib/cosmosV2-source.js similarity index 86% rename from lib/gaiav2-source.js rename to lib/cosmosV2-source.js index fde0eb518a..08140aa42c 100644 --- a/lib/gaiav2-source.js +++ b/lib/cosmosV2-source.js @@ -1,11 +1,11 @@ -const GaiaV0API = require('./gaiav0-source') +const CosmosV0API = require('./cosmosV0-source') -const { transactionReducer } = require('./reducers/gaiav0-reducers') +const { transactionReducer } = require('./reducers/cosmosV0-reducers') const { uniqWith, sortBy, reverse } = require('lodash') -class GaiaV2API extends GaiaV0API { +class CosmosV2API extends CosmosV0API { setReducers() { - this.reducers = require('./reducers/gaiav2-reducers') + this.reducers = require('./reducers/cosmosV2-reducers') } async query(url) { @@ -59,4 +59,4 @@ class GaiaV2API extends GaiaV0API { } } -module.exports = GaiaV2API +module.exports = CosmosV2API diff --git a/lib/reducers/gaiav0-reducers.js b/lib/reducers/cosmosV0-reducers.js similarity index 98% rename from lib/reducers/gaiav0-reducers.js rename to lib/reducers/cosmosV0-reducers.js index 92f59c1efc..c928bd1243 100644 --- a/lib/reducers/gaiav0-reducers.js +++ b/lib/reducers/cosmosV0-reducers.js @@ -139,7 +139,7 @@ function validatorReducer(networkId, signedBlocksWindow, validator) { uptimePercentage: 1 - Number(validator.signing_info.missed_blocks_counter) / - Number(signedBlocksWindow), + Number(signedBlocksWindow), tokens: atoms(validator.tokens), commissionUpdateTime: validator.commission.update_time, commission: validator.commission.rate, @@ -188,7 +188,7 @@ function coinReducer(coin) { } function delegationReducer(delegation, validator) { - // in gaiav0 we need to convert shares (gaia internal representation) to token balance + // in cosmos SDK v0 we need to convert shares (cosmos internal representation) to token balance const balance = calculateTokens(validator, delegation.shares) return { diff --git a/lib/reducers/gaiav2-reducers.js b/lib/reducers/cosmosV2-reducers.js similarity index 96% rename from lib/reducers/gaiav2-reducers.js rename to lib/reducers/cosmosV2-reducers.js index 08fe4aa061..64435a3da8 100644 --- a/lib/reducers/gaiav2-reducers.js +++ b/lib/reducers/cosmosV2-reducers.js @@ -1,4 +1,4 @@ -const gaiav0Reducers = require('./gaiav0-reducers') +const cosmosV0Reducers = require('./cosmosV0-reducers') const { proposalBeginTime, proposalEndTime, @@ -6,7 +6,7 @@ const { tallyReducer, atoms, getValidatorStatus -} = gaiav0Reducers +} = cosmosV0Reducers function proposalReducer(networkId, proposal, totalBondedTokens) { return { @@ -76,7 +76,7 @@ function validatorReducer(networkId, signedBlocksWindow, validator) { } module.exports = { - ...gaiav0Reducers, + ...cosmosV0Reducers, proposalReducer, delegationReducer, validatorReducer From bd3fbd686550be5c24d86257671b2f07119c8301 Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Mon, 21 Oct 2019 10:48:50 -0400 Subject: [PATCH 09/19] more renaming --- index.js | 14 +++++++------- lib/cosmosV0-source.js | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index 59c486f48a..4c8a985639 100644 --- a/index.js +++ b/index.js @@ -1,23 +1,23 @@ const { ApolloServer } = require('apollo-server') const typeDefs = require('./lib/schema') const resolvers = require('./lib/resolvers') -const CosmosHubAPI = require('./lib/gaiav0-source') -const GaiaTestnetAPI = require('./lib/gaiav2-source') +const CosmosV0API = require('./lib/cosmosV0-source') +const CosmosV2API = require('./lib/cosmosV2-source') const LunieDBAPI = require('./lib/luniedb-source') const { networks } = require('./data/networks') const config = require('./config') -const cosmosHubApi = new CosmosHubAPI(networks['cosmoshub']) -const gaiaTestnetApi = new GaiaTestnetAPI(networks['gaia-testnet']) +const cosmosHubMainnetAPI = new CosmosV0API(networks['cosmoshub']) +const cosmosHubTestnetAPI = new CosmosV2API(networks['gaia-testnet']) const lunieDBAPI = new LunieDBAPI() const dataSources = { - CosmosAPI: cosmosHubApi, - GaiaAPI: gaiaTestnetApi, + CosmosAPI: cosmosHubMainnetAPI, + GaiaAPI: cosmosHubTestnetAPI, LunieDBAPI: lunieDBAPI } if (config.enableTestnet) { - const testnetAPI = new CosmosHubAPI(networks['testnet']) + const testnetAPI = new CosmosV0API(networks['testnet']) dataSources.TestnetAPI = testnetAPI } diff --git a/lib/cosmosV0-source.js b/lib/cosmosV0-source.js index 856c3abd36..760746341c 100644 --- a/lib/cosmosV0-source.js +++ b/lib/cosmosV0-source.js @@ -26,7 +26,7 @@ class CosmosAPI extends RESTDataSource { } setReducers() { - this.reducers = require('./reducers/gaiav0-reducers') + this.reducers = require('./reducers/cosmosV0-reducers') } async query(url) { From c6429a34934085d36e944bb162e61f27d7da9c61 Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Mon, 21 Oct 2019 10:52:03 -0400 Subject: [PATCH 10/19] more renaming --- index.js | 4 ++-- lib/resolvers.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 4c8a985639..ec39aafdff 100644 --- a/index.js +++ b/index.js @@ -12,8 +12,8 @@ const cosmosHubTestnetAPI = new CosmosV2API(networks['gaia-testnet']) const lunieDBAPI = new LunieDBAPI() const dataSources = { - CosmosAPI: cosmosHubMainnetAPI, - GaiaAPI: cosmosHubTestnetAPI, + CosmosHubMainnetAPI: cosmosHubMainnetAPI, + CosmosHubTestnetAPI: cosmosHubTestnetAPI, LunieDBAPI: lunieDBAPI } if (config.enableTestnet) { diff --git a/lib/resolvers.js b/lib/resolvers.js index f98b786d5c..9b5eb29479 100644 --- a/lib/resolvers.js +++ b/lib/resolvers.js @@ -3,9 +3,9 @@ const { blockAdded, userTransactionAdded } = require('./chain-pubsub') function selectFrom(dataSources, networkId) { switch (networkId) { case 'cosmoshub': - return dataSources.CosmosAPI + return dataSources.CosmosHubMainnetAPI case 'gaia-testnet': - return dataSources.GaiaAPI + return dataSources.CosmosHubTestnetAPI case 'testnet': return dataSources.TestnetAPI } From 292e0b919c120b8f1a8f15fa36f5f13014c08fce Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Mon, 21 Oct 2019 13:50:35 -0400 Subject: [PATCH 11/19] comments from jordan --- data/{networks.js => network-configs.js} | 12 ++-- index.js | 12 ++-- lib/cosmosV0-source.js | 28 ++++---- lib/reducers/cosmosV0-reducers.js | 8 +-- lib/reducers/gaia-reducer-helpers.js | 83 ++++++++++++++++++++++++ lib/resolvers.js | 6 +- 6 files changed, 120 insertions(+), 29 deletions(-) rename data/{networks.js => network-configs.js} (67%) create mode 100644 lib/reducers/gaia-reducer-helpers.js diff --git a/data/networks.js b/data/network-configs.js similarity index 67% rename from data/networks.js rename to data/network-configs.js index ce12424e04..6a76a89b74 100644 --- a/data/networks.js +++ b/data/network-configs.js @@ -1,21 +1,21 @@ const config = require('../config') let networks = { - 'gaia-testnet': { - id: 'gaia-testnet', + 'cosmos-hub-testnet': { + id: 'cosmos-hub-testnet', api_url: 'https://gaia-13006.lunie.io', rpc_url: 'wss://gaia-13006.lunie.io:26657/websocket' }, - cosmoshub: { - id: 'cosmoshub', + 'cosmos-hub-mainnet': { + id: 'cosmos-hub-mainnet', api_url: 'https://lcd.nylira.net', rpc_url: 'wss://rpc.nylira.net/websocket' } } if (config.enableTestnet) { - networks.testnet = { - id: 'testnet', + networks['local-cosmos-hub-testnet'] = { + id: 'local-cosmos-hub-testnet', api_url: 'http://localhost:9070', rpc_url: 'ws://localhost:26657/websocket' } diff --git a/index.js b/index.js index ec39aafdff..e0c8f6282d 100644 --- a/index.js +++ b/index.js @@ -4,11 +4,11 @@ const resolvers = require('./lib/resolvers') const CosmosV0API = require('./lib/cosmosV0-source') const CosmosV2API = require('./lib/cosmosV2-source') const LunieDBAPI = require('./lib/luniedb-source') -const { networks } = require('./data/networks') +const { networks } = require('./data/network-configs') const config = require('./config') -const cosmosHubMainnetAPI = new CosmosV0API(networks['cosmoshub']) -const cosmosHubTestnetAPI = new CosmosV2API(networks['gaia-testnet']) +const cosmosHubMainnetAPI = new CosmosV0API(networks['cosmos-hub-mainnet']) +const cosmosHubTestnetAPI = new CosmosV2API(networks['cosmos-hub-testnet']) const lunieDBAPI = new LunieDBAPI() const dataSources = { @@ -17,8 +17,10 @@ const dataSources = { LunieDBAPI: lunieDBAPI } if (config.enableTestnet) { - const testnetAPI = new CosmosV0API(networks['testnet']) - dataSources.TestnetAPI = testnetAPI + const localCosmosTestnetAPI = new CosmosV0API( + networks['local-cosmos-hub-testnet'] + ) + dataSources.TestnetAPI = localCosmosTestnetAPI } let options = { diff --git a/lib/cosmosV0-source.js b/lib/cosmosV0-source.js index 760746341c..57363147a7 100644 --- a/lib/cosmosV0-source.js +++ b/lib/cosmosV0-source.js @@ -29,6 +29,9 @@ class CosmosAPI extends RESTDataSource { this.reducers = require('./reducers/cosmosV0-reducers') } + // querying data from the cosmos REST API + // is overwritten in cosmos v2 to extract from a differnt result format + // some endpoints /blocks and /txs have a different response format so they use this.get directly async query(url) { return this.get(url) } @@ -38,7 +41,6 @@ class CosmosAPI extends RESTDataSource { this.signedBlocksWindow = (await this.query( '/slashing/parameters' )).signed_blocks_window - // this.depositDenom = (await this.query("/gov/parameters/deposit")).min_deposit[0].denom } // subscribe to blocks via Tendermint @@ -369,24 +371,28 @@ class CosmosAPI extends RESTDataSource { 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}`), - this.get(`/txs?action=deposit&depositor=${address}`), - this.get(`/txs?action=vote&voter=${address}`), + 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}`), - this.get(`/txs?action=begin_redelegate&delegator=${address}`), - this.get(`/txs?action=begin_unbonding&delegator=${address}`), + 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}`), this.get( - `/txs?action=withdraw_validator_rewards_all&source-validator=${address}` + `/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 dupFreeTxs = uniqWith(txs, (a, b) => a.txhash === b.txhash) - const sortedTxs = sortBy(dupFreeTxs, ['timestamp']) + 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) } diff --git a/lib/reducers/cosmosV0-reducers.js b/lib/reducers/cosmosV0-reducers.js index c928bd1243..9da19a2445 100644 --- a/lib/reducers/cosmosV0-reducers.js +++ b/lib/reducers/cosmosV0-reducers.js @@ -43,7 +43,7 @@ function getDeposit(proposal, bondDenom) { ) } -function getTotalVotedPercentage(proposal, totalBondedTokens, totalVoted) { +function getTotalVotePercentage(proposal, totalBondedTokens, totalVoted) { // for passed proposals we can't calculate the total voted percentage, as we don't know the totalBondedTokens in the past if (['Passed', 'Rejected'].indexOf(proposal.proposal_status) !== -1) return -1 if (totalVoted.eq(0)) return 0 @@ -67,7 +67,7 @@ function tallyReducer(proposal, totalBondedTokens) { abstain: atoms(proposal.final_tally_result.abstain), veto: atoms(proposal.final_tally_result.no_with_veto), total: totalVoted, - totalVotedPercentage: getTotalVotedPercentage( + totalVotedPercentage: getTotalVotePercentage( proposal, totalBondedTokens, totalVoted @@ -139,7 +139,7 @@ function validatorReducer(networkId, signedBlocksWindow, validator) { uptimePercentage: 1 - Number(validator.signing_info.missed_blocks_counter) / - Number(signedBlocksWindow), + Number(signedBlocksWindow), tokens: atoms(validator.tokens), commissionUpdateTime: validator.commission.update_time, commission: validator.commission.rate, @@ -307,6 +307,6 @@ module.exports = { proposalBeginTime, proposalEndTime, getDeposit, - getTotalVotedPercentage, + getTotalVotePercentage, getValidatorStatus } diff --git a/lib/reducers/gaia-reducer-helpers.js b/lib/reducers/gaia-reducer-helpers.js new file mode 100644 index 0000000000..e1d43472e1 --- /dev/null +++ b/lib/reducers/gaia-reducer-helpers.js @@ -0,0 +1,83 @@ +const BigNumber = require('bignumber.js') + +function proposalBeginTime(proposal) { + switch (proposal.proposal_status.toLowerCase()) { + case 'depositperiod': + return proposal.submit_time + case 'votingperiod': + return proposal.voting_start_time + case 'passed': + case 'rejected': + return proposal.voting_end_time + } +} + +function proposalEndTime(proposal) { + switch (proposal.proposal_status.toLowerCase()) { + case 'depositperiod': + return proposal.voting_start_time + case 'votingperiod': + // the end time lives in the past already if the proposal is finalized + case 'passed': + case 'rejected': + return proposal.voting_end_time + } +} + +function atoms(nanoAtoms) { + return BigNumber(nanoAtoms) + .div(1000000) + .toFixed(6) +} + +// reduce deposits to one number and filter by required denom +function getDeposit(proposal, bondDenom) { + return atoms( + proposal.total_deposit.reduce( + (sum, cur) => sum.plus(cur.denom === bondDenom ? cur.amount : 0), + BigNumber(0) + ) + ) +} + +function getTotalVotePercentage(proposal, totalBondedTokens, totalVoted) { + // for passed proposals we can't calculate the total voted percentage, as we don't know the totalBondedTokens in the past + if (['Passed', 'Rejected'].indexOf(proposal.proposal_status) !== -1) return -1 + if (totalVoted.eq(0)) return 0 + if (!totalBondedTokens) return -1 + return BigNumber(totalBondedTokens) + .div(atoms(totalVoted)) + .toNumber() +} + +function getValidatorStatus(validator) { + if (validator.status === 2) { + return { + status: 'ACTIVE', + status_detailed: 'active' + } + } + if ( + validator.signing_info && + new Date(validator.signing_info.jailed_until) > new Date(9000, 1, 1) + ) { + return { + status: 'INACTIVE', + status_detailed: 'banned' + } + } + + return { + status: 'INACTIVE', + status_detailed: 'inactive' + } +} + +module.exports = { + atoms, + proposalBeginTime, + proposalEndTime, + getDeposit, + getTotalVotePercentage, + getValidatorStatus +} diff --git a/lib/resolvers.js b/lib/resolvers.js index 9b5eb29479..10a7735e06 100644 --- a/lib/resolvers.js +++ b/lib/resolvers.js @@ -2,11 +2,11 @@ const { blockAdded, userTransactionAdded } = require('./chain-pubsub') function selectFrom(dataSources, networkId) { switch (networkId) { - case 'cosmoshub': + case 'cosmos-hub-mainnet': return dataSources.CosmosHubMainnetAPI - case 'gaia-testnet': + case 'cosmos-hub-testnet': return dataSources.CosmosHubTestnetAPI - case 'testnet': + case 'local-cosmos-hub-testnet': return dataSources.TestnetAPI } } From 1c0480b396870e556bccc3d554f9d5952f3a5a55 Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Mon, 21 Oct 2019 13:59:43 -0400 Subject: [PATCH 12/19] extract addresses in gaia testnet --- lib/cosmosV2-source.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/cosmosV2-source.js b/lib/cosmosV2-source.js index 08140aa42c..1fafd12290 100644 --- a/lib/cosmosV2-source.js +++ b/lib/cosmosV2-source.js @@ -37,8 +37,28 @@ class CosmosV2API extends CosmosV0API { } extractInvolvedAddresses(transaction) { - console.log('TODO extractInvolvedAddresses', transaction) - return [] + // extract all addresses from events that are either sender or recipient + const involvedAddresses = transaction.events.reduce( + (involvedAddresses, event) => { + const senderAttribute = event.attributes.find( + ({ key }) => key === 'sender' + ) + if (senderAttribute) { + involvedAddresses.push(senderAttribute.value) + } + + const recipientAttribute = event.attributes.find( + ({ key }) => key === 'recipient' + ) + if (recipientAttribute) { + involvedAddresses.push(recipientAttribute.value) + } + + return involvedAddresses + }, + [] + ) + return involvedAddresses } async getRewards(delegatorAddress) { From 9f0e1fed54483cdbacca265f84d1c05dfe66334f Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Mon, 21 Oct 2019 14:25:26 -0400 Subject: [PATCH 13/19] removed dead file --- lib/reducers/gaia-reducer-helpers.js | 83 ---------------------------- 1 file changed, 83 deletions(-) delete mode 100644 lib/reducers/gaia-reducer-helpers.js diff --git a/lib/reducers/gaia-reducer-helpers.js b/lib/reducers/gaia-reducer-helpers.js deleted file mode 100644 index e1d43472e1..0000000000 --- a/lib/reducers/gaia-reducer-helpers.js +++ /dev/null @@ -1,83 +0,0 @@ -const BigNumber = require('bignumber.js') - -function proposalBeginTime(proposal) { - switch (proposal.proposal_status.toLowerCase()) { - case 'depositperiod': - return proposal.submit_time - case 'votingperiod': - return proposal.voting_start_time - case 'passed': - case 'rejected': - return proposal.voting_end_time - } -} - -function proposalEndTime(proposal) { - switch (proposal.proposal_status.toLowerCase()) { - case 'depositperiod': - return proposal.voting_start_time - case 'votingperiod': - // the end time lives in the past already if the proposal is finalized - case 'passed': - case 'rejected': - return proposal.voting_end_time - } -} - -function atoms(nanoAtoms) { - return BigNumber(nanoAtoms) - .div(1000000) - .toFixed(6) -} - -// reduce deposits to one number and filter by required denom -function getDeposit(proposal, bondDenom) { - return atoms( - proposal.total_deposit.reduce( - (sum, cur) => sum.plus(cur.denom === bondDenom ? cur.amount : 0), - BigNumber(0) - ) - ) -} - -function getTotalVotePercentage(proposal, totalBondedTokens, totalVoted) { - // for passed proposals we can't calculate the total voted percentage, as we don't know the totalBondedTokens in the past - if (['Passed', 'Rejected'].indexOf(proposal.proposal_status) !== -1) return -1 - if (totalVoted.eq(0)) return 0 - if (!totalBondedTokens) return -1 - return BigNumber(totalBondedTokens) - .div(atoms(totalVoted)) - .toNumber() -} - -function getValidatorStatus(validator) { - if (validator.status === 2) { - return { - status: 'ACTIVE', - status_detailed: 'active' - } - } - if ( - validator.signing_info && - new Date(validator.signing_info.jailed_until) > new Date(9000, 1, 1) - ) { - return { - status: 'INACTIVE', - status_detailed: 'banned' - } - } - - return { - status: 'INACTIVE', - status_detailed: 'inactive' - } -} - -module.exports = { - atoms, - proposalBeginTime, - proposalEndTime, - getDeposit, - getTotalVotePercentage, - getValidatorStatus -} From 656ac8d2688f3c92e8fb43b695535b0d625bb4aa Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Mon, 21 Oct 2019 15:34:44 -0400 Subject: [PATCH 14/19] change schema names in db access queries --- lib/luniedb-source.js | 2 +- lib/queries.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/luniedb-source.js b/lib/luniedb-source.js index 88d45b5040..99062af0d9 100644 --- a/lib/luniedb-source.js +++ b/lib/luniedb-source.js @@ -49,7 +49,7 @@ class LunieDBAPI extends RESTDataSource { ? `(where: {operator_address: {_eq: "${validatorId}"}})` : '' return await this.getData( - networkID.replace('-', '_') + '_validatorprofiles', + networkID.replace(/-/g, '_') + '_validatorprofiles', selection ) } diff --git a/lib/queries.js b/lib/queries.js index 23a8c467f5..099bc33626 100644 --- a/lib/queries.js +++ b/lib/queries.js @@ -37,9 +37,9 @@ const queries = { } } `, - gaia_testnet_validatorprofiles: selection => ` + cosmos_hub_testnet_validatorprofiles: selection => ` query { - gaia_testnet_validatorprofiles${selection} { + cosmos_hub_testnet_validatorprofiles${selection} { details name picture @@ -48,9 +48,9 @@ const queries = { } } `, - cosmoshub_validatorprofiles: selection => ` + cosmos_hub_mainnet_validatorprofiles: selection => ` query { - cosmoshub_validatorprofiles${selection} { + cosmos_hub_mainnet_validatorprofiles${selection} { details name picture From 23b64717834b4ebbc11c59b9ac504573427e6ed6 Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Mon, 21 Oct 2019 18:02:26 -0400 Subject: [PATCH 15/19] refactoring of rewards --- lib/cosmosV0-source.js | 6 +-- lib/reducers/cosmosV0-reducers.js | 73 +++++++++++++++++++++++++++++-- lib/reducers/cosmosV2-reducers.js | 16 +++++-- lib/tools.js | 42 ------------------ 4 files changed, 85 insertions(+), 52 deletions(-) diff --git a/lib/cosmosV0-source.js b/lib/cosmosV0-source.js index 57363147a7..5f0c6974b0 100644 --- a/lib/cosmosV0-source.js +++ b/lib/cosmosV0-source.js @@ -150,9 +150,9 @@ class CosmosAPI extends RESTDataSource { validators.forEach(validator => { const consensusAddress = pubkeyToAddress(validator.consensus_pubkey) validator.voting_power = consensusValidators[consensusAddress] - ? BigNumber(consensusValidators[consensusAddress].voting_power).div( - totalVotingPower - ) + ? BigNumber(consensusValidators[consensusAddress].voting_power) + .div(totalVotingPower) + .toNumber() : 0 validator.expected_returns = expectedReturns(validator, annualProvision) validator.signing_info = signingInfos[consensusAddress] diff --git a/lib/reducers/cosmosV0-reducers.js b/lib/reducers/cosmosV0-reducers.js index 9da19a2445..dfd619b9f3 100644 --- a/lib/reducers/cosmosV0-reducers.js +++ b/lib/reducers/cosmosV0-reducers.js @@ -1,4 +1,3 @@ -const { calculateTokens } = require('../tools') const { cosmosMessageType } = require('../message-types') const BigNumber = require('bignumber.js') const _ = require('lodash') @@ -33,6 +32,62 @@ function atoms(nanoAtoms) { .toFixed(6) } +const calculateTokens = (validator, shares) => { + // this is the based on the idea that tokens should equal + // (myShares / totalShares) * totalTokens where totalShares + // and totalTokens are both represented as fractions + const myShares = new BigNumber(shares || 0) + const totalShares = new BigNumber(validator.delegatorShares) + const totalTokens = new BigNumber(validator.tokens) + + if (totalShares.eq(0)) return new BigNumber(0) + return myShares + .times(totalTokens) + .div(totalShares) + .toFixed(6) +} + +// share of all provisioned block rewards all delegators of this validator get +const provisionShare = (votingPower, commission) => { + const validatorProvisionShare = votingPower + const delegatorProvisionShare = validatorProvisionShare * (1 - commission) + + return delegatorProvisionShare +} + +// expected rewards if delegator stakes x tokens +const expectedRewards = ( + validator, + commission, + annualProvision, + delegatedTokens +) => { + if (validator.status === 0 || validator.jailed === true) { + return 0 + } + const delegatorProvisionShare = provisionShare( + validator.voting_power, + commission + ) + const annualAllDelegatorRewards = delegatorProvisionShare * annualProvision + const annualDelegatorRewardsShare = delegatedTokens / validator.tokens + const annualDelegatorRewards = + annualDelegatorRewardsShare * annualAllDelegatorRewards + return annualDelegatorRewards +} + +const expectedReturns = (validator, annualProvision, commission) => { + const standardizedTokenamount = 10000000000 + return ( + expectedRewards( + validator, + commission, + annualProvision, + standardizedTokenamount + ) / standardizedTokenamount + ) +} + // reduce deposits to one number and filter by required denom function getDeposit(proposal, bondDenom) { return atoms( @@ -114,7 +169,12 @@ function getValidatorStatus(validator) { } } -function validatorReducer(networkId, signedBlocksWindow, validator) { +function validatorReducer( + networkId, + signedBlocksWindow, + validator, + annualProvision +) { const statusInfo = getValidatorStatus(validator) let websiteURL = validator.description.website if (!websiteURL || websiteURL === '[do-not-modify]') { @@ -148,7 +208,11 @@ function validatorReducer(networkId, signedBlocksWindow, validator) { status: statusInfo.status, statusDetailed: statusInfo.status_detailed, delegatorShares: validator.delegator_shares, // needed to calculate delegation token amounts from shares - expectedReturns: validator.expected_returns + expectedReturns: expectedReturns( + validator, + annualProvision, + validator.commission.rate + ) } } @@ -308,5 +372,6 @@ module.exports = { proposalEndTime, getDeposit, getTotalVotePercentage, - getValidatorStatus + getValidatorStatus, + expectedReturns } diff --git a/lib/reducers/cosmosV2-reducers.js b/lib/reducers/cosmosV2-reducers.js index 64435a3da8..c049cef1d7 100644 --- a/lib/reducers/cosmosV2-reducers.js +++ b/lib/reducers/cosmosV2-reducers.js @@ -5,7 +5,8 @@ const { getDeposit, tallyReducer, atoms, - getValidatorStatus + getValidatorStatus, + expectedReturns } = cosmosV0Reducers function proposalReducer(networkId, proposal, totalBondedTokens) { @@ -33,7 +34,12 @@ function delegationReducer(delegation, validator) { } } -function validatorReducer(networkId, signedBlocksWindow, validator) { +function validatorReducer( + networkId, + signedBlocksWindow, + validator, + annualProvision +) { const statusInfo = getValidatorStatus(validator) let websiteURL = validator.description.website if (!websiteURL || websiteURL === '[do-not-modify]') { @@ -71,7 +77,11 @@ function validatorReducer(networkId, signedBlocksWindow, validator) { status: statusInfo.status, statusDetailed: statusInfo.status_detailed, delegatorShares: validator.delegator_shares, // needed to calculate delegation token amounts from shares - expectedReturns: validator.expected_returns + expectedReturns: expectedReturns( + validator, + annualProvision, + validator.commission.commission_rates.rate + ) } } diff --git a/lib/tools.js b/lib/tools.js index cb296a0b61..e4bbecd04a 100644 --- a/lib/tools.js +++ b/lib/tools.js @@ -1,5 +1,4 @@ const bech32 = require('bech32') -const BigNumber = require('bignumber.js') const crypto = require('crypto') const hexToValidatorAddress = address => { @@ -25,26 +24,6 @@ const pubkeyToAddress = cosmosvalconspub => { return hexToValidatorAddress(hexAddress) } -// expected rewards if delegator stakes x tokens -const expectedRewards = (validator, annualProvision, delegatedTokens) => { - if (validator.status === 0 || validator.jailed === true) { - return 0 - } - const delegatorProvisionShare = BigNumber(validator.voting_power) - const annualAllDelegatorRewards = delegatorProvisionShare.times( - annualProvision - ) - const annualDelegatorRewardsShare = BigNumber(delegatedTokens).div( - validator.tokens - ) - const annualDelegatorRewards = BigNumber(annualDelegatorRewardsShare).times( - annualAllDelegatorRewards - ) - return annualDelegatorRewards -} - -// simplified expected rewards with a fixed token amount - module.exports = { decodeB32(value) { const words = bech32.decode(value) @@ -54,26 +33,5 @@ module.exports = { const words = bech32.toWords(Buffer.from(value, type)) return bech32.encode(prefix, words) }, - expectedReturns(validator, annualProvision) { - const standardizedTokenamount = 10000000000 - return ( - expectedRewards(validator, annualProvision, standardizedTokenamount) / - standardizedTokenamount - ) - }, - calculateTokens(validator, shares) { - // this is the based on the idea that tokens should equal - // (myShares / totalShares) * totalTokens where totalShares - // and totalTokens are both represented as fractions - const myShares = new BigNumber(shares || 0) - const totalShares = new BigNumber(validator.delegatorShares) - const totalTokens = new BigNumber(validator.tokens) - - if (totalShares.eq(0)) return new BigNumber(0) - return myShares - .times(totalTokens) - .div(totalShares) - .toFixed(6) - }, pubkeyToAddress } From 881dba6065ed6b886a325e26b73448b9a1589c7b Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Mon, 21 Oct 2019 18:50:41 -0400 Subject: [PATCH 16/19] refactored rewards calculation --- lib/cosmosV0-source.js | 11 ++---- lib/reducers/cosmosV0-reducers.js | 57 +++++++++++-------------------- lib/reducers/cosmosV2-reducers.js | 8 ++--- 3 files changed, 26 insertions(+), 50 deletions(-) diff --git a/lib/cosmosV0-source.js b/lib/cosmosV0-source.js index 5f0c6974b0..95df0ba21f 100644 --- a/lib/cosmosV0-source.js +++ b/lib/cosmosV0-source.js @@ -3,12 +3,7 @@ const BigNumber = require('bignumber.js') const _ = require('lodash') const chainpubsub = require('./chain-pubsub') const { uniqWith, sortBy, reverse } = require('lodash') -const { - encodeB32, - decodeB32, - expectedReturns, - pubkeyToAddress -} = require('./tools') +const { encodeB32, decodeB32, pubkeyToAddress } = require('./tools') class CosmosAPI extends RESTDataSource { constructor(network) { @@ -154,7 +149,6 @@ class CosmosAPI extends RESTDataSource { .div(totalVotingPower) .toNumber() : 0 - validator.expected_returns = expectedReturns(validator, annualProvision) validator.signing_info = signingInfos[consensusAddress] }) @@ -162,7 +156,8 @@ class CosmosAPI extends RESTDataSource { this.reducers.validatorReducer( this.networkId, this.signedBlocksWindow, - validator + validator, + annualProvision ) ) } diff --git a/lib/reducers/cosmosV0-reducers.js b/lib/reducers/cosmosV0-reducers.js index dfd619b9f3..7f20eb8c14 100644 --- a/lib/reducers/cosmosV0-reducers.js +++ b/lib/reducers/cosmosV0-reducers.js @@ -47,45 +47,26 @@ const calculateTokens = (validator, shares) => { .toFixed(6) } -// share of all provisioned block rewards all delegators of this validator get -const provisionShare = (votingPower, commission) => { - const validatorProvisionShare = votingPower - const delegatorProvisionShare = validatorProvisionShare * (1 - commission) - - return delegatorProvisionShare -} - +/* if you don't get this, write fabian@lunie.io */ // expected rewards if delegator stakes x tokens -const expectedRewards = ( - validator, - commission, - annualProvision, - delegatedTokens -) => { +const expectedRewardsPerToken = (validator, commission, annualProvision) => { if (validator.status === 0 || validator.jailed === true) { return 0 } - const delegatorProvisionShare = provisionShare( - validator.voting_power, - commission - ) - const annualAllDelegatorRewards = delegatorProvisionShare * annualProvision - const annualDelegatorRewardsShare = delegatedTokens / validator.tokens - const annualDelegatorRewards = - annualDelegatorRewardsShare * annualAllDelegatorRewards - return annualDelegatorRewards -} -const expectedReturns = (validator, annualProvision, commission) => { - const standardizedTokenamount = 10000000000 - return ( - expectedRewards( - validator, - commission, - annualProvision, - standardizedTokenamount - ) / standardizedTokenamount - ) + // share of all provisioned block rewards all delegators of this validator get + const totalAnnualValidatorRewards = validator.voting_power * annualProvision + // the validator takes a cut in amount of the commission + const totalAnnualDelegatorRewards = + totalAnnualValidatorRewards * (1 - commission) + + // validator.tokens is the amount of all tokens delegated to that validator + // one token delegated would receive x percentage of all delegator rewards + const delegatorSharePerToken = 1 / validator.tokens + const annualDelegatorRewardsPerToken = + totalAnnualDelegatorRewards * delegatorSharePerToken + + return annualDelegatorRewardsPerToken } // reduce deposits to one number and filter by required denom @@ -208,10 +189,10 @@ function validatorReducer( status: statusInfo.status, statusDetailed: statusInfo.status_detailed, delegatorShares: validator.delegator_shares, // needed to calculate delegation token amounts from shares - expectedReturns: expectedReturns( + expectedReturns: expectedRewardsPerToken( validator, - annualProvision, - validator.commission.rate + validator.commission.rate, + annualProvision ) } } @@ -373,5 +354,5 @@ module.exports = { getDeposit, getTotalVotePercentage, getValidatorStatus, - expectedReturns + expectedRewardsPerToken } diff --git a/lib/reducers/cosmosV2-reducers.js b/lib/reducers/cosmosV2-reducers.js index c049cef1d7..646f9f9d9a 100644 --- a/lib/reducers/cosmosV2-reducers.js +++ b/lib/reducers/cosmosV2-reducers.js @@ -6,7 +6,7 @@ const { tallyReducer, atoms, getValidatorStatus, - expectedReturns + expectedRewardsPerToken } = cosmosV0Reducers function proposalReducer(networkId, proposal, totalBondedTokens) { @@ -77,10 +77,10 @@ function validatorReducer( status: statusInfo.status, statusDetailed: statusInfo.status_detailed, delegatorShares: validator.delegator_shares, // needed to calculate delegation token amounts from shares - expectedReturns: expectedReturns( + expectedReturns: expectedRewardsPerToken( validator, - annualProvision, - validator.commission.commission_rates.rate + validator.commission.commission_rates.rate, + annualProvision ) } } From ce03d57391dd04b05c83facb50ae3efa110935ec Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Mon, 21 Oct 2019 19:04:26 -0400 Subject: [PATCH 17/19] fixed sorting --- lib/resolvers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/resolvers.js b/lib/resolvers.js index 10a7735e06..5a35064dad 100644 --- a/lib/resolvers.js +++ b/lib/resolvers.js @@ -38,7 +38,7 @@ async function validators(_, { networkId, all, query }, { dataSources }) { } if (query) { validators = validators.filter(({ name, operatorAddress }) => { - return name.indexOf(query) !== -1 || operatorAddress.indexOf(query) + return name.indexOf(query) !== -1 || operatorAddress.indexOf(query) !== -1 }) } From ff12b3b3802b2c353b45d3a51deda48bcfbdfc33 Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Mon, 21 Oct 2019 21:07:26 -0400 Subject: [PATCH 18/19] added testnet hacks --- lib/luniedb-source.js | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/lib/luniedb-source.js b/lib/luniedb-source.js index 99062af0d9..95bb1c8774 100644 --- a/lib/luniedb-source.js +++ b/lib/luniedb-source.js @@ -2,6 +2,32 @@ const { RESTDataSource } = require('apollo-datasource-rest') const config = require('../config') const queries = require('./queries') +const testnet = { + id: 'local-cosmos-hub-testnet', + title: 'Local Cosmos Testnet', + chain_id: 'testnet', + rpc_url: 'http://localhost:26657', + api_url: 'http://localhost:9071', + bech32_prefix: 'cosmos', + testnet: true, + feature_session: true, + feature_portfolio: true, + feature_validators: true, + feature_proposals: true, + feature_activity: true, + feature_explorer: true, + action_send: true, + action_claim_rewards: true, + action_delegate: true, + action_redelegate: true, + action_undelegate: true, + action_deposit: true, + action_vote: true, + action_proposal: true, + experimental: true, + stakingDenom: 'STAKE' +} + class LunieDBAPI extends RESTDataSource { constructor() { super() @@ -30,12 +56,20 @@ class LunieDBAPI extends RESTDataSource { async getNetworks() { const response = await this.getData('networks') + + if (config.enableTestnet) { + response.push(testnet) + } + return response } async getNetwork(networkID) { + if (networkID === testnet.id) return testnet + const selection = networkID ? `(where: {id: {_eq: "${networkID}"}})` : '' const response = await this.getData('networks', selection) + return response && response.length ? response[0] : false } @@ -45,6 +79,10 @@ class LunieDBAPI extends RESTDataSource { } async getValidatorInfoByAddress(validatorId, networkID) { + if (networkID === 'local-cosmos-hub-testnet') { + return [] + } + const selection = networkID ? `(where: {operator_address: {_eq: "${validatorId}"}})` : '' From bd93fa6be6d4f6c897f1bc3168ad959f3863eb0e Mon Sep 17 00:00:00 2001 From: Fabian Weber Date: Mon, 21 Oct 2019 21:07:35 -0400 Subject: [PATCH 19/19] fixed deposit denom --- lib/cosmosV0-source.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/cosmosV0-source.js b/lib/cosmosV0-source.js index 95df0ba21f..bcaa65de41 100644 --- a/lib/cosmosV0-source.js +++ b/lib/cosmosV0-source.js @@ -146,8 +146,8 @@ class CosmosAPI extends RESTDataSource { const consensusAddress = pubkeyToAddress(validator.consensus_pubkey) validator.voting_power = consensusValidators[consensusAddress] ? BigNumber(consensusValidators[consensusAddress].voting_power) - .div(totalVotingPower) - .toNumber() + .div(totalVotingPower) + .toNumber() : 0 validator.signing_info = signingInfos[consensusAddress] }) @@ -222,7 +222,7 @@ class CosmosAPI extends RESTDataSource { votingThreshold: tallyingParamers.threshold, vetoThreshold: tallyingParamers.veto, // for now assuming one deposit denom - depositDenom: 'ATOM', + depositDenom: depositParameters.min_deposit[0].denom.toUpperCase(), depositThreshold: BigNumber(depositParameters.min_deposit[0].amount).div( 1000000 )