diff --git a/lib/cash-in/cash-in-tx.js b/lib/cash-in/cash-in-tx.js index 2da9f8da..57ea8ccf 100644 --- a/lib/cash-in/cash-in-tx.js +++ b/lib/cash-in/cash-in-tx.js @@ -36,8 +36,15 @@ function post (machineTx, pi) { let addressReuse = false let walletScore = {} - return Promise.all([settingsLoader.loadLatest(), checkForBlacklisted(updatedTx), doesTxReuseAddress(updatedTx), getWalletScore(updatedTx, pi)]) - .then(([{ config }, blacklistItems, isReusedAddress, fetchedWalletScore]) => { + const promises = [settingsLoader.loadLatest()] + + const isFirstPost = !r.tx.fiat || r.tx.fiat.isZero() + if (isFirstPost) { + promises.push(checkForBlacklisted(updatedTx), doesTxReuseAddress(updatedTx), getWalletScore(updatedTx, pi)) + } + + return Promise.all(promises) + .then(([{ config }, blacklistItems = false, isReusedAddress = false, fetchedWalletScore = null]) => { const rejectAddressReuse = configManager.getCompliance(config).rejectAddressReuse walletScore = fetchedWalletScore @@ -87,11 +94,7 @@ function logActionById (action, _rec, txId) { } function checkForBlacklisted (tx) { - // Check only on addressScan and avoid testing for blacklist on every bill inserted - if (!tx.fiat || tx.fiat.isZero()) { - return blacklist.blocked(tx.toAddress, tx.cryptoCode) - } - return Promise.resolve(false) + return blacklist.blocked(tx.toAddress, tx.cryptoCode) } function postProcess (r, pi, isBlacklisted, addressReuse, walletScore) { @@ -113,7 +116,7 @@ function postProcess (r, pi, isBlacklisted, addressReuse, walletScore) { return Promise.resolve({ walletScore: walletScore.score, operatorCompleted: true, - error: 'Ciphertrace score is above defined threshold', + error: 'Chain analysis score is above defined threshold', errorCode: 'scoreThresholdReached' }) } @@ -167,32 +170,20 @@ function postProcess (r, pi, isBlacklisted, addressReuse, walletScore) { } function doesTxReuseAddress (tx) { - if (!tx.fiat || tx.fiat.isZero()) { - const sql = ` - 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) + const sql = ` + 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) } function getWalletScore (tx, pi) { return pi.isWalletScoringEnabled(tx) .then(isEnabled => { if (!isEnabled) return null - if (!tx.fiat || tx.fiat.isZero()) { - return pi.rateWallet(tx.cryptoCode, tx.toAddress) - } - // Passthrough the previous result - return pi.isValidWalletScore(tx.walletScore) - .then(isValid => ({ - address: tx.toAddress, - score: tx.walletScore, - isValid - })) + return pi.rateAddress(tx.cryptoCode, tx.toAddress) }) } diff --git a/lib/cash-out/cash-out-tx.js b/lib/cash-out/cash-out-tx.js index f0437f3c..93274e4d 100644 --- a/lib/cash-out/cash-out-tx.js +++ b/lib/cash-out/cash-out-tx.js @@ -117,7 +117,6 @@ function processTxStatus (tx, settings) { } function getWalletScore (tx, pi) { - const rejectEmpty = message => x => _.isNil(x) || _.isEmpty(x) ? Promise.reject(new Error(message)) : x const statuses = ['published', 'authorized', 'confirmed', 'insufficientFunds'] if (!_.includes(tx.status, statuses) || !_.isNil(tx.walletScore)) { @@ -128,20 +127,13 @@ function getWalletScore (tx, pi) { return pi.isWalletScoringEnabled(tx) .then(isEnabled => { if (!isEnabled) return tx - return pi.getTransactionHash(tx) - .then(rejectEmpty('No transaction hashes')) - .then(txHashes => pi.getInputAddresses(tx, txHashes)) - .then(rejectEmpty('No input addresses')) - .then(addresses => Promise.all(_.map(it => pi.rateWallet(tx.cryptoCode, it), addresses))) - .then(rejectEmpty('No score ratings')) - .then(_.maxBy(_.get(['score']))) - .then(highestScore => - // Conservatively assign the highest risk of all input addresses to the risk of this transaction - highestScore.isValid - ? _.assign(tx, { walletScore: highestScore.score }) + return pi.rateTransaction(tx) + .then(res => + res.isValid + ? _.assign(tx, { walletScore: res.score }) : _.assign(tx, { - walletScore: highestScore.score, - error: 'Ciphertrace score is above defined threshold', + walletScore: res.score, + error: 'Chain analysis score is above defined threshold', errorCode: 'scoreThresholdReached', dispense: true }) @@ -149,7 +141,7 @@ function getWalletScore (tx, pi) { .catch(error => _.assign(tx, { walletScore: 10, error: `Failure getting address score: ${error.message}`, - errorCode: 'ciphertraceError', + errorCode: 'walletScoringError', dispense: true })) }) diff --git a/lib/customers.js b/lib/customers.js index 371e5634..071604cd 100644 --- a/lib/customers.js +++ b/lib/customers.js @@ -18,7 +18,7 @@ const sms = require('./sms') const settingsLoader = require('./new-settings-loader') const logger = require('./logger') -const TX_PASSTHROUGH_ERROR_CODES = ['operatorCancel', 'scoreThresholdReached', 'ciphertraceError'] +const TX_PASSTHROUGH_ERROR_CODES = ['operatorCancel', 'scoreThresholdReached', 'walletScoringError'] const ID_PHOTO_CARD_DIR = process.env.ID_PHOTO_CARD_DIR const FRONT_CAMERA_DIR = process.env.FRONT_CAMERA_DIR diff --git a/lib/new-admin/config/accounts.js b/lib/new-admin/config/accounts.js index 36bcee22..0041ba93 100644 --- a/lib/new-admin/config/accounts.js +++ b/lib/new-admin/config/accounts.js @@ -59,7 +59,7 @@ const ALL_ACCOUNTS = [ { code: 'none', display: 'None', class: ZERO_CONF, cryptos: ALL_CRYPTOS }, { 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: 'ciphertrace', display: 'CipherTrace', class: WALLET_SCORING, cryptos: [BTC, ETH, LTC, BCH] }, + { code: 'scorechain', display: 'Scorechain', class: WALLET_SCORING, cryptos: [BTC, ETH, LTC, BCH, DASH, USDT, USDT_TRON, TRX] }, { code: 'mock-scoring', display: 'Mock scoring', class: WALLET_SCORING, cryptos: ALL_CRYPTOS, dev: true } ] diff --git a/lib/plugins.js b/lib/plugins.js index 18aaee5f..fe6b5b79 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -994,20 +994,8 @@ function plugins (settings, deviceId) { .then(buildRates) } - function rateWallet (cryptoCode, address) { - return walletScoring.rateWallet(settings, cryptoCode, address) - } - - function isValidWalletScore (score) { - return walletScoring.isValidWalletScore(settings, score) - } - - function getTransactionHash (tx) { - return walletScoring.getTransactionHash(settings, tx.cryptoCode, tx.toAddress) - } - - function getInputAddresses (tx, txHashes) { - return walletScoring.getInputAddresses(settings, tx.cryptoCode, txHashes) + function rateAddress (cryptoCode, address) { + return walletScoring.rateAddress(settings, cryptoCode, address) } function isWalletScoringEnabled (tx) { @@ -1046,10 +1034,7 @@ function plugins (settings, deviceId) { notifyOperator, fetchCurrentConfigVersion, pruneMachinesHeartbeat, - rateWallet, - isValidWalletScore, - getTransactionHash, - getInputAddresses, + rateAddress, isWalletScoringEnabled, probeLN, buildAvailableUnits diff --git a/lib/plugins/wallet-scoring/ciphertrace/ciphertrace.js b/lib/plugins/wallet-scoring/ciphertrace/ciphertrace.js deleted file mode 100644 index d3922525..00000000 --- a/lib/plugins/wallet-scoring/ciphertrace/ciphertrace.js +++ /dev/null @@ -1,150 +0,0 @@ -const coins = require('@lamassu/coins') -const axios = require('axios') -const _ = require('lodash/fp') - -const logger = require('../../../logger') - -const NAME = 'CipherTrace' -const SUPPORTED_COINS = ['BTC', 'ETH', 'BCH', 'LTC'] - -function getClient (account) { - if (_.isNil(account) || !account.enabled) return null - - const [ctv1, username, secretKey] = account.authorizationValue.split(':') - if (_.isNil(ctv1) || _.isNil(username) || _.isNil(secretKey)) { - throw new Error('Invalid CipherTrace configuration') - } - - const apiVersion = ctv1.slice(-2) - const authHeader = { - 'Authorization': account.authorizationValue - } - return { apiVersion, authHeader } -} - -function rateWallet (account, cryptoCode, address) { - const client = getClient(account) - if (!_.includes(_.toUpper(cryptoCode), SUPPORTED_COINS) || _.isNil(client)) return Promise.resolve(null) - - const { apiVersion, authHeader } = client - const threshold = account.scoreThreshold - - 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}`, { - headers: authHeader - }) - .then(res => ({ address, score: res.data.risk, isValid: res.data.risk < threshold })) - .then(result => { - logger.info(`** DEBUG ** rateWallet RETURN: ${result}`) - return result - }) - .catch(err => { - logger.error(`** DEBUG ** rateWallet ERROR: ${err.message}`) - throw err - }) -} - -function isValidWalletScore (account, score) { - const client = getClient(account) - if (_.isNil(client)) return Promise.resolve(true) - - const threshold = account.scoreThreshold - return _.isNil(account) ? Promise.resolve(true) : Promise.resolve(score < threshold) -} - -function getAddressTransactionsHashes (receivingAddress, cryptoCode, client, wallet) { - const { apiVersion, authHeader } = client - - 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`, { - headers: authHeader - }) - .then(_.flow( - _.get(['data', 'txHistory']), - _.map(_.get(['txHash'])) - )) - .catch(err => { - logger.error(`** DEBUG ** getTransactionHash ERROR: ${err}`) - logger.error(`** DEBUG ** Fetching transactions hashes via wallet node...`) - return wallet.getTxHashesByAddress(cryptoCode, receivingAddress) - }) -} - -function getTransactionHash (account, cryptoCode, receivingAddress, wallet) { - const client = getClient(account) - if (!_.includes(_.toUpper(cryptoCode), SUPPORTED_COINS) || _.isNil(client)) return Promise.resolve(null) - return getAddressTransactionsHashes(receivingAddress, cryptoCode, client, wallet) - .then(txHashes => { - if (_.size(txHashes) > 1) { - logger.warn('An address generated by this wallet was used in more than one transaction') - } - logger.info('** DEBUG ** getTransactionHash RETURN: ', _.join(', ', txHashes)) - return txHashes - }) - .catch(err => { - logger.error('** DEBUG ** getTransactionHash from wallet node ERROR: ', err) - throw err - }) -} - -function getInputAddresses (account, cryptoCode, txHashes) { - const client = getClient(account) - if (_.isEmpty(txHashes) || !_.includes(_.toUpper(cryptoCode), SUPPORTED_COINS) || _.isNil(client)) - return Promise.resolve([]) - - /* NOTE: The API accepts at most 10 hashes, and for us here more than 1 is already an exception, not the norm. */ - if (_.size(txHashes) > 10) - return Promise.reject(new Error("Too many tx hashes -- shouldn't happen!")) - - const { apiVersion, authHeader } = client - - cryptoCode = _.toLower(cryptoCode) - const lastPathComp = cryptoCode !== 'btc' ? cryptoCode + '_tx' : 'tx' - - txHashes = _(txHashes).take(10).join(',') - - const url = `https://rest.ciphertrace.com/api/${apiVersion}/${lastPathComp}?txhashes=${txHashes}` - console.log('** DEBUG ** getInputAddresses ENDPOINT: ', url) - - return axios.get(url, { headers: authHeader }) - .then(res => { - const data = res.data - if (_.size(data.transactions) > 1) { - logger.warn('An address generated by this wallet was used in more than one transaction') - } - - const transactionInputs = _.flatMap(it => it.inputs, data.transactions) - const inputAddresses = _.map(it => it.address, transactionInputs) - - logger.info(`** DEBUG ** getInputAddresses RETURN: ${inputAddresses}`) - - return inputAddresses - }) - .catch(err => { - logger.error(`** DEBUG ** getInputAddresses ERROR: ${err.message}`) - throw err - }) -} - -function isWalletScoringEnabled (account, cryptoCode) { - const isAccountEnabled = !_.isNil(account) && account.enabled - - if (!isAccountEnabled) return Promise.resolve(false) - - if (!SUPPORTED_COINS.includes(cryptoCode) && !coins.utils.isErc20Token(cryptoCode)) { - return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) - } - - return Promise.resolve(true) -} - -module.exports = { - NAME, - rateWallet, - isValidWalletScore, - getTransactionHash, - getInputAddresses, - isWalletScoringEnabled -} diff --git a/lib/plugins/wallet-scoring/mock-scoring/mock-scoring.js b/lib/plugins/wallet-scoring/mock-scoring/mock-scoring.js index 455ff4bf..0f154ffc 100644 --- a/lib/plugins/wallet-scoring/mock-scoring/mock-scoring.js +++ b/lib/plugins/wallet-scoring/mock-scoring/mock-scoring.js @@ -2,7 +2,7 @@ const NAME = 'FakeScoring' const { WALLET_SCORE_THRESHOLD } = require('../../../constants') -function rateWallet (account, cryptoCode, address) { +function rateAddress (account, cryptoCode, address) { return new Promise((resolve, _) => { setTimeout(() => { console.log('[WALLET-SCORING] DEBUG: Mock scoring rating wallet address %s', address) @@ -12,30 +12,6 @@ function rateWallet (account, cryptoCode, address) { }) } -function isValidWalletScore (account, score) { - return new Promise((resolve, _) => { - setTimeout(() => { - return resolve(score < WALLET_SCORE_THRESHOLD) - }, 100) - }) -} - -function getTransactionHash (account, cryptoCode, receivingAddress) { - return new Promise((resolve, _) => { - setTimeout(() => { - return resolve('') - }, 100) - }) -} - -function getInputAddresses (account, cryptoCode, txHashes) { - return new Promise((resolve, _) => { - setTimeout(() => { - return resolve(['']) - }, 100) - }) -} - function isWalletScoringEnabled (account, cryptoCode) { return new Promise((resolve, _) => { setTimeout(() => { @@ -44,12 +20,9 @@ function isWalletScoringEnabled (account, cryptoCode) { }) } - module.exports = { NAME, - rateWallet, - isValidWalletScore, - getTransactionHash, - getInputAddresses, + rateAddress, + rateTransaction:rateAddress, isWalletScoringEnabled } diff --git a/lib/plugins/wallet-scoring/scorechain/scorechain.js b/lib/plugins/wallet-scoring/scorechain/scorechain.js new file mode 100644 index 00000000..dd0a82f2 --- /dev/null +++ b/lib/plugins/wallet-scoring/scorechain/scorechain.js @@ -0,0 +1,74 @@ +const axios = require('axios') +const _ = require('lodash/fp') + + +const NAME = 'Scorechain' +const SUPPORTED_COINS = { + BTC: 'BITCOIN', + ETH: 'ETHEREUM', + USDT: 'ETHEREUM', + BCH: 'BITCOINCASH', + LTC: 'LITECOIN', + DASH: 'DASH', + TRX: 'TRON', + USDT_TRON: 'TRON' +} + +const TYPE = { + TRANSACTION: 'TRANSACTION', + ADDRESS: 'ADDRESS' +} + +function rate (account, objectType, cryptoCode, objectId) { + if (_.isNil(account) || !account.enabled || !Object.keys(SUPPORTED_COINS).includes(cryptoCode)) return Promise.resolve(null) + + const threshold = account.scoreThreshold + const payload = { + analysisType: 'ASSIGNED', + objectType, + objectId, + blockchain: SUPPORTED_COINS[cryptoCode], + coin: "ALL" + } + + return axios.post(`https://api.scorechain.com/v1/scoringAnalysis`, payload, { headers: { 'X-API-KEY': account.apiKey } + }) + .then(res => { + const resScore = res.data?.analysis?.assigned?.result?.score + if (!resScore) throw new Error('Failed to get score from Scorechain API') + + // normalize score to 0-10 where 0 is the highest risk + // use 101 instead of 100 to avoid division by zero + return { score: (101 - resScore) / 10 - 0.1, isValid: resScore >= threshold } + }) + .catch(err => { + throw err + }) +} + +function rateTransaction (account, cryptoCode, transactionId) { + rate(account, TYPE.TRANSACTION, cryptoCode, transactionId) +} + +function rateAddress (account, cryptoCode, address) { + 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 +} diff --git a/lib/routes/txRoutes.js b/lib/routes/txRoutes.js index a3261147..f63755a0 100644 --- a/lib/routes/txRoutes.js +++ b/lib/routes/txRoutes.js @@ -22,7 +22,7 @@ function postTx (req, res, next) { throw httpError(tx.error, 570) case 'scoreThresholdReached': throw httpError(tx.error, 571) - case 'ciphertraceError': + case 'walletScoringError': throw httpError(tx.error, 572) default: throw httpError(tx.error, 500) diff --git a/lib/tx.js b/lib/tx.js index ab07e0c0..219ec5cf 100644 --- a/lib/tx.js +++ b/lib/tx.js @@ -82,7 +82,7 @@ function customerHistory (customerId, thresholdDays) { FROM cash_out_txs txOut WHERE txOut.customer_id = $1 AND txOut.created > now() - interval $2 - AND (error_code IS NULL OR error_code NOT IN ('operatorCancel', 'scoreThresholdReached', 'ciphertraceError')) + AND (error_code IS NULL OR error_code NOT IN ('operatorCancel', 'scoreThresholdReached', 'walletScoringError', 'ciphertraceError')) AND fiat > 0 ) ch WHERE NOT ch.expired ORDER BY ch.created` diff --git a/lib/wallet-scoring.js b/lib/wallet-scoring.js index 74de6130..516ac19b 100644 --- a/lib/wallet-scoring.js +++ b/lib/wallet-scoring.js @@ -3,7 +3,7 @@ const argv = require('minimist')(process.argv.slice(2)) const configManager = require('./new-config-manager') function loadWalletScoring (settings, cryptoCode) { - const pluginCode = argv.mockScoring ? 'mock-scoring' : 'ciphertrace' + const pluginCode = argv.mockScoring ? 'mock-scoring' : 'scorechain' const wallet = cryptoCode ? ph.load(ph.WALLET, configManager.getWalletSettings(cryptoCode, settings.config).wallet) : null const plugin = ph.load(ph.WALLET_SCORING, pluginCode) const account = settings.accounts[pluginCode] @@ -11,39 +11,21 @@ function loadWalletScoring (settings, cryptoCode) { return { plugin, account, wallet } } -function rateWallet (settings, cryptoCode, address) { +function rateTransaction (settings, cryptoCode, address) { return Promise.resolve() .then(() => { const { plugin, account } = loadWalletScoring(settings) - return plugin.rateWallet(account, cryptoCode, address) + return plugin.rateAddress(account, cryptoCode, address) }) } -function isValidWalletScore (settings, score) { +function rateAddress (settings, cryptoCode, address) { return Promise.resolve() .then(() => { const { plugin, account } = loadWalletScoring(settings) - return plugin.isValidWalletScore(account, score) - }) -} - -function getTransactionHash (settings, cryptoCode, receivingAddress) { - return Promise.resolve() - .then(() => { - const { plugin, account, wallet } = loadWalletScoring(settings, cryptoCode) - - return plugin.getTransactionHash(account, cryptoCode, receivingAddress, wallet) - }) -} - -function getInputAddresses (settings, cryptoCode, txHashes) { - return Promise.resolve() - .then(() => { - const { plugin, account } = loadWalletScoring(settings) - - return plugin.getInputAddresses(account, cryptoCode, txHashes) + return plugin.rateAddress(account, cryptoCode, address) }) } @@ -57,9 +39,7 @@ function isWalletScoringEnabled (settings, cryptoCode) { } module.exports = { - rateWallet, - isValidWalletScore, - getTransactionHash, - getInputAddresses, + rateAddress, + rateTransaction, isWalletScoringEnabled } diff --git a/new-lamassu-admin/src/pages/Services/schemas/index.js b/new-lamassu-admin/src/pages/Services/schemas/index.js index e077222a..fdcf2097 100644 --- a/new-lamassu-admin/src/pages/Services/schemas/index.js +++ b/new-lamassu-admin/src/pages/Services/schemas/index.js @@ -5,12 +5,12 @@ import bitgo from './bitgo' import bitstamp from './bitstamp' import blockcypher from './blockcypher' import cex from './cex' -import ciphertrace from './ciphertrace' import galoy from './galoy' import infura from './infura' import itbit from './itbit' import kraken from './kraken' import mailgun from './mailgun' +import scorechain from './scorechain' import telnyx from './telnyx' import trongrid from './trongrid' import twilio from './twilio' @@ -30,7 +30,7 @@ export default { [twilio.code]: twilio, [binanceus.code]: binanceus, [cex.code]: cex, - [ciphertrace.code]: ciphertrace, + [scorechain.code]: scorechain, [trongrid.code]: trongrid, [binance.code]: binance, [bitfinex.code]: bitfinex diff --git a/new-lamassu-admin/src/pages/Services/schemas/ciphertrace.js b/new-lamassu-admin/src/pages/Services/schemas/scorechain.js similarity index 69% rename from new-lamassu-admin/src/pages/Services/schemas/ciphertrace.js rename to new-lamassu-admin/src/pages/Services/schemas/scorechain.js index d48c8e41..ed991692 100644 --- a/new-lamassu-admin/src/pages/Services/schemas/ciphertrace.js +++ b/new-lamassu-admin/src/pages/Services/schemas/scorechain.js @@ -7,13 +7,13 @@ import SecretInputFormik from 'src/components/inputs/formik/SecretInput' import { secretTest, leadingZerosTest } from './helper' export default { - code: 'ciphertrace', - name: 'CipherTrace', - title: 'CipherTrace (Scoring)', + code: 'scorechain', + name: 'Scorechain', + title: 'Scorechain (Scoring)', elements: [ { - code: 'authorizationValue', - display: 'Authorization value', + code: 'apikey', + display: 'API Key', component: SecretInputFormik }, { @@ -29,8 +29,7 @@ export default { settings: { enabled: true, disabledMessage: 'This plugin is disabled', - label: - 'Enabled (Supported coins: BTC, ETH, BCH, LTC and all active ERC-20 tokens)', + label: 'Enabled', requirement: null, rightSideLabel: true }, @@ -39,13 +38,13 @@ export default { ], getValidationSchema: account => { return Yup.object().shape({ - authorizationValue: Yup.string('The authorization value must be a string') + apiKey: Yup.string('The API key must be a string') .max(100, 'Too long') - .test(secretTest(account?.authorizationValue, 'authorization value')), + .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 10') - .max(10, 'The score threshold must be between 1 and 10') + .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', diff --git a/new-lamassu-admin/src/pages/Transactions/DetailsCard.js b/new-lamassu-admin/src/pages/Transactions/DetailsCard.js index 1cfeacc9..74c5451f 100644 --- a/new-lamassu-admin/src/pages/Transactions/DetailsCard.js +++ b/new-lamassu-admin/src/pages/Transactions/DetailsCard.js @@ -183,9 +183,13 @@ const DetailsRow = ({ it: tx, timezone }) => { FileSaver.saveAs(content, zipFilename) } - const hasCiphertraceError = tx => + const hasChainAnalysisError = tx => !R.isNil(tx.errorCode) && - R.includes(tx.errorCode, ['scoreThresholdReached', 'ciphertraceError']) + R.includes(tx.errorCode, [ + 'scoreThresholdReached', + 'walletScoringError', + 'ciphertraceError' + ]) const errorElements = ( <> @@ -205,10 +209,10 @@ const DetailsRow = ({ it: tx, timezone }) => { r={3.5} fill={ it < tx.walletScore - ? !hasCiphertraceError(tx) + ? !hasChainAnalysisError(tx) ? primaryColor : errorColor - : !hasCiphertraceError(tx) + : !hasChainAnalysisError(tx) ? subheaderColor : offErrorColor } @@ -221,7 +225,7 @@ const DetailsRow = ({ it: tx, timezone }) => { noMargin className={classNames({ [classes.bold]: true, - [classes.error]: hasCiphertraceError(tx) + [classes.error]: hasChainAnalysisError(tx) })}> {tx.walletScore}

@@ -359,7 +363,7 @@ const DetailsRow = ({ it: tx, timezone }) => { {!R.isNil(tx.walletScore) && ( - {`CipherTrace score: ${tx.walletScore}/10`} + {`Chain analysis score: ${tx.walletScore}/10`} )} diff --git a/package-lock.json b/package-lock.json index 358c3058..03365f19 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1148,6 +1148,25 @@ "fastpriorityqueue": "^0.7.1", "typeforce": "^1.11.3", "varuint-bitcoin": "^1.1.2" + }, + "dependencies": { + "bitcoinjs-lib": { + "version": "npm:@bitgo-forks/bitcoinjs-lib@7.1.0-master.7", + "resolved": "https://registry.npmjs.org/@bitgo-forks/bitcoinjs-lib/-/bitcoinjs-lib-7.1.0-master.7.tgz", + "integrity": "sha512-FZle7954KnbbVXFCc5uYGtjq+0PFOnFxVchNwt3Kcv2nVusezTp29aeQwDi2Y+lM1dCoup2gJGXMkkREenY7KQ==", + "requires": { + "bech32": "^2.0.0", + "bip174": "npm:@bitgo-forks/bip174@3.1.0-master.4", + "bs58check": "^2.1.2", + "create-hash": "^1.1.0", + "fastpriorityqueue": "^0.7.1", + "json5": "^2.2.3", + "ripemd160": "^2.0.2", + "typeforce": "^1.11.3", + "varuint-bitcoin": "^1.1.2", + "wif": "^2.0.1" + } + } } }, "@bitgo/utxo-ord": { @@ -4610,23 +4629,6 @@ "resolved": "https://registry.npmjs.org/bitcoin-ops/-/bitcoin-ops-1.4.1.tgz", "integrity": "sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow==" }, - "bitcoinjs-lib": { - "version": "npm:@bitgo-forks/bitcoinjs-lib@7.1.0-master.7", - "resolved": "https://registry.npmjs.org/@bitgo-forks/bitcoinjs-lib/-/bitcoinjs-lib-7.1.0-master.7.tgz", - "integrity": "sha512-FZle7954KnbbVXFCc5uYGtjq+0PFOnFxVchNwt3Kcv2nVusezTp29aeQwDi2Y+lM1dCoup2gJGXMkkREenY7KQ==", - "requires": { - "bech32": "^2.0.0", - "bip174": "npm:@bitgo-forks/bip174@3.1.0-master.4", - "bs58check": "^2.1.2", - "create-hash": "^1.1.0", - "fastpriorityqueue": "^0.7.1", - "json5": "^2.2.3", - "ripemd160": "^2.0.2", - "typeforce": "^1.11.3", - "varuint-bitcoin": "^1.1.2", - "wif": "^2.0.1" - } - }, "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",