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
2
.github/workflows/docker-build.yml
vendored
2
.github/workflows/docker-build.yml
vendored
|
|
@ -3,7 +3,7 @@ name: Docker Build and Publish
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- release-10.0
|
- dev
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DOCKERHUB_SERVER_REPO: lamassu/lamassu-server
|
DOCKERHUB_SERVER_REPO: lamassu/lamassu-server
|
||||||
|
|
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -104,7 +104,7 @@ NOW, THEREFORE, in consideration of the mutual promises set forth herein, Lamass
|
||||||
|
|
||||||
13. Non-assignment
|
13. Non-assignment
|
||||||
|
|
||||||
13.1. This Agreement, any claims and the licenses granted by it may not be assigned, sublicensed, or otherwise transferred by Licensee without the prior written consent of Lamassu. This Section 16.1 does not apply to Licensee with regard to current and future entities of Lamassu.
|
13.1. This Agreement, any claims and the licenses granted by it may not be assigned, sublicensed, or otherwise transferred by Licensee without the prior written consent of Lamassu.
|
||||||
|
|
||||||
13.2. This Agreement and any claims hereunder may not be assigned by Lamassu to any third party without the prior written consent of Licensee.
|
13.2. This Agreement and any claims hereunder may not be assigned by Lamassu to any third party without the prior written consent of Licensee.
|
||||||
|
|
||||||
|
|
|
||||||
47
bin/lamassu-btc-bumpfee
Normal file
47
bin/lamassu-btc-bumpfee
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const inquirer = require('inquirer')
|
||||||
|
|
||||||
|
const bitcoind = require('../lib/plugins/wallet/bitcoind/bitcoind')
|
||||||
|
const BN = require('../lib/bn')
|
||||||
|
const mempool = require('../lib/blockexplorers/mempool.space')
|
||||||
|
|
||||||
|
const txId = process.argv[2]
|
||||||
|
if (!txId) {
|
||||||
|
console.error('Please provide a BTC transaction hash as input.')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const bumpTransactionFee = async (txId) => {
|
||||||
|
const txData = await bitcoind.fetch('gettransaction', [txId, true, true])
|
||||||
|
|
||||||
|
const fee = new BN(txData.fee).abs().shiftedBy(8).decimalPlaces(0)
|
||||||
|
const size = txData.decoded.vsize
|
||||||
|
const satPerVb = fee.div(size)
|
||||||
|
|
||||||
|
console.log(`Current fee: ${satPerVb.toFixed(2).toString()} sat/vB`)
|
||||||
|
|
||||||
|
const recommendedFees = await mempool.getSatBEstimateFees()
|
||||||
|
|
||||||
|
console.log('Recommended fees (sat/vB):', recommendedFees)
|
||||||
|
|
||||||
|
const { selectedFee } = await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: 'list',
|
||||||
|
name: 'selectedFee',
|
||||||
|
message: 'Select a fee higher than the current one:',
|
||||||
|
choices: Object.entries(recommendedFees)
|
||||||
|
.filter(([_, value]) => satPerVb.lt(value))
|
||||||
|
.map(([key, value]) => ({name: `${key}: ${value} sat/vB`, value})),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const { txid } = await bitcoind.fetch('bumpfee', [txId, {fee_rate: selectedFee}])
|
||||||
|
|
||||||
|
console.log(`
|
||||||
|
Fee bumped to ${selectedFee.toFixed(2)} sat/vB
|
||||||
|
Transaction ID: ${txid}
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
bumpTransactionFee(txId)
|
||||||
53
bin/lamassu-trx-recovery
Normal file
53
bin/lamassu-trx-recovery
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
require('../lib/environment-helper')
|
||||||
|
const TronWeb = require('tronweb')
|
||||||
|
const db = require('../lib/db')
|
||||||
|
const _ = require('lodash/fp')
|
||||||
|
|
||||||
|
const pify = require('pify')
|
||||||
|
const fs = pify(require('fs'))
|
||||||
|
|
||||||
|
const MNEMONIC_PATH = process.env.MNEMONIC_PATH
|
||||||
|
|
||||||
|
const defaultPrefixPath = "m/44'/195'/0'/0"
|
||||||
|
const paymentPrefixPath = "m/44'/195'/1'/0"
|
||||||
|
|
||||||
|
const address = process.argv[2]
|
||||||
|
|
||||||
|
if (!MNEMONIC_PATH) {
|
||||||
|
console.error(`Unable to fetch mnemonic from your account!`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!address) {
|
||||||
|
console.log('Usage: lamassu-trx-recovery <cash-out address>')
|
||||||
|
process.exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
function run (address) {
|
||||||
|
Promise.all([fetchMnemonic(), searchForHdIndex(address)])
|
||||||
|
.then(([mnemonic, hdIndex]) => {
|
||||||
|
try {
|
||||||
|
const prefix = !_.isNil(hdIndex) ? `${paymentPrefixPath}/${hdIndex}` : `${defaultPrefixPath}/0`
|
||||||
|
const privKey = TronWeb.fromMnemonic(mnemonic.replace(/[\r\n]/gm, ' ').trim(), prefix).privateKey
|
||||||
|
console.log(`Private key: `, privKey.slice(2))
|
||||||
|
process.exit(0)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error while retrieving private key!`)
|
||||||
|
process.exit(3)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchForHdIndex (address) {
|
||||||
|
const sql = `SELECT hd_index FROM cash_out_txs WHERE to_address = $1`
|
||||||
|
return db.oneOrNone(sql, [address])
|
||||||
|
.then(result => _.get('hd_index', result))
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchMnemonic () {
|
||||||
|
return fs.readFile(MNEMONIC_PATH, 'utf8')
|
||||||
|
}
|
||||||
|
|
||||||
|
run(address)
|
||||||
|
|
@ -92,7 +92,7 @@ function loadLatestConfig (filterSchemaVersion = true) {
|
||||||
order by id desc
|
order by id desc
|
||||||
limit 1`
|
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(row => row.data.config)
|
||||||
.then(configValidate.validate)
|
.then(configValidate.validate)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
|
@ -240,6 +240,7 @@ module.exports = {
|
||||||
loadRecentConfig,
|
loadRecentConfig,
|
||||||
load,
|
load,
|
||||||
loadLatest,
|
loadLatest,
|
||||||
|
loadLatestConfig,
|
||||||
save,
|
save,
|
||||||
loadFixture,
|
loadFixture,
|
||||||
mergeValues,
|
mergeValues,
|
||||||
|
|
|
||||||
|
|
@ -30,37 +30,37 @@ const BINARIES = {
|
||||||
BTC: {
|
BTC: {
|
||||||
defaultUrl: 'https://bitcoincore.org/bin/bitcoin-core-0.20.1/bitcoin-0.20.1-x86_64-linux-gnu.tar.gz',
|
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',
|
defaultDir: 'bitcoin-0.20.1/bin',
|
||||||
url: 'https://bitcoincore.org/bin/bitcoin-core-27.1/bitcoin-27.1-x86_64-linux-gnu.tar.gz',
|
url: 'https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz',
|
||||||
dir: 'bitcoin-27.1/bin'
|
dir: 'bitcoin-28.0/bin'
|
||||||
},
|
},
|
||||||
ETH: {
|
ETH: {
|
||||||
url: 'https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.14.8-a9523b64.tar.gz',
|
url: 'https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.14.12-293a300d.tar.gz',
|
||||||
dir: 'geth-linux-amd64-1.14.8-a9523b64'
|
dir: 'geth-linux-amd64-1.14.12-293a300d'
|
||||||
},
|
},
|
||||||
ZEC: {
|
ZEC: {
|
||||||
url: 'https://github.com/zcash/artifacts/raw/master/v5.9.0/bullseye/zcash-5.9.0-linux64-debian-bullseye.tar.gz',
|
url: 'https://download.z.cash/downloads/zcash-6.0.0-linux64-debian-bullseye.tar.gz',
|
||||||
dir: 'zcash-5.9.0/bin'
|
dir: 'zcash-6.0.0/bin'
|
||||||
},
|
},
|
||||||
DASH: {
|
DASH: {
|
||||||
defaultUrl: 'https://github.com/dashpay/dash/releases/download/v18.1.0/dashcore-18.1.0-x86_64-linux-gnu.tar.gz',
|
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',
|
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',
|
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.0/bin'
|
dir: 'dashcore-21.1.1/bin'
|
||||||
},
|
},
|
||||||
LTC: {
|
LTC: {
|
||||||
defaultUrl: 'https://download.litecoin.org/litecoin-0.18.1/linux/litecoin-0.18.1-x86_64-linux-gnu.tar.gz',
|
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',
|
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',
|
url: 'https://download.litecoin.org/litecoin-0.21.4/linux/litecoin-0.21.4-x86_64-linux-gnu.tar.gz',
|
||||||
dir: 'litecoin-0.21.3/bin'
|
dir: 'litecoin-0.21.4/bin'
|
||||||
},
|
},
|
||||||
BCH: {
|
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',
|
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-27.1.0/bin',
|
dir: 'bitcoin-cash-node-28.0.0/bin',
|
||||||
files: [['bitcoind', 'bitcoincashd'], ['bitcoin-cli', 'bitcoincash-cli']]
|
files: [['bitcoind', 'bitcoincashd'], ['bitcoin-cli', 'bitcoincash-cli']]
|
||||||
},
|
},
|
||||||
XMR: {
|
XMR: {
|
||||||
url: 'https://downloads.getmonero.org/cli/monero-linux-x64-v0.18.3.3.tar.bz2',
|
url: 'https://downloads.getmonero.org/cli/monero-linux-x64-v0.18.3.4.tar.bz2',
|
||||||
dir: 'monero-x86_64-linux-gnu-v0.18.3.3',
|
dir: 'monero-x86_64-linux-gnu-v0.18.3.4',
|
||||||
files: [['monerod', 'monerod'], ['monero-wallet-rpc', 'monero-wallet-rpc']]
|
files: [['monerod', 'monerod'], ['monero-wallet-rpc', 'monero-wallet-rpc']]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,12 @@ const getSatBEstimateFee = () => {
|
||||||
.then(r => r.data.hourFee)
|
.then(r => r.data.hourFee)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { getSatBEstimateFee }
|
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 addressReuse = false
|
||||||
let walletScore = {}
|
let walletScore = {}
|
||||||
|
|
||||||
const promises = [settingsLoader.loadLatest()]
|
const promises = [settingsLoader.loadLatestConfig()]
|
||||||
|
|
||||||
const isFirstPost = !r.tx.fiat || r.tx.fiat.isZero()
|
const isFirstPost = !r.tx.fiat || r.tx.fiat.isZero()
|
||||||
if (isFirstPost) {
|
if (isFirstPost) {
|
||||||
|
|
@ -44,7 +44,7 @@ function post (machineTx, pi) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(promises)
|
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
|
const rejectAddressReuse = configManager.getCompliance(config).rejectAddressReuse
|
||||||
|
|
||||||
walletScore = fetchedWalletScore
|
walletScore = fetchedWalletScore
|
||||||
|
|
|
||||||
|
|
@ -474,8 +474,4 @@ function migrate (config, accounts) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = { migrate }
|
||||||
migrateConfig,
|
|
||||||
migrateAccounts,
|
|
||||||
migrate
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ const CASH_UNIT_CAPACITY = {
|
||||||
|
|
||||||
const CASH_OUT_MINIMUM_AMOUNT_OF_CASSETTES = 2
|
const CASH_OUT_MINIMUM_AMOUNT_OF_CASSETTES = 2
|
||||||
const CASH_OUT_MAXIMUM_AMOUNT_OF_CASSETTES = 4
|
const CASH_OUT_MAXIMUM_AMOUNT_OF_CASSETTES = 4
|
||||||
|
const CASH_OUT_MAXIMUM_AMOUNT_OF_RECYCLERS = 6
|
||||||
const AUTHENTICATOR_ISSUER_ENTITY = 'Lamassu'
|
const AUTHENTICATOR_ISSUER_ENTITY = 'Lamassu'
|
||||||
const AUTH_TOKEN_EXPIRATION_TIME = '30 minutes'
|
const AUTH_TOKEN_EXPIRATION_TIME = '30 minutes'
|
||||||
const REGISTRATION_TOKEN_EXPIRATION_TIME = '30 minutes'
|
const REGISTRATION_TOKEN_EXPIRATION_TIME = '30 minutes'
|
||||||
|
|
@ -85,6 +86,7 @@ module.exports = {
|
||||||
CONFIRMATION_CODE,
|
CONFIRMATION_CODE,
|
||||||
CASH_OUT_MINIMUM_AMOUNT_OF_CASSETTES,
|
CASH_OUT_MINIMUM_AMOUNT_OF_CASSETTES,
|
||||||
CASH_OUT_MAXIMUM_AMOUNT_OF_CASSETTES,
|
CASH_OUT_MAXIMUM_AMOUNT_OF_CASSETTES,
|
||||||
|
CASH_OUT_MAXIMUM_AMOUNT_OF_RECYCLERS,
|
||||||
WALLET_SCORE_THRESHOLD,
|
WALLET_SCORE_THRESHOLD,
|
||||||
RECEIPT,
|
RECEIPT,
|
||||||
PSQL_URL,
|
PSQL_URL,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ const nmd = require('nano-markdown')
|
||||||
|
|
||||||
const plugins = require('../plugins')
|
const plugins = require('../plugins')
|
||||||
const configManager = require('../new-config-manager')
|
const configManager = require('../new-config-manager')
|
||||||
|
const settingsLoader = require('../new-settings-loader')
|
||||||
const { batchGetCustomInfoRequest, getCustomInfoRequests } = require('../new-admin/services/customInfoRequests')
|
const { batchGetCustomInfoRequest, getCustomInfoRequests } = require('../new-admin/services/customInfoRequests')
|
||||||
const state = require('../middlewares/state')
|
const state = require('../middlewares/state')
|
||||||
const { getMachine } = require('../machine-loader')
|
const { getMachine } = require('../machine-loader')
|
||||||
|
|
@ -323,8 +324,7 @@ const terms = (parent, { currentConfigVersion, currentHash }, { deviceId, settin
|
||||||
const isHashNew = hash !== currentHash
|
const isHashNew = hash !== currentHash
|
||||||
const text = isHashNew ? latestTerms.text : null
|
const text = isHashNew ? latestTerms.text : null
|
||||||
|
|
||||||
return plugins(settings, deviceId)
|
return settingsLoader.fetchCurrentConfigVersion()
|
||||||
.fetchCurrentConfigVersion()
|
|
||||||
.catch(() => null)
|
.catch(() => null)
|
||||||
.then(configVersion => isHashNew || _.isNil(currentConfigVersion) || currentConfigVersion < configVersion)
|
.then(configVersion => isHashNew || _.isNil(currentConfigVersion) || currentConfigVersion < configVersion)
|
||||||
.then(isVersionNew => isVersionNew ? _.omit(['text'], latestTerms) : null)
|
.then(isVersionNew => isVersionNew ? _.omit(['text'], latestTerms) : null)
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ type OperatorInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
type MachineInfo {
|
type MachineInfo {
|
||||||
deviceId: String!
|
deviceId: String! @deprecated(reason: "unused by the machine")
|
||||||
deviceName: String
|
deviceName: String
|
||||||
numberOfCassettes: Int
|
numberOfCassettes: Int
|
||||||
numberOfRecyclers: Int
|
numberOfRecyclers: Int
|
||||||
|
|
@ -107,7 +107,7 @@ type Trigger {
|
||||||
suspensionDays: Float
|
suspensionDays: Float
|
||||||
threshold: Int
|
threshold: Int
|
||||||
thresholdDays: Int
|
thresholdDays: Int
|
||||||
customInfoRequestId: String
|
customInfoRequestId: String @deprecated(reason: "use customInfoRequest.id")
|
||||||
customInfoRequest: CustomInfoRequest
|
customInfoRequest: CustomInfoRequest
|
||||||
externalService: String
|
externalService: String
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ const pairing = require('./pairing')
|
||||||
const { checkPings, checkStuckScreen } = require('./notifier')
|
const { checkPings, checkStuckScreen } = require('./notifier')
|
||||||
const dbm = require('./postgresql_interface')
|
const dbm = require('./postgresql_interface')
|
||||||
const configManager = require('./new-config-manager')
|
const configManager = require('./new-config-manager')
|
||||||
const settingsLoader = require('./new-settings-loader')
|
|
||||||
const notifierUtils = require('./notifier/utils')
|
const notifierUtils = require('./notifier/utils')
|
||||||
const notifierQueries = require('./notifier/queries')
|
const notifierQueries = require('./notifier/queries')
|
||||||
const { ApolloError } = require('apollo-server-errors');
|
const { ApolloError } = require('apollo-server-errors');
|
||||||
|
|
@ -94,9 +93,7 @@ function getUnpairedMachines () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getConfig (defaultConfig) {
|
function getConfig (defaultConfig) {
|
||||||
if (defaultConfig) return Promise.resolve(defaultConfig)
|
return defaultConfig ? Promise.resolve(defaultConfig) : loadLatestConfig()
|
||||||
|
|
||||||
return settingsLoader.loadLatest().config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStatus = (ping, stuck) => {
|
const getStatus = (ping, stuck) => {
|
||||||
|
|
@ -529,7 +526,6 @@ module.exports = {
|
||||||
updateNetworkHeartbeat,
|
updateNetworkHeartbeat,
|
||||||
getNetworkPerformance,
|
getNetworkPerformance,
|
||||||
getNetworkHeartbeat,
|
getNetworkHeartbeat,
|
||||||
getConfig,
|
|
||||||
getMachineIds,
|
getMachineIds,
|
||||||
emptyMachineUnits,
|
emptyMachineUnits,
|
||||||
refillMachineUnits,
|
refillMachineUnits,
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ const ALL_ACCOUNTS = [
|
||||||
{ code: 'blockcypher', display: 'Blockcypher', class: ZERO_CONF, cryptos: [BTC] },
|
{ 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: '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: '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: 'mock-scoring', display: 'Mock scoring', class: WALLET_SCORING, cryptos: ALL_CRYPTOS, dev: true },
|
||||||
{ code: 'sumsub', display: 'Sumsub', class: COMPLIANCE },
|
{ code: 'sumsub', display: 'Sumsub', class: COMPLIANCE },
|
||||||
{ code: 'mock-compliance', display: 'Mock Compliance', class: COMPLIANCE, dev: true },
|
{ code: 'mock-compliance', display: 'Mock Compliance', class: COMPLIANCE, dev: true },
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,7 @@ const resolvers = {
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
saveAccounts: (...[, { accounts }]) => settingsLoader.saveAccounts(accounts),
|
saveAccounts: (...[, { accounts }]) => settingsLoader.saveAccounts(accounts),
|
||||||
// resetAccounts: (...[, { schemaVersion }]) => settingsLoader.resetAccounts(schemaVersion),
|
|
||||||
saveConfig: (...[, { config }]) => settingsLoader.saveConfig(config),
|
saveConfig: (...[, { config }]) => settingsLoader.saveConfig(config),
|
||||||
// resetConfig: (...[, { schemaVersion }]) => settingsLoader.resetConfig(schemaVersion),
|
|
||||||
// migrateConfigAndAccounts: () => settingsLoader.migrate()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,7 @@ const typeDef = gql`
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
saveAccounts(accounts: JSONObject): JSONObject @auth
|
saveAccounts(accounts: JSONObject): JSONObject @auth
|
||||||
# resetAccounts(schemaVersion: Int): JSONObject @auth
|
|
||||||
saveConfig(config: JSONObject): JSONObject @auth
|
saveConfig(config: JSONObject): JSONObject @auth
|
||||||
# resetConfig(schemaVersion: Int): JSONObject @auth
|
|
||||||
# migrateConfigAndAccounts: JSONObject @auth
|
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,39 +56,45 @@ const addTermsHash = configs => {
|
||||||
)(terms)
|
)(terms)
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountsSql = `update user_config set data = $2, valid = $3, schema_version = $4 where type = $1;
|
const notifyReload = (dbOrTx, operatorId) =>
|
||||||
insert into user_config (type, data, valid, schema_version)
|
dbOrTx.none(
|
||||||
select $1, $2, $3, $4 where $1 not in (select type from user_config)`
|
'NOTIFY $1:name, $2',
|
||||||
|
['reload', JSON.stringify({ schema: asyncLocalStorage.getStore().get('schema'), operatorId })]
|
||||||
|
)
|
||||||
|
|
||||||
function saveAccounts (accounts) {
|
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')])
|
return Promise.all([loadAccounts(), getOperatorId('middleware')])
|
||||||
.then(([currentAccounts, operatorId]) => {
|
.then(([currentAccounts, operatorId]) => {
|
||||||
const newAccounts = _.merge(currentAccounts, accounts)
|
const newAccounts = _.merge(currentAccounts, accounts)
|
||||||
return db.tx(t => {
|
|
||||||
return t.none(accountsSql, ['accounts', { accounts: newAccounts }, true, NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
// Only allow one wallet scoring active at a time
|
||||||
.then(() => t.none('NOTIFY $1:name, $2', ['reload', JSON.stringify({ schema: asyncLocalStorage.getStore().get('schema'), operatorId })]))
|
if (accounts.elliptic?.enabled && newAccounts.scorechain) {
|
||||||
}).catch(console.error)
|
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) {
|
function loadAccounts (schemaVersion) {
|
||||||
const sql = `select data
|
const sql = `SELECT data
|
||||||
from user_config
|
FROM user_config
|
||||||
where type=$1
|
WHERE type = $1
|
||||||
and schema_version=$2
|
AND schema_version = $2
|
||||||
and valid
|
AND valid
|
||||||
order by id desc
|
ORDER BY id DESC
|
||||||
limit 1`
|
LIMIT 1`
|
||||||
|
|
||||||
return db.oneOrNone(sql, ['accounts', schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
return db.oneOrNone(sql, ['accounts', schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
||||||
.then(_.compose(_.defaultTo({}), _.get('data.accounts')))
|
.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) {
|
function saveConfig (config) {
|
||||||
return Promise.all([loadLatestConfigOrNone(), getOperatorId('middleware')])
|
return Promise.all([loadLatestConfigOrNone(), getOperatorId('middleware')])
|
||||||
.then(([currentConfig, operatorId]) => {
|
.then(([currentConfig, operatorId]) => {
|
||||||
const newConfig = addTermsHash(_.assign(currentConfig, config))
|
const newConfig = addTermsHash(_.assign(currentConfig, config))
|
||||||
return db.tx(t => {
|
return db.tx(t =>
|
||||||
return t.none(configSql, ['config', { config: newConfig }, true, NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
insertConfigRow(t, { config: newConfig })
|
||||||
.then(() => t.none('NOTIFY $1:name, $2', ['reload', JSON.stringify({ schema: asyncLocalStorage.getStore().get('schema'), operatorId })]))
|
.then(() => notifyReload(t, operatorId))
|
||||||
}).catch(console.error)
|
).catch(console.error)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,10 +133,10 @@ function removeFromConfig (fields) {
|
||||||
return Promise.all([loadLatestConfigOrNone(), getOperatorId('middleware')])
|
return Promise.all([loadLatestConfigOrNone(), getOperatorId('middleware')])
|
||||||
.then(([currentConfig, operatorId]) => {
|
.then(([currentConfig, operatorId]) => {
|
||||||
const newConfig = _.omit(fields, currentConfig)
|
const newConfig = _.omit(fields, currentConfig)
|
||||||
return db.tx(t => {
|
return db.tx(t =>
|
||||||
return t.none(configSql, ['config', { config: newConfig }, true, NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
insertConfigRow(t, { config: newConfig })
|
||||||
.then(() => t.none('NOTIFY $1:name, $2', ['reload', JSON.stringify({ schema: asyncLocalStorage.getStore().get('schema'), operatorId })]))
|
.then(() => notifyReload(t, operatorId))
|
||||||
}).catch(console.error)
|
).catch(console.error)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,23 +144,11 @@ function migrationSaveConfig (config) {
|
||||||
return loadLatestConfigOrNone()
|
return loadLatestConfigOrNone()
|
||||||
.then(currentConfig => {
|
.then(currentConfig => {
|
||||||
const newConfig = _.assign(currentConfig, config)
|
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)
|
.catch(console.error)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetConfig (schemaVersion) {
|
|
||||||
return db.none(
|
|
||||||
configSql,
|
|
||||||
[
|
|
||||||
'config',
|
|
||||||
{ config: schemaVersion === NEW_SETTINGS_LOADER_SCHEMA_VERSION ? {} : [] },
|
|
||||||
true,
|
|
||||||
schemaVersion
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadLatest (schemaVersion) {
|
function loadLatest (schemaVersion) {
|
||||||
return Promise.all([loadLatestConfigOrNoneReturningVersion(schemaVersion), loadAccounts(schemaVersion)])
|
return Promise.all([loadLatestConfigOrNoneReturningVersion(schemaVersion), loadAccounts(schemaVersion)])
|
||||||
.then(([configObj, accounts]) => ({
|
.then(([configObj, accounts]) => ({
|
||||||
|
|
@ -160,15 +159,15 @@ function loadLatest (schemaVersion) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadLatestConfig () {
|
function loadLatestConfig () {
|
||||||
const sql = `select data
|
const sql = `SELECT data
|
||||||
from user_config
|
FROM user_config
|
||||||
where type=$1
|
WHERE type = 'config'
|
||||||
and schema_version=$2
|
AND schema_version = $1
|
||||||
and valid
|
AND valid
|
||||||
order by id desc
|
ORDER BY id DESC
|
||||||
limit 1`
|
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 : {})
|
.then(row => row ? row.data.config : {})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
throw err
|
throw err
|
||||||
|
|
@ -176,38 +175,39 @@ function loadLatestConfig () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadLatestConfigOrNoneReturningVersion (schemaVersion) {
|
function loadLatestConfigOrNoneReturningVersion (schemaVersion) {
|
||||||
const sql = `select data, id
|
const sql = `SELECT data, id
|
||||||
from user_config
|
FROM user_config
|
||||||
where type=$1
|
WHERE type = 'config'
|
||||||
and schema_version=$2
|
AND schema_version = $1
|
||||||
order by id desc
|
AND valid
|
||||||
limit 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 ? { config: row.data.config, version: row.id } : {})
|
.then(row => row ? { config: row.data.config, version: row.id } : {})
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadLatestConfigOrNone (schemaVersion) {
|
function loadLatestConfigOrNone (schemaVersion) {
|
||||||
const sql = `select data
|
const sql = `SELECT data
|
||||||
from user_config
|
FROM user_config
|
||||||
where type=$1
|
WHERE type = 'config'
|
||||||
and schema_version=$2
|
AND schema_version = $1
|
||||||
order by id desc
|
ORDER BY id DESC
|
||||||
limit 1`
|
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 : {})
|
.then(row => row ? row.data.config : {})
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadConfig (versionId) {
|
function loadConfig (versionId) {
|
||||||
const sql = `select data
|
const sql = `SELECT data
|
||||||
from user_config
|
FROM user_config
|
||||||
where id=$1
|
WHERE id = $1
|
||||||
and type=$2
|
AND type = 'config'
|
||||||
and schema_version=$3
|
AND schema_version = $2
|
||||||
and valid`
|
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)
|
.then(row => row.data.config)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
if (err.name === 'QueryResultError') {
|
if (err.name === 'QueryResultError') {
|
||||||
|
|
@ -228,29 +228,25 @@ function load (versionId) {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
function migrate () {
|
const fetchCurrentConfigVersion = () => {
|
||||||
return loadLatest(OLD_SETTINGS_LOADER_SCHEMA_VERSION)
|
const sql = `SELECT id FROM user_config
|
||||||
.then(res => {
|
WHERE type = 'config'
|
||||||
const migrated = migration.migrate(res.config, res.accounts)
|
AND valid
|
||||||
saveConfig(migrated.config)
|
ORDER BY id DESC
|
||||||
saveAccounts(migrated.accounts)
|
LIMIT 1`
|
||||||
|
return db.one(sql).then(row => row.id)
|
||||||
return migrated
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
saveConfig,
|
saveConfig,
|
||||||
migrationSaveConfig,
|
migrationSaveConfig,
|
||||||
resetConfig,
|
|
||||||
saveAccounts,
|
saveAccounts,
|
||||||
resetAccounts,
|
|
||||||
loadAccounts,
|
loadAccounts,
|
||||||
showAccounts,
|
showAccounts,
|
||||||
loadLatest,
|
loadLatest,
|
||||||
loadLatestConfig,
|
loadLatestConfig,
|
||||||
loadLatestConfigOrNone,
|
loadLatestConfigOrNone,
|
||||||
load,
|
load,
|
||||||
migrate,
|
removeFromConfig,
|
||||||
removeFromConfig
|
fetchCurrentConfigVersion,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ const LOW_CRYPTO_BALANCE = 'LOW_CRYPTO_BALANCE'
|
||||||
const HIGH_CRYPTO_BALANCE = 'HIGH_CRYPTO_BALANCE'
|
const HIGH_CRYPTO_BALANCE = 'HIGH_CRYPTO_BALANCE'
|
||||||
const CASH_BOX_FULL = 'CASH_BOX_FULL'
|
const CASH_BOX_FULL = 'CASH_BOX_FULL'
|
||||||
const LOW_CASH_OUT = 'LOW_CASH_OUT'
|
const LOW_CASH_OUT = 'LOW_CASH_OUT'
|
||||||
|
const LOW_RECYCLER_STACKER = 'LOW_RECYCLER_STACKER'
|
||||||
const SECURITY = 'SECURITY'
|
const SECURITY = 'SECURITY'
|
||||||
|
|
||||||
const CODES_DISPLAY = {
|
const CODES_DISPLAY = {
|
||||||
|
|
@ -41,6 +42,7 @@ module.exports = {
|
||||||
HIGH_CRYPTO_BALANCE,
|
HIGH_CRYPTO_BALANCE,
|
||||||
CASH_BOX_FULL,
|
CASH_BOX_FULL,
|
||||||
LOW_CASH_OUT,
|
LOW_CASH_OUT,
|
||||||
|
LOW_RECYCLER_STACKER,
|
||||||
SECURITY,
|
SECURITY,
|
||||||
CODES_DISPLAY,
|
CODES_DISPLAY,
|
||||||
NETWORK_DOWN_TIME,
|
NETWORK_DOWN_TIME,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ const {
|
||||||
HIGH_CRYPTO_BALANCE,
|
HIGH_CRYPTO_BALANCE,
|
||||||
CASH_BOX_FULL,
|
CASH_BOX_FULL,
|
||||||
LOW_CASH_OUT,
|
LOW_CASH_OUT,
|
||||||
|
LOW_RECYCLER_STACKER,
|
||||||
SECURITY
|
SECURITY
|
||||||
} = require('./codes')
|
} = require('./codes')
|
||||||
|
|
||||||
|
|
@ -80,6 +81,8 @@ function emailAlert (alert) {
|
||||||
return `Cash box full on ${alert.machineName} [${alert.notes} banknotes]`
|
return `Cash box full on ${alert.machineName} [${alert.notes} banknotes]`
|
||||||
case LOW_CASH_OUT:
|
case LOW_CASH_OUT:
|
||||||
return `Cassette for ${alert.denomination} ${alert.fiatCode} low [${alert.notes} banknotes]`
|
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:
|
case SECURITY:
|
||||||
return `Cashbox removed on ${alert.machineName}`
|
return `Cashbox removed on ${alert.machineName}`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,8 +130,8 @@ function checkStuckScreen (deviceEvents, machine) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function transactionNotify (tx, rec) {
|
function transactionNotify (tx, rec) {
|
||||||
return settingsLoader.loadLatest().then(settings => {
|
return settingsLoader.loadLatestConfig().then(config => {
|
||||||
const notifSettings = configManager.getGlobalNotifications(settings.config)
|
const notifSettings = configManager.getGlobalNotifications(config)
|
||||||
const highValueTx = tx.fiat.gt(notifSettings.highValueTransaction || Infinity)
|
const highValueTx = tx.fiat.gt(notifSettings.highValueTransaction || Infinity)
|
||||||
const isCashOut = tx.direction === 'cashOut'
|
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
|
// 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 zeroConfLimit = walletSettings.zeroConfLimit || 0
|
||||||
const zeroConf = isCashOut && tx.fiat.lte(zeroConfLimit)
|
const zeroConf = isCashOut && tx.fiat.lte(zeroConfLimit)
|
||||||
const notificationsEnabled = notifSettings.sms.transactions || notifSettings.email.transactions
|
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
|
// for notification center, check if type of notification is active before calling the respective notify function
|
||||||
const notifyIfActive = (type, fnName, ...args) => {
|
const notifyIfActive = (type, fnName, ...args) => {
|
||||||
return settingsLoader.loadLatest().then(settings => {
|
return settingsLoader.loadLatestConfig().then(config => {
|
||||||
const notificationSettings = configManager.getGlobalNotifications(settings.config).notificationCenter
|
const notificationSettings = configManager.getGlobalNotifications(config).notificationCenter
|
||||||
if (!notificationCenter[fnName]) return Promise.reject(new Error(`Notification function ${fnName} for type ${type} does not exist`))
|
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()
|
if (!(notificationSettings.active && notificationSettings[type])) return Promise.resolve()
|
||||||
return notificationCenter[fnName](...args)
|
return notificationCenter[fnName](...args)
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,27 @@ const _ = require('lodash/fp')
|
||||||
|
|
||||||
const queries = require('./queries')
|
const queries = require('./queries')
|
||||||
const utils = require('./utils')
|
const utils = require('./utils')
|
||||||
const codes = require('./codes')
|
|
||||||
const customers = require('../customers')
|
const customers = require('../customers')
|
||||||
|
const {
|
||||||
|
NOTIFICATION_TYPES: {
|
||||||
|
SECURITY,
|
||||||
|
COMPLIANCE,
|
||||||
|
CRYPTO_BALANCE,
|
||||||
|
FIAT_BALANCE,
|
||||||
|
ERROR,
|
||||||
|
HIGH_VALUE_TX,
|
||||||
|
NORMAL_VALUE_TX
|
||||||
|
},
|
||||||
|
|
||||||
const { NOTIFICATION_TYPES: {
|
STALE,
|
||||||
SECURITY,
|
PING,
|
||||||
COMPLIANCE,
|
|
||||||
CRYPTO_BALANCE,
|
|
||||||
FIAT_BALANCE,
|
|
||||||
ERROR,
|
|
||||||
HIGH_VALUE_TX,
|
|
||||||
NORMAL_VALUE_TX }
|
|
||||||
} = codes
|
|
||||||
|
|
||||||
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 sanctionsNotify = (customer, phone) => {
|
||||||
const code = 'SANCTIONS'
|
const code = 'SANCTIONS'
|
||||||
|
|
@ -71,9 +78,13 @@ const fiatBalancesNotify = (fiatWarnings) => {
|
||||||
const { cassette, deviceId } = o.detail
|
const { cassette, deviceId } = o.detail
|
||||||
return cassette === balance.cassette && deviceId === balance.deviceId
|
return cassette === balance.cassette && deviceId === balance.deviceId
|
||||||
}, notInvalidated)) return
|
}, 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-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 })
|
const detailB = utils.buildDetail({ deviceId: balance.deviceId, cassette: balance.cassette })
|
||||||
return queries.addNotification(FIAT_BALANCE, message, detailB)
|
return queries.addNotification(FIAT_BALANCE, message, detailB)
|
||||||
})
|
})
|
||||||
|
|
@ -105,7 +116,7 @@ const cryptoBalancesNotify = (cryptoWarnings) => {
|
||||||
}, notInvalidated)) return
|
}, notInvalidated)) return
|
||||||
|
|
||||||
const fiat = utils.formatCurrency(balance.fiatBalance.balance, balance.fiatCode)
|
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 })
|
const detailB = utils.buildDetail({ cryptoCode: balance.cryptoCode, code: balance.code })
|
||||||
return queries.addNotification(CRYPTO_BALANCE, message, detailB)
|
return queries.addNotification(CRYPTO_BALANCE, message, detailB)
|
||||||
})
|
})
|
||||||
|
|
@ -113,8 +124,8 @@ const cryptoBalancesNotify = (cryptoWarnings) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const balancesNotify = (balances) => {
|
const balancesNotify = (balances) => {
|
||||||
const isCryptoCode = c => _.includes(c, ['HIGH_CRYPTO_BALANCE', 'LOW_CRYPTO_BALANCE'])
|
const isCryptoCode = c => _.includes(c, [HIGH_CRYPTO_BALANCE, LOW_CRYPTO_BALANCE])
|
||||||
const isFiatCode = c => _.includes(c, ['LOW_CASH_OUT', 'CASH_BOX_FULL'])
|
const isFiatCode = c => _.includes(c, [LOW_CASH_OUT, CASH_BOX_FULL, LOW_RECYCLER_STACKER])
|
||||||
const by = o =>
|
const by = o =>
|
||||||
isCryptoCode(o) ? 'crypto' :
|
isCryptoCode(o) ? 'crypto' :
|
||||||
isFiatCode(o) ? 'fiat' :
|
isFiatCode(o) ? 'fiat' :
|
||||||
|
|
|
||||||
229
lib/plugins.js
229
lib/plugins.js
|
|
@ -11,6 +11,7 @@ const logger = require('./logger')
|
||||||
const logs = require('./logs')
|
const logs = require('./logs')
|
||||||
const T = require('./time')
|
const T = require('./time')
|
||||||
const configManager = require('./new-config-manager')
|
const configManager = require('./new-config-manager')
|
||||||
|
const settingsLoader = require('./new-settings-loader')
|
||||||
const ticker = require('./ticker')
|
const ticker = require('./ticker')
|
||||||
const wallet = require('./wallet')
|
const wallet = require('./wallet')
|
||||||
const walletScoring = require('./wallet-scoring')
|
const walletScoring = require('./wallet-scoring')
|
||||||
|
|
@ -23,7 +24,13 @@ const commissionMath = require('./commission-math')
|
||||||
const loyalty = require('./loyalty')
|
const loyalty = require('./loyalty')
|
||||||
const transactionBatching = require('./tx-batching')
|
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')
|
const notifier = require('./notifier')
|
||||||
|
|
||||||
|
|
@ -237,17 +244,6 @@ function plugins (settings, deviceId) {
|
||||||
.then(([cassettes, recyclers]) => ({ cassettes: cassettes.cassettes, recyclers: recyclers.recyclers }))
|
.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) {
|
function mapCoinSettings (coinParams) {
|
||||||
const [ cryptoCode, cryptoNetwork ] = coinParams
|
const [ cryptoCode, cryptoNetwork ] = coinParams
|
||||||
const commissions = configManager.getCommissions(cryptoCode, deviceId, settings.config)
|
const commissions = configManager.getCommissions(cryptoCode, deviceId, settings.config)
|
||||||
|
|
@ -289,7 +285,7 @@ function plugins (settings, deviceId) {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
buildAvailableCassettes(),
|
buildAvailableCassettes(),
|
||||||
buildAvailableRecyclers(),
|
buildAvailableRecyclers(),
|
||||||
fetchCurrentConfigVersion(),
|
settingsLoader.fetchCurrentConfigVersion(),
|
||||||
millisecondsToMinutes(getTimezoneOffset(localeConfig.timezone)),
|
millisecondsToMinutes(getTimezoneOffset(localeConfig.timezone)),
|
||||||
loyalty.getNumberOfAvailablePromoCodes(),
|
loyalty.getNumberOfAvailablePromoCodes(),
|
||||||
Promise.all(supportsBatchingPromise),
|
Promise.all(supportsBatchingPromise),
|
||||||
|
|
@ -692,169 +688,73 @@ function plugins (settings, deviceId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkDeviceCashBalances (fiatCode, device) {
|
function checkDeviceCashBalances (fiatCode, device) {
|
||||||
const cashOutConfig = configManager.getCashOut(device.deviceId, settings.config)
|
const deviceId = device.deviceId
|
||||||
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 machineName = device.name
|
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',
|
code: 'CASH_BOX_FULL',
|
||||||
machineName,
|
machineName,
|
||||||
deviceId: device.deviceId,
|
deviceId,
|
||||||
notes: device.cashUnits.cashbox
|
notes: device.cashUnits.cashbox
|
||||||
}
|
}]
|
||||||
: null
|
: []
|
||||||
|
|
||||||
const cassette1Alert = device.numberOfCassettes >= 1 && isUnitLow(device.cashUnits.cassette1, getCashUnitCapacity(device.model, 'cassette'), notifications.fillingPercentageCassette1)
|
const cashOutConfig = configManager.getCashOut(deviceId, settings.config)
|
||||||
? {
|
const cashOutEnabled = cashOutConfig.active
|
||||||
code: 'LOW_CASH_OUT',
|
const isUnitLow = (have, max, limit) => ((have / max) * 100) < limit
|
||||||
cassette: 1,
|
|
||||||
machineName,
|
|
||||||
deviceId: device.deviceId,
|
|
||||||
notes: device.cashUnits.cassette1,
|
|
||||||
denomination: denomination1,
|
|
||||||
fiatCode
|
|
||||||
}
|
|
||||||
: null
|
|
||||||
|
|
||||||
const cassette2Alert = device.numberOfCassettes >= 2 && isUnitLow(device.cashUnits.cassette2, getCashUnitCapacity(device.model, 'cassette'), notifications.fillingPercentageCassette2)
|
if (!cashOutEnabled)
|
||||||
? {
|
return cashInAlerts
|
||||||
code: 'LOW_CASH_OUT',
|
|
||||||
cassette: 2,
|
|
||||||
machineName,
|
|
||||||
deviceId: device.deviceId,
|
|
||||||
notes: device.cashUnits.cassette2,
|
|
||||||
denomination: denomination2,
|
|
||||||
fiatCode
|
|
||||||
}
|
|
||||||
: null
|
|
||||||
|
|
||||||
const cassette3Alert = device.numberOfCassettes >= 3 && isUnitLow(device.cashUnits.cassette3, getCashUnitCapacity(device.model, 'cassette'), notifications.fillingPercentageCassette3)
|
const cassetteCapacity = getCashUnitCapacity(device.model, 'cassette')
|
||||||
? {
|
const cassetteAlerts = Array(Math.min(device.numberOfCassettes ?? 0, CASH_OUT_MAXIMUM_AMOUNT_OF_CASSETTES))
|
||||||
code: 'LOW_CASH_OUT',
|
.fill(null)
|
||||||
cassette: 3,
|
.flatMap((_elem, idx) => {
|
||||||
machineName,
|
const nth = idx + 1
|
||||||
deviceId: device.deviceId,
|
const cassetteField = `cassette${nth}`
|
||||||
notes: device.cashUnits.cassette3,
|
const notes = device.cashUnits[cassetteField]
|
||||||
denomination: denomination3,
|
const denomination = cashOutConfig[cassetteField]
|
||||||
fiatCode
|
|
||||||
}
|
|
||||||
: null
|
|
||||||
|
|
||||||
const cassette4Alert = device.numberOfCassettes >= 4 && isUnitLow(device.cashUnits.cassette4, getCashUnitCapacity(device.model, 'cassette'), notifications.fillingPercentageCassette4)
|
const limit = notifications[`fillingPercentageCassette${nth}`]
|
||||||
? {
|
return isUnitLow(notes, cassetteCapacity, limit) ?
|
||||||
code: 'LOW_CASH_OUT',
|
[{
|
||||||
cassette: 4,
|
code: 'LOW_CASH_OUT',
|
||||||
machineName,
|
cassette: nth,
|
||||||
deviceId: device.deviceId,
|
machineName,
|
||||||
notes: device.cashUnits.cassette4,
|
deviceId,
|
||||||
denomination: denomination4,
|
notes,
|
||||||
fiatCode
|
denomination,
|
||||||
}
|
fiatCode
|
||||||
: null
|
}] :
|
||||||
|
[]
|
||||||
|
})
|
||||||
|
|
||||||
const recycler1Alert = device.numberOfRecyclers >= 1 && isUnitLow(device.cashUnits.recycler1, getCashUnitCapacity(device.model, 'recycler'), notifications.fillingPercentageRecycler1)
|
const recyclerCapacity = getCashUnitCapacity(device.model, 'recycler')
|
||||||
? {
|
const recyclerAlerts = Array(Math.min(device.numberOfRecyclers ?? 0, CASH_OUT_MAXIMUM_AMOUNT_OF_RECYCLERS))
|
||||||
code: 'LOW_RECYCLER_STACKER',
|
.fill(null)
|
||||||
cassette: 4,
|
.flatMap((_elem, idx) => {
|
||||||
machineName,
|
const nth = idx + 1
|
||||||
deviceId: device.deviceId,
|
const recyclerField = `recycler${nth}`
|
||||||
notes: device.cashUnits.recycler1,
|
const notes = device.cashUnits[recyclerField]
|
||||||
denomination: denominationRecycler1,
|
const denomination = cashOutConfig[recyclerField]
|
||||||
fiatCode
|
|
||||||
}
|
|
||||||
: null
|
|
||||||
|
|
||||||
const recycler2Alert = device.numberOfRecyclers >= 2 && isUnitLow(device.cashUnits.recycler2, getCashUnitCapacity(device.model, 'recycler'), notifications.fillingPercentageRecycler2)
|
const limit = notifications[`fillingPercentageRecycler${nth}`]
|
||||||
? {
|
return isUnitLow(notes, recyclerCapacity, limit) ?
|
||||||
code: 'LOW_RECYCLER_STACKER',
|
[{
|
||||||
cassette: 4,
|
code: 'LOW_RECYCLER_STACKER',
|
||||||
machineName,
|
cassette: nth, // @see DETAIL_TEMPLATE in /lib/notifier/utils.js
|
||||||
deviceId: device.deviceId,
|
machineName,
|
||||||
notes: device.cashUnits.recycler2,
|
deviceId,
|
||||||
denomination: denominationRecycler2,
|
notes,
|
||||||
fiatCode
|
denomination,
|
||||||
}
|
fiatCode
|
||||||
: null
|
}] :
|
||||||
|
[]
|
||||||
|
})
|
||||||
|
|
||||||
const recycler3Alert = device.numberOfRecyclers >= 3 && isUnitLow(device.cashUnits.recycler3, getCashUnitCapacity(device.model, 'recycler'), notifications.fillingPercentageRecycler3)
|
return [].concat(cashInAlerts, cassetteAlerts, recyclerAlerts)
|
||||||
? {
|
|
||||||
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
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkCryptoBalances (fiatCode, devices) {
|
function checkCryptoBalances (fiatCode, devices) {
|
||||||
|
|
@ -1036,7 +936,6 @@ function plugins (settings, deviceId) {
|
||||||
sell,
|
sell,
|
||||||
getNotificationConfig,
|
getNotificationConfig,
|
||||||
notifyOperator,
|
notifyOperator,
|
||||||
fetchCurrentConfigVersion,
|
|
||||||
pruneMachinesHeartbeat,
|
pruneMachinesHeartbeat,
|
||||||
rateAddress,
|
rateAddress,
|
||||||
rateTransaction,
|
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,
|
sendCoinsBatch,
|
||||||
checkBlockchainStatus,
|
checkBlockchainStatus,
|
||||||
getTxHashesByAddress,
|
getTxHashesByAddress,
|
||||||
|
fetch,
|
||||||
SUPPORTS_BATCHING
|
SUPPORTS_BATCHING
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,10 @@ const SWEEP_QUEUE = new PQueue({
|
||||||
interval: 250,
|
interval: 250,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const SEND_QUEUE = new PQueue({
|
||||||
|
concurrency: 1,
|
||||||
|
})
|
||||||
|
|
||||||
const infuraCalls = {}
|
const infuraCalls = {}
|
||||||
|
|
||||||
const pify = _function => {
|
const pify = _function => {
|
||||||
|
|
@ -78,18 +82,20 @@ function sendCoins (account, tx, settings, operatorId, feeMultiplier) {
|
||||||
const { toAddress, cryptoAtoms, cryptoCode } = tx
|
const { toAddress, cryptoAtoms, cryptoCode } = tx
|
||||||
const isErc20Token = coins.utils.isErc20Token(cryptoCode)
|
const isErc20Token = coins.utils.isErc20Token(cryptoCode)
|
||||||
|
|
||||||
return (isErc20Token ? generateErc20Tx : generateTx)(toAddress, defaultWallet(account), cryptoAtoms, false, cryptoCode)
|
return SEND_QUEUE.add(() =>
|
||||||
.then(pify(web3.eth.sendSignedTransaction))
|
(isErc20Token ? generateErc20Tx : generateTx)(toAddress, defaultWallet(account), cryptoAtoms, false, cryptoCode)
|
||||||
.then(txid => {
|
.then(pify(web3.eth.sendSignedTransaction))
|
||||||
return pify(web3.eth.getTransaction)(txid)
|
.then(txid => {
|
||||||
.then(tx => {
|
return pify(web3.eth.getTransaction)(txid)
|
||||||
if (!tx) return { 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) {
|
function checkCryptoCode (cryptoCode) {
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@ const base = require('../geth/base')
|
||||||
const T = require('../../../time')
|
const T = require('../../../time')
|
||||||
const { BALANCE_FETCH_SPEED_MULTIPLIER } = require('../../../constants')
|
const { BALANCE_FETCH_SPEED_MULTIPLIER } = require('../../../constants')
|
||||||
|
|
||||||
const REGULAR_TX_POLLING = 5 * T.seconds
|
|
||||||
|
|
||||||
const NAME = 'infura'
|
const NAME = 'infura'
|
||||||
|
|
||||||
function run (account) {
|
function run (account) {
|
||||||
|
|
@ -27,21 +25,13 @@ function shouldGetStatus (tx) {
|
||||||
const timePassedSinceTx = Date.now() - new Date(tx.created)
|
const timePassedSinceTx = Date.now() - new Date(tx.created)
|
||||||
const timePassedSinceReq = Date.now() - new Date(txsCache.get(tx.id).lastReqTime)
|
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
|
if (timePassedSinceTx < 3 * T.minutes) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 10 * T.seconds
|
||||||
// Until first 5 minutes - 1/2 regular polling speed
|
if (timePassedSinceTx < 5 * T.minutes) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 20 * T.seconds
|
||||||
// Until first 10 minutes - 1/4 regular polling speed
|
if (timePassedSinceTx < 30 * T.minutes) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > T.minute
|
||||||
// Until first hour - 1/8 polling speed
|
if (timePassedSinceTx < 1 * T.hour) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 2 * T.minute
|
||||||
// Until first two hours - 1/12 polling speed
|
if (timePassedSinceTx < 3 * T.hours) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 5 * T.minute
|
||||||
// Until first four hours - 1/16 polling speed
|
if (timePassedSinceTx < 1 * T.day) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > T.hour
|
||||||
// Until first day - 1/24 polling speed
|
return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > T.hour
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override geth's getStatus function to allow for different polling timing
|
// 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(helmet())
|
||||||
app.use(nocache())
|
app.use(nocache())
|
||||||
app.use(express.json({ limit: '2mb' }))
|
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 /pair and /ca routes
|
||||||
app.use('/', pairingRoutes)
|
app.use('/', pairingRoutes)
|
||||||
|
|
|
||||||
|
|
@ -311,6 +311,7 @@ function getOrAddCustomerPhone (req, res, next) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOrAddCustomerEmail (req, res, next) {
|
function getOrAddCustomerEmail (req, res, next) {
|
||||||
|
const deviceId = req.deviceId
|
||||||
const customerData = req.body
|
const customerData = req.body
|
||||||
|
|
||||||
const pi = plugins(req.settings, req.deviceId)
|
const pi = plugins(req.settings, req.deviceId)
|
||||||
|
|
@ -318,7 +319,7 @@ function getOrAddCustomerEmail (req, res, next) {
|
||||||
|
|
||||||
return pi.getEmailCode(email)
|
return pi.getEmailCode(email)
|
||||||
.then(code => {
|
.then(code => {
|
||||||
return addOrUpdateCustomer(customerData, req.settings.config, true)
|
return addOrUpdateCustomer(customerData, deviceId, req.settings.config, true)
|
||||||
.then(customer => respond(req, res, { code, customer }))
|
.then(customer => respond(req, res, { code, customer }))
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ const nmd = require('nano-markdown')
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
|
|
||||||
const configManager = require('../new-config-manager')
|
const configManager = require('../new-config-manager')
|
||||||
const plugins = require('../plugins')
|
const settingsLoader = require('../new-settings-loader')
|
||||||
|
|
||||||
const createTerms = terms => (terms.active && terms.text) ? ({
|
const createTerms = terms => (terms.active && terms.text) ? ({
|
||||||
delay: terms.delay,
|
delay: terms.delay,
|
||||||
|
|
@ -18,15 +18,10 @@ const createTerms = terms => (terms.active && terms.text) ? ({
|
||||||
|
|
||||||
function getTermsConditions (req, res, next) {
|
function getTermsConditions (req, res, next) {
|
||||||
const deviceId = req.deviceId
|
const deviceId = req.deviceId
|
||||||
const settings = req.settings
|
const { config } = req.settings
|
||||||
|
const terms = configManager.getTermsConditions(config)
|
||||||
const terms = configManager.getTermsConditions(settings.config)
|
return settingsLoader.fetchCurrentConfigVersion()
|
||||||
|
.then(version => res.json({ terms: createTerms(terms), version }))
|
||||||
const pi = plugins(settings, deviceId)
|
|
||||||
|
|
||||||
return pi.fetchCurrentConfigVersion().then(version => {
|
|
||||||
return res.json({ terms: createTerms(terms), version })
|
|
||||||
})
|
|
||||||
.catch(next)
|
.catch(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,23 @@
|
||||||
const ph = require('./plugin-helper')
|
const ph = require('./plugin-helper')
|
||||||
const argv = require('minimist')(process.argv.slice(2))
|
const argv = require('minimist')(process.argv.slice(2))
|
||||||
const configManager = require('./new-config-manager')
|
|
||||||
|
|
||||||
function loadWalletScoring (settings, cryptoCode) {
|
// TODO - This function should be rolled back after UI is created for this feature
|
||||||
const pluginCode = argv.mockScoring ? 'mock-scoring' : 'scorechain'
|
function loadWalletScoring (settings) {
|
||||||
const wallet = cryptoCode ? ph.load(ph.WALLET, configManager.getWalletSettings(cryptoCode, settings.config).wallet) : null
|
if (argv.mockScoring) {
|
||||||
const plugin = ph.load(ph.WALLET_SCORING, pluginCode)
|
const mock = ph.load(ph.WALLET_SCORING, 'mock-scoring')
|
||||||
const account = settings.accounts[pluginCode]
|
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) {
|
function rateTransaction (settings, cryptoCode, address) {
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ const configManager = require('../lib/new-config-manager')
|
||||||
|
|
||||||
exports.up = function (next) {
|
exports.up = function (next) {
|
||||||
return db.tx(async t => {
|
return db.tx(async t => {
|
||||||
const settingsPromise = settingsLoader.loadLatest()
|
const settingsPromise = settingsLoader.loadLatestConfig()
|
||||||
const machinesPromise = t.any('SELECT device_id FROM devices')
|
const machinesPromise = t.any('SELECT device_id FROM devices')
|
||||||
const [{ config }, machines] = await Promise.all([settingsPromise, machinesPromise])
|
const [config, machines] = await Promise.all([settingsPromise, machinesPromise])
|
||||||
const cryptoCodes = configManager.getCryptosFromWalletNamespace(config)
|
const cryptoCodes = configManager.getCryptosFromWalletNamespace(config)
|
||||||
|
|
||||||
const deviceIds = _.map(_.get('device_id'))(machines)
|
const deviceIds = _.map(_.get('device_id'))(machines)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ const settingsLoader = require('../lib/new-settings-loader')
|
||||||
const configManager = require('../lib/new-config-manager')
|
const configManager = require('../lib/new-config-manager')
|
||||||
|
|
||||||
exports.up = async function (next) {
|
exports.up = async function (next) {
|
||||||
const { config } = await settingsLoader.loadLatest()
|
const config = await settingsLoader.loadLatestConfig()
|
||||||
const cryptoCodes = configManager.getCryptosFromWalletNamespace(config)
|
const cryptoCodes = configManager.getCryptosFromWalletNamespace(config)
|
||||||
_.forEach(cryptoCode => {
|
_.forEach(cryptoCode => {
|
||||||
const key = `wallets_${cryptoCode}_zeroConf`
|
const key = `wallets_${cryptoCode}_zeroConf`
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
const _ = require('lodash/fp')
|
const _ = require('lodash/fp')
|
||||||
const { migrationSaveConfig, loadLatest } = require('../lib/new-settings-loader')
|
const { migrationSaveConfig, loadLatestConfig } = require('../lib/new-settings-loader')
|
||||||
const CASSETTE_MAX_CAPACITY = 500
|
const CASSETTE_MAX_CAPACITY = 500
|
||||||
|
|
||||||
exports.up = function (next) {
|
exports.up = function (next) {
|
||||||
return loadLatest()
|
return loadLatestConfig()
|
||||||
.then(({ config }) => {
|
.then(config => {
|
||||||
const fiatBalance1 = config.notifications_fiatBalanceCassette1
|
const fiatBalance1 = config.notifications_fiatBalanceCassette1
|
||||||
const fiatBalance2 = config.notifications_fiatBalanceCassette2
|
const fiatBalance2 = config.notifications_fiatBalanceCassette2
|
||||||
const fiatBalance3 = config.notifications_fiatBalanceCassette3
|
const fiatBalance3 = config.notifications_fiatBalanceCassette3
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ const _ = require('lodash/fp')
|
||||||
const settingsLoader = require('../lib/new-settings-loader')
|
const settingsLoader = require('../lib/new-settings-loader')
|
||||||
|
|
||||||
exports.up = function (next) {
|
exports.up = function (next) {
|
||||||
settingsLoader.loadLatest()
|
settingsLoader.loadLatestConfig()
|
||||||
.then(({ config }) => {
|
.then(config => {
|
||||||
if (!_.isEmpty(config))
|
if (!_.isEmpty(config))
|
||||||
config.locale_timezone = '0:0'
|
config.locale_timezone = '0:0'
|
||||||
return settingsLoader.migrationSaveConfig(config)
|
return settingsLoader.migrationSaveConfig(config)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
const db = require('./db')
|
const db = require('./db')
|
||||||
const { migrationSaveConfig, loadLatest } = require('../lib/new-settings-loader')
|
const { migrationSaveConfig } = require('../lib/new-settings-loader')
|
||||||
|
|
||||||
exports.up = function (next) {
|
exports.up = function (next) {
|
||||||
const sql = [
|
const sql = [
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
const { migrationSaveConfig, loadLatest } = require('../lib/new-settings-loader')
|
const { migrationSaveConfig, loadLatestConfig } = require('../lib/new-settings-loader')
|
||||||
const { getCryptosFromWalletNamespace } = require('../lib/new-config-manager.js')
|
const { getCryptosFromWalletNamespace } = require('../lib/new-config-manager.js')
|
||||||
const { utils: coinUtils } = require('@lamassu/coins')
|
const { utils: coinUtils } = require('@lamassu/coins')
|
||||||
const _ = require('lodash/fp')
|
const _ = require('lodash/fp')
|
||||||
|
|
||||||
exports.up = function (next) {
|
exports.up = function (next) {
|
||||||
loadLatest()
|
loadLatestConfig()
|
||||||
.then(settings => {
|
.then(config => {
|
||||||
const newSettings = {}
|
const newSettings = {}
|
||||||
const activeCryptos = getCryptosFromWalletNamespace(settings.config)
|
const activeCryptos = getCryptosFromWalletNamespace(config)
|
||||||
if (!activeCryptos.length) return Promise.resolve()
|
if (!activeCryptos.length) return Promise.resolve()
|
||||||
_.map(crypto => {
|
_.map(crypto => {
|
||||||
const defaultUnit = _.head(_.keys(coinUtils.getCryptoCurrency(crypto).units))
|
const defaultUnit = _.head(_.keys(coinUtils.getCryptoCurrency(crypto).units))
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
var db = require('./db')
|
var db = require('./db')
|
||||||
const _ = require('lodash/fp')
|
const _ = require('lodash/fp')
|
||||||
const { migrationSaveConfig, loadLatest } = require('../lib/new-settings-loader')
|
const { migrationSaveConfig, loadLatestConfig } = require('../lib/new-settings-loader')
|
||||||
const { getMachineIds } = require('../lib/machine-loader')
|
const { getMachineIds } = require('../lib/machine-loader')
|
||||||
|
|
||||||
exports.up = function (next) {
|
exports.up = function (next) {
|
||||||
|
|
@ -25,16 +25,16 @@ exports.up = function (next) {
|
||||||
ADD COLUMN denomination_4 INTEGER`
|
ADD COLUMN denomination_4 INTEGER`
|
||||||
]
|
]
|
||||||
|
|
||||||
return Promise.all([loadLatest(), getMachineIds()])
|
return Promise.all([loadLatestConfig(), getMachineIds()])
|
||||||
.then(([config, machineIds]) => {
|
.then(([config, machineIds]) => {
|
||||||
const newConfig = _.reduce((acc, value) => {
|
const newConfig = _.reduce((acc, value) => {
|
||||||
const deviceId = value.device_id
|
const deviceId = value.device_id
|
||||||
if (_.includes(`cashOut_${deviceId}_top`, _.keys(config.config))) {
|
if (_.includes(`cashOut_${deviceId}_top`, _.keys(config))) {
|
||||||
acc[`cashOut_${deviceId}_cassette1`] = config.config[`cashOut_${deviceId}_top`]
|
acc[`cashOut_${deviceId}_cassette1`] = config[`cashOut_${deviceId}_top`]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_.includes(`cashOut_${deviceId}_bottom`, _.keys(config.config))) {
|
if (_.includes(`cashOut_${deviceId}_bottom`, _.keys(config))) {
|
||||||
acc[`cashOut_${deviceId}_cassette2`] = config.config[`cashOut_${deviceId}_bottom`]
|
acc[`cashOut_${deviceId}_cassette2`] = config[`cashOut_${deviceId}_bottom`]
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc
|
return acc
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
const uuid = require('uuid')
|
const uuid = require('uuid')
|
||||||
const { saveConfig, loadLatest } = require('../lib/new-settings-loader')
|
const { saveConfig } = require('../lib/new-settings-loader')
|
||||||
|
|
||||||
exports.up = function (next) {
|
exports.up = function (next) {
|
||||||
const newConfig = {}
|
const newConfig = {
|
||||||
return loadLatest()
|
wallets_advanced_feeMultiplier: '1',
|
||||||
.then(config => {
|
wallets_advanced_cryptoUnits: 'full',
|
||||||
newConfig[`wallets_advanced_feeMultiplier`] = '1'
|
wallets_advanced_allowTransactionBatching: false,
|
||||||
newConfig[`wallets_advanced_cryptoUnits`] = 'full'
|
wallets_advanced_id: uuid.v4(),
|
||||||
newConfig[`wallets_advanced_allowTransactionBatching`] = false
|
}
|
||||||
newConfig[`wallets_advanced_id`] = uuid.v4()
|
return saveConfig(newConfig)
|
||||||
return saveConfig(newConfig)
|
|
||||||
})
|
|
||||||
.then(next)
|
.then(next)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
return next(err)
|
return next(err)
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
const _ = require('lodash/fp')
|
const _ = require('lodash/fp')
|
||||||
const { saveConfig, loadLatest } = require('../lib/new-settings-loader')
|
const { saveConfig, loadLatestConfig } = require('../lib/new-settings-loader')
|
||||||
|
|
||||||
exports.up = function (next) {
|
exports.up = function (next) {
|
||||||
const newConfig = {}
|
return loadLatestConfig()
|
||||||
return loadLatest()
|
|
||||||
.then(config => {
|
.then(config => {
|
||||||
if (!_.isNil(config.config.locale_timezone)) return
|
if (!_.isNil(config.locale_timezone)) return
|
||||||
newConfig[`locale_timezone`] = 'GMT'
|
const newConfig = { locale_timezone: 'GMT' }
|
||||||
return saveConfig(newConfig)
|
return saveConfig(newConfig)
|
||||||
})
|
})
|
||||||
.then(next)
|
.then(next)
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
const { saveConfig, loadLatest } = require('../lib/new-settings-loader')
|
const { saveConfig, loadLatestConfig } = require('../lib/new-settings-loader')
|
||||||
|
|
||||||
exports.up = function (next) {
|
exports.up = function (next) {
|
||||||
const newConfig = {}
|
return loadLatestConfig()
|
||||||
return loadLatest()
|
|
||||||
.then(config => {
|
.then(config => {
|
||||||
if (config.config.locale_timezone === "0:0") {
|
if (config.locale_timezone === "0:0") {
|
||||||
newConfig[`locale_timezone`] = 'GMT'
|
const newConfig = { locale_timezone: 'GMT' }
|
||||||
return saveConfig(newConfig)
|
return saveConfig(newConfig)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
const { removeFromConfig, loadLatest } = require('../lib/new-settings-loader')
|
const { removeFromConfig, loadLatestConfig } = require('../lib/new-settings-loader')
|
||||||
const { getCryptosFromWalletNamespace } = require('../lib/new-config-manager.js')
|
const { getCryptosFromWalletNamespace } = require('../lib/new-config-manager.js')
|
||||||
const _ = require('lodash/fp')
|
const _ = require('lodash/fp')
|
||||||
|
|
||||||
exports.up = function (next) {
|
exports.up = function (next) {
|
||||||
loadLatest()
|
loadLatestConfig()
|
||||||
.then(settings => {
|
.then(config => {
|
||||||
const configuredCryptos = getCryptosFromWalletNamespace(settings.config)
|
const configuredCryptos = getCryptosFromWalletNamespace(config)
|
||||||
if (!configuredCryptos.length) return Promise.resolve()
|
if (!configuredCryptos.length) return Promise.resolve()
|
||||||
|
|
||||||
return removeFromConfig(_.map(it => `wallets_${it}_cryptoUnits`, configuredCryptos))
|
return removeFromConfig(_.map(it => `wallets_${it}_cryptoUnits`, configuredCryptos))
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
const _ = require('lodash/fp')
|
const _ = require('lodash/fp')
|
||||||
|
|
||||||
const { saveConfig, loadLatest } = require('../lib/new-settings-loader')
|
const { saveConfig, loadLatestConfig } = require('../lib/new-settings-loader')
|
||||||
|
|
||||||
exports.up = function (next) {
|
exports.up = function (next) {
|
||||||
const newConfig = {}
|
return loadLatestConfig()
|
||||||
return loadLatest()
|
|
||||||
.then(config => {
|
.then(config => {
|
||||||
if (!_.isNil(config.config.wallets_ETH_zeroConfLimit) && config.config.wallets_ETH_zeroConfLimit !== 0) {
|
if (!_.isNil(config.wallets_ETH_zeroConfLimit) && config.wallets_ETH_zeroConfLimit !== 0) {
|
||||||
newConfig[`wallets_ETH_zeroConfLimit`] = 0
|
const newConfig = { wallets_ETH_zeroConfLimit: 0 }
|
||||||
return saveConfig(newConfig)
|
return saveConfig(newConfig)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ module.exports = {migrateNames}
|
||||||
function migrateNames () {
|
function migrateNames () {
|
||||||
const cs = new pgp.helpers.ColumnSet(['?device_id', 'name'], {table: 'devices'})
|
const cs = new pgp.helpers.ColumnSet(['?device_id', 'name'], {table: 'devices'})
|
||||||
|
|
||||||
return settingsLoader.loadLatest(false)
|
return settingsLoader.loadLatestConfig(false)
|
||||||
.then(r => machineLoader.getMachineNames(r.config))
|
.then(config => machineLoader.getMachineNames(config))
|
||||||
.then(_.map(r => ({device_id: r.deviceId, name: r.name})))
|
.then(_.map(r => ({device_id: r.deviceId, name: r.name})))
|
||||||
.then(data => pgp.helpers.update(data, cs) + ' WHERE t.device_id=v.device_id')
|
.then(data => pgp.helpers.update(data, cs) + ' WHERE t.device_id=v.device_id')
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ const BooleanPropertiesTable = memo(
|
||||||
elements.map(it => [it.name, data[it.name]?.toString() ?? null])
|
elements.map(it => [it.name, data[it.name]?.toString() ?? null])
|
||||||
)
|
)
|
||||||
|
|
||||||
const schemaValidation = R.fromPairs(
|
const validationSchema = R.fromPairs(
|
||||||
elements.map(it => [it.name, Yup.boolean().required()])
|
elements.map(it => [it.name, Yup.boolean().required()])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -56,7 +56,7 @@ const BooleanPropertiesTable = memo(
|
||||||
enableReinitialize
|
enableReinitialize
|
||||||
onSubmit={innerSave}
|
onSubmit={innerSave}
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
schemaValidation={schemaValidation}>
|
validationSchema={validationSchema}>
|
||||||
{({ resetForm }) => {
|
{({ resetForm }) => {
|
||||||
return (
|
return (
|
||||||
<Form>
|
<Form>
|
||||||
|
|
|
||||||
|
|
@ -126,9 +126,9 @@ const validationSchema = Yup.object().shape({
|
||||||
'unique-name',
|
'unique-name',
|
||||||
'Machine name is already in use.',
|
'Machine name is already in use.',
|
||||||
(value, context) =>
|
(value, context) =>
|
||||||
!R.any(
|
!R.includes(
|
||||||
it => R.equals(R.toLower(it), R.toLower(value)),
|
R.toLower(value),
|
||||||
context.options.context.machineNames
|
R.map(R.toLower, context.options.context.machineNames)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
import { useMutation } from '@apollo/react-hooks'
|
|
||||||
import { Box } from '@material-ui/core'
|
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
|
||||||
import gql from 'graphql-tag'
|
|
||||||
import React, { useState } from 'react'
|
|
||||||
|
|
||||||
import Title from 'src/components/Title'
|
|
||||||
import { Button } from 'src/components/buttons'
|
|
||||||
|
|
||||||
const styles = {
|
|
||||||
button: {
|
|
||||||
marginBottom: 10
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const useStyles = makeStyles(styles)
|
|
||||||
|
|
||||||
const RESET = gql`
|
|
||||||
mutation Reset($schemaVersion: Int) {
|
|
||||||
resetConfig(schemaVersion: $schemaVersion)
|
|
||||||
resetAccounts(schemaVersion: $schemaVersion)
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const MIGRATE = gql`
|
|
||||||
mutation Migrate {
|
|
||||||
migrateConfigAndAccounts
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const OLD_SCHEMA_VERSION = 1
|
|
||||||
const NEW_SCHEMA_VERSION = 2
|
|
||||||
|
|
||||||
const ConfigMigration = () => {
|
|
||||||
const [loading, setLoading] = useState(false)
|
|
||||||
const [reset] = useMutation(RESET, {
|
|
||||||
onCompleted: () => setLoading(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
const [migrate] = useMutation(MIGRATE, {
|
|
||||||
onCompleted: () => setLoading(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
const classes = useStyles()
|
|
||||||
|
|
||||||
const innerReset = schemaVersion => {
|
|
||||||
setLoading(true)
|
|
||||||
reset({ variables: { schemaVersion } })
|
|
||||||
}
|
|
||||||
|
|
||||||
const innerMigrate = () => {
|
|
||||||
setLoading(true)
|
|
||||||
migrate()
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Title>Config Migration</Title>
|
|
||||||
<Box display="flex" alignItems="center" flexDirection="column">
|
|
||||||
<Button
|
|
||||||
className={classes.button}
|
|
||||||
disabled={loading}
|
|
||||||
onClick={() => innerReset(OLD_SCHEMA_VERSION)}>
|
|
||||||
Reset old admin
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
className={classes.button}
|
|
||||||
disabled={loading}
|
|
||||||
onClick={() => innerReset(NEW_SCHEMA_VERSION)}>
|
|
||||||
Reset new admin
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
className={classes.button}
|
|
||||||
disabled={loading}
|
|
||||||
onClick={() => innerMigrate()}>
|
|
||||||
Migrate
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ConfigMigration
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import Grid from '@material-ui/core/Grid'
|
import Grid from '@material-ui/core/Grid'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import { parse, format } from 'date-fns/fp'
|
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import { useState, React } from 'react'
|
import { useState, React } from 'react'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
|
|
@ -30,6 +29,7 @@ import {
|
||||||
customerDataElements,
|
customerDataElements,
|
||||||
customerDataSchemas,
|
customerDataSchemas,
|
||||||
formatDates,
|
formatDates,
|
||||||
|
tryFormatDate,
|
||||||
getFormattedPhone
|
getFormattedPhone
|
||||||
} from './helper.js'
|
} from './helper.js'
|
||||||
|
|
||||||
|
|
@ -113,18 +113,10 @@ const CustomerData = ({
|
||||||
firstName: R.path(['firstName'])(idData) ?? '',
|
firstName: R.path(['firstName'])(idData) ?? '',
|
||||||
lastName: R.path(['lastName'])(idData) ?? '',
|
lastName: R.path(['lastName'])(idData) ?? '',
|
||||||
documentNumber: R.path(['documentNumber'])(idData) ?? '',
|
documentNumber: R.path(['documentNumber'])(idData) ?? '',
|
||||||
dateOfBirth:
|
dateOfBirth: tryFormatDate(rawDob),
|
||||||
(rawDob &&
|
|
||||||
format('yyyy-MM-dd')(parse(new Date(), 'yyyyMMdd', rawDob))) ??
|
|
||||||
'',
|
|
||||||
gender: R.path(['gender'])(idData) ?? '',
|
gender: R.path(['gender'])(idData) ?? '',
|
||||||
country: R.path(['country'])(idData) ?? '',
|
country: R.path(['country'])(idData) ?? '',
|
||||||
expirationDate:
|
expirationDate: tryFormatDate(rawExpirationDate)
|
||||||
(rawExpirationDate &&
|
|
||||||
format('yyyy-MM-dd')(
|
|
||||||
parse(new Date(), 'yyyyMMdd', rawExpirationDate)
|
|
||||||
)) ??
|
|
||||||
''
|
|
||||||
},
|
},
|
||||||
usSsn: {
|
usSsn: {
|
||||||
usSsn: customer.usSsn ?? ''
|
usSsn: customer.usSsn ?? ''
|
||||||
|
|
|
||||||
|
|
@ -528,13 +528,22 @@ const requirementElements = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tryFormatDate = rawDate => {
|
||||||
|
try {
|
||||||
|
return (
|
||||||
|
(rawDate &&
|
||||||
|
format('yyyy-MM-dd')(parse(new Date(), 'yyyyMMdd', rawDate))) ??
|
||||||
|
''
|
||||||
|
)
|
||||||
|
} catch (err) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const formatDates = values => {
|
const formatDates = values => {
|
||||||
R.map(
|
R.forEach(elem => {
|
||||||
elem =>
|
values[elem] = tryFormatDate(values[elem])
|
||||||
(values[elem] = format('yyyyMMdd')(
|
})(['dateOfBirth', 'expirationDate'])
|
||||||
parse(new Date(), 'yyyy-MM-dd', values[elem])
|
|
||||||
))
|
|
||||||
)(['dateOfBirth', 'expirationDate'])
|
|
||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -579,6 +588,7 @@ export {
|
||||||
customerDataElements,
|
customerDataElements,
|
||||||
customerDataSchemas,
|
customerDataSchemas,
|
||||||
formatDates,
|
formatDates,
|
||||||
|
tryFormatDate,
|
||||||
REQUIREMENT,
|
REQUIREMENT,
|
||||||
CUSTOM,
|
CUSTOM,
|
||||||
ID_CARD_DATA
|
ID_CARD_DATA
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ const FiatBalanceOverrides = ({ config, section }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialValues = {
|
const initialValues = {
|
||||||
[MACHINE_KEY]: null,
|
[MACHINE_KEY]: '',
|
||||||
[CASHBOX_KEY]: '',
|
[CASHBOX_KEY]: '',
|
||||||
[CASSETTE_1_KEY]: '',
|
[CASSETTE_1_KEY]: '',
|
||||||
[CASSETTE_2_KEY]: '',
|
[CASSETTE_2_KEY]: '',
|
||||||
|
|
|
||||||
64
new-lamassu-admin/src/pages/Services/schemas/elliptic.js
Normal file
64
new-lamassu-admin/src/pages/Services/schemas/elliptic.js
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
import CheckboxFormik from 'src/components/inputs/formik/Checkbox'
|
||||||
|
import NumberInputFormik from 'src/components/inputs/formik/NumberInput'
|
||||||
|
import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
|
||||||
|
|
||||||
|
import { secretTest, leadingZerosTest } from './helper'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
code: 'elliptic',
|
||||||
|
name: 'Elliptic',
|
||||||
|
title: 'Elliptic (Scoring)',
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
code: 'apiKey',
|
||||||
|
display: 'API Key',
|
||||||
|
component: SecretInputFormik
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: 'apiSecret',
|
||||||
|
display: 'API Secret',
|
||||||
|
component: SecretInputFormik
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: 'scoreThreshold',
|
||||||
|
display: 'Score threshold',
|
||||||
|
component: NumberInputFormik,
|
||||||
|
face: true,
|
||||||
|
long: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: 'enabled',
|
||||||
|
component: CheckboxFormik,
|
||||||
|
settings: {
|
||||||
|
enabled: true,
|
||||||
|
disabledMessage: 'This plugin is disabled',
|
||||||
|
label: 'Enabled',
|
||||||
|
requirement: null,
|
||||||
|
rightSideLabel: true
|
||||||
|
},
|
||||||
|
face: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
getValidationSchema: account => {
|
||||||
|
return Yup.object().shape({
|
||||||
|
apiKey: Yup.string('The API key must be a string')
|
||||||
|
.max(100, 'Too long')
|
||||||
|
.test(secretTest(account?.apiKey, 'API key')),
|
||||||
|
apiSecret: Yup.string('The API secret must be a string')
|
||||||
|
.max(100, 'Too long')
|
||||||
|
.test(secretTest(account?.apiKey, 'API key')),
|
||||||
|
scoreThreshold: Yup.number('The score threshold must be a number')
|
||||||
|
.required('A score threshold is required')
|
||||||
|
.min(1, 'The score threshold must be between 1 and 100')
|
||||||
|
.max(100, 'The score threshold must be between 1 and 100')
|
||||||
|
.integer('The score threshold must be an integer')
|
||||||
|
.test(
|
||||||
|
'no-leading-zeros',
|
||||||
|
'The score threshold must not have leading zeros',
|
||||||
|
leadingZerosTest
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ import bitgo from './bitgo'
|
||||||
import bitstamp from './bitstamp'
|
import bitstamp from './bitstamp'
|
||||||
import blockcypher from './blockcypher'
|
import blockcypher from './blockcypher'
|
||||||
import cex from './cex'
|
import cex from './cex'
|
||||||
|
import elliptic from './elliptic'
|
||||||
import galoy from './galoy'
|
import galoy from './galoy'
|
||||||
import inforu from './inforu'
|
import inforu from './inforu'
|
||||||
import infura from './infura'
|
import infura from './infura'
|
||||||
|
|
@ -23,6 +24,7 @@ export default {
|
||||||
[galoy.code]: galoy,
|
[galoy.code]: galoy,
|
||||||
[bitstamp.code]: bitstamp,
|
[bitstamp.code]: bitstamp,
|
||||||
[blockcypher.code]: blockcypher,
|
[blockcypher.code]: blockcypher,
|
||||||
|
[elliptic.code]: elliptic,
|
||||||
[inforu.code]: inforu,
|
[inforu.code]: inforu,
|
||||||
[infura.code]: infura,
|
[infura.code]: infura,
|
||||||
[itbit.code]: itbit,
|
[itbit.code]: itbit,
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,9 @@ const ChooseType = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const validationSchema = Yup.object().shape({
|
const validationSchema = Yup.object().shape({
|
||||||
inputType: Yup.string().required()
|
inputType: Yup.string()
|
||||||
|
.label('Input type')
|
||||||
|
.required()
|
||||||
})
|
})
|
||||||
|
|
||||||
const defaultValues = {
|
const defaultValues = {
|
||||||
|
|
|
||||||
|
|
@ -29,14 +29,14 @@ const NameOfRequirement = () => {
|
||||||
const validationSchema = existingRequirements =>
|
const validationSchema = existingRequirements =>
|
||||||
Yup.object().shape({
|
Yup.object().shape({
|
||||||
requirementName: Yup.string()
|
requirementName: Yup.string()
|
||||||
.required('A requirement name is required')
|
.required('Name is required')
|
||||||
.test(
|
.test(
|
||||||
'unique-name',
|
'unique-name',
|
||||||
'A custom information requirement with that name already exists',
|
'A custom information requirement with that name already exists',
|
||||||
(value, _context) =>
|
(value, _context) =>
|
||||||
!R.any(
|
!R.includes(
|
||||||
it => R.equals(R.toLower(it), R.toLower(value)),
|
R.toLower(R.defaultTo('', value)),
|
||||||
R.map(it => it.customRequest.name, existingRequirements)
|
R.map(it => R.toLower(it.customRequest.name), existingRequirements)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,12 @@ const Screen1Information = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const validationSchema = Yup.object().shape({
|
const validationSchema = Yup.object().shape({
|
||||||
screen1Title: Yup.string().required(),
|
screen1Title: Yup.string()
|
||||||
screen1Text: Yup.string().required()
|
.label('Screen title')
|
||||||
|
.required(),
|
||||||
|
screen1Text: Yup.string()
|
||||||
|
.label('Screen text')
|
||||||
|
.required()
|
||||||
})
|
})
|
||||||
|
|
||||||
const defaultValues = {
|
const defaultValues = {
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,12 @@ const ScreenInformation = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const validationSchema = Yup.object().shape({
|
const validationSchema = Yup.object().shape({
|
||||||
screen2Title: Yup.string().required(),
|
screen2Title: Yup.string()
|
||||||
screen2Text: Yup.string().required()
|
.label('Screen title')
|
||||||
|
.required(),
|
||||||
|
screen2Text: Yup.string()
|
||||||
|
.label('Screen text')
|
||||||
|
.required()
|
||||||
})
|
})
|
||||||
|
|
||||||
const defaultValues = {
|
const defaultValues = {
|
||||||
|
|
|
||||||
|
|
@ -40,28 +40,38 @@ const validationSchema = Yup.lazy(values => {
|
||||||
switch (values.inputType) {
|
switch (values.inputType) {
|
||||||
case 'numerical':
|
case 'numerical':
|
||||||
return Yup.object({
|
return Yup.object({
|
||||||
constraintType: Yup.string().required(),
|
constraintType: Yup.string()
|
||||||
|
.label('Constraint type')
|
||||||
|
.required(),
|
||||||
inputLength: Yup.number().when('constraintType', {
|
inputLength: Yup.number().when('constraintType', {
|
||||||
is: 'length',
|
is: 'length',
|
||||||
then: Yup.number()
|
then: Yup.number()
|
||||||
.min(0)
|
.min(0)
|
||||||
.required(),
|
.required('The number of digits is required'),
|
||||||
else: Yup.mixed().notRequired()
|
else: Yup.mixed().notRequired()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
case 'text':
|
case 'text':
|
||||||
return Yup.object({
|
return Yup.object({
|
||||||
constraintType: Yup.string().required(),
|
constraintType: Yup.string()
|
||||||
inputLabel1: Yup.string().required(),
|
.label('Constraint type')
|
||||||
|
.required(),
|
||||||
|
inputLabel1: Yup.string()
|
||||||
|
.label('Text entry label')
|
||||||
|
.required(),
|
||||||
inputLabel2: Yup.string().when('constraintType', {
|
inputLabel2: Yup.string().when('constraintType', {
|
||||||
is: 'spaceSeparation',
|
is: 'spaceSeparation',
|
||||||
then: Yup.string().required(),
|
then: Yup.string()
|
||||||
|
.label('Second word label')
|
||||||
|
.required(),
|
||||||
else: Yup.mixed().notRequired()
|
else: Yup.mixed().notRequired()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
case 'choiceList':
|
case 'choiceList':
|
||||||
return Yup.object({
|
return Yup.object({
|
||||||
constraintType: Yup.string().required(),
|
constraintType: Yup.string()
|
||||||
|
.label('Constraint type')
|
||||||
|
.required(),
|
||||||
listChoices: Yup.array().test(
|
listChoices: Yup.array().test(
|
||||||
'has-2-or-more',
|
'has-2-or-more',
|
||||||
'Choice list needs to have two or more non empty fields',
|
'Choice list needs to have two or more non empty fields',
|
||||||
|
|
|
||||||
|
|
@ -53,39 +53,26 @@ const styles = {
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const getStep = (step, existingRequirements) => {
|
const getStep = (step, existingRequirements) =>
|
||||||
switch (step) {
|
[
|
||||||
case 1:
|
{
|
||||||
return {
|
validationSchema: nameOfReqSchema(existingRequirements),
|
||||||
schema: nameOfReqSchema(existingRequirements),
|
Component: NameOfRequirement
|
||||||
Component: NameOfRequirement
|
},
|
||||||
}
|
{
|
||||||
case 2:
|
validationSchema: screen1InfoSchema,
|
||||||
return {
|
Component: Screen1Information
|
||||||
schema: screen1InfoSchema,
|
},
|
||||||
Component: Screen1Information
|
{ validationSchema: chooseTypeSchema, Component: ChooseType },
|
||||||
}
|
{
|
||||||
case 3:
|
validationSchema: screen2InfoSchema,
|
||||||
return { schema: chooseTypeSchema, Component: ChooseType }
|
Component: Screen2Information
|
||||||
case 4:
|
},
|
||||||
return {
|
{
|
||||||
schema: screen2InfoSchema,
|
validationSchema: typeFieldsValidationSchema,
|
||||||
Component: Screen2Information
|
Component: TypeFields
|
||||||
}
|
}
|
||||||
case 5:
|
][step - 1]
|
||||||
return {
|
|
||||||
schema: typeFieldsValidationSchema,
|
|
||||||
Component: TypeFields
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return {
|
|
||||||
schema: {},
|
|
||||||
Component: () => {
|
|
||||||
return <h1>Default component step</h1>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const nonEmptyStr = obj => obj.text && obj.text.length
|
const nonEmptyStr = obj => obj.text && obj.text.length
|
||||||
|
|
||||||
|
|
@ -139,10 +126,9 @@ const formatValues = (values, isEditing) => {
|
||||||
return resObj
|
return resObj
|
||||||
}
|
}
|
||||||
|
|
||||||
const makeEditingValues = it => {
|
const makeEditingValues = ({ customRequest, id }) => {
|
||||||
const { customRequest } = it
|
|
||||||
return {
|
return {
|
||||||
id: it.id,
|
id,
|
||||||
requirementName: customRequest.name,
|
requirementName: customRequest.name,
|
||||||
screen1Title: customRequest.screen1.title,
|
screen1Title: customRequest.screen1.title,
|
||||||
screen1Text: customRequest.screen1.text,
|
screen1Text: customRequest.screen1.text,
|
||||||
|
|
@ -157,10 +143,7 @@ const makeEditingValues = it => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const chooseNotNull = (a, b) => {
|
const chooseNotNull = (a, b) => (R.isNil(b) ? a : b)
|
||||||
if (!R.isNil(b)) return b
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
const Wizard = ({
|
const Wizard = ({
|
||||||
onClose,
|
onClose,
|
||||||
|
|
@ -174,11 +157,19 @@ const Wizard = ({
|
||||||
const isEditing = !R.isNil(toBeEdited)
|
const isEditing = !R.isNil(toBeEdited)
|
||||||
const [step, setStep] = useState(isEditing ? 1 : 0)
|
const [step, setStep] = useState(isEditing ? 1 : 0)
|
||||||
|
|
||||||
|
const defaultValues = {
|
||||||
|
...nameOfReqDefaults,
|
||||||
|
...screen1InfoDefaults,
|
||||||
|
...screen2InfoDefaults,
|
||||||
|
...chooseTypeDefaults,
|
||||||
|
...typeFieldsDefaults
|
||||||
|
}
|
||||||
|
|
||||||
// If we're editing, filter out the requirement being edited so that validation schemas don't enter in circular conflicts
|
// If we're editing, filter out the requirement being edited so that validation schemas don't enter in circular conflicts
|
||||||
const _existingRequirements = isEditing
|
existingRequirements = isEditing
|
||||||
? R.filter(it => it.id !== toBeEdited.id, existingRequirements)
|
? R.filter(it => it.id !== toBeEdited.id, existingRequirements)
|
||||||
: existingRequirements
|
: existingRequirements
|
||||||
const stepOptions = getStep(step, _existingRequirements)
|
const stepOptions = getStep(step, existingRequirements)
|
||||||
const isLastStep = step === LAST_STEP
|
const isLastStep = step === LAST_STEP
|
||||||
|
|
||||||
const onContinue = (values, actions) => {
|
const onContinue = (values, actions) => {
|
||||||
|
|
@ -202,6 +193,7 @@ const Wizard = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const editingValues = isEditing ? makeEditingValues(toBeEdited) : {}
|
const editingValues = isEditing ? makeEditingValues(toBeEdited) : {}
|
||||||
|
const initialValues = R.mergeWith(chooseNotNull, defaultValues, editingValues)
|
||||||
const wizardTitle = isEditing
|
const wizardTitle = isEditing
|
||||||
? 'Editing custom requirement'
|
? 'Editing custom requirement'
|
||||||
: 'New custom requirement'
|
: 'New custom requirement'
|
||||||
|
|
@ -226,18 +218,8 @@ const Wizard = ({
|
||||||
validateOnChange={false}
|
validateOnChange={false}
|
||||||
enableReinitialize={true}
|
enableReinitialize={true}
|
||||||
onSubmit={onContinue}
|
onSubmit={onContinue}
|
||||||
initialValues={R.mergeWith(
|
initialValues={initialValues}
|
||||||
chooseNotNull,
|
validationSchema={stepOptions.validationSchema}>
|
||||||
{
|
|
||||||
...nameOfReqDefaults,
|
|
||||||
...screen1InfoDefaults,
|
|
||||||
...screen2InfoDefaults,
|
|
||||||
...chooseTypeDefaults,
|
|
||||||
...typeFieldsDefaults
|
|
||||||
},
|
|
||||||
editingValues
|
|
||||||
)}
|
|
||||||
validationSchema={stepOptions.schema}>
|
|
||||||
{({ errors }) => (
|
{({ errors }) => (
|
||||||
<Form className={classes.form} id={'custom-requirement-form'}>
|
<Form className={classes.form} id={'custom-requirement-form'}>
|
||||||
<stepOptions.Component />
|
<stepOptions.Component />
|
||||||
|
|
|
||||||
|
|
@ -73,11 +73,12 @@ const Triggers = () => {
|
||||||
|
|
||||||
const [twilioSetupPopup, setTwilioSetupPopup] = useState(false)
|
const [twilioSetupPopup, setTwilioSetupPopup] = useState(false)
|
||||||
|
|
||||||
const customInfoRequests =
|
const enabledCustomInfoRequests = R.pipe(
|
||||||
R.path(['customInfoRequests'])(customInfoReqData) ?? []
|
R.path(['customInfoRequests']),
|
||||||
const enabledCustomInfoRequests = R.filter(R.propEq('enabled', true))(
|
R.defaultTo([]),
|
||||||
customInfoRequests
|
R.filter(R.propEq('enabled', true))
|
||||||
)
|
)(customInfoReqData)
|
||||||
|
|
||||||
const emailAuth =
|
const emailAuth =
|
||||||
data?.config?.triggersConfig_customerAuthentication === 'EMAIL'
|
data?.config?.triggersConfig_customerAuthentication === 'EMAIL'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,6 @@ const Routes = () => {
|
||||||
<PrivateRoute path="/machines" component={Machines} />
|
<PrivateRoute path="/machines" component={Machines} />
|
||||||
<PrivateRoute path="/wizard" component={Wizard} />
|
<PrivateRoute path="/wizard" component={Wizard} />
|
||||||
<PublicRoute path="/register" component={Register} />
|
<PublicRoute path="/register" component={Register} />
|
||||||
{/* <PublicRoute path="/configmigration" component={ConfigMigration} /> */}
|
|
||||||
<PublicRoute path="/login" restricted component={Login} />
|
<PublicRoute path="/login" restricted component={Login} />
|
||||||
<PublicRoute path="/resetpassword" component={ResetPassword} />
|
<PublicRoute path="/resetpassword" component={ResetPassword} />
|
||||||
<PublicRoute path="/reset2fa" component={Reset2FA} />
|
<PublicRoute path="/reset2fa" component={Reset2FA} />
|
||||||
|
|
|
||||||
49
package-lock.json
generated
49
package-lock.json
generated
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "lamassu-server",
|
"name": "lamassu-server",
|
||||||
"version": "10.0.7",
|
"version": "10.1.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -1105,6 +1105,11 @@
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
|
||||||
"integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ=="
|
"integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ=="
|
||||||
|
},
|
||||||
|
"buffer-equals": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-equals/-/buffer-equals-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-99MsCq0j5+RhubVEtKQgKaD6EM+UP3xJgIvQqwJ3SOLDUekzxMX1ylXBng+Wa2sh7mGT0W6RUly8ojjr1Tt6nA=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -1302,6 +1307,28 @@
|
||||||
"superagent": "^3.8.3",
|
"superagent": "^3.8.3",
|
||||||
"tweetnacl": "^1.0.3",
|
"tweetnacl": "^1.0.3",
|
||||||
"uuid": "^8.3.2"
|
"uuid": "^8.3.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bitcoinjs-message": {
|
||||||
|
"version": "npm:@bitgo-forks/bitcoinjs-message@1.0.0-master.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@bitgo-forks/bitcoinjs-message/-/bitcoinjs-message-1.0.0-master.2.tgz",
|
||||||
|
"integrity": "sha512-XSDGM3rA75vcDxeKqHPexika/TgWUFWdfKTv1lV8TZTb5XFHHD6ARckLdMOBiCf29eZSzbJQvF/OIWqNqMl/2A==",
|
||||||
|
"requires": {
|
||||||
|
"bech32": "^1.1.3",
|
||||||
|
"bs58check": "^2.1.2",
|
||||||
|
"buffer-equals": "^1.0.3",
|
||||||
|
"create-hash": "^1.1.2",
|
||||||
|
"secp256k1": "5.0.0",
|
||||||
|
"varuint-bitcoin": "^1.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bech32": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@bitgo/sdk-lib-mpc": {
|
"@bitgo/sdk-lib-mpc": {
|
||||||
|
|
@ -6209,6 +6236,26 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"elliptic-sdk": {
|
||||||
|
"version": "0.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/elliptic-sdk/-/elliptic-sdk-0.7.2.tgz",
|
||||||
|
"integrity": "sha512-TMhcMmBGyuGe7GcDHEd2AnTPjq4G9+aYn7D93U9/r3fwqiD/WRCQLg63gzWdXAmsq9KnuE4bbRiFmyF6tItbZw==",
|
||||||
|
"requires": {
|
||||||
|
"axios": "^1.3.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": {
|
||||||
|
"version": "1.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz",
|
||||||
|
"integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==",
|
||||||
|
"requires": {
|
||||||
|
"follow-redirects": "^1.15.6",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"emoji-regex": {
|
"emoji-regex": {
|
||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "lamassu-server",
|
"name": "lamassu-server",
|
||||||
"description": "bitcoin atm client server protocol module",
|
"description": "bitcoin atm client server protocol module",
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"version": "10.0.7",
|
"version": "10.1.0",
|
||||||
"license": "./LICENSE",
|
"license": "./LICENSE",
|
||||||
"author": "Lamassu (https://lamassu.is)",
|
"author": "Lamassu (https://lamassu.is)",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -19,8 +19,8 @@
|
||||||
"@lamassu/coins": "v1.4.12",
|
"@lamassu/coins": "v1.4.12",
|
||||||
"@simplewebauthn/server": "^3.0.0",
|
"@simplewebauthn/server": "^3.0.0",
|
||||||
"@vonage/auth": "1.5.0",
|
"@vonage/auth": "1.5.0",
|
||||||
"@vonage/sms": "1.7.0",
|
|
||||||
"@vonage/server-client": "1.7.0",
|
"@vonage/server-client": "1.7.0",
|
||||||
|
"@vonage/sms": "1.7.0",
|
||||||
"@vonage/vetch": "1.5.0",
|
"@vonage/vetch": "1.5.0",
|
||||||
"apollo-server-express": "2.25.1",
|
"apollo-server-express": "2.25.1",
|
||||||
"argon2": "0.28.2",
|
"argon2": "0.28.2",
|
||||||
|
|
@ -42,6 +42,7 @@
|
||||||
"date-fns-tz": "^1.1.6",
|
"date-fns-tz": "^1.1.6",
|
||||||
"dateformat": "^3.0.3",
|
"dateformat": "^3.0.3",
|
||||||
"dotenv": "^16.0.0",
|
"dotenv": "^16.0.0",
|
||||||
|
"elliptic-sdk": "^0.7.2",
|
||||||
"ethereumjs-tx": "^1.3.3",
|
"ethereumjs-tx": "^1.3.3",
|
||||||
"ethereumjs-util": "^5.2.0",
|
"ethereumjs-util": "^5.2.0",
|
||||||
"ethereumjs-wallet": "^0.6.3",
|
"ethereumjs-wallet": "^0.6.3",
|
||||||
|
|
@ -112,12 +113,14 @@
|
||||||
"lamassu-ofac-update": "./bin/lamassu-ofac-update",
|
"lamassu-ofac-update": "./bin/lamassu-ofac-update",
|
||||||
"lamassu-send-coins": "./bin/lamassu-send-coins",
|
"lamassu-send-coins": "./bin/lamassu-send-coins",
|
||||||
"lamassu-update-to-mnemonic": "./bin/lamassu-update-to-mnemonic",
|
"lamassu-update-to-mnemonic": "./bin/lamassu-update-to-mnemonic",
|
||||||
|
"lamassu-btc-bumpfee": "./bin/lamassu-btc-bumpfee",
|
||||||
"lamassu-update-wallet-nodes": "./bin/lamassu-update-wallet-nodes",
|
"lamassu-update-wallet-nodes": "./bin/lamassu-update-wallet-nodes",
|
||||||
"lamassu-configure-frontcamera": "./bin/lamassu-configure-frontcamera",
|
"lamassu-configure-frontcamera": "./bin/lamassu-configure-frontcamera",
|
||||||
"lamassu-devices": "./bin/lamassu-devices",
|
"lamassu-devices": "./bin/lamassu-devices",
|
||||||
"lamassu-operator": "./bin/lamassu-operator",
|
"lamassu-operator": "./bin/lamassu-operator",
|
||||||
"lamassu-coinatmradar": "./bin/lamassu-coinatmradar",
|
"lamassu-coinatmradar": "./bin/lamassu-coinatmradar",
|
||||||
"lamassu-eth-recovery": "./bin/lamassu-eth-recovery",
|
"lamassu-eth-recovery": "./bin/lamassu-eth-recovery",
|
||||||
|
"lamassu-trx-recovery": "./bin/lamassu-trx-recovery",
|
||||||
"lamassu-update-cassettes": "./bin/lamassu-update-cassettes",
|
"lamassu-update-cassettes": "./bin/lamassu-update-cassettes",
|
||||||
"lamassu-clean-parsed-id": "./bin/lamassu-clean-parsed-id"
|
"lamassu-clean-parsed-id": "./bin/lamassu-clean-parsed-id"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,6 @@ rm /tmp/Lamassu_OP.csr.pem
|
||||||
mkdir -p $OFAC_DATA_DIR/sources
|
mkdir -p $OFAC_DATA_DIR/sources
|
||||||
touch $OFAC_DATA_DIR/etags.json
|
touch $OFAC_DATA_DIR/etags.json
|
||||||
|
|
||||||
node bin/scripts/build-dev-env.js
|
node tools/build-dev-env.js
|
||||||
|
|
||||||
echo "Done."
|
echo "Done."
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue