Merge pull request #1713 from RafaelTaranto/feat/elliptic-wallet-scoring
LAM-1145 feat: elliptic wallet scoring
This commit is contained in:
commit
543b09b084
9 changed files with 298 additions and 69 deletions
|
|
@ -62,6 +62,7 @@ const ALL_ACCOUNTS = [
|
|||
{ code: 'blockcypher', display: 'Blockcypher', class: ZERO_CONF, cryptos: [BTC] },
|
||||
{ code: 'mock-zero-conf', display: 'Mock 0-conf', class: ZERO_CONF, cryptos: ALL_CRYPTOS, dev: true },
|
||||
{ code: 'scorechain', display: 'Scorechain', class: WALLET_SCORING, cryptos: [BTC, ETH, LTC, BCH, DASH, USDT, USDT_TRON, TRX] },
|
||||
{ code: 'elliptic', display: 'Elliptic', class: WALLET_SCORING, cryptos: [BTC, ETH, LTC, BCH, USDT, USDT_TRON, TRX, ZEC] },
|
||||
{ code: 'mock-scoring', display: 'Mock scoring', class: WALLET_SCORING, cryptos: ALL_CRYPTOS, dev: true },
|
||||
{ code: 'sumsub', display: 'Sumsub', class: COMPLIANCE },
|
||||
{ code: 'mock-compliance', display: 'Mock Compliance', class: COMPLIANCE, dev: true },
|
||||
|
|
|
|||
|
|
@ -63,6 +63,16 @@ function saveAccounts (accounts) {
|
|||
return Promise.all([loadAccounts(), getOperatorId('middleware')])
|
||||
.then(([currentAccounts, operatorId]) => {
|
||||
const newAccounts = _.merge(currentAccounts, accounts)
|
||||
|
||||
// Only allow one wallet scoring active at a time
|
||||
if (accounts.elliptic?.enabled && newAccounts.scorechain) {
|
||||
newAccounts.scorechain.enabled = false
|
||||
}
|
||||
|
||||
if (accounts.scorechain?.enabled && newAccounts.elliptic) {
|
||||
newAccounts.elliptic.enabled = false
|
||||
}
|
||||
|
||||
return db.tx(t => {
|
||||
return t.none(accountsSql, ['accounts', { accounts: newAccounts }, true, NEW_SETTINGS_LOADER_SCHEMA_VERSION])
|
||||
.then(() => t.none('NOTIFY $1:name, $2', ['reload', JSON.stringify({ schema: asyncLocalStorage.getStore().get('schema'), operatorId })]))
|
||||
|
|
|
|||
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
|
||||
}
|
||||
|
|
@ -1,14 +1,23 @@
|
|||
const ph = require('./plugin-helper')
|
||||
const argv = require('minimist')(process.argv.slice(2))
|
||||
const configManager = require('./new-config-manager')
|
||||
|
||||
function loadWalletScoring (settings, cryptoCode) {
|
||||
const pluginCode = argv.mockScoring ? 'mock-scoring' : 'scorechain'
|
||||
const wallet = cryptoCode ? ph.load(ph.WALLET, configManager.getWalletSettings(cryptoCode, settings.config).wallet) : null
|
||||
const plugin = ph.load(ph.WALLET_SCORING, pluginCode)
|
||||
const account = settings.accounts[pluginCode]
|
||||
// TODO - This function should be rolled back after UI is created for this feature
|
||||
function loadWalletScoring (settings) {
|
||||
if (argv.mockScoring) {
|
||||
const mock = ph.load(ph.WALLET_SCORING, 'mock-scoring')
|
||||
return { plugin: mock, account: {} }
|
||||
}
|
||||
|
||||
return { plugin, account, wallet }
|
||||
const scorechainAccount = settings.accounts['scorechain']
|
||||
if (scorechainAccount?.enabled) {
|
||||
const scorechain = ph.load(ph.WALLET_SCORING, 'scorechain')
|
||||
return { plugin: scorechain, account: scorechainAccount}
|
||||
}
|
||||
|
||||
const ellipticAccount = settings.accounts['elliptic']
|
||||
const elliptic = ph.load(ph.WALLET_SCORING, 'elliptic')
|
||||
|
||||
return { plugin: elliptic, account: ellipticAccount }
|
||||
}
|
||||
|
||||
function rateTransaction (settings, cryptoCode, address) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue