diff --git a/lib/cash-in/cash-in-low.js b/lib/cash-in/cash-in-low.js index 42f8d3bd..1b7ddf07 100644 --- a/lib/cash-in/cash-in-low.js +++ b/lib/cash-in/cash-in-low.js @@ -102,7 +102,7 @@ function diff (oldTx, newTx) { } function ensureRatchet (oldField, newField, fieldKey) { - const monotonic = ['cryptoAtoms', 'fiat', 'cashInFeeCrypto', 'send', 'sendConfirmed', 'operatorCompleted', 'timedout', 'txVersion'] + const monotonic = ['cryptoAtoms', 'fiat', 'cashInFeeCrypto', 'send', 'sendConfirmed', 'operatorCompleted', 'timedout', 'txVersion', 'batched'] const free = ['sendPending', 'error', 'errorCode', 'customerId'] if (_.isNil(oldField)) return true @@ -138,11 +138,11 @@ function nilEqual (a, b) { function isClearToSend (oldTx, newTx) { const now = Date.now() - return newTx.send && + return (newTx.send || newTx.batched) && (!oldTx || (!oldTx.sendPending && !oldTx.sendConfirmed)) && (newTx.created > now - PENDING_INTERVAL_MS) } function isFinalTxStage (txChanges) { - return txChanges.send + return txChanges.send || txChanges.batched } diff --git a/lib/cash-in/cash-in-tx.js b/lib/cash-in/cash-in-tx.js index f76c6fab..6f932bd5 100644 --- a/lib/cash-in/cash-in-tx.js +++ b/lib/cash-in/cash-in-tx.js @@ -120,15 +120,27 @@ function postProcess (r, pi, isBlacklisted, addressReuse, failedWalletScore) { if (!cashInLow.isClearToSend(r.dbTx, r.tx)) return Promise.resolve({}) return pi.sendCoins(r.tx) - .then(txObj => ({ - txHash: txObj.txid, - fee: txObj.fee, - sendConfirmed: true, - sendTime: 'now()^', - sendPending: false, - error: null, - errorCode: null - })) + .then(txObj => { + if (txObj.batched) { + return { + batched: true, + batchTime: 'now()^', + sendPending: true, + error: null, + errorCode: null + } + } + + return { + txHash: txObj.txid, + fee: txObj.fee, + sendConfirmed: true, + sendTime: 'now()^', + sendPending: false, + error: null, + errorCode: null + } + }) .catch(err => { // Important: We don't know what kind of error this is // so not safe to assume that funds weren't sent. diff --git a/lib/new-admin/graphql/types/transaction.type.js b/lib/new-admin/graphql/types/transaction.type.js index f4c86e3e..c121d0bd 100644 --- a/lib/new-admin/graphql/types/transaction.type.js +++ b/lib/new-admin/graphql/types/transaction.type.js @@ -45,6 +45,8 @@ const typeDef = gql` discount: Int txCustomerPhotoPath: String txCustomerPhotoAt: Date + batched: Boolean + batchTime: Date } type Filter { diff --git a/lib/plugins.js b/lib/plugins.js index 89c6a018..fe7bd663 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -260,7 +260,8 @@ function plugins (settings, deviceId) { const configVersion = arr[2] const tz = arr[3] const cryptoCodesCount = cryptoCodes.length - const batchableCoins = arr.slice(4, cryptoCodesCount + 4) + const batchableCoinsRes = arr.slice(4, cryptoCodesCount + 4) + const batchableCoins = batchableCoinsRes.map(it => ({ batchable: it })) const tickers = arr.slice(cryptoCodesCount + 4, 2 * cryptoCodesCount + 4) const balances = arr.slice(2 * cryptoCodesCount + 4, 3 * cryptoCodesCount + 4) const testNets = arr.slice(3 * cryptoCodesCount + 4, arr.length - 1) @@ -272,7 +273,7 @@ function plugins (settings, deviceId) { cassettes, rates: buildRates(tickers), balances: buildBalances(balances), - coins: _.zipWith(_.assign, coinsWithoutRate, tickers), + coins: _.zipWith(_.assign, _.zipWith(_.assign, coinsWithoutRate, tickers), batchableCoins), configVersion, areThereAvailablePromoCodes, timezone: tz diff --git a/lib/tx-batching.js b/lib/tx-batching.js index 93cd5eb9..fef98b93 100644 --- a/lib/tx-batching.js +++ b/lib/tx-batching.js @@ -2,6 +2,7 @@ const _ = require('lodash/fp') const pgp = require('pg-promise')() const uuid = require('uuid') +const BN = require('./bn') const db = require('./db') const wallet = require('./wallet') @@ -17,10 +18,13 @@ function closeTransactionBatch (batch) { return db.none(sql, [batch.id]) } -function confirmSentBatch (batch) { - const sql = `UPDATE transaction_batches SET status='sent', error_message=NULL WHERE id=$1` +function confirmSentBatch (batch, tx) { + return db.tx(t => { + const q1 = t.none(`UPDATE transaction_batches SET status='sent', error_message=NULL WHERE id=$1`, [batch.id]) + const q2 = t.none(`UPDATE cash_in_txs SET tx_hash=$1, fee=$2, send=true, send_confirmed=true, send_time=now(), send_pending=false, error=NULL, error_code=NULL WHERE batch_id=$3`, [tx.txid, tx.fee.toString(), batch.id]) - return db.none(sql, [batch.id]) + return t.batch([q1, q2]) + }) } function setErroredBatch (batch, errorMsg) { @@ -57,7 +61,7 @@ function submitBatch (settings, batch) { getBatchTransactions(batch) .then(txs => { wallet.sendCoinsBatch(settings, txs, batch.crypto_code) - .then(() => confirmSentBatch(batch)) + .then(res => confirmSentBatch(batch, res)) .catch(err => setErroredBatch(batch, err.message)) }) } diff --git a/migrations/1621556014244-add-btc-tx-batching.js b/migrations/1621556014244-add-btc-tx-batching.js index 69eafc24..fdb92702 100644 --- a/migrations/1621556014244-add-btc-tx-batching.js +++ b/migrations/1621556014244-add-btc-tx-batching.js @@ -11,7 +11,9 @@ exports.up = function (next) { closed_at TIMESTAMPTZ, error_message TEXT )`, - `ALTER TABLE cash_in_txs ADD COLUMN batch_id UUID REFERENCES transaction_batches(id)` + `ALTER TABLE cash_in_txs ADD COLUMN batch_id UUID REFERENCES transaction_batches(id)`, + `ALTER TABLE cash_in_txs ADD COLUMN batched BOOLEAN NOT NULL DEFAULT false`, + `ALTER TABLE cash_in_txs ADD COLUMN batch_time TIMESTAMPTZ` ] db.multi(sql, next) diff --git a/new-lamassu-admin/src/pages/Transactions/Transactions.js b/new-lamassu-admin/src/pages/Transactions/Transactions.js index 049aa210..64cfbac4 100644 --- a/new-lamassu-admin/src/pages/Transactions/Transactions.js +++ b/new-lamassu-admin/src/pages/Transactions/Transactions.js @@ -110,6 +110,8 @@ const GET_TRANSACTIONS = gql` discount customerId isAnonymous + batched + batchTime } } ` diff --git a/new-lamassu-admin/src/pages/Transactions/helper.js b/new-lamassu-admin/src/pages/Transactions/helper.js index 749782db..5e90626d 100644 --- a/new-lamassu-admin/src/pages/Transactions/helper.js +++ b/new-lamassu-admin/src/pages/Transactions/helper.js @@ -11,6 +11,7 @@ const getCashInStatus = it => { if (it.hasError) return 'Error' if (it.sendConfirmed) return 'Sent' if (it.expired) return 'Expired' + if (it.batched) return 'Batched' return 'Pending' }