Merge branch 'releases/v10.1' into releases/v10.0.7
This commit is contained in:
commit
5075e90c87
62 changed files with 742 additions and 603 deletions
|
|
@ -92,7 +92,7 @@ function loadLatestConfig (filterSchemaVersion = true) {
|
|||
order by id desc
|
||||
limit 1`
|
||||
|
||||
return db.one(sql, ['config', configValidate.SETTINGS_LOADER_SCHEMA_VERSION])
|
||||
return db.oneOrNone(sql, ['config', configValidate.SETTINGS_LOADER_SCHEMA_VERSION])
|
||||
.then(row => row.data.config)
|
||||
.then(configValidate.validate)
|
||||
.catch(err => {
|
||||
|
|
@ -240,6 +240,7 @@ module.exports = {
|
|||
loadRecentConfig,
|
||||
load,
|
||||
loadLatest,
|
||||
loadLatestConfig,
|
||||
save,
|
||||
loadFixture,
|
||||
mergeValues,
|
||||
|
|
|
|||
|
|
@ -30,37 +30,37 @@ const BINARIES = {
|
|||
BTC: {
|
||||
defaultUrl: 'https://bitcoincore.org/bin/bitcoin-core-0.20.1/bitcoin-0.20.1-x86_64-linux-gnu.tar.gz',
|
||||
defaultDir: 'bitcoin-0.20.1/bin',
|
||||
url: 'https://bitcoincore.org/bin/bitcoin-core-27.1/bitcoin-27.1-x86_64-linux-gnu.tar.gz',
|
||||
dir: 'bitcoin-27.1/bin'
|
||||
url: 'https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz',
|
||||
dir: 'bitcoin-28.0/bin'
|
||||
},
|
||||
ETH: {
|
||||
url: 'https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.14.8-a9523b64.tar.gz',
|
||||
dir: 'geth-linux-amd64-1.14.8-a9523b64'
|
||||
url: 'https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.14.12-293a300d.tar.gz',
|
||||
dir: 'geth-linux-amd64-1.14.12-293a300d'
|
||||
},
|
||||
ZEC: {
|
||||
url: 'https://github.com/zcash/artifacts/raw/master/v5.9.0/bullseye/zcash-5.9.0-linux64-debian-bullseye.tar.gz',
|
||||
dir: 'zcash-5.9.0/bin'
|
||||
url: 'https://download.z.cash/downloads/zcash-6.0.0-linux64-debian-bullseye.tar.gz',
|
||||
dir: 'zcash-6.0.0/bin'
|
||||
},
|
||||
DASH: {
|
||||
defaultUrl: 'https://github.com/dashpay/dash/releases/download/v18.1.0/dashcore-18.1.0-x86_64-linux-gnu.tar.gz',
|
||||
defaultDir: 'dashcore-18.1.0/bin',
|
||||
url: 'https://github.com/dashpay/dash/releases/download/v21.1.0/dashcore-21.1.0-x86_64-linux-gnu.tar.gz',
|
||||
dir: 'dashcore-21.1.0/bin'
|
||||
url: 'https://github.com/dashpay/dash/releases/download/v21.1.1/dashcore-21.1.1-x86_64-linux-gnu.tar.gz',
|
||||
dir: 'dashcore-21.1.1/bin'
|
||||
},
|
||||
LTC: {
|
||||
defaultUrl: 'https://download.litecoin.org/litecoin-0.18.1/linux/litecoin-0.18.1-x86_64-linux-gnu.tar.gz',
|
||||
defaultDir: 'litecoin-0.18.1/bin',
|
||||
url: 'https://download.litecoin.org/litecoin-0.21.3/linux/litecoin-0.21.3-x86_64-linux-gnu.tar.gz',
|
||||
dir: 'litecoin-0.21.3/bin'
|
||||
url: 'https://download.litecoin.org/litecoin-0.21.4/linux/litecoin-0.21.4-x86_64-linux-gnu.tar.gz',
|
||||
dir: 'litecoin-0.21.4/bin'
|
||||
},
|
||||
BCH: {
|
||||
url: 'https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v27.1.0/bitcoin-cash-node-27.1.0-x86_64-linux-gnu.tar.gz',
|
||||
dir: 'bitcoin-cash-node-27.1.0/bin',
|
||||
url: 'https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v28.0.0/bitcoin-cash-node-28.0.0-x86_64-linux-gnu.tar.gz',
|
||||
dir: 'bitcoin-cash-node-28.0.0/bin',
|
||||
files: [['bitcoind', 'bitcoincashd'], ['bitcoin-cli', 'bitcoincash-cli']]
|
||||
},
|
||||
XMR: {
|
||||
url: 'https://downloads.getmonero.org/cli/monero-linux-x64-v0.18.3.3.tar.bz2',
|
||||
dir: 'monero-x86_64-linux-gnu-v0.18.3.3',
|
||||
url: 'https://downloads.getmonero.org/cli/monero-linux-x64-v0.18.3.4.tar.bz2',
|
||||
dir: 'monero-x86_64-linux-gnu-v0.18.3.4',
|
||||
files: [['monerod', 'monerod'], ['monero-wallet-rpc', 'monero-wallet-rpc']]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,16 @@
|
|||
const axios = require("axios");
|
||||
|
||||
const getSatBEstimateFee = () => {
|
||||
return axios.get('https://mempool.space/api/v1/fees/recommended')
|
||||
.then(r => r.data.hourFee)
|
||||
}
|
||||
|
||||
module.exports = { getSatBEstimateFee }
|
||||
const axios = require("axios");
|
||||
|
||||
const getSatBEstimateFee = () => {
|
||||
return axios.get('https://mempool.space/api/v1/fees/recommended')
|
||||
.then(r => r.data.hourFee)
|
||||
}
|
||||
|
||||
const getSatBEstimateFees = () => {
|
||||
return axios.get('https://mempool.space/api/v1/fees/recommended')
|
||||
.then(r => r.data)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getSatBEstimateFees,
|
||||
getSatBEstimateFee
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ function post (machineTx, pi) {
|
|||
let addressReuse = false
|
||||
let walletScore = {}
|
||||
|
||||
const promises = [settingsLoader.loadLatest()]
|
||||
const promises = [settingsLoader.loadLatestConfig()]
|
||||
|
||||
const isFirstPost = !r.tx.fiat || r.tx.fiat.isZero()
|
||||
if (isFirstPost) {
|
||||
|
|
@ -44,7 +44,7 @@ function post (machineTx, pi) {
|
|||
}
|
||||
|
||||
return Promise.all(promises)
|
||||
.then(([{ config }, blacklistItems = false, isReusedAddress = false, fetchedWalletScore = null]) => {
|
||||
.then(([config, blacklistItems = false, isReusedAddress = false, fetchedWalletScore = null]) => {
|
||||
const rejectAddressReuse = configManager.getCompliance(config).rejectAddressReuse
|
||||
|
||||
walletScore = fetchedWalletScore
|
||||
|
|
|
|||
|
|
@ -474,8 +474,4 @@ function migrate (config, accounts) {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
migrateConfig,
|
||||
migrateAccounts,
|
||||
migrate
|
||||
}
|
||||
module.exports = { migrate }
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ const CASH_UNIT_CAPACITY = {
|
|||
|
||||
const CASH_OUT_MINIMUM_AMOUNT_OF_CASSETTES = 2
|
||||
const CASH_OUT_MAXIMUM_AMOUNT_OF_CASSETTES = 4
|
||||
const CASH_OUT_MAXIMUM_AMOUNT_OF_RECYCLERS = 6
|
||||
const AUTHENTICATOR_ISSUER_ENTITY = 'Lamassu'
|
||||
const AUTH_TOKEN_EXPIRATION_TIME = '30 minutes'
|
||||
const REGISTRATION_TOKEN_EXPIRATION_TIME = '30 minutes'
|
||||
|
|
@ -85,6 +86,7 @@ module.exports = {
|
|||
CONFIRMATION_CODE,
|
||||
CASH_OUT_MINIMUM_AMOUNT_OF_CASSETTES,
|
||||
CASH_OUT_MAXIMUM_AMOUNT_OF_CASSETTES,
|
||||
CASH_OUT_MAXIMUM_AMOUNT_OF_RECYCLERS,
|
||||
WALLET_SCORE_THRESHOLD,
|
||||
RECEIPT,
|
||||
PSQL_URL,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ const nmd = require('nano-markdown')
|
|||
|
||||
const plugins = require('../plugins')
|
||||
const configManager = require('../new-config-manager')
|
||||
const settingsLoader = require('../new-settings-loader')
|
||||
const { batchGetCustomInfoRequest, getCustomInfoRequests } = require('../new-admin/services/customInfoRequests')
|
||||
const state = require('../middlewares/state')
|
||||
const { getMachine } = require('../machine-loader')
|
||||
|
|
@ -323,8 +324,7 @@ const terms = (parent, { currentConfigVersion, currentHash }, { deviceId, settin
|
|||
const isHashNew = hash !== currentHash
|
||||
const text = isHashNew ? latestTerms.text : null
|
||||
|
||||
return plugins(settings, deviceId)
|
||||
.fetchCurrentConfigVersion()
|
||||
return settingsLoader.fetchCurrentConfigVersion()
|
||||
.catch(() => null)
|
||||
.then(configVersion => isHashNew || _.isNil(currentConfigVersion) || currentConfigVersion < configVersion)
|
||||
.then(isVersionNew => isVersionNew ? _.omit(['text'], latestTerms) : null)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ type OperatorInfo {
|
|||
}
|
||||
|
||||
type MachineInfo {
|
||||
deviceId: String!
|
||||
deviceId: String! @deprecated(reason: "unused by the machine")
|
||||
deviceName: String
|
||||
numberOfCassettes: Int
|
||||
numberOfRecyclers: Int
|
||||
|
|
@ -107,7 +107,7 @@ type Trigger {
|
|||
suspensionDays: Float
|
||||
threshold: Int
|
||||
thresholdDays: Int
|
||||
customInfoRequestId: String
|
||||
customInfoRequestId: String @deprecated(reason: "use customInfoRequest.id")
|
||||
customInfoRequest: CustomInfoRequest
|
||||
externalService: String
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ const pairing = require('./pairing')
|
|||
const { checkPings, checkStuckScreen } = require('./notifier')
|
||||
const dbm = require('./postgresql_interface')
|
||||
const configManager = require('./new-config-manager')
|
||||
const settingsLoader = require('./new-settings-loader')
|
||||
const notifierUtils = require('./notifier/utils')
|
||||
const notifierQueries = require('./notifier/queries')
|
||||
const { ApolloError } = require('apollo-server-errors');
|
||||
|
|
@ -94,9 +93,7 @@ function getUnpairedMachines () {
|
|||
}
|
||||
|
||||
function getConfig (defaultConfig) {
|
||||
if (defaultConfig) return Promise.resolve(defaultConfig)
|
||||
|
||||
return settingsLoader.loadLatest().config
|
||||
return defaultConfig ? Promise.resolve(defaultConfig) : loadLatestConfig()
|
||||
}
|
||||
|
||||
const getStatus = (ping, stuck) => {
|
||||
|
|
@ -529,7 +526,6 @@ module.exports = {
|
|||
updateNetworkHeartbeat,
|
||||
getNetworkPerformance,
|
||||
getNetworkHeartbeat,
|
||||
getConfig,
|
||||
getMachineIds,
|
||||
emptyMachineUnits,
|
||||
refillMachineUnits,
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ const ALL_ACCOUNTS = [
|
|||
{ code: 'blockcypher', display: 'Blockcypher', class: ZERO_CONF, cryptos: [BTC] },
|
||||
{ code: 'mock-zero-conf', display: 'Mock 0-conf', class: ZERO_CONF, cryptos: ALL_CRYPTOS, dev: true },
|
||||
{ code: 'scorechain', display: 'Scorechain', class: WALLET_SCORING, cryptos: [BTC, ETH, LTC, BCH, DASH, USDT, USDT_TRON, TRX] },
|
||||
{ code: 'elliptic', display: 'Elliptic', class: WALLET_SCORING, cryptos: [BTC, ETH, LTC, BCH, USDT, USDT_TRON, TRX, ZEC] },
|
||||
{ code: 'mock-scoring', display: 'Mock scoring', class: WALLET_SCORING, cryptos: ALL_CRYPTOS, dev: true },
|
||||
{ code: 'sumsub', display: 'Sumsub', class: COMPLIANCE },
|
||||
{ code: 'mock-compliance', display: 'Mock Compliance', class: COMPLIANCE, dev: true },
|
||||
|
|
|
|||
|
|
@ -7,10 +7,7 @@ const resolvers = {
|
|||
},
|
||||
Mutation: {
|
||||
saveAccounts: (...[, { accounts }]) => settingsLoader.saveAccounts(accounts),
|
||||
// resetAccounts: (...[, { schemaVersion }]) => settingsLoader.resetAccounts(schemaVersion),
|
||||
saveConfig: (...[, { config }]) => settingsLoader.saveConfig(config),
|
||||
// resetConfig: (...[, { schemaVersion }]) => settingsLoader.resetConfig(schemaVersion),
|
||||
// migrateConfigAndAccounts: () => settingsLoader.migrate()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,7 @@ const typeDef = gql`
|
|||
|
||||
type Mutation {
|
||||
saveAccounts(accounts: JSONObject): JSONObject @auth
|
||||
# resetAccounts(schemaVersion: Int): JSONObject @auth
|
||||
saveConfig(config: JSONObject): JSONObject @auth
|
||||
# resetConfig(schemaVersion: Int): JSONObject @auth
|
||||
# migrateConfigAndAccounts: JSONObject @auth
|
||||
}
|
||||
`
|
||||
|
||||
|
|
|
|||
|
|
@ -56,39 +56,45 @@ const addTermsHash = configs => {
|
|||
)(terms)
|
||||
}
|
||||
|
||||
const accountsSql = `update user_config set data = $2, valid = $3, schema_version = $4 where type = $1;
|
||||
insert into user_config (type, data, valid, schema_version)
|
||||
select $1, $2, $3, $4 where $1 not in (select type from user_config)`
|
||||
const notifyReload = (dbOrTx, operatorId) =>
|
||||
dbOrTx.none(
|
||||
'NOTIFY $1:name, $2',
|
||||
['reload', JSON.stringify({ schema: asyncLocalStorage.getStore().get('schema'), operatorId })]
|
||||
)
|
||||
|
||||
function saveAccounts (accounts) {
|
||||
const accountsSql = `UPDATE user_config SET data = $1, valid = TRUE, schema_version = $2 WHERE type = 'accounts';
|
||||
INSERT INTO user_config (type, data, valid, schema_version)
|
||||
SELECT 'accounts', $1, TRUE, $2 WHERE 'accounts' NOT IN (SELECT type FROM user_config)`
|
||||
|
||||
return Promise.all([loadAccounts(), getOperatorId('middleware')])
|
||||
.then(([currentAccounts, operatorId]) => {
|
||||
const newAccounts = _.merge(currentAccounts, accounts)
|
||||
return db.tx(t => {
|
||||
return t.none(accountsSql, ['accounts', { accounts: newAccounts }, true, NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
||||
.then(() => t.none('NOTIFY $1:name, $2', ['reload', JSON.stringify({ schema: asyncLocalStorage.getStore().get('schema'), operatorId })]))
|
||||
}).catch(console.error)
|
||||
|
||||
// Only allow one wallet scoring active at a time
|
||||
if (accounts.elliptic?.enabled && newAccounts.scorechain) {
|
||||
newAccounts.scorechain.enabled = false
|
||||
}
|
||||
|
||||
if (accounts.scorechain?.enabled && newAccounts.elliptic) {
|
||||
newAccounts.elliptic.enabled = false
|
||||
}
|
||||
|
||||
return db.tx(t =>
|
||||
t.none(accountsSql, [{ accounts: newAccounts }, NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
||||
.then(() => notifyReload(t, operatorId))
|
||||
).catch(console.error)
|
||||
})
|
||||
}
|
||||
function resetAccounts (schemaVersion) {
|
||||
return db.none(
|
||||
accountsSql,
|
||||
[
|
||||
'accounts',
|
||||
{ accounts: NEW_SETTINGS_LOADER_SCHEMA_VERSION ? {} : [] },
|
||||
true,
|
||||
schemaVersion
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
function loadAccounts (schemaVersion) {
|
||||
const sql = `select data
|
||||
from user_config
|
||||
where type=$1
|
||||
and schema_version=$2
|
||||
and valid
|
||||
order by id desc
|
||||
limit 1`
|
||||
const sql = `SELECT data
|
||||
FROM user_config
|
||||
WHERE type = $1
|
||||
AND schema_version = $2
|
||||
AND valid
|
||||
ORDER BY id DESC
|
||||
LIMIT 1`
|
||||
|
||||
return db.oneOrNone(sql, ['accounts', schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
||||
.then(_.compose(_.defaultTo({}), _.get('data.accounts')))
|
||||
|
|
@ -106,15 +112,20 @@ function showAccounts (schemaVersion) {
|
|||
})
|
||||
}
|
||||
|
||||
const configSql = 'insert into user_config (type, data, valid, schema_version) values ($1, $2, $3, $4)'
|
||||
const insertConfigRow = (dbOrTx, data) =>
|
||||
dbOrTx.none(
|
||||
"INSERT INTO user_config (type, data, valid, schema_version) VALUES ('config', $1, TRUE, $2)",
|
||||
[data, NEW_SETTINGS_LOADER_SCHEMA_VERSION]
|
||||
)
|
||||
|
||||
function saveConfig (config) {
|
||||
return Promise.all([loadLatestConfigOrNone(), getOperatorId('middleware')])
|
||||
.then(([currentConfig, operatorId]) => {
|
||||
const newConfig = addTermsHash(_.assign(currentConfig, config))
|
||||
return db.tx(t => {
|
||||
return t.none(configSql, ['config', { config: newConfig }, true, NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
||||
.then(() => t.none('NOTIFY $1:name, $2', ['reload', JSON.stringify({ schema: asyncLocalStorage.getStore().get('schema'), operatorId })]))
|
||||
}).catch(console.error)
|
||||
return db.tx(t =>
|
||||
insertConfigRow(t, { config: newConfig })
|
||||
.then(() => notifyReload(t, operatorId))
|
||||
).catch(console.error)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -122,10 +133,10 @@ function removeFromConfig (fields) {
|
|||
return Promise.all([loadLatestConfigOrNone(), getOperatorId('middleware')])
|
||||
.then(([currentConfig, operatorId]) => {
|
||||
const newConfig = _.omit(fields, currentConfig)
|
||||
return db.tx(t => {
|
||||
return t.none(configSql, ['config', { config: newConfig }, true, NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
||||
.then(() => t.none('NOTIFY $1:name, $2', ['reload', JSON.stringify({ schema: asyncLocalStorage.getStore().get('schema'), operatorId })]))
|
||||
}).catch(console.error)
|
||||
return db.tx(t =>
|
||||
insertConfigRow(t, { config: newConfig })
|
||||
.then(() => notifyReload(t, operatorId))
|
||||
).catch(console.error)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -133,23 +144,11 @@ function migrationSaveConfig (config) {
|
|||
return loadLatestConfigOrNone()
|
||||
.then(currentConfig => {
|
||||
const newConfig = _.assign(currentConfig, config)
|
||||
return db.none(configSql, ['config', { config: newConfig }, true, NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
||||
return insertConfigRow(db, { config: newConfig })
|
||||
.catch(console.error)
|
||||
})
|
||||
}
|
||||
|
||||
function resetConfig (schemaVersion) {
|
||||
return db.none(
|
||||
configSql,
|
||||
[
|
||||
'config',
|
||||
{ config: schemaVersion === NEW_SETTINGS_LOADER_SCHEMA_VERSION ? {} : [] },
|
||||
true,
|
||||
schemaVersion
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
function loadLatest (schemaVersion) {
|
||||
return Promise.all([loadLatestConfigOrNoneReturningVersion(schemaVersion), loadAccounts(schemaVersion)])
|
||||
.then(([configObj, accounts]) => ({
|
||||
|
|
@ -160,15 +159,15 @@ function loadLatest (schemaVersion) {
|
|||
}
|
||||
|
||||
function loadLatestConfig () {
|
||||
const sql = `select data
|
||||
from user_config
|
||||
where type=$1
|
||||
and schema_version=$2
|
||||
and valid
|
||||
order by id desc
|
||||
limit 1`
|
||||
const sql = `SELECT data
|
||||
FROM user_config
|
||||
WHERE type = 'config'
|
||||
AND schema_version = $1
|
||||
AND valid
|
||||
ORDER BY id DESC
|
||||
LIMIT 1`
|
||||
|
||||
return db.one(sql, ['config', NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
||||
return db.oneOrNone(sql, [NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
||||
.then(row => row ? row.data.config : {})
|
||||
.catch(err => {
|
||||
throw err
|
||||
|
|
@ -176,38 +175,39 @@ function loadLatestConfig () {
|
|||
}
|
||||
|
||||
function loadLatestConfigOrNoneReturningVersion (schemaVersion) {
|
||||
const sql = `select data, id
|
||||
from user_config
|
||||
where type=$1
|
||||
and schema_version=$2
|
||||
order by id desc
|
||||
limit 1`
|
||||
const sql = `SELECT data, id
|
||||
FROM user_config
|
||||
WHERE type = 'config'
|
||||
AND schema_version = $1
|
||||
AND valid
|
||||
ORDER BY id DESC
|
||||
LIMIT 1`
|
||||
|
||||
return db.oneOrNone(sql, ['config', schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
||||
return db.oneOrNone(sql, [schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
||||
.then(row => row ? { config: row.data.config, version: row.id } : {})
|
||||
}
|
||||
|
||||
function loadLatestConfigOrNone (schemaVersion) {
|
||||
const sql = `select data
|
||||
from user_config
|
||||
where type=$1
|
||||
and schema_version=$2
|
||||
order by id desc
|
||||
limit 1`
|
||||
const sql = `SELECT data
|
||||
FROM user_config
|
||||
WHERE type = 'config'
|
||||
AND schema_version = $1
|
||||
ORDER BY id DESC
|
||||
LIMIT 1`
|
||||
|
||||
return db.oneOrNone(sql, ['config', schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
||||
return db.oneOrNone(sql, [schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
||||
.then(row => row ? row.data.config : {})
|
||||
}
|
||||
|
||||
function loadConfig (versionId) {
|
||||
const sql = `select data
|
||||
from user_config
|
||||
where id=$1
|
||||
and type=$2
|
||||
and schema_version=$3
|
||||
and valid`
|
||||
const sql = `SELECT data
|
||||
FROM user_config
|
||||
WHERE id = $1
|
||||
AND type = 'config'
|
||||
AND schema_version = $2
|
||||
AND valid`
|
||||
|
||||
return db.one(sql, [versionId, 'config', NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
||||
return db.one(sql, [versionId, NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
||||
.then(row => row.data.config)
|
||||
.catch(err => {
|
||||
if (err.name === 'QueryResultError') {
|
||||
|
|
@ -228,29 +228,25 @@ function load (versionId) {
|
|||
}))
|
||||
}
|
||||
|
||||
function migrate () {
|
||||
return loadLatest(OLD_SETTINGS_LOADER_SCHEMA_VERSION)
|
||||
.then(res => {
|
||||
const migrated = migration.migrate(res.config, res.accounts)
|
||||
saveConfig(migrated.config)
|
||||
saveAccounts(migrated.accounts)
|
||||
|
||||
return migrated
|
||||
})
|
||||
const fetchCurrentConfigVersion = () => {
|
||||
const sql = `SELECT id FROM user_config
|
||||
WHERE type = 'config'
|
||||
AND valid
|
||||
ORDER BY id DESC
|
||||
LIMIT 1`
|
||||
return db.one(sql).then(row => row.id)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
saveConfig,
|
||||
migrationSaveConfig,
|
||||
resetConfig,
|
||||
saveAccounts,
|
||||
resetAccounts,
|
||||
loadAccounts,
|
||||
showAccounts,
|
||||
loadLatest,
|
||||
loadLatestConfig,
|
||||
loadLatestConfigOrNone,
|
||||
load,
|
||||
migrate,
|
||||
removeFromConfig
|
||||
removeFromConfig,
|
||||
fetchCurrentConfigVersion,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ const LOW_CRYPTO_BALANCE = 'LOW_CRYPTO_BALANCE'
|
|||
const HIGH_CRYPTO_BALANCE = 'HIGH_CRYPTO_BALANCE'
|
||||
const CASH_BOX_FULL = 'CASH_BOX_FULL'
|
||||
const LOW_CASH_OUT = 'LOW_CASH_OUT'
|
||||
const LOW_RECYCLER_STACKER = 'LOW_RECYCLER_STACKER'
|
||||
const SECURITY = 'SECURITY'
|
||||
|
||||
const CODES_DISPLAY = {
|
||||
|
|
@ -41,6 +42,7 @@ module.exports = {
|
|||
HIGH_CRYPTO_BALANCE,
|
||||
CASH_BOX_FULL,
|
||||
LOW_CASH_OUT,
|
||||
LOW_RECYCLER_STACKER,
|
||||
SECURITY,
|
||||
CODES_DISPLAY,
|
||||
NETWORK_DOWN_TIME,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ const {
|
|||
HIGH_CRYPTO_BALANCE,
|
||||
CASH_BOX_FULL,
|
||||
LOW_CASH_OUT,
|
||||
LOW_RECYCLER_STACKER,
|
||||
SECURITY
|
||||
} = require('./codes')
|
||||
|
||||
|
|
@ -80,6 +81,8 @@ function emailAlert (alert) {
|
|||
return `Cash box full on ${alert.machineName} [${alert.notes} banknotes]`
|
||||
case LOW_CASH_OUT:
|
||||
return `Cassette for ${alert.denomination} ${alert.fiatCode} low [${alert.notes} banknotes]`
|
||||
case LOW_RECYCLER_STACKER:
|
||||
return `Recycler for ${alert.denomination} ${alert.fiatCode} low [${alert.notes} banknotes]`
|
||||
case SECURITY:
|
||||
return `Cashbox removed on ${alert.machineName}`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,8 +130,8 @@ function checkStuckScreen (deviceEvents, machine) {
|
|||
}
|
||||
|
||||
function transactionNotify (tx, rec) {
|
||||
return settingsLoader.loadLatest().then(settings => {
|
||||
const notifSettings = configManager.getGlobalNotifications(settings.config)
|
||||
return settingsLoader.loadLatestConfig().then(config => {
|
||||
const notifSettings = configManager.getGlobalNotifications(config)
|
||||
const highValueTx = tx.fiat.gt(notifSettings.highValueTransaction || Infinity)
|
||||
const isCashOut = tx.direction === 'cashOut'
|
||||
|
||||
|
|
@ -147,7 +147,7 @@ function transactionNotify (tx, rec) {
|
|||
}
|
||||
|
||||
// alert through sms or email any transaction or high value transaction, if SMS || email alerts are enabled
|
||||
const walletSettings = configManager.getWalletSettings(tx.cryptoCode, settings.config)
|
||||
const walletSettings = configManager.getWalletSettings(tx.cryptoCode, config)
|
||||
const zeroConfLimit = walletSettings.zeroConfLimit || 0
|
||||
const zeroConf = isCashOut && tx.fiat.lte(zeroConfLimit)
|
||||
const notificationsEnabled = notifSettings.sms.transactions || notifSettings.email.transactions
|
||||
|
|
@ -308,8 +308,8 @@ function cashboxNotify (deviceId) {
|
|||
|
||||
// for notification center, check if type of notification is active before calling the respective notify function
|
||||
const notifyIfActive = (type, fnName, ...args) => {
|
||||
return settingsLoader.loadLatest().then(settings => {
|
||||
const notificationSettings = configManager.getGlobalNotifications(settings.config).notificationCenter
|
||||
return settingsLoader.loadLatestConfig().then(config => {
|
||||
const notificationSettings = configManager.getGlobalNotifications(config).notificationCenter
|
||||
if (!notificationCenter[fnName]) return Promise.reject(new Error(`Notification function ${fnName} for type ${type} does not exist`))
|
||||
if (!(notificationSettings.active && notificationSettings[type])) return Promise.resolve()
|
||||
return notificationCenter[fnName](...args)
|
||||
|
|
|
|||
|
|
@ -2,20 +2,27 @@ const _ = require('lodash/fp')
|
|||
|
||||
const queries = require('./queries')
|
||||
const utils = require('./utils')
|
||||
const codes = require('./codes')
|
||||
const customers = require('../customers')
|
||||
const {
|
||||
NOTIFICATION_TYPES: {
|
||||
SECURITY,
|
||||
COMPLIANCE,
|
||||
CRYPTO_BALANCE,
|
||||
FIAT_BALANCE,
|
||||
ERROR,
|
||||
HIGH_VALUE_TX,
|
||||
NORMAL_VALUE_TX
|
||||
},
|
||||
|
||||
const { NOTIFICATION_TYPES: {
|
||||
SECURITY,
|
||||
COMPLIANCE,
|
||||
CRYPTO_BALANCE,
|
||||
FIAT_BALANCE,
|
||||
ERROR,
|
||||
HIGH_VALUE_TX,
|
||||
NORMAL_VALUE_TX }
|
||||
} = codes
|
||||
STALE,
|
||||
PING,
|
||||
|
||||
const { STALE, PING } = codes
|
||||
HIGH_CRYPTO_BALANCE,
|
||||
LOW_CRYPTO_BALANCE,
|
||||
CASH_BOX_FULL,
|
||||
LOW_CASH_OUT,
|
||||
LOW_RECYCLER_STACKER,
|
||||
} = require('./codes')
|
||||
|
||||
const sanctionsNotify = (customer, phone) => {
|
||||
const code = 'SANCTIONS'
|
||||
|
|
@ -71,9 +78,13 @@ const fiatBalancesNotify = (fiatWarnings) => {
|
|||
const { cassette, deviceId } = o.detail
|
||||
return cassette === balance.cassette && deviceId === balance.deviceId
|
||||
}, notInvalidated)) return
|
||||
const message = balance.code === 'LOW_CASH_OUT' ?
|
||||
const message = balance.code === LOW_CASH_OUT ?
|
||||
`Cash-out cassette ${balance.cassette} low or empty!` :
|
||||
`Cash box full or almost full!`
|
||||
balance.code === LOW_RECYCLER_STACKER ?
|
||||
`Recycler ${balance.cassette} low or empty!` :
|
||||
balance.code === CASH_BOX_FULL ?
|
||||
`Cash box full or almost full!` :
|
||||
`Cash box full or almost full!` /* Shouldn't happen */
|
||||
const detailB = utils.buildDetail({ deviceId: balance.deviceId, cassette: balance.cassette })
|
||||
return queries.addNotification(FIAT_BALANCE, message, detailB)
|
||||
})
|
||||
|
|
@ -105,7 +116,7 @@ const cryptoBalancesNotify = (cryptoWarnings) => {
|
|||
}, notInvalidated)) return
|
||||
|
||||
const fiat = utils.formatCurrency(balance.fiatBalance.balance, balance.fiatCode)
|
||||
const message = `${balance.code === 'HIGH_CRYPTO_BALANCE' ? 'High' : 'Low'} balance in ${balance.cryptoCode} [${fiat}]`
|
||||
const message = `${balance.code === HIGH_CRYPTO_BALANCE ? 'High' : 'Low'} balance in ${balance.cryptoCode} [${fiat}]`
|
||||
const detailB = utils.buildDetail({ cryptoCode: balance.cryptoCode, code: balance.code })
|
||||
return queries.addNotification(CRYPTO_BALANCE, message, detailB)
|
||||
})
|
||||
|
|
@ -113,8 +124,8 @@ const cryptoBalancesNotify = (cryptoWarnings) => {
|
|||
}
|
||||
|
||||
const balancesNotify = (balances) => {
|
||||
const isCryptoCode = c => _.includes(c, ['HIGH_CRYPTO_BALANCE', 'LOW_CRYPTO_BALANCE'])
|
||||
const isFiatCode = c => _.includes(c, ['LOW_CASH_OUT', 'CASH_BOX_FULL'])
|
||||
const isCryptoCode = c => _.includes(c, [HIGH_CRYPTO_BALANCE, LOW_CRYPTO_BALANCE])
|
||||
const isFiatCode = c => _.includes(c, [LOW_CASH_OUT, CASH_BOX_FULL, LOW_RECYCLER_STACKER])
|
||||
const by = o =>
|
||||
isCryptoCode(o) ? 'crypto' :
|
||||
isFiatCode(o) ? 'fiat' :
|
||||
|
|
|
|||
229
lib/plugins.js
229
lib/plugins.js
|
|
@ -11,6 +11,7 @@ const logger = require('./logger')
|
|||
const logs = require('./logs')
|
||||
const T = require('./time')
|
||||
const configManager = require('./new-config-manager')
|
||||
const settingsLoader = require('./new-settings-loader')
|
||||
const ticker = require('./ticker')
|
||||
const wallet = require('./wallet')
|
||||
const walletScoring = require('./wallet-scoring')
|
||||
|
|
@ -23,7 +24,13 @@ const commissionMath = require('./commission-math')
|
|||
const loyalty = require('./loyalty')
|
||||
const transactionBatching = require('./tx-batching')
|
||||
|
||||
const { CASH_UNIT_CAPACITY, CASH_OUT_DISPENSE_READY, CONFIRMATION_CODE } = require('./constants')
|
||||
const {
|
||||
CASH_OUT_DISPENSE_READY,
|
||||
CASH_OUT_MAXIMUM_AMOUNT_OF_CASSETTES,
|
||||
CASH_OUT_MAXIMUM_AMOUNT_OF_RECYCLERS,
|
||||
CASH_UNIT_CAPACITY,
|
||||
CONFIRMATION_CODE,
|
||||
} = require('./constants')
|
||||
|
||||
const notifier = require('./notifier')
|
||||
|
||||
|
|
@ -237,17 +244,6 @@ function plugins (settings, deviceId) {
|
|||
.then(([cassettes, recyclers]) => ({ cassettes: cassettes.cassettes, recyclers: recyclers.recyclers }))
|
||||
}
|
||||
|
||||
function fetchCurrentConfigVersion () {
|
||||
const sql = `select id from user_config
|
||||
where type=$1
|
||||
and valid
|
||||
order by id desc
|
||||
limit 1`
|
||||
|
||||
return db.one(sql, ['config'])
|
||||
.then(row => row.id)
|
||||
}
|
||||
|
||||
function mapCoinSettings (coinParams) {
|
||||
const [ cryptoCode, cryptoNetwork ] = coinParams
|
||||
const commissions = configManager.getCommissions(cryptoCode, deviceId, settings.config)
|
||||
|
|
@ -289,7 +285,7 @@ function plugins (settings, deviceId) {
|
|||
return Promise.all([
|
||||
buildAvailableCassettes(),
|
||||
buildAvailableRecyclers(),
|
||||
fetchCurrentConfigVersion(),
|
||||
settingsLoader.fetchCurrentConfigVersion(),
|
||||
millisecondsToMinutes(getTimezoneOffset(localeConfig.timezone)),
|
||||
loyalty.getNumberOfAvailablePromoCodes(),
|
||||
Promise.all(supportsBatchingPromise),
|
||||
|
|
@ -692,169 +688,73 @@ function plugins (settings, deviceId) {
|
|||
}
|
||||
|
||||
function checkDeviceCashBalances (fiatCode, device) {
|
||||
const cashOutConfig = configManager.getCashOut(device.deviceId, settings.config)
|
||||
const denomination1 = cashOutConfig.cassette1
|
||||
const denomination2 = cashOutConfig.cassette2
|
||||
const denomination3 = cashOutConfig.cassette3
|
||||
const denomination4 = cashOutConfig.cassette4
|
||||
const denominationRecycler1 = cashOutConfig.recycler1
|
||||
const denominationRecycler2 = cashOutConfig.recycler2
|
||||
const denominationRecycler3 = cashOutConfig.recycler3
|
||||
const denominationRecycler4 = cashOutConfig.recycler4
|
||||
const denominationRecycler5 = cashOutConfig.recycler5
|
||||
const denominationRecycler6 = cashOutConfig.recycler6
|
||||
const cashOutEnabled = cashOutConfig.active
|
||||
const isUnitLow = (have, max, limit) => cashOutEnabled && ((have / max) * 100) < limit
|
||||
// const isUnitHigh = (have, max, limit) => cashOutEnabled && ((have / max) * 100) > limit
|
||||
|
||||
// const isUnitOutOfBounds = (have, max, lowerBound, upperBound) => isUnitLow(have, max, lowerBound) || isUnitHigh(have, max, upperBound)
|
||||
|
||||
const notifications = configManager.getNotifications(null, device.deviceId, settings.config)
|
||||
|
||||
const deviceId = device.deviceId
|
||||
const machineName = device.name
|
||||
const notifications = configManager.getNotifications(null, deviceId, settings.config)
|
||||
|
||||
const cashInAlert = device.cashUnits.cashbox > notifications.cashInAlertThreshold
|
||||
? {
|
||||
const cashInAlerts = device.cashUnits.cashbox > notifications.cashInAlertThreshold
|
||||
? [{
|
||||
code: 'CASH_BOX_FULL',
|
||||
machineName,
|
||||
deviceId: device.deviceId,
|
||||
deviceId,
|
||||
notes: device.cashUnits.cashbox
|
||||
}
|
||||
: null
|
||||
}]
|
||||
: []
|
||||
|
||||
const cassette1Alert = device.numberOfCassettes >= 1 && isUnitLow(device.cashUnits.cassette1, getCashUnitCapacity(device.model, 'cassette'), notifications.fillingPercentageCassette1)
|
||||
? {
|
||||
code: 'LOW_CASH_OUT',
|
||||
cassette: 1,
|
||||
machineName,
|
||||
deviceId: device.deviceId,
|
||||
notes: device.cashUnits.cassette1,
|
||||
denomination: denomination1,
|
||||
fiatCode
|
||||
}
|
||||
: null
|
||||
const cashOutConfig = configManager.getCashOut(deviceId, settings.config)
|
||||
const cashOutEnabled = cashOutConfig.active
|
||||
const isUnitLow = (have, max, limit) => ((have / max) * 100) < limit
|
||||
|
||||
const cassette2Alert = device.numberOfCassettes >= 2 && isUnitLow(device.cashUnits.cassette2, getCashUnitCapacity(device.model, 'cassette'), notifications.fillingPercentageCassette2)
|
||||
? {
|
||||
code: 'LOW_CASH_OUT',
|
||||
cassette: 2,
|
||||
machineName,
|
||||
deviceId: device.deviceId,
|
||||
notes: device.cashUnits.cassette2,
|
||||
denomination: denomination2,
|
||||
fiatCode
|
||||
}
|
||||
: null
|
||||
if (!cashOutEnabled)
|
||||
return cashInAlerts
|
||||
|
||||
const cassette3Alert = device.numberOfCassettes >= 3 && isUnitLow(device.cashUnits.cassette3, getCashUnitCapacity(device.model, 'cassette'), notifications.fillingPercentageCassette3)
|
||||
? {
|
||||
code: 'LOW_CASH_OUT',
|
||||
cassette: 3,
|
||||
machineName,
|
||||
deviceId: device.deviceId,
|
||||
notes: device.cashUnits.cassette3,
|
||||
denomination: denomination3,
|
||||
fiatCode
|
||||
}
|
||||
: null
|
||||
const cassetteCapacity = getCashUnitCapacity(device.model, 'cassette')
|
||||
const cassetteAlerts = Array(Math.min(device.numberOfCassettes ?? 0, CASH_OUT_MAXIMUM_AMOUNT_OF_CASSETTES))
|
||||
.fill(null)
|
||||
.flatMap((_elem, idx) => {
|
||||
const nth = idx + 1
|
||||
const cassetteField = `cassette${nth}`
|
||||
const notes = device.cashUnits[cassetteField]
|
||||
const denomination = cashOutConfig[cassetteField]
|
||||
|
||||
const cassette4Alert = device.numberOfCassettes >= 4 && isUnitLow(device.cashUnits.cassette4, getCashUnitCapacity(device.model, 'cassette'), notifications.fillingPercentageCassette4)
|
||||
? {
|
||||
code: 'LOW_CASH_OUT',
|
||||
cassette: 4,
|
||||
machineName,
|
||||
deviceId: device.deviceId,
|
||||
notes: device.cashUnits.cassette4,
|
||||
denomination: denomination4,
|
||||
fiatCode
|
||||
}
|
||||
: null
|
||||
const limit = notifications[`fillingPercentageCassette${nth}`]
|
||||
return isUnitLow(notes, cassetteCapacity, limit) ?
|
||||
[{
|
||||
code: 'LOW_CASH_OUT',
|
||||
cassette: nth,
|
||||
machineName,
|
||||
deviceId,
|
||||
notes,
|
||||
denomination,
|
||||
fiatCode
|
||||
}] :
|
||||
[]
|
||||
})
|
||||
|
||||
const recycler1Alert = device.numberOfRecyclers >= 1 && isUnitLow(device.cashUnits.recycler1, getCashUnitCapacity(device.model, 'recycler'), notifications.fillingPercentageRecycler1)
|
||||
? {
|
||||
code: 'LOW_RECYCLER_STACKER',
|
||||
cassette: 4,
|
||||
machineName,
|
||||
deviceId: device.deviceId,
|
||||
notes: device.cashUnits.recycler1,
|
||||
denomination: denominationRecycler1,
|
||||
fiatCode
|
||||
}
|
||||
: null
|
||||
const recyclerCapacity = getCashUnitCapacity(device.model, 'recycler')
|
||||
const recyclerAlerts = Array(Math.min(device.numberOfRecyclers ?? 0, CASH_OUT_MAXIMUM_AMOUNT_OF_RECYCLERS))
|
||||
.fill(null)
|
||||
.flatMap((_elem, idx) => {
|
||||
const nth = idx + 1
|
||||
const recyclerField = `recycler${nth}`
|
||||
const notes = device.cashUnits[recyclerField]
|
||||
const denomination = cashOutConfig[recyclerField]
|
||||
|
||||
const recycler2Alert = device.numberOfRecyclers >= 2 && isUnitLow(device.cashUnits.recycler2, getCashUnitCapacity(device.model, 'recycler'), notifications.fillingPercentageRecycler2)
|
||||
? {
|
||||
code: 'LOW_RECYCLER_STACKER',
|
||||
cassette: 4,
|
||||
machineName,
|
||||
deviceId: device.deviceId,
|
||||
notes: device.cashUnits.recycler2,
|
||||
denomination: denominationRecycler2,
|
||||
fiatCode
|
||||
}
|
||||
: null
|
||||
const limit = notifications[`fillingPercentageRecycler${nth}`]
|
||||
return isUnitLow(notes, recyclerCapacity, limit) ?
|
||||
[{
|
||||
code: 'LOW_RECYCLER_STACKER',
|
||||
cassette: nth, // @see DETAIL_TEMPLATE in /lib/notifier/utils.js
|
||||
machineName,
|
||||
deviceId,
|
||||
notes,
|
||||
denomination,
|
||||
fiatCode
|
||||
}] :
|
||||
[]
|
||||
})
|
||||
|
||||
const recycler3Alert = device.numberOfRecyclers >= 3 && isUnitLow(device.cashUnits.recycler3, getCashUnitCapacity(device.model, 'recycler'), notifications.fillingPercentageRecycler3)
|
||||
? {
|
||||
code: 'LOW_RECYCLER_STACKER',
|
||||
cassette: 4,
|
||||
machineName,
|
||||
deviceId: device.deviceId,
|
||||
notes: device.cashUnits.recycler3,
|
||||
denomination: denominationRecycler3,
|
||||
fiatCode
|
||||
}
|
||||
: null
|
||||
|
||||
const recycler4Alert = device.numberOfRecyclers >= 4 && isUnitLow(device.cashUnits.recycler4, getCashUnitCapacity(device.model, 'recycler'), notifications.fillingPercentageRecycler4)
|
||||
? {
|
||||
code: 'LOW_RECYCLER_STACKER',
|
||||
cassette: 4,
|
||||
machineName,
|
||||
deviceId: device.deviceId,
|
||||
notes: device.cashUnits.recycler4,
|
||||
denomination: denominationRecycler4,
|
||||
fiatCode
|
||||
}
|
||||
: null
|
||||
|
||||
const recycler5Alert = device.numberOfRecyclers >= 5 && isUnitLow(device.cashUnits.recycler5, getCashUnitCapacity(device.model, 'recycler'), notifications.fillingPercentageRecycler5)
|
||||
? {
|
||||
code: 'LOW_RECYCLER_STACKER',
|
||||
cassette: 4,
|
||||
machineName,
|
||||
deviceId: device.deviceId,
|
||||
notes: device.cashUnits.recycler5,
|
||||
denomination: denominationRecycler5,
|
||||
fiatCode
|
||||
}
|
||||
: null
|
||||
|
||||
const recycler6Alert = device.numberOfRecyclers >= 6 && isUnitLow(device.cashUnits.recycler6, getCashUnitCapacity(device.model, 'recycler'), notifications.fillingPercentageRecycler6)
|
||||
? {
|
||||
code: 'LOW_RECYCLER_STACKER',
|
||||
cassette: 4,
|
||||
machineName,
|
||||
deviceId: device.deviceId,
|
||||
notes: device.cashUnits.recycler6,
|
||||
denomination: denominationRecycler6,
|
||||
fiatCode
|
||||
}
|
||||
: null
|
||||
|
||||
return _.compact([
|
||||
cashInAlert,
|
||||
cassette1Alert,
|
||||
cassette2Alert,
|
||||
cassette3Alert,
|
||||
cassette4Alert,
|
||||
recycler1Alert,
|
||||
recycler2Alert,
|
||||
recycler3Alert,
|
||||
recycler4Alert,
|
||||
recycler5Alert,
|
||||
recycler6Alert
|
||||
])
|
||||
return [].concat(cashInAlerts, cassetteAlerts, recyclerAlerts)
|
||||
}
|
||||
|
||||
function checkCryptoBalances (fiatCode, devices) {
|
||||
|
|
@ -1036,7 +936,6 @@ function plugins (settings, deviceId) {
|
|||
sell,
|
||||
getNotificationConfig,
|
||||
notifyOperator,
|
||||
fetchCurrentConfigVersion,
|
||||
pruneMachinesHeartbeat,
|
||||
rateAddress,
|
||||
rateTransaction,
|
||||
|
|
|
|||
95
lib/plugins/wallet-scoring/elliptic/elliptic.js
Normal file
95
lib/plugins/wallet-scoring/elliptic/elliptic.js
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
const { AML } = require('elliptic-sdk')
|
||||
const _ = require('lodash/fp')
|
||||
|
||||
const NAME = 'Elliptic'
|
||||
|
||||
const HOLLISTIC_COINS = {
|
||||
BTC: 'BTC',
|
||||
ETH: 'ETH',
|
||||
USDT: 'USDT',
|
||||
USDT_TRON: 'USDT',
|
||||
LTC: 'LTC',
|
||||
TRX: 'TRX'
|
||||
}
|
||||
|
||||
const SINGLE_ASSET_COINS = {
|
||||
ZEC: {
|
||||
asset: 'ZEC',
|
||||
blockchain: 'zcash'
|
||||
},
|
||||
BCH: {
|
||||
asset: 'BCH',
|
||||
blockchain: 'bitcoin_cash'
|
||||
}
|
||||
}
|
||||
|
||||
const TYPE = {
|
||||
TRANSACTION: 'transaction',
|
||||
ADDRESS: 'address'
|
||||
}
|
||||
|
||||
const SUPPORTED_COINS = { ...HOLLISTIC_COINS, ...SINGLE_ASSET_COINS }
|
||||
|
||||
function rate (account, objectType, cryptoCode, objectId) {
|
||||
return isWalletScoringEnabled(account, cryptoCode).then(isEnabled => {
|
||||
if (!isEnabled) return Promise.resolve(null)
|
||||
|
||||
const aml = new AML({
|
||||
key: account.apiKey,
|
||||
secret: account.apiSecret
|
||||
})
|
||||
|
||||
const isHolistic = Object.keys(HOLLISTIC_COINS).includes(cryptoCode)
|
||||
|
||||
const requestBody = {
|
||||
subject: {
|
||||
asset: isHolistic ? 'holistic' : SINGLE_ASSET_COINS[cryptoCode].asset,
|
||||
blockchain: isHolistic ? 'holistic' : SINGLE_ASSET_COINS[cryptoCode].blockchain,
|
||||
type: objectType,
|
||||
hash: objectId
|
||||
},
|
||||
type: objectType === TYPE.ADDRESS ? 'wallet_exposure' : 'source_of_funds'
|
||||
}
|
||||
|
||||
const threshold = account.scoreThreshold
|
||||
const endpoint = objectType === TYPE.ADDRESS ? '/v2/wallet/synchronous' : '/v2/analysis/synchronous'
|
||||
|
||||
return aml.client
|
||||
.post(endpoint, requestBody)
|
||||
.then((res) => {
|
||||
const resScore = res.data?.risk_score
|
||||
|
||||
// elliptic returns 0-1 score, but we're accepting 0-100 config
|
||||
// normalize score to 0-10 where 0 is the lowest risk
|
||||
// elliptic score can be null and contains decimals
|
||||
return {score: (resScore || 0) * 10, isValid: ((resScore || 0) * 100) < threshold}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function rateTransaction (account, cryptoCode, transactionId) {
|
||||
return rate(account, TYPE.TRANSACTION, cryptoCode, transactionId)
|
||||
}
|
||||
|
||||
function rateAddress (account, cryptoCode, address) {
|
||||
return rate(account, TYPE.ADDRESS, cryptoCode, address)
|
||||
}
|
||||
|
||||
function isWalletScoringEnabled (account, cryptoCode) {
|
||||
const isAccountEnabled = !_.isNil(account) && account.enabled
|
||||
|
||||
if (!isAccountEnabled) return Promise.resolve(false)
|
||||
|
||||
if (!Object.keys(SUPPORTED_COINS).includes(cryptoCode)) {
|
||||
return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode))
|
||||
}
|
||||
|
||||
return Promise.resolve(true)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
NAME,
|
||||
rateAddress,
|
||||
rateTransaction,
|
||||
isWalletScoringEnabled
|
||||
}
|
||||
|
|
@ -219,5 +219,6 @@ module.exports = {
|
|||
sendCoinsBatch,
|
||||
checkBlockchainStatus,
|
||||
getTxHashesByAddress,
|
||||
fetch,
|
||||
SUPPORTS_BATCHING
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ const SWEEP_QUEUE = new PQueue({
|
|||
interval: 250,
|
||||
})
|
||||
|
||||
const SEND_QUEUE = new PQueue({
|
||||
concurrency: 1,
|
||||
})
|
||||
|
||||
const infuraCalls = {}
|
||||
|
||||
const pify = _function => {
|
||||
|
|
@ -78,18 +82,20 @@ function sendCoins (account, tx, settings, operatorId, feeMultiplier) {
|
|||
const { toAddress, cryptoAtoms, cryptoCode } = tx
|
||||
const isErc20Token = coins.utils.isErc20Token(cryptoCode)
|
||||
|
||||
return (isErc20Token ? generateErc20Tx : generateTx)(toAddress, defaultWallet(account), cryptoAtoms, false, cryptoCode)
|
||||
.then(pify(web3.eth.sendSignedTransaction))
|
||||
.then(txid => {
|
||||
return pify(web3.eth.getTransaction)(txid)
|
||||
.then(tx => {
|
||||
if (!tx) return { txid }
|
||||
return SEND_QUEUE.add(() =>
|
||||
(isErc20Token ? generateErc20Tx : generateTx)(toAddress, defaultWallet(account), cryptoAtoms, false, cryptoCode)
|
||||
.then(pify(web3.eth.sendSignedTransaction))
|
||||
.then(txid => {
|
||||
return pify(web3.eth.getTransaction)(txid)
|
||||
.then(tx => {
|
||||
if (!tx) return { txid }
|
||||
|
||||
const fee = new BN(tx.gas).times(new BN(tx.gasPrice)).decimalPlaces(0)
|
||||
const fee = new BN(tx.gas).times(new BN(tx.gasPrice)).decimalPlaces(0)
|
||||
|
||||
return { txid, fee }
|
||||
})
|
||||
})
|
||||
return { txid, fee }
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function checkCryptoCode (cryptoCode) {
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ const base = require('../geth/base')
|
|||
const T = require('../../../time')
|
||||
const { BALANCE_FETCH_SPEED_MULTIPLIER } = require('../../../constants')
|
||||
|
||||
const REGULAR_TX_POLLING = 5 * T.seconds
|
||||
|
||||
const NAME = 'infura'
|
||||
|
||||
function run (account) {
|
||||
|
|
@ -27,21 +25,13 @@ function shouldGetStatus (tx) {
|
|||
const timePassedSinceTx = Date.now() - new Date(tx.created)
|
||||
const timePassedSinceReq = Date.now() - new Date(txsCache.get(tx.id).lastReqTime)
|
||||
|
||||
// Allow for infura to gradually lower the amount of requests based on the time passed since the transaction
|
||||
// Until first 5 minutes - 1/2 regular polling speed
|
||||
// Until first 10 minutes - 1/4 regular polling speed
|
||||
// Until first hour - 1/8 polling speed
|
||||
// Until first two hours - 1/12 polling speed
|
||||
// Until first four hours - 1/16 polling speed
|
||||
// Until first day - 1/24 polling speed
|
||||
// After first day - 1/32 polling speed
|
||||
if (timePassedSinceTx < 5 * T.minutes) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 2 * REGULAR_TX_POLLING
|
||||
if (timePassedSinceTx < 10 * T.minutes) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 4 * REGULAR_TX_POLLING
|
||||
if (timePassedSinceTx < 1 * T.hour) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 8 * REGULAR_TX_POLLING
|
||||
if (timePassedSinceTx < 2 * T.hours) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 12 * REGULAR_TX_POLLING
|
||||
if (timePassedSinceTx < 4 * T.hours) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 16 * REGULAR_TX_POLLING
|
||||
if (timePassedSinceTx < 1 * T.day) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 24 * REGULAR_TX_POLLING
|
||||
return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 32 * REGULAR_TX_POLLING
|
||||
if (timePassedSinceTx < 3 * T.minutes) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 10 * T.seconds
|
||||
if (timePassedSinceTx < 5 * T.minutes) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 20 * T.seconds
|
||||
if (timePassedSinceTx < 30 * T.minutes) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > T.minute
|
||||
if (timePassedSinceTx < 1 * T.hour) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 2 * T.minute
|
||||
if (timePassedSinceTx < 3 * T.hours) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 5 * T.minute
|
||||
if (timePassedSinceTx < 1 * T.day) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > T.hour
|
||||
return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > T.hour
|
||||
}
|
||||
|
||||
// Override geth's getStatus function to allow for different polling timing
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ app.use(compression({ threshold: 500 }))
|
|||
app.use(helmet())
|
||||
app.use(nocache())
|
||||
app.use(express.json({ limit: '2mb' }))
|
||||
app.use(morgan(':method :url :status :response-time ms - :res[content-length]', { stream: logger.stream }))
|
||||
app.use(morgan(':method :url :status :response-time ms -- :req[content-length]/:res[content-length] b', { stream: logger.stream }))
|
||||
|
||||
// app /pair and /ca routes
|
||||
app.use('/', pairingRoutes)
|
||||
|
|
|
|||
|
|
@ -311,6 +311,7 @@ function getOrAddCustomerPhone (req, res, next) {
|
|||
}
|
||||
|
||||
function getOrAddCustomerEmail (req, res, next) {
|
||||
const deviceId = req.deviceId
|
||||
const customerData = req.body
|
||||
|
||||
const pi = plugins(req.settings, req.deviceId)
|
||||
|
|
@ -318,7 +319,7 @@ function getOrAddCustomerEmail (req, res, next) {
|
|||
|
||||
return pi.getEmailCode(email)
|
||||
.then(code => {
|
||||
return addOrUpdateCustomer(customerData, req.settings.config, true)
|
||||
return addOrUpdateCustomer(customerData, deviceId, req.settings.config, true)
|
||||
.then(customer => respond(req, res, { code, customer }))
|
||||
})
|
||||
.catch(err => {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ const nmd = require('nano-markdown')
|
|||
const router = express.Router()
|
||||
|
||||
const configManager = require('../new-config-manager')
|
||||
const plugins = require('../plugins')
|
||||
const settingsLoader = require('../new-settings-loader')
|
||||
|
||||
const createTerms = terms => (terms.active && terms.text) ? ({
|
||||
delay: terms.delay,
|
||||
|
|
@ -18,15 +18,10 @@ const createTerms = terms => (terms.active && terms.text) ? ({
|
|||
|
||||
function getTermsConditions (req, res, next) {
|
||||
const deviceId = req.deviceId
|
||||
const settings = req.settings
|
||||
|
||||
const terms = configManager.getTermsConditions(settings.config)
|
||||
|
||||
const pi = plugins(settings, deviceId)
|
||||
|
||||
return pi.fetchCurrentConfigVersion().then(version => {
|
||||
return res.json({ terms: createTerms(terms), version })
|
||||
})
|
||||
const { config } = req.settings
|
||||
const terms = configManager.getTermsConditions(config)
|
||||
return settingsLoader.fetchCurrentConfigVersion()
|
||||
.then(version => res.json({ terms: createTerms(terms), version }))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,23 @@
|
|||
const ph = require('./plugin-helper')
|
||||
const argv = require('minimist')(process.argv.slice(2))
|
||||
const configManager = require('./new-config-manager')
|
||||
|
||||
function loadWalletScoring (settings, cryptoCode) {
|
||||
const pluginCode = argv.mockScoring ? 'mock-scoring' : 'scorechain'
|
||||
const wallet = cryptoCode ? ph.load(ph.WALLET, configManager.getWalletSettings(cryptoCode, settings.config).wallet) : null
|
||||
const plugin = ph.load(ph.WALLET_SCORING, pluginCode)
|
||||
const account = settings.accounts[pluginCode]
|
||||
// TODO - This function should be rolled back after UI is created for this feature
|
||||
function loadWalletScoring (settings) {
|
||||
if (argv.mockScoring) {
|
||||
const mock = ph.load(ph.WALLET_SCORING, 'mock-scoring')
|
||||
return { plugin: mock, account: {} }
|
||||
}
|
||||
|
||||
return { plugin, account, wallet }
|
||||
const scorechainAccount = settings.accounts['scorechain']
|
||||
if (scorechainAccount?.enabled) {
|
||||
const scorechain = ph.load(ph.WALLET_SCORING, 'scorechain')
|
||||
return { plugin: scorechain, account: scorechainAccount}
|
||||
}
|
||||
|
||||
const ellipticAccount = settings.accounts['elliptic']
|
||||
const elliptic = ph.load(ph.WALLET_SCORING, 'elliptic')
|
||||
|
||||
return { plugin: elliptic, account: ellipticAccount }
|
||||
}
|
||||
|
||||
function rateTransaction (settings, cryptoCode, address) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue