aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/scripts/controllers/transactions.js209
-rw-r--r--app/scripts/lib/tx-state-manager.js201
-rw-r--r--test/unit/tx-controller-test.js222
-rw-r--r--test/unit/tx-state-manager-test.js227
4 files changed, 471 insertions, 388 deletions
diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js
index 58c468e22..48a882a71 100644
--- a/app/scripts/controllers/transactions.js
+++ b/app/scripts/controllers/transactions.js
@@ -4,6 +4,7 @@ const clone = require('clone')
const ObservableStore = require('obs-store')
const ethUtil = require('ethereumjs-util')
const EthQuery = require('ethjs-query')
+const TransactionStateManger = require('../lib/tx-state-manager')
const TxProviderUtil = require('../lib/tx-utils')
const PendingTransactionTracker = require('../lib/pending-tx-tracker')
const createId = require('../lib/random-id')
@@ -12,18 +13,23 @@ const NonceTracker = require('../lib/nonce-tracker')
module.exports = class TransactionController extends EventEmitter {
constructor (opts) {
super()
- this.store = new ObservableStore(extend({
- transactions: [],
- }, opts.initState))
- this.memStore = new ObservableStore({})
this.networkStore = opts.networkStore || new ObservableStore({})
this.preferencesStore = opts.preferencesStore || new ObservableStore({})
- this.txHistoryLimit = opts.txHistoryLimit
this.provider = opts.provider
this.blockTracker = opts.blockTracker
this.signEthTx = opts.signTransaction
this.ethStore = opts.ethStore
+ this.memStore = new ObservableStore({})
+ this.query = new EthQuery(this.provider)
+ this.txProviderUtil = new TxProviderUtil(this.provider)
+
+ this.txStateManager = new TransactionStateManger(extend({
+ transactions: [],
+ txHistoryLimit: opts.txHistoryLimit,
+ getNetwork: this.getNetwork.bind(this),
+ }, opts.initState))
+
this.nonceTracker = new NonceTracker({
provider: this.provider,
getPendingTransactions: (address) => {
@@ -34,8 +40,6 @@ module.exports = class TransactionController extends EventEmitter {
})
},
})
- this.query = new EthQuery(this.provider)
- this.txProviderUtil = new TxProviderUtil(this.provider)
this.pendingTxTracker = new PendingTransactionTracker({
provider: this.provider,
@@ -69,7 +73,7 @@ module.exports = class TransactionController extends EventEmitter {
this.blockTracker.on('sync', this.pendingTxTracker.queryPendingTxs.bind(this.pendingTxTracker))
// memstore is computed from a few different stores
this._updateMemstore()
- this.store.subscribe(() => this._updateMemstore())
+ this.txStateManager.subscribe(() => this._updateMemstore())
this.networkStore.subscribe(() => this._updateMemstore())
this.preferencesStore.subscribe(() => this._updateMemstore())
}
@@ -86,84 +90,27 @@ module.exports = class TransactionController extends EventEmitter {
return this.preferencesStore.getState().selectedAddress
}
- // Returns the number of txs for the current network.
- getTxCount () {
- return this.getTxList().length
- }
-
- // Returns the full tx list across all networks
- getFullTxList () {
- return this.store.getState().transactions
- }
-
getUnapprovedTxCount () {
return Object.keys(this.getUnapprovedTxList()).length
}
getPendingTxCount () {
- return this.getTxsByMetaData('status', 'signed').length
+ return this.txStateManager.getTxsByMetaData('status', 'signed').length
}
// Returns the tx list
- getTxList () {
- const network = this.getNetwork()
- const fullTxList = this.getFullTxList()
- return this.getTxsByMetaData('metamaskNetworkId', network, fullTxList)
- }
- // gets tx by Id and returns it
- getTx (txId) {
- const txList = this.getTxList()
- const txMeta = txList.find(txData => txData.id === txId)
- return txMeta
- }
getUnapprovedTxList () {
- const txList = this.getTxList()
- return txList.filter((txMeta) => txMeta.status === 'unapproved')
- .reduce((result, tx) => {
+ const txList = this.txStateManager.getTxsByMetaData('status', 'unapproved')
+ return txList.reduce((result, tx) => {
result[tx.id] = tx
return result
}, {})
}
- updateTx (txMeta) {
- // create txMeta snapshot for history
- const txMetaForHistory = clone(txMeta)
- // dont include previous history in this snapshot
- delete txMetaForHistory.history
- // add snapshot to tx history
- if (!txMeta.history) txMeta.history = []
- txMeta.history.push(txMetaForHistory)
-
- const txId = txMeta.id
- const txList = this.getFullTxList()
- const index = txList.findIndex(txData => txData.id === txId)
- if (!txMeta.history) txMeta.history = []
- txMeta.history.push(txMetaForHistory)
-
- txList[index] = txMeta
- this._saveTxList(txList)
- this.emit('update')
- }
-
// Adds a tx to the txlist
addTx (txMeta) {
- const txCount = this.getTxCount()
- const network = this.getNetwork()
- const fullTxList = this.getFullTxList()
- const txHistoryLimit = this.txHistoryLimit
-
- // checks if the length of the tx history is
- // longer then desired persistence limit
- // and then if it is removes only confirmed
- // or rejected tx's.
- // not tx's that are pending or unapproved
- if (txCount > txHistoryLimit - 1) {
- const index = fullTxList.findIndex((metaTx) => ((metaTx.status === 'confirmed' || metaTx.status === 'rejected') && network === txMeta.metamaskNetworkId))
- fullTxList.splice(index, 1)
- }
- fullTxList.push(txMeta)
- this._saveTxList(fullTxList)
+ this.txStateManager.addTx(txMeta)
this.emit('update')
this.once(`${txMeta.id}:signed`, function (txId) {
@@ -211,7 +158,7 @@ module.exports = class TransactionController extends EventEmitter {
// add default tx params
await this.addTxDefaults(txMeta)
// save txMeta
- this.addTx(txMeta)
+ this.txStateManager.addTx(txMeta)
return txMeta
}
@@ -306,133 +253,11 @@ module.exports = class TransactionController extends EventEmitter {
this.updateTx(txMeta)
}
- /*
- Takes an object of fields to search for eg:
- let thingsToLookFor = {
- to: '0x0..',
- from: '0x0..',
- status: 'signed',
- err: undefined,
- }
- and returns a list of tx with all
- options matching
-
- ****************HINT****************
- | `err: undefined` is like looking |
- | for a tx with no err |
- | so you can also search txs that |
- | dont have something as well by |
- | setting the value as undefined |
- ************************************
-
- this is for things like filtering a the tx list
- for only tx's from 1 account
- or for filltering for all txs from one account
- and that have been 'confirmed'
- */
- getFilteredTxList (opts) {
- let filteredTxList
- Object.keys(opts).forEach((key) => {
- filteredTxList = this.getTxsByMetaData(key, opts[key], filteredTxList)
- })
- return filteredTxList
- }
-
- getTxsByMetaData (key, value, txList = this.getTxList()) {
- return txList.filter((txMeta) => {
- if (txMeta.txParams[key]) {
- return txMeta.txParams[key] === value
- } else {
- return txMeta[key] === value
- }
- })
- }
-
- // STATUS METHODS
- // get::set status
-
- // should return the status of the tx.
- getTxStatus (txId) {
- const txMeta = this.getTx(txId)
- return txMeta.status
- }
-
- // should update the status of the tx to 'rejected'.
- setTxStatusRejected (txId) {
- this._setTxStatus(txId, 'rejected')
- }
-
- // should update the status of the tx to 'approved'.
- setTxStatusApproved (txId) {
- this._setTxStatus(txId, 'approved')
- }
-
- // should update the status of the tx to 'signed'.
- setTxStatusSigned (txId) {
- this._setTxStatus(txId, 'signed')
- }
-
- // should update the status of the tx to 'submitted'.
- setTxStatusSubmitted (txId) {
- this._setTxStatus(txId, 'submitted')
- }
-
- // should update the status of the tx to 'confirmed'.
- setTxStatusConfirmed (txId) {
- this._setTxStatus(txId, 'confirmed')
- }
-
- setTxStatusFailed (txId, err) {
- const txMeta = this.getTx(txId)
- txMeta.err = {
- message: err.toString(),
- stack: err.stack,
- }
- this.updateTx(txMeta)
- this._setTxStatus(txId, 'failed')
- }
-
- // merges txParams obj onto txData.txParams
- // use extend to ensure that all fields are filled
- updateTxParams (txId, txParams) {
- const txMeta = this.getTx(txId)
- txMeta.txParams = extend(txMeta.txParams, txParams)
- this.updateTx(txMeta)
- }
-
/* _____________________________________
| |
| PRIVATE METHODS |
|______________________________________*/
-
- // Should find the tx in the tx list and
- // update it.
- // should set the status in txData
- // - `'unapproved'` the user has not responded
- // - `'rejected'` the user has responded no!
- // - `'approved'` the user has approved the tx
- // - `'signed'` the tx is signed
- // - `'submitted'` the tx is sent to a server
- // - `'confirmed'` the tx has been included in a block.
- // - `'failed'` the tx failed for some reason, included on tx data.
- _setTxStatus (txId, status) {
- const txMeta = this.getTx(txId)
- txMeta.status = status
- this.emit(`${txMeta.id}:${status}`, txId)
- if (status === 'submitted' || status === 'rejected') {
- this.emit(`${txMeta.id}:finished`, txMeta)
- }
- this.updateTx(txMeta)
- this.emit('updateBadge')
- }
-
- // Saves the new/updated txList.
- // Function is intended only for internal use
- _saveTxList (transactions) {
- this.store.updateState({ transactions })
- }
-
_updateMemstore () {
const unapprovedTxs = this.getUnapprovedTxList()
const selectedAddressTxList = this.getFilteredTxList({
diff --git a/app/scripts/lib/tx-state-manager.js b/app/scripts/lib/tx-state-manager.js
new file mode 100644
index 000000000..91271cc4a
--- /dev/null
+++ b/app/scripts/lib/tx-state-manager.js
@@ -0,0 +1,201 @@
+const clone = require('clone')
+const extend = require('xtend')
+const ObservableStore = require('obs-store')
+
+module.exports = class TransactionStateManger extends ObservableStore {
+ constructor ({initState, txHistoryLimit, getNetwork}) {
+ super(initState)
+ this.txHistoryLimit = txHistoryLimit
+ this.getNetwork = getNetwork
+ }
+ // Returns the number of txs for the current network.
+ getTxCount () {
+ return this.getTxList().length
+ }
+
+ getTxList () {
+ const network = this.getNetwork()
+ const fullTxList = this.getFullTxList()
+ return fullTxList.filter((txMeta) => txMeta.metamaskNetworkId === network)
+ }
+
+ getFullTxList () {
+ return this.getState().transactions
+ }
+
+ addTx (txMeta) {
+ const transactions = this.getFullTxList()
+ const txCount = this.getTxCount()
+ const txHistoryLimit = this.txHistoryLimit
+
+ // checks if the length of the tx history is
+ // longer then desired persistence limit
+ // and then if it is removes only confirmed
+ // or rejected tx's.
+ // not tx's that are pending or unapproved
+ if (txCount > txHistoryLimit - 1) {
+ const index = transactions.findIndex((metaTx) => ((metaTx.status === 'confirmed' || metaTx.status === 'rejected')))
+ transactions.splice(index, 1)
+ }
+ transactions.push(txMeta)
+ transactions.push(txMeta)
+ this._saveTxList(transactions)
+ }
+ // gets tx by Id and returns it
+ getTx (txId) {
+ const txMeta = this.getTxsByMetaData('id', txId)[0]
+ return txMeta
+ }
+
+ updateTx (txMeta) {
+ // create txMeta snapshot for history
+ const txMetaForHistory = clone(txMeta)
+ // dont include previous history in this snapshot
+ delete txMetaForHistory.history
+ // add snapshot to tx history
+ if (!txMeta.history) txMeta.history = []
+ txMeta.history.push(txMetaForHistory)
+
+ const txId = txMeta.id
+ const txList = this.getFullTxList()
+ const index = txList.findIndex(txData => txData.id === txId)
+ if (!txMeta.history) txMeta.history = []
+ txMeta.history.push(txMetaForHistory)
+
+ txList[index] = txMeta
+ this._saveTxList(txList)
+ }
+
+
+ // merges txParams obj onto txData.txParams
+ // use extend to ensure that all fields are filled
+ updateTxParams (txId, txParams) {
+ const txMeta = this.getTx(txId)
+ txMeta.txParams = extend(txMeta.txParams, txParams)
+ this.updateTx(txMeta)
+ }
+
+/*
+ Takes an object of fields to search for eg:
+ let thingsToLookFor = {
+ to: '0x0..',
+ from: '0x0..',
+ status: 'signed',
+ err: undefined,
+ }
+ and returns a list of tx with all
+ options matching
+
+ ****************HINT****************
+ | `err: undefined` is like looking |
+ | for a tx with no err |
+ | so you can also search txs that |
+ | dont have something as well by |
+ | setting the value as undefined |
+ ************************************
+
+ this is for things like filtering a the tx list
+ for only tx's from 1 account
+ or for filltering for all txs from one account
+ and that have been 'confirmed'
+ */
+ getFilteredTxList (opts, initialList) {
+ let filteredTxList = initialList
+ Object.keys(opts).forEach((key) => {
+ filteredTxList = this.getTxsByMetaData(key, opts[key], filteredTxList)
+ })
+ return filteredTxList
+ }
+
+ getTxsByMetaData (key, value, txList = this.getTxList()) {
+ return txList.filter((txMeta) => {
+ if (txMeta.txParams[key]) {
+ return txMeta.txParams[key] === value
+ } else {
+ return txMeta[key] === value
+ }
+ })
+ }
+
+ // STATUS METHODS
+ // statuses:
+ // - `'unapproved'` the user has not responded
+ // - `'rejected'` the user has responded no!
+ // - `'approved'` the user has approved the tx
+ // - `'signed'` the tx is signed
+ // - `'submitted'` the tx is sent to a server
+ // - `'confirmed'` the tx has been included in a block.
+ // - `'failed'` the tx failed for some reason, included on tx data.
+
+ // get::set status
+
+ // should return the status of the tx.
+ getTxStatus (txId) {
+ const txMeta = this.getTx(txId)
+ return txMeta.status
+ }
+
+ // should update the status of the tx to 'rejected'.
+ setTxStatusRejected (txId) {
+ this._setTxStatus(txId, 'rejected')
+ }
+
+ // should update the status of the tx to 'approved'.
+ setTxStatusApproved (txId) {
+ this._setTxStatus(txId, 'approved')
+ }
+
+ // should update the status of the tx to 'signed'.
+ setTxStatusSigned (txId) {
+ this._setTxStatus(txId, 'signed')
+ }
+
+ // should update the status of the tx to 'submitted'.
+ setTxStatusSubmitted (txId) {
+ this._setTxStatus(txId, 'submitted')
+ }
+
+ // should update the status of the tx to 'confirmed'.
+ setTxStatusConfirmed (txId) {
+ this._setTxStatus(txId, 'confirmed')
+ }
+
+ setTxStatusFailed (txId, reason) {
+ const txMeta = this.getTx(txId)
+ txMeta.err = reason
+ this.updateTx(txMeta)
+ this._setTxStatus(txId, 'failed')
+ }
+
+/* _____________________________________
+| |
+| PRIVATE METHODS |
+|______________________________________*/
+
+ // Should find the tx in the tx list and
+ // update it.
+ // should set the status in txData
+ // - `'unapproved'` the user has not responded
+ // - `'rejected'` the user has responded no!
+ // - `'approved'` the user has approved the tx
+ // - `'signed'` the tx is signed
+ // - `'submitted'` the tx is sent to a server
+ // - `'confirmed'` the tx has been included in a block.
+ // - `'failed'` the tx failed for some reason, included on tx data.
+ _setTxStatus (txId, status) {
+ const txMeta = this.getTx(txId)
+ txMeta.status = status
+ this.emit(`${txMeta.id}:${status}`, txId)
+ if (status === 'submitted' || status === 'rejected') {
+ this.emit(`${txMeta.id}:finished`, txMeta)
+ }
+ this.updateTx(txMeta)
+ this.emit('updateBadge')
+ }
+
+ // Saves the new/updated txList.
+ // Function is intended only for internal use
+ _saveTxList (transactions) {
+ this.updateState({ transactions })
+ }
+} \ No newline at end of file
diff --git a/test/unit/tx-controller-test.js b/test/unit/tx-controller-test.js
index 57d7deccd..40be490c0 100644
--- a/test/unit/tx-controller-test.js
+++ b/test/unit/tx-controller-test.js
@@ -47,11 +47,12 @@ describe('Transaction Controller', function () {
metamaskNetworkId: currentNetworkId,
txParams,
}
- txController._saveTxList([txMeta])
+ txController.txStateManager._saveTxList([txMeta])
stub = sinon.stub(txController, 'addUnapprovedTransaction').returns(Promise.resolve(txMeta))
})
afterEach(function () {
+ txController.txStateManager._saveTxList([])
stub.restore()
})
@@ -69,7 +70,7 @@ describe('Transaction Controller', function () {
txController.once('newUnaprovedTx', (txMetaFromEmit) => {
setTimeout(() => {
txController.setTxHash(txMetaFromEmit.id, '0x0')
- txController.setTxStatusSubmitted(txMetaFromEmit.id)
+ txController.txStateManager.setTxStatusSubmitted(txMetaFromEmit.id)
}, 10)
})
@@ -84,7 +85,7 @@ describe('Transaction Controller', function () {
it('should reject when finished and status is rejected', function (done) {
txController.once('newUnaprovedTx', (txMetaFromEmit) => {
setTimeout(() => {
- txController.setTxStatusRejected(txMetaFromEmit.id)
+ txController.txStateManager.setTxStatusRejected(txMetaFromEmit.id)
}, 10)
})
@@ -107,7 +108,7 @@ describe('Transaction Controller', function () {
assert(('txParams' in txMeta), 'should have a txParams')
assert(('history' in txMeta), 'should have a history')
- const memTxMeta = txController.getTx(txMeta.id)
+ const memTxMeta = txController.txStateManager.getTx(txMeta.id)
assert.deepEqual(txMeta, memTxMeta, `txMeta should be stored in txController after adding it\n expected: ${txMeta} \n got: ${memTxMeta}`)
addTxDefaultsStub.restore()
done()
@@ -160,202 +161,31 @@ describe('Transaction Controller', function () {
})
})
- describe('#getTxList', function () {
- it('when new should return empty array', function () {
- var result = txController.getTxList()
- assert.ok(Array.isArray(result))
- assert.equal(result.length, 0)
- })
- })
-
describe('#addTx', function () {
- it('adds a tx returned in getTxList', function () {
- var tx = { id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
- txController.addTx(tx, noop)
- var result = txController.getTxList()
- assert.ok(Array.isArray(result))
- assert.equal(result.length, 1)
- assert.equal(result[0].id, 1)
- })
-
- it('does not override txs from other networks', function () {
- var tx = { id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
- var tx2 = { id: 2, status: 'confirmed', metamaskNetworkId: otherNetworkId, txParams: {} }
- txController.addTx(tx, noop)
- txController.addTx(tx2, noop)
- var result = txController.getFullTxList()
- var result2 = txController.getTxList()
- assert.equal(result.length, 2, 'txs were deleted')
- assert.equal(result2.length, 1, 'incorrect number of txs on network.')
- })
-
- it('cuts off early txs beyond a limit', function () {
- const limit = txController.txHistoryLimit
- for (let i = 0; i < limit + 1; i++) {
- const tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
- txController.addTx(tx, noop)
- }
- var result = txController.getTxList()
- assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
- assert.equal(result[0].id, 1, 'early txs truncted')
- })
-
- it('cuts off early txs beyond a limit whether or not it is confirmed or rejected', function () {
- const limit = txController.txHistoryLimit
- for (let i = 0; i < limit + 1; i++) {
- const tx = { id: i, time: new Date(), status: 'rejected', metamaskNetworkId: currentNetworkId, txParams: {} }
- txController.addTx(tx, noop)
- }
- var result = txController.getTxList()
- assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
- assert.equal(result[0].id, 1, 'early txs truncted')
- })
-
- it('cuts off early txs beyond a limit but does not cut unapproved txs', function () {
- var unconfirmedTx = { id: 0, time: new Date(), status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
- txController.addTx(unconfirmedTx, noop)
- const limit = txController.txHistoryLimit
- for (let i = 1; i < limit + 1; i++) {
- const tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
- txController.addTx(tx, noop)
- }
- var result = txController.getTxList()
- assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
- assert.equal(result[0].id, 0, 'first tx should still be there')
- assert.equal(result[0].status, 'unapproved', 'first tx should be unapproved')
- assert.equal(result[1].id, 2, 'early txs truncted')
- })
- })
-
- describe('#setTxStatusSigned', function () {
- it('sets the tx status to signed', function () {
- var tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
- txController.addTx(tx, noop)
- txController.setTxStatusSigned(1)
- var result = txController.getTxList()
- assert.ok(Array.isArray(result))
- assert.equal(result.length, 1)
- assert.equal(result[0].status, 'signed')
- })
-
- it('should emit a signed event to signal the exciton of callback', (done) => {
- this.timeout(10000)
- var tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
- const noop = function () {
- assert(true, 'event listener has been triggered and noop executed')
- done()
- }
- txController.addTx(tx)
- txController.on('1:signed', noop)
- txController.setTxStatusSigned(1)
- })
- })
-
- describe('#setTxStatusRejected', function () {
- it('sets the tx status to rejected', function () {
- var tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
- txController.addTx(tx)
- txController.setTxStatusRejected(1)
- var result = txController.getTxList()
- assert.ok(Array.isArray(result))
- assert.equal(result.length, 1)
- assert.equal(result[0].status, 'rejected')
- })
-
- it('should emit a rejected event to signal the exciton of callback', (done) => {
- this.timeout(10000)
- var tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
- txController.addTx(tx)
- const noop = function (err, txId) {
- assert(true, 'event listener has been triggered and noop executed')
- done()
- }
- txController.on('1:rejected', noop)
- txController.setTxStatusRejected(1)
- })
- })
-
- describe('#updateTx', function () {
- it('replaces the tx with the same id', function () {
- txController.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
- txController.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
- txController.updateTx({ id: '1', status: 'blah', hash: 'foo', metamaskNetworkId: currentNetworkId, txParams: {} })
- var result = txController.getTx('1')
- assert.equal(result.hash, 'foo')
- })
-
- it('updates gas price', function () {
- const originalGasPrice = '0x01'
- const desiredGasPrice = '0x02'
-
- const txMeta = {
- id: '1',
+ it('should emit updates', function (done) {
+ txMeta = {
status: 'unapproved',
+ id: 1,
metamaskNetworkId: currentNetworkId,
- txParams: {
- gasPrice: originalGasPrice,
- },
+ txParams: {}
}
- const updatedMeta = clone(txMeta)
-
+ const eventNames = ['update', 'updateBadge', '1:unapproved']
+ const listeners = []
+ eventNames.forEach((eventName) => {
+ listeners.push(new Promise((resolve) => {
+ txController.once(eventName, (arg) => {
+ resolve(arg)
+ })
+ }))
+ })
+ Promise.all(listeners)
+ .then((returnValues) => {
+ assert.deepEqual(returnValues.pop(), txMeta, 'last event 1:unapproved should return txMeta')
+ done()
+ })
+ .catch(done)
txController.addTx(txMeta)
- updatedMeta.txParams.gasPrice = desiredGasPrice
- txController.updateTx(updatedMeta)
- var result = txController.getTx('1')
- assert.equal(result.txParams.gasPrice, desiredGasPrice, 'gas price updated')
- })
- })
-
- describe('#getUnapprovedTxList', function () {
- it('returns unapproved txs in a hash', function () {
- txController.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
- txController.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
- const result = txController.getUnapprovedTxList()
- assert.equal(typeof result, 'object')
- assert.equal(result['1'].status, 'unapproved')
- assert.equal(result['2'], undefined)
- })
- })
-
- describe('#getTx', function () {
- it('returns a tx with the requested id', function () {
- txController.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
- txController.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
- assert.equal(txController.getTx('1').status, 'unapproved')
- assert.equal(txController.getTx('2').status, 'confirmed')
- })
- })
-
- describe('#getFilteredTxList', function () {
- it('returns a tx with the requested data', function () {
- const txMetas = [
- { id: 0, status: 'unapproved', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
- { id: 1, status: 'unapproved', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
- { id: 2, status: 'unapproved', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
- { id: 3, status: 'unapproved', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
- { id: 4, status: 'unapproved', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
- { id: 5, status: 'confirmed', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
- { id: 6, status: 'confirmed', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
- { id: 7, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
- { id: 8, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
- { id: 9, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
- ]
- txMetas.forEach((txMeta) => txController.addTx(txMeta, noop))
- let filterParams
-
- filterParams = { status: 'unapproved', from: '0xaa' }
- assert.equal(txController.getFilteredTxList(filterParams).length, 3, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
- filterParams = { status: 'unapproved', to: '0xaa' }
- assert.equal(txController.getFilteredTxList(filterParams).length, 2, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
- filterParams = { status: 'confirmed', from: '0xbb' }
- assert.equal(txController.getFilteredTxList(filterParams).length, 3, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
- filterParams = { status: 'confirmed' }
- assert.equal(txController.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
- filterParams = { from: '0xaa' }
- assert.equal(txController.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
- filterParams = { to: '0xaa' }
- assert.equal(txController.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
})
})
@@ -389,11 +219,11 @@ describe('Transaction Controller', function () {
const pubStub = sinon.stub(txController, 'publishTransaction').callsFake(() => {
txController.setTxHash('1', originalValue)
- txController.setTxStatusSubmitted('1')
+ txController.txStateManager.setTxStatusSubmitted('1')
})
txController.approveTransaction(txMeta.id).then(() => {
- const result = txController.getTx(txMeta.id)
+ const result = txController.txStateManager.getTx(txMeta.id)
const params = result.txParams
assert.equal(params.gas, originalValue, 'gas unmodified')
diff --git a/test/unit/tx-state-manager-test.js b/test/unit/tx-state-manager-test.js
new file mode 100644
index 000000000..0b35465d6
--- /dev/null
+++ b/test/unit/tx-state-manager-test.js
@@ -0,0 +1,227 @@
+const assert = require('assert')
+const clone = require('clone')
+const ObservableStore = require('obs-store')
+const TxStateManager = require('../../app/scripts/lib/tx-state-manager')
+const noop = () => true
+
+describe('TransactionStateManger', function () {
+ let txStateManager
+ const currentNetworkId = 42
+ const otherNetworkId = 2
+
+ beforeEach(function () {
+ txStateManager = new TxStateManager({
+ initState: {
+ transactions: [],
+ },
+ txHistoryLimit: 10,
+ getNetwork: () => currentNetworkId
+ })
+ })
+
+ describe('#setTxStatusSigned', function () {
+ it('sets the tx status to signed', function () {
+ let tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
+ txStateManager.addTx(tx, noop)
+ txStateManager.setTxStatusSigned(1)
+ let result = txStateManager.getTxList()
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 1)
+ assert.equal(result[0].status, 'signed')
+ })
+
+ it('should emit a signed event to signal the exciton of callback', (done) => {
+ let tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
+ const noop = function () {
+ assert(true, 'event listener has been triggered and noop executed')
+ done()
+ }
+ txStateManager.addTx(tx)
+ txStateManager.on('1:signed', noop)
+ txStateManager.setTxStatusSigned(1)
+
+ })
+ })
+
+ describe('#setTxStatusRejected', function () {
+ it('sets the tx status to rejected', function () {
+ let tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
+ txStateManager.addTx(tx)
+ txStateManager.setTxStatusRejected(1)
+ let result = txStateManager.getTxList()
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 1)
+ assert.equal(result[0].status, 'rejected')
+ })
+
+ it('should emit a rejected event to signal the exciton of callback', (done) => {
+ let tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
+ txStateManager.addTx(tx)
+ const noop = function (err, txId) {
+ assert(true, 'event listener has been triggered and noop executed')
+ done()
+ }
+ txStateManager.on('1:rejected', noop)
+ txStateManager.setTxStatusRejected(1)
+ })
+ })
+
+ describe('#getFullTxList', function () {
+ it('when new should return empty array', function () {
+ let result = txStateManager.getTxList()
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 0)
+ })
+ })
+
+ describe('#getTxList', function () {
+ it('when new should return empty array', function () {
+ let result = txStateManager.getTxList()
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 0)
+ })
+ })
+
+ describe('#addTx', function () {
+ it('adds a tx returned in getTxList', function () {
+ let tx = { id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
+ txStateManager.addTx(tx, noop)
+ let result = txStateManager.getTxList()
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 1)
+ assert.equal(result[0].id, 1)
+ })
+
+ it('does not override txs from other networks', function () {
+ let tx = { id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
+ let tx2 = { id: 2, status: 'confirmed', metamaskNetworkId: otherNetworkId, txParams: {} }
+ txStateManager.addTx(tx, noop)
+ txStateManager.addTx(tx2, noop)
+ let result = txStateManager.getFullTxList()
+ let result2 = txStateManager.getTxList()
+ assert.equal(result.length, 2, 'txs were deleted')
+ assert.equal(result2.length, 1, 'incorrect number of txs on network.')
+ })
+
+ it('cuts off early txs beyond a limit', function () {
+ const limit = txStateManager.txHistoryLimit
+ for (let i = 0; i < limit + 1; i++) {
+ const tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
+ txStateManager.addTx(tx, noop)
+ }
+ let result = txStateManager.getTxList()
+ assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
+ assert.equal(result[0].id, 1, 'early txs truncted')
+ })
+
+ it('cuts off early txs beyond a limit whether or not it is confirmed or rejected', function () {
+ const limit = txStateManager.txHistoryLimit
+ for (let i = 0; i < limit + 1; i++) {
+ const tx = { id: i, time: new Date(), status: 'rejected', metamaskNetworkId: currentNetworkId, txParams: {} }
+ txStateManager.addTx(tx, noop)
+ }
+ let result = txStateManager.getTxList()
+ assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
+ assert.equal(result[0].id, 1, 'early txs truncted')
+ })
+
+ it('cuts off early txs beyond a limit but does not cut unapproved txs', function () {
+ let unconfirmedTx = { id: 0, time: new Date(), status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
+ txStateManager.addTx(unconfirmedTx, noop)
+ const limit = txStateManager.txHistoryLimit
+ for (let i = 1; i < limit + 1; i++) {
+ const tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
+ txStateManager.addTx(tx, noop)
+ }
+ let result = txStateManager.getTxList()
+ assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
+ assert.equal(result[0].id, 0, 'first tx should still be there')
+ assert.equal(result[0].status, 'unapproved', 'first tx should be unapproved')
+ assert.equal(result[1].id, 2, 'early txs truncted')
+ })
+ })
+
+ describe('#updateTx', function () {
+ it('replaces the tx with the same id', function () {
+ txStateManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
+ txStateManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
+ txStateManager.updateTx({ id: '1', status: 'blah', hash: 'foo', metamaskNetworkId: currentNetworkId, txParams: {} })
+ let result = txStateManager.getTx('1')
+ assert.equal(result.hash, 'foo')
+ })
+
+ it('updates gas price', function () {
+ const originalGasPrice = '0x01'
+ const desiredGasPrice = '0x02'
+
+ const txMeta = {
+ id: '1',
+ status: 'unapproved',
+ metamaskNetworkId: currentNetworkId,
+ txParams: {
+ gasPrice: originalGasPrice,
+ },
+ }
+
+ const updatedMeta = clone(txMeta)
+
+ txStateManager.addTx(txMeta)
+ updatedMeta.txParams.gasPrice = desiredGasPrice
+ txStateManager.updateTx(updatedMeta)
+ let result = txStateManager.getTx('1')
+ assert.equal(result.txParams.gasPrice, desiredGasPrice, 'gas price updated')
+ })
+ })
+
+ describe('#getUnapprovedTxList', function () {
+ it('returns unapproved txs in a hash', function () {
+ txStateManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
+ txStateManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
+ const result = txStateManager.getUnapprovedTxList()
+ assert.equal(typeof result, 'object')
+ assert.equal(result['1'].status, 'unapproved')
+ assert.equal(result['2'], undefined)
+ })
+ })
+
+ describe('#getTx', function () {
+ it('returns a tx with the requested id', function () {
+ txStateManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
+ txStateManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
+ assert.equal(txStateManager.getTx('1').status, 'unapproved')
+ assert.equal(txStateManager.getTx('2').status, 'confirmed')
+ })
+ })
+
+ describe('#getFilteredTxList', function () {
+ it('returns a tx with the requested data', function () {
+ const txMetas = [
+ { id: 0, status: 'unapproved', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
+ { id: 1, status: 'unapproved', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
+ { id: 2, status: 'unapproved', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
+ { id: 3, status: 'unapproved', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
+ { id: 4, status: 'unapproved', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
+ { id: 5, status: 'confirmed', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
+ { id: 6, status: 'confirmed', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
+ { id: 7, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
+ { id: 8, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
+ { id: 9, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
+ ]
+ txMetas.forEach((txMeta) => txStateManager.addTx(txMeta, noop))
+ let filterParams
+
+ filterParams = { status: 'unapproved', from: '0xaa' }
+ assert.equal(txStateManager.getFilteredTxList(filterParams).length, 3, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { status: 'unapproved', to: '0xaa' }
+ assert.equal(txStateManager.getFilteredTxList(filterParams).length, 2, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { status: 'confirmed', from: '0xbb' }
+ assert.equal(txStateManager.getFilteredTxList(filterParams).length, 3, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { status: 'confirmed' }
+ assert.equal(txStateManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { from: '0xaa' }
+ assert.equal(txStateManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { to: '0xaa' }
+ assert.equal(txStateManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ })
+ })
+}) \ No newline at end of file