diff --git a/data/networks-local.js b/data/networks-local.js index 3f39687338..9cd7b08447 100644 --- a/data/networks-local.js +++ b/data/networks-local.js @@ -8,7 +8,7 @@ module.exports = [ rpc_url: config.testnetRPC, api_url: config.testnetAPI, bech32_prefix: 'cosmos', - source_class_name: 'source/cosmosV0-source', + source_class_name: 'source/cosmosV2-source', block_listener_class_name: 'block-listeners/cosmos-node-subscription', testnet: true, feature_explore: true, diff --git a/data/networks.json b/data/networks.json index c28d215fdd..f6dad606c5 100644 --- a/data/networks.json +++ b/data/networks.json @@ -67,7 +67,7 @@ "source_class_name": "source/regenV0-source", "block_listener_class_name": "block-listeners/cosmos-node-subscription", "testnet": true, - "feature_session": false, + "feature_session": true, "feature_explore": true, "feature_portfolio": true, "feature_validators": true, @@ -96,7 +96,7 @@ "source_class_name": "source/terraV3-source", "block_listener_class_name": "block-listeners/cosmos-node-subscription", "testnet": true, - "feature_session": false, + "feature_session": true, "feature_explore": true, "feature_portfolio": true, "feature_validators": true, @@ -125,8 +125,8 @@ "source_class_name": "source/emoneyV0-source", "block_listener_class_name": "block-listeners/cosmos-node-subscription", "testnet": true, - "feature_session": false, - "feature_explore": false, + "feature_session": true, + "feature_explore": true, "feature_portfolio": false, "feature_validators": true, "feature_proposals": false, diff --git a/lib/block-listeners/cosmos-node-subscription.js b/lib/block-listeners/cosmos-node-subscription.js index 9fa80de608..8f0ab268ac 100644 --- a/lib/block-listeners/cosmos-node-subscription.js +++ b/lib/block-listeners/cosmos-node-subscription.js @@ -1,4 +1,5 @@ const _ = require('lodash') +const io = require('@pm2/io') const Tendermint = require('../rpc/tendermint') const { publishBlockAdded, @@ -16,17 +17,26 @@ class CosmosNodeSubscription { this.network = network this.cosmosAPI = new CosmosApiClass(network) this.store = store + this.lastupdate = 0 + this.metric = io.metric({ + name: `${this.network.id}_update` + }) // Create a RPC subscription for each network that will react to new block events. Tendermint() .connect(this.network.rpc_url) .then(connectedClient => { - connectedClient.subscribe({ query: "tm.event='NewBlock'" }, event => + connectedClient.subscribe({ query: "tm.event='NewBlock'" }, event => { + if (this.lastupdate) { + const diff = Date.now() - this.lastupdate + this.metric.set(diff) + } + this.lastupdate = Date.now() setTimeout( () => this.newBlockHandler(event.block.header.height), WAIT_FOR_BLOCK_DELAY ) - ) + }) }) .catch(e => { Sentry.withScope(function(scope) { diff --git a/lib/controller/transaction/index.js b/lib/controller/transaction/index.js index dc9db979eb..d3298dba8b 100644 --- a/lib/controller/transaction/index.js +++ b/lib/controller/transaction/index.js @@ -17,9 +17,9 @@ async function estimate() { // const message = getMessage(tx.messageType, tx.txProperties, context) try { - // const gasEstimate = (await message.simulate({ memo: tx.memo })) * 4 // we quadrupled the gas to be safe. rn there are several txs failing on cosmoshub-3 + // const gasEstimate = await message.simulate({ memo: tx.memo }) + 100000 // HACK: fixed to this value for now. too high gas can lead to transactions never being included. too low gas can lead to rejected txs and cost the user money. - const gasEstimate = 450000 + const gasEstimate = 550000 return { gasEstimate, diff --git a/lib/reducers/cosmosV0-reducers.js b/lib/reducers/cosmosV0-reducers.js index 0607ee99e3..11c2d47dde 100644 --- a/lib/reducers/cosmosV0-reducers.js +++ b/lib/reducers/cosmosV0-reducers.js @@ -98,11 +98,11 @@ const expectedRewardsPerToken = (validator, commission, annualProvision) => { return annualDelegatorRewardsPerToken.div(1000000) } -// reduce deposits to one number and filter by required denom -function getDeposit(proposal, bondDenom) { +// reduce deposits to one number +function getDeposit(proposal) { return atoms( proposal.total_deposit.reduce( - (sum, cur) => sum.plus(cur.denom === bondDenom ? cur.amount : 0), + (sum, cur) => sum.plus(cur.amount), BigNumber(0) ) ) @@ -163,7 +163,7 @@ function proposalReducer( statusBeginTime: proposalBeginTime(proposal), statusEndTime: proposalEndTime(proposal), tally: tallyReducer(proposal, tally, totalBondedTokens), - deposit: getDeposit(proposal, 'uatom'), // TODO use denom lookup + deposit: getDeposit(proposal), proposer: proposer.proposer } } diff --git a/lib/reducers/emoneyV0-reducers.js b/lib/reducers/emoneyV0-reducers.js index e1c07c362c..41e4f53802 100644 --- a/lib/reducers/emoneyV0-reducers.js +++ b/lib/reducers/emoneyV0-reducers.js @@ -1,29 +1,126 @@ const terraV3Reducers = require('./terraV3-reducers') const BigNumber = require('bignumber.js') +const fetch = require('node-fetch') +const _ = require('lodash') +const EMoneyAPIUrl = `https://api.e-money.com/v1/` +const exchangeAPIURL = `https://api.exchangeratesapi.io/latest?` +const Sentry = require('@sentry/node') -function expectedRewardsPerToken( +async function totalBackedValueReducer(totalBackedValue) { + const exchangeRates = await fetchTokenExchangeRates() + const token = `e`.concat(totalBackedValue.denom.substring(2)) + const ticker = totalBackedValue.denom.substring(2).toUpperCase() + // First we calculate the fiat net value of the token's total supply in it counterpart + // fiat currency + // TODO Not all the tokens have yet their rate value in the e-Money API. So the calculation + // is not yet 100% accurate + const fiatValue = exchangeRates[token] + ? BigNumber(totalBackedValue.amount) + .div(100000000) + .times(exchangeRates[token][ticker]) + .toNumber() + : null + // Now that we have its net fiat value, we transform this value into euro + const { rates } = await fetchFiatExchangeRates(`EUR`, ticker) + const eurRate = ticker === `EUR` ? 1 : rates[ticker] + return (totalBackedValue = { + ...totalBackedValue, + amount: exchangeRates[token] + ? BigNumber(totalBackedValue.amount).times( + Object.values(exchangeRates[token])[0] + ) + : BigNumber(totalBackedValue.amount), + // The total net EUR value of the token's total supply equals its net value in its + // counterpart fiat currency times this fiat currency's EUR rate + eurValue: fiatValue * eurRate + }) +} + +async function fetchTokenExchangeRates() { + return await fetch(`${EMoneyAPIUrl}rates.json`) + .then(r => r.json()) + .catch(err => { + Sentry.withScope(function(scope) { + scope.setExtra('fetch', `${EMoneyAPIUrl}rates.json`) + Sentry.captureException(err) + }) + }) +} + +async function fetchFiatExchangeRates(selectedFiatCurrency, ticker) { + return await fetch( + `${exchangeAPIURL}base=${selectedFiatCurrency}&symbols=${ticker}` + ) + .then(r => r.json()) + .catch(err => { + Sentry.withScope(function(scope) { + scope.setExtra( + 'fetch', + `${exchangeAPIURL}base=${selectedFiatCurrency}&symbols=${ticker}` + ) + Sentry.captureException(err) + }) + }) +} + +async function expectedRewardsPerToken( validator, commission, - inflation, - totalBackedValue + inflations, + totalBackedValues ) { const percentTotalStakedNGM = BigNumber(validator.votingPower).times(1000) const totalNGMStakedToValidator = validator.tokens const division = BigNumber(percentTotalStakedNGM) - .times(BigNumber(1).minus(commission)) + .times(BigNumber(1).minus(BigNumber(commission))) .div(BigNumber(totalNGMStakedToValidator)) + // Now we need to multiply each total supply of backed tokens with its corresponding + // inflation + let accumulator = BigNumber(0) + const totalBackedValueDictionary = _.keyBy(totalBackedValues, 'denom') + + inflations.forEach(inflation => { + accumulator = BigNumber(accumulator) + .plus(accumulator) + .plus( + BigNumber(inflation.inflation) + .times(totalBackedValueDictionary[inflation.denom].amount) + .div(1000000) + ) + }) + const totalBackedValuesTimesInflations = accumulator // First we calculate the total value of rewards we get for staking one single // NGM in this particular validator - const delegatorSharePerToken = BigNumber(inflation * totalBackedValue).div( - division - ) - return delegatorSharePerToken.div(1000000) - // TODO - // Now we convert delegatorRewardsPerToken to the amount of NGM we can buy now - // with this reward + let delegatorSharePerToken = totalBackedValuesTimesInflations.div(division) + // In testnet some validators have commission at 100% and therefore division is + // equal to 0. Dividing by 0 we get infinity, and we want to make sure that + // we don't display such value + const infinity = new BigNumber(Infinity) + // This is just how BigNumber works. Comparing to 0 and if true, bignumber equals the given parameter. + // Documentation on this method can be found here: https://mikemcl.github.io/bignumber.js/#cmp + delegatorSharePerToken = + delegatorSharePerToken.comparedTo(infinity) === 0 + ? BigNumber(0) + : delegatorSharePerToken.div(1000000) + // Now we get the total net value in EUR of all token's total supplies + let eurAccumulator = 0 + totalBackedValues.forEach(totalBackedValue => { + eurAccumulator += totalBackedValue.eurValue + }) + const totalEURGains = eurAccumulator * delegatorSharePerToken.toNumber() + // How many NGM tokens can we buy with the total gain in EUR we make in a year's time? + // 0.50€ is the price the NGM tokens will be first sold. Therefore, this is the official value + // until they reach an exchange + const pricePerNGM = 0.5 + const ngmGains = totalEURGains / pricePerNGM + // We divide by 1 because we assume 1 NGM > gain per 1 NGM + const expectedReturns = parseFloat(ngmGains / 1).toFixed(2) + // TODO the FE is the one converting to percentage now, so we need to divide by 100 + return expectedReturns / 100 } module.exports = { ...terraV3Reducers, - expectedRewardsPerToken + expectedRewardsPerToken, + totalBackedValueReducer } diff --git a/lib/resolvers.js b/lib/resolvers.js index 62ccda4065..9f3e66f639 100644 --- a/lib/resolvers.js +++ b/lib/resolvers.js @@ -250,13 +250,24 @@ const resolvers = { }, maintenance: (_, __, { dataSources }) => dataSources.LunieDBAPI.getMaintenance(), - balances: async (_, { networkId, address }, { dataSources }) => - remoteFetch(dataSources, networkId).getBalancesFromAddress(address), - balance: async (_, { networkId, address, denom }, { dataSources }) => { + balances: async ( + _, + { networkId, address, fiatCurrency }, + { dataSources } + ) => + remoteFetch(dataSources, networkId).getBalancesFromAddress( + address, + fiatCurrency + ), + balance: async ( + _, + { networkId, address, denom, fiatCurrency }, + { dataSources } + ) => { const balances = await remoteFetch( dataSources, networkId - ).getBalancesFromAddress(address) + ).getBalancesFromAddress(address, fiatCurrency) const balance = balances.find(balance => balance.denom === denom) return balance || { denom, amount: 0 } }, diff --git a/lib/schema.js b/lib/schema.js index f027e0b29c..998b55dbaa 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -24,6 +24,12 @@ const typeDefs = gql` amount: String } + type Balance { + denom: String! + amount: String! + fiatValue: String + } + type Proposal { networkId: String! id: Int @@ -139,7 +145,6 @@ const typeDefs = gql` fee: Coin signature: String! value: String! - amount: String undelegationEndTime: String } @@ -203,8 +208,17 @@ const typeDefs = gql` networks: [Network] network(id: String): Network maintenance: [Maintenance] - balances(networkId: String!, address: String!): [Coin] - balance(networkId: String!, address: String!, denom: String!): Coin + balances( + networkId: String! + address: String! + fiatCurrency: String + ): [Balance] + balance( + networkId: String! + address: String! + denom: String! + fiatCurrency: String + ): Balance overview(networkId: String!, address: String!): Overview delegation( networkId: String! diff --git a/lib/source/cosmosV0-source.js b/lib/source/cosmosV0-source.js index 6016cd3090..93f44e2434 100644 --- a/lib/source/cosmosV0-source.js +++ b/lib/source/cosmosV0-source.js @@ -11,6 +11,7 @@ class CosmosV0API extends RESTDataSource { this.baseURL = network.api_url this.initialize({}) this.networkId = network.id + this.networkTitle = network.title this.delegatorBech32Prefix = network.bech32_prefix this.validatorConsensusBech32Prefix = `${network.bech32_prefix}valcons` @@ -55,8 +56,16 @@ class CosmosV0API extends RESTDataSource { return involvedAddresses } + checkAddress(address) { + if (!address.startsWith(this.delegatorBech32Prefix)) { + throw new UserInputError( + `The address you entered doesn't belong to the ${this.networkTitle} network` + ) + } + } + async getTransactionsByHeight(height) { - const txs = await this.get(`txs?tx.height=${height}`) + const txs = await this.loadPaginatedTxs(`txs?tx.height=${height}`) return Array.isArray(txs) ? txs.map(transaction => this.reducers.transactionReducer(transaction, this.reducers) @@ -248,6 +257,7 @@ class CosmosV0API extends RESTDataSource { } async getDelegatorVote({ proposalId, address }) { + this.checkAddress(address) const response = await this.query(`gov/proposals/${proposalId}/votes`) const votes = response || [] const vote = votes.find(({ voter }) => voter === address) || {} @@ -274,10 +284,28 @@ class CosmosV0API extends RESTDataSource { return this.reducers.blockReducer(this.networkId, block, transactions) } - async getBalancesFromAddress(address) { + async addFiatValue(balances, fiatCurrency) { + return Promise.all( + balances.map( + balance => + (balance = { + ...balance, + fiatValue: this.calculateFiatValue + ? this.calculateFiatValue(balance, fiatCurrency) + : null + }) + ) + ) + } + + async getBalancesFromAddress(address, fiatCurrency) { + this.checkAddress(address) const response = await this.query(`bank/balances/${address}`) - const balances = response || [] - return balances.map(this.reducers.coinReducer) + let balances = response || [] + balances = balances.map(this.reducers.coinReducer) + // We calculate the fiatValue field for networks with multiple tokens + // For now, it is just e-Money + return await this.addFiatValue(balances, fiatCurrency) } async getAccountInfo(address) { @@ -288,6 +316,7 @@ class CosmosV0API extends RESTDataSource { } async getDelegationsForDelegatorAddress(address, validatorsDictionary) { + this.checkAddress(address) let delegations = (await this.query(`staking/delegators/${address}/delegations`)) || [] @@ -309,6 +338,7 @@ class CosmosV0API extends RESTDataSource { } async getUndelegationsForDelegatorAddress(address, validatorsDictionary) { + this.checkAddress(address) let undelegations = (await this.query( `staking/delegators/${address}/unbonding_delegations` @@ -339,6 +369,7 @@ class CosmosV0API extends RESTDataSource { } async getDelegationForValidator(delegatorAddress, validator) { + this.checkAddress(delegatorAddress) const operatorAddress = validator.operatorAddress const delegation = await this.query( `staking/delegators/${delegatorAddress}/delegations/${operatorAddress}` @@ -366,6 +397,7 @@ class CosmosV0API extends RESTDataSource { } async getRewards(delegatorAddress, validatorsDictionary, delegations = null) { + this.checkAddress(delegatorAddress) if (!delegations) { delegations = await this.getDelegationsForDelegatorAddress( delegatorAddress, @@ -389,6 +421,7 @@ class CosmosV0API extends RESTDataSource { } async getOverview(delegatorAddress, validatorsDictionary) { + this.checkAddress(delegatorAddress) const [ balances, delegations, @@ -420,29 +453,46 @@ class CosmosV0API extends RESTDataSource { ) } + async loadPaginatedTxs(url, page = 1, totalAmount = 0) { + const pagination = `&limit=1000000000&page=${page}` + let allTxs = [] + + const { txs, total_count } = await this.get(`${url}${pagination}`) + allTxs = allTxs.concat(txs) + + // there is a bug in page_number in gaia-13007 so we can't use is + if (allTxs.length + totalAmount < Number(total_count)) { + return allTxs.concat( + await this.loadPaginatedTxs(url, page + 1, totalAmount + allTxs.length) + ) + } + + return allTxs + } + async getTransactions(address) { - const pagination = `&limit=${1000000000}` + this.checkAddress(address) const txs = await Promise.all([ - this.get(`/txs?sender=${address}${pagination}`), - this.get(`/txs?recipient=${address}${pagination}`), - this.get(`/txs?action=submit_proposal&proposer=${address}${pagination}`), - this.get(`/txs?action=deposit&depositor=${address}${pagination}`), - this.get(`/txs?action=vote&voter=${address}${pagination}`), + this.loadPaginatedTxs(`/txs?sender=${address}`), + this.loadPaginatedTxsget(`/txs?recipient=${address}`), + this.loadPaginatedTxs(`/txs?action=submit_proposal&proposer=${address}`), + this.loadPaginatedTxs(`/txs?action=deposit&depositor=${address}`), + this.loadPaginatedTxs(`/txs?action=vote&voter=${address}`), // this.get(`/txs?action=create_validator&destination-validator=${valAddress}`), // TODO // this.get(`/txs?action=edit_validator&destination-validator=${valAddress}`), // TODO - this.get(`/txs?action=delegate&delegator=${address}${pagination}`), - this.get( - `/txs?action=begin_redelegate&delegator=${address}${pagination}` + this.loadPaginatedTxs(`/txs?action=delegate&delegator=${address}`), + this.loadPaginatedTxs( + `/txs?action=begin_redelegate&delegator=${address}` ), - this.get(`/txs?action=begin_unbonding&delegator=${address}${pagination}`), + this.loadPaginatedTxs(`/txs?action=begin_unbonding&delegator=${address}`), // this.get(`/txs?action=unjail&source-validator=${address}`), // TODO // this.get(`/txs?action=set_withdraw_address&delegator=${address}`), // other - this.get( - `/txs?action=withdraw_delegator_reward&delegator=${address}${pagination}` + this.loadPaginatedTxs( + `/txs?action=withdraw_delegator_reward&delegator=${address}` ), - this.get( - `/txs?action=withdraw_validator_rewards_all&source-validator=${address}${pagination}` + this.loadPaginatedTxs( + `/txs?action=withdraw_validator_rewards_all&source-validator=${address}` ) ]).then(transactionGroups => [].concat(...transactionGroups)) diff --git a/lib/source/cosmosV2-source.js b/lib/source/cosmosV2-source.js index f0c3e2a4c3..4e6e1724ee 100644 --- a/lib/source/cosmosV2-source.js +++ b/lib/source/cosmosV2-source.js @@ -16,16 +16,29 @@ class CosmosV2API extends CosmosV0API { return signingInfos } + async loadPaginatedTxs(url, page = 1, totalAmount = 0) { + const pagination = `&limit=1000000000&page=${page}` + let allTxs = [] + + const { txs, total_count } = await this.get(`${url}${pagination}`) + allTxs = allTxs.concat(txs) + + // there is a bug in page_number in gaia-13007 so we can't use is + if (allTxs.length + totalAmount < Number(total_count)) { + return allTxs.concat( + await this.loadPaginatedTxs(url, page + 1, totalAmount + allTxs.length) + ) + } + + return allTxs + } + async getTransactions(address) { - const pagination = `&limit=${1000000000}` + this.checkAddress(address) const txs = await Promise.all([ - this.get(`/txs?message.sender=${address}${pagination}`).then( - ({ txs }) => txs - ), - this.get(`/txs?transfer.recipient=${address}${pagination}`).then( - ({ txs }) => txs - ) + this.loadPaginatedTxs(`/txs?message.sender=${address}`), + this.loadPaginatedTxs(`/txs?transfer.recipient=${address}`) ]).then(([senderTxs, recipientTxs]) => [].concat(senderTxs, recipientTxs)) const dupFreeTxs = uniqWith(txs, (a, b) => a.txhash === b.txhash) @@ -43,11 +56,11 @@ class CosmosV2API extends CosmosV0API { // 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 senderAttributes = event.attributes + .filter(({ key }) => key === 'sender') + .map(sender => sender.value) + if (senderAttributes.length) { + involvedAddresses = [...involvedAddresses, ...senderAttributes] } const recipientAttribute = event.attributes.find( @@ -65,7 +78,7 @@ class CosmosV2API extends CosmosV0API { } async getTransactionsByHeight(height) { - const { txs } = await this.get(`txs?tx.height=${height}`) + const txs = await this.loadPaginatedTxs(`txs?tx.height=${height}`) return Array.isArray(txs) ? txs.map(transaction => this.reducers.transactionReducer(transaction, this.reducers) @@ -74,6 +87,7 @@ class CosmosV2API extends CosmosV0API { } async getRewards(delegatorAddress, validatorsDictionary) { + this.checkAddress(delegatorAddress) const result = await this.query( `distribution/delegators/${delegatorAddress}/rewards` ) diff --git a/lib/source/emoneyV0-source.js b/lib/source/emoneyV0-source.js index 4b11e52714..31ad817a26 100644 --- a/lib/source/emoneyV0-source.js +++ b/lib/source/emoneyV0-source.js @@ -1,27 +1,143 @@ const TerraV3API = require('./terraV3-source') - -// This is just a dummy value. -// It is kind of difficult to calculate the expected returns for e-Money since it is -// planned that the issuance of the fiat-backed tokens varies within one same year. -const totalBackedValueEUR = '100000' -// E-Money has a fixed inflation of 1% for fiat-backed tokens -const inflation = 0.01 +const BigNumber = require('bignumber.js') +const fetch = require('node-fetch') +const apiURL = `https://api.exchangeratesapi.io/latest?` +const { UserInputError } = require('apollo-server') class EMoneyV0API extends TerraV3API { setReducers() { this.reducers = require('../reducers/emoneyV0-reducers') } + // Here we query for the current inflation rates for all the backed tokens + async getTokensInflations() { + const inflations = await this.get(`inflation/current`) + return inflations.result.assets.filter(asset => asset.denom !== `x3chf`) + } + + // Here we query for the current total supply for all the backed tokens + // We filter NGM tokens out + async getTotalBackedValues() { + let totalBackedValues = await this.get(`supply/total`) + totalBackedValues = totalBackedValues.result.filter( + asset => !asset.denom.includes(`ngm`) + ) + const mapTotalBackedValues = async () => { + return Promise.all( + totalBackedValues.map(totalBackedValue => + this.reducers.totalBackedValueReducer(totalBackedValue) + ) + ) + } + return await mapTotalBackedValues() + } + async getExpectedReturns(validator) { + const inflations = await this.getTokensInflations() + const totalBackedValues = await this.getTotalBackedValues() // Right now we are displaying only the EUR value of expected returns const expectedReturns = this.reducers.expectedRewardsPerToken( validator, validator.commission, - inflation, - totalBackedValueEUR + inflations, + totalBackedValues ) return expectedReturns } + + // Currently this function only works for e-Money and is very e-Money centered. + // But soon will also be enabled for other similar multiple-tokens networks like Terra. + async calculateFiatValue(balance, selectedFiatCurrency) { + if (!selectedFiatCurrency) { + throw new UserInputError( + `No fiatCurrency selected. Please, enter the fiat currency you'd like the fiat value to be displayed in` + ) + } + // When e-Money goes live they will count with a trading platform where the value + // for the different backed tokens will be changing slightly. + // They will provide with an API for us to query these values. + // For now we will assume a 1:1 ratio and treat each token like it were the real + // fiat currency it represents. + const denom = balance.denom.substring(2).toUpperCase() + + // To handle the NGM balance, first we convert to EUR value and then to the selected + // fiat currency value + if (denom === 'NGM') { + const eurValue = + BigNumber(balance.amount) + .div(100000000) + .toNumber() * 0.5 // 0.50€ is the price the NGM tokens will be first sold. Therefore, the official value until they reach an exchange + if (selectedFiatCurrency === `EUR`) { + return parseFloat(eurValue) + .toFixed(6) + .toString() + .concat(`€`) + } else { + const { rates } = await this.fetchExchangeRates( + selectedFiatCurrency, + `EUR` + ) + const fiatValue = eurValue / rates[selectedFiatCurrency] + const currencySign = this.getCurrencySign(selectedFiatCurrency) + return parseFloat(fiatValue) + .toFixed(6) + .toString() + .concat(`${currencySign}`) + } + // For all other balances we use the public API https://exchangeratesapi.io/ to add all balances into + // a single fiat currency value, the selectedFiatCurrency + } else { + const { rates } = await this.fetchExchangeRates( + selectedFiatCurrency, + denom + ) + let totalFiatValue = 0 + // Here we check if the balance we are currently calculating the total fiat value from is + // the same currency we want the fiat value to be displayed in. + // In that case, we simply add it, don't need to convert it. + if (denom !== selectedFiatCurrency) { + totalFiatValue = + BigNumber(balance.amount) + .div(100000000) + .toNumber() / rates[denom] + } else { + totalFiatValue = BigNumber(balance.amount) + .div(100000000) + .toNumber() + } + // Finally we get the proper currency sign to display + const currencySign = this.getCurrencySign(selectedFiatCurrency) + return parseFloat(totalFiatValue) + .toFixed(6) + .toString() + .concat(`${currencySign}`) + } + } + + async fetchExchangeRates(selectedFiatCurrency, ticker) { + return await fetch( + `${apiURL}base=${selectedFiatCurrency}&symbols=${ticker}` + ) + .then(r => r.json()) + .catch(error => console.error(error)) + } + + getCurrencySign(currency) { + switch (currency) { + case `EUR`: + return `€` + case `USD`: + return `$` + case `GBP`: + return `£` + case `CHF`: + return `Fr` + case `JPY`: + return `¥` + default: + return `?` + } + } } module.exports = EMoneyV0API diff --git a/lib/source/regenV0-source.js b/lib/source/regenV0-source.js index e2fd17f32b..9867758238 100644 --- a/lib/source/regenV0-source.js +++ b/lib/source/regenV0-source.js @@ -23,6 +23,7 @@ class RegenV0API extends CosmosV0API { } async getTransactions(address) { + this.checkAddress(address) const pagination = `&limit=${1000000000}` const txs = await Promise.all([ diff --git a/package.json b/package.json index d35b0691be..c85b97e763 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "license": "ISC", "dependencies": { "@hapi/joi": "^16.1.8", + "@pm2/io": "^4.3.3", "@sentry/node": "^5.9.0", "apollo-datasource-rest": "^0.6.6", "apollo-server": "^2.9.9", diff --git a/tests/routes.test.js b/tests/routes.test.js index dd701a8837..7417ce607f 100644 --- a/tests/routes.test.js +++ b/tests/routes.test.js @@ -52,7 +52,7 @@ describe('POST /transaction', function() { .set('Accept', 'application/json') .expect('Content-Type', /json/) // .expect({ gasEstimate: 74072, success: true }) - .expect({ gasEstimate: 450000, success: true }) // fixed for now + .expect({ gasEstimate: 550000, success: true }) // fixed for now .expect(200, done) }) }) diff --git a/yarn.lock b/yarn.lock index ebcca297e5..d9129c3737 100644 --- a/yarn.lock +++ b/yarn.lock @@ -477,6 +477,63 @@ "@types/istanbul-reports" "^1.1.1" "@types/yargs" "^13.0.0" +"@opencensus/core@^0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@opencensus/core/-/core-0.0.8.tgz#df01f200c2d2fbfe14dae129a1a86fb87286db92" + integrity sha512-yUFT59SFhGMYQgX0PhoTR0LBff2BEhPrD9io1jWfF/VDbakRfs6Pq60rjv0Z7iaTav5gQlttJCX2+VPxFWCuoQ== + dependencies: + continuation-local-storage "^3.2.1" + log-driver "^1.2.7" + semver "^5.5.0" + shimmer "^1.2.0" + uuid "^3.2.1" + +"@opencensus/core@^0.0.9": + version "0.0.9" + resolved "https://registry.yarnpkg.com/@opencensus/core/-/core-0.0.9.tgz#b16f775435ee309433e4126af194d37313fc93b3" + integrity sha512-31Q4VWtbzXpVUd2m9JS6HEaPjlKvNMOiF7lWKNmXF84yUcgfAFL5re7/hjDmdyQbOp32oGc+RFV78jXIldVz6Q== + dependencies: + continuation-local-storage "^3.2.1" + log-driver "^1.2.7" + semver "^5.5.0" + shimmer "^1.2.0" + uuid "^3.2.1" + +"@opencensus/propagation-b3@^0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@opencensus/propagation-b3/-/propagation-b3-0.0.8.tgz#0751e6fd75f09400d9d3c419001e9e15a0df68e9" + integrity sha512-PffXX2AL8Sh0VHQ52jJC4u3T0H6wDK6N/4bg7xh4ngMYOIi13aR1kzVvX1sVDBgfGwDOkMbl4c54Xm3tlPx/+A== + dependencies: + "@opencensus/core" "^0.0.8" + uuid "^3.2.1" + +"@pm2/agent-node@^1.1.10": + version "1.1.10" + resolved "https://registry.yarnpkg.com/@pm2/agent-node/-/agent-node-1.1.10.tgz#29fafc9d1b75288dec87b6af1216ddfab8ea9b06" + integrity sha512-xRcrk7OEwhS3d/227/kKGvxgmbIi6Yyp27FzGlFNermEKhgddmFaRnmd7GRLIsBM/KB28NrwflBZulzk/mma6g== + dependencies: + debug "^3.1.0" + eventemitter2 "^5.0.1" + proxy-agent "^3.0.3" + ws "^6.0.0" + +"@pm2/io@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@pm2/io/-/io-4.3.3.tgz#3a719da5b0897718173d51ab313d25d7e1471c9b" + integrity sha512-ENGsdSVpnwbYMGdeB0/Xy2eZYo7oltzApoCsMD4ssqWNXDg9C4uQZy5J09iPsb0IHFwSDjU5oylXdwKDSoqODw== + dependencies: + "@opencensus/core" "^0.0.9" + "@opencensus/propagation-b3" "^0.0.8" + "@pm2/agent-node" "^1.1.10" + async "~2.6.1" + debug "3.1.0" + eventemitter2 "~5.0.1" + require-in-the-middle "^5.0.0" + semver "5.5.0" + shimmer "~1.2.0" + signal-exit "3.0.2" + tslib "1.9.3" + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -882,13 +939,20 @@ acorn@^7.1.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ== -agent-base@4, agent-base@^4.3.0: +agent-base@4, agent-base@^4.2.0, agent-base@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== dependencies: es6-promisify "^5.0.0" +agent-base@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" + integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== + dependencies: + es6-promisify "^5.0.0" + ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5: version "6.10.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" @@ -1237,6 +1301,11 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +ast-types@0.x.x: + version "0.13.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.2.tgz#df39b677a911a83f3a049644fb74fdded23cea48" + integrity sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA== + astral-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" @@ -1252,6 +1321,14 @@ async-limiter@~1.0.0: resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== +async-listener@^0.6.0: + version "0.6.10" + resolved "https://registry.yarnpkg.com/async-listener/-/async-listener-0.6.10.tgz#a7c97abe570ba602d782273c0de60a51e3e17cbc" + integrity sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw== + dependencies: + semver "^5.3.0" + shimmer "^1.1.0" + async-retry@^1.2.1: version "1.2.3" resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.2.3.tgz#a6521f338358d322b1a0012b79030c6f411d1ce0" @@ -1259,6 +1336,13 @@ async-retry@^1.2.1: dependencies: retry "0.12.0" +async@~2.6.1: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -1755,6 +1839,14 @@ content-type@~1.0.4: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== +continuation-local-storage@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz#11f613f74e914fe9b34c92ad2d28fe6ae1db7ffb" + integrity sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA== + dependencies: + async-listener "^0.6.0" + emitter-listener "^1.1.1" + convert-source-map@^1.4.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" @@ -1887,6 +1979,11 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +data-uri-to-buffer@1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835" + integrity sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ== + data-urls@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" @@ -1906,7 +2003,7 @@ date-and-time@^0.11.0: resolved "https://registry.yarnpkg.com/date-and-time/-/date-and-time-0.11.0.tgz#1e22b61533af303953d79cc8c5e92e228fc5e4d2" integrity sha512-VyzhHurex4wlg9oMszn7O+kxHchphWjzDn7Mv0WfkFKI6hSNOQePpTBFGsnRakvLNzQKXqPBAVV8DOxUGtUxqA== -debug@2.6.9, debug@^2.2.0, debug@^2.3.3: +debug@2, debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -1920,6 +2017,13 @@ debug@3.1.0: dependencies: ms "2.0.0" +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + debug@^3.1.0, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" @@ -1927,13 +2031,6 @@ debug@^3.1.0, debug@^3.2.6: dependencies: ms "^2.1.1" -debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== - dependencies: - ms "^2.1.1" - decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -2002,6 +2099,15 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" +degenerator@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095" + integrity sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU= + dependencies: + ast-types "0.x.x" + escodegen "1.x.x" + esprima "3.x.x" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -2132,6 +2238,13 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= +emitter-listener@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/emitter-listener/-/emitter-listener-1.1.2.tgz#56b140e8f6992375b3d7cb2cab1cc7432d9632e8" + integrity sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ== + dependencies: + shimmer "^1.2.0" + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -2213,6 +2326,18 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escodegen@1.x.x: + version "1.12.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.12.1.tgz#08770602a74ac34c7a90ca9229e7d51e379abc76" + integrity sha512-Q8t2YZ+0e0pc7NRVj3B4tSQ9rim1oi4Fh46k2xhJ2qOiEwhQfdjyEQddWdj7ZFaKmU+5104vn1qrcjEPWq+bgQ== + dependencies: + esprima "^3.1.3" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + escodegen@^1.9.1: version "1.12.0" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.12.0.tgz#f763daf840af172bb3a2b6dd7219c0e17f7ff541" @@ -2311,7 +2436,7 @@ espree@^6.1.2: acorn-jsx "^5.1.0" eslint-visitor-keys "^1.1.0" -esprima@^3.1.3: +esprima@3.x.x, esprima@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= @@ -2355,6 +2480,11 @@ event-target-shim@^5.0.0: resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== +eventemitter2@^5.0.1, eventemitter2@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-5.0.1.tgz#6197a095d5fb6b57e8942f6fd7eaad63a09c9452" + integrity sha1-YZegldX7a1folC9v1+qtY6CclFI= + eventemitter3@^3.1.0: version "3.1.2" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" @@ -2563,6 +2693,11 @@ file-entry-cache@^5.0.1: dependencies: flat-cache "^2.0.1" +file-uri-to-path@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -2704,6 +2839,14 @@ fsevents@^1.2.7: nan "^2.12.1" node-pre-gyp "^0.12.0" +ftp@~0.3.10: + version "0.3.10" + resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" + integrity sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0= + dependencies: + readable-stream "1.1.x" + xregexp "2.0.0" + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -2791,6 +2934,18 @@ get-stream@^4.0.0: dependencies: pump "^3.0.0" +get-uri@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.4.tgz#d4937ab819e218d4cb5ae18e4f5962bef169cc6a" + integrity sha512-v7LT/s8kVjs+Tx0ykk1I+H/rbpzkHvuIq87LmeXptcf5sNWm9uQiwjNAt94SJPA1zOlCntmnOlJvVWKmzsxG8Q== + dependencies: + data-uri-to-buffer "1" + debug "2" + extend "~3.0.2" + file-uri-to-path "1" + ftp "~0.3.10" + readable-stream "2" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -3097,7 +3252,7 @@ http-errors@1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" -http-errors@^1.7.3, http-errors@~1.7.2: +http-errors@1.7.3, http-errors@^1.7.3, http-errors@~1.7.2: version "1.7.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== @@ -3277,6 +3432,11 @@ ioredis@^4.0.0, ioredis@^4.6.3: redis-parser "^3.0.0" standard-as-callback "^2.0.1" +ip@1.1.5, ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + ipaddr.js@1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" @@ -4262,6 +4422,11 @@ lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +log-driver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" + integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg== + long@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" @@ -4287,7 +4452,7 @@ lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" -lru-cache@^5.0.0: +lru-cache@^5.0.0, lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== @@ -4458,6 +4623,11 @@ mkdirp@^0.5.0, mkdirp@^0.5.1: dependencies: minimist "0.0.8" +module-details-from-path@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" + integrity sha1-EUyUlnPiqKNenTV4hSeqN7Z52is= + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -4524,6 +4694,11 @@ neo-async@^2.6.0: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== +netmask@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" + integrity sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU= + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -4854,6 +5029,31 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +pac-proxy-agent@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-3.0.1.tgz#115b1e58f92576cac2eba718593ca7b0e37de2ad" + integrity sha512-44DUg21G/liUZ48dJpUSjZnFfZro/0K5JTyFYLBcmh9+T6Ooi4/i4efwUiEy0+4oQusCBqWdhv16XohIj1GqnQ== + dependencies: + agent-base "^4.2.0" + debug "^4.1.1" + get-uri "^2.0.0" + http-proxy-agent "^2.1.0" + https-proxy-agent "^3.0.0" + pac-resolver "^3.0.0" + raw-body "^2.2.0" + socks-proxy-agent "^4.0.1" + +pac-resolver@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-3.0.0.tgz#6aea30787db0a891704deb7800a722a7615a6f26" + integrity sha512-tcc38bsjuE3XZ5+4vP96OfhOugrX+JcnpUbhfuc4LuXBLQhoTthOstZeoQJBDnQUDYzYmdImKsbz0xSl1/9qeA== + dependencies: + co "^4.6.0" + degenerator "^1.0.4" + ip "^1.1.5" + netmask "^1.0.6" + thunkify "^2.1.2" + package-json@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed" @@ -5096,6 +5296,25 @@ proxy-addr@~2.0.5: forwarded "~0.1.2" ipaddr.js "1.9.0" +proxy-agent@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-3.1.1.tgz#7e04e06bf36afa624a1540be247b47c970bd3014" + integrity sha512-WudaR0eTsDx33O3EJE16PjBRZWcX8GqCEeERw1W3hZJgH/F2a46g7jty6UGty6NeJ4CKQy8ds2CJPMiyeqaTvw== + dependencies: + agent-base "^4.2.0" + debug "4" + http-proxy-agent "^2.1.0" + https-proxy-agent "^3.0.0" + lru-cache "^5.1.1" + pac-proxy-agent "^3.0.1" + proxy-from-env "^1.0.0" + socks-proxy-agent "^4.0.1" + +proxy-from-env@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" + integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4= + pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" @@ -5168,6 +5387,16 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" +raw-body@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" + integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== + dependencies: + bytes "3.1.0" + http-errors "1.7.3" + iconv-lite "0.4.24" + unpipe "1.0.0" + rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -5210,6 +5439,29 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" +readable-stream@1.1.x: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@2: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + "readable-stream@2 || 3", readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" @@ -5372,6 +5624,15 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-in-the-middle@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-5.0.2.tgz#ce3593007a61583b39ccdcd2c167a2a326c670b2" + integrity sha512-l2r6F9i6t5xp4OE9cw/daB/ooQKHZOOW1AYPADhEvk/Tj/THJDS8gePp76Zyuht6Cj57a0KL+eHK5Dyv7wZnKA== + dependencies: + debug "^4.1.1" + module-details-from-path "^1.0.3" + resolve "^1.12.0" + require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -5411,6 +5672,13 @@ resolve@^1.10.0, resolve@^1.3.2: dependencies: path-parse "^1.0.6" +resolve@^1.12.0: + version "1.14.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.2.tgz#dbf31d0fa98b1f29aa5169783b9c290cb865fea2" + integrity sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ== + dependencies: + path-parse "^1.0.6" + restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" @@ -5534,6 +5802,11 @@ semver-diff@^2.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== +semver@5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" + integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== + semver@^6.0.0, semver@^6.1.2, semver@^6.2.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" @@ -5625,6 +5898,11 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== +shimmer@^1.1.0, shimmer@^1.2.0, shimmer@~1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" + integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== + should-equal@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/should-equal/-/should-equal-2.0.0.tgz#6072cf83047360867e68e98b09d71143d04ee0c3" @@ -5669,7 +5947,7 @@ should@^13.2.3: should-type-adaptors "^1.0.1" should-util "^1.0.0" -signal-exit@^3.0.0, signal-exit@^3.0.2: +signal-exit@3.0.2, signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= @@ -5698,6 +5976,11 @@ slice-ansi@^2.1.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" +smart-buffer@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" + integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== + snakeize@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/snakeize/-/snakeize-0.1.0.tgz#10c088d8b58eb076b3229bb5a04e232ce126422d" @@ -5733,6 +6016,22 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" +socks-proxy-agent@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz#3c8991f3145b2799e70e11bd5fbc8b1963116386" + integrity sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg== + dependencies: + agent-base "~4.2.1" + socks "~2.3.2" + +socks@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.3.tgz#01129f0a5d534d2b897712ed8aceab7ee65d78e3" + integrity sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA== + dependencies: + ip "1.1.5" + smart-buffer "^4.1.0" + source-map-resolve@^0.5.0: version "0.5.2" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" @@ -6136,6 +6435,11 @@ through@^2.3.6: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +thunkify@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d" + integrity sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0= + timed-out@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" @@ -6230,6 +6534,11 @@ tslib@1.10.0, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== +tslib@1.9.3: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -6404,7 +6713,7 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= -uuid@^3.1.0, uuid@^3.3.2: +uuid@^3.1.0, uuid@^3.2.1, uuid@^3.3.2: version "3.3.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== @@ -6635,6 +6944,11 @@ xml-name-validator@^3.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +xregexp@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" + integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= + xtend@^4.0.1, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"