support for Coin ATM Radar
This commit is contained in:
parent
2cdc8b0d13
commit
6c3099921f
13 changed files with 262 additions and 8 deletions
16
dev/coinatmradar.js
Normal file
16
dev/coinatmradar.js
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
const car = require('../lib/coinatmradar/coinatmradar')
|
||||||
|
const plugins = require('../lib/plugins')
|
||||||
|
|
||||||
|
require('../lib/settings-loader').loadLatest()
|
||||||
|
.then(settings => {
|
||||||
|
const pi = plugins(settings)
|
||||||
|
const config = settings.config
|
||||||
|
|
||||||
|
return pi.getRates()
|
||||||
|
.then(rates => {
|
||||||
|
return car.update({rates, config})
|
||||||
|
.then(require('../lib/pp')('DEBUG100'))
|
||||||
|
.catch(console.log)
|
||||||
|
.then(() => process.exit())
|
||||||
|
})
|
||||||
|
})
|
||||||
15
dev/coinatmradarserver.js
Normal file
15
dev/coinatmradarserver.js
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
const express = require('express')
|
||||||
|
const app = express()
|
||||||
|
const bodyParser = require('body-parser')
|
||||||
|
|
||||||
|
app.use(bodyParser.raw({type: '*/*'}))
|
||||||
|
|
||||||
|
app.post('/api/lamassu', (req, res) => {
|
||||||
|
console.log(req.headers)
|
||||||
|
console.log(req.body.toString())
|
||||||
|
res.send('Hello World!')
|
||||||
|
})
|
||||||
|
|
||||||
|
app.listen(3200, () => console.log('Example app listening on port 3200!'))
|
||||||
|
|
||||||
|
// "url": "https://coinatmradar.info/api/lamassu/"
|
||||||
8
dev/config.js
Normal file
8
dev/config.js
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
const settingsLoader = require('../lib/settings-loader')
|
||||||
|
const configManager = require('../lib/config-manager')
|
||||||
|
|
||||||
|
settingsLoader.loadLatest()
|
||||||
|
.then(settings => {
|
||||||
|
const config = settings.config
|
||||||
|
require('../lib/pp')('config')(configManager.all('cryptoCurrencies', config))
|
||||||
|
})
|
||||||
10
dev/plugins.js
Normal file
10
dev/plugins.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
const plugins = require('../lib/plugins')
|
||||||
|
const settingsLoader = require('../lib/settings-loader')
|
||||||
|
const pp = require('../lib/pp')
|
||||||
|
|
||||||
|
settingsLoader.loadLatest()
|
||||||
|
.then(settings => {
|
||||||
|
console.log('DEBUG300')
|
||||||
|
const pi = plugins(settings)
|
||||||
|
pi.getRates().then(r => console.log(JSON.stringify(r)))
|
||||||
|
})
|
||||||
|
|
@ -82,6 +82,16 @@
|
||||||
"hardLimitVerificationThreshold"
|
"hardLimitVerificationThreshold"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"code": "coinAtmRadar",
|
||||||
|
"display": "Coin ATM Radar",
|
||||||
|
"cryptoScope": "global",
|
||||||
|
"machineScope": "global",
|
||||||
|
"fields": [
|
||||||
|
"coinAtmRadarActive",
|
||||||
|
"coinAtmRadarShowRates"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"code": "walletSettings",
|
"code": "walletSettings",
|
||||||
"display": "Wallet Settings",
|
"display": "Wallet Settings",
|
||||||
|
|
@ -756,6 +766,27 @@
|
||||||
"notificationsEmailEnabled"
|
"notificationsEmailEnabled"
|
||||||
],
|
],
|
||||||
"fieldValidation": [{"code": "required"}]
|
"fieldValidation": [{"code": "required"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "coinAtmRadarActive",
|
||||||
|
"displayBottom": "Active",
|
||||||
|
"displayTopCount": 0,
|
||||||
|
"fieldType": "onOff",
|
||||||
|
"fieldClass": null,
|
||||||
|
"fieldValidation": [{"code": "required"}],
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "coinAtmRadarShowRates",
|
||||||
|
"displayBottom": "Show Rates",
|
||||||
|
"displayTopCount": 0,
|
||||||
|
"fieldType": "onOff",
|
||||||
|
"fieldClass": null,
|
||||||
|
"enabledIfAny": [
|
||||||
|
"coinAtmRadarActive"
|
||||||
|
],
|
||||||
|
"fieldValidation": [{"code": "required"}],
|
||||||
|
"default": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
124
lib/coinatmradar/coinatmradar.js
Normal file
124
lib/coinatmradar/coinatmradar.js
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
const axios = require('axios')
|
||||||
|
const _ = require('lodash/fp')
|
||||||
|
|
||||||
|
const db = require('../db')
|
||||||
|
const configManager = require('../config-manager')
|
||||||
|
const options = require('../options')
|
||||||
|
const logger = require('../logger')
|
||||||
|
|
||||||
|
const TIMEOUT = 10000
|
||||||
|
const MAX_CONTENT_LENGTH = 2000
|
||||||
|
|
||||||
|
// How long a machine can be down before it's considered offline
|
||||||
|
const STALE_INTERVAL = '2 minutes'
|
||||||
|
|
||||||
|
module.exports = {update, mapRecord}
|
||||||
|
|
||||||
|
function mapCoin (info, deviceId, cryptoCode) {
|
||||||
|
const config = info.config
|
||||||
|
const rates = info.rates[cryptoCode] || {cashIn: null, cashOut: null}
|
||||||
|
const cryptoConfig = configManager.scoped(cryptoCode, deviceId, config)
|
||||||
|
const unscoped = configManager.unscoped(config)
|
||||||
|
const showRates = unscoped.coinAtmRadarShowRates
|
||||||
|
|
||||||
|
const cashInFee = showRates ? cryptoConfig.cashInCommission / 100 : null
|
||||||
|
const cashOutFee = showRates ? cryptoConfig.cashOutCommission / 100 : null
|
||||||
|
const cashInRate = showRates ? rates.cashIn.toNumber() : null
|
||||||
|
const cashOutRate = showRates ? rates.cashOut.toNumber() : null
|
||||||
|
|
||||||
|
return {
|
||||||
|
cryptoCode,
|
||||||
|
cashInFee,
|
||||||
|
cashOutFee,
|
||||||
|
cashInRate,
|
||||||
|
cashOutRate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapIdentification (info, deviceId) {
|
||||||
|
const machineConfig = configManager.machineScoped(deviceId, info.config)
|
||||||
|
|
||||||
|
return {
|
||||||
|
isPhone: machineConfig.smsVerificationActive,
|
||||||
|
isPalmVein: false,
|
||||||
|
isPhoto: false,
|
||||||
|
isIdDocScan: machineConfig.idCardDataVerificationActive,
|
||||||
|
isFingerprint: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapMachine (info, machineRow) {
|
||||||
|
const deviceId = machineRow.device_id
|
||||||
|
const config = info.config
|
||||||
|
const machineConfig = configManager.machineScoped(deviceId, config)
|
||||||
|
|
||||||
|
const lastOnline = machineRow.last_online.toISOString()
|
||||||
|
const status = machineRow.stale ? 'online' : 'offline'
|
||||||
|
|
||||||
|
const cashLimit = machineConfig.hardLimitVerificationActive
|
||||||
|
? machineConfig.hardLimitVerificationThreshold
|
||||||
|
: Infinity
|
||||||
|
|
||||||
|
const cryptoCurrencies = machineConfig.cryptoCurrencies
|
||||||
|
const identification = mapIdentification(info, deviceId)
|
||||||
|
const coins = _.map(_.partial(mapCoin, [info, deviceId]), cryptoCurrencies)
|
||||||
|
|
||||||
|
return {
|
||||||
|
machineId: deviceId,
|
||||||
|
status,
|
||||||
|
lastOnline,
|
||||||
|
cashIn: true,
|
||||||
|
cashOut: machineConfig.cashOutEnabled,
|
||||||
|
manufacturer: 'lamassu',
|
||||||
|
cashInTxLimit: cashLimit,
|
||||||
|
cashOutTxLimit: cashLimit,
|
||||||
|
cashInDailyLimit: cashLimit,
|
||||||
|
cashOutDailyLimit: cashLimit,
|
||||||
|
fiatCurrency: machineConfig.fiatCurrency,
|
||||||
|
identification,
|
||||||
|
coins
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMachines (info) {
|
||||||
|
const sql = `select device_id, last_online, now() - last_online < $1 as stale from devices
|
||||||
|
where display=TRUE and
|
||||||
|
paired=TRUE
|
||||||
|
order by created`
|
||||||
|
|
||||||
|
return db.any(sql, [STALE_INTERVAL])
|
||||||
|
.then(_.map(_.partial(mapMachine, [info])))
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendRadar (data) {
|
||||||
|
const config = {
|
||||||
|
url: options.coinAtmRadar.url,
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
timeout: TIMEOUT,
|
||||||
|
maxContentLength: MAX_CONTENT_LENGTH
|
||||||
|
}
|
||||||
|
|
||||||
|
return axios(config)
|
||||||
|
.then(r => console.log(r.status))
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapRecord (info) {
|
||||||
|
const timestamp = new Date().toISOString()
|
||||||
|
return getMachines(info)
|
||||||
|
.then(machines => ({
|
||||||
|
operatorId: options.operatorId,
|
||||||
|
timestamp,
|
||||||
|
machines
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
function update (info) {
|
||||||
|
const config = configManager.unscoped(info.config)
|
||||||
|
|
||||||
|
if (!config.coinAtmRadarActive) return Promise.resolve()
|
||||||
|
|
||||||
|
return mapRecord(info)
|
||||||
|
.then(sendRadar)
|
||||||
|
.catch(err => logger.error(err))
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,8 @@ module.exports = {
|
||||||
cryptoScoped,
|
cryptoScoped,
|
||||||
machineScoped,
|
machineScoped,
|
||||||
scoped,
|
scoped,
|
||||||
scopedValue
|
scopedValue,
|
||||||
|
all
|
||||||
}
|
}
|
||||||
|
|
||||||
function matchesValue (crypto, machine, instance) {
|
function matchesValue (crypto, machine, instance) {
|
||||||
|
|
@ -60,3 +61,7 @@ function cryptoScoped (crypto, config) {
|
||||||
function scoped (crypto, machine, config) {
|
function scoped (crypto, machine, config) {
|
||||||
return generalScoped(crypto, machine, config)
|
return generalScoped(crypto, machine, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function all (code, config) {
|
||||||
|
return _.uniq(_.map('fieldValue.value', _.filter(i => i.fieldLocator.code === code, config)))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -144,8 +144,6 @@ function ensureConstraints (config) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const pp = require('./pp')
|
|
||||||
|
|
||||||
function validateRequires (config) {
|
function validateRequires (config) {
|
||||||
return fetchMachines()
|
return fetchMachines()
|
||||||
.then(machineList => {
|
.then(machineList => {
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,8 @@ function plugins (settings, deviceId) {
|
||||||
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
|
const rate = rateRec.rates
|
||||||
rates[cryptoCode] = {
|
rates[cryptoCode] = {
|
||||||
cashIn: rate.ask.mul(cashInCommission),
|
cashIn: rate.ask.mul(cashInCommission).round(5),
|
||||||
cashOut: cashOutCommission && rate.bid.div(cashOutCommission)
|
cashOut: cashOutCommission && rate.bid.div(cashOutCommission).round(5)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -620,7 +620,18 @@ function plugins (settings, deviceId) {
|
||||||
return machineLoader.getMachineNames(settings.config)
|
return machineLoader.getMachineNames(settings.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRates () {
|
||||||
|
const config = configManager.unscoped(settings.config)
|
||||||
|
const cryptoCodes = _.flatten(configManager.all('cryptoCurrencies', settings.config))
|
||||||
|
const fiatCode = config.fiatCurrency
|
||||||
|
const tickerPromises = cryptoCodes.map(c => ticker.getRates(settings, fiatCode, c))
|
||||||
|
|
||||||
|
return Promise.all(tickerPromises)
|
||||||
|
.then(buildRates)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
getRates,
|
||||||
pollQueries,
|
pollQueries,
|
||||||
sendCoins,
|
sendCoins,
|
||||||
newAddress,
|
newAddress,
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ const cashOutTx = require('./cash-out/cash-out-tx')
|
||||||
const cashInTx = require('./cash-in/cash-in-tx')
|
const cashInTx = require('./cash-in/cash-in-tx')
|
||||||
const sanctionsUpdater = require('./ofac/update')
|
const sanctionsUpdater = require('./ofac/update')
|
||||||
const sanctions = require('./ofac/index')
|
const sanctions = require('./ofac/index')
|
||||||
|
const coinAtmRadar = require('./coinatmradar/coinatmradar')
|
||||||
|
|
||||||
const INCOMING_TX_INTERVAL = 30 * T.seconds
|
const INCOMING_TX_INTERVAL = 30 * T.seconds
|
||||||
const LIVE_INCOMING_TX_INTERVAL = 5 * T.seconds
|
const LIVE_INCOMING_TX_INTERVAL = 5 * T.seconds
|
||||||
|
|
@ -15,6 +16,7 @@ const TRADE_INTERVAL = 60 * T.seconds
|
||||||
const PONG_INTERVAL = 10 * T.seconds
|
const PONG_INTERVAL = 10 * T.seconds
|
||||||
const PONG_CLEAR_INTERVAL = 1 * T.day
|
const PONG_CLEAR_INTERVAL = 1 * T.day
|
||||||
const SANCTIONS_UPDATE_INTERVAL = 1 * T.week
|
const SANCTIONS_UPDATE_INTERVAL = 1 * T.week
|
||||||
|
const RADAR_UPDATE_INTERVAL = 5 * T.minutes
|
||||||
|
|
||||||
const CHECK_NOTIFICATION_INTERVAL = 20 * T.seconds
|
const CHECK_NOTIFICATION_INTERVAL = 20 * T.seconds
|
||||||
|
|
||||||
|
|
@ -38,6 +40,17 @@ function updateAndLoadSanctions () {
|
||||||
.then(() => logger.info('Sanctions database updated.'))
|
.then(() => logger.info('Sanctions database updated.'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pp = require('./pp')
|
||||||
|
function updateCoinAtmRadar () {
|
||||||
|
const config = settings().config
|
||||||
|
|
||||||
|
return pi().getRates()
|
||||||
|
.then(rates => {
|
||||||
|
pp('DEBUG200')(rates)
|
||||||
|
return coinAtmRadar.update({rates, config})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function start (__settings) {
|
function start (__settings) {
|
||||||
reload(__settings)
|
reload(__settings)
|
||||||
|
|
||||||
|
|
@ -49,6 +62,7 @@ function start (__settings) {
|
||||||
cashOutTx.monitorUnnotified(settings())
|
cashOutTx.monitorUnnotified(settings())
|
||||||
pi().sweepHd()
|
pi().sweepHd()
|
||||||
notifier.checkNotification(pi())
|
notifier.checkNotification(pi())
|
||||||
|
updateCoinAtmRadar()
|
||||||
|
|
||||||
setInterval(() => pi().executeTrades(), TRADE_INTERVAL)
|
setInterval(() => pi().executeTrades(), TRADE_INTERVAL)
|
||||||
setInterval(() => cashOutTx.monitorLiveIncoming(settings()), LIVE_INCOMING_TX_INTERVAL)
|
setInterval(() => cashOutTx.monitorLiveIncoming(settings()), LIVE_INCOMING_TX_INTERVAL)
|
||||||
|
|
@ -60,6 +74,7 @@ function start (__settings) {
|
||||||
setInterval(() => pi().pongClear(), PONG_CLEAR_INTERVAL)
|
setInterval(() => pi().pongClear(), PONG_CLEAR_INTERVAL)
|
||||||
setInterval(() => notifier.checkNotification(pi()), CHECK_NOTIFICATION_INTERVAL)
|
setInterval(() => notifier.checkNotification(pi()), CHECK_NOTIFICATION_INTERVAL)
|
||||||
setInterval(updateAndLoadSanctions, SANCTIONS_UPDATE_INTERVAL)
|
setInterval(updateAndLoadSanctions, SANCTIONS_UPDATE_INTERVAL)
|
||||||
|
setInterval(updateCoinAtmRadar, RADAR_UPDATE_INTERVAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {start, reload}
|
module.exports = {start, reload}
|
||||||
|
|
|
||||||
13
migrations/1526038623129-add_device_location.js
Normal file
13
migrations/1526038623129-add_device_location.js
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
var db = require('./db')
|
||||||
|
|
||||||
|
exports.up = function (next) {
|
||||||
|
var sql = [
|
||||||
|
'alter table devices add column last_online timestamptz not null default now()',
|
||||||
|
"alter table devices add column location json not null default '{}'"
|
||||||
|
]
|
||||||
|
db.multi(sql, next)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.down = function (next) {
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
"@fczbkk/uuid4": "^3.0.0",
|
"@fczbkk/uuid4": "^3.0.0",
|
||||||
"axios": "^0.16.1",
|
"axios": "^0.16.1",
|
||||||
"base-x": "^3.0.2",
|
"base-x": "^3.0.2",
|
||||||
"bignumber.js": "^4.0.2",
|
"bignumber.js": "^4.1.0",
|
||||||
"bip39": "^2.3.1",
|
"bip39": "^2.3.1",
|
||||||
"bitcoind-rpc": "^0.7.0",
|
"bitcoind-rpc": "^0.7.0",
|
||||||
"bitcore-lib": "^0.15.0",
|
"bitcore-lib": "^0.15.0",
|
||||||
|
|
|
||||||
|
|
@ -36714,7 +36714,11 @@ var _user$project$NavBar$determineConfigCategory = function (configCode) {
|
||||||
_1: {
|
_1: {
|
||||||
ctor: '::',
|
ctor: '::',
|
||||||
_0: 'compliance',
|
_0: 'compliance',
|
||||||
_1: {ctor: '[]'}
|
_1: {
|
||||||
|
ctor: '::',
|
||||||
|
_0: 'coinAtmRadar',
|
||||||
|
_1: {ctor: '[]'}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}) ? _elm_lang$core$Maybe$Just(_user$project$CoreTypes$GlobalSettingsCat) : _elm_lang$core$Maybe$Nothing);
|
}) ? _elm_lang$core$Maybe$Just(_user$project$CoreTypes$GlobalSettingsCat) : _elm_lang$core$Maybe$Nothing);
|
||||||
|
|
@ -37155,7 +37159,11 @@ var _user$project$NavBar$view = F2(
|
||||||
_1: {
|
_1: {
|
||||||
ctor: '::',
|
ctor: '::',
|
||||||
_0: A2(configLink, 'compliance', 'Compliance'),
|
_0: A2(configLink, 'compliance', 'Compliance'),
|
||||||
_1: {ctor: '[]'}
|
_1: {
|
||||||
|
ctor: '::',
|
||||||
|
_0: A2(configLink, 'coinAtmRadar', 'Coin ATM Radar'),
|
||||||
|
_1: {ctor: '[]'}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue