Merge branch 'release-8.1' into fix/ct-ui-score-error-color
This commit is contained in:
commit
01e0c57655
40 changed files with 332 additions and 160 deletions
|
|
@ -1,5 +1,7 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
require('../lib/environment-helper')
|
||||||
|
|
||||||
const argv = require('minimist')(process.argv.slice(2))
|
const argv = require('minimist')(process.argv.slice(2))
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
const db = require('../lib/db')
|
const db = require('../lib/db')
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
require('../lib/environment-helper')
|
||||||
|
|
||||||
const install = require('../lib/blockchain/install')
|
const install = require('../lib/blockchain/install')
|
||||||
|
|
||||||
install.run()
|
install.run()
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
require('../lib/environment-helper')
|
||||||
|
|
||||||
const settingsLoader = require('../lib/new-settings-loader')
|
const settingsLoader = require('../lib/new-settings-loader')
|
||||||
const pp = require('../lib/pp')
|
const pp = require('../lib/pp')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
require('../lib/environment-helper')
|
||||||
|
|
||||||
const hdkey = require('ethereumjs-wallet/hdkey')
|
const hdkey = require('ethereumjs-wallet/hdkey')
|
||||||
const hkdf = require('futoin-hkdf')
|
const hkdf = require('futoin-hkdf')
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
require('../lib/environment-helper')
|
||||||
|
|
||||||
const migrate = require('../lib/migrate-options')
|
const migrate = require('../lib/migrate-options')
|
||||||
|
|
||||||
migrate.run()
|
migrate.run()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
require('../lib/environment-helper')
|
||||||
|
|
||||||
const ofac = require('../lib/ofac/update')
|
const ofac = require('../lib/ofac/update')
|
||||||
|
|
||||||
console.log('Updating OFAC databases.')
|
console.log('Updating OFAC databases.')
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
require('../lib/environment-helper')
|
||||||
|
|
||||||
const settingsLoader = require('../lib/new-settings-loader')
|
const settingsLoader = require('../lib/new-settings-loader')
|
||||||
const configManager = require('../lib/new-config-manager')
|
const configManager = require('../lib/new-config-manager')
|
||||||
const wallet = require('../lib/wallet')
|
const wallet = require('../lib/wallet')
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
require('../lib/environment-helper')
|
||||||
|
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
const db = require('../lib/db')
|
const db = require('../lib/db')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
require('../lib/environment-helper')
|
||||||
const _ = require('lodash/fp')
|
const _ = require('lodash/fp')
|
||||||
const common = require('../lib/blockchain/common')
|
const common = require('../lib/blockchain/common')
|
||||||
const { utils: coinUtils } = require('@lamassu/coins')
|
const { utils: coinUtils } = require('@lamassu/coins')
|
||||||
|
|
|
||||||
36
build/Dockerfile
Normal file
36
build/Dockerfile
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
FROM ubuntu:20.04 as base
|
||||||
|
|
||||||
|
ARG VERSION
|
||||||
|
ENV SERVER_VERSION=$VERSION
|
||||||
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
ENV TZ=Europe/Lisbon
|
||||||
|
|
||||||
|
RUN apt-get update
|
||||||
|
|
||||||
|
RUN apt-get install -y -q curl \
|
||||||
|
sudo \
|
||||||
|
git \
|
||||||
|
python2-minimal \
|
||||||
|
build-essential \
|
||||||
|
libpq-dev \
|
||||||
|
net-tools \
|
||||||
|
tar
|
||||||
|
|
||||||
|
RUN curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
|
||||||
|
RUN apt-get install nodejs -y -q
|
||||||
|
|
||||||
|
FROM base as l-s-build
|
||||||
|
|
||||||
|
WORKDIR /lamassu
|
||||||
|
|
||||||
|
RUN git clone https://github.com/lamassu/lamassu-server -b ${SERVER_VERSION}
|
||||||
|
RUN rm -rf /lamassu/lamassu-server/public/*
|
||||||
|
RUN cd lamassu-server && npm install --production
|
||||||
|
|
||||||
|
RUN cd lamassu-server/new-lamassu-admin && npm install && npm run build
|
||||||
|
RUN cp -r /lamassu/lamassu-server/new-lamassu-admin/build/* /lamassu/lamassu-server/public
|
||||||
|
RUN rm -rf /lamassu/lamassu-server/new-lamassu-admin/node_modules
|
||||||
|
|
||||||
|
RUN tar -zcvf lamassu-server-$SERVER_VERSION.tar.gz lamassu-server/
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/bin/bash" ]
|
||||||
13
build/build.sh
Executable file
13
build/build.sh
Executable file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
echo "Error: no arguments specified"
|
||||||
|
echo "Usage: ./build.sh <SERVER_VERSION_TAG>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker build --rm --build-arg VERSION=$1 --tag l-s-prepackage:$1 --file Dockerfile .
|
||||||
|
|
||||||
|
id=$(docker create l-s-prepackage:$1)
|
||||||
|
docker cp $id:/lamassu/lamassu-server-$1.tar.gz ./lamassu-server-$1.tar.gz
|
||||||
|
docker rm -v $id
|
||||||
|
|
@ -33,12 +33,12 @@ const BINARIES = {
|
||||||
dir: 'geth-linux-amd64-1.10.25-69568c55'
|
dir: 'geth-linux-amd64-1.10.25-69568c55'
|
||||||
},
|
},
|
||||||
ZEC: {
|
ZEC: {
|
||||||
url: 'https://z.cash/downloads/zcash-5.2.0-linux64-debian-bullseye.tar.gz',
|
url: 'https://z.cash/downloads/zcash-5.3.0-linux64-debian-bullseye.tar.gz',
|
||||||
dir: 'zcash-5.2.0/bin'
|
dir: 'zcash-5.3.0/bin'
|
||||||
},
|
},
|
||||||
DASH: {
|
DASH: {
|
||||||
url: 'https://github.com/dashpay/dash/releases/download/v18.0.1/dashcore-18.0.1-x86_64-linux-gnu.tar.gz',
|
url: 'https://github.com/dashpay/dash/releases/download/v18.1.0/dashcore-18.1.0-x86_64-linux-gnu.tar.gz',
|
||||||
dir: 'dashcore-18.0.1/bin'
|
dir: 'dashcore-18.1.0/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',
|
||||||
|
|
|
||||||
|
|
@ -168,23 +168,32 @@ function postProcess (r, pi, isBlacklisted, addressReuse, walletScore) {
|
||||||
|
|
||||||
function doesTxReuseAddress (tx) {
|
function doesTxReuseAddress (tx) {
|
||||||
if (!tx.fiat || tx.fiat.isZero()) {
|
if (!tx.fiat || tx.fiat.isZero()) {
|
||||||
const sql = `SELECT EXISTS (SELECT DISTINCT to_address FROM cash_in_txs WHERE to_address = $1)`
|
const sql = `
|
||||||
return db.any(sql, [tx.toAddress])
|
SELECT EXISTS (
|
||||||
|
SELECT DISTINCT to_address FROM (
|
||||||
|
SELECT to_address FROM cash_in_txs WHERE id != $1
|
||||||
|
) AS x WHERE to_address = $2
|
||||||
|
)`
|
||||||
|
return db.one(sql, [tx.id, tx.toAddress]).then(({ exists }) => exists)
|
||||||
}
|
}
|
||||||
return Promise.resolve(false)
|
return Promise.resolve(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWalletScore (tx, pi) {
|
function getWalletScore (tx, pi) {
|
||||||
if (!tx.fiat || tx.fiat.isZero()) {
|
pi.isWalletScoringEnabled(tx)
|
||||||
return pi.rateWallet(tx.cryptoCode, tx.toAddress)
|
.then(isEnabled => {
|
||||||
}
|
if(!isEnabled) return null
|
||||||
// Passthrough the previous result
|
if (!tx.fiat || tx.fiat.isZero()) {
|
||||||
return pi.isValidWalletScore(tx.walletScore)
|
return pi.rateWallet(tx.cryptoCode, tx.toAddress)
|
||||||
.then(isValid => ({
|
}
|
||||||
address: tx.toAddress,
|
// Passthrough the previous result
|
||||||
score: tx.walletScore,
|
return pi.isValidWalletScore(tx.walletScore)
|
||||||
isValid
|
.then(isValid => ({
|
||||||
}))
|
address: tx.toAddress,
|
||||||
|
score: tx.walletScore,
|
||||||
|
isValid
|
||||||
|
}))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function monitorPending (settings) {
|
function monitorPending (settings) {
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,8 @@ function selfPost (tx, pi) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function post (tx, pi, fromClient = true) {
|
function post (tx, pi, fromClient = true) {
|
||||||
logger.silly('Updating cashout tx:', tx)
|
logger.silly('Updating cashout -- tx:', JSON.stringify(tx))
|
||||||
|
logger.silly('Updating cashout -- fromClient:', JSON.stringify(fromClient))
|
||||||
return cashOutAtomic.atomic(tx, pi, fromClient)
|
return cashOutAtomic.atomic(tx, pi, fromClient)
|
||||||
.then(txVector => {
|
.then(txVector => {
|
||||||
const [, newTx, justAuthorized] = txVector
|
const [, newTx, justAuthorized] = txVector
|
||||||
|
|
@ -63,7 +64,7 @@ function postProcess (txVector, justAuthorized, pi) {
|
||||||
fiat: newTx.fiat
|
fiat: newTx.fiat
|
||||||
})
|
})
|
||||||
const bills = billMath.makeChange(cassettes.cassettes, newTx.fiat)
|
const bills = billMath.makeChange(cassettes.cassettes, newTx.fiat)
|
||||||
logger.silly('Bills to dispense:', bills)
|
logger.silly('Bills to dispense:', JSON.stringify(bills))
|
||||||
|
|
||||||
if (!bills) throw httpError('Out of bills', INSUFFICIENT_FUNDS_CODE)
|
if (!bills) throw httpError('Out of bills', INSUFFICIENT_FUNDS_CODE)
|
||||||
return bills
|
return bills
|
||||||
|
|
@ -121,30 +122,34 @@ function getWalletScore (tx, pi) {
|
||||||
return tx
|
return tx
|
||||||
|
|
||||||
// Transaction shows up on the blockchain, we can request the sender address
|
// Transaction shows up on the blockchain, we can request the sender address
|
||||||
return pi.getTransactionHash(tx)
|
return pi.isWalletScoringEnabled(tx)
|
||||||
.then(rejectEmpty("No transaction hashes"))
|
.then(isEnabled => {
|
||||||
.then(txHashes => pi.getInputAddresses(tx, txHashes))
|
if (!isEnabled) return tx
|
||||||
.then(rejectEmpty("No input addresses"))
|
return pi.getTransactionHash(tx)
|
||||||
.then(addresses => Promise.all(_.map(it => pi.rateWallet(tx.cryptoCode, it), addresses)))
|
.then(rejectEmpty("No transaction hashes"))
|
||||||
.then(rejectEmpty("No score ratings"))
|
.then(txHashes => pi.getInputAddresses(tx, txHashes))
|
||||||
.then(_.maxBy(_.get(['score'])))
|
.then(rejectEmpty("No input addresses"))
|
||||||
.then(highestScore =>
|
.then(addresses => Promise.all(_.map(it => pi.rateWallet(tx.cryptoCode, it), addresses)))
|
||||||
// Conservatively assign the highest risk of all input addresses to the risk of this transaction
|
.then(rejectEmpty("No score ratings"))
|
||||||
highestScore.isValid
|
.then(_.maxBy(_.get(['score'])))
|
||||||
? _.assign(tx, { walletScore: highestScore.score })
|
.then(highestScore =>
|
||||||
: _.assign(tx, {
|
// Conservatively assign the highest risk of all input addresses to the risk of this transaction
|
||||||
walletScore: highestScore.score,
|
highestScore.isValid
|
||||||
error: 'Ciphertrace score is above defined threshold',
|
? _.assign(tx, { walletScore: highestScore.score })
|
||||||
errorCode: 'scoreThresholdReached',
|
: _.assign(tx, {
|
||||||
|
walletScore: highestScore.score,
|
||||||
|
error: 'Ciphertrace score is above defined threshold',
|
||||||
|
errorCode: 'scoreThresholdReached',
|
||||||
|
dispense: true
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.catch(error => _.assign(tx, {
|
||||||
|
walletScore: 10,
|
||||||
|
error: `Failure getting address score: ${error.message}`,
|
||||||
|
errorCode: 'ciphertraceError',
|
||||||
dispense: true
|
dispense: true
|
||||||
})
|
}))
|
||||||
)
|
})
|
||||||
.catch(error => _.assign(tx, {
|
|
||||||
walletScore: 10,
|
|
||||||
error: `Failure getting address score: ${error.message}`,
|
|
||||||
errorCode: 'ciphertraceError',
|
|
||||||
dispense: true
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function monitorLiveIncoming (settings) {
|
function monitorLiveIncoming (settings) {
|
||||||
|
|
|
||||||
|
|
@ -262,7 +262,7 @@ function deleteEditedData (id, data) {
|
||||||
*/
|
*/
|
||||||
async function updateEditedPhoto (id, photo, photoType) {
|
async function updateEditedPhoto (id, photo, photoType) {
|
||||||
const newPatch = {}
|
const newPatch = {}
|
||||||
const baseDir = photoType === 'frontCamera' ? frontCameraBaseDir : idPhotoCardBasedir
|
const baseDir = photoType === 'frontCamera' ? FRONT_CAMERA_DIR : ID_PHOTO_CARD_DIR
|
||||||
const { createReadStream, filename } = photo
|
const { createReadStream, filename } = photo
|
||||||
const stream = createReadStream()
|
const stream = createReadStream()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -198,7 +198,10 @@ const dynamicConfig = ({ deviceId, operatorId, pid, pq, settings, }) => {
|
||||||
_.toPairs,
|
_.toPairs,
|
||||||
|
|
||||||
/* [[cryptoCode, { balance, ask, bid, cashIn, cashOut }], ...] => [{ cryptoCode, balance, ask, bid, cashIn, cashOut }, ...] */
|
/* [[cryptoCode, { balance, ask, bid, cashIn, cashOut }], ...] => [{ cryptoCode, balance, ask, bid, cashIn, cashOut }, ...] */
|
||||||
_.map(([cryptoCode, obj]) => _.set('cryptoCode', cryptoCode, obj))
|
_.map(([cryptoCode, obj]) => _.set('cryptoCode', cryptoCode, obj)),
|
||||||
|
|
||||||
|
/* Only send coins which have all information needed by the machine. This prevents the machine going down if there's an issue with the coin node */
|
||||||
|
_.filter(coin => ['ask', 'bid', 'balance', 'cashIn', 'cashOut', 'cryptoCode'].every(it => it in coin))
|
||||||
)(_.concat(balances, coins))
|
)(_.concat(balances, coins))
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|
@ -232,6 +235,7 @@ const configs = (parent, { currentConfigVersion }, { deviceId, deviceName, opera
|
||||||
|
|
||||||
|
|
||||||
const massageTerms = terms => (terms.active && terms.text) ? ({
|
const massageTerms = terms => (terms.active && terms.text) ? ({
|
||||||
|
tcPhoto: Boolean(terms.tcPhoto),
|
||||||
delay: Boolean(terms.delay),
|
delay: Boolean(terms.delay),
|
||||||
title: terms.title,
|
title: terms.title,
|
||||||
text: nmd(terms.text),
|
text: nmd(terms.text),
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,32 @@ type TriggersAutomation {
|
||||||
usSsn: Boolean!
|
usSsn: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CustomScreen {
|
||||||
|
text: String!
|
||||||
|
title: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomInput {
|
||||||
|
type: String!
|
||||||
|
constraintType: String!
|
||||||
|
label1: String
|
||||||
|
label2: String
|
||||||
|
choiceList: [String]
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomRequest {
|
||||||
|
name: String!
|
||||||
|
input: CustomInput!
|
||||||
|
screen1: CustomScreen!
|
||||||
|
screen2: CustomScreen!
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomInfoRequest {
|
||||||
|
id: String!
|
||||||
|
enabled: Boolean!
|
||||||
|
customRequest: CustomRequest!
|
||||||
|
}
|
||||||
|
|
||||||
type Trigger {
|
type Trigger {
|
||||||
id: String!
|
id: String!
|
||||||
customInfoRequestId: String!
|
customInfoRequestId: String!
|
||||||
|
|
@ -68,9 +94,11 @@ type Trigger {
|
||||||
suspensionDays: Float
|
suspensionDays: Float
|
||||||
threshold: Int
|
threshold: Int
|
||||||
thresholdDays: Int
|
thresholdDays: Int
|
||||||
|
customInfoRequest: CustomInfoRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
type TermsDetails {
|
type TermsDetails {
|
||||||
|
tcPhoto: Boolean!
|
||||||
delay: Boolean!
|
delay: Boolean!
|
||||||
title: String!
|
title: String!
|
||||||
accept: String!
|
accept: String!
|
||||||
|
|
|
||||||
|
|
@ -55,30 +55,54 @@ const populateSettings = function (req, res, next) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const operatorSettings = settingsCache.get(operatorId)
|
// Priority of configs to retrieve
|
||||||
if (!versionId && (!operatorSettings || !!needsSettingsReload[operatorId])) {
|
// 1. Machine is in the middle of a transaction and has the config-version header set, fetch that config from cache or database, depending on whether it exists in cache
|
||||||
|
// 2. The operator settings changed, so we must update the cache
|
||||||
|
// 3. There's a cached config, send the cached value
|
||||||
|
// 4. There's no cached config, cache and send the latest config
|
||||||
|
|
||||||
|
if (versionId) {
|
||||||
|
const cachedVersionedSettings = settingsCache.get(`${operatorId}-v${versionId}`)
|
||||||
|
|
||||||
|
if (!cachedVersionedSettings) {
|
||||||
|
logger.debug('Fetching a specific config version cached value')
|
||||||
|
return newSettingsLoader.load(versionId)
|
||||||
|
.then(settings => {
|
||||||
|
settingsCache.set(`${operatorId}-v${versionId}`, settings)
|
||||||
|
req.settings = settings
|
||||||
|
})
|
||||||
|
.then(() => next())
|
||||||
|
.catch(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug('Fetching and caching a specific config version')
|
||||||
|
req.settings = cachedVersionedSettings
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
|
||||||
|
const operatorSettings = settingsCache.get(`${operatorId}-latest`)
|
||||||
|
|
||||||
|
if (!!needsSettingsReload[operatorId] || !operatorSettings) {
|
||||||
|
!!needsSettingsReload[operatorId]
|
||||||
|
? logger.debug('Fetching and caching a new latest config value, as a reload was requested')
|
||||||
|
: logger.debug('Fetching the latest config version because there\'s no cached value')
|
||||||
|
|
||||||
return newSettingsLoader.loadLatest()
|
return newSettingsLoader.loadLatest()
|
||||||
.then(settings => {
|
.then(settings => {
|
||||||
settingsCache.set(operatorId, settings)
|
settingsCache.set(`${operatorId}-latest`, settings)
|
||||||
delete needsSettingsReload[operatorId]
|
if (!!needsSettingsReload[operatorId]) delete needsSettingsReload[operatorId]
|
||||||
req.settings = settings
|
req.settings = settings
|
||||||
})
|
})
|
||||||
.then(() => next())
|
.then(() => next())
|
||||||
.catch(next)
|
.catch(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!versionId && operatorSettings) {
|
logger.debug('Fetching the latest config value from cache')
|
||||||
req.settings = operatorSettings
|
req.settings = operatorSettings
|
||||||
return next()
|
return next()
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
newSettingsLoader.load(versionId)
|
|
||||||
.then(settings => { req.settings = settings })
|
|
||||||
.then(() => next())
|
|
||||||
.catch(next)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = populateSettings
|
module.exports = populateSettings
|
||||||
|
|
|
||||||
|
|
@ -145,13 +145,13 @@ const getTriggersAutomation = (customInfoRequests, config) => {
|
||||||
|
|
||||||
const splitGetFirst = _.compose(_.head, _.split('_'))
|
const splitGetFirst = _.compose(_.head, _.split('_'))
|
||||||
|
|
||||||
const getCryptosFromWalletNamespace = config => {
|
const getCryptosFromWalletNamespace =
|
||||||
return _.uniq(_.map(splitGetFirst, _.keys(fromNamespace('wallets', config))))
|
_.compose(_.without(['advanced']), _.uniq, _.map(splitGetFirst), _.keys, fromNamespace('wallets'))
|
||||||
}
|
|
||||||
|
|
||||||
const getCashInSettings = config => fromNamespace(namespaces.CASH_IN)(config)
|
const getCashInSettings = config => fromNamespace(namespaces.CASH_IN)(config)
|
||||||
|
|
||||||
const getCryptoUnits = (crypto, config) => getWalletSettings(crypto, config).cryptoUnits
|
const getCryptoUnits = (crypto, config) =>
|
||||||
|
getWalletSettings(crypto, config).cryptoUnits ?? 'full'
|
||||||
|
|
||||||
const setTermsConditions = toNamespace(namespaces.TERMS_CONDITIONS)
|
const setTermsConditions = toNamespace(namespaces.TERMS_CONDITIONS)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -850,6 +850,10 @@ function plugins (settings, deviceId) {
|
||||||
return walletScoring.getInputAddresses(settings, tx.cryptoCode, txHashes)
|
return walletScoring.getInputAddresses(settings, tx.cryptoCode, txHashes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isWalletScoringEnabled (tx) {
|
||||||
|
return walletScoring.isWalletScoringEnabled(settings, tx.cryptoCode)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getRates,
|
getRates,
|
||||||
recordPing,
|
recordPing,
|
||||||
|
|
@ -882,7 +886,8 @@ function plugins (settings, deviceId) {
|
||||||
rateWallet,
|
rateWallet,
|
||||||
isValidWalletScore,
|
isValidWalletScore,
|
||||||
getTransactionHash,
|
getTransactionHash,
|
||||||
getInputAddresses
|
getInputAddresses,
|
||||||
|
isWalletScoringEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,18 +28,18 @@ function rateWallet (account, cryptoCode, address) {
|
||||||
const { apiVersion, authHeader } = client
|
const { apiVersion, authHeader } = client
|
||||||
const threshold = account.scoreThreshold
|
const threshold = account.scoreThreshold
|
||||||
|
|
||||||
console.log(`** DEBUG ** rateWallet ENDPOINT: https://rest.ciphertrace.com/aml/${apiVersion}/${_.toLower(cryptoCode)}/risk?address=${address}`)
|
logger.info(`** DEBUG ** rateWallet ENDPOINT: https://rest.ciphertrace.com/aml/${apiVersion}/${_.toLower(cryptoCode)}/risk?address=${address}`)
|
||||||
|
|
||||||
return axios.get(`https://rest.ciphertrace.com/aml/${apiVersion}/${_.toLower(cryptoCode)}/risk?address=${address}`, {
|
return axios.get(`https://rest.ciphertrace.com/aml/${apiVersion}/${_.toLower(cryptoCode)}/risk?address=${address}`, {
|
||||||
headers: authHeader
|
headers: authHeader
|
||||||
})
|
})
|
||||||
.then(res => ({ address, score: res.data.risk, isValid: res.data.risk < threshold }))
|
.then(res => ({ address, score: res.data.risk, isValid: res.data.risk < threshold }))
|
||||||
.then(result => {
|
.then(result => {
|
||||||
console.log('** DEBUG ** rateWallet RETURN:', result)
|
logger.info(`** DEBUG ** rateWallet RETURN: ${result}`)
|
||||||
return result
|
return result
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.log(`** DEBUG ** rateWallet ERROR: ${err.message}`)
|
logger.error(`** DEBUG ** rateWallet ERROR: ${err.message}`)
|
||||||
throw err
|
throw err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -54,7 +54,8 @@ function isValidWalletScore (account, score) {
|
||||||
|
|
||||||
function getAddressTransactionsHashes (receivingAddress, cryptoCode, client, wallet) {
|
function getAddressTransactionsHashes (receivingAddress, cryptoCode, client, wallet) {
|
||||||
const { apiVersion, authHeader } = client
|
const { apiVersion, authHeader } = client
|
||||||
console.log(`** DEBUG ** getTransactionHash ENDPOINT: https://rest.ciphertrace.com/api/${apiVersion}/${_.toLower(cryptoCode) !== 'btc' ? `${_.toLower(cryptoCode)}_` : ``}address/search?features=tx&address=${receivingAddress}&mempool=true`)
|
|
||||||
|
logger.info(`** DEBUG ** getTransactionHash ENDPOINT: https://rest.ciphertrace.com/api/${apiVersion}/${_.toLower(cryptoCode) !== 'btc' ? `${_.toLower(cryptoCode)}_` : ``}address/search?features=tx&address=${receivingAddress}&mempool=true`)
|
||||||
|
|
||||||
return axios.get(`https://rest.ciphertrace.com/api/${apiVersion}/${_.toLower(cryptoCode) !== 'btc' ? `${_.toLower(cryptoCode)}_` : ``}address/search?features=tx&address=${receivingAddress}&mempool=true`, {
|
return axios.get(`https://rest.ciphertrace.com/api/${apiVersion}/${_.toLower(cryptoCode) !== 'btc' ? `${_.toLower(cryptoCode)}_` : ``}address/search?features=tx&address=${receivingAddress}&mempool=true`, {
|
||||||
headers: authHeader
|
headers: authHeader
|
||||||
|
|
@ -64,8 +65,8 @@ function getAddressTransactionsHashes (receivingAddress, cryptoCode, client, wal
|
||||||
_.map(_.get(['txHash']))
|
_.map(_.get(['txHash']))
|
||||||
))
|
))
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.log(`** DEBUG ** getTransactionHash ERROR: ${err}`)
|
logger.error(`** DEBUG ** getTransactionHash ERROR: ${err}`)
|
||||||
console.log(`** DEBUG ** Fetching transactions hashes via wallet node...`)
|
logger.error(`** DEBUG ** Fetching transactions hashes via wallet node...`)
|
||||||
return wallet.getTxHashesByAddress(cryptoCode, receivingAddress)
|
return wallet.getTxHashesByAddress(cryptoCode, receivingAddress)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -78,11 +79,11 @@ function getTransactionHash (account, cryptoCode, receivingAddress, wallet) {
|
||||||
if (_.size(txHashes) > 1) {
|
if (_.size(txHashes) > 1) {
|
||||||
logger.warn('An address generated by this wallet was used in more than one transaction')
|
logger.warn('An address generated by this wallet was used in more than one transaction')
|
||||||
}
|
}
|
||||||
console.log('** DEBUG ** getTransactionHash RETURN: ', _.join(', ', txHashes))
|
logger.info('** DEBUG ** getTransactionHash RETURN: ', _.join(', ', txHashes))
|
||||||
return txHashes
|
return txHashes
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.log('** DEBUG ** getTransactionHash from wallet node ERROR: ', err)
|
logger.error('** DEBUG ** getTransactionHash from wallet node ERROR: ', err)
|
||||||
throw err
|
throw err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -116,20 +117,29 @@ function getInputAddresses (account, cryptoCode, txHashes) {
|
||||||
const transactionInputs = _.flatMap(it => it.inputs, data.transactions)
|
const transactionInputs = _.flatMap(it => it.inputs, data.transactions)
|
||||||
const inputAddresses = _.map(it => it.address, transactionInputs)
|
const inputAddresses = _.map(it => it.address, transactionInputs)
|
||||||
|
|
||||||
console.log(`** DEBUG ** getInputAddresses RETURN: ${inputAddresses}`)
|
logger.info(`** DEBUG ** getInputAddresses RETURN: ${inputAddresses}`)
|
||||||
|
|
||||||
return inputAddresses
|
return inputAddresses
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.log(`** DEBUG ** getInputAddresses ERROR: ${err.message}`)
|
logger.error(`** DEBUG ** getInputAddresses ERROR: ${err.message}`)
|
||||||
throw err
|
throw err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isWalletScoringEnabled (account, cryptoCode) {
|
||||||
|
if (!SUPPORTED_COINS.includes(cryptoCode)) {
|
||||||
|
return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode))
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(!_.isNil(account) && account.enabled)
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
NAME,
|
NAME,
|
||||||
rateWallet,
|
rateWallet,
|
||||||
isValidWalletScore,
|
isValidWalletScore,
|
||||||
getTransactionHash,
|
getTransactionHash,
|
||||||
getInputAddresses
|
getInputAddresses,
|
||||||
|
isWalletScoringEnabled
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,10 +36,20 @@ function getInputAddresses (account, cryptoCode, txHashes) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isWalletScoringEnabled (account, cryptoCode) {
|
||||||
|
return new Promise((resolve, _) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
return resolve(true)
|
||||||
|
}, 100)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
NAME,
|
NAME,
|
||||||
rateWallet,
|
rateWallet,
|
||||||
isValidWalletScore,
|
isValidWalletScore,
|
||||||
getTransactionHash,
|
getTransactionHash,
|
||||||
getInputAddresses
|
getInputAddresses,
|
||||||
|
isWalletScoringEnabled
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,9 +47,19 @@ function getInputAddresses (settings, cryptoCode, txHashes) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isWalletScoringEnabled (settings, cryptoCode) {
|
||||||
|
return Promise.resolve()
|
||||||
|
.then(() => {
|
||||||
|
const { plugin, account } = loadWalletScoring(settings)
|
||||||
|
|
||||||
|
return plugin.isWalletScoringEnabled(account, cryptoCode)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
rateWallet,
|
rateWallet,
|
||||||
isValidWalletScore,
|
isValidWalletScore,
|
||||||
getTransactionHash,
|
getTransactionHash,
|
||||||
getInputAddresses
|
getInputAddresses,
|
||||||
|
isWalletScoringEnabled
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import { compareAsc, differenceInDays, set } from 'date-fns/fp'
|
import { compareAsc, differenceInDays, set } from 'date-fns/fp'
|
||||||
|
import * as R from 'ramda'
|
||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
|
|
||||||
import Calendar from './Calendar'
|
import Calendar from './Calendar'
|
||||||
|
|
@ -37,7 +38,12 @@ const DateRangePicker = ({ minDate, maxDate, className, onRangeChange }) => {
|
||||||
set({ hours: 23, minutes: 59, seconds: 59, milliseconds: 999 }, day)
|
set({ hours: 23, minutes: 59, seconds: 59, milliseconds: 999 }, day)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
setTo(from)
|
setTo(
|
||||||
|
set(
|
||||||
|
{ hours: 23, minutes: 59, seconds: 59, milliseconds: 999 },
|
||||||
|
R.clone(from)
|
||||||
|
)
|
||||||
|
)
|
||||||
setFrom(day)
|
setFrom(day)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ const Header = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapElement = (
|
const mapElement = (
|
||||||
{ name, width = DEFAULT_COL_SIZE, header, textAlign },
|
{ name, display, width = DEFAULT_COL_SIZE, header, textAlign },
|
||||||
idx
|
idx
|
||||||
) => {
|
) => {
|
||||||
const orderClasses = classnames({
|
const orderClasses = classnames({
|
||||||
|
|
@ -99,7 +99,7 @@ const Header = () => {
|
||||||
<>{attachOrderedByToComplexHeader(header) ?? header}</>
|
<>{attachOrderedByToComplexHeader(header) ?? header}</>
|
||||||
) : (
|
) : (
|
||||||
<span className={orderClasses}>
|
<span className={orderClasses}>
|
||||||
{startCase(name)}{' '}
|
{!R.isNil(display) ? display : startCase(name)}{' '}
|
||||||
{!R.isNil(orderedBy) && R.equals(name, orderedBy.code) && '-'}
|
{!R.isNil(orderedBy) && R.equals(name, orderedBy.code) && '-'}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -277,7 +277,7 @@ const Analytics = () => {
|
||||||
case 'topMachines':
|
case 'topMachines':
|
||||||
return (
|
return (
|
||||||
<TopMachinesWrapper
|
<TopMachinesWrapper
|
||||||
title="Transactions over time"
|
title="Top 5 Machines"
|
||||||
representing={representing}
|
representing={representing}
|
||||||
period={period}
|
period={period}
|
||||||
data={R.map(convertFiatToLocale)(filteredData(period.code).current)}
|
data={R.map(convertFiatToLocale)(filteredData(period.code).current)}
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ const GET_TRANSACTIONS = gql`
|
||||||
customerId
|
customerId
|
||||||
isAnonymous
|
isAnonymous
|
||||||
rawTickerPrice
|
rawTickerPrice
|
||||||
|
profit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,9 @@ const CASSETTE_LIST = [
|
||||||
]
|
]
|
||||||
|
|
||||||
const widthsByNumberOfCassettes = {
|
const widthsByNumberOfCassettes = {
|
||||||
2: { machine: 230, cassette: 250 },
|
2: { machine: 230, cashbox: 150, cassette: 250 },
|
||||||
3: { machine: 216, cassette: 270 },
|
3: { machine: 216, cashbox: 150, cassette: 270 },
|
||||||
4: { machine: 210, cassette: 204 }
|
4: { machine: 210, cashbox: 150, cassette: 204 }
|
||||||
}
|
}
|
||||||
|
|
||||||
const FiatBalanceOverrides = ({ config, section }) => {
|
const FiatBalanceOverrides = ({ config, section }) => {
|
||||||
|
|
@ -44,19 +44,17 @@ const FiatBalanceOverrides = ({ config, section }) => {
|
||||||
|
|
||||||
const setupValues = data?.fiatBalanceOverrides ?? []
|
const setupValues = data?.fiatBalanceOverrides ?? []
|
||||||
const innerSetEditing = it => setEditing(NAME, it)
|
const innerSetEditing = it => setEditing(NAME, it)
|
||||||
|
|
||||||
const cashoutConfig = it => fromNamespace(it)(config)
|
const cashoutConfig = it => fromNamespace(it)(config)
|
||||||
|
|
||||||
const overriddenMachines = R.map(override => override.machine, setupValues)
|
const overriddenMachines = R.map(override => override.machine, setupValues)
|
||||||
const suggestionFilter = R.filter(
|
const suggestions = R.differenceWith(
|
||||||
it =>
|
(it, m) => it.deviceId === m,
|
||||||
!R.includes(it.deviceId, overriddenMachines) &&
|
machines,
|
||||||
cashoutConfig(it.deviceId).active
|
overriddenMachines
|
||||||
)
|
)
|
||||||
const suggestions = suggestionFilter(machines)
|
|
||||||
|
|
||||||
const findSuggestion = it => {
|
const findSuggestion = it => {
|
||||||
const coin = R.compose(R.find(R.propEq('deviceId', it?.machine)))(machines)
|
const coin = R.find(R.propEq('deviceId', it?.machine), machines)
|
||||||
return coin ? [coin] : []
|
return coin ? [coin] : []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,7 +81,6 @@ const FiatBalanceOverrides = ({ config, section }) => {
|
||||||
.shape({
|
.shape({
|
||||||
[MACHINE_KEY]: Yup.string()
|
[MACHINE_KEY]: Yup.string()
|
||||||
.label('Machine')
|
.label('Machine')
|
||||||
.nullable()
|
|
||||||
.required(),
|
.required(),
|
||||||
[CASHBOX_KEY]: Yup.number()
|
[CASHBOX_KEY]: Yup.number()
|
||||||
.label('Cash box')
|
.label('Cash box')
|
||||||
|
|
@ -121,51 +118,49 @@ const FiatBalanceOverrides = ({ config, section }) => {
|
||||||
.max(percentMax)
|
.max(percentMax)
|
||||||
.nullable()
|
.nullable()
|
||||||
})
|
})
|
||||||
.test((values, context) => {
|
.test((values, context) =>
|
||||||
const picked = R.pick(CASSETTE_LIST, values)
|
R.any(key => !R.isNil(values[key]), R.prepend(CASHBOX_KEY, CASSETTE_LIST))
|
||||||
|
? undefined
|
||||||
if (CASSETTE_LIST.some(it => !R.isNil(picked[it]))) return
|
: context.createError({
|
||||||
|
path: CASHBOX_KEY,
|
||||||
return context.createError({
|
message:
|
||||||
path: CASSETTE_1_KEY,
|
'The cash box or at least one of the cassettes must have a value'
|
||||||
message: 'At least one of the cassettes must have a value'
|
})
|
||||||
})
|
)
|
||||||
})
|
|
||||||
|
|
||||||
const viewMachine = it =>
|
const viewMachine = it =>
|
||||||
R.compose(R.path(['name']), R.find(R.propEq('deviceId', it)))(machines)
|
R.compose(R.path(['name']), R.find(R.propEq('deviceId', it)))(machines)
|
||||||
|
|
||||||
const elements = [
|
const elements = R.concat(
|
||||||
{
|
[
|
||||||
name: MACHINE_KEY,
|
{
|
||||||
width: widthsByNumberOfCassettes[maxNumberOfCassettes].machine,
|
name: MACHINE_KEY,
|
||||||
size: 'sm',
|
display: 'Machine',
|
||||||
view: viewMachine,
|
width: widthsByNumberOfCassettes[maxNumberOfCassettes].machine,
|
||||||
input: Autocomplete,
|
size: 'sm',
|
||||||
inputProps: {
|
view: viewMachine,
|
||||||
options: it => R.concat(suggestions, findSuggestion(it)),
|
input: Autocomplete,
|
||||||
valueProp: 'deviceId',
|
inputProps: {
|
||||||
labelProp: 'name'
|
options: it => R.concat(suggestions, findSuggestion(it)),
|
||||||
|
valueProp: 'deviceId',
|
||||||
|
labelProp: 'name'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CASHBOX_KEY,
|
||||||
|
display: 'Cash box',
|
||||||
|
width: widthsByNumberOfCassettes[maxNumberOfCassettes].cashbox,
|
||||||
|
textAlign: 'right',
|
||||||
|
bold: true,
|
||||||
|
input: NumberInput,
|
||||||
|
suffix: 'notes',
|
||||||
|
inputProps: {
|
||||||
|
decimalPlaces: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
],
|
||||||
{
|
R.map(
|
||||||
name: CASHBOX_KEY,
|
it => ({
|
||||||
display: 'Cashbox',
|
|
||||||
width: 155,
|
|
||||||
textAlign: 'right',
|
|
||||||
bold: true,
|
|
||||||
input: NumberInput,
|
|
||||||
suffix: 'notes',
|
|
||||||
inputProps: {
|
|
||||||
decimalPlaces: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
R.until(
|
|
||||||
R.gt(R.__, maxNumberOfCassettes),
|
|
||||||
it => {
|
|
||||||
elements.push({
|
|
||||||
name: `fillingPercentageCassette${it}`,
|
name: `fillingPercentageCassette${it}`,
|
||||||
display: `Cash cassette ${it}`,
|
display: `Cash cassette ${it}`,
|
||||||
width: widthsByNumberOfCassettes[maxNumberOfCassettes].cassette,
|
width: widthsByNumberOfCassettes[maxNumberOfCassettes].cassette,
|
||||||
|
|
@ -177,15 +172,18 @@ const FiatBalanceOverrides = ({ config, section }) => {
|
||||||
inputProps: {
|
inputProps: {
|
||||||
decimalPlaces: 0
|
decimalPlaces: 0
|
||||||
},
|
},
|
||||||
view: it => it?.toString() ?? '—',
|
view: el => el?.toString() ?? '—',
|
||||||
isHidden: value =>
|
isHidden: value =>
|
||||||
|
!cashoutConfig(value.machine).active ||
|
||||||
it >
|
it >
|
||||||
machines.find(({ deviceId }) => deviceId === value.machine)
|
R.defaultTo(
|
||||||
?.numberOfCassettes
|
0,
|
||||||
})
|
machines.find(({ deviceId }) => deviceId === value.machine)
|
||||||
return R.add(1, it)
|
?.numberOfCassettes
|
||||||
},
|
)
|
||||||
1
|
}),
|
||||||
|
R.range(1, maxNumberOfCassettes + 1)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ const DetailsRow = ({ it: tx, timezone }) => {
|
||||||
const zip = new JSZip()
|
const zip = new JSZip()
|
||||||
|
|
||||||
const [fetchSummary] = useLazyQuery(TX_SUMMARY, {
|
const [fetchSummary] = useLazyQuery(TX_SUMMARY, {
|
||||||
onCompleted: data => createCsv(data)
|
onCompleted: data => createCsv(R.filter(it => !R.isEmpty(it), data))
|
||||||
})
|
})
|
||||||
|
|
||||||
const [cancelTransaction] = useMutation(
|
const [cancelTransaction] = useMutation(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { utils as coinUtils } from '@lamassu/coins'
|
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
|
|
@ -54,13 +53,9 @@ const AllSet = ({ data: currentData, doContinue }) => {
|
||||||
const cryptoCurrencies = data?.cryptoCurrencies ?? []
|
const cryptoCurrencies = data?.cryptoCurrencies ?? []
|
||||||
|
|
||||||
const save = () => {
|
const save = () => {
|
||||||
const defaultCryptoUnit = R.head(
|
|
||||||
R.keys(coinUtils.getCryptoCurrency(coin).units)
|
|
||||||
)
|
|
||||||
const adjustedData = {
|
const adjustedData = {
|
||||||
zeroConfLimit: 0,
|
zeroConfLimit: 0,
|
||||||
...currentData,
|
...currentData
|
||||||
cryptoUnits: defaultCryptoUnit
|
|
||||||
}
|
}
|
||||||
if (!WalletSchema.isValidSync(adjustedData)) return setError(true)
|
if (!WalletSchema.isValidSync(adjustedData)) return setError(true)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@
|
||||||
"test": "mocha --recursive tests",
|
"test": "mocha --recursive tests",
|
||||||
"jtest": "jest --detectOpenHandles",
|
"jtest": "jest --detectOpenHandles",
|
||||||
"build-admin": "npm run build-admin:css && npm run build-admin:main && npm run build-admin:lamassu",
|
"build-admin": "npm run build-admin:css && npm run build-admin:main && npm run build-admin:lamassu",
|
||||||
"server": "nodemon bin/lamassu-server --mockSms --logLevel silly",
|
"server": "nodemon bin/lamassu-server --mockSms --mockScoring --logLevel silly",
|
||||||
"admin-server": "nodemon bin/lamassu-admin-server --dev --logLevel silly",
|
"admin-server": "nodemon bin/lamassu-admin-server --dev --logLevel silly",
|
||||||
"graphql-server": "nodemon bin/new-graphql-dev-insecure",
|
"graphql-server": "nodemon bin/new-graphql-dev-insecure",
|
||||||
"watch": "concurrently \"npm:server\" \"npm:admin-server\" \"npm:graphql-server\"",
|
"watch": "concurrently \"npm:server\" \"npm:admin-server\" \"npm:graphql-server\"",
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
{
|
{
|
||||||
"files": {
|
"files": {
|
||||||
"main.js": "/static/js/main.589d2bd0.chunk.js",
|
"main.js": "/static/js/main.aa68bc4d.chunk.js",
|
||||||
"main.js.map": "/static/js/main.589d2bd0.chunk.js.map",
|
"main.js.map": "/static/js/main.aa68bc4d.chunk.js.map",
|
||||||
"runtime-main.js": "/static/js/runtime-main.5b925903.js",
|
"runtime-main.js": "/static/js/runtime-main.5b925903.js",
|
||||||
"runtime-main.js.map": "/static/js/runtime-main.5b925903.js.map",
|
"runtime-main.js.map": "/static/js/runtime-main.5b925903.js.map",
|
||||||
"static/js/2.56a90c80.chunk.js": "/static/js/2.56a90c80.chunk.js",
|
"static/js/2.4b3df17b.chunk.js": "/static/js/2.4b3df17b.chunk.js",
|
||||||
"static/js/2.56a90c80.chunk.js.map": "/static/js/2.56a90c80.chunk.js.map",
|
"static/js/2.4b3df17b.chunk.js.map": "/static/js/2.4b3df17b.chunk.js.map",
|
||||||
"index.html": "/index.html",
|
"index.html": "/index.html",
|
||||||
"static/js/2.56a90c80.chunk.js.LICENSE.txt": "/static/js/2.56a90c80.chunk.js.LICENSE.txt",
|
"static/js/2.4b3df17b.chunk.js.LICENSE.txt": "/static/js/2.4b3df17b.chunk.js.LICENSE.txt",
|
||||||
"static/media/3-cassettes-open-1-left.d6d9aa73.svg": "/static/media/3-cassettes-open-1-left.d6d9aa73.svg",
|
"static/media/3-cassettes-open-1-left.d6d9aa73.svg": "/static/media/3-cassettes-open-1-left.d6d9aa73.svg",
|
||||||
"static/media/3-cassettes-open-2-left.a9ee8d4c.svg": "/static/media/3-cassettes-open-2-left.a9ee8d4c.svg",
|
"static/media/3-cassettes-open-2-left.a9ee8d4c.svg": "/static/media/3-cassettes-open-2-left.a9ee8d4c.svg",
|
||||||
"static/media/3-cassettes-open-3-left.08fed660.svg": "/static/media/3-cassettes-open-3-left.08fed660.svg",
|
"static/media/3-cassettes-open-3-left.08fed660.svg": "/static/media/3-cassettes-open-3-left.08fed660.svg",
|
||||||
|
|
@ -153,7 +153,7 @@
|
||||||
},
|
},
|
||||||
"entrypoints": [
|
"entrypoints": [
|
||||||
"static/js/runtime-main.5b925903.js",
|
"static/js/runtime-main.5b925903.js",
|
||||||
"static/js/2.56a90c80.chunk.js",
|
"static/js/2.4b3df17b.chunk.js",
|
||||||
"static/js/main.589d2bd0.chunk.js"
|
"static/js/main.aa68bc4d.chunk.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -1 +1 @@
|
||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><meta name="robots" content="noindex"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="/manifest.json"/><title>Lamassu Admin</title></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root" class="root"></div><script>!function(e){function r(r){for(var n,a,l=r[0],i=r[1],f=r[2],c=0,s=[];c<l.length;c++)a=l[c],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(e[n]=i[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,l=1;l<t.length;l++){var i=t[l];0!==o[i]&&(n=!1)}n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={1:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,function(r){return e[r]}.bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="/";var l=this["webpackJsonplamassu-admin"]=this["webpackJsonplamassu-admin"]||[],i=l.push.bind(l);l.push=r,l=l.slice();for(var f=0;f<l.length;f++)r(l[f]);var p=i;t()}([])</script><script src="/static/js/2.56a90c80.chunk.js"></script><script src="/static/js/main.589d2bd0.chunk.js"></script></body></html>
|
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><meta name="robots" content="noindex"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="/manifest.json"/><title>Lamassu Admin</title></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root" class="root"></div><script>!function(e){function r(r){for(var n,a,l=r[0],i=r[1],f=r[2],c=0,s=[];c<l.length;c++)a=l[c],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(e[n]=i[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,l=1;l<t.length;l++){var i=t[l];0!==o[i]&&(n=!1)}n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={1:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,function(r){return e[r]}.bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="/";var l=this["webpackJsonplamassu-admin"]=this["webpackJsonplamassu-admin"]||[],i=l.push.bind(l);l.push=r,l=l.slice();for(var f=0;f<l.length;f++)r(l[f]);var p=i;t()}([])</script><script src="/static/js/2.4b3df17b.chunk.js"></script><script src="/static/js/main.aa68bc4d.chunk.js"></script></body></html>
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/static/js/main.aa68bc4d.chunk.js
Normal file
2
public/static/js/main.aa68bc4d.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
public/static/js/main.aa68bc4d.chunk.js.map
Normal file
1
public/static/js/main.aa68bc4d.chunk.js.map
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue