diff options
Merge pull request #1 from MetaMask/master
Merge from the source
Diffstat (limited to 'app/scripts/lib')
23 files changed, 347 insertions, 54 deletions
diff --git a/app/scripts/lib/account-tracker.js b/app/scripts/lib/account-tracker.js index cdc21282d..8c3dd8c71 100644 --- a/app/scripts/lib/account-tracker.js +++ b/app/scripts/lib/account-tracker.js @@ -38,6 +38,29 @@ class AccountTracker extends EventEmitter { // public // + syncWithAddresses (addresses) { + const accounts = this.store.getState().accounts + const locals = Object.keys(accounts) + + const toAdd = [] + addresses.forEach((upstream) => { + if (!locals.includes(upstream)) { + toAdd.push(upstream) + } + }) + + const toRemove = [] + locals.forEach((local) => { + if (!addresses.includes(local)) { + toRemove.push(local) + } + }) + + toAdd.forEach(upstream => this.addAccount(upstream)) + toRemove.forEach(local => this.removeAccount(local)) + this._updateAccounts() + } + addAccount (address) { const accounts = this.store.getState().accounts accounts[address] = {} @@ -94,8 +117,6 @@ class AccountTracker extends EventEmitter { const query = this._query async.parallel({ balance: query.getBalance.bind(query, address), - nonce: query.getTransactionCount.bind(query, address), - code: query.getCode.bind(query, address), }, cb) } diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index 9c0dffe9c..34b603b96 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -42,6 +42,17 @@ ConfigManager.prototype.getData = function () { return this.store.getState() } +ConfigManager.prototype.setPasswordForgotten = function (passwordForgottenState) { + const data = this.getData() + data.forgottenPassword = passwordForgottenState + this.setData(data) +} + +ConfigManager.prototype.getPasswordForgotten = function (passwordForgottenState) { + const data = this.getData() + return data.forgottenPassword +} + ConfigManager.prototype.setWallet = function (wallet) { var data = this.getData() data.wallet = wallet diff --git a/app/scripts/lib/createLoggerMiddleware.js b/app/scripts/lib/createLoggerMiddleware.js index b92a965de..2707cbd9e 100644 --- a/app/scripts/lib/createLoggerMiddleware.js +++ b/app/scripts/lib/createLoggerMiddleware.js @@ -1,7 +1,7 @@ // log rpc activity module.exports = createLoggerMiddleware -function createLoggerMiddleware({ origin }) { +function createLoggerMiddleware ({ origin }) { return function loggerMiddleware (req, res, next, end) { next((cb) => { if (res.error) { @@ -12,4 +12,4 @@ function createLoggerMiddleware({ origin }) { cb() }) } -}
\ No newline at end of file +} diff --git a/app/scripts/lib/createOriginMiddleware.js b/app/scripts/lib/createOriginMiddleware.js index e1e097cc4..f8bdb2dc2 100644 --- a/app/scripts/lib/createOriginMiddleware.js +++ b/app/scripts/lib/createOriginMiddleware.js @@ -1,9 +1,9 @@ // append dapp origin domain to request module.exports = createOriginMiddleware -function createOriginMiddleware({ origin }) { +function createOriginMiddleware ({ origin }) { return function originMiddleware (req, res, next, end) { req.origin = origin next() } -}
\ No newline at end of file +} diff --git a/app/scripts/lib/createProviderMiddleware.js b/app/scripts/lib/createProviderMiddleware.js index 6dd192411..4e667bac2 100644 --- a/app/scripts/lib/createProviderMiddleware.js +++ b/app/scripts/lib/createProviderMiddleware.js @@ -1,8 +1,7 @@ - module.exports = createProviderMiddleware // forward requests to provider -function createProviderMiddleware({ provider }) { +function createProviderMiddleware ({ provider }) { return (req, res, next, end) => { provider.sendAsync(req, (err, _res) => { if (err) return end(err) @@ -10,4 +9,4 @@ function createProviderMiddleware({ provider }) { end() }) } -}
\ No newline at end of file +} diff --git a/app/scripts/lib/environment-type.js b/app/scripts/lib/environment-type.js new file mode 100644 index 000000000..7966926eb --- /dev/null +++ b/app/scripts/lib/environment-type.js @@ -0,0 +1,10 @@ +module.exports = function environmentType () { + const url = window.location.href + if (url.match(/popup.html$/)) { + return 'popup' + } else if (url.match(/home.html$/)) { + return 'responsive' + } else { + return 'notification' + } +} diff --git a/app/scripts/lib/events-proxy.js b/app/scripts/lib/events-proxy.js index d1199a278..c0a490b05 100644 --- a/app/scripts/lib/events-proxy.js +++ b/app/scripts/lib/events-proxy.js @@ -1,4 +1,4 @@ -module.exports = function createEventEmitterProxy(eventEmitter, listeners) { +module.exports = function createEventEmitterProxy (eventEmitter, listeners) { let target = eventEmitter const eventHandlers = listeners || {} const proxy = new Proxy({}, { @@ -28,4 +28,4 @@ module.exports = function createEventEmitterProxy(eventEmitter, listeners) { } if (listeners) proxy.setTarget(eventEmitter) return proxy -}
\ No newline at end of file +} diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js index da75c4be2..99cc5d2cf 100644 --- a/app/scripts/lib/inpage-provider.js +++ b/app/scripts/lib/inpage-provider.js @@ -3,6 +3,7 @@ const RpcEngine = require('json-rpc-engine') const createIdRemapMiddleware = require('json-rpc-engine/src/idRemapMiddleware') const createStreamMiddleware = require('json-rpc-middleware-stream') const LocalStorageStore = require('obs-store') +const asStream = require('obs-store/lib/asStream') const ObjectMultiplex = require('obj-multiplex') module.exports = MetamaskInpageProvider @@ -21,9 +22,10 @@ function MetamaskInpageProvider (connectionStream) { // subscribe to metamask public config (one-way) self.publicConfigStore = new LocalStorageStore({ storageKey: 'MetaMask-Config' }) + pump( mux.createStream('publicConfig'), - self.publicConfigStore, + asStream(self.publicConfigStore), (err) => logStreamDisconnectWarning('MetaMask PublicConfigStore', err) ) diff --git a/app/scripts/lib/is-popup-or-notification.js b/app/scripts/lib/is-popup-or-notification.js index 693fa8751..e2999411f 100644 --- a/app/scripts/lib/is-popup-or-notification.js +++ b/app/scripts/lib/is-popup-or-notification.js @@ -1,6 +1,9 @@ module.exports = function isPopupOrNotification () { const url = window.location.href - if (url.match(/popup.html$/)) { + // if (url.match(/popup.html$/) || url.match(/home.html$/)) { + // Below regexes needed for feature toggles (e.g. see line ~340 in ui/app/app.js) + // Revert below regexes to above commented out regexes before merge to master + if (url.match(/popup.html(?:\?.+)*$/) || url.match(/home.html(?:\?.+)*$/)) { return 'popup' } else { return 'notification' diff --git a/app/scripts/lib/local-store.js b/app/scripts/lib/local-store.js new file mode 100644 index 000000000..5b47985f6 --- /dev/null +++ b/app/scripts/lib/local-store.js @@ -0,0 +1,62 @@ +// We should not rely on local storage in an extension! +// We should use this instead! +// https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/storage/local + +const extension = require('extensionizer') + +module.exports = class ExtensionStore { + constructor() { + this.isSupported = !!(extension.storage.local) + if (!this.isSupported) { + log.error('Storage local API not available.') + } + } + + async get() { + if (!this.isSupported) return undefined + const result = await this._get() + // extension.storage.local always returns an obj + // if the object is empty, treat it as undefined + if (isEmpty(result)) { + return undefined + } else { + return result + } + } + + async set(state) { + return this._set(state) + } + + _get() { + const local = extension.storage.local + return new Promise((resolve, reject) => { + local.get(null, (result) => { + const err = extension.runtime.lastError + if (err) { + reject(err) + } else { + resolve(result) + } + }) + }) + } + + _set(obj) { + const local = extension.storage.local + return new Promise((resolve, reject) => { + local.set(obj, () => { + const err = extension.runtime.lastError + if (err) { + reject(err) + } else { + resolve() + } + }) + }) + } +} + +function isEmpty(obj) { + return Object.keys(obj).length === 0 +} diff --git a/app/scripts/lib/nodeify.js b/app/scripts/lib/nodeify.js index d24e92206..9b595d93c 100644 --- a/app/scripts/lib/nodeify.js +++ b/app/scripts/lib/nodeify.js @@ -1,8 +1,8 @@ const promiseToCallback = require('promise-to-callback') -const noop = function(){} +const noop = function () {} module.exports = function nodeify (fn, context) { - return function(){ + return function () { const args = [].slice.call(arguments) const lastArg = args[args.length - 1] const lastArgIsCallback = typeof lastArg === 'function' diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index 0029ac953..ed9dd3f11 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -56,7 +56,7 @@ class NonceTracker { const blockTracker = this._getBlockTracker() const currentBlock = blockTracker.getCurrentBlock() if (currentBlock) return currentBlock - return await Promise((reject, resolve) => { + return await new Promise((reject, resolve) => { blockTracker.once('latest', resolve) }) } diff --git a/app/scripts/lib/notification-manager.js b/app/scripts/lib/notification-manager.js index 7846ef7f0..adaf60c65 100644 --- a/app/scripts/lib/notification-manager.js +++ b/app/scripts/lib/notification-manager.js @@ -1,5 +1,5 @@ const extension = require('extensionizer') -const height = 520 +const height = 620 const width = 360 diff --git a/app/scripts/lib/pending-balance-calculator.js b/app/scripts/lib/pending-balance-calculator.js index cea642f1a..6ae526463 100644 --- a/app/scripts/lib/pending-balance-calculator.js +++ b/app/scripts/lib/pending-balance-calculator.js @@ -13,7 +13,7 @@ class PendingBalanceCalculator { this.getNetworkBalance = getBalance } - async getBalance() { + async getBalance () { const results = await Promise.all([ this.getNetworkBalance(), this.getPendingTransactions(), diff --git a/app/scripts/lib/pending-tx-tracker.js b/app/scripts/lib/pending-tx-tracker.js index df504c126..e8869e6b8 100644 --- a/app/scripts/lib/pending-tx-tracker.js +++ b/app/scripts/lib/pending-tx-tracker.js @@ -23,7 +23,6 @@ module.exports = class PendingTransactionTracker extends EventEmitter { this.query = new EthQuery(config.provider) this.nonceTracker = config.nonceTracker // default is one day - this.retryTimePeriod = config.retryTimePeriod || 86400000 this.getPendingTransactions = config.getPendingTransactions this.getCompletedTransactions = config.getCompletedTransactions this.publishTransaction = config.publishTransaction @@ -65,11 +64,11 @@ module.exports = class PendingTransactionTracker extends EventEmitter { } - resubmitPendingTxs () { + resubmitPendingTxs (block) { const pending = this.getPendingTransactions() // only try resubmitting if their are transactions to resubmit if (!pending.length) return - pending.forEach((txMeta) => this._resubmitTx(txMeta).catch((err) => { + pending.forEach((txMeta) => this._resubmitTx(txMeta, block.number).catch((err) => { /* Dont marked as failed if the error is a "known" transaction warning "there is already a transaction with the same sender-nonce @@ -81,14 +80,14 @@ module.exports = class PendingTransactionTracker extends EventEmitter { const errorMessage = err.message.toLowerCase() const isKnownTx = ( // geth - errorMessage.includes('replacement transaction underpriced') - || errorMessage.includes('known transaction') + errorMessage.includes('replacement transaction underpriced') || + errorMessage.includes('known transaction') || // parity - || errorMessage.includes('gas price too low to replace') - || errorMessage.includes('transaction with the same hash was already imported') + errorMessage.includes('gas price too low to replace') || + errorMessage.includes('transaction with the same hash was already imported') || // other - || errorMessage.includes('gateway timeout') - || errorMessage.includes('nonce too low') + errorMessage.includes('gateway timeout') || + errorMessage.includes('nonce too low') ) // ignore resubmit warnings, return early if (isKnownTx) return @@ -101,13 +100,19 @@ module.exports = class PendingTransactionTracker extends EventEmitter { })) } - async _resubmitTx (txMeta) { - if (Date.now() > txMeta.time + this.retryTimePeriod) { - const hours = (this.retryTimePeriod / 3.6e+6).toFixed(1) - const err = new Error(`Gave up submitting after ${hours} hours.`) - return this.emit('tx:failed', txMeta.id, err) + async _resubmitTx (txMeta, latestBlockNumber) { + if (!txMeta.firstRetryBlockNumber) { + this.emit('tx:block-update', txMeta, latestBlockNumber) } + const firstRetryBlockNumber = txMeta.firstRetryBlockNumber || latestBlockNumber + const txBlockDistance = Number.parseInt(latestBlockNumber, 16) - Number.parseInt(firstRetryBlockNumber, 16) + + const retryCount = txMeta.retryCount || 0 + + // Exponential backoff to limit retries at publishing + if (txBlockDistance <= Math.pow(2, retryCount) - 1) return + // Only auto-submit already-signed txs: if (!('rawTx' in txMeta)) return @@ -173,7 +178,8 @@ module.exports = class PendingTransactionTracker extends EventEmitter { } async _checkIfNonceIsTaken (txMeta) { - const completed = this.getCompletedTransactions() + const address = txMeta.txParams.from + const completed = this.getCompletedTransactions(address) const sameNonce = completed.filter((otherMeta) => { return otherMeta.txParams.nonce === txMeta.txParams.nonce }) diff --git a/app/scripts/lib/port-stream.js b/app/scripts/lib/port-stream.js index 648d88087..a9716fb00 100644 --- a/app/scripts/lib/port-stream.js +++ b/app/scripts/lib/port-stream.js @@ -1,6 +1,6 @@ const Duplex = require('readable-stream').Duplex const inherits = require('util').inherits -const noop = function(){} +const noop = function () {} module.exports = PortDuplexStream diff --git a/app/scripts/lib/reportFailedTxToSentry.js b/app/scripts/lib/reportFailedTxToSentry.js new file mode 100644 index 000000000..ee73f6845 --- /dev/null +++ b/app/scripts/lib/reportFailedTxToSentry.js @@ -0,0 +1,38 @@ +const ethJsRpcSlug = 'Error: [ethjs-rpc] rpc error with payload ' +const errorLabelPrefix = 'Error: ' + +module.exports = reportFailedTxToSentry + +// +// utility for formatting failed transaction messages +// for sending to sentry +// + +function reportFailedTxToSentry({ raven, txMeta }) { + const errorMessage = extractErrorMessage(txMeta.err.message) + raven.captureMessage(errorMessage, { + // "extra" key is required by Sentry + extra: txMeta, + }) +} + +// +// ethjs-rpc provides overly verbose error messages +// if we detect this type of message, we extract the important part +// Below is an example input and output +// +// Error: [ethjs-rpc] rpc error with payload {"id":3947817945380,"jsonrpc":"2.0","params":["0xf8eb8208708477359400830398539406012c8cf97bead5deae237070f9587f8e7a266d80b8843d7d3f5a0000000000000000000000000000000000000000000000000000000000081d1a000000000000000000000000000000000000000000000000001ff973cafa800000000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000000000000000000000000000000000000003f48025a04c32a9b630e0d9e7ff361562d850c86b7a884908135956a7e4a336fa0300d19ca06830776423f25218e8d19b267161db526e66895567147015b1f3fc47aef9a3c7"],"method":"eth_sendRawTransaction"} Error: replacement transaction underpriced +// +// Transaction Failed: replacement transaction underpriced +// + +function extractErrorMessage(errorMessage) { + const isEthjsRpcError = errorMessage.includes(ethJsRpcSlug) + if (isEthjsRpcError) { + const payloadAndError = errorMessage.slice(ethJsRpcSlug.length) + const originalError = payloadAndError.slice(payloadAndError.indexOf(errorLabelPrefix) + errorLabelPrefix.length) + return `Transaction Failed: ${originalError}` + } else { + return `Transaction Failed: ${errorMessage}` + } +} diff --git a/app/scripts/lib/seed-phrase-verifier.js b/app/scripts/lib/seed-phrase-verifier.js new file mode 100644 index 000000000..9cea22029 --- /dev/null +++ b/app/scripts/lib/seed-phrase-verifier.js @@ -0,0 +1,48 @@ +const KeyringController = require('eth-keyring-controller') + +const seedPhraseVerifier = { + + // Verifies if the seed words can restore the accounts. + // + // The seed words can recreate the primary keyring and the accounts belonging to it. + // The created accounts in the primary keyring are always the same. + // The keyring always creates the accounts in the same sequence. + verifyAccounts (createdAccounts, seedWords) { + + return new Promise((resolve, reject) => { + + if (!createdAccounts || createdAccounts.length < 1) { + return reject(new Error('No created accounts defined.')) + } + + const keyringController = new KeyringController({}) + const Keyring = keyringController.getKeyringClassForType('HD Key Tree') + const opts = { + mnemonic: seedWords, + numberOfAccounts: createdAccounts.length, + } + + const keyring = new Keyring(opts) + keyring.getAccounts() + .then((restoredAccounts) => { + + log.debug('Created accounts: ' + JSON.stringify(createdAccounts)) + log.debug('Restored accounts: ' + JSON.stringify(restoredAccounts)) + + if (restoredAccounts.length !== createdAccounts.length) { + // this should not happen... + return reject(new Error('Wrong number of accounts')) + } + + for (let i = 0; i < restoredAccounts.length; i++) { + if (restoredAccounts[i].toLowerCase() !== createdAccounts[i].toLowerCase()) { + return reject(new Error('Not identical accounts! Original: ' + createdAccounts[i] + ', Restored: ' + restoredAccounts[i])) + } + } + return resolve() + }) + }) + }, +} + +module.exports = seedPhraseVerifier diff --git a/app/scripts/lib/setupMetamaskMeshMetrics.js b/app/scripts/lib/setupMetamaskMeshMetrics.js new file mode 100644 index 000000000..40343f017 --- /dev/null +++ b/app/scripts/lib/setupMetamaskMeshMetrics.js @@ -0,0 +1,9 @@ + +module.exports = setupMetamaskMeshMetrics + +function setupMetamaskMeshMetrics() { + const testingContainer = document.createElement('iframe') + testingContainer.src = 'https://metamask.github.io/mesh-testing/' + console.log('Injecting MetaMask Mesh testing client') + document.head.appendChild(testingContainer) +} diff --git a/app/scripts/lib/setupRaven.js b/app/scripts/lib/setupRaven.js new file mode 100644 index 000000000..42e48cb90 --- /dev/null +++ b/app/scripts/lib/setupRaven.js @@ -0,0 +1,26 @@ +const Raven = require('../vendor/raven.min.js') +const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG' +const PROD = 'https://3567c198f8a8412082d32655da2961d0@sentry.io/273505' +const DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496' + +module.exports = setupRaven + +// Setup raven / sentry remote error reporting +function setupRaven(opts) { + const { release } = opts + let ravenTarget + + if (METAMASK_DEBUG) { + console.log('Setting up Sentry Remote Error Reporting: DEV') + ravenTarget = DEV + } else { + console.log('Setting up Sentry Remote Error Reporting: PROD') + ravenTarget = PROD + } + + Raven.config(ravenTarget, { + release, + }).install() + + return Raven +} diff --git a/app/scripts/lib/tx-gas-utils.js b/app/scripts/lib/tx-gas-utils.js index 41f67e230..0fa9dd8d4 100644 --- a/app/scripts/lib/tx-gas-utils.js +++ b/app/scripts/lib/tx-gas-utils.js @@ -4,6 +4,8 @@ const { BnMultiplyByFraction, bnToHex, } = require('./util') +const { addHexPrefix, isValidAddress } = require('ethereumjs-util') +const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send. /* tx-utils are utility methods for Transaction manager @@ -11,7 +13,8 @@ its passed ethquery and used to do things like calculate gas of a tx. */ -module.exports = class txProvideUtil { +module.exports = class TxGasUtil { + constructor (provider) { this.query = new EthQuery(provider) } @@ -22,7 +25,11 @@ module.exports = class txProvideUtil { try { estimatedGasHex = await this.estimateTxGas(txMeta, block.gasLimit) } catch (err) { - if (err.message.includes('Transaction execution error.')) { + const simulationFailed = ( + err.message.includes('Transaction execution error.') || + err.message.includes('gas required exceeds allowance or always failing transaction') + ) + if (simulationFailed) { txMeta.simulationFails = true return txMeta } @@ -33,25 +40,41 @@ module.exports = class txProvideUtil { async estimateTxGas (txMeta, blockGasLimitHex) { const txParams = txMeta.txParams + // check if gasLimit is already specified txMeta.gasLimitSpecified = Boolean(txParams.gas) - // if not, fallback to block gasLimit - if (!txMeta.gasLimitSpecified) { - const blockGasLimitBN = hexToBn(blockGasLimitHex) - const saferGasLimitBN = BnMultiplyByFraction(blockGasLimitBN, 19, 20) - txParams.gas = bnToHex(saferGasLimitBN) + + // if it is, use that value + if (txMeta.gasLimitSpecified) { + return txParams.gas } + + // if recipient has no code, gas is 21k max: + const recipient = txParams.to + const hasRecipient = Boolean(recipient) + const code = await this.query.getCode(recipient) + if (hasRecipient && (!code || code === '0x')) { + txParams.gas = SIMPLE_GAS_COST + txMeta.simpleSend = true // Prevents buffer addition + return SIMPLE_GAS_COST + } + + // if not, fall back to block gasLimit + const blockGasLimitBN = hexToBn(blockGasLimitHex) + const saferGasLimitBN = BnMultiplyByFraction(blockGasLimitBN, 19, 20) + txParams.gas = bnToHex(saferGasLimitBN) + // run tx return await this.query.estimateGas(txParams) } setTxGas (txMeta, blockGasLimitHex, estimatedGasHex) { - txMeta.estimatedGas = estimatedGasHex + txMeta.estimatedGas = addHexPrefix(estimatedGasHex) const txParams = txMeta.txParams // if gasLimit was specified and doesnt OOG, // use original specified amount - if (txMeta.gasLimitSpecified) { + if (txMeta.gasLimitSpecified || txMeta.simpleSend) { txMeta.estimatedGas = txParams.gas return } @@ -77,8 +100,28 @@ module.exports = class txProvideUtil { } async validateTxParams (txParams) { - if (('value' in txParams) && txParams.value.indexOf('-') === 0) { - throw new Error(`Invalid transaction value of ${txParams.value} not a positive number.`) + this.validateRecipient(txParams) + if ('value' in txParams) { + const value = txParams.value.toString() + if (value.includes('-')) { + throw new Error(`Invalid transaction value of ${txParams.value} not a positive number.`) + } + + if (value.includes('.')) { + throw new Error(`Invalid transaction value of ${txParams.value} number must be in wei`) + } + } + } + validateRecipient (txParams) { + if (txParams.to === '0x' || txParams.to === null ) { + if (txParams.data) { + delete txParams.to + } else { + throw new Error('Invalid recipient address') + } + } else if ( txParams.to !== undefined && !isValidAddress(txParams.to) ) { + throw new Error('Invalid recipient address') } + return txParams } -}
\ No newline at end of file +} diff --git a/app/scripts/lib/tx-state-history-helper.js b/app/scripts/lib/tx-state-history-helper.js index db6e3bc9f..94c7b6792 100644 --- a/app/scripts/lib/tx-state-history-helper.js +++ b/app/scripts/lib/tx-state-history-helper.js @@ -9,7 +9,7 @@ module.exports = { } -function migrateFromSnapshotsToDiffs(longHistory) { +function migrateFromSnapshotsToDiffs (longHistory) { return ( longHistory // convert non-initial history entries into diffs @@ -20,22 +20,22 @@ function migrateFromSnapshotsToDiffs(longHistory) { ) } -function generateHistoryEntry(previousState, newState, note) { +function generateHistoryEntry (previousState, newState, note) { const entry = jsonDiffer.compare(previousState, newState) // Add a note to the first op, since it breaks if we append it to the entry if (note && entry[0]) entry[0].note = note return entry } -function replayHistory(_shortHistory) { +function replayHistory (_shortHistory) { const shortHistory = clone(_shortHistory) return shortHistory.reduce((val, entry) => jsonDiffer.applyPatch(val, entry).newDocument) } -function snapshotFromTxMeta(txMeta) { +function snapshotFromTxMeta (txMeta) { // create txMeta snapshot for history const snapshot = clone(txMeta) // dont include previous history in this snapshot delete snapshot.history return snapshot -}
\ No newline at end of file +} diff --git a/app/scripts/lib/tx-state-manager.js b/app/scripts/lib/tx-state-manager.js index 2250403f6..2eb006380 100644 --- a/app/scripts/lib/tx-state-manager.js +++ b/app/scripts/lib/tx-state-manager.js @@ -4,7 +4,7 @@ const ObservableStore = require('obs-store') const ethUtil = require('ethereumjs-util') const txStateHistoryHelper = require('./tx-state-history-helper') -module.exports = class TransactionStateManger extends EventEmitter { +module.exports = class TransactionStateManager extends EventEmitter { constructor ({ initState, txHistoryLimit, getNetwork }) { super() @@ -91,7 +91,7 @@ module.exports = class TransactionStateManger extends EventEmitter { updateTx (txMeta, note) { if (txMeta.txParams) { Object.keys(txMeta.txParams).forEach((key) => { - let value = txMeta.txParams[key] + const value = txMeta.txParams[key] if (typeof value !== 'string') console.error(`${key}: ${value} in txParams is not a string`) if (!ethUtil.isHexPrefixed(value)) console.error('is not hex prefixed, anything on txParams must be hex prefixed') }) @@ -187,6 +187,10 @@ module.exports = class TransactionStateManger extends EventEmitter { this._setTxStatus(txId, 'rejected') } + // should update the status of the tx to 'unapproved'. + setTxStatusUnapproved (txId) { + this._setTxStatus(txId, 'unapproved') + } // should update the status of the tx to 'approved'. setTxStatusApproved (txId) { this._setTxStatus(txId, 'approved') @@ -217,6 +221,17 @@ module.exports = class TransactionStateManger extends EventEmitter { this._setTxStatus(txId, 'failed') } + wipeTransactions (address) { + // network only tx + const txs = this.getFullTxList() + const network = this.getNetwork() + + // Filter out the ones from the current account and network + const otherAccountTxs = txs.filter((txMeta) => !(txMeta.txParams.from === address && txMeta.metamaskNetworkId === network)) + + // Update state + this._saveTxList(otherAccountTxs) + } // // PRIVATE METHODS // @@ -236,7 +251,7 @@ module.exports = class TransactionStateManger extends EventEmitter { txMeta.status = status this.emit(`${txMeta.id}:${status}`, txId) this.emit(`tx:status-update`, txId, status) - if (status === 'submitted' || status === 'rejected') { + if (['submitted', 'rejected', 'failed'].includes(status)) { this.emit(`${txMeta.id}:finished`, txMeta) } this.updateTx(txMeta, `txStateManager: setting status to ${status}`) |