Feat: save highVolumeTxs on DB and plugins code refactor

Fix: remove unused module and add space before '('

Chore: add jest tests for transactionNotify
This commit is contained in:
Cesar 2020-12-04 19:58:17 +00:00 committed by Josh Harvey
parent 75bfb4b991
commit 2ced230020
8 changed files with 646 additions and 414 deletions

View file

@ -1,4 +1,3 @@
const uuid = require('uuid')
const _ = require('lodash/fp')
const argv = require('minimist')(process.argv.slice(2))
const crypto = require('crypto')
@ -24,6 +23,8 @@ const coinUtils = require('./coin-utils')
const commissionMath = require('./commission-math')
const promoCodes = require('./promo-codes')
const notifier = require('./notifier/index')
const mapValuesWithKey = _.mapValues.convert({
cap: false
})
@ -54,7 +55,8 @@ function plugins (settings, deviceId) {
? undefined
: BN(1).add(BN(commissions.cashOut).div(100))
if (Date.now() - rateRec.timestamp > STALE_TICKER) return logger.warn('Stale rate for ' + cryptoCode)
if (Date.now() - rateRec.timestamp > STALE_TICKER)
return logger.warn('Stale rate for ' + cryptoCode)
const rate = rateRec.rates
withCommission ? rates[cryptoCode] = {
@ -88,8 +90,10 @@ function plugins (settings, deviceId) {
cryptoCodes.forEach((cryptoCode, i) => {
const balanceRec = balanceRecs[i]
if (!balanceRec) return logger.warn('No balance for ' + cryptoCode + ' yet')
if (Date.now() - balanceRec.timestamp > STALE_BALANCE) return logger.warn('Stale balance for ' + cryptoCode)
if (!balanceRec)
return logger.warn('No balance for ' + cryptoCode + ' yet')
if (Date.now() - balanceRec.timestamp > STALE_BALANCE)
return logger.warn('Stale balance for ' + cryptoCode)
balances[cryptoCode] = balanceRec.balance
})
@ -109,10 +113,13 @@ function plugins (settings, deviceId) {
const sumTxs = (sum, tx) => {
const bills = tx.bills
const sameDenominations = a => a[0].denomination === a[1].denomination
const doDenominationsMatch = _.every(sameDenominations, _.zip(cassettes, bills))
const doDenominationsMatch = _.every(
sameDenominations,
_.zip(cassettes, bills)
)
if (!doDenominationsMatch) {
throw new Error('Denominations don\'t add up, cassettes were changed.')
throw new Error("Denominations don't add up, cassettes were changed.")
}
return _.map(r => r[0] + r[1].provisioned, _.zip(sum, tx.bills))
@ -155,30 +162,41 @@ function plugins (settings, deviceId) {
? argv.cassettes.split(',')
: rec.counts
const cassettes = [
{
denomination: parseInt(denominations[0], 10),
count: parseInt(counts[0], 10)
},
{
denomination: parseInt(denominations[1], 10),
count: parseInt(counts[1], 10)
}
]
return Promise.all([
dbm.cassetteCounts(deviceId),
cashOutHelper.redeemableTxs(deviceId, excludeTxId)
]).then(([rec, _redeemableTxs]) => {
const redeemableTxs = _.reject(
_.matchesProperty('id', excludeTxId),
_redeemableTxs
)
try {
return {
cassettes: computeAvailableCassettes(cassettes, redeemableTxs),
virtualCassettes
}
} catch (err) {
logger.error(err)
return {
cassettes,
virtualCassettes
}
const counts = argv.cassettes ? argv.cassettes.split(',') : rec.counts
const cassettes = [
{
denomination: parseInt(denominations[0], 10),
count: parseInt(counts[0], 10)
},
{
denomination: parseInt(denominations[1], 10),
count: parseInt(counts[1], 10)
}
})
]
try {
return {
cassettes: computeAvailableCassettes(cassettes, redeemableTxs),
virtualCassettes
}
} catch (err) {
logger.error(err)
return {
cassettes,
virtualCassettes
}
}
})
}
function fetchCurrentConfigVersion () {
@ -188,18 +206,23 @@ function plugins (settings, deviceId) {
order by id desc
limit 1`
return db.one(sql, ['config'])
.then(row => row.id)
return db.one(sql, ['config']).then(row => row.id)
}
function mapCoinSettings (coinParams) {
const cryptoCode = coinParams[0]
const cryptoNetwork = coinParams[1]
const commissions = configManager.getCommissions(cryptoCode, deviceId, settings.config)
const commissions = configManager.getCommissions(
cryptoCode,
deviceId,
settings.config
)
const minimumTx = BN(commissions.minimumTx)
const cashInFee = BN(commissions.fixedFee)
const cashInCommission = BN(commissions.cashIn)
const cashOutCommission = _.isNumber(commissions.cashOut) ? BN(commissions.cashOut) : null
const cashOutCommission = _.isNumber(commissions.cashOut)
? BN(commissions.cashOut)
: null
const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode)
return {
@ -213,15 +236,25 @@ function plugins (settings, deviceId) {
}
}
function pollQueries (serialNumber, deviceTime, deviceRec, machineVersion, machineModel) {
function pollQueries (
serialNumber,
deviceTime,
deviceRec,
machineVersion,
machineModel
) {
const localeConfig = configManager.getLocale(deviceId, settings.config)
const fiatCode = localeConfig.fiatCurrency
const cryptoCodes = localeConfig.cryptoCurrencies
const tickerPromises = cryptoCodes.map(c => ticker.getRates(settings, fiatCode, c))
const tickerPromises = cryptoCodes.map(c =>
ticker.getRates(settings, fiatCode, c)
)
const balancePromises = cryptoCodes.map(c => fiatBalance(fiatCode, c))
const testnetPromises = cryptoCodes.map(c => wallet.cryptoNetwork(settings, c))
const testnetPromises = cryptoCodes.map(c =>
wallet.cryptoNetwork(settings, c)
)
const pingPromise = recordPing(deviceTime, machineVersion, machineModel)
const currentConfigVersionPromise = fetchCurrentConfigVersion()
const currentAvailablePromoCodes = promoCodes.getNumberOfAvailablePromoCodes()
@ -256,7 +289,12 @@ function plugins (settings, deviceId) {
}
function sendCoins (tx) {
return wallet.sendCoins(settings, tx.toAddress, tx.cryptoAtoms, tx.cryptoCode)
return wallet.sendCoins(
settings,
tx.toAddress,
tx.cryptoAtoms,
tx.cryptoCode
)
}
function recordPing (deviceTime, version, model) {
@ -267,11 +305,18 @@ function plugins (settings, deviceId) {
}
return Promise.all([
db.none(`insert into machine_pings(device_id, device_time) values($1, $2)
ON CONFLICT (device_id) DO UPDATE SET device_time = $2, updated = now()`, [deviceId, deviceTime]),
db.none(pgp.helpers.update(devices, null, 'devices') + 'WHERE device_id = ${deviceId}', {
deviceId
})
db.none(
`insert into machine_pings(device_id, device_time) values($1, $2)
ON CONFLICT (device_id) DO UPDATE SET device_time = $2, updated = now()`,
[deviceId, deviceTime]
),
db.none(
pgp.helpers.update(devices, null, 'devices') +
'WHERE device_id = ${deviceId}',
{
deviceId
}
)
])
}
@ -303,34 +348,37 @@ function plugins (settings, deviceId) {
}
function fiatBalance (fiatCode, cryptoCode) {
const commissions = configManager.getCommissions(cryptoCode, deviceId, settings.config)
const commissions = configManager.getCommissions(
cryptoCode,
deviceId,
settings.config
)
return Promise.all([
ticker.getRates(settings, fiatCode, cryptoCode),
wallet.balance(settings, cryptoCode)
])
.then(([rates, balanceRec]) => {
if (!rates || !balanceRec) return null
]).then(([rates, balanceRec]) => {
if (!rates || !balanceRec) return null
const rawRate = rates.rates.ask
const cashInCommission = BN(1).minus(BN(commissions.cashIn).div(100))
const balance = balanceRec.balance
const rawRate = rates.rates.ask
const cashInCommission = BN(1).minus(BN(commissions.cashIn).div(100))
const balance = balanceRec.balance
if (!rawRate || !balance) return null
if (!rawRate || !balance) return null
const rate = rawRate.div(cashInCommission)
const rate = rawRate.div(cashInCommission)
const lowBalanceMargin = BN(1.03)
const lowBalanceMargin = BN(1.03)
const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode)
const unitScale = cryptoRec.unitScale
const shiftedRate = rate.shift(-unitScale)
const fiatTransferBalance = balance.mul(shiftedRate).div(lowBalanceMargin)
const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode)
const unitScale = cryptoRec.unitScale
const shiftedRate = rate.shift(-unitScale)
const fiatTransferBalance = balance.mul(shiftedRate).div(lowBalanceMargin)
return {
timestamp: balanceRec.timestamp,
balance: fiatTransferBalance.truncated().toString()
}
})
return {
timestamp: balanceRec.timestamp,
balance: fiatTransferBalance.truncated().toString()
}
})
}
function notifyConfirmation (tx) {
@ -345,100 +393,31 @@ function plugins (settings, deviceId) {
}
}
return sms.sendMessage(settings, rec)
.then(() => {
const sql = 'update cash_out_txs set notified=$1 where id=$2'
const values = [true, tx.id]
return sms.sendMessage(settings, rec).then(() => {
const sql = 'update cash_out_txs set notified=$1 where id=$2'
const values = [true, tx.id]
return db.none(sql, values)
})
return db.none(sql, values)
})
}
function notifyOperator (tx, rec) {
const notifications = configManager.getGlobalNotifications(settings.config)
const notificationsEnabled = notifications.sms.transactions || notifications.email.transactions
const highValueTx = tx.fiat.gt(notifications.highValueTransaction || Infinity)
if (!notificationsEnabled && !highValueTx) return Promise.resolve()
const isCashOut = tx.direction === 'cashOut'
const zeroConf = isCashOut && isZeroConf(tx)
// 0-conf cash-out should only send notification on redemption
if (zeroConf && isCashOut && !rec.isRedemption && !rec.error) return Promise.resolve()
if (!zeroConf && rec.isRedemption) return sendRedemptionMessage(tx.id, rec.error)
const customerPromise = tx.customerId ? customers.getById(tx.customerId) : Promise.resolve({})
return Promise.all([machineLoader.getMachineName(tx.deviceId), customerPromise])
.then(([machineName, customer]) => {
const direction = isCashOut ? 'Cash Out' : 'Cash In'
const crypto = `${coinUtils.toUnit(tx.cryptoAtoms, tx.cryptoCode)} ${tx.cryptoCode}`
const fiat = `${tx.fiat} ${tx.fiatCode}`
const customerName = customer.name || customer.id
const phone = customer.phone ? `- Phone: ${customer.phone}` : ''
let status
if (rec.error) {
status = `Error - ${rec.error}`
} else {
status = !isCashOut ? 'Successful' : !rec.isRedemption
? 'Successful & awaiting redemption' : 'Successful & dispensed'
}
const body = `
- Transaction ID: ${tx.id}
- Status: ${status}
- Machine name: ${machineName}
- ${direction}
- ${fiat}
- ${crypto}
- Customer: ${customerName}
${phone}
`
const smsSubject = `A ${highValueTx ? 'high value ' : ''}${direction.toLowerCase()} transaction just happened at ${machineName} for ${fiat}`
const emailSubject = `A ${highValueTx ? 'high value ' : ''}transaction just happened`
return [{
sms: {
body: `${smsSubject} ${status}`
},
email: {
emailSubject,
body
}
}, highValueTx]
})
.then(([rec, highValueTx]) => sendTransactionMessage(rec, highValueTx))
}
function sendRedemptionMessage (txId, error) {
const subject = `Here's an update on transaction ${txId}`
const body = error ? `Error: ${error}` : 'It was just dispensed successfully'
const rec = {
sms: {
body: `${subject} - ${body}`
},
email: {
subject,
body
}
}
return sendTransactionMessage(rec)
// notify operator about new transaction and add high volume txs to database
return notifier.transactionNotify(tx, rec)
}
function clearOldLogs () {
return logs.clearOldLogs()
.catch(logger.error)
return logs.clearOldLogs().catch(logger.error)
}
function pong () {
return db.none(`UPDATE server_events SET created=now() WHERE event_type=$1;
return db
.none(
`UPDATE server_events SET created=now() WHERE event_type=$1;
INSERT INTO server_events (event_type) SELECT $1
WHERE NOT EXISTS (SELECT 1 FROM server_events WHERE event_type=$1);`, ['ping'])
WHERE NOT EXISTS (SELECT 1 FROM server_events WHERE event_type=$1);`,
['ping']
)
.catch(logger.error)
}
@ -479,15 +458,18 @@ function plugins (settings, deviceId) {
const marketTradesQueues = tradesQueues[market]
if (!marketTradesQueues || marketTradesQueues.length === 0) return null
logger.debug('[%s] tradesQueues size: %d', market, marketTradesQueues.length)
logger.debug(
'[%s] tradesQueues size: %d',
market,
marketTradesQueues.length
)
logger.debug('[%s] tradesQueues head: %j', market, marketTradesQueues[0])
const t1 = Date.now()
const filtered = marketTradesQueues
.filter(tradeEntry => {
return t1 - tradeEntry.timestamp < TRADE_TTL
})
const filtered = marketTradesQueues.filter(tradeEntry => {
return t1 - tradeEntry.timestamp < TRADE_TTL
})
const filteredCount = marketTradesQueues.length - filtered.length
@ -498,10 +480,14 @@ function plugins (settings, deviceId) {
if (filtered.length === 0) return null
const cryptoAtoms = filtered
.reduce((prev, current) => prev.plus(current.cryptoAtoms), BN(0))
const cryptoAtoms = filtered.reduce(
(prev, current) => prev.plus(current.cryptoAtoms),
BN(0)
)
const timestamp = filtered.map(r => r.timestamp).reduce((acc, r) => Math.max(acc, r), 0)
const timestamp = filtered
.map(r => r.timestamp)
.reduce((acc, r) => Math.max(acc, r), 0)
const consolidatedTrade = {
fiatCode,
@ -517,11 +503,15 @@ function plugins (settings, deviceId) {
}
function executeTrades () {
return machineLoader.getMachines()
return machineLoader
.getMachines()
.then(devices => {
const deviceIds = devices.map(device => device.deviceId)
const lists = deviceIds.map(deviceId => {
const localeConfig = configManager.getLocale(deviceId, settings.config)
const localeConfig = configManager.getLocale(
deviceId,
settings.config
)
const fiatCode = localeConfig.fiatCurrency
const cryptoCodes = localeConfig.cryptoCurrencies
@ -531,8 +521,9 @@ function plugins (settings, deviceId) {
}))
})
const tradesPromises = _.uniq(_.flatten(lists))
.map(r => executeTradesForMarket(settings, r.fiatCode, r.cryptoCode))
const tradesPromises = _.uniq(_.flatten(lists)).map(r =>
executeTradesForMarket(settings, r.fiatCode, r.cryptoCode)
)
return Promise.all(tradesPromises)
})
@ -547,41 +538,43 @@ function plugins (settings, deviceId) {
if (tradeEntry === null || tradeEntry.cryptoAtoms.eq(0)) return
return executeTradeForType(tradeEntry)
.catch(err => {
tradesQueues[market].push(tradeEntry)
if (err.name === 'orderTooSmall') return logger.debug(err.message)
logger.error(err)
})
return executeTradeForType(tradeEntry).catch(err => {
tradesQueues[market].push(tradeEntry)
if (err.name === 'orderTooSmall') return logger.debug(err.message)
logger.error(err)
})
}
function executeTradeForType (_tradeEntry) {
const expand = te => _.assign(te, {
cryptoAtoms: te.cryptoAtoms.abs(),
type: te.cryptoAtoms.gte(0) ? 'buy' : 'sell'
})
const expand = te =>
_.assign(te, {
cryptoAtoms: te.cryptoAtoms.abs(),
type: te.cryptoAtoms.gte(0) ? 'buy' : 'sell'
})
const tradeEntry = expand(_tradeEntry)
const execute = tradeEntry.type === 'buy' ? exchange.buy : exchange.sell
return execute(settings, tradeEntry.cryptoAtoms, tradeEntry.fiatCode, tradeEntry.cryptoCode)
return execute(
settings,
tradeEntry.cryptoAtoms,
tradeEntry.fiatCode,
tradeEntry.cryptoCode
)
.then(() => recordTrade(tradeEntry))
.catch(err => {
return recordTrade(tradeEntry, err)
.then(() => {
throw err
})
return recordTrade(tradeEntry, err).then(() => {
throw err
})
})
}
function convertBigNumFields (obj) {
const convert = (value, key) => _.includes(key, ['cryptoAtoms', 'fiat'])
? value.toString()
: value
const convert = (value, key) =>
_.includes(key, ['cryptoAtoms', 'fiat']) ? value.toString() : value
const convertKey = key => _.includes(key, ['cryptoAtoms', 'fiat'])
? key + '#'
: key
const convertKey = key =>
_.includes(key, ['cryptoAtoms', 'fiat']) ? key + '#' : key
return _.mapKeys(convertKey, mapValuesWithKey(convert, obj))
}
@ -611,22 +604,10 @@ function plugins (settings, deviceId) {
const notifications = configManager.getGlobalNotifications(settings.config)
let promises = []
if (notifications.email.active && rec.email) promises.push(email.sendMessage(settings, rec))
if (notifications.sms.active && rec.sms) promises.push(sms.sendMessage(settings, rec))
return Promise.all(promises)
}
function sendTransactionMessage (rec, isHighValueTx) {
const notifications = configManager.getGlobalNotifications(settings.config)
let promises = []
const emailActive = notifications.email.active && (notifications.email.transactions || isHighValueTx)
if (emailActive) promises.push(email.sendMessage(settings, rec))
const smsActive = notifications.sms.active && (notifications.sms.transactions || isHighValueTx)
if (smsActive) promises.push(sms.sendMessage(settings, rec))
if (notifications.email.active && rec.email)
promises.push(email.sendMessage(settings, rec))
if (notifications.sms.active && rec.sms)
promises.push(sms.sendMessage(settings, rec))
return Promise.all(promises)
}
@ -636,53 +617,64 @@ function plugins (settings, deviceId) {
}
function checkDeviceCashBalances (fiatCode, device) {
const cashOutConfig = configManager.getCashOut(device.deviceId, settings.config)
const cashOutConfig = configManager.getCashOut(
device.deviceId,
settings.config
)
const denomination1 = cashOutConfig.top
const denomination2 = cashOutConfig.bottom
const cashOutEnabled = cashOutConfig.active
const notifications = configManager.getNotifications(null, device.deviceId, settings.config)
const notifications = configManager.getNotifications(
null,
device.deviceId,
settings.config
)
const machineName = device.name
const cashInAlert = device.cashbox > notifications.cashInAlertThreshold
? {
code: 'CASH_BOX_FULL',
machineName,
deviceId: device.deviceId,
notes: device.cashbox
}
: null
const cashInAlert =
device.cashbox > notifications.cashInAlertThreshold
? {
code: 'CASH_BOX_FULL',
machineName,
deviceId: device.deviceId,
notes: device.cashbox
}
: null
const cassette1Alert = cashOutEnabled && device.cassette1 < notifications.fiatBalanceCassette1
? {
code: 'LOW_CASH_OUT',
cassette: 1,
machineName,
deviceId: device.deviceId,
notes: device.cassette1,
denomination: denomination1,
fiatCode
}
: null
const cassette1Alert =
cashOutEnabled && device.cassette1 < notifications.fiatBalanceCassette1
? {
code: 'LOW_CASH_OUT',
cassette: 1,
machineName,
deviceId: device.deviceId,
notes: device.cassette1,
denomination: denomination1,
fiatCode
}
: null
const cassette2Alert = cashOutEnabled && device.cassette2 < notifications.fiatBalanceCassette2
? {
code: 'LOW_CASH_OUT',
cassette: 2,
machineName,
deviceId: device.deviceId,
notes: device.cassette2,
denomination: denomination2,
fiatCode
}
: null
const cassette2Alert =
cashOutEnabled && device.cassette2 < notifications.fiatBalanceCassette2
? {
code: 'LOW_CASH_OUT',
cassette: 2,
machineName,
deviceId: device.deviceId,
notes: device.cassette2,
denomination: denomination2,
fiatCode
}
: null
return _.compact([cashInAlert, cassette1Alert, cassette2Alert])
}
function checkCryptoBalances (fiatCode, devices) {
const fiatBalancePromises = cryptoCodes => _.map(c => fiatBalance(fiatCode, c), cryptoCodes)
const fiatBalancePromises = cryptoCodes =>
_.map(c => fiatBalance(fiatCode, c), cryptoCodes)
const fetchCryptoCodes = _deviceId => {
const localeConfig = configManager.getLocale(_deviceId, settings.config)
@ -693,8 +685,9 @@ function plugins (settings, deviceId) {
const cryptoCodes = union(devices)
const checkCryptoBalanceWithFiat = _.partial(checkCryptoBalance, [fiatCode])
return Promise.all(fiatBalancePromises(cryptoCodes))
.then(balances => _.map(checkCryptoBalanceWithFiat, _.zip(cryptoCodes, balances)))
return Promise.all(fiatBalancePromises(cryptoCodes)).then(balances =>
_.map(checkCryptoBalanceWithFiat, _.zip(cryptoCodes, balances))
)
}
function checkCryptoBalance (fiatCode, rec) {
@ -702,20 +695,30 @@ function plugins (settings, deviceId) {
if (!fiatBalance) return null
const notifications = configManager.getNotifications(cryptoCode, null, settings.config)
const notifications = configManager.getNotifications(
cryptoCode,
null,
settings.config
)
const lowAlertThreshold = notifications.cryptoLowBalance
const highAlertThreshold = notifications.cryptoHighBalance
const req = {
cryptoCode,
fiatBalance,
fiatCode,
fiatCode
}
if (_.isFinite(lowAlertThreshold) && BN(fiatBalance.balance).lt(lowAlertThreshold))
if (
_.isFinite(lowAlertThreshold) &&
BN(fiatBalance.balance).lt(lowAlertThreshold)
)
return _.set('code')('LOW_CRYPTO_BALANCE')(req)
if (_.isFinite(highAlertThreshold) && BN(fiatBalance.balance).gt(highAlertThreshold))
if (
_.isFinite(highAlertThreshold) &&
BN(fiatBalance.balance).gt(highAlertThreshold)
)
return _.set('code')('HIGH_CRYPTO_BALANCE')(req)
return null
@ -725,24 +728,23 @@ function plugins (settings, deviceId) {
const localeConfig = configManager.getGlobalLocale(settings.config)
const fiatCode = localeConfig.fiatCurrency
return machineLoader.getMachines()
.then(devices => {
return Promise.all([
checkCryptoBalances(fiatCode, devices),
checkDevicesCashBalances(fiatCode, devices)
])
.then(_.flow(_.flattenDeep, _.compact))
})
return machineLoader.getMachines().then(devices => {
return Promise.all([
checkCryptoBalances(fiatCode, devices),
checkDevicesCashBalances(fiatCode, devices)
]).then(_.flow(_.flattenDeep, _.compact))
})
}
function randomCode () {
return BN(crypto.randomBytes(3).toString('hex'), 16).shift(-6).toFixed(6).slice(-6)
return BN(crypto.randomBytes(3).toString('hex'), 16)
.shift(-6)
.toFixed(6)
.slice(-6)
}
function getPhoneCode (phone) {
const code = argv.mockSms
? '123'
: randomCode()
const code = argv.mockSms ? '123' : randomCode()
const rec = {
sms: {
@ -751,14 +753,14 @@ function plugins (settings, deviceId) {
}
}
return sms.sendMessage(settings, rec)
.then(() => code)
return sms.sendMessage(settings, rec).then(() => code)
}
function sweepHdRow (row) {
const cryptoCode = row.crypto_code
return wallet.sweep(settings, cryptoCode, row.hd_index)
return wallet
.sweep(settings, cryptoCode, row.hd_index)
.then(txHash => {
if (txHash) {
logger.debug('[%s] Swept address with tx: %s', cryptoCode, txHash)
@ -769,14 +771,17 @@ function plugins (settings, deviceId) {
return db.none(sql, row.id)
}
})
.catch(err => logger.error('[%s] Sweep error: %s', cryptoCode, err.message))
.catch(err =>
logger.error('[%s] Sweep error: %s', cryptoCode, err.message)
)
}
function sweepHd () {
const sql = `select id, crypto_code, hd_index from cash_out_txs
where hd_index is not null and not swept and status in ('confirmed', 'instant')`
return db.any(sql)
return db
.any(sql)
.then(rows => Promise.all(rows.map(sweepHdRow)))
.catch(err => logger.error(err))
}
@ -790,14 +795,15 @@ function plugins (settings, deviceId) {
const fiatCode = localeConfig.fiatCurrency
const cryptoCodes = configManager.getAllCryptoCurrencies(settings.config)
const tickerPromises = cryptoCodes.map(c => ticker.getRates(settings, fiatCode, c))
const tickerPromises = cryptoCodes.map(c =>
ticker.getRates(settings, fiatCode, c)
)
return Promise.all(tickerPromises)
}
function getRates () {
return getRawRates()
.then(buildRates)
return getRawRates().then(buildRates)
}
return {