diff options
author | Dan Finlay <dan@danfinlay.com> | 2017-07-21 03:39:06 +0800 |
---|---|---|
committer | Dan Finlay <dan@danfinlay.com> | 2017-07-21 03:39:06 +0800 |
commit | a41e5788cf4a8e680fdbc4d16690a8f5cc8af265 (patch) | |
tree | 5c546e149ce0cf2d88bbef1d1645b124f11d0eb5 /app | |
parent | 86d367957fe8ac04462f716fe0ba2bfa4e5ff3f6 (diff) | |
parent | 9b24ab0e70d72471ddc928a763bc2096790ab966 (diff) | |
download | tangerine-wallet-browser-a41e5788cf4a8e680fdbc4d16690a8f5cc8af265.tar tangerine-wallet-browser-a41e5788cf4a8e680fdbc4d16690a8f5cc8af265.tar.gz tangerine-wallet-browser-a41e5788cf4a8e680fdbc4d16690a8f5cc8af265.tar.bz2 tangerine-wallet-browser-a41e5788cf4a8e680fdbc4d16690a8f5cc8af265.tar.lz tangerine-wallet-browser-a41e5788cf4a8e680fdbc4d16690a8f5cc8af265.tar.xz tangerine-wallet-browser-a41e5788cf4a8e680fdbc4d16690a8f5cc8af265.tar.zst tangerine-wallet-browser-a41e5788cf4a8e680fdbc4d16690a8f5cc8af265.zip |
Merge branch 'master' into NewUI
Diffstat (limited to 'app')
-rw-r--r-- | app/manifest.json | 7 | ||||
-rw-r--r-- | app/scripts/blacklister.js | 13 | ||||
-rw-r--r-- | app/scripts/controllers/infura.js | 1 | ||||
-rw-r--r-- | app/scripts/controllers/transactions.js | 223 | ||||
-rw-r--r-- | app/scripts/lib/auto-faucet.js | 20 | ||||
-rw-r--r-- | app/scripts/lib/nodeify.js | 27 | ||||
-rw-r--r-- | app/scripts/lib/nonce-tracker.js | 84 | ||||
-rw-r--r-- | app/scripts/lib/tx-utils.js | 18 | ||||
-rw-r--r-- | app/scripts/metamask-controller.js | 44 | ||||
-rw-r--r-- | app/scripts/migrations/016.js | 41 | ||||
-rw-r--r-- | app/scripts/migrations/017.js | 40 | ||||
-rw-r--r-- | app/scripts/migrations/index.js | 2 |
12 files changed, 364 insertions, 156 deletions
diff --git a/app/manifest.json b/app/manifest.json index 12ff6c2ea..eadd99590 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "MetaMask", "short_name": "Metamask", - "version": "3.8.2", + "version": "3.9.1", "manifest_version": 2, "author": "https://metamask.io", "description": "Ethereum Browser Extension", @@ -52,6 +52,11 @@ ], "run_at": "document_start", "all_frames": true + }, + { + "run_at": "document_start", + "matches": ["http://*/*", "https://*/*"], + "js": ["scripts/blacklister.js"] } ], "permissions": [ diff --git a/app/scripts/blacklister.js b/app/scripts/blacklister.js new file mode 100644 index 000000000..a45265a75 --- /dev/null +++ b/app/scripts/blacklister.js @@ -0,0 +1,13 @@ +const blacklistedDomains = require('etheraddresslookup/blacklists/domains.json')
+
+function detectBlacklistedDomain() {
+ var strCurrentTab = window.location.hostname
+ if (blacklistedDomains && blacklistedDomains.includes(strCurrentTab)) {
+ window.location.href = 'https://metamask.io/phishing.html'
+ }
+}
+
+window.addEventListener('load', function() {
+ detectBlacklistedDomain()
+})
+
diff --git a/app/scripts/controllers/infura.js b/app/scripts/controllers/infura.js index 98375b446..b34b0bc03 100644 --- a/app/scripts/controllers/infura.js +++ b/app/scripts/controllers/infura.js @@ -26,6 +26,7 @@ class InfuraController { this.store.updateState({ infuraNetworkStatus: parsedResponse, }) + return parsedResponse }) } diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index 52251d66e..5f3d84ebe 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -1,12 +1,12 @@ const EventEmitter = require('events') const async = require('async') const extend = require('xtend') -const Semaphore = require('semaphore') const ObservableStore = require('obs-store') const ethUtil = require('ethereumjs-util') +const pify = require('pify') const TxProviderUtil = require('../lib/tx-utils') const createId = require('../lib/random-id') -const denodeify = require('denodeify') +const NonceTracker = require('../lib/nonce-tracker') module.exports = class TransactionController extends EventEmitter { constructor (opts) { @@ -20,13 +20,26 @@ module.exports = class TransactionController extends EventEmitter { this.txHistoryLimit = opts.txHistoryLimit this.provider = opts.provider this.blockTracker = opts.blockTracker + this.nonceTracker = new NonceTracker({ + provider: this.provider, + blockTracker: this.provider._blockTracker, + getPendingTransactions: (address) => { + return this.getFilteredTxList({ + from: address, + status: 'submitted', + err: undefined, + }) + }, + }) this.query = opts.ethQuery this.txProviderUtils = new TxProviderUtil(this.query) this.blockTracker.on('rawBlock', this.checkForTxInBlock.bind(this)) - this.blockTracker.on('latest', this.resubmitPendingTxs.bind(this)) + // this is a little messy but until ethstore has been either + // removed or redone this is to guard against the race condition + // where ethStore hasent been populated by the results yet + this.blockTracker.once('latest', () => this.blockTracker.on('latest', this.resubmitPendingTxs.bind(this))) this.blockTracker.on('sync', this.queryPendingTxs.bind(this)) this.signEthTx = opts.signTransaction - this.nonceLock = Semaphore(1) this.ethStore = opts.ethStore // memstore is computed from a few different stores this._updateMemstore() @@ -170,29 +183,32 @@ module.exports = class TransactionController extends EventEmitter { }, {}) } - approveTransaction (txId, cb = warn) { - const self = this - // approve - self.setTxStatusApproved(txId) - // only allow one tx at a time for atomic nonce usage - self.nonceLock.take(() => { - // begin signature process - async.waterfall([ - (cb) => self.fillInTxParams(txId, cb), - (cb) => self.signTransaction(txId, cb), - (rawTx, cb) => self.publishTransaction(txId, rawTx, cb), - ], (err) => { - self.nonceLock.leave() - if (err) { - this.setTxStatusFailed(txId, { - errCode: err.errCode || err, - message: err.message || 'Transaction failed during approval', - }) - return cb(err) - } - cb() + async approveTransaction (txId) { + let nonceLock + try { + // approve + this.setTxStatusApproved(txId) + // get next nonce + const txMeta = this.getTx(txId) + const fromAddress = txMeta.txParams.from + nonceLock = await this.nonceTracker.getNonceLock(fromAddress) + txMeta.txParams.nonce = nonceLock.nextNonce + this.updateTx(txMeta) + // sign transaction + const rawTx = await this.signTransaction(txId) + await this.publishTransaction(txId, rawTx) + // must set transaction to submitted/failed before releasing lock + nonceLock.releaseLock() + } catch (err) { + this.setTxStatusFailed(txId, { + errCode: err.errCode || err, + message: err.message || 'Transaction failed during approval', }) - }) + // must set transaction to submitted/failed before releasing lock + if (nonceLock) nonceLock.releaseLock() + // continue with error chain + throw err + } } cancelTransaction (txId, cb = warn) { @@ -200,13 +216,9 @@ module.exports = class TransactionController extends EventEmitter { cb() } - fillInTxParams (txId, cb) { - const txMeta = this.getTx(txId) - this.txProviderUtils.fillInTxParams(txMeta.txParams, (err) => { - if (err) return cb(err) - this.updateTx(txMeta) - cb() - }) + async updateAndApproveTransaction (txMeta) { + this.updateTx(txMeta) + await this.approveTransaction(txMeta.id) } getChainId () { @@ -219,31 +231,27 @@ module.exports = class TransactionController extends EventEmitter { } } - signTransaction (txId, cb) { + async signTransaction (txId) { const txMeta = this.getTx(txId) const txParams = txMeta.txParams const fromAddress = txParams.from // add network/chain id txParams.chainId = this.getChainId() const ethTx = this.txProviderUtils.buildEthTxFromParams(txParams) - this.signEthTx(ethTx, fromAddress).then(() => { + const rawTx = await this.signEthTx(ethTx, fromAddress).then(() => { this.setTxStatusSigned(txMeta.id) - cb(null, ethUtil.bufferToHex(ethTx.serialize())) - }).catch((err) => { - cb(err) + return ethUtil.bufferToHex(ethTx.serialize()) }) + return rawTx } - publishTransaction (txId, rawTx, cb = warn) { + async publishTransaction (txId, rawTx) { const txMeta = this.getTx(txId) txMeta.rawTx = rawTx this.updateTx(txMeta) - - this.txProviderUtils.publishTransaction(rawTx, (err, txHash) => { - if (err) return cb(err) + await this.txProviderUtils.publishTransaction(rawTx).then((txHash) => { this.setTxHash(txId, txHash) this.setTxStatusSubmitted(txId) - cb() }) } @@ -261,10 +269,19 @@ module.exports = class TransactionController extends EventEmitter { 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 @@ -413,65 +430,103 @@ module.exports = class TransactionController extends EventEmitter { const pending = this.getTxsByMetaData('status', 'submitted') // only try resubmitting if their are transactions to resubmit if (!pending.length) return - const resubmit = denodeify(this._resubmitTx.bind(this)) - Promise.all(pending.map(txMeta => resubmit(txMeta))) - .catch((reason) => { - log.info('Problem resubmitting tx', reason) - }) + pending.forEach((txMeta) => this._resubmitTx(txMeta).catch((err) => { + /* + Dont marked as failed if the error is a "known" transaction warning + "there is already a transaction with the same sender-nonce + but higher/same gas price" + */ + const errorMessage = err.message.toLowerCase() + const isKnownTx = ( + // geth + 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') + // other + || errorMessage.includes('gateway timeout') + || errorMessage.includes('nonce too low') + ) + // ignore resubmit warnings, return early + if (isKnownTx) return + // encountered real error - transition to error state + this.setTxStatusFailed(txMeta.id, { + errCode: err.errCode || err, + message: err.message, + }) + })) } - _resubmitTx (txMeta, cb) { + async _resubmitTx (txMeta, cb) { const address = txMeta.txParams.from const balance = this.ethStore.getState().accounts[address].balance - const nonce = Number.parseInt(this.ethStore.getState().accounts[address].nonce) - const txNonce = Number.parseInt(txMeta.txParams.nonce) - const gtBalance = Number.parseInt(txMeta.txParams.value) > Number.parseInt(balance) if (!('retryCount' in txMeta)) txMeta.retryCount = 0 - // if the value of the transaction is greater then the balance - // or the nonce of the transaction is lower then the accounts nonce - // dont resubmit the tx - if (gtBalance || txNonce < nonce) return cb() + // if the value of the transaction is greater then the balance, fail. + if (!this.txProviderUtils.sufficientBalance(txMeta.txParams, balance)) { + const message = 'Insufficient balance.' + this.setTxStatusFailed(txMeta.id, { message }) + cb() + return log.error(message) + } + // Only auto-submit already-signed txs: if (!('rawTx' in txMeta)) return cb() // Increment a try counter. txMeta.retryCount++ const rawTx = txMeta.rawTx - this.txProviderUtils.publishTransaction(rawTx, cb) + return await this.txProviderUtils.publishTransaction(rawTx, cb) } // checks the network for signed txs and // if confirmed sets the tx status as 'confirmed' - _checkPendingTxs () { - var signedTxList = this.getFilteredTxList({status: 'submitted'}) - if (!signedTxList.length) return - signedTxList.forEach((txMeta) => { - var txHash = txMeta.hash - var txId = txMeta.id - if (!txHash) { - const errReason = { - errCode: 'No hash was provided', - message: 'We had an error while submitting this transaction, please try again.', - } - return this.setTxStatusFailed(txId, errReason) + async _checkPendingTxs () { + const signedTxList = this.getFilteredTxList({status: 'submitted'}) + // in order to keep the nonceTracker accurate we block it while updating pending transactions + const nonceGlobalLock = await this.nonceTracker.getGlobalLock() + try { + await Promise.all(signedTxList.map((txMeta) => this._checkPendingTx(txMeta))) + } catch (err) { + console.error('TransactionController - Error updating pending transactions') + console.error(err) + } + nonceGlobalLock.releaseLock() + } + + async _checkPendingTx (txMeta) { + const txHash = txMeta.hash + const txId = txMeta.id + // extra check in case there was an uncaught error during the + // signature and submission process + if (!txHash) { + const errReason = { + errCode: 'No hash was provided', + message: 'We had an error while submitting this transaction, please try again.', } - this.query.getTransactionByHash(txHash, (err, txParams) => { - if (err || !txParams) { - if (!txParams) return - txMeta.err = { - isWarning: true, - errorCode: err, - message: 'There was a problem loading this transaction.', - } - this.updateTx(txMeta) - return log.error(err) - } - if (txParams.blockNumber) { - this.setTxStatusConfirmed(txId) + this.setTxStatusFailed(txId, errReason) + return + } + // get latest transaction status + let txParams + try { + txParams = await pify((cb) => this.query.getTransactionByHash(txHash, cb))() + if (!txParams) return + if (txParams.blockNumber) { + this.setTxStatusConfirmed(txId) + } + } catch (err) { + if (err || !txParams) { + txMeta.err = { + isWarning: true, + errorCode: err, + message: 'There was a problem loading this transaction.', } - }) - }) + this.updateTx(txMeta) + log.error(err) + } + } } } diff --git a/app/scripts/lib/auto-faucet.js b/app/scripts/lib/auto-faucet.js deleted file mode 100644 index 38d54ba5e..000000000 --- a/app/scripts/lib/auto-faucet.js +++ /dev/null @@ -1,20 +0,0 @@ -const uri = 'https://faucet.metamask.io/' -const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG' -const env = process.env.METAMASK_ENV - -module.exports = function (address) { - // Don't faucet in development or test - if (METAMASK_DEBUG === true || env === 'test') return - global.log.info('auto-fauceting:', address) - const data = address - const headers = new Headers() - headers.append('Content-type', 'application/rawdata') - fetch(uri, { - method: 'POST', - headers, - body: data, - }) - .catch((err) => { - console.error(err) - }) -} diff --git a/app/scripts/lib/nodeify.js b/app/scripts/lib/nodeify.js index 51d89a8fb..299bfe624 100644 --- a/app/scripts/lib/nodeify.js +++ b/app/scripts/lib/nodeify.js @@ -1,24 +1,9 @@ -module.exports = function (promiseFn) { - return function () { - var args = [] - for (var i = 0; i < arguments.length - 1; i++) { - args.push(arguments[i]) - } - var cb = arguments[arguments.length - 1] +const promiseToCallback = require('promise-to-callback') - const nodeified = promiseFn.apply(this, args) - - if (!nodeified) { - const methodName = String(promiseFn).split('(')[0] - throw new Error(`The ${methodName} did not return a Promise, but was nodeified.`) - } - nodeified.then(function (result) { - cb(null, result) - }) - .catch(function (reason) { - cb(reason) - }) - - return nodeified +module.exports = function(fn, context) { + return function(){ + const args = [].slice.call(arguments) + const callback = args.pop() + promiseToCallback(fn.apply(context, args))(callback) } } diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js new file mode 100644 index 000000000..b76dac4e8 --- /dev/null +++ b/app/scripts/lib/nonce-tracker.js @@ -0,0 +1,84 @@ +const EthQuery = require('eth-query') +const assert = require('assert') +const Mutex = require('await-semaphore').Mutex + +class NonceTracker { + + constructor ({ blockTracker, provider, getPendingTransactions }) { + this.blockTracker = blockTracker + this.ethQuery = new EthQuery(provider) + this.getPendingTransactions = getPendingTransactions + this.lockMap = {} + } + + async getGlobalLock () { + const globalMutex = this._lookupMutex('global') + // await global mutex free + const releaseLock = await globalMutex.acquire() + return { releaseLock } + } + + // releaseLock must be called + // releaseLock must be called after adding signed tx to pending transactions (or discarding) + async getNonceLock (address) { + // await global mutex free + await this._globalMutexFree() + // await lock free, then take lock + const releaseLock = await this._takeMutex(address) + // calculate next nonce + // we need to make sure our base count + // and pending count are from the same block + const currentBlock = await this._getCurrentBlock() + const pendingTransactions = this.getPendingTransactions(address) + const pendingCount = pendingTransactions.length + assert(Number.isInteger(pendingCount), 'nonce-tracker - pendingCount is an integer') + const baseCountHex = await this._getTxCount(address, currentBlock) + const baseCount = parseInt(baseCountHex, 16) + assert(Number.isInteger(baseCount), 'nonce-tracker - baseCount is an integer') + const nextNonce = baseCount + pendingCount + assert(Number.isInteger(nextNonce), 'nonce-tracker - nextNonce is an integer') + // return next nonce and release cb + return { nextNonce, releaseLock } + } + + async _getCurrentBlock () { + const currentBlock = this.blockTracker.getCurrentBlock() + if (currentBlock) return currentBlock + return await Promise((reject, resolve) => { + this.blockTracker.once('latest', resolve) + }) + } + + async _getTxCount (address, currentBlock) { + const blockNumber = currentBlock.number + return new Promise((resolve, reject) => { + this.ethQuery.getTransactionCount(address, blockNumber, (err, result) => { + err ? reject(err) : resolve(result) + }) + }) + } + + async _globalMutexFree () { + const globalMutex = this._lookupMutex('global') + const release = await globalMutex.acquire() + release() + } + + 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 + } + +} + +module.exports = NonceTracker diff --git a/app/scripts/lib/tx-utils.js b/app/scripts/lib/tx-utils.js index 149d93102..8f6943937 100644 --- a/app/scripts/lib/tx-utils.js +++ b/app/scripts/lib/tx-utils.js @@ -106,8 +106,13 @@ module.exports = class txProviderUtils { return ethTx } - publishTransaction (rawTx, cb) { - this.query.sendRawTransaction(rawTx, cb) + publishTransaction (rawTx) { + return new Promise((resolve, reject) => { + this.query.sendRawTransaction(rawTx, (err, ress) => { + if (err) reject(err) + else resolve(ress) + }) + }) } validateTxParams (txParams, cb) { @@ -118,6 +123,15 @@ module.exports = class txProviderUtils { } } + sufficientBalance (txParams, hexBalance) { + const balance = hexToBn(hexBalance) + const value = hexToBn(txParams.value) + const gasLimit = hexToBn(txParams.gas) + const gasPrice = hexToBn(txParams.gasPrice) + + const maxCost = value.add(gasLimit.mul(gasPrice)) + return balance.gte(maxCost) + } } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 73093dfad..11dcde2c1 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -20,7 +20,6 @@ const MessageManager = require('./lib/message-manager') const PersonalMessageManager = require('./lib/personal-message-manager') const TransactionController = require('./controllers/transactions') const ConfigManager = require('./lib/config-manager') -const autoFaucet = require('./lib/auto-faucet') const nodeify = require('./lib/nodeify') const accountImporter = require('./account-import-strategies') const getBuyEthUrl = require('./lib/buy-eth-url') @@ -90,9 +89,6 @@ module.exports = class MetamaskController extends EventEmitter { this.keyringController.on('newAccount', (address) => { this.preferencesController.setSelectedAddress(address) }) - this.keyringController.on('newVault', (address) => { - autoFaucet(address) - }) // address book controller this.addressBookController = new AddressBookController({ @@ -294,34 +290,33 @@ module.exports = class MetamaskController extends EventEmitter { submitPassword: this.submitPassword.bind(this), // PreferencesController - setSelectedAddress: nodeify(preferencesController.setSelectedAddress).bind(preferencesController), - addToken: nodeify(preferencesController.addToken).bind(preferencesController), - setCurrentAccountTab: nodeify(preferencesController.setCurrentAccountTab).bind(preferencesController), - setDefaultRpc: nodeify(this.setDefaultRpc).bind(this), - setCustomRpc: nodeify(this.setCustomRpc).bind(this), + setSelectedAddress: nodeify(preferencesController.setSelectedAddress, preferencesController), + addToken: nodeify(preferencesController.addToken, preferencesController), + setCurrentAccountTab: nodeify(preferencesController.setCurrentAccountTab, preferencesController), + setDefaultRpc: nodeify(this.setDefaultRpc, this), + setCustomRpc: nodeify(this.setCustomRpc, this), // AddressController - setAddressBook: nodeify(addressBookController.setAddressBook).bind(addressBookController), + setAddressBook: nodeify(addressBookController.setAddressBook, addressBookController), // KeyringController - setLocked: nodeify(keyringController.setLocked).bind(keyringController), - createNewVaultAndKeychain: nodeify(keyringController.createNewVaultAndKeychain).bind(keyringController), - createNewVaultAndRestore: nodeify(keyringController.createNewVaultAndRestore).bind(keyringController), - addNewKeyring: nodeify(keyringController.addNewKeyring).bind(keyringController), - saveAccountLabel: nodeify(keyringController.saveAccountLabel).bind(keyringController), - exportAccount: nodeify(keyringController.exportAccount).bind(keyringController), + setLocked: nodeify(keyringController.setLocked, keyringController), + createNewVaultAndKeychain: nodeify(keyringController.createNewVaultAndKeychain, keyringController), + createNewVaultAndRestore: nodeify(keyringController.createNewVaultAndRestore, keyringController), + addNewKeyring: nodeify(keyringController.addNewKeyring, keyringController), + saveAccountLabel: nodeify(keyringController.saveAccountLabel, keyringController), + exportAccount: nodeify(keyringController.exportAccount, keyringController), // txController - approveTransaction: txController.approveTransaction.bind(txController), cancelTransaction: txController.cancelTransaction.bind(txController), - updateAndApproveTransaction: this.updateAndApproveTx.bind(this), + updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController), // messageManager - signMessage: nodeify(this.signMessage).bind(this), + signMessage: nodeify(this.signMessage, this), cancelMessage: this.cancelMessage.bind(this), // personalMessageManager - signPersonalMessage: nodeify(this.signPersonalMessage).bind(this), + signPersonalMessage: nodeify(this.signPersonalMessage, this), cancelPersonalMessage: this.cancelPersonalMessage.bind(this), // notices @@ -367,7 +362,7 @@ module.exports = class MetamaskController extends EventEmitter { function onResponse (err, request, response) { if (err) return console.error(err) if (response.error) { - console.error('Error in RPC response:\n', response.error) + console.error('Error in RPC response:\n', response) } if (request.isMetamaskInternal) return log.info(`RPC (${originDomain}):`, request, '->', response) @@ -502,13 +497,6 @@ module.exports = class MetamaskController extends EventEmitter { }) } - updateAndApproveTx (txMeta, cb) { - log.debug(`MetaMaskController - updateAndApproveTx: ${JSON.stringify(txMeta)}`) - const txController = this.txController - txController.updateTx(txMeta) - txController.approveTransaction(txMeta.id, cb) - } - signMessage (msgParams, cb) { log.info('MetaMaskController - signMessage') const msgId = msgParams.metamaskId diff --git a/app/scripts/migrations/016.js b/app/scripts/migrations/016.js new file mode 100644 index 000000000..4fc534f1c --- /dev/null +++ b/app/scripts/migrations/016.js @@ -0,0 +1,41 @@ +const version = 16 + +/* + +This migration sets transactions with the 'Gave up submitting tx.' err message +to a 'failed' stated + +*/ + +const clone = require('clone') + +module.exports = { + version, + + migrate: function (originalVersionedData) { + const versionedData = clone(originalVersionedData) + versionedData.meta.version = version + try { + const state = versionedData.data + const newState = transformState(state) + versionedData.data = newState + } catch (err) { + console.warn(`MetaMask Migration #${version}` + err.stack) + } + return Promise.resolve(versionedData) + }, +} + +function transformState (state) { + const newState = state + const transactions = newState.TransactionController.transactions + newState.TransactionController.transactions = transactions.map((txMeta) => { + if (!txMeta.err) return txMeta + if (txMeta.err === 'transaction with the same hash was already imported.') { + txMeta.status = 'submitted' + delete txMeta.err + } + return txMeta + }) + return newState +} diff --git a/app/scripts/migrations/017.js b/app/scripts/migrations/017.js new file mode 100644 index 000000000..24959cd3a --- /dev/null +++ b/app/scripts/migrations/017.js @@ -0,0 +1,40 @@ +const version = 17 + +/* + +This migration sets transactions who were retried and marked as failed to submitted + +*/ + +const clone = require('clone') + +module.exports = { + version, + + migrate: function (originalVersionedData) { + const versionedData = clone(originalVersionedData) + versionedData.meta.version = version + try { + const state = versionedData.data + const newState = transformState(state) + versionedData.data = newState + } catch (err) { + console.warn(`MetaMask Migration #${version}` + err.stack) + } + return Promise.resolve(versionedData) + }, +} + +function transformState (state) { + const newState = state + const transactions = newState.TransactionController.transactions + newState.TransactionController.transactions = transactions.map((txMeta) => { + if (!txMeta.status === 'failed') return txMeta + if (txMeta.retryCount > 0 && txMeta.retryCount < 2) { + txMeta.status = 'submitted' + delete txMeta.err + } + return txMeta + }) + return newState +} diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index 651ee6a9c..f4c87499f 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -26,4 +26,6 @@ module.exports = [ require('./013'), require('./014'), require('./015'), + require('./016'), + require('./017'), ] |