diff --git a/CHANGELOG.md b/CHANGELOG.md index e7934dc77..3a471108c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Re-enable default token list. - Add origin header to dapp-bound requests to allow providers to throttle sites. +- Fix bug that could sometimes resubmit a transaction that had been stalled due to low balance after balance was restored. ## 3.8.2 2017-7-3 diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index 52251d66e..7946d10d1 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -428,10 +428,22 @@ module.exports = class TransactionController extends EventEmitter { const gtBalance = Number.parseInt(txMeta.txParams.value) > Number.parseInt(balance) if (!('retryCount' in txMeta)) txMeta.retryCount = 0 - // if the value of the transaction is greater then the balance - // or the nonce of the transaction is lower then the accounts nonce - // dont resubmit the tx - if (gtBalance || txNonce < nonce) return cb() + // if the value of the transaction is greater then the balance, fail. + if (gtBalance) { + const message = 'Insufficient balance.' + this.setTxStatusFailed(txMeta.id, message) + cb() + return log.error(message) + } + + // if the nonce of the transaction is lower then the accounts nonce, fail. + if (txNonce < nonce) { + const message = 'Invalid nonce.' + this.setTxStatusFailed(txMeta.id, message) + cb() + return log.error(message) + } + // Only auto-submit already-signed txs: if (!('rawTx' in txMeta)) return cb() diff --git a/test/unit/tx-controller-test.js b/test/unit/tx-controller-test.js index 0d35cd62c..01a498820 100644 --- a/test/unit/tx-controller-test.js +++ b/test/unit/tx-controller-test.js @@ -19,6 +19,7 @@ describe('Transaction Controller', function () { txController = new TransactionController({ networkStore: new ObservableStore(currentNetworkId), txHistoryLimit: 10, + ethStore: { getState: noop }, provider: { _blockTracker: new EventEmitter()}, blockTracker: new EventEmitter(), ethQuery: new EthQuery(new EventEmitter()), @@ -322,4 +323,43 @@ describe('Transaction Controller', function () { }) }) }) + + describe('#_resubmitTx with a too-low balance', function () { + it('should fail the transaction', function (done) { + const from = '0xda0da0' + const txMeta = { + id: 1, + status: 'submitted', + metamaskNetworkId: currentNetworkId, + txParams: { + from, + nonce: '0x1', + value: '0xfffff', + }, + } + + const lowBalance = '0x0' + const fakeStoreState = { accounts: {} } + fakeStoreState.accounts[from] = { + balance: lowBalance, + nonce: '0x0', + } + + // Stubbing out current account state: + const getStateStub = sinon.stub(txController.ethStore, 'getState') + .returns(fakeStoreState) + + // Adding the fake tx: + txController.addTx(clone(txMeta)) + + txController._resubmitTx(txMeta, function (err) { + assert.ifError(err, 'should not throw an error') + const updatedMeta = txController.getTx(txMeta.id) + assert.notEqual(updatedMeta.status, txMeta.status, 'status changed.') + assert.equal(updatedMeta.status, 'failed', 'tx set to failed.') + done() + }) + }) + }) }) +