Log unsuccessful trade attempts in the db (#224)
* Save trade error in table trades * Add one migration, Add error column to trades table * changes in plugins.js file * Apply standardjs to plugins.js file
This commit is contained in:
parent
13040f41a1
commit
907cd08cf1
3 changed files with 103 additions and 41 deletions
127
lib/plugins.js
127
lib/plugins.js
|
|
@ -21,7 +21,9 @@ const machineLoader = require('./machine-loader')
|
||||||
const customers = require('./customers')
|
const customers = require('./customers')
|
||||||
const coinUtils = require('./coin-utils')
|
const coinUtils = require('./coin-utils')
|
||||||
|
|
||||||
const mapValuesWithKey = _.mapValues.convert({cap: false})
|
const mapValuesWithKey = _.mapValues.convert({
|
||||||
|
cap: false
|
||||||
|
})
|
||||||
|
|
||||||
const TRADE_TTL = 2 * T.minutes
|
const TRADE_TTL = 2 * T.minutes
|
||||||
const STALE_TICKER = 3 * T.minutes
|
const STALE_TICKER = 3 * T.minutes
|
||||||
|
|
@ -132,8 +134,9 @@ function plugins (settings, deviceId) {
|
||||||
|
|
||||||
if (!config.cashOutEnabled) return Promise.resolve()
|
if (!config.cashOutEnabled) return Promise.resolve()
|
||||||
|
|
||||||
const denominations = [ config.topCashOutDenomination,
|
const denominations = [config.topCashOutDenomination,
|
||||||
config.bottomCashOutDenomination ]
|
config.bottomCashOutDenomination
|
||||||
|
]
|
||||||
const virtualCassettes = [config.virtualCashOutDenomination]
|
const virtualCassettes = [config.virtualCashOutDenomination]
|
||||||
|
|
||||||
return Promise.all([dbm.cassetteCounts(deviceId), cashOutHelper.redeemableTxs(deviceId, excludeTxId)])
|
return Promise.all([dbm.cassetteCounts(deviceId), cashOutHelper.redeemableTxs(deviceId, excludeTxId)])
|
||||||
|
|
@ -162,17 +165,20 @@ function plugins (settings, deviceId) {
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err)
|
logger.error(err)
|
||||||
return {cassettes, virtualCassettes}
|
return {
|
||||||
|
cassettes,
|
||||||
|
virtualCassettes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchCurrentConfigVersion () {
|
function fetchCurrentConfigVersion () {
|
||||||
const sql = `select id from user_config
|
const sql = `select id from user_config
|
||||||
where type=$1
|
where type=$1
|
||||||
and valid
|
and valid
|
||||||
order by id desc
|
order by id desc
|
||||||
limit 1`
|
limit 1`
|
||||||
|
|
||||||
return db.one(sql, ['config'])
|
return db.one(sql, ['config'])
|
||||||
.then(row => row.id)
|
.then(row => row.id)
|
||||||
|
|
@ -243,8 +249,10 @@ function plugins (settings, deviceId) {
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
db.none(`insert into machine_pings(device_id, device_time) values($1, $2)
|
db.none(`insert into machine_pings(device_id, device_time) values($1, $2)
|
||||||
ON CONFLICT (device_id) DO UPDATE SET device_time = $2`, [deviceId, deviceTime]),
|
ON CONFLICT (device_id) DO UPDATE SET device_time = $2`, [deviceId, deviceTime]),
|
||||||
db.none(pgp.helpers.update(devices, null, 'devices') + 'WHERE device_id = ${deviceId}', { deviceId })
|
db.none(pgp.helpers.update(devices, null, 'devices') + 'WHERE device_id = ${deviceId}', {
|
||||||
|
deviceId
|
||||||
|
})
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,8 +278,9 @@ function plugins (settings, deviceId) {
|
||||||
|
|
||||||
function dispenseAck (tx) {
|
function dispenseAck (tx) {
|
||||||
const config = configManager.machineScoped(deviceId, settings.config)
|
const config = configManager.machineScoped(deviceId, settings.config)
|
||||||
const cassettes = [ config.topCashOutDenomination,
|
const cassettes = [config.topCashOutDenomination,
|
||||||
config.bottomCashOutDenomination ]
|
config.bottomCashOutDenomination
|
||||||
|
]
|
||||||
|
|
||||||
return dbm.addDispense(deviceId, tx, cassettes)
|
return dbm.addDispense(deviceId, tx, cassettes)
|
||||||
}
|
}
|
||||||
|
|
@ -300,7 +309,10 @@ function plugins (settings, deviceId) {
|
||||||
const shiftedRate = rate.shift(-unitScale)
|
const shiftedRate = rate.shift(-unitScale)
|
||||||
const fiatTransferBalance = balance.mul(shiftedRate).div(lowBalanceMargin)
|
const fiatTransferBalance = balance.mul(shiftedRate).div(lowBalanceMargin)
|
||||||
|
|
||||||
return {timestamp: balanceRec.timestamp, balance: fiatTransferBalance.truncated().toString()}
|
return {
|
||||||
|
timestamp: balanceRec.timestamp,
|
||||||
|
balance: fiatTransferBalance.truncated().toString()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -355,15 +367,15 @@ function plugins (settings, deviceId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const body = `
|
const body = `
|
||||||
- Transaction ID: ${tx.id}
|
- Transaction ID: ${tx.id}
|
||||||
- Status: ${status}
|
- Status: ${status}
|
||||||
- Machine name: ${machineName}
|
- Machine name: ${machineName}
|
||||||
- ${direction}
|
- ${direction}
|
||||||
- ${fiat}
|
- ${fiat}
|
||||||
- ${crypto}
|
- ${crypto}
|
||||||
- Customer: ${customerName}
|
- Customer: ${customerName}
|
||||||
${phone}
|
${phone}
|
||||||
`
|
`
|
||||||
const subject = `A transaction just happened`
|
const subject = `A transaction just happened`
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -387,7 +399,10 @@ function plugins (settings, deviceId) {
|
||||||
sms: {
|
sms: {
|
||||||
body: `${subject} - ${body}`
|
body: `${subject} - ${body}`
|
||||||
},
|
},
|
||||||
email: { subject, body }
|
email: {
|
||||||
|
subject,
|
||||||
|
body
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return sendTransactionMessage(rec)
|
return sendTransactionMessage(rec)
|
||||||
}
|
}
|
||||||
|
|
@ -399,16 +414,16 @@ function plugins (settings, deviceId) {
|
||||||
|
|
||||||
function pongClear () {
|
function pongClear () {
|
||||||
const sql = `delete from server_events
|
const sql = `delete from server_events
|
||||||
where event_type=$1
|
where event_type=$1
|
||||||
and created < now() - interval $2`
|
and created < now() - interval $2`
|
||||||
|
|
||||||
db.none(sql, ['ping', PONG_TTL])
|
db.none(sql, ['ping', PONG_TTL])
|
||||||
.catch(logger.error)
|
.catch(logger.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Trader functions
|
* Trader functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function buy (rec) {
|
function buy (rec) {
|
||||||
return buyAndSell(rec, true)
|
return buyAndSell(rec, true)
|
||||||
|
|
@ -489,7 +504,10 @@ function plugins (settings, deviceId) {
|
||||||
const fiatCode = config.fiatCurrency
|
const fiatCode = config.fiatCurrency
|
||||||
const cryptoCodes = config.cryptoCurrencies
|
const cryptoCodes = config.cryptoCurrencies
|
||||||
|
|
||||||
return cryptoCodes.map(cryptoCode => ({fiatCode, cryptoCode}))
|
return cryptoCodes.map(cryptoCode => ({
|
||||||
|
fiatCode,
|
||||||
|
cryptoCode
|
||||||
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
const tradesPromises = _.uniq(_.flatten(lists))
|
const tradesPromises = _.uniq(_.flatten(lists))
|
||||||
|
|
@ -527,6 +545,12 @@ function plugins (settings, deviceId) {
|
||||||
|
|
||||||
return execute(settings, tradeEntry.cryptoAtoms, tradeEntry.fiatCode, tradeEntry.cryptoCode)
|
return execute(settings, tradeEntry.cryptoAtoms, tradeEntry.fiatCode, tradeEntry.cryptoCode)
|
||||||
.then(() => recordTrade(tradeEntry))
|
.then(() => recordTrade(tradeEntry))
|
||||||
|
.catch(err => {
|
||||||
|
return recordTrade(tradeEntry, err)
|
||||||
|
.then(() => {
|
||||||
|
throw err
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertBigNumFields (obj) {
|
function convertBigNumFields (obj) {
|
||||||
|
|
@ -541,14 +565,23 @@ function plugins (settings, deviceId) {
|
||||||
return _.mapKeys(convertKey, mapValuesWithKey(convert, obj))
|
return _.mapKeys(convertKey, mapValuesWithKey(convert, obj))
|
||||||
}
|
}
|
||||||
|
|
||||||
function recordTrade (_tradeEntry) {
|
function mergeTradeEntryAndError (tradeEntry, error) {
|
||||||
|
if (error && error.message) {
|
||||||
|
return Object.assign({}, tradeEntry, {
|
||||||
|
error: error.message.slice(0, 200)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return tradeEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
function recordTrade (_tradeEntry, error) {
|
||||||
const massage = _.flow(
|
const massage = _.flow(
|
||||||
_.pick(['cryptoCode', 'cryptoAtoms', 'fiatCode', 'type']),
|
mergeTradeEntryAndError,
|
||||||
|
_.pick(['cryptoCode', 'cryptoAtoms', 'fiatCode', 'type', 'error']),
|
||||||
convertBigNumFields,
|
convertBigNumFields,
|
||||||
_.mapKeys(_.snakeCase)
|
_.mapKeys(_.snakeCase)
|
||||||
)
|
)
|
||||||
|
const tradeEntry = massage(_tradeEntry, error)
|
||||||
const tradeEntry = massage(_tradeEntry)
|
|
||||||
const sql = pgp.helpers.insert(tradeEntry, null, 'trades')
|
const sql = pgp.helpers.insert(tradeEntry, null, 'trades')
|
||||||
return db.none(sql)
|
return db.none(sql)
|
||||||
}
|
}
|
||||||
|
|
@ -585,27 +618,36 @@ function plugins (settings, deviceId) {
|
||||||
const cashOutEnabled = config.cashOutEnabled
|
const cashOutEnabled = config.cashOutEnabled
|
||||||
|
|
||||||
const cashInAlert = device.cashbox > config.cashInAlertThreshold
|
const cashInAlert = device.cashbox > config.cashInAlertThreshold
|
||||||
? {code: 'CASH_BOX_FULL', machineName, deviceId: device.deviceId, notes: device.cashbox}
|
? {
|
||||||
|
code: 'CASH_BOX_FULL',
|
||||||
|
machineName,
|
||||||
|
deviceId: device.deviceId,
|
||||||
|
notes: device.cashbox
|
||||||
|
}
|
||||||
: null
|
: null
|
||||||
|
|
||||||
const cassette1Alert = cashOutEnabled && device.cassette1 < config.cashOutCassette1AlertThreshold
|
const cassette1Alert = cashOutEnabled && device.cassette1 < config.cashOutCassette1AlertThreshold
|
||||||
? {code: 'LOW_CASH_OUT',
|
? {
|
||||||
|
code: 'LOW_CASH_OUT',
|
||||||
cassette: 1,
|
cassette: 1,
|
||||||
machineName,
|
machineName,
|
||||||
deviceId: device.deviceId,
|
deviceId: device.deviceId,
|
||||||
notes: device.cassette1,
|
notes: device.cassette1,
|
||||||
denomination: denomination1,
|
denomination: denomination1,
|
||||||
fiatCode}
|
fiatCode
|
||||||
|
}
|
||||||
: null
|
: null
|
||||||
|
|
||||||
const cassette2Alert = cashOutEnabled && device.cassette2 < config.cashOutCassette2AlertThreshold
|
const cassette2Alert = cashOutEnabled && device.cassette2 < config.cashOutCassette2AlertThreshold
|
||||||
? {code: 'LOW_CASH_OUT',
|
? {
|
||||||
|
code: 'LOW_CASH_OUT',
|
||||||
cassette: 2,
|
cassette: 2,
|
||||||
machineName,
|
machineName,
|
||||||
deviceId: device.deviceId,
|
deviceId: device.deviceId,
|
||||||
notes: device.cassette2,
|
notes: device.cassette2,
|
||||||
denomination: denomination2,
|
denomination: denomination2,
|
||||||
fiatCode}
|
fiatCode
|
||||||
|
}
|
||||||
: null
|
: null
|
||||||
|
|
||||||
return _.compact([cashInAlert, cassette1Alert, cassette2Alert])
|
return _.compact([cashInAlert, cassette1Alert, cassette2Alert])
|
||||||
|
|
@ -636,7 +678,12 @@ function plugins (settings, deviceId) {
|
||||||
const cryptoAlertThreshold = config.cryptoAlertThreshold
|
const cryptoAlertThreshold = config.cryptoAlertThreshold
|
||||||
|
|
||||||
return BN(fiatBalance.balance).lt(cryptoAlertThreshold)
|
return BN(fiatBalance.balance).lt(cryptoAlertThreshold)
|
||||||
? {code: 'LOW_CRYPTO_BALANCE', cryptoCode, fiatBalance, fiatCode}
|
? {
|
||||||
|
code: 'LOW_CRYPTO_BALANCE',
|
||||||
|
cryptoCode,
|
||||||
|
fiatBalance,
|
||||||
|
fiatCode
|
||||||
|
}
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -683,7 +730,7 @@ function plugins (settings, deviceId) {
|
||||||
logger.debug('[%s] Swept address with tx: %s', cryptoCode, txHash)
|
logger.debug('[%s] Swept address with tx: %s', cryptoCode, txHash)
|
||||||
|
|
||||||
const sql = `update cash_out_txs set swept='t'
|
const sql = `update cash_out_txs set swept='t'
|
||||||
where id=$1`
|
where id=$1`
|
||||||
|
|
||||||
return db.none(sql, row.id)
|
return db.none(sql, row.id)
|
||||||
}
|
}
|
||||||
|
|
@ -693,7 +740,7 @@ function plugins (settings, deviceId) {
|
||||||
|
|
||||||
function sweepHd () {
|
function sweepHd () {
|
||||||
const sql = `select id, crypto_code, hd_index from cash_out_txs
|
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')`
|
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)))
|
.then(rows => Promise.all(rows.map(sweepHdRow)))
|
||||||
|
|
|
||||||
15
migrations/1542811343367-add-error-to-trades.js
Normal file
15
migrations/1542811343367-add-error-to-trades.js
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
var db = require('./db')
|
||||||
|
|
||||||
|
exports.up = function (next) {
|
||||||
|
const sql = [
|
||||||
|
'alter table trades add column error text',
|
||||||
|
]
|
||||||
|
|
||||||
|
db.multi(sql, next)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.down = function (next) {
|
||||||
|
const sql = ['alter table trades drop column error']
|
||||||
|
|
||||||
|
db.multi(sql, next)
|
||||||
|
}
|
||||||
2
package-lock.json
generated
2
package-lock.json
generated
|
|
@ -3281,7 +3281,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"doctrine": {
|
"doctrine": {
|
||||||
"version": "1.5.0",
|
"version": "1.5.0",
|
||||||
"resolved": "http://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
|
||||||
"integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
|
"integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue