diff --git a/data/network-capabilities.js b/data/network-capabilities.js index e874191806..7dbbd826af 100644 --- a/data/network-capabilities.js +++ b/data/network-capabilities.js @@ -1,139 +1,175 @@ +const capabilityEnum = { ENABLED: 'ENABLED', DISABLED: 'DISABLED', MISSING: 'MISSING' } + const getNetworkCapabilities = { "cosmos-hub-mainnet": { - feature_session: true, - feature_explore: 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 + feature_session: capabilityEnum.ENABLED, + feature_explore: capabilityEnum.ENABLED, + feature_portfolio: capabilityEnum.ENABLED, + feature_validators: capabilityEnum.ENABLED, + feature_proposals: capabilityEnum.ENABLED, + feature_activity: capabilityEnum.ENABLED, + feature_explorer: capabilityEnum.ENABLED, + action_send: capabilityEnum.ENABLED, + action_claim_rewards: capabilityEnum.ENABLED, + action_delegate: capabilityEnum.ENABLED, + action_redelegate: capabilityEnum.ENABLED, + action_undelegate: capabilityEnum.ENABLED, + action_deposit: capabilityEnum.ENABLED, + action_vote: capabilityEnum.ENABLED, + action_proposal: capabilityEnum.ENABLED }, "cosmos-hub-testnet": { - feature_session: true, - feature_explore: 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, + feature_session: capabilityEnum.ENABLED, + feature_explore: capabilityEnum.ENABLED, + feature_portfolio: capabilityEnum.ENABLED, + feature_validators: capabilityEnum.ENABLED, + feature_proposals: capabilityEnum.ENABLED, + feature_activity: capabilityEnum.ENABLED, + feature_explorer: capabilityEnum.ENABLED, + action_send: capabilityEnum.ENABLED, + action_claim_rewards: capabilityEnum.ENABLED, + action_delegate: capabilityEnum.ENABLED, + action_redelegate: capabilityEnum.ENABLED, + action_undelegate: capabilityEnum.ENABLED, + action_deposit: capabilityEnum.ENABLED, + action_vote: capabilityEnum.ENABLED, + action_proposal: capabilityEnum.ENABLED, }, "terra-mainnet": { - feature_session: true, - feature_explore: 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: false, - action_vote: false, - action_proposal: false + feature_session: capabilityEnum.ENABLED, + feature_explore: capabilityEnum.ENABLED, + feature_portfolio: capabilityEnum.ENABLED, + feature_validators: capabilityEnum.ENABLED, + feature_proposals: capabilityEnum.ENABLED, + feature_activity: capabilityEnum.ENABLED, + feature_explorer: capabilityEnum.ENABLED, + action_send: capabilityEnum.ENABLED, + action_claim_rewards: capabilityEnum.ENABLED, + action_delegate: capabilityEnum.ENABLED, + action_redelegate: capabilityEnum.ENABLED, + action_undelegate: capabilityEnum.ENABLED, + action_deposit: capabilityEnum.ENABLED, + action_vote: capabilityEnum.ENABLED, + action_proposal: capabilityEnum.ENABLED }, "terra-testnet": { - feature_session: true, - feature_explore: true, - feature_portfolio: true, - feature_validators: true, - feature_proposals: false, - feature_activity: true, - feature_explorer: true, - action_send: true, - action_claim_rewards: true, - action_delegate: true, - action_redelegate: true, - action_undelegate: true, - action_deposit: false, - action_vote: false, - action_proposal: false + feature_session: capabilityEnum.ENABLED, + feature_explore: capabilityEnum.ENABLED, + feature_portfolio: capabilityEnum.ENABLED, + feature_validators: capabilityEnum.ENABLED, + feature_proposals: capabilityEnum.ENABLED, + feature_activity: capabilityEnum.ENABLED, + feature_explorer: capabilityEnum.ENABLED, + action_send: capabilityEnum.ENABLED, + action_claim_rewards: capabilityEnum.ENABLED, + action_delegate: capabilityEnum.ENABLED, + action_redelegate: capabilityEnum.ENABLED, + action_undelegate: capabilityEnum.ENABLED, + action_deposit: capabilityEnum.ENABLED, + action_vote: capabilityEnum.ENABLED, + action_proposal: capabilityEnum.ENABLED + }, + "emoney-mainnet": { + feature_session: capabilityEnum.ENABLED, + feature_explore: capabilityEnum.ENABLED, + feature_portfolio: capabilityEnum.ENABLED, + feature_validators: capabilityEnum.ENABLED, + feature_proposals: capabilityEnum.MISSING, + feature_activity: capabilityEnum.ENABLED, + feature_explorer: capabilityEnum.ENABLED, + action_send: capabilityEnum.ENABLED, + action_claim_rewards: capabilityEnum.ENABLED, + action_delegate: capabilityEnum.ENABLED, + action_redelegate: capabilityEnum.ENABLED, + action_undelegate: capabilityEnum.ENABLED, + action_deposit: capabilityEnum.MISSING, + action_vote: capabilityEnum.MISSING, + action_proposal: capabilityEnum.MISSING }, "emoney-mainnet": { - feature_session: true, - feature_explore: true, - feature_portfolio: true, - feature_validators: true, - feature_proposals: false, - feature_activity: true, - feature_explorer: true, - action_send: true, - action_claim_rewards: true, - action_delegate: true, - action_redelegate: true, - action_undelegate: true, - action_deposit: false, - action_vote: false, - action_proposal: false + feature_session: capabilityEnum.ENABLED, + feature_explore: capabilityEnum.ENABLED, + feature_portfolio: capabilityEnum.ENABLED, + feature_validators: capabilityEnum.ENABLED, + feature_proposals: capabilityEnum.MISSING, + feature_activity: capabilityEnum.ENABLED, + feature_explorer: capabilityEnum.ENABLED, + action_send: capabilityEnum.ENABLED, + action_claim_rewards: capabilityEnum.ENABLED, + action_delegate: capabilityEnum.ENABLED, + action_redelegate: capabilityEnum.ENABLED, + action_undelegate: capabilityEnum.ENABLED, + action_deposit: capabilityEnum.MISSING, + action_vote: capabilityEnum.MISSING, + action_proposal: capabilityEnum.MISSING }, "emoney-testnet": { - feature_session: true, - feature_explore: true, - feature_portfolio: true, - feature_validators: true, - feature_proposals: false, - feature_activity: true, - feature_explorer: true, - action_send: true, - action_claim_rewards: true, - action_delegate: true, - action_redelegate: true, - action_undelegate: true, - action_deposit: false, - action_vote: false, - action_proposal: false + feature_session: capabilityEnum.ENABLED, + feature_explore: capabilityEnum.ENABLED, + feature_portfolio: capabilityEnum.ENABLED, + feature_validators: capabilityEnum.ENABLED, + feature_proposals: capabilityEnum.MISSING, + feature_activity: capabilityEnum.ENABLED, + feature_explorer: capabilityEnum.ENABLED, + action_send: capabilityEnum.ENABLED, + action_claim_rewards: capabilityEnum.ENABLED, + action_delegate: capabilityEnum.ENABLED, + action_redelegate: capabilityEnum.ENABLED, + action_undelegate: capabilityEnum.ENABLED, + action_deposit: capabilityEnum.MISSING, + action_vote: capabilityEnum.MISSING, + action_proposal: capabilityEnum.MISSING }, "polkadot-testnet": { - feature_session: true, - feature_explore: true, - feature_portfolio: true, - feature_validators: true, - feature_proposals: false, - feature_activity: false, - feature_explorer: false, - action_send: true, - action_claim_rewards: false, - action_delegate: true, - action_redelegate: true, - action_undelegate: true, - action_deposit: false, - action_vote: false, - action_proposal: false + feature_session: capabilityEnum.ENABLED, + feature_explore: capabilityEnum.ENABLED, + feature_portfolio: capabilityEnum.ENABLED, + feature_validators: capabilityEnum.ENABLED, + feature_proposals: capabilityEnum.DISABLED, + feature_activity: capabilityEnum.DISABLED, + feature_explorer: capabilityEnum.DISABLED, + action_send: capabilityEnum.ENABLED, + action_claim_rewards: capabilityEnum.DISABLED, + action_delegate: capabilityEnum.ENABLED, + action_redelegate: capabilityEnum.ENABLED, + action_undelegate: capabilityEnum.ENABLED, + action_deposit: capabilityEnum.DISABLED, + action_vote: capabilityEnum.DISABLED, + action_proposal: capabilityEnum.DISABLED + }, + "local-cosmos-hub-testnet": { + feature_explore: capabilityEnum.ENABLED, + feature_session: capabilityEnum.ENABLED, + feature_portfolio: capabilityEnum.ENABLED, + feature_validators: capabilityEnum.ENABLED, + feature_proposals: capabilityEnum.ENABLED, + feature_activity: capabilityEnum.ENABLED, + feature_explorer: capabilityEnum.ENABLED, + action_send: capabilityEnum.ENABLED, + action_claim_rewards: capabilityEnum.ENABLED, + action_delegate: capabilityEnum.ENABLED, + action_redelegate: capabilityEnum.ENABLED, + action_undelegate: capabilityEnum.ENABLED, + action_deposit: capabilityEnum.ENABLED, + action_vote: capabilityEnum.ENABLED, + action_proposal: capabilityEnum.ENABLED }, "akash-testnet": { - feature_session: true, - feature_explore: true, - feature_portfolio: true, - feature_validators: true, - feature_proposals: false, - feature_activity: false, - feature_explorer: false, - action_send: false, - action_claim_rewards: false, - action_delegate: false, - action_redelegate: false, - action_undelegate: false, - action_deposit: false, - action_vote: false, - action_proposal: false + feature_session: capabilityEnum.ENABLED, + feature_explore: capabilityEnum.ENABLED, + feature_portfolio: capabilityEnum.ENABLED, + feature_validators: capabilityEnum.ENABLED, + feature_proposals: capabilityEnum.DISABLED, + feature_activity: capabilityEnum.DISABLED, + feature_explorer: capabilityEnum.DISABLED, + action_send: capabilityEnum.DISABLED, + action_claim_rewards: capabilityEnum.DISABLED, + action_delegate: capabilityEnum.DISABLED, + action_redelegate: capabilityEnum.DISABLED, + action_undelegate: capabilityEnum.DISABLED, + action_deposit: capabilityEnum.DISABLED, + action_vote: capabilityEnum.DISABLED, + action_proposal: capabilityEnum.DISABLED } } diff --git a/data/network-coinlookups.js b/data/network-coinlookups.js new file mode 100644 index 0000000000..c3c2167eb4 --- /dev/null +++ b/data/network-coinlookups.js @@ -0,0 +1,100 @@ +const terraCoinLookup = [ + { + chainDenom: 'uluna', + viewDenom: 'LUNA', + chainToViewConversionFactor: 1e-6 + }, + { + chainDenom: 'ukrw', + viewDenom: 'KRT', + chainToViewConversionFactor: 1e-6 + }, + { + chainDenom: 'ukrw', + viewDenom: 'KRT', + chainToViewConversionFactor: 1e-6 + }, + { + chainDenom: 'umnt', + viewDenom: 'MNT', + chainToViewConversionFactor: 1e-6 + }, + { + chainDenom: 'usdr', + viewDenom: 'SDT', + chainToViewConversionFactor: 1e-6 + }, + { + chainDenom: 'uusd', + viewDenom: 'UST', + chainToViewConversionFactor: 1e-6 + } + ] +// in the case of e-Money, some tokens only exist in mainnet and viceversa, some tokens only exist in testnet +// still, this will probably soon change with the new testnet +// also it is just more simple to add them all in the same dictionary +const emoneyCoinLookup = [ + { + chainDenom: 'ungm', + viewDenom: 'NGM', + chainToViewConversionFactor: 1e-6 + }, + { + chainDenom: 'edkk', + viewDenom: 'eDKK', + chainToViewConversionFactor: 1e-6 + }, + { + chainDenom: 'enok', + viewDenom: 'eNOK', + chainToViewConversionFactor: 1e-6 + }, + { + chainDenom: 'esek', + viewDenom: 'eSEK', + chainToViewConversionFactor: 1e-6 + }, + { + chainDenom: 'echf', + viewDenom: 'eCHF', + chainToViewConversionFactor: 1e-6 + }, + { + chainDenom: 'eeur', + viewDenom: 'eEUR', + chainToViewConversionFactor: 1e-6 + }, + { + chainDenom: 'eusd', + viewDenom: 'eUSD', + chainToViewConversionFactor: 1e-6 + }, + { + chainDenom: 'ejpy', + viewDenom: 'eJPY', + chainToViewConversionFactor: 1e-6 + } + ] +const coinLookupDictionary = { + "cosmos-hub-mainnet": [{ + chainDenom: 'uatom', + viewDenom: 'ATOM', + chainToViewConversionFactor: 1e-6 + }], + "cosmos-hub-testnet": [{ + chainDenom: 'umuon', + viewDenom: 'MUON', + chainToViewConversionFactor: 1e-6 + }], + "terra-mainnet": terraCoinLookup, + "terra-testnet": terraCoinLookup, + "emoney-mainnet": emoneyCoinLookup, + "emoney-testnet": emoneyCoinLookup, + "polkadot-testnet": [{ + chainDenom: 'Planck', + viewDenom: 'KSM', + chainToViewConversionFactor: 1e-12 + }] +} + +module.exports = {coinLookupDictionary} \ No newline at end of file diff --git a/data/network-fees.js b/data/network-fees.js index 46c5e382b5..28963fe727 100644 --- a/data/network-fees.js +++ b/data/network-fees.js @@ -1,16 +1,36 @@ const { UserInputError } = require('apollo-server') -const transactionTypesSet = new Set(['SendTx', 'StakeTx', 'UnstakeTx', 'RestakeTx', 'ClaimRewardsTx', 'SubmitProposalTx', 'VoteTx', 'DepositTx', 'UnknownTx']) +const transactionTypesSet = new Set([ + 'SendTx', + 'StakeTx', + 'UnstakeTx', + 'RestakeTx', + 'ClaimRewardsTx', + 'SubmitProposalTx', + 'VoteTx', + 'DepositTx', + 'UnknownTx' +]) const getNetworkTransactionGasEstimates = (networkId, transactionType) => { - if (transactionType && !transactionTypesSet.has(transactionType)) { - throw new UserInputError(`Unrecognized transaction type. Valid transaction types are 'SendTx', 'StakeTx', 'UnstakeTx', 'RestakeTx', 'ClaimRewardsTx', 'SubmitProposalTx', 'VoteTx', 'DepositTx' and 'UnknownTx'`) - } - const networkGasEstimates = networkGasEstimatesDictionary[networkId] - if (!networkGasEstimates) { - throw new UserInputError(`Unrecognized network. Currently only Cosmos, Terra and e-Money do have a network fees set`) - } - return transactionType && networkGasEstimates[`${transactionType}`] ? networkGasEstimates[`${transactionType}`] : networkGasEstimates.default + if (transactionType && !transactionTypesSet.has(transactionType)) { + throw new UserInputError( + `Unrecognized transaction type. Valid transaction types are ${Array.from( + transactionTypesSet.keys() + ).join(', ')}.` + ) + } + const networkGasEstimates = networkGasEstimatesDictionary[networkId] + if (!networkGasEstimates) { + throw new UserInputError( + `Unrecognized network. Currently only ${Object.keys( + networkGasEstimatesDictionary + ).join(', ')} are supported` + ) + } + return transactionType && networkGasEstimates[`${transactionType}`] + ? networkGasEstimates[`${transactionType}`] + : networkGasEstimates.default } const terraGasEstimates = { @@ -19,26 +39,31 @@ const terraGasEstimates = { } const cosmosGasEstimates = { - default: 550000 + default: 550000 } const emoneyGasEstimates = { - default: 200000, - ClaimRewardsTx: 550000 + default: 200000, + ClaimRewardsTx: 550000 } const akashGasEstimates = { - default: 200000 + default: 200000 +} + +const polkadotGasEstimates = { + default: 0 } const networkGasEstimatesDictionary = { - "cosmos-hub-mainnet": cosmosGasEstimates, - "cosmos-hub-testnet": cosmosGasEstimates, - "terra-mainnet": terraGasEstimates, - "terra-testnet": terraGasEstimates, - "emoney-mainnet": emoneyGasEstimates, - "emoney-testnet": emoneyGasEstimates, - "akash-testnet": akashGasEstimates + 'cosmos-hub-mainnet': cosmosGasEstimates, + 'cosmos-hub-testnet': cosmosGasEstimates, + 'terra-mainnet': terraGasEstimates, + 'terra-testnet': terraGasEstimates, + 'emoney-mainnet': emoneyGasEstimates, + 'emoney-testnet': emoneyGasEstimates, + 'akash-testnet': akashGasEstimates, + 'polkadot-testnet': polkadotGasEstimates } -module.exports = {getNetworkTransactionGasEstimates} \ No newline at end of file +module.exports = { getNetworkTransactionGasEstimates } diff --git a/data/networks-local.js b/data/networks-local.js index c2b5329143..68701cb0b1 100644 --- a/data/networks-local.js +++ b/data/networks-local.js @@ -1,3 +1,4 @@ +const { getNetworkCapabilities } = require('./network-capabilities') const config = require('../config') module.exports = [ @@ -15,21 +16,7 @@ module.exports = [ source_class_name: 'source/cosmosV2-source', block_listener_class_name: 'block-listeners/cosmos-node-subscription', testnet: true, - feature_explore: 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, + ...getNetworkCapabilities[`local-cosmos-hub-testnet`], experimental: true, stakingDenom: 'STAKE', enabled: true, diff --git a/data/networks.js b/data/networks.js index 76380cf90f..adc586ea93 100644 --- a/data/networks.js +++ b/data/networks.js @@ -1,4 +1,5 @@ const { getNetworkCapabilities } = require('./network-capabilities') +const { coinLookupDictionary } = require('./network-coinlookups') module.exports = [ { @@ -19,13 +20,7 @@ module.exports = [ ...getNetworkCapabilities[`cosmos-hub-testnet`], default: false, stakingDenom: 'MUON', - coinLookup: [ - { - chainDenom: 'umuon', - viewDenom: 'MUON', - chainToViewConversionFactor: 1e-6 - } - ], + coinLookup: coinLookupDictionary[`cosmos-hub-testnet`], enabled: true, icon: 'https://app.lunie.io/img/networks/cosmos-hub-mainnet.png', slug: 'cosmos-hub-testnet', @@ -53,13 +48,7 @@ module.exports = [ ...getNetworkCapabilities[`cosmos-hub-mainnet`], default: true, stakingDenom: 'ATOM', - coinLookup: [ - { - chainDenom: 'uatom', - viewDenom: 'ATOM', - chainToViewConversionFactor: 1e-6 - } - ], + coinLookup: coinLookupDictionary[`cosmos-hub-mainnet`], enabled: true, icon: 'https://app.lunie.io/img/networks/cosmos-hub-mainnet.png', slug: 'cosmos-hub', @@ -87,13 +76,7 @@ module.exports = [ ...getNetworkCapabilities[`terra-mainnet`], default: false, stakingDenom: 'LUNA', - coinLookup: [ - { - chainDenom: 'uluna', - viewDenom: 'LUNA', - chainToViewConversionFactor: 1e-6 - } - ], + coinLookup: coinLookupDictionary[`terra-mainnet`], enabled: true, icon: 'https://app.lunie.io/img/networks/terra-mainnet.png', slug: 'terra' @@ -115,13 +98,7 @@ module.exports = [ ...getNetworkCapabilities[`terra-testnet`], default: false, stakingDenom: 'LUNA', - coinLookup: [ - { - chainDenom: 'uluna', - viewDenom: 'LUNA', - chainToViewConversionFactor: 1e-6 - } - ], + coinLookup: coinLookupDictionary[`terra-testnet`], enabled: true, icon: 'https://app.lunie.io/img/networks/terra-testnet.png', slug: 'terra-testnet' @@ -143,13 +120,7 @@ module.exports = [ ...getNetworkCapabilities[`emoney-mainnet`], default: false, stakingDenom: 'NGM', - coinLookup: [ - { - chainDenom: 'ungm', - viewDenom: 'NGM', - chainToViewConversionFactor: 1e-6 - } - ], + coinLookup: coinLookupDictionary[`emoney-mainnet`], enabled: true, icon: 'https://app.lunie.io/img/networks/emoney-mainnet.png', slug: 'emoney' @@ -171,13 +142,7 @@ module.exports = [ ...getNetworkCapabilities[`emoney-testnet`], default: false, stakingDenom: 'NGM', - coinLookup: [ - { - chainDenom: 'ungm', - viewDenom: 'NGM', - chainToViewConversionFactor: 1e-6 - } - ], + coinLookup: coinLookupDictionary[`emoney-testnet`], enabled: true, icon: 'https://app.lunie.io/img/networks/emoney-testnet.png', slug: 'emoney-testnet' @@ -200,46 +165,40 @@ module.exports = [ default: false, stakingDenom: 'KSM', // https://wiki.polkadot.network/docs/en/learn-DOT - coinLookup: [ - { - chainDenom: 'Planck', - viewDenom: 'KSM', - chainToViewConversionFactor: 1e-12 - } - ], + coinLookup: coinLookupDictionary[`polkadot-testnet`], enabled: false, icon: 'https://app.lunie.io/img/networks/polkadot-testnet.png', slug: 'kusama' - }, - { - id: 'akash-testnet', - title: 'Akash Testnet', - chain_id: 'devnet', - api_url: 'http://95.179.133.80:8080', - rpc_url: 'wss://95.179.133.80:26657/websocket', - bech32_prefix: 'akash', - address_prefix: 'akash', - address_creator: 'cosmos', - ledger_app: 'cosmos', - network_type: 'cosmos', - source_class_name: 'source/akashV0-source', - block_listener_class_name: 'block-listeners/cosmos-node-subscription', - testnet: true, - ...getNetworkCapabilities[`akash-testnet`], - default: false, - stakingDenom: 'STAKE', // this is only in my private testnet - coinLookup: [ - { - chainDenom: 'uakt', - viewDenom: 'AKT', - chainToViewConversionFactor: 1e-6 - } - ], - enabled: false, - icon: 'https://app.lunie.io/img/networks/akash-testnet.png', - slug: 'akash-testnet' } // { + // id: 'akash-testnet', + // title: 'Akash Testnet', + // chain_id: 'devnet', + // api_url: 'http://95.179.133.80:8080', + // rpc_url: 'wss://95.179.133.80:26657/websocket', + // bech32_prefix: 'akash', + // address_prefix: 'akash', + // address_creator: 'cosmos', + // ledger_app: 'cosmos', + // network_type: 'cosmos', + // source_class_name: 'source/akashV0-source', + // block_listener_class_name: 'block-listeners/cosmos-node-subscription', + // testnet: true, + // ...getNetworkCapabilities[`akash-testnet`], + // default: false, + // stakingDenom: 'STAKE', // this is only in my private testnet + // coinLookup: [ + // { + // chainDenom: 'uakt', + // viewDenom: 'AKT', + // chainToViewConversionFactor: 1e-6 + // } + // ], + // enabled: false, + // icon: 'https://app.lunie.io/img/networks/akash-testnet.png', + // slug: 'akash-testnet' + // } + // { // id: 'livepeer-mainnet', // title: 'Livepeer', // chain_id: 'ethereum-1', diff --git a/lib/block-listeners/polkadot-node-subscription.js b/lib/block-listeners/polkadot-node-subscription.js index bcb7303450..7034a02d81 100644 --- a/lib/block-listeners/polkadot-node-subscription.js +++ b/lib/block-listeners/polkadot-node-subscription.js @@ -25,7 +25,9 @@ class PolkadotNodeSubscription { this.db = new database(config)(networkSchemaName) this.height = 0 this.currentSessionIndex = 0 + this.currentEra = 0 this.blockQueue = [] + this.chainId = `kusama-cc3` // TODO make dynamic this.subscribeForNewBlock() } @@ -123,7 +125,26 @@ class PolkadotNodeSubscription { `\x1b[36mCurrent session index is ${block.sessionIndex}, fetching validators!\x1b[0m` ) this.currentSessionIndex = block.sessionIndex - this.sessionValidators = await this.polkadotAPI.getAllValidators() + const [sessionValidators, era] = await Promise.all([ + this.polkadotAPI.getAllValidators(), + this.api.query.staking.currentEra().then(async era => { + return Number(era.toHuman()) + }) + ]) + this.sessionValidators = sessionValidators + + if (this.currentEra < era || this.currentEra === 0) { + console.log( + `\x1b[36mCurrent staking era is ${era}, fetching rewards!\x1b[0m` + ) + this.currentEra = era + + if (!(await this.eraRewardsExist(era))) { + this.updateRewards(era, this.sessionValidators[0].chainId).catch( + console.error + ) + } + } } this.updateDBValidatorProfiles(this.sessionValidators) @@ -146,7 +167,7 @@ class PolkadotNodeSubscription { }) }) } catch (error) { - console.error(`newBlockHandler failed`, JSON.stringify(error, null, 2)) + console.error(`newBlockHandler failed`, error.message) Sentry.captureException(error) } } @@ -180,7 +201,7 @@ class PolkadotNodeSubscription { name: v.name })) ] - // update only new onces + // update only new ones const validatorRows = newValidators.map( ({ operatorAddress, name, chainId }) => ({ operator_address: operatorAddress, @@ -190,6 +211,43 @@ class PolkadotNodeSubscription { ) return this.db.upsert('validatorprofiles', validatorRows) } + async updateRewards(era, chainId) { + console.time() + console.log('update rewards') + const rewards = await this.polkadotAPI.getEraRewards(era) // ATTENTION: era means "get all rewards of all eras since that era". putting a low number takes a long time. + console.timeEnd() + + console.time() + console.log('store rewards', rewards.length) + await this.storeRewards( + rewards + .filter(({ amount }) => amount > 0) + .map(({ amount, height, validatorAddress, denom, address }) => ({ + amount, + height, + validator: validatorAddress, + denom, + address + })), + era, + chainId + ) + console.timeEnd() + } + + storeRewards(rewards, chainId) { + return this.db.insert('rewards', rewards, undefined, chainId) // height is in the rewards rows already + } + + async eraRewardsExist(era) { + const response = await this.db.read( + 'rewards', + 'rewardsExitCheck', + ['amount'], + `limit:1 where:{height:{_eq:"${era}"}}` + ) + return response.length > 0 + } } module.exports = PolkadotNodeSubscription diff --git a/lib/block-store.js b/lib/block-store.js index c8e2d90aef..a6b48241d3 100644 --- a/lib/block-store.js +++ b/lib/block-store.js @@ -1,5 +1,5 @@ class BlockStore { - constructor(network) { + constructor(network, database) { this.network = network this.latestHeight = 0 this.block = {} @@ -13,22 +13,19 @@ class BlockStore { this.dataReady = new Promise(resolve => { this.resolveReady = resolve }) + this.db = database } update({ height, block = this.block, - stakingDenom = this.stakingDenom, - annualProvision = this.annualProvision, - signedBlocksWindow = this.signedBlocksWindow, - validators = this.validators + validators = this.validators, + data = this.data // multi purpose block to be used for any chain specific data }) { this.latestHeight = Number(height) this.block = block - this.stakingDenom = stakingDenom - this.annualProvision = Number(annualProvision) - this.signedBlocksWindow = Number(signedBlocksWindow) this.validators = validators + this.data = data this.resolveReady() } diff --git a/lib/network-container.js b/lib/network-container.js index e2abe56f2a..021cb90209 100644 --- a/lib/network-container.js +++ b/lib/network-container.js @@ -1,4 +1,11 @@ const BlockStore = require('./block-store') +const database = require('./database') +const config = require('../config') + +function createDBInstance(networkId) { + const networkSchemaName = networkId ? networkId.replace(/-/g, '_') : false + return new database(config)(networkSchemaName) +} /* This class handles creation and management of each network. @@ -10,6 +17,7 @@ class NetworkContainer { constructor(network) { this.network = network this.id = network.id + this.db = createDBInstance(network.id) this.requireSourceClass() this.requireSubscriptionClass() } @@ -20,7 +28,7 @@ class NetworkContainer { } createStore() { - this.store = new BlockStore(this.network) + this.store = new BlockStore(this.network, this.db) } requireSourceClass() { @@ -55,7 +63,8 @@ class NetworkContainer { this.blockListener = new this.subscriptionClass( this.network, this.sourceClass, - this.store + this.store, + this.db ) } } diff --git a/lib/reducers/cosmosV2-reducers.js b/lib/reducers/cosmosV2-reducers.js index 8787ec3021..38e557fab3 100644 --- a/lib/reducers/cosmosV2-reducers.js +++ b/lib/reducers/cosmosV2-reducers.js @@ -68,33 +68,27 @@ function unstakeDetailsReducer(message, reducers) { } } -function claimRewardsDetailsReducer( - message, - reducers, - transaction, - stakingDenom -) { +function claimRewardsDetailsReducer(message, reducers, transaction) { return { from: message.validators, - amounts: claimRewardsAmountReducer(transaction, reducers, stakingDenom) + amounts: claimRewardsAmountReducer(transaction, reducers) } } -function claimRewardsAmountReducer(transaction, reducers, stakingDenom) { - const isTxSucceded = - transaction.success || - transaction.logs.find(({ success }) => success === true) - if (!isTxSucceded) { - return [ - { - amount: 0, - denom: reducers.denomLookup(stakingDenom) - } - ] - } - const allClaimedRewards = transaction.events +function claimRewardsAmountReducer(transaction, reducers) { + // first collect all the claimed amounts from events + let transactionClaimEvents = transaction.events .find(event => event.type === `transfer`) .attributes.filter(attribute => attribute.key === `amount`) + // filter out unsuccessful messages + if (transaction.logs) { + transaction.logs.forEach((log, index) => { + if (log.success !== true) { + transactionClaimEvents.splice(index, 1) + } + }) + } + const allClaimedRewards = transactionClaimEvents .map(amount => amount.value) .map(rewardValue => reducers.rewardCoinReducer(rewardValue)) const aggregatedClaimRewardsObject = allClaimedRewards.reduce( @@ -129,22 +123,15 @@ function voteProposalDetailsReducer(message) { } } -// TO TEST! function depositDetailsReducer(message, reducers) { return { proposalId: message.proposal_id, - amount: reducers.coinReducer(message.amount) + amount: reducers.coinReducer(message.amount[0]) } } // function to map cosmos messages to our details format -function transactionDetailsReducer( - type, - message, - reducers, - transaction, - stakingDenom -) { +function transactionDetailsReducer(type, message, reducers, transaction) { let details switch (type) { case lunieMessageTypes.SEND: @@ -160,12 +147,7 @@ function transactionDetailsReducer( details = unstakeDetailsReducer(message, reducers) break case lunieMessageTypes.CLAIM_REWARDS: - details = claimRewardsDetailsReducer( - message, - reducers, - transaction, - stakingDenom - ) + details = claimRewardsDetailsReducer(message, reducers, transaction) break case lunieMessageTypes.SUBMIT_PROPOSAL: details = submitProposalDetailsReducer(message, reducers) @@ -222,7 +204,7 @@ function proposalReducer( } } -function transactionReducerV2(transaction, reducers, stakingDenom) { +function transactionReducerV2(transaction, reducers) { try { // TODO check if this is anywhere not an array let fees @@ -265,8 +247,7 @@ function transactionReducerV2(transaction, reducers, stakingDenom) { getMessageType(type), value, reducers, - transaction, - stakingDenom + transaction ), timestamp: transaction.timestamp, memo: transaction.tx.value.memo, @@ -293,15 +274,13 @@ function transactionReducerV2(transaction, reducers, stakingDenom) { } } -function transactionsReducerV2(txs, reducers, stakingDenom) { +function transactionsReducerV2(txs, reducers) { const duplicateFreeTxs = uniqWith(txs, (a, b) => a.txhash === b.txhash) const sortedTxs = sortBy(duplicateFreeTxs, ['timestamp']) const reversedTxs = reverse(sortedTxs) // here we filter out all transactions related to validators return reversedTxs.reduce((collection, transaction) => { - return collection.concat( - transactionReducerV2(transaction, reducers, stakingDenom) - ) + return collection.concat(transactionReducerV2(transaction, reducers)) }, []) } diff --git a/lib/reducers/polkadotV0-reducers.js b/lib/reducers/polkadotV0-reducers.js index b0139eedd6..9d1d24092e 100644 --- a/lib/reducers/polkadotV0-reducers.js +++ b/lib/reducers/polkadotV0-reducers.js @@ -9,7 +9,7 @@ function blockReducer( blockHash, sessionIndex, blockAuthor, - blockEvents + transactions ) { return { networkId, @@ -18,7 +18,7 @@ function blockReducer( hash: blockHash, sessionIndex, time: new Date().toISOString(), // TODO: Get from blockchain state - transactions: blockEvents, // TODO: IMPROVE! + transactions, proposer_address: blockAuthor } } @@ -62,14 +62,23 @@ function validatorReducer(network, validator) { function balanceReducer(network, balance, total) { // hack. We convert the balance into an Array to make it an Iterable + if (balance == 0) { + return [] + } return [ { - amount: BigNumber(balance) - .times(network.coinLookup[0].chainToViewConversionFactor) - .toFixed(4), - total: BigNumber(total) - .times(network.coinLookup[0].chainToViewConversionFactor) - .toFixed(4), + amount: + (BigNumber(balance) + .times(network.coinLookup[0].chainToViewConversionFactor) + .toFixed(4) * + 10000) / + 10000, + total: + (BigNumber(total) + .times(network.coinLookup[0].chainToViewConversionFactor) + .toFixed(4) * + 10000) / + 10000, denom: network.coinLookup[0].viewDenom } ] @@ -184,6 +193,47 @@ function extractInvolvedAddresses(extrinsic) { return [] } +// Flatten era rewards and then aggregate per validator +function rewardsReducer(network, validators, rewards, reducers) { + return rewards.reduce((collection, reward) => { + const validatorRewards = reducers.rewardReducer( + network, + validators, + reward, + reducers + ) + validatorRewards.forEach(validatorReward => { + const existingRewardCollectionForValidator = collection.find( + collectionReward => + collectionReward.validator === validatorReward.validator + ) + if (existingRewardCollectionForValidator) { + existingRewardCollectionForValidator.amount = + parseFloat(existingRewardCollectionForValidator.amount) + + parseFloat(validatorReward.amount) + } else { + collection.push(validatorReward) + } + }) + return collection + }, []) +} + +function rewardReducer(network, validators, reward, reducers) { + let parsedRewards = [] + Object.entries(reward.validators).map(validatorReward => { + const lunieReward = { + ...reducers.coinReducer(network, validatorReward[1].toString()), + height: reward.era, + address: reward.address, + validator: validators[validatorReward[0]], + validatorAddress: validatorReward[0] // added for writing the validator to the db even it it is not in the dictionary + } + parsedRewards.push(lunieReward) + }) + return parsedRewards +} + module.exports = { blockReducer, validatorReducer, @@ -193,5 +243,7 @@ module.exports = { transactionsReducerV2, transactionDetailsReducer, sendDetailsReducer, - coinReducer + coinReducer, + rewardReducer, + rewardsReducer } diff --git a/lib/resolvers.js b/lib/resolvers.js index 024a0b67bd..84a490f6b4 100644 --- a/lib/resolvers.js +++ b/lib/resolvers.js @@ -41,8 +41,8 @@ function localStore(dataSources, networkId) { } // TODO updating the validators with the profiles should happen already in the store so we don't query the db all the time -async function getValidatorInfosMap(networkId) { - const validatorInfo = await createDBInstance(networkId).getValidatorsInfo() +async function getValidatorInfosMap(store) { + const validatorInfo = await store.db.getValidatorsInfo() const validatorInfoMap = keyBy(validatorInfo, 'operator_address') return validatorInfoMap } @@ -71,7 +71,9 @@ async function validators( if (activeOnly) { validators = validators.filter(({ status }) => status === 'ACTIVE') } - const validatorInfoMap = await getValidatorInfosMap(networkId) + const validatorInfoMap = await getValidatorInfosMap( + localStore(dataSources, networkId) + ) validators = validators.map(validator => enrichValidator(validatorInfoMap[validator.operatorAddress], validator) ) @@ -89,9 +91,10 @@ async function validators( async function validator(_, { networkId, operatorAddress }, { dataSources }) { await localStore(dataSources, networkId).dataReady - const validatorInfo = await createDBInstance( + const validatorInfo = await localStore( + dataSources, networkId - ).getValidatorInfoByAddress(operatorAddress) + ).db.getValidatorInfoByAddress(operatorAddress) // here first we check if validators in localStore are an empty object // this is because sometimes it takes time to fetch them on startup @@ -140,7 +143,9 @@ async function delegations( dataSources, networkId ).getDelegationsForDelegatorAddress(delegatorAddress, validatorsDictionary) - const validatorInfoMap = await getValidatorInfosMap(networkId) + const validatorInfoMap = await getValidatorInfosMap( + localStore(dataSources, networkId) + ) return Promise.all( delegations.map(async delegation => ({ @@ -164,7 +169,9 @@ async function undelegations( dataSources, networkId ).getUndelegationsForDelegatorAddress(delegatorAddress, validatorsDictionary) - const validatorInfoMap = await getValidatorInfosMap(networkId) + const validatorInfoMap = await getValidatorInfosMap( + localStore(dataSources, networkId) + ) return Promise.all( undelegations.map(async undelegation => ({ @@ -189,10 +196,8 @@ const resolvers = { { dataSources } ) => { await localStore(dataSources, networkId).dataReady - const validatorsDictionary = localStore(dataSources, networkId).validators return remoteFetch(dataSources, networkId).getRewards( address, - validatorsDictionary, fiatCurrency ) }, @@ -202,10 +207,8 @@ const resolvers = { { dataSources } ) => { await localStore(dataSources, networkId).dataReady - const validatorsDictionary = localStore(dataSources, networkId).validators const rewards = await remoteFetch(dataSources, networkId).getRewards( address, - validatorsDictionary, fiatCurrency ) const stakingDenom = await remoteFetch( @@ -349,10 +352,8 @@ const resolvers = { { dataSources } ) => { await localStore(dataSources, networkId).dataReady - const validatorsDictionary = localStore(dataSources, networkId).validators let rewards = await remoteFetch(dataSources, networkId).getRewards( delegatorAddress, - validatorsDictionary, fiatCurrency ) diff --git a/lib/schema.js b/lib/schema.js index a5316dc84e..f559cffc88 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -115,6 +115,12 @@ const typeDefs = gql` chainToViewConversionFactor: Float! } + enum CapabilityEnum { + ENABLED + DISABLED + MISSING + } + type Network { id: String title: String @@ -127,21 +133,21 @@ const typeDefs = gql` network_type: String ledger_app: String testnet: Boolean - feature_session: Boolean - feature_explore: Boolean - feature_portfolio: Boolean - feature_validators: Boolean - feature_proposals: Boolean - feature_activity: Boolean - feature_explorer: Boolean - action_send: Boolean - action_claim_rewards: Boolean - action_delegate: Boolean - action_redelegate: Boolean - action_undelegate: Boolean - action_deposit: Boolean - action_vote: Boolean - action_proposal: Boolean + feature_session: CapabilityEnum! + feature_explore: CapabilityEnum! + feature_portfolio: CapabilityEnum! + feature_validators: CapabilityEnum! + feature_proposals: CapabilityEnum! + feature_activity: CapabilityEnum! + feature_explorer: CapabilityEnum! + action_send: CapabilityEnum! + action_claim_rewards: CapabilityEnum! + action_delegate: CapabilityEnum! + action_redelegate: CapabilityEnum! + action_undelegate: CapabilityEnum! + action_deposit: CapabilityEnum! + action_vote: CapabilityEnum! + action_proposal: CapabilityEnum! default: Boolean stakingDenom: String coinLookup: [coinLookup] diff --git a/lib/source/cosmosV0-source.js b/lib/source/cosmosV0-source.js index 53f79fed3c..5f868ced74 100644 --- a/lib/source/cosmosV0-source.js +++ b/lib/source/cosmosV0-source.js @@ -453,11 +453,11 @@ class CosmosV0API extends RESTDataSource { return expectedReturns } - async getRewards(delegatorAddress, validatorsDictionary) { + async getRewards(delegatorAddress) { this.checkAddress(delegatorAddress) const delegations = await this.getDelegationsForDelegatorAddress( delegatorAddress, - validatorsDictionary + this.store.validators ) const rewards = await Promise.all( delegations.map(async ({ validatorAddress, validator }) => ({ diff --git a/lib/source/cosmosV2-source.js b/lib/source/cosmosV2-source.js index 2757dd6414..d2e4013884 100644 --- a/lib/source/cosmosV2-source.js +++ b/lib/source/cosmosV2-source.js @@ -138,8 +138,7 @@ class CosmosV2API extends CosmosV0API { [].concat(...results) ) - const stakingDenom = await this.getStakingDenom() - return this.reducers.transactionsReducerV2(txs, this.reducers, stakingDenom) + return this.reducers.transactionsReducerV2(txs, this.reducers) } async getTransactionsByHeight(height) { @@ -151,7 +150,7 @@ class CosmosV2API extends CosmosV0API { : [] } - async getRewards(delegatorAddress, validatorsDictionary) { + async getRewards(delegatorAddress) { this.checkAddress(delegatorAddress) const result = await this.query( `distribution/delegators/${delegatorAddress}/rewards` @@ -162,7 +161,7 @@ class CosmosV2API extends CosmosV0API { .map(({ reward, validator_address }) => this.reducers.rewardReducer( reward[0], - validatorsDictionary[validator_address] + this.store.validators[validator_address] ) ) } diff --git a/lib/source/polkadotV0-source.js b/lib/source/polkadotV0-source.js index cf2f6eec71..4fb88147e6 100644 --- a/lib/source/polkadotV0-source.js +++ b/lib/source/polkadotV0-source.js @@ -159,13 +159,74 @@ class polkadotAPI { return null } - async getRewards() { - return null + async getRewards(delegatorAddress) { + // This is currently super slow if the account have many pending payouts (same as in Polkadot JS UI) + // Example execution times: + // - 10-15s for an account with 1 pending payout + // - Hangs API for an account with >= 20 eras pending payouts + const api = await this.getAPI() + const validators = this.store.validators + const stakingInfo = await api.derive.staking.query(delegatorAddress) + if (!stakingInfo.stakingLedger) { + // in this case we assume the delegator has no rewards + return [] + } + const lastEraReward = parseInt(stakingInfo.stakingLedger.lastReward) + 1 + const rewards = await api.derive.staking.stakerRewards( + delegatorAddress, + lastEraReward + ) + return this.reducers.rewardsReducer( + this.network, + validators, + rewards.map(reward => ({ + ...reward, + address: delegatorAddress + })), + this.reducers + ) + } + + getAllDelegators() { + const validators = this.store.validators + const delegators = [] + Object.values(validators).forEach(validator => { + validator.nominations.forEach(nomination => { + if (!delegators.find(address => address === nomination.who)) { + delegators.push(nomination.who) + } + }) + }) + + return delegators + } + + // ATTENTION: era since to putting a low number will take a long time + async getEraRewards(era) { + const api = await this.getAPI() + const delegators = this.getAllDelegators() + + const nestedRewards = await Promise.all( + delegators.map(async address => { + const rewards = await api.derive.staking.stakerRewards(address, era - 1) + return rewards.map(reward => ({ + ...reward, + address + })) + }) + ) + const rewards = [].concat(...nestedRewards) + + return this.reducers.rewardsReducer( + this.network, + this.store.validators, + rewards, + this.reducers + ) } async getOverview(delegatorAddress) { const accountBalances = await this.getBalancesFromAddress(delegatorAddress) - return { networkId: this.networkId, address: delegatorAddress, diff --git a/lib/source/terraV3-source.js b/lib/source/terraV3-source.js index dbbd26ec7e..f0ee6ba9e3 100644 --- a/lib/source/terraV3-source.js +++ b/lib/source/terraV3-source.js @@ -123,7 +123,7 @@ class TerraV3API extends CosmosV2API { } // Terra will be the root source for functions specific to Tendermint multidenom networks - async getRewards(delegatorAddress, validatorsDictionary, fiatCurrency) { + async getRewards(delegatorAddress, fiatCurrency) { this.checkAddress(delegatorAddress) const result = await this.query( `distribution/delegators/${delegatorAddress}/rewards` @@ -136,7 +136,7 @@ class TerraV3API extends CosmosV2API { : rewards return this.reducers.rewardReducer( rewardsWithFiatValue, - validatorsDictionary + this.store.validators ) } diff --git a/package.json b/package.json index 57aaaa81fa..6cacc5b808 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "license": "ISC", "dependencies": { "@hapi/joi": "^16.1.8", - "@polkadot/api": "^v1.8.0-beta.1", + "@polkadot/api": "^1.9.1", "@polkadot/util": "^2.6.1", "@sentry/node": "^5.9.0", "apollo-datasource-rest": "^0.6.6", @@ -59,4 +59,4 @@ "resolutions": { "minimist": "1.2.2" } -} \ No newline at end of file +} diff --git a/scripts/getOldPolkadotRewardEras.js b/scripts/getOldPolkadotRewardEras.js new file mode 100644 index 0000000000..0d6686956c --- /dev/null +++ b/scripts/getOldPolkadotRewardEras.js @@ -0,0 +1,55 @@ +const database = require('../lib/database') +const config = require('../config') +const db = database(config)('polkadot_testnet') +const { ApiPromise, WsProvider } = require('@polkadot/api') +const _ = require('lodash') + +async function initPolkadotRPC(network, store) { + console.time('init polkadot') + const api = new ApiPromise({ + provider: new WsProvider(network.rpc_url) + }) + store.polkadotRPC = api + await api.isReady + console.timeEnd('init polkadot') +} + +function storeRewards(rewards, chainId) { + return db.upsert('rewards', rewards, undefined, chainId) // height is in the rewards rows already +} + +async function main() { + const networks = require('../data/networks') + const network = networks.find(({ id }) => id === 'polkadot-testnet') + const PolkadotApiClass = require('../lib/' + network.source_class_name) + const store = {} + await initPolkadotRPC(network, store) + const polkadotAPI = new PolkadotApiClass(network, store) + + const validators = await polkadotAPI.getAllValidators() + store.validators = _.keyBy(validators, 'operatorAddress') + const delegators = await polkadotAPI.getAllDelegators() + + await Promise.all( + delegators.map(async delegator => { + const rewards = await polkadotAPI.getRewards(delegator) + const storableRewards = rewards + ? rewards.filter(({ amount }) => amount > 0) + : [] + if (storableRewards.length > 0) { + await storeRewards( + rewards.map(reward => ({ + amount: reward.amount, + height: reward.height, + denom: reward.denom, + address: reward.address, + validator: reward.validatorAddress + })), + validators[0].chainId + ) + } + }) + ) +} + +main() diff --git a/tests/network-configs.test.js b/tests/network-configs.test.js index 7e82a733be..fcddcf5e64 100644 --- a/tests/network-configs.test.js +++ b/tests/network-configs.test.js @@ -21,21 +21,21 @@ const schema = Joi.object({ source_class_name: Joi.string(), block_listener_class_name: Joi.string(), testnet: Joi.boolean(), - feature_explore: Joi.boolean(), - feature_session: Joi.boolean(), - feature_portfolio: Joi.boolean(), - feature_validators: Joi.boolean(), - feature_proposals: Joi.boolean(), - feature_activity: Joi.boolean(), - feature_explorer: Joi.boolean(), - action_send: Joi.boolean(), - action_claim_rewards: Joi.boolean(), - action_delegate: Joi.boolean(), - action_redelegate: Joi.boolean(), - action_undelegate: Joi.boolean(), - action_deposit: Joi.boolean(), - action_vote: Joi.boolean(), - action_proposal: Joi.boolean(), + feature_explore: Joi.string().valid('ENABLED', 'DISABLED', 'MISSING'), + feature_session: Joi.string().valid('ENABLED', 'DISABLED', 'MISSING'), + feature_portfolio: Joi.string().valid('ENABLED', 'DISABLED', 'MISSING'), + feature_validators: Joi.string().valid('ENABLED', 'DISABLED', 'MISSING'), + feature_proposals: Joi.string().valid('ENABLED', 'DISABLED', 'MISSING'), + feature_activity: Joi.string().valid('ENABLED', 'DISABLED', 'MISSING'), + feature_explorer: Joi.string().valid('ENABLED', 'DISABLED', 'MISSING'), + action_send: Joi.string().valid('ENABLED', 'DISABLED', 'MISSING'), + action_claim_rewards: Joi.string().valid('ENABLED', 'DISABLED', 'MISSING'), + action_delegate: Joi.string().valid('ENABLED', 'DISABLED', 'MISSING'), + action_redelegate: Joi.string().valid('ENABLED', 'DISABLED', 'MISSING'), + action_undelegate: Joi.string().valid('ENABLED', 'DISABLED', 'MISSING'), + action_deposit: Joi.string().valid('ENABLED', 'DISABLED', 'MISSING'), + action_vote: Joi.string().valid('ENABLED', 'DISABLED', 'MISSING'), + action_proposal: Joi.string().valid('ENABLED', 'DISABLED', 'MISSING'), default: Joi.boolean(), stakingDenom: Joi.string().uppercase(), coinLookup: Joi.array() diff --git a/tests/source/cosmosv2-source.test.js b/tests/source/cosmosv2-source.test.js index 7b6ccb2c09..ff43cac1dc 100644 --- a/tests/source/cosmosv2-source.test.js +++ b/tests/source/cosmosv2-source.test.js @@ -28,22 +28,22 @@ jest.mock('apollo-datasource-rest', () => { describe('Cosmos V2 API', function() { describe('getRewards()', function() { - let api, cosmosNetworkConfig + let api, cosmosNetworkConfig, store beforeEach(() => { cosmosNetworkConfig = networks.find( network => network.id === 'cosmos-hub-testnet' ) - api = new CosmosV2API(cosmosNetworkConfig, {}) + store = { + validators: mockValidatorsDictionary + } + api = new CosmosV2API(cosmosNetworkConfig, store) mockDelegatorRewards = JSON.parse(JSON.stringify(delegatorRewards)) }) it('When an existing delegator address is passed, it should return the calculated rewards', async () => { //Act - const result = await api.getRewards( - delegatorAddress, - mockValidatorsDictionary - ) + const result = await api.getRewards(delegatorAddress) //Assert expect(result[0]).toHaveProperty('amount') @@ -58,9 +58,7 @@ describe('Cosmos V2 API', function() { mockDelegatorRewards.result.rewards = [] //Act & Assert - await expect( - api.getRewards(delegatorAddress, mockValidatorsDictionary) - ).resolves.toEqual([]) + await expect(api.getRewards(delegatorAddress)).resolves.toEqual([]) }) it('When an existing delegator address is passed with a reward 49000000 (umuon), it should return amount 49 (muon)', async () => { @@ -68,9 +66,7 @@ describe('Cosmos V2 API', function() { mockDelegatorRewards.result.rewards[0].reward[0].amount = 49000000 //Act & Assert - await expect( - api.getRewards(delegatorAddress, mockValidatorsDictionary) - ).resolves.toEqual([ + await expect(api.getRewards(delegatorAddress)).resolves.toEqual([ { amount: '49.000000', denom: 'MUON', @@ -84,9 +80,7 @@ describe('Cosmos V2 API', function() { mockDelegatorRewards.result.rewards[0].reward[0].amount = 0.05 //Act & Assert - await expect( - api.getRewards(delegatorAddress, mockValidatorsDictionary) - ).resolves.toEqual([ + await expect(api.getRewards(delegatorAddress)).resolves.toEqual([ { amount: '0.000000', denom: 'MUON', diff --git a/yarn.lock b/yarn.lock index d24148b636..db4369a75c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -128,13 +128,20 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": +"@babel/runtime@^7.8.4": version "7.8.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.7.tgz#8fefce9802db54881ba59f90bb28719b4996324d" integrity sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg== dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.9.2": + version "7.9.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06" + integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.4.0", "@babel/template@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.7.4.tgz#428a7d9eecffe27deac0a98e23bf8e3675d2a77b" @@ -484,120 +491,121 @@ "@types/istanbul-reports" "^1.1.1" "@types/yargs" "^13.0.0" -"@polkadot/api-derive@1.8.0-beta.1": - version "1.8.0-beta.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-1.8.0-beta.1.tgz#f3d4a34c50d213fa385a4f9355752cd2435e5fa3" - integrity sha512-GqaYIRH4gOacUqQGp9wYaxYi9D3YX2+Dp68G5C9gEKq8GvLx5eSqGxVPtqtK1gwks0XEIlfjhOP4YEwbHY3wJw== - dependencies: - "@babel/runtime" "^7.8.7" - "@polkadot/api" "1.8.0-beta.1" - "@polkadot/rpc-core" "1.8.0-beta.1" - "@polkadot/rpc-provider" "1.8.0-beta.1" - "@polkadot/types" "1.8.0-beta.1" - "@polkadot/util" "^2.6.2" - "@polkadot/util-crypto" "^2.6.2" +"@polkadot/api-derive@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-1.9.1.tgz#0b0fb580d3d5c42570207ee36a8652b21f51a556" + integrity sha512-aktzdVHpEPGqtRNCjjuSFS9Crt5S1JSDYDKq6BvK0tDNAxfNfeD2e73P9kPPNhuC8R50bD5sMmSvsJS0PQTULw== + dependencies: + "@babel/runtime" "^7.9.2" + "@polkadot/api" "1.9.1" + "@polkadot/rpc-core" "1.9.1" + "@polkadot/rpc-provider" "1.9.1" + "@polkadot/types" "1.9.1" + "@polkadot/util" "^2.7.1" + "@polkadot/util-crypto" "^2.7.1" bn.js "^5.1.1" memoizee "^0.4.14" rxjs "^6.5.4" -"@polkadot/api@1.8.0-beta.1", "@polkadot/api@^v1.8.0-beta.1": - version "1.8.0-beta.1" - resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-1.8.0-beta.1.tgz#50337edc2f7ec8c373428d05515f91b821512c62" - integrity sha512-mLtGwO4GElh/QnCEYTTgTai06kOnonOzn+/mbvyXz/Bj35AZwWxXgGMdfrEOVX+9ThpHXgRR1K2cjrkdtwI7wg== - dependencies: - "@babel/runtime" "^7.8.7" - "@polkadot/api-derive" "1.8.0-beta.1" - "@polkadot/keyring" "^2.6.2" - "@polkadot/metadata" "1.8.0-beta.1" - "@polkadot/rpc-core" "1.8.0-beta.1" - "@polkadot/rpc-provider" "1.8.0-beta.1" - "@polkadot/types" "1.8.0-beta.1" - "@polkadot/util" "^2.6.2" - "@polkadot/util-crypto" "^2.6.2" +"@polkadot/api@1.9.1", "@polkadot/api@^1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-1.9.1.tgz#10956c3c07bf6ba132f94722bd4bc23d536a7296" + integrity sha512-dJlWvpilZCVdRgZ2HsG6fwkNGuHomfqHfjMMPPz1yLdnMybWpsWYxK5cYfYZ3Y9EI0KSv0W+ZU0o6LnQyE/lAA== + dependencies: + "@babel/runtime" "^7.9.2" + "@polkadot/api-derive" "1.9.1" + "@polkadot/keyring" "^2.7.1" + "@polkadot/metadata" "1.9.1" + "@polkadot/rpc-core" "1.9.1" + "@polkadot/rpc-provider" "1.9.1" + "@polkadot/types" "1.9.1" + "@polkadot/types-known" "1.9.1" + "@polkadot/util" "^2.7.1" + "@polkadot/util-crypto" "^2.7.1" bn.js "^5.1.1" eventemitter3 "^4.0.0" rxjs "^6.5.4" -"@polkadot/jsonrpc@1.8.0-beta.1": - version "1.8.0-beta.1" - resolved "https://registry.yarnpkg.com/@polkadot/jsonrpc/-/jsonrpc-1.8.0-beta.1.tgz#c5fa8a2d235d1b594eaf50f65742eb3bdb5ad63b" - integrity sha512-DQ4wtEa5k/MDCoUSGqmDU0EwEmCy4yOZGmKjTyGOWT3jwnzWlsZR+pR4ylmBT7UJ/nqjihivCsl17++hdqE4UQ== - dependencies: - "@babel/runtime" "^7.8.7" - "@polkadot/types" "1.8.0-beta.1" - "@polkadot/util" "^2.6.2" - -"@polkadot/keyring@^2.6.2": - version "2.6.2" - resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-2.6.2.tgz#a0fc5b12e6b2bba738384fbc1cd93a0c61f30bac" - integrity sha512-R7rB0+bE0FjZoTJtd07oWQ2sD6l8+eQUKOs4rYr6JFC3eaveFu/G/5thRt+gs2a/X0TdRGnahlt02SaFX6sZpg== - dependencies: - "@babel/runtime" "^7.8.7" - "@polkadot/util" "2.6.2" - "@polkadot/util-crypto" "2.6.2" - -"@polkadot/metadata@1.8.0-beta.1": - version "1.8.0-beta.1" - resolved "https://registry.yarnpkg.com/@polkadot/metadata/-/metadata-1.8.0-beta.1.tgz#5c430da2da618722fab7083e7225c1a910c346fb" - integrity sha512-40XC4i5gZ6oboOJCvIBPvh2v9Th+go+aFPyoozkzobFIHSGhAGFeAo74QtPcE78AhGACVuhOaMMLrmXYkvJ+rQ== - dependencies: - "@babel/runtime" "^7.8.7" - "@polkadot/types" "1.8.0-beta.1" - "@polkadot/util" "^2.6.2" - "@polkadot/util-crypto" "^2.6.2" +"@polkadot/keyring@^2.7.1": + version "2.7.1" + resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-2.7.1.tgz#714d564b80305a343d83ff07baae59173af5f463" + integrity sha512-aQkc6TT0kaLwEa7rxit/M6/uWJ03wWY1owZychW9slfXH/tmWMCSM0m6z78MIAzdnSTVY7ztpjDfxrPOCHlLYA== + dependencies: + "@babel/runtime" "^7.9.2" + "@polkadot/util" "2.7.1" + "@polkadot/util-crypto" "2.7.1" + +"@polkadot/metadata@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/metadata/-/metadata-1.9.1.tgz#8f2453fe981bbbc6a3aba333f209195e5faff166" + integrity sha512-xGDto2AGSXRx3nMfqLIK0D15gJwuowL4D+RdLFC7rA6pMBCM2Ja4uVDD+f76IN0aF+/1p+4dTN1FGkuNMPSqAg== + dependencies: + "@babel/runtime" "^7.9.2" + "@polkadot/types" "1.9.1" + "@polkadot/types-known" "1.9.1" + "@polkadot/util" "^2.7.1" + "@polkadot/util-crypto" "^2.7.1" bn.js "^5.1.1" -"@polkadot/rpc-core@1.8.0-beta.1": - version "1.8.0-beta.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-1.8.0-beta.1.tgz#1fe7922a57fb303f0248b7ab6280eae14cd94896" - integrity sha512-Rk82zwCez88kCHM7zbbIIFO18xXVNzBCsHa67Xk1jRF4dkvVBZjUER2RFNtcope62m2SEYkc8nCY/2eRT/RbBA== - dependencies: - "@babel/runtime" "^7.8.7" - "@polkadot/jsonrpc" "1.8.0-beta.1" - "@polkadot/metadata" "1.8.0-beta.1" - "@polkadot/rpc-provider" "1.8.0-beta.1" - "@polkadot/types" "1.8.0-beta.1" - "@polkadot/util" "^2.6.2" +"@polkadot/rpc-core@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-1.9.1.tgz#40cab586bba133a0a80481a8a4f81082e62bf08d" + integrity sha512-9CnhCSF4maNzaD6n9O2aYQvbEqqHIZdA2XOFZ+Tjauo2YOvgskC7VAD3gjS2aJ8HcwKY2vaRpgp/lRFnt8+rMg== + dependencies: + "@babel/runtime" "^7.9.2" + "@polkadot/metadata" "1.9.1" + "@polkadot/rpc-provider" "1.9.1" + "@polkadot/types" "1.9.1" + "@polkadot/util" "^2.7.1" memoizee "^0.4.14" rxjs "^6.5.4" -"@polkadot/rpc-provider@1.8.0-beta.1": - version "1.8.0-beta.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-1.8.0-beta.1.tgz#075c14b09c233d2220ad9adf2310a02f0fdfc0a8" - integrity sha512-KAbB8+sOca79G/jXNAFX3bvHviDwUi8IGLKxSrDVP1Hzf1i+v3vDudjy1eDf6rueQLeSPelrjzpMwA1T4d4pAg== - dependencies: - "@babel/runtime" "^7.8.7" - "@polkadot/jsonrpc" "1.8.0-beta.1" - "@polkadot/metadata" "1.8.0-beta.1" - "@polkadot/types" "1.8.0-beta.1" - "@polkadot/util" "^2.6.2" - "@polkadot/util-crypto" "^2.6.2" +"@polkadot/rpc-provider@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-1.9.1.tgz#06c511a3a71b5ec2c5b15b0e07c6203f01e4c994" + integrity sha512-HW3p9Z3eltqot1qReEwO8EcyBeyh+GZdp3Mb5l1cH9rjKm7GTc7tPpGgLzQP1DBgJMdUpxlPfZ4wZHJO6JresA== + dependencies: + "@babel/runtime" "^7.9.2" + "@polkadot/metadata" "1.9.1" + "@polkadot/types" "1.9.1" + "@polkadot/util" "^2.7.1" + "@polkadot/util-crypto" "^2.7.1" bn.js "^5.1.1" eventemitter3 "^4.0.0" isomorphic-fetch "^2.2.1" websocket "^1.0.31" -"@polkadot/types@1.8.0-beta.1": - version "1.8.0-beta.1" - resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-1.8.0-beta.1.tgz#da3c22d2a7504c7b0643e978b72e0df7a6a0be91" - integrity sha512-R2BleuF+5gU5pILUOEdGb4FJGLUJjp8Cenx9ZtWNbDGI5PcxEwxRgSqbhwZDK/A2VrBCwSqiHwNsUifRHA+/Ug== +"@polkadot/types-known@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-1.9.1.tgz#38f1d1ebaf77824066e9b78f82f336c269cc9d89" + integrity sha512-G0YUfEmrr9yW0Iz5SXXsO6zpkALUTLf4pocNY9ISeCjrUcdfk+Rt8OnsI2DRVC8Qjaf9a4rb9lC2HIMTqFJjgQ== dependencies: - "@babel/runtime" "^7.8.7" - "@polkadot/metadata" "1.8.0-beta.1" - "@polkadot/util" "^2.6.2" - "@polkadot/util-crypto" "^2.6.2" + "@babel/runtime" "^7.9.2" + "@polkadot/types" "1.9.1" + "@polkadot/util" "^2.7.1" + bn.js "^5.1.1" + +"@polkadot/types@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-1.9.1.tgz#88d426241ace9e6ec3a2ce3e1a51d8333ad28822" + integrity sha512-c1mzuxJiOPIWrkEWMRdT2seW21lEC3C+EetshgCMXpcsAv5iQeB59J1fTbRiSsQIESKIiP/zv+Igpj7wO/2hMw== + dependencies: + "@babel/runtime" "^7.9.2" + "@polkadot/metadata" "1.9.1" + "@polkadot/util" "^2.7.1" + "@polkadot/util-crypto" "^2.7.1" "@types/bn.js" "^4.11.6" bn.js "^5.1.1" memoizee "^0.4.14" rxjs "^6.5.4" -"@polkadot/util-crypto@2.6.2", "@polkadot/util-crypto@^2.6.2": - version "2.6.2" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-2.6.2.tgz#6118de39986a42213406e629fcb79ff74cabede6" - integrity sha512-U+M10kkVcCrDm5FXo6uzUmyL/iEr0PTlSinOlNTZHuRPzdbQmgw3XZ3Deqg/7CPx15KT2K9wIKv8jOfFu/dg2w== +"@polkadot/util-crypto@2.7.1", "@polkadot/util-crypto@^2.7.1": + version "2.7.1" + resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-2.7.1.tgz#627ff9aa29379da7c5aa3727829ae8a0e80cd7d8" + integrity sha512-8Y+9Fv0g/AfyJLB1a9WyCClzYuf9IVLRGhAcb0I4mAL/KqfHoEzGCBGd1PjmcRboOBrtq6IYTOppwA0c6WOGww== dependencies: - "@babel/runtime" "^7.8.7" - "@polkadot/util" "2.6.2" + "@babel/runtime" "^7.9.2" + "@polkadot/util" "2.7.1" "@polkadot/wasm-crypto" "^1.2.1" base-x "^3.0.8" bip39 "^3.0.2" @@ -610,12 +618,12 @@ tweetnacl "^1.0.3" xxhashjs "^0.2.2" -"@polkadot/util@2.6.2", "@polkadot/util@^2.6.2": - version "2.6.2" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-2.6.2.tgz#1d32819b2451a695e9117ec8aef08a3ee1a2524f" - integrity sha512-/dsOuNPsl0IdNu0YZZm+BskESbiTG77NBnRDk0MwAoBmYhvulS34De+Ev/Qx2okS+C8aQ6Uai0AtnYX6rlEXfA== +"@polkadot/util@2.7.1", "@polkadot/util@^2.7.1": + version "2.7.1" + resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-2.7.1.tgz#adc5b9c8ea17179e4ea2061ddee84d21a0d50cc2" + integrity sha512-bvYnqhXV3N8m+i92uq2XrqRi4DEUYgdMhDeCIzf0t8ZQ0mcGhhROMuULhlpT+IlHwAlfewQ17HwB2qLtB6mGLw== dependencies: - "@babel/runtime" "^7.8.7" + "@babel/runtime" "^7.9.2" "@types/bn.js" "^4.11.6" bn.js "^5.1.1" camelcase "^5.3.1"