Merge branch 'dev' into fix/lam-266/profits-calculation

This commit is contained in:
André Sá 2022-02-04 18:25:06 +00:00
commit c985458bff
33 changed files with 95 additions and 114 deletions

View file

@ -3,7 +3,7 @@
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')
const coinUtils = require('../lib/coin-utils') const { utils: coinUtils } = require('lamassu-coins')
const BN = require('../lib/bn') const BN = require('../lib/bn')
const inquirer = require('inquirer') const inquirer = require('inquirer')
const ticker = require('../lib/ticker') const ticker = require('../lib/ticker')

View file

@ -47,6 +47,7 @@ const typeDef = gql`
txCustomerPhotoAt: Date txCustomerPhotoAt: Date
batched: Boolean batched: Boolean
batchTime: Date batchTime: Date
batchError: String
walletScore: Int walletScore: Int
} }

View file

@ -4,6 +4,7 @@ const settingsLoader = require('../../new-settings-loader')
const configManager = require('../../new-config-manager') const configManager = require('../../new-config-manager')
const wallet = require('../../wallet') const wallet = require('../../wallet')
const ticker = require('../../ticker') const ticker = require('../../ticker')
const txBatching = require('../../tx-batching')
const { utils: coinUtils } = require('lamassu-coins') const { utils: coinUtils } = require('lamassu-coins')
function computeCrypto (cryptoCode, _balance) { function computeCrypto (cryptoCode, _balance) {
@ -23,16 +24,17 @@ function computeFiat (rate, cryptoCode, _balance) {
function getSingleCoinFunding (settings, fiatCode, cryptoCode) { function getSingleCoinFunding (settings, fiatCode, cryptoCode) {
const promises = [ const promises = [
wallet.newFunding(settings, cryptoCode), wallet.newFunding(settings, cryptoCode),
ticker.getRates(settings, fiatCode, cryptoCode) ticker.getRates(settings, fiatCode, cryptoCode),
txBatching.getOpenBatchCryptoValue(cryptoCode)
] ]
return Promise.all(promises) return Promise.all(promises)
.then(([fundingRec, ratesRec]) => { .then(([fundingRec, ratesRec, batchRec]) => {
const rates = ratesRec.rates const rates = ratesRec.rates
const rate = (rates.ask.plus(rates.bid)).div(2) const rate = (rates.ask.plus(rates.bid)).div(2)
const fundingConfirmedBalance = fundingRec.fundingConfirmedBalance const fundingConfirmedBalance = fundingRec.fundingConfirmedBalance
const fiatConfirmedBalance = computeFiat(rate, cryptoCode, fundingConfirmedBalance) const fiatConfirmedBalance = computeFiat(rate, cryptoCode, fundingConfirmedBalance)
const pending = fundingRec.fundingPendingBalance const pending = fundingRec.fundingPendingBalance.minus(batchRec)
const fiatPending = computeFiat(rate, cryptoCode, pending) const fiatPending = computeFiat(rate, cryptoCode, pending)
const fundingAddress = fundingRec.fundingAddress const fundingAddress = fundingRec.fundingAddress
const fundingAddressUrl = coinUtils.buildUrl(cryptoCode, fundingAddress) const fundingAddressUrl = coinUtils.buildUrl(cryptoCode, fundingAddress)

View file

@ -54,10 +54,12 @@ function batch (
c.id_card_photo_path AS customer_id_card_photo_path, c.id_card_photo_path AS customer_id_card_photo_path,
txs.tx_customer_photo_at AS tx_customer_photo_at, txs.tx_customer_photo_at AS tx_customer_photo_at,
txs.tx_customer_photo_path AS tx_customer_photo_path, txs.tx_customer_photo_path AS tx_customer_photo_path,
((NOT txs.send_confirmed) AND (txs.created <= now() - interval $1)) AS expired ((NOT txs.send_confirmed) AND (txs.created <= now() - interval $1)) AS expired,
tb.error_message AS batch_error
FROM (SELECT *, ${cashInTx.TRANSACTION_STATES} AS txStatus FROM cash_in_txs) AS txs FROM (SELECT *, ${cashInTx.TRANSACTION_STATES} AS txStatus FROM cash_in_txs) AS txs
LEFT OUTER JOIN customers c ON txs.customer_id = c.id LEFT OUTER JOIN customers c ON txs.customer_id = c.id
LEFT JOIN devices d ON txs.device_id = d.device_id LEFT JOIN devices d ON txs.device_id = d.device_id
LEFT OUTER JOIN transaction_batches tb ON txs.batch_id = tb.id
WHERE txs.created >= $2 AND txs.created <= $3 ${ WHERE txs.created >= $2 AND txs.created <= $3 ${
id !== null ? `AND txs.device_id = $6` : `` id !== null ? `AND txs.device_id = $6` : ``
} }
@ -69,7 +71,7 @@ function batch (
AND ($12 is null or txs.to_address = $12) AND ($12 is null or txs.to_address = $12)
AND ($13 is null or txs.txStatus = $13) AND ($13 is null or txs.txStatus = $13)
${excludeTestingCustomers ? `AND c.is_test_customer is false` : ``} ${excludeTestingCustomers ? `AND c.is_test_customer is false` : ``}
AND (error IS NOT null OR fiat > 0) AND (error IS NOT null OR tb.error_message IS NOT null OR fiat > 0)
ORDER BY created DESC limit $4 offset $5` ORDER BY created DESC limit $4 offset $5`
const cashOutSql = `SELECT 'cashOut' AS tx_class, const cashOutSql = `SELECT 'cashOut' AS tx_class,
@ -158,6 +160,7 @@ const getCashOutStatus = it => {
const getCashInStatus = it => { const getCashInStatus = it => {
if (it.operatorCompleted) return 'Cancelled' if (it.operatorCompleted) return 'Cancelled'
if (it.hasError) return 'Error' if (it.hasError) return 'Error'
if (it.batchError) return 'Error'
if (it.sendConfirmed) return 'Sent' if (it.sendConfirmed) return 'Sent'
if (it.expired) return 'Expired' if (it.expired) return 'Expired'
return 'Pending' return 'Pending'
@ -183,9 +186,11 @@ function getCustomerTransactionsBatch (ids) {
c.name AS customer_name, c.name AS customer_name,
c.front_camera_path AS customer_front_camera_path, c.front_camera_path AS customer_front_camera_path,
c.id_card_photo_path AS customer_id_card_photo_path, c.id_card_photo_path AS customer_id_card_photo_path,
((NOT txs.send_confirmed) AND (txs.created <= now() - interval $2)) AS expired ((NOT txs.send_confirmed) AND (txs.created <= now() - interval $2)) AS expired,
tb.error_message AS batch_error
FROM cash_in_txs AS txs FROM cash_in_txs AS txs
LEFT OUTER JOIN customers c ON txs.customer_id = c.id LEFT OUTER JOIN customers c ON txs.customer_id = c.id
LEFT OUTER JOIN transaction_batches tb ON txs.batch_id = tb.id
WHERE c.id IN ($1^) WHERE c.id IN ($1^)
ORDER BY created DESC limit $3` ORDER BY created DESC limit $3`
@ -227,9 +232,11 @@ function single (txId) {
c.name AS customer_name, c.name AS customer_name,
c.front_camera_path AS customer_front_camera_path, c.front_camera_path AS customer_front_camera_path,
c.id_card_photo_path AS customer_id_card_photo_path, c.id_card_photo_path AS customer_id_card_photo_path,
((NOT txs.send_confirmed) AND (txs.created <= now() - interval $1)) AS expired ((NOT txs.send_confirmed) AND (txs.created <= now() - interval $1)) AS expired,
tb.error_message AS batch_error
FROM cash_in_txs AS txs FROM cash_in_txs AS txs
LEFT OUTER JOIN customers c ON txs.customer_id = c.id LEFT OUTER JOIN customers c ON txs.customer_id = c.id
LEFT OUTER JOIN transaction_batches tb ON txs.batch_id = tb.id
WHERE id=$2` WHERE id=$2`
const cashOutSql = `SELECT 'cashOut' AS tx_class, const cashOutSql = `SELECT 'cashOut' AS tx_class,

View file

@ -8,8 +8,6 @@ const { utils: coinUtils } = require('lamassu-coins')
const cryptoRec = coinUtils.getCryptoCurrency('BCH') const cryptoRec = coinUtils.getCryptoCurrency('BCH')
const unitScale = cryptoRec.unitScale const unitScale = cryptoRec.unitScale
const SUPPORTS_BATCHING = false
const rpcConfig = jsonRpc.rpcConfig(cryptoRec) const rpcConfig = jsonRpc.rpcConfig(cryptoRec)
function fetch (method, params) { function fetch (method, params) {
@ -118,11 +116,6 @@ function cryptoNetwork (account, cryptoCode, settings, operatorId) {
.then(() => parseInt(rpcConfig.port, 10) === 18332 ? 'test' : 'main') .then(() => parseInt(rpcConfig.port, 10) === 18332 ? 'test' : 'main')
} }
function supportsBatching (cryptoCode) {
return checkCryptoCode(cryptoCode)
.then(() => SUPPORTS_BATCHING)
}
function checkBlockchainStatus (cryptoCode) { function checkBlockchainStatus (cryptoCode) {
return checkCryptoCode(cryptoCode) return checkCryptoCode(cryptoCode)
.then(() => fetch('getblockchaininfo')) .then(() => fetch('getblockchaininfo'))
@ -136,6 +129,5 @@ module.exports = {
getStatus, getStatus,
newFunding, newFunding,
cryptoNetwork, cryptoNetwork,
supportsBatching,
checkBlockchainStatus checkBlockchainStatus
} }

View file

@ -9,7 +9,6 @@ const { utils: coinUtils } = require('lamassu-coins')
const cryptoRec = coinUtils.getCryptoCurrency('BTC') const cryptoRec = coinUtils.getCryptoCurrency('BTC')
const unitScale = cryptoRec.unitScale const unitScale = cryptoRec.unitScale
const SUPPORTS_BATCHING = true
const rpcConfig = jsonRpc.rpcConfig(cryptoRec) const rpcConfig = jsonRpc.rpcConfig(cryptoRec)
function fetch (method, params) { function fetch (method, params) {
@ -80,19 +79,19 @@ function sendCoins (account, tx, settings, operatorId, feeMultiplier) {
}) })
} }
function sendCoinsBatch (account, txs, cryptoCode) { function sendCoinsBatch (account, txs, cryptoCode, feeMultiplier) {
return checkCryptoCode(cryptoCode) return checkCryptoCode(cryptoCode)
.then(() => calculateFeeDiscount(feeMultiplier)) .then(() => calculateFeeDiscount(feeMultiplier))
.then(newFee => fetch('settxfee', [newFee])) .then(newFee => fetch('settxfee', [newFee]))
.then(() => { .then(() => {
const txAddressAmountPairs = _.map(tx => [tx.address, tx.cryptoAtoms.shift(-unitScale).toFixed(8)], txs) const txAddressAmountPairs = _.map(tx => [tx.address, tx.cryptoAtoms.shiftedBy(-unitScale).toFixed(8)], txs)
return Promise.all([JSON.stringify(_.fromPairs(txAddressAmountPairs))]) return Promise.all([JSON.stringify(_.fromPairs(txAddressAmountPairs))])
}) })
.then(([obj]) => fetch('sendmany', ['', obj])) .then(([obj]) => fetch('sendmany', ['', obj]))
.then((txId) => fetch('gettransaction', [txId])) .then((txId) => fetch('gettransaction', [txId]))
.then((res) => _.pick(['fee', 'txid'], res)) .then((res) => _.pick(['fee', 'txid'], res))
.then((pickedObj) => ({ .then((pickedObj) => ({
fee: BN(pickedObj.fee).abs().shift(unitScale).round(), fee: new BN(pickedObj.fee).abs().shiftedBy(unitScale).decimalPlaces(0),
txid: pickedObj.txid txid: pickedObj.txid
})) }))
.catch(err => { .catch(err => {
@ -171,11 +170,6 @@ function fetchRBF (txId) {
}) })
} }
function supportsBatching (cryptoCode) {
return checkCryptoCode(cryptoCode)
.then(() => SUPPORTS_BATCHING)
}
function checkBlockchainStatus (cryptoCode) { function checkBlockchainStatus (cryptoCode) {
return checkCryptoCode(cryptoCode) return checkCryptoCode(cryptoCode)
.then(() => fetch('getblockchaininfo')) .then(() => fetch('getblockchaininfo'))
@ -192,6 +186,5 @@ module.exports = {
fetchRBF, fetchRBF,
estimateFee, estimateFee,
sendCoinsBatch, sendCoinsBatch,
supportsBatching,
checkBlockchainStatus checkBlockchainStatus
} }

View file

@ -14,8 +14,6 @@ const NAME = 'BitGo'
const SUPPORTED_COINS = ['BTC', 'ZEC', 'LTC', 'BCH', 'DASH'] const SUPPORTED_COINS = ['BTC', 'ZEC', 'LTC', 'BCH', 'DASH']
const BCH_CODES = ['BCH', 'TBCH'] const BCH_CODES = ['BCH', 'TBCH']
const SUPPORTS_BATCHING = false
function buildBitgo (account) { function buildBitgo (account) {
const env = account.environment === 'test' ? 'test' : 'prod' const env = account.environment === 'test' ? 'test' : 'prod'
return new BitGo.BitGo({ accessToken: account.token.trim(), env, userAgent: userAgent }) return new BitGo.BitGo({ accessToken: account.token.trim(), env, userAgent: userAgent })
@ -159,11 +157,6 @@ function cryptoNetwork (account, cryptoCode, settings, operatorId) {
.then(() => account.environment === 'test' ? 'test' : 'main') .then(() => account.environment === 'test' ? 'test' : 'main')
} }
function supportsBatching (cryptoCode) {
return checkCryptoCode(cryptoCode)
.then(() => SUPPORTS_BATCHING)
}
function checkBlockchainStatus (cryptoCode) { function checkBlockchainStatus (cryptoCode) {
return checkCryptoCode(cryptoCode) return checkCryptoCode(cryptoCode)
.then(() => Promise.resolve('ready')) .then(() => Promise.resolve('ready'))
@ -177,6 +170,5 @@ module.exports = {
getStatus, getStatus,
newFunding, newFunding,
cryptoNetwork, cryptoNetwork,
supportsBatching,
checkBlockchainStatus checkBlockchainStatus
} }

View file

@ -9,7 +9,6 @@ const E = require('../../../error')
const cryptoRec = coinUtils.getCryptoCurrency('DASH') const cryptoRec = coinUtils.getCryptoCurrency('DASH')
const unitScale = cryptoRec.unitScale const unitScale = cryptoRec.unitScale
const SUPPORTS_BATCHING = false
const rpcConfig = jsonRpc.rpcConfig(cryptoRec) const rpcConfig = jsonRpc.rpcConfig(cryptoRec)
function fetch (method, params) { function fetch (method, params) {
@ -113,11 +112,6 @@ function newFunding (account, cryptoCode, settings, operatorId) {
})) }))
} }
function supportsBatching (cryptoCode) {
return checkCryptoCode(cryptoCode)
.then(() => SUPPORTS_BATCHING)
}
function checkBlockchainStatus (cryptoCode) { function checkBlockchainStatus (cryptoCode) {
return checkCryptoCode(cryptoCode) return checkCryptoCode(cryptoCode)
.then(() => fetch('getblockchaininfo')) .then(() => fetch('getblockchaininfo'))
@ -130,6 +124,5 @@ module.exports = {
newAddress, newAddress,
getStatus, getStatus,
newFunding, newFunding,
supportsBatching,
checkBlockchainStatus checkBlockchainStatus
} }

View file

@ -17,8 +17,6 @@ const paymentPrefixPath = "m/44'/60'/0'/0'"
const defaultPrefixPath = "m/44'/60'/1'/0'" const defaultPrefixPath = "m/44'/60'/1'/0'"
let lastUsedNonces = {} let lastUsedNonces = {}
const SUPPORTS_BATCHING = false
module.exports = { module.exports = {
NAME, NAME,
balance, balance,
@ -32,7 +30,6 @@ module.exports = {
privateKey, privateKey,
isStrictAddress, isStrictAddress,
connect, connect,
supportsBatching,
checkBlockchainStatus checkBlockchainStatus
} }
@ -227,11 +224,6 @@ function newFunding (account, cryptoCode, settings, operatorId) {
}) })
} }
function supportsBatching (cryptoCode) {
return checkCryptoCode(cryptoCode)
.then(() => SUPPORTS_BATCHING)
}
function checkBlockchainStatus (cryptoCode) { function checkBlockchainStatus (cryptoCode) {
return checkCryptoCode(cryptoCode) return checkCryptoCode(cryptoCode)
.then(pify(web3.eth.isSyncing)) .then(pify(web3.eth.isSyncing))

View file

@ -9,7 +9,6 @@ const E = require('../../../error')
const cryptoRec = coinUtils.getCryptoCurrency('LTC') const cryptoRec = coinUtils.getCryptoCurrency('LTC')
const unitScale = cryptoRec.unitScale const unitScale = cryptoRec.unitScale
const SUPPORTS_BATCHING = false
const rpcConfig = jsonRpc.rpcConfig(cryptoRec) const rpcConfig = jsonRpc.rpcConfig(cryptoRec)
function fetch (method, params) { function fetch (method, params) {
@ -113,11 +112,6 @@ function newFunding (account, cryptoCode, settings, operatorId) {
})) }))
} }
function supportsBatching (cryptoCode) {
return checkCryptoCode(cryptoCode)
.then(() => SUPPORTS_BATCHING)
}
function checkBlockchainStatus (cryptoCode) { function checkBlockchainStatus (cryptoCode) {
return checkCryptoCode(cryptoCode) return checkCryptoCode(cryptoCode)
.then(() => fetch('getblockchaininfo')) .then(() => fetch('getblockchaininfo'))
@ -130,6 +124,5 @@ module.exports = {
newAddress, newAddress,
getStatus, getStatus,
newFunding, newFunding,
supportsBatching,
checkBlockchainStatus checkBlockchainStatus
} }

View file

@ -5,7 +5,6 @@ const E = require('../../../error')
const { utils: coinUtils } = require('lamassu-coins') const { utils: coinUtils } = require('lamassu-coins')
const NAME = 'FakeWallet' const NAME = 'FakeWallet'
const BATCHABLE_COINS = ['BTC']
const SECONDS = 1000 const SECONDS = 1000
const PUBLISH_TIME = 3 * SECONDS const PUBLISH_TIME = 3 * SECONDS
@ -111,10 +110,6 @@ function getStatus (account, tx, requested, settings, operatorId) {
return Promise.resolve({status: 'confirmed'}) return Promise.resolve({status: 'confirmed'})
} }
function supportsBatching (cryptoCode) {
return Promise.resolve(_.includes(cryptoCode, BATCHABLE_COINS))
}
function checkBlockchainStatus (cryptoCode) { function checkBlockchainStatus (cryptoCode) {
return checkCryptoCode(cryptoCode) return checkCryptoCode(cryptoCode)
.then(() => Promise.resolve('ready')) .then(() => Promise.resolve('ready'))
@ -128,6 +123,5 @@ module.exports = {
newAddress, newAddress,
getStatus, getStatus,
newFunding, newFunding,
supportsBatching,
checkBlockchainStatus checkBlockchainStatus
} }

View file

@ -17,8 +17,6 @@ const configPath = utils.configPath(cryptoRec, blockchainDir)
const walletDir = path.resolve(utils.cryptoDir(cryptoRec, blockchainDir), 'wallets') const walletDir = path.resolve(utils.cryptoDir(cryptoRec, blockchainDir), 'wallets')
const unitScale = cryptoRec.unitScale const unitScale = cryptoRec.unitScale
const SUPPORTS_BATCHING = false
function rpcConfig () { function rpcConfig () {
try { try {
const config = jsonRpc.parseConf(configPath) const config = jsonRpc.parseConf(configPath)
@ -92,7 +90,7 @@ function accountBalance (cryptoCode) {
.then(() => refreshWallet()) .then(() => refreshWallet())
.then(() => fetch('get_balance', { account_index: 0, address_indices: [0] })) .then(() => fetch('get_balance', { account_index: 0, address_indices: [0] }))
.then(res => { .then(res => {
return BN(res.unlocked_balance).shift(unitScale).round() return BN(res.unlocked_balance).shiftedBy(unitScale).decimalPlaces(0)
}) })
.catch(err => handleError(err)) .catch(err => handleError(err))
} }
@ -177,7 +175,7 @@ function newFunding (account, cryptoCode) {
fetch('create_address', { account_index: 0 }) fetch('create_address', { account_index: 0 })
])) ]))
.then(([balanceRes, addressRes]) => ({ .then(([balanceRes, addressRes]) => ({
fundingPendingBalance: BN(balanceRes.balance).sub(balanceRes.unlocked_balance), fundingPendingBalance: BN(balanceRes.balance).minus(balanceRes.unlocked_balance),
fundingConfirmedBalance: BN(balanceRes.unlocked_balance), fundingConfirmedBalance: BN(balanceRes.unlocked_balance),
fundingAddress: addressRes.address fundingAddress: addressRes.address
})) }))
@ -200,11 +198,6 @@ function cryptoNetwork (account, cryptoCode) {
}) })
} }
function supportsBatching (cryptoCode) {
return checkCryptoCode(cryptoCode)
.then(() => SUPPORTS_BATCHING)
}
function checkBlockchainStatus (cryptoCode) { function checkBlockchainStatus (cryptoCode) {
return checkCryptoCode(cryptoCode) return checkCryptoCode(cryptoCode)
.then(() => { .then(() => {
@ -236,6 +229,5 @@ module.exports = {
getStatus, getStatus,
newFunding, newFunding,
cryptoNetwork, cryptoNetwork,
supportsBatching,
checkBlockchainStatus checkBlockchainStatus
} }

View file

@ -9,7 +9,6 @@ const E = require('../../../error')
const cryptoRec = coinUtils.getCryptoCurrency('ZEC') const cryptoRec = coinUtils.getCryptoCurrency('ZEC')
const unitScale = cryptoRec.unitScale const unitScale = cryptoRec.unitScale
const SUPPORTS_BATCHING = false
const rpcConfig = jsonRpc.rpcConfig(cryptoRec) const rpcConfig = jsonRpc.rpcConfig(cryptoRec)
@ -139,11 +138,6 @@ function newFunding (account, cryptoCode, settings, operatorId) {
})) }))
} }
function supportsBatching (cryptoCode) {
return checkCryptoCode(cryptoCode)
.then(() => SUPPORTS_BATCHING)
}
function checkBlockchainStatus (cryptoCode) { function checkBlockchainStatus (cryptoCode) {
return checkCryptoCode(cryptoCode) return checkCryptoCode(cryptoCode)
.then(() => fetch('getblockchaininfo')) .then(() => fetch('getblockchaininfo'))
@ -156,6 +150,5 @@ module.exports = {
newAddress, newAddress,
getStatus, getStatus,
newFunding, newFunding,
supportsBatching,
checkBlockchainStatus checkBlockchainStatus
} }

View file

@ -48,8 +48,7 @@ const lastBalance = {}
function _balance (settings, cryptoCode) { function _balance (settings, cryptoCode) {
return fetchWallet(settings, cryptoCode) return fetchWallet(settings, cryptoCode)
.then(r => r.wallet.balance(r.account, cryptoCode, settings, r.operatorId)) .then(r => r.wallet.balance(r.account, cryptoCode, settings, r.operatorId))
.then(balance => Promise.all([balance, supportsBatching(settings, cryptoCode)])) .then(balance => Promise.all([balance, getOpenBatchCryptoValue(cryptoCode)]))
.then(([balance, supportsBatching]) => Promise.all([balance, supportsBatching ? getOpenBatchCryptoValue(cryptoCode) : Promise.resolve(BN(0))]))
.then(([balance, reservedBalance]) => ({ balance: balance.minus(reservedBalance), reservedBalance, timestamp: Date.now() })) .then(([balance, reservedBalance]) => ({ balance: balance.minus(reservedBalance), reservedBalance, timestamp: Date.now() }))
.then(r => { .then(r => {
lastBalance[cryptoCode] = r lastBalance[cryptoCode] = r
@ -82,7 +81,8 @@ function sendCoins (settings, tx) {
function sendCoinsBatch (settings, txs, cryptoCode) { function sendCoinsBatch (settings, txs, cryptoCode) {
return fetchWallet(settings, cryptoCode) return fetchWallet(settings, cryptoCode)
.then(r => { .then(r => {
return r.wallet.sendCoinsBatch(r.account, txs, cryptoCode) const feeMultiplier = settings[`wallets_${cryptoCode}_feeMultiplier`]
return r.wallet.sendCoinsBatch(r.account, txs, cryptoCode, feeMultiplier)
.then(res => { .then(res => {
mem.clear(module.exports.balance) mem.clear(module.exports.balance)
return res return res
@ -233,8 +233,7 @@ function isStrictAddress (settings, cryptoCode, toAddress) {
} }
function supportsBatching (settings, cryptoCode) { function supportsBatching (settings, cryptoCode) {
return fetchWallet(settings, cryptoCode) return Promise.resolve(!!configManager.getWalletSettings(cryptoCode, settings.config).allowTransactionBatching)
.then(r => r.wallet.supportsBatching(cryptoCode))
} }
function checkBlockchainStatus (settings, cryptoCode) { function checkBlockchainStatus (settings, cryptoCode) {

View file

@ -41,14 +41,14 @@ const useStyles = makeStyles({
}) })
const CheckboxInput = ({ name, onChange, value, settings, ...props }) => { const CheckboxInput = ({ name, onChange, value, settings, ...props }) => {
const { enabled, label, disabledMessage } = settings const { enabled, label, disabledMessage, rightSideLabel } = settings
const classes = useStyles() const classes = useStyles()
return ( return (
<> <>
{enabled ? ( {enabled ? (
<div className={classes.checkBoxLabel}> <div className={classes.checkBoxLabel}>
<Label2>{label}</Label2> {!rightSideLabel && <Label2>{label}</Label2>}
<Checkbox <Checkbox
id={name} id={name}
classes={{ classes={{
@ -67,6 +67,7 @@ const CheckboxInput = ({ name, onChange, value, settings, ...props }) => {
disableRipple disableRipple
{...props} {...props}
/> />
{rightSideLabel && <Label2>{label}</Label2>}
</div> </div>
) : ( ) : (
<div className={classes.wrapper}> <div className={classes.wrapper}>

View file

@ -61,9 +61,9 @@ const Row = ({
expandable && expandRow(id, data) expandable && expandRow(id, data)
onClick && onClick(data) onClick && onClick(data)
}} }}
error={data.error || data.hasError} error={data.error || data.hasError || data.batchError}
shouldShowError={false} shouldShowError={false}
errorMessage={data.errorMessage || data.hasError}> errorMessage={data.errorMessage || data.hasError || data.batchError}>
{elements.map(({ view = it => it?.toString(), ...props }, idx) => ( {elements.map(({ view = it => it?.toString(), ...props }, idx) => (
<Td key={idx} {...props}> <Td key={idx} {...props}>
{view(data)} {view(data)}

View file

@ -21,7 +21,6 @@ export default {
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
'& > div': { '& > div': {
width: 144,
height: 37, height: 37,
marginBottom: 15, marginBottom: 15,
marginRight: 55 marginRight: 55

View file

@ -27,9 +27,11 @@ export default {
settings: { settings: {
field: 'wallets_BTC_wallet', field: 'wallets_BTC_wallet',
enabled: true, enabled: true,
disabledMessage: 'RBF verification not available', disabledMessage:
'Lower the confidence of RBF transactions (Available when using bitcoind.)',
label: 'Lower the confidence of RBF transactions', label: 'Lower the confidence of RBF transactions',
requirement: 'bitcoind' requirement: 'bitcoind',
rightSideLabel: true
}, },
face: true face: true
} }

View file

@ -169,8 +169,8 @@ const DetailsRow = ({ it: tx, timezone }) => {
'' ''
} }
const from = sub({ minutes: MINUTES_OFFSET }, tx.created) const from = sub({ minutes: MINUTES_OFFSET }, new Date(tx.created))
const until = add({ minutes: MINUTES_OFFSET }, tx.created) const until = add({ minutes: MINUTES_OFFSET }, new Date(tx.created))
const downloadRawLogs = ({ id: txId, deviceId, txClass }, timezone) => { const downloadRawLogs = ({ id: txId, deviceId, txClass }, timezone) => {
fetchSummary({ fetchSummary({
@ -438,5 +438,6 @@ export default memo(
(prev, next) => (prev, next) =>
prev.it.id === next.it.id && prev.it.id === next.it.id &&
prev.it.hasError === next.it.hasError && prev.it.hasError === next.it.hasError &&
prev.it.batchError === next.it.batchError &&
getStatus(prev.it) === getStatus(next.it) getStatus(prev.it) === getStatus(next.it)
) )

View file

@ -117,6 +117,7 @@ const GET_TRANSACTIONS = gql`
batched batched
batchTime batchTime
rawTickerPrice rawTickerPrice
batchError
walletScore walletScore
} }
} }
@ -191,7 +192,7 @@ const Transactions = () => {
<div className={classes.overflowTd}>{getCustomerDisplayName(it)}</div> <div className={classes.overflowTd}>{getCustomerDisplayName(it)}</div>
{!it.isAnonymous && ( {!it.isAnonymous && (
<div onClick={() => redirect(it.customerId)}> <div onClick={() => redirect(it.customerId)}>
{it.hasError ? ( {it.hasError || it.batchError ? (
<CustomerLinkWhiteIcon className={classes.customerLinkIcon} /> <CustomerLinkWhiteIcon className={classes.customerLinkIcon} />
) : ( ) : (
<CustomerLinkIcon className={classes.customerLinkIcon} /> <CustomerLinkIcon className={classes.customerLinkIcon} />

View file

@ -1,3 +1,5 @@
import * as R from 'ramda'
const getCashOutStatus = it => { const getCashOutStatus = it => {
if (it.hasError === 'Operator cancel') return 'Cancelled' if (it.hasError === 'Operator cancel') return 'Cancelled'
if (it.hasError) return 'Error' if (it.hasError) return 'Error'
@ -8,7 +10,7 @@ const getCashOutStatus = it => {
const getCashInStatus = it => { const getCashInStatus = it => {
if (it.operatorCompleted) return 'Cancelled' if (it.operatorCompleted) return 'Cancelled'
if (it.hasError) return 'Error' if (it.hasError || it.batchError) return 'Error'
if (it.sendConfirmed) return 'Sent' if (it.sendConfirmed) return 'Sent'
if (it.expired) return 'Expired' if (it.expired) return 'Expired'
if (it.batched) return 'Batched' if (it.batched) return 'Batched'
@ -23,11 +25,14 @@ const getStatus = it => {
} }
const getStatusDetails = it => { const getStatusDetails = it => {
return it.hasError ? it.hasError : null if (!R.isNil(it.hasError)) return it.hasError
if (!R.isNil(it.batchError)) return `Batch error: ${it.batchError}`
return null
} }
const getStatusProperties = status => ({ const getStatusProperties = status => ({
hasError: status === 'Error' || null, hasError: status === 'Error' || null,
batchError: status === 'Error' || null,
dispense: status === 'Success' || null, dispense: status === 'Success' || null,
expired: status === 'Expired' || null, expired: status === 'Expired' || null,
operatorCompleted: status === 'Cancelled' || null, operatorCompleted: status === 'Cancelled' || null,

View file

@ -118,6 +118,9 @@ const styles = {
actionButtonWrapper: { actionButtonWrapper: {
display: 'flex', display: 'flex',
gap: 12 gap: 12
},
enterButton: {
display: 'none'
} }
} }

View file

@ -7,7 +7,11 @@ import React from 'react'
import { NamespacedTable as EditableTable } from 'src/components/editableTable' import { NamespacedTable as EditableTable } from 'src/components/editableTable'
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config' import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
import { AdvancedWalletSchema, getAdvancedWalletElements } from './helper' import {
WalletSchema,
AdvancedWalletSchema,
getAdvancedWalletElements
} from './helper'
const SAVE_CONFIG = gql` const SAVE_CONFIG = gql`
mutation Save($config: JSONObject, $accounts: JSONObject) { mutation Save($config: JSONObject, $accounts: JSONObject) {
@ -51,8 +55,9 @@ const AdvancedWallet = () => {
enableEdit enableEdit
editWidth={174} editWidth={174}
save={save} save={save}
stripeWhen={it => !WalletSchema.isValidSync(it)}
validationSchema={AdvancedWalletSchema} validationSchema={AdvancedWalletSchema}
elements={getAdvancedWalletElements(cryptoCurrencies, coinUtils)} elements={getAdvancedWalletElements(cryptoCurrencies, coinUtils, config)}
/> />
) )
} }

View file

@ -1,8 +1,11 @@
import * as R from 'ramda' import * as R from 'ramda'
import * as Yup from 'yup' import * as Yup from 'yup'
import { NumberInput } from 'src/components/inputs/formik' import {
import Autocomplete from 'src/components/inputs/formik/Autocomplete.js' Autocomplete,
Checkbox,
NumberInput
} from 'src/components/inputs/formik'
import { disabledColor } from 'src/styling/variables' import { disabledColor } from 'src/styling/variables'
import { CURRENCY_MAX } from 'src/utils/constants' import { CURRENCY_MAX } from 'src/utils/constants'
import { transformNumber } from 'src/utils/number' import { transformNumber } from 'src/utils/number'
@ -29,10 +32,11 @@ const WalletSchema = Yup.object().shape({
}) })
const AdvancedWalletSchema = Yup.object().shape({ const AdvancedWalletSchema = Yup.object().shape({
cryptoUnits: Yup.string().required() cryptoUnits: Yup.string().required(),
allowTransactionBatching: Yup.boolean()
}) })
const getAdvancedWalletElements = (cryptoCurrencies, coinUtils) => { const getAdvancedWalletElements = (cryptoCurrencies, coinUtils, config) => {
const viewCryptoCurrency = it => const viewCryptoCurrency = it =>
R.compose( R.compose(
R.prop(['display']), R.prop(['display']),
@ -66,6 +70,19 @@ const getAdvancedWalletElements = (cryptoCurrencies, coinUtils) => {
valueProp: 'code', valueProp: 'code',
labelProp: 'display' labelProp: 'display'
} }
},
{
name: 'allowTransactionBatching',
size: 'sm',
stripe: true,
width: 250,
view: (_, ite) => {
if (ite.id !== 'BTC')
return <span style={classes.editDisabled}>{`No`}</span>
return config[`${ite.id}_allowTransactionBatching`] ? 'Yes' : 'No'
},
input: Checkbox,
editable: it => it.id === 'BTC'
} }
] ]
} }

View file

@ -1,4 +1,8 @@
const url = `https://${window.location.hostname}` const url = `https://${
process.env.NODE_ENV === 'development'
? window.location.host
: window.location.hostname
}`
const urlResolver = content => `${url}${content}` const urlResolver = content => `${url}${content}`

View file

@ -1,7 +1,7 @@
{ {
"files": { "files": {
"main.js": "/static/js/main.6ef30c04.chunk.js", "main.js": "/static/js/main.8cb55c42.chunk.js",
"main.js.map": "/static/js/main.6ef30c04.chunk.js.map", "main.js.map": "/static/js/main.8cb55c42.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.c4e7abab.chunk.js": "/static/js/2.c4e7abab.chunk.js", "static/js/2.c4e7abab.chunk.js": "/static/js/2.c4e7abab.chunk.js",
@ -17,8 +17,8 @@
"static/media/4-cassettes-open-4-left.bc1a9829.svg": "/static/media/4-cassettes-open-4-left.bc1a9829.svg", "static/media/4-cassettes-open-4-left.bc1a9829.svg": "/static/media/4-cassettes-open-4-left.bc1a9829.svg",
"static/media/acceptor-left.f37bcb1a.svg": "/static/media/acceptor-left.f37bcb1a.svg", "static/media/acceptor-left.f37bcb1a.svg": "/static/media/acceptor-left.f37bcb1a.svg",
"static/media/both-filled.7af80d5f.svg": "/static/media/both-filled.7af80d5f.svg", "static/media/both-filled.7af80d5f.svg": "/static/media/both-filled.7af80d5f.svg",
"static/media/carousel-left-arrow.04e38344.svg": "/static/media/carousel-left-arrow.04e38344.svg", "static/media/carousel-left-arrow.c6575d9d.svg": "/static/media/carousel-left-arrow.c6575d9d.svg",
"static/media/carousel-right-arrow.4748b93d.svg": "/static/media/carousel-right-arrow.4748b93d.svg", "static/media/carousel-right-arrow.1d5e04d1.svg": "/static/media/carousel-right-arrow.1d5e04d1.svg",
"static/media/cash-in.c06970a7.svg": "/static/media/cash-in.c06970a7.svg", "static/media/cash-in.c06970a7.svg": "/static/media/cash-in.c06970a7.svg",
"static/media/cash-out.f029ae96.svg": "/static/media/cash-out.f029ae96.svg", "static/media/cash-out.f029ae96.svg": "/static/media/cash-out.f029ae96.svg",
"static/media/cashbox-empty.828bd3b9.svg": "/static/media/cashbox-empty.828bd3b9.svg", "static/media/cashbox-empty.828bd3b9.svg": "/static/media/cashbox-empty.828bd3b9.svg",
@ -151,6 +151,6 @@
"entrypoints": [ "entrypoints": [
"static/js/runtime-main.5b925903.js", "static/js/runtime-main.5b925903.js",
"static/js/2.c4e7abab.chunk.js", "static/js/2.c4e7abab.chunk.js",
"static/js/main.6ef30c04.chunk.js" "static/js/main.8cb55c42.chunk.js"
] ]
} }

View file

@ -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.c4e7abab.chunk.js"></script><script src="/static/js/main.6ef30c04.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.c4e7abab.chunk.js"></script><script src="/static/js/main.8cb55c42.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

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<svg width="13px" height="33px" viewBox="0 0 13 33" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <svg width="13px" height="33px" viewBox="0 0 13 33" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<polygon id="Simple-Arrow-White" fill="#FFFFFF" fill-rule="nonzero" points="12.1912718 1.56064837 10.8306233 0.395663059 0.196798664 16.2200463 10.8250965 32.3956631 12.1967987 31.2473125 2.33241023 16.233075"></polygon> <polygon id="Simple-Arrow-White" fill="#1b2559" fill-rule="nonzero" points="12.1912718 1.56064837 10.8306233 0.395663059 0.196798664 16.2200463 10.8250965 32.3956631 12.1967987 31.2473125 2.33241023 16.233075"></polygon>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 512 B

After

Width:  |  Height:  |  Size: 512 B

Before After
Before After

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<svg width="15px" height="34px" viewBox="0 0 15 34" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <svg width="15px" height="34px" viewBox="0 0 15 34" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Group-2-Copy" transform="translate(1.000000, 1.000000)" stroke="#FFFFFF" stroke-width="2"> <g id="Group-2-Copy" transform="translate(1.000000, 1.000000)" stroke="#1b2559" stroke-width="2">
<polyline id="Path-4-Copy" points="0 0 12 15.8202247 0 32"></polyline> <polyline id="Path-4-Copy" points="0 0 12 15.8202247 0 32"></polyline>
</g> </g>
</g> </g>

Before

Width:  |  Height:  |  Size: 485 B

After

Width:  |  Height:  |  Size: 485 B

Before After
Before After