Merge pull request #339 from poanetwork/refactor
Refactoring 3 before EIP - 1193
This commit is contained in:
commit
2d6753d182
|
@ -12,9 +12,9 @@ abiDecoder.addABI(abi)
|
|||
import TransactionStateManager from './tx-state-manager'
|
||||
const TxGasUtil = require('./tx-gas-utils')
|
||||
const PendingTransactionTracker = require('./pending-tx-tracker')
|
||||
const NonceTracker = require('./nonce-tracker')
|
||||
import NonceTracker from 'nonce-tracker'
|
||||
import * as txUtils from './lib/util'
|
||||
const cleanErrorStack = require('../../lib/cleanErrorStack')
|
||||
import cleanErrorStack from '../../lib/cleanErrorStack'
|
||||
import log from 'loglevel'
|
||||
const recipientBlacklistChecker = require('./lib/recipient-blacklist-checker')
|
||||
const {
|
||||
|
@ -228,18 +228,9 @@ class TransactionController extends EventEmitter {
|
|||
@return {txMeta}
|
||||
*/
|
||||
|
||||
async retryTransaction (originalTxId, gasPrice) {
|
||||
async retryTransaction (originalTxId) {
|
||||
const originalTxMeta = this.txStateManager.getTx(originalTxId)
|
||||
const { txParams } = originalTxMeta
|
||||
const lastGasPrice = gasPrice || originalTxMeta.txParams.gasPrice
|
||||
const suggestedGasPriceBN = new ethUtil.BN(ethUtil.stripHexPrefix(this.getGasPrice()), 16)
|
||||
const lastGasPriceBN = new ethUtil.BN(ethUtil.stripHexPrefix(lastGasPrice), 16)
|
||||
// essentially lastGasPrice * 1.1 but
|
||||
// dont trust decimals so a round about way of doing that
|
||||
const lastGasPriceBNBumped = lastGasPriceBN.mul(new ethUtil.BN(110, 10)).div(new ethUtil.BN(100, 10))
|
||||
// transactions that are being retried require a >=%10 bump or the clients will throw an error
|
||||
txParams.gasPrice = suggestedGasPriceBN.gt(lastGasPriceBNBumped) ? `0x${suggestedGasPriceBN.toString(16)}` : `0x${lastGasPriceBNBumped.toString(16)}`
|
||||
|
||||
const lastGasPrice = originalTxMeta.txParams.gasPrice
|
||||
const txMeta = this.txStateManager.generateTxMeta({
|
||||
txParams: originalTxMeta.txParams,
|
||||
lastGasPrice,
|
||||
|
|
|
@ -1,161 +0,0 @@
|
|||
const EthQuery = require('ethjs-query')
|
||||
const assert = require('assert')
|
||||
const Mutex = require('await-semaphore').Mutex
|
||||
/**
|
||||
@param opts {Object}
|
||||
@param {Object} opts.provider a ethereum provider
|
||||
@param {Function} opts.getPendingTransactions a function that returns an array of txMeta
|
||||
whosee status is `submitted`
|
||||
@param {Function} opts.getConfirmedTransactions a function that returns an array of txMeta
|
||||
whose status is `confirmed`
|
||||
@class
|
||||
*/
|
||||
class NonceTracker {
|
||||
|
||||
constructor ({ provider, blockTracker, getPendingTransactions, getConfirmedTransactions }) {
|
||||
this.provider = provider
|
||||
this.blockTracker = blockTracker
|
||||
this.ethQuery = new EthQuery(provider)
|
||||
this.getPendingTransactions = getPendingTransactions
|
||||
this.getConfirmedTransactions = getConfirmedTransactions
|
||||
this.lockMap = {}
|
||||
}
|
||||
|
||||
/**
|
||||
@returns {Promise<Object>} with the key releaseLock (the gloabl mutex)
|
||||
*/
|
||||
async getGlobalLock () {
|
||||
const globalMutex = this._lookupMutex('global')
|
||||
// await global mutex free
|
||||
const releaseLock = await globalMutex.acquire()
|
||||
return { releaseLock }
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef NonceDetails
|
||||
* @property {number} highestLocallyConfirmed - A hex string of the highest nonce on a confirmed transaction.
|
||||
* @property {number} nextNetworkNonce - The next nonce suggested by the eth_getTransactionCount method.
|
||||
* @property {number} highestSuggested - The maximum between the other two, the number returned.
|
||||
*/
|
||||
|
||||
/**
|
||||
this will return an object with the `nextNonce` `nonceDetails` of type NonceDetails, and the releaseLock
|
||||
Note: releaseLock must be called after adding a signed tx to pending transactions (or discarding).
|
||||
|
||||
@param address {string} the hex string for the address whose nonce we are calculating
|
||||
@returns {Promise<NonceDetails>}
|
||||
*/
|
||||
async getNonceLock (address) {
|
||||
// await global mutex free
|
||||
await this._globalMutexFree()
|
||||
// await lock free, then take lock
|
||||
const releaseLock = await this._takeMutex(address)
|
||||
try {
|
||||
// evaluate multiple nextNonce strategies
|
||||
const nonceDetails = {}
|
||||
const networkNonceResult = await this._getNetworkNextNonce(address)
|
||||
const highestLocallyConfirmed = this._getHighestLocallyConfirmed(address)
|
||||
const nextNetworkNonce = networkNonceResult.nonce
|
||||
const highestSuggested = Math.max(nextNetworkNonce, highestLocallyConfirmed)
|
||||
|
||||
const pendingTxs = this.getPendingTransactions(address)
|
||||
const localNonceResult = this._getHighestContinuousFrom(pendingTxs, highestSuggested) || 0
|
||||
|
||||
nonceDetails.params = {
|
||||
highestLocallyConfirmed,
|
||||
highestSuggested,
|
||||
nextNetworkNonce,
|
||||
}
|
||||
nonceDetails.local = localNonceResult
|
||||
nonceDetails.network = networkNonceResult
|
||||
|
||||
const nextNonce = Math.max(networkNonceResult.nonce, localNonceResult.nonce)
|
||||
assert(Number.isInteger(nextNonce), `nonce-tracker - nextNonce is not an integer - got: (${typeof nextNonce}) "${nextNonce}"`)
|
||||
|
||||
// return nonce and release cb
|
||||
return { nextNonce, nonceDetails, releaseLock }
|
||||
} catch (err) {
|
||||
// release lock if we encounter an error
|
||||
releaseLock()
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async _globalMutexFree () {
|
||||
const globalMutex = this._lookupMutex('global')
|
||||
const releaseLock = await globalMutex.acquire()
|
||||
releaseLock()
|
||||
}
|
||||
|
||||
async _takeMutex (lockId) {
|
||||
const mutex = this._lookupMutex(lockId)
|
||||
const releaseLock = await mutex.acquire()
|
||||
return releaseLock
|
||||
}
|
||||
|
||||
_lookupMutex (lockId) {
|
||||
let mutex = this.lockMap[lockId]
|
||||
if (!mutex) {
|
||||
mutex = new Mutex()
|
||||
this.lockMap[lockId] = mutex
|
||||
}
|
||||
return mutex
|
||||
}
|
||||
|
||||
async _getNetworkNextNonce (address) {
|
||||
// calculate next nonce
|
||||
// we need to make sure our base count
|
||||
// and pending count are from the same block
|
||||
const blockNumber = await this.blockTracker.getLatestBlock()
|
||||
const baseCountBN = await this.ethQuery.getTransactionCount(address, blockNumber)
|
||||
const baseCount = baseCountBN.toNumber()
|
||||
assert(Number.isInteger(baseCount), `nonce-tracker - baseCount is not an integer - got: (${typeof baseCount}) "${baseCount}"`)
|
||||
const nonceDetails = { blockNumber, baseCount }
|
||||
return { name: 'network', nonce: baseCount, details: nonceDetails }
|
||||
}
|
||||
|
||||
_getHighestLocallyConfirmed (address) {
|
||||
const confirmedTransactions = this.getConfirmedTransactions(address)
|
||||
const highest = this._getHighestNonce(confirmedTransactions)
|
||||
return Number.isInteger(highest) ? highest + 1 : 0
|
||||
}
|
||||
|
||||
_getHighestNonce (txList) {
|
||||
const nonces = txList.map((txMeta) => {
|
||||
const nonce = txMeta.txParams.nonce
|
||||
assert(typeof nonce, 'string', 'nonces should be hex strings')
|
||||
return parseInt(nonce, 16)
|
||||
})
|
||||
const highestNonce = Math.max.apply(null, nonces)
|
||||
return highestNonce
|
||||
}
|
||||
|
||||
/**
|
||||
@typedef {object} highestContinuousFrom
|
||||
@property {string} - name the name for how the nonce was calculated based on the data used
|
||||
@property {number} - nonce the next suggested nonce
|
||||
@property {object} - details the provided starting nonce that was used (for debugging)
|
||||
*/
|
||||
/**
|
||||
@param txList {array} - list of txMeta's
|
||||
@param startPoint {number} - the highest known locally confirmed nonce
|
||||
@returns {highestContinuousFrom}
|
||||
*/
|
||||
_getHighestContinuousFrom (txList, startPoint) {
|
||||
const nonces = txList.map((txMeta) => {
|
||||
const nonce = txMeta.txParams.nonce
|
||||
assert(typeof nonce, 'string', 'nonces should be hex strings')
|
||||
return parseInt(nonce, 16)
|
||||
})
|
||||
|
||||
let highest = startPoint
|
||||
while (nonces.includes(highest)) {
|
||||
highest++
|
||||
}
|
||||
|
||||
return { name: 'local', nonce: highest, details: { startPoint, highest } }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = NonceTracker
|
|
@ -1,11 +1,9 @@
|
|||
import ethUtil from 'ethereumjs-util'
|
||||
import extend from 'extend'
|
||||
import EventEmitter from 'safe-event-emitter'
|
||||
import ObservableStore from 'obs-store'
|
||||
import log from 'loglevel'
|
||||
import txStateHistoryHelper from './lib/tx-state-history-helper'
|
||||
import createId from '../../lib/random-id'
|
||||
import { getFinalStates } from './lib/util'
|
||||
import { getFinalStates, normalizeTxParams } from './lib/util'
|
||||
/**
|
||||
TransactionStateManager is responsible for the state of a transaction and
|
||||
storing the transaction
|
||||
|
@ -21,8 +19,8 @@ import { getFinalStates } from './lib/util'
|
|||
<br> - `'confirmed'` the tx has been included in a block.
|
||||
<br> - `'failed'` the tx failed for some reason, included on tx data.
|
||||
<br> - `'dropped'` the tx nonce was already used
|
||||
@param opts {object}
|
||||
@param {object} [opts.initState={ transactions: [] }] initial transactions list with the key transaction {array}
|
||||
@param {Object} opts
|
||||
@param {Object} [opts.initState={ transactions: [] }] initial transactions list with the key transaction {array}
|
||||
@param {number} [opts.txHistoryLimit] limit for how many finished
|
||||
transactions can hang around in state
|
||||
@param {function} opts.getNetwork return network number
|
||||
|
@ -33,29 +31,33 @@ class TransactionStateManager extends EventEmitter {
|
|||
super()
|
||||
|
||||
this.store = new ObservableStore(
|
||||
extend({
|
||||
Object.assign({
|
||||
transactions: [],
|
||||
}, initState))
|
||||
}, initState))
|
||||
this.txHistoryLimit = txHistoryLimit
|
||||
this.getNetwork = getNetwork
|
||||
}
|
||||
|
||||
/**
|
||||
@param opts {object} - the object to use when overwriting defaults
|
||||
@returns {txMeta} the default txMeta object
|
||||
@param {Object} opts - the object to use when overwriting defaults
|
||||
@returns {txMeta} - the default txMeta object
|
||||
*/
|
||||
generateTxMeta (opts) {
|
||||
return extend({
|
||||
const netId = this.getNetwork()
|
||||
if (netId === 'loading') {
|
||||
throw new Error('MetaMask is having trouble connecting to the network')
|
||||
}
|
||||
return Object.assign({
|
||||
id: createId(),
|
||||
time: (new Date()).getTime(),
|
||||
status: 'unapproved',
|
||||
metamaskNetworkId: this.getNetwork(),
|
||||
metamaskNetworkId: netId,
|
||||
loadingDefaults: true,
|
||||
}, opts)
|
||||
}
|
||||
|
||||
/**
|
||||
@returns {array} of txMetas that have been filtered for only the current network
|
||||
@returns {array} - of txMetas that have been filtered for only the current network
|
||||
*/
|
||||
getTxList () {
|
||||
const network = this.getNetwork()
|
||||
|
@ -64,14 +66,14 @@ class TransactionStateManager extends EventEmitter {
|
|||
}
|
||||
|
||||
/**
|
||||
@returns {array} of all the txMetas in store
|
||||
@returns {array} - of all the txMetas in store
|
||||
*/
|
||||
getFullTxList () {
|
||||
return this.store.getState().transactions
|
||||
}
|
||||
|
||||
/**
|
||||
@returns {array} the tx list whos status is unapproved
|
||||
@returns {array} - the tx list whos status is unapproved
|
||||
*/
|
||||
getUnapprovedTxList () {
|
||||
const txList = this.getTxsByMetaData('status', 'unapproved')
|
||||
|
@ -83,23 +85,40 @@ class TransactionStateManager extends EventEmitter {
|
|||
|
||||
/**
|
||||
@param [address] {string} - hex prefixed address to sort the txMetas for [optional]
|
||||
@returns {array} the tx list whos status is submitted if no address is provide
|
||||
returns all txMetas who's status is submitted for the current network
|
||||
@returns {array} - the tx list whos status is approved if no address is provide
|
||||
returns all txMetas who's status is approved for the current network
|
||||
*/
|
||||
getPendingTransactions (address) {
|
||||
const opts = { status: 'submitted' }
|
||||
if (address) opts.from = address
|
||||
getApprovedTransactions (address) {
|
||||
const opts = { status: 'approved' }
|
||||
if (address) {
|
||||
opts.from = address
|
||||
}
|
||||
return this.getFilteredTxList(opts)
|
||||
}
|
||||
|
||||
/**
|
||||
@param [address] {string} - hex prefixed address to sort the txMetas for [optional]
|
||||
@returns {array} the tx list whos status is confirmed if no address is provide
|
||||
@returns {array} - the tx list whos status is submitted if no address is provide
|
||||
returns all txMetas who's status is submitted for the current network
|
||||
*/
|
||||
getPendingTransactions (address) {
|
||||
const opts = { status: 'submitted' }
|
||||
if (address) {
|
||||
opts.from = address
|
||||
}
|
||||
return this.getFilteredTxList(opts)
|
||||
}
|
||||
|
||||
/**
|
||||
@param [address] {string} - hex prefixed address to sort the txMetas for [optional]
|
||||
@returns {array} - the tx list whos status is confirmed if no address is provide
|
||||
returns all txMetas who's status is confirmed for the current network
|
||||
*/
|
||||
getConfirmedTransactions (address) {
|
||||
const opts = { status: 'confirmed' }
|
||||
if (address) opts.from = address
|
||||
if (address) {
|
||||
opts.from = address
|
||||
}
|
||||
return this.getFilteredTxList(opts)
|
||||
}
|
||||
|
||||
|
@ -109,14 +128,19 @@ class TransactionStateManager extends EventEmitter {
|
|||
is in its final state
|
||||
it will allso add the key `history` to the txMeta with the snap shot of the original
|
||||
object
|
||||
@param txMeta {Object}
|
||||
@returns {object} the txMeta
|
||||
@param {Object} txMeta
|
||||
@returns {Object} - the txMeta
|
||||
*/
|
||||
addTx (txMeta) {
|
||||
this.once(`${txMeta.id}:signed`, function (txId) {
|
||||
// normalize and validate txParams if present
|
||||
if (txMeta.txParams) {
|
||||
txMeta.txParams = this.normalizeAndValidateTxParams(txMeta.txParams)
|
||||
}
|
||||
|
||||
this.once(`${txMeta.id}:signed`, function () {
|
||||
this.removeAllListeners(`${txMeta.id}:rejected`)
|
||||
})
|
||||
this.once(`${txMeta.id}:rejected`, function (txId) {
|
||||
this.once(`${txMeta.id}:rejected`, function () {
|
||||
this.removeAllListeners(`${txMeta.id}:signed`)
|
||||
})
|
||||
// initialize history
|
||||
|
@ -142,13 +166,18 @@ class TransactionStateManager extends EventEmitter {
|
|||
transactions.splice(index, 1)
|
||||
}
|
||||
}
|
||||
transactions.push(txMeta)
|
||||
const newTxIndex = transactions
|
||||
.findIndex((currentTxMeta) => currentTxMeta.time > txMeta.time)
|
||||
|
||||
newTxIndex === -1
|
||||
? transactions.push(txMeta)
|
||||
: transactions.splice(newTxIndex, 0, txMeta)
|
||||
this._saveTxList(transactions)
|
||||
return txMeta
|
||||
}
|
||||
/**
|
||||
@param txId {number}
|
||||
@returns {object} the txMeta who matches the given id if none found
|
||||
@param {number} txId
|
||||
@returns {Object} - the txMeta who matches the given id if none found
|
||||
for the network returns undefined
|
||||
*/
|
||||
getTx (txId) {
|
||||
|
@ -158,17 +187,13 @@ class TransactionStateManager extends EventEmitter {
|
|||
|
||||
/**
|
||||
updates the txMeta in the list and adds a history entry
|
||||
@param txMeta {Object} - the txMeta to update
|
||||
@param [note] {string} - a note about the update for history
|
||||
@param {Object} txMeta - the txMeta to update
|
||||
@param {string} [note] - a note about the update for history
|
||||
*/
|
||||
updateTx (txMeta, note) {
|
||||
// validate txParams
|
||||
// normalize and validate txParams if present
|
||||
if (txMeta.txParams) {
|
||||
if (typeof txMeta.txParams.data === 'undefined') {
|
||||
delete txMeta.txParams.data
|
||||
}
|
||||
|
||||
this.validateTxParams(txMeta.txParams)
|
||||
txMeta.txParams = this.normalizeAndValidateTxParams(txMeta.txParams)
|
||||
}
|
||||
|
||||
// create txMeta snapshot for history
|
||||
|
@ -182,7 +207,7 @@ class TransactionStateManager extends EventEmitter {
|
|||
// commit txMeta to state
|
||||
const txId = txMeta.id
|
||||
const txList = this.getFullTxList()
|
||||
const index = txList.findIndex(txData => txData.id === txId)
|
||||
const index = txList.findIndex((txData) => txData.id === txId)
|
||||
txList[index] = txMeta
|
||||
this._saveTxList(txList)
|
||||
}
|
||||
|
@ -191,18 +216,31 @@ class TransactionStateManager extends EventEmitter {
|
|||
/**
|
||||
merges txParams obj onto txMeta.txParams
|
||||
use extend to ensure that all fields are filled
|
||||
@param txId {number} - the id of the txMeta
|
||||
@param txParams {object} - the updated txParams
|
||||
@param {number} txId - the id of the txMeta
|
||||
@param {Object} txParams - the updated txParams
|
||||
*/
|
||||
updateTxParams (txId, txParams) {
|
||||
const txMeta = this.getTx(txId)
|
||||
txMeta.txParams = extend(txMeta.txParams, txParams)
|
||||
txMeta.txParams = { ...txMeta.txParams, ...txParams }
|
||||
this.updateTx(txMeta, `txStateManager#updateTxParams`)
|
||||
}
|
||||
|
||||
/**
|
||||
* normalize and validate txParams members
|
||||
* @param {Object} txParams - txParams
|
||||
*/
|
||||
normalizeAndValidateTxParams (txParams) {
|
||||
if (typeof txParams.data === 'undefined') {
|
||||
delete txParams.data
|
||||
}
|
||||
txParams = normalizeTxParams(txParams, false)
|
||||
this.validateTxParams(txParams)
|
||||
return txParams
|
||||
}
|
||||
|
||||
/**
|
||||
validates txParams members by type
|
||||
@param txParams {object} - txParams to validate
|
||||
@param {Object} txParams - txParams to validate
|
||||
*/
|
||||
validateTxParams (txParams) {
|
||||
Object.keys(txParams).forEach((key) => {
|
||||
|
@ -210,26 +248,31 @@ class TransactionStateManager extends EventEmitter {
|
|||
// validate types
|
||||
switch (key) {
|
||||
case 'chainId':
|
||||
if (typeof value !== 'number' && typeof value !== 'string') throw new Error(`${key} in txParams is not a Number or hex string. got: (${value})`)
|
||||
if (typeof value !== 'number' && typeof value !== 'string') {
|
||||
throw new Error(`${key} in txParams is not a Number or hex string. got: (${value})`)
|
||||
}
|
||||
break
|
||||
default:
|
||||
if (typeof value !== 'string') throw new Error(`${key} in txParams is not a string. got: (${value})`)
|
||||
if (!ethUtil.isHexPrefixed(value)) throw new Error(`${key} in txParams is not hex prefixed. got: (${value})`)
|
||||
if (typeof value !== 'string') {
|
||||
throw new Error(`${key} in txParams is not a string. got: (${value})`)
|
||||
}
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@param opts {object} - an object of fields to search for eg:<br>
|
||||
/**
|
||||
@param {Object} opts - an object of fields to search for eg:<br>
|
||||
let <code>thingsToLookFor = {<br>
|
||||
to: '0x0..',<br>
|
||||
from: '0x0..',<br>
|
||||
status: 'signed',<br>
|
||||
status: 'signed', \\ (status) => status !== 'rejected' give me all txs who's status is not rejected<br>
|
||||
err: undefined,<br>
|
||||
}<br></code>
|
||||
optionally the values of the keys can be functions for situations like where
|
||||
you want all but one status.
|
||||
@param [initialList=this.getTxList()]
|
||||
@returns a {array} of txMeta with all
|
||||
@returns {array} - array of txMeta with all
|
||||
options matching
|
||||
*/
|
||||
/*
|
||||
|
@ -243,7 +286,7 @@ class TransactionStateManager extends EventEmitter {
|
|||
|
||||
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
|
||||
or for filtering for all txs from one account
|
||||
and that have been 'confirmed'
|
||||
*/
|
||||
getFilteredTxList (opts, initialList) {
|
||||
|
@ -255,18 +298,20 @@ class TransactionStateManager extends EventEmitter {
|
|||
}
|
||||
/**
|
||||
|
||||
@param key {string} - the key to check
|
||||
@param value - the value your looking for
|
||||
@param {string} key - the key to check
|
||||
@param value - the value your looking for can also be a function that returns a bool
|
||||
@param [txList=this.getTxList()] {array} - the list to search. default is the txList
|
||||
from txStateManager#getTxList
|
||||
@returns {array} a list of txMetas who matches the search params
|
||||
@returns {array} - a list of txMetas who matches the search params
|
||||
*/
|
||||
getTxsByMetaData (key, value, txList = this.getTxList()) {
|
||||
const filter = typeof value === 'function' ? value : (v) => v === value
|
||||
|
||||
return txList.filter((txMeta) => {
|
||||
if (key in txMeta.txParams) {
|
||||
return txMeta.txParams[key] === value
|
||||
return filter(txMeta.txParams[key])
|
||||
} else {
|
||||
return txMeta[key] === value
|
||||
return filter(txMeta[key])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -274,8 +319,8 @@ class TransactionStateManager extends EventEmitter {
|
|||
// get::set status
|
||||
|
||||
/**
|
||||
@param txId {number} - the txMeta Id
|
||||
@return {string} the status of the tx.
|
||||
@param {number} txId - the txMeta Id
|
||||
@returns {string} - the status of the tx.
|
||||
*/
|
||||
getTxStatus (txId) {
|
||||
const txMeta = this.getTx(txId)
|
||||
|
@ -284,7 +329,7 @@ class TransactionStateManager extends EventEmitter {
|
|||
|
||||
/**
|
||||
should update the status of the tx to 'rejected'.
|
||||
@param txId {number} - the txMeta Id
|
||||
@param {number} txId - the txMeta Id
|
||||
*/
|
||||
setTxStatusRejected (txId) {
|
||||
this._setTxStatus(txId, 'rejected')
|
||||
|
@ -293,14 +338,14 @@ class TransactionStateManager extends EventEmitter {
|
|||
|
||||
/**
|
||||
should update the status of the tx to 'unapproved'.
|
||||
@param txId {number} - the txMeta Id
|
||||
@param {number} txId - the txMeta Id
|
||||
*/
|
||||
setTxStatusUnapproved (txId) {
|
||||
this._setTxStatus(txId, 'unapproved')
|
||||
}
|
||||
/**
|
||||
should update the status of the tx to 'approved'.
|
||||
@param txId {number} - the txMeta Id
|
||||
@param {number} txId - the txMeta Id
|
||||
*/
|
||||
setTxStatusApproved (txId) {
|
||||
this._setTxStatus(txId, 'approved')
|
||||
|
@ -308,7 +353,7 @@ class TransactionStateManager extends EventEmitter {
|
|||
|
||||
/**
|
||||
should update the status of the tx to 'signed'.
|
||||
@param txId {number} - the txMeta Id
|
||||
@param {number} txId - the txMeta Id
|
||||
*/
|
||||
setTxStatusSigned (txId) {
|
||||
this._setTxStatus(txId, 'signed')
|
||||
|
@ -317,7 +362,7 @@ class TransactionStateManager extends EventEmitter {
|
|||
/**
|
||||
should update the status of the tx to 'submitted'.
|
||||
and add a time stamp for when it was called
|
||||
@param txId {number} - the txMeta Id
|
||||
@param {number} txId - the txMeta Id
|
||||
*/
|
||||
setTxStatusSubmitted (txId) {
|
||||
const txMeta = this.getTx(txId)
|
||||
|
@ -328,7 +373,7 @@ class TransactionStateManager extends EventEmitter {
|
|||
|
||||
/**
|
||||
should update the status of the tx to 'confirmed'.
|
||||
@param txId {number} - the txMeta Id
|
||||
@param {number} txId - the txMeta Id
|
||||
*/
|
||||
setTxStatusConfirmed (txId) {
|
||||
this._setTxStatus(txId, 'confirmed')
|
||||
|
@ -336,7 +381,7 @@ class TransactionStateManager extends EventEmitter {
|
|||
|
||||
/**
|
||||
should update the status of the tx to 'dropped'.
|
||||
@param txId {number} - the txMeta Id
|
||||
@param {number} txId - the txMeta Id
|
||||
*/
|
||||
setTxStatusDropped (txId) {
|
||||
this._setTxStatus(txId, 'dropped')
|
||||
|
@ -346,24 +391,26 @@ class TransactionStateManager extends EventEmitter {
|
|||
/**
|
||||
should update the status of the tx to 'failed'.
|
||||
and put the error on the txMeta
|
||||
@param txId {number} - the txMeta Id
|
||||
@param err {erroObject} - error object
|
||||
@param {number} txId - the txMeta Id
|
||||
@param {erroObject} err - error object
|
||||
*/
|
||||
setTxStatusFailed (txId, err) {
|
||||
const error = !err ? new Error('Internal metamask failure') : err
|
||||
|
||||
const txMeta = this.getTx(txId)
|
||||
txMeta.err = {
|
||||
message: (err ? (err.message || err.error || err) : '').toString(),
|
||||
rpc: err.value,
|
||||
stack: err.stack,
|
||||
message: error.toString(),
|
||||
rpc: error.value,
|
||||
stack: error.stack,
|
||||
}
|
||||
this.updateTx(txMeta)
|
||||
this.updateTx(txMeta, 'transactions:tx-state-manager#fail - add error')
|
||||
this._setTxStatus(txId, 'failed')
|
||||
}
|
||||
|
||||
/**
|
||||
Removes transaction from the given address for the current network
|
||||
from the txList
|
||||
@param address {string} - hex string of the from address on the txParams to remove
|
||||
@param {string} address - hex string of the from address on the txParams to remove
|
||||
*/
|
||||
wipeTransactions (address) {
|
||||
// network only tx
|
||||
|
@ -376,9 +423,9 @@ class TransactionStateManager extends EventEmitter {
|
|||
// Update state
|
||||
this._saveTxList(otherAccountTxs)
|
||||
}
|
||||
//
|
||||
// PRIVATE METHODS
|
||||
//
|
||||
//
|
||||
// PRIVATE METHODS
|
||||
//
|
||||
|
||||
// STATUS METHODS
|
||||
// statuses:
|
||||
|
@ -392,8 +439,8 @@ class TransactionStateManager extends EventEmitter {
|
|||
// - `'dropped'` the tx nonce was already used
|
||||
|
||||
/**
|
||||
@param txId {number} - the txMeta Id
|
||||
@param status {string} - the status to set on the txMeta
|
||||
@param {number} txId - the txMeta Id
|
||||
@param {string} status - the status to set on the txMeta
|
||||
@emits tx:status-update - passes txId and status
|
||||
@emits ${txMeta.id}:finished - if it is a finished state. Passes the txMeta
|
||||
@emits update:badge
|
||||
|
@ -423,7 +470,7 @@ class TransactionStateManager extends EventEmitter {
|
|||
|
||||
/**
|
||||
Saves the new/updated txList.
|
||||
@param transactions {array} - the list of transactions to save
|
||||
@param {array} transactions - the list of transactions to save
|
||||
*/
|
||||
// Function is intended only for internal use
|
||||
_saveTxList (transactions) {
|
||||
|
@ -436,4 +483,4 @@ class TransactionStateManager extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = TransactionStateManager
|
||||
export default TransactionStateManager
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/**
|
||||
* Returns error without stack trace for better UI display
|
||||
* @param {Error} err - error
|
||||
* @returns {Error} Error with clean stack trace.
|
||||
* @returns {Error} - Error with clean stack trace.
|
||||
*/
|
||||
function cleanErrorStack (err) {
|
||||
var name = err.name
|
||||
let name = err.name
|
||||
name = (name === undefined) ? 'Error' : String(name)
|
||||
|
||||
var msg = err.message
|
||||
let msg = err.message
|
||||
msg = (msg === undefined) ? '' : String(msg)
|
||||
|
||||
if (name === '') {
|
||||
|
@ -21,4 +21,4 @@ function cleanErrorStack (err) {
|
|||
return err
|
||||
}
|
||||
|
||||
module.exports = cleanErrorStack
|
||||
export default cleanErrorStack
|
||||
|
|
|
@ -4674,7 +4674,6 @@
|
|||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
|
||||
"integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"object-assign": "^4.1.1",
|
||||
"util": "0.10.3"
|
||||
|
@ -4683,14 +4682,12 @@
|
|||
"inherits": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
|
||||
"integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
|
||||
"dev": true
|
||||
"integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE="
|
||||
},
|
||||
"util": {
|
||||
"version": "0.10.3",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
|
||||
"integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "2.0.1"
|
||||
}
|
||||
|
@ -45273,6 +45270,16 @@
|
|||
"integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8=",
|
||||
"dev": true
|
||||
},
|
||||
"nonce-tracker": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nonce-tracker/-/nonce-tracker-1.0.0.tgz",
|
||||
"integrity": "sha512-hxKokxgLvOZx9A5qPQKwL34G1/YwMC5xJWZHFUKfvwxypkn2nP0KVJjbcoXwY6pXsRRa11KdFEPW61N4YCGnWQ==",
|
||||
"requires": {
|
||||
"assert": "^1.4.1",
|
||||
"await-semaphore": "^0.1.3",
|
||||
"ethjs-query": "^0.3.8"
|
||||
}
|
||||
},
|
||||
"nopt": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
|
||||
|
|
|
@ -157,6 +157,7 @@
|
|||
"multihashes": "^0.4.12",
|
||||
"nanoid": "^2.1.6",
|
||||
"nifty-wallet-inpage-provider": "github:poanetwork/nifty-wallet-inpage-provider#1.2.3",
|
||||
"nonce-tracker": "^1.0.0",
|
||||
"number-to-bn": "^1.7.0",
|
||||
"obj-multiplex": "^1.0.0",
|
||||
"obs-store": "^4.0.3",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const assert = require('assert')
|
||||
const cleanErrorStack = require('../../../app/scripts/lib/cleanErrorStack')
|
||||
import assert from 'assert'
|
||||
import cleanErrorStack from '../../../app/scripts/lib/cleanErrorStack'
|
||||
|
||||
describe('Clean Error Stack', () => {
|
||||
describe('Clean Error Stack', function () {
|
||||
|
||||
const testMessage = 'Test Message'
|
||||
const testError = new Error(testMessage)
|
||||
|
@ -9,24 +9,24 @@ describe('Clean Error Stack', () => {
|
|||
const blankErrorName = new Error(testMessage)
|
||||
const blankMsgError = new Error()
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(function () {
|
||||
undefinedErrorName.name = undefined
|
||||
blankErrorName.name = ''
|
||||
})
|
||||
|
||||
it('tests error with message', () => {
|
||||
it('tests error with message', function () {
|
||||
assert.equal(cleanErrorStack(testError), 'Error: Test Message')
|
||||
})
|
||||
|
||||
it('tests error with undefined name', () => {
|
||||
it('tests error with undefined name', function () {
|
||||
assert.equal(cleanErrorStack(undefinedErrorName).toString(), 'Error: Test Message')
|
||||
})
|
||||
|
||||
it('tests error with blank name', () => {
|
||||
it('tests error with blank name', function () {
|
||||
assert.equal(cleanErrorStack(blankErrorName).toString(), 'Test Message')
|
||||
})
|
||||
|
||||
it('tests error with blank message', () => {
|
||||
it('tests error with blank message', function () {
|
||||
assert.equal(cleanErrorStack(blankMsgError), 'Error')
|
||||
})
|
||||
|
||||
|
|
|
@ -1,238 +0,0 @@
|
|||
const assert = require('assert')
|
||||
const NonceTracker = require('../../../../../app/scripts/controllers/transactions/nonce-tracker')
|
||||
const MockTxGen = require('../../../../lib/mock-tx-gen')
|
||||
const providerResultStub = {}
|
||||
|
||||
describe('Nonce Tracker', function () {
|
||||
let nonceTracker, pendingTxs, confirmedTxs
|
||||
|
||||
describe('#getNonceLock', function () {
|
||||
|
||||
describe('with 3 confirmed and 1 pending', function () {
|
||||
beforeEach(function () {
|
||||
const txGen = new MockTxGen()
|
||||
confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 3 })
|
||||
pendingTxs = txGen.generate({ status: 'submitted' }, { count: 1 })
|
||||
nonceTracker = generateNonceTrackerWith(pendingTxs, confirmedTxs, '0x1')
|
||||
})
|
||||
|
||||
it('should return 4', async function () {
|
||||
this.timeout(15000)
|
||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
|
||||
assert.equal(nonceLock.nextNonce, '4', `nonce should be 4 got ${nonceLock.nextNonce}`)
|
||||
await nonceLock.releaseLock()
|
||||
})
|
||||
|
||||
it('should use localNonce if network returns a nonce lower then a confirmed tx in state', async function () {
|
||||
this.timeout(15000)
|
||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
|
||||
assert.equal(nonceLock.nextNonce, '4', 'nonce should be 4')
|
||||
await nonceLock.releaseLock()
|
||||
})
|
||||
})
|
||||
|
||||
describe('sentry issue 476304902', function () {
|
||||
beforeEach(function () {
|
||||
const txGen = new MockTxGen()
|
||||
pendingTxs = txGen.generate({ status: 'submitted' }, {
|
||||
fromNonce: 3,
|
||||
count: 29,
|
||||
})
|
||||
nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x3')
|
||||
})
|
||||
|
||||
it('should return 9', async function () {
|
||||
this.timeout(15000)
|
||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
|
||||
assert.equal(nonceLock.nextNonce, '32', `nonce should be 32 got ${nonceLock.nextNonce}`)
|
||||
await nonceLock.releaseLock()
|
||||
})
|
||||
})
|
||||
|
||||
describe('issue 3670', function () {
|
||||
beforeEach(function () {
|
||||
const txGen = new MockTxGen()
|
||||
pendingTxs = txGen.generate({ status: 'submitted' }, {
|
||||
fromNonce: 6,
|
||||
count: 3,
|
||||
})
|
||||
nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x6')
|
||||
})
|
||||
|
||||
it('should return 9', async function () {
|
||||
this.timeout(15000)
|
||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
|
||||
assert.equal(nonceLock.nextNonce, '9', `nonce should be 9 got ${nonceLock.nextNonce}`)
|
||||
await nonceLock.releaseLock()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with no previous txs', function () {
|
||||
beforeEach(function () {
|
||||
nonceTracker = generateNonceTrackerWith([], [])
|
||||
})
|
||||
|
||||
it('should return 0', async function () {
|
||||
this.timeout(15000)
|
||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
|
||||
assert.equal(nonceLock.nextNonce, '0', `nonce should be 0 returned ${nonceLock.nextNonce}`)
|
||||
await nonceLock.releaseLock()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with multiple previous txs with same nonce', function () {
|
||||
beforeEach(function () {
|
||||
const txGen = new MockTxGen()
|
||||
confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 1 })
|
||||
pendingTxs = txGen.generate({
|
||||
status: 'submitted',
|
||||
txParams: { nonce: '0x01' },
|
||||
}, { count: 5 })
|
||||
|
||||
nonceTracker = generateNonceTrackerWith(pendingTxs, confirmedTxs, '0x0')
|
||||
})
|
||||
|
||||
it('should return nonce after those', async function () {
|
||||
this.timeout(15000)
|
||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
|
||||
assert.equal(nonceLock.nextNonce, '2', `nonce should be 2 got ${nonceLock.nextNonce}`)
|
||||
await nonceLock.releaseLock()
|
||||
})
|
||||
})
|
||||
|
||||
describe('when local confirmed count is higher than network nonce', function () {
|
||||
beforeEach(function () {
|
||||
const txGen = new MockTxGen()
|
||||
confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 3 })
|
||||
nonceTracker = generateNonceTrackerWith([], confirmedTxs, '0x1')
|
||||
})
|
||||
|
||||
it('should return nonce after those', async function () {
|
||||
this.timeout(15000)
|
||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
|
||||
assert.equal(nonceLock.nextNonce, '3', `nonce should be 3 got ${nonceLock.nextNonce}`)
|
||||
await nonceLock.releaseLock()
|
||||
})
|
||||
})
|
||||
|
||||
describe('when local pending count is higher than other metrics', function () {
|
||||
beforeEach(function () {
|
||||
const txGen = new MockTxGen()
|
||||
pendingTxs = txGen.generate({ status: 'submitted' }, { count: 2 })
|
||||
nonceTracker = generateNonceTrackerWith(pendingTxs, [])
|
||||
})
|
||||
|
||||
it('should return nonce after those', async function () {
|
||||
this.timeout(15000)
|
||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
|
||||
assert.equal(nonceLock.nextNonce, '2', `nonce should be 2 got ${nonceLock.nextNonce}`)
|
||||
await nonceLock.releaseLock()
|
||||
})
|
||||
})
|
||||
|
||||
describe('when provider nonce is higher than other metrics', function () {
|
||||
beforeEach(function () {
|
||||
const txGen = new MockTxGen()
|
||||
pendingTxs = txGen.generate({ status: 'submitted' }, { count: 2 })
|
||||
nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x05')
|
||||
})
|
||||
|
||||
it('should return nonce after those', async function () {
|
||||
this.timeout(15000)
|
||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
|
||||
assert.equal(nonceLock.nextNonce, '5', `nonce should be 5 got ${nonceLock.nextNonce}`)
|
||||
await nonceLock.releaseLock()
|
||||
})
|
||||
})
|
||||
|
||||
describe('when there are some pending nonces below the remote one and some over.', function () {
|
||||
beforeEach(function () {
|
||||
const txGen = new MockTxGen()
|
||||
pendingTxs = txGen.generate({ status: 'submitted' }, { count: 5 })
|
||||
nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x03')
|
||||
})
|
||||
|
||||
it('should return nonce after those', async function () {
|
||||
this.timeout(15000)
|
||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
|
||||
assert.equal(nonceLock.nextNonce, '5', `nonce should be 5 got ${nonceLock.nextNonce}`)
|
||||
await nonceLock.releaseLock()
|
||||
})
|
||||
})
|
||||
|
||||
describe('when there are pending nonces non sequentially over the network nonce.', function () {
|
||||
beforeEach(function () {
|
||||
const txGen = new MockTxGen()
|
||||
txGen.generate({ status: 'submitted' }, { count: 5 })
|
||||
// 5 over that number
|
||||
pendingTxs = txGen.generate({ status: 'submitted' }, { count: 5 })
|
||||
nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x00')
|
||||
})
|
||||
|
||||
it('should return nonce after network nonce', async function () {
|
||||
this.timeout(15000)
|
||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
|
||||
assert.equal(nonceLock.nextNonce, '0', `nonce should be 0 got ${nonceLock.nextNonce}`)
|
||||
await nonceLock.releaseLock()
|
||||
})
|
||||
})
|
||||
|
||||
describe('When all three return different values', function () {
|
||||
beforeEach(function () {
|
||||
const txGen = new MockTxGen()
|
||||
confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 10 })
|
||||
pendingTxs = txGen.generate({
|
||||
status: 'submitted',
|
||||
nonce: 100,
|
||||
}, { count: 1 })
|
||||
// 0x32 is 50 in hex:
|
||||
nonceTracker = generateNonceTrackerWith(pendingTxs, confirmedTxs, '0x32')
|
||||
})
|
||||
|
||||
it('should return nonce after network nonce', async function () {
|
||||
this.timeout(15000)
|
||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
|
||||
assert.equal(nonceLock.nextNonce, '50', `nonce should be 50 got ${nonceLock.nextNonce}`)
|
||||
await nonceLock.releaseLock()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Faq issue 67', function () {
|
||||
beforeEach(function () {
|
||||
const txGen = new MockTxGen()
|
||||
confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 64 })
|
||||
pendingTxs = txGen.generate({
|
||||
status: 'submitted',
|
||||
}, { count: 10 })
|
||||
// 0x40 is 64 in hex:
|
||||
nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x40')
|
||||
})
|
||||
|
||||
it('should return nonce after network nonce', async function () {
|
||||
this.timeout(15000)
|
||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
|
||||
assert.equal(nonceLock.nextNonce, '74', `nonce should be 74 got ${nonceLock.nextNonce}`)
|
||||
await nonceLock.releaseLock()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function generateNonceTrackerWith (pending, confirmed, providerStub = '0x0') {
|
||||
const getPendingTransactions = () => pending
|
||||
const getConfirmedTransactions = () => confirmed
|
||||
providerResultStub.result = providerStub
|
||||
const provider = {
|
||||
sendAsync: (_, cb) => { cb(undefined, providerResultStub) },
|
||||
}
|
||||
const blockTracker = {
|
||||
getCurrentBlock: () => '0x11b568',
|
||||
getLatestBlock: async () => '0x11b568',
|
||||
}
|
||||
return new NonceTracker({
|
||||
provider,
|
||||
blockTracker,
|
||||
getPendingTransactions,
|
||||
getConfirmedTransactions,
|
||||
})
|
||||
}
|
|
@ -417,28 +417,27 @@ describe('Transaction Controller', function () {
|
|||
})
|
||||
|
||||
describe('#retryTransaction', function () {
|
||||
it('should create a new txMeta with the same txParams as the original one but with a higher gasPrice', function (done) {
|
||||
it('should create a new txMeta with the same txParams as the original one', function (done) {
|
||||
const txParams = {
|
||||
gasPrice: '0xee6b2800',
|
||||
nonce: '0x00',
|
||||
from: '0xB09d8505E1F4EF1CeA089D47094f5DD3464083d4',
|
||||
to: '0xB09d8505E1F4EF1CeA089D47094f5DD3464083d4',
|
||||
from: '0xb09d8505e1f4ef1cea089d47094f5dd3464083d4',
|
||||
to: '0xb09d8505e1f4ef1cea089d47094f5dd3464083d4',
|
||||
data: '0x0',
|
||||
}
|
||||
txController.txStateManager._saveTxList([
|
||||
{ id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams, history: [{}] },
|
||||
{ id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams, history: [] },
|
||||
])
|
||||
txController.retryTransaction(1)
|
||||
.then((txMeta) => {
|
||||
assert.equal(txMeta.txParams.gasPrice, '0x10642ac00', 'gasPrice should have a %10 gasPrice bump')
|
||||
assert.equal(txMeta.txParams.nonce, txParams.nonce, 'nonce should be the same')
|
||||
assert.equal(txMeta.txParams.from, txParams.from, 'from should be the same')
|
||||
assert.equal(txMeta.txParams.to, txParams.to, 'to should be the same')
|
||||
assert.equal(txMeta.txParams.data, txParams.data, 'data should be the same')
|
||||
assert.ok(('lastGasPrice' in txMeta), 'should have the key `lastGasPrice`')
|
||||
assert.equal(txController.txStateManager.getTxList().length, 2)
|
||||
done()
|
||||
}).catch(done)
|
||||
.then((txMeta) => {
|
||||
assert.equal(txMeta.txParams.nonce, txParams.nonce, 'nonce should be the same')
|
||||
assert.equal(txMeta.txParams.from, txParams.from, 'from should be the same')
|
||||
assert.equal(txMeta.txParams.to, txParams.to, 'to should be the same')
|
||||
assert.equal(txMeta.txParams.data, txParams.data, 'data should be the same')
|
||||
assert.ok(('lastGasPrice' in txMeta), 'should have the key `lastGasPrice`')
|
||||
assert.equal(txController.txStateManager.getTxList().length, 2)
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const assert = require('assert')
|
||||
const TxStateManager = require('../../../../../app/scripts/controllers/transactions/tx-state-manager')
|
||||
import assert from 'assert'
|
||||
import TxStateManager from '../../../../../app/scripts/controllers/transactions/tx-state-manager'
|
||||
import txStateHistoryHelper from '../../../../../app/scripts/controllers/transactions/lib/tx-state-history-helper'
|
||||
const noop = () => true
|
||||
|
||||
|
@ -29,7 +29,7 @@ describe('TransactionStateManager', function () {
|
|||
assert.equal(result[0].status, 'signed')
|
||||
})
|
||||
|
||||
it('should emit a signed event to signal the exciton of callback', (done) => {
|
||||
it('should emit a signed event to signal the exciton of callback', function (done) {
|
||||
const tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
|
||||
const noop = function () {
|
||||
assert(true, 'event listener has been triggered and noop executed')
|
||||
|
@ -43,7 +43,7 @@ describe('TransactionStateManager', function () {
|
|||
})
|
||||
|
||||
describe('#setTxStatusRejected', function () {
|
||||
it('sets the tx status to rejected and removes it from history', function () {
|
||||
it('sets the tx status to rejected and removes it from history', function () {
|
||||
const tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
|
||||
txStateManager.addTx(tx)
|
||||
txStateManager.setTxStatusRejected(1)
|
||||
|
@ -52,15 +52,12 @@ describe('TransactionStateManager', function () {
|
|||
assert.equal(result.length, 0)
|
||||
})
|
||||
|
||||
it('should emit a rejected event to signal the exciton of callback', (done) => {
|
||||
it('should emit a rejected event to signal the exciton of callback', function (done) {
|
||||
const tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
|
||||
txStateManager.addTx(tx)
|
||||
const noop = function (err, txId) {
|
||||
if (err) {
|
||||
console.log('Error: ', err)
|
||||
}
|
||||
assert(true, 'event listener has been triggered and noop executed')
|
||||
done()
|
||||
const noop = () => {
|
||||
assert(true, 'event listener has been triggered and noop executed')
|
||||
done()
|
||||
}
|
||||
txStateManager.on('1:rejected', noop)
|
||||
txStateManager.setTxStatusRejected(1)
|
||||
|
@ -93,6 +90,37 @@ describe('TransactionStateManager', function () {
|
|||
assert.equal(result[0].id, 1)
|
||||
})
|
||||
|
||||
it('throws error and does not add tx if txParams are invalid', function () {
|
||||
const validTxParams = {
|
||||
from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
|
||||
to: '0x0039f22efb07a647557c7c5d17854cfd6d489ef3',
|
||||
nonce: '0x3',
|
||||
gas: '0x77359400',
|
||||
gasPrice: '0x77359400',
|
||||
value: '0x0',
|
||||
data: '0x0',
|
||||
}
|
||||
const invalidValues = [1, true, {}, Symbol('1')]
|
||||
|
||||
for (const key in validTxParams) {
|
||||
for (const value of invalidValues) {
|
||||
const tx = {
|
||||
id: 1,
|
||||
status: 'unapproved',
|
||||
metamaskNetworkId: currentNetworkId,
|
||||
txParams: {
|
||||
...validTxParams,
|
||||
[key]: value,
|
||||
},
|
||||
}
|
||||
assert.throws(txStateManager.addTx.bind(txStateManager, tx), 'addTx should throw error')
|
||||
const result = txStateManager.getTxList()
|
||||
assert.ok(Array.isArray(result), 'txList should be an array')
|
||||
assert.equal(result.length, 0, 'txList should be empty')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('does not override txs from other networks', function () {
|
||||
const tx = { id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
|
||||
const tx2 = { id: 2, status: 'confirmed', metamaskNetworkId: otherNetworkId, txParams: {} }
|
||||
|
@ -153,6 +181,37 @@ describe('TransactionStateManager', function () {
|
|||
assert.equal(result.hash, 'foo')
|
||||
})
|
||||
|
||||
it('throws error and does not update tx if txParams are invalid', function () {
|
||||
const validTxParams = {
|
||||
from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
|
||||
to: '0x0039f22efb07a647557c7c5d17854cfd6d489ef3',
|
||||
nonce: '0x3',
|
||||
gas: '0x77359400',
|
||||
gasPrice: '0x77359400',
|
||||
value: '0x0',
|
||||
data: '0x0',
|
||||
}
|
||||
const invalidValues = [1, true, {}, Symbol('1')]
|
||||
|
||||
txStateManager.addTx({ id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: validTxParams })
|
||||
|
||||
for (const key in validTxParams) {
|
||||
for (const value of invalidValues) {
|
||||
const originalTx = txStateManager.getTx(1)
|
||||
const newTx = {
|
||||
...originalTx,
|
||||
txParams: {
|
||||
...originalTx.txParams,
|
||||
[key]: value,
|
||||
},
|
||||
}
|
||||
assert.throws(txStateManager.updateTx.bind(txStateManager, newTx), 'updateTx should throw an error')
|
||||
const result = txStateManager.getTx(1)
|
||||
assert.deepEqual(result, originalTx, 'tx should not be updated')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('updates gas price and adds history items', function () {
|
||||
const originalGasPrice = '0x01'
|
||||
const desiredGasPrice = '0x02'
|
||||
|
@ -241,6 +300,8 @@ describe('TransactionStateManager', function () {
|
|||
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)}`)
|
||||
filterParams = { status: (status) => status !== 'confirmed' }
|
||||
assert.equal(txStateManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -283,19 +344,18 @@ describe('TransactionStateManager', function () {
|
|||
|
||||
assert.equal(txsFromCurrentNetworkAndAddress.length, 0)
|
||||
assert.equal(txFromOtherNetworks.length, 2)
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
describe('#_removeTx', function () {
|
||||
it('should remove the transaction from the storage', () => {
|
||||
txStateManager._saveTxList([ {id: 1} ])
|
||||
it('should remove the transaction from the storage', function () {
|
||||
txStateManager._saveTxList([ { id: 1 } ])
|
||||
txStateManager._removeTx(1)
|
||||
assert(!txStateManager.getFullTxList().length, 'txList should be empty')
|
||||
})
|
||||
|
||||
it('should only remove the transaction with ID 1 from the storage', () => {
|
||||
txStateManager._saveTxList([ {id: 1}, {id: 2} ])
|
||||
it('should only remove the transaction with ID 1 from the storage', function () {
|
||||
txStateManager._saveTxList([ { id: 1 }, { id: 2 } ])
|
||||
txStateManager._removeTx(1)
|
||||
assert.equal(txStateManager.getFullTxList()[0].id, 2, 'txList should have a id of 2')
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue