aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorDan Finlay <dan@danfinlay.com>2017-07-21 03:39:06 +0800
committerDan Finlay <dan@danfinlay.com>2017-07-21 03:39:06 +0800
commita41e5788cf4a8e680fdbc4d16690a8f5cc8af265 (patch)
tree5c546e149ce0cf2d88bbef1d1645b124f11d0eb5 /app
parent86d367957fe8ac04462f716fe0ba2bfa4e5ff3f6 (diff)
parent9b24ab0e70d72471ddc928a763bc2096790ab966 (diff)
downloadtangerine-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.json7
-rw-r--r--app/scripts/blacklister.js13
-rw-r--r--app/scripts/controllers/infura.js1
-rw-r--r--app/scripts/controllers/transactions.js223
-rw-r--r--app/scripts/lib/auto-faucet.js20
-rw-r--r--app/scripts/lib/nodeify.js27
-rw-r--r--app/scripts/lib/nonce-tracker.js84
-rw-r--r--app/scripts/lib/tx-utils.js18
-rw-r--r--app/scripts/metamask-controller.js44
-rw-r--r--app/scripts/migrations/016.js41
-rw-r--r--app/scripts/migrations/017.js40
-rw-r--r--app/scripts/migrations/index.js2
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'),
]