aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorbrunobar79 <brunobar79@gmail.com>2018-07-04 02:21:17 +0800
committerbrunobar79 <brunobar79@gmail.com>2018-07-04 02:21:17 +0800
commit595447ccac0c6d178d63850d45f0ad5456964e4f (patch)
tree61b83e1ba63615351da8e4c2dc7b446353411c9d /app
parent11736e6318182ab5b43430410a46059e5f46ad52 (diff)
parent2e9bd7e9d101287b4466475561df9131f0ef56a6 (diff)
downloadtangerine-wallet-browser-595447ccac0c6d178d63850d45f0ad5456964e4f.tar
tangerine-wallet-browser-595447ccac0c6d178d63850d45f0ad5456964e4f.tar.gz
tangerine-wallet-browser-595447ccac0c6d178d63850d45f0ad5456964e4f.tar.bz2
tangerine-wallet-browser-595447ccac0c6d178d63850d45f0ad5456964e4f.tar.lz
tangerine-wallet-browser-595447ccac0c6d178d63850d45f0ad5456964e4f.tar.xz
tangerine-wallet-browser-595447ccac0c6d178d63850d45f0ad5456964e4f.tar.zst
tangerine-wallet-browser-595447ccac0c6d178d63850d45f0ad5456964e4f.zip
Merge remote-tracking branch 'upstream/develop' into HEAD
Diffstat (limited to 'app')
-rw-r--r--app/_locales/en/messages.json16
-rw-r--r--app/_locales/ja/messages.json43
-rw-r--r--app/manifest.json6
-rw-r--r--app/scripts/account-import-strategies/index.js13
-rw-r--r--app/scripts/background.js16
-rw-r--r--app/scripts/contentscript.js5
-rw-r--r--app/scripts/controllers/balance.js4
-rw-r--r--app/scripts/controllers/blacklist.js2
-rw-r--r--app/scripts/controllers/computed-balances.js2
-rw-r--r--app/scripts/controllers/currency.js8
-rw-r--r--app/scripts/controllers/network/network.js13
-rw-r--r--app/scripts/controllers/preferences.js81
-rw-r--r--app/scripts/controllers/recent-blocks.js2
-rw-r--r--app/scripts/controllers/transactions/index.js15
-rw-r--r--app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js24
-rw-r--r--app/scripts/controllers/transactions/lib/recipient-blacklist.js17
-rw-r--r--app/scripts/controllers/transactions/nonce-tracker.js54
-rw-r--r--app/scripts/controllers/transactions/pending-tx-tracker.js4
-rw-r--r--app/scripts/controllers/transactions/tx-gas-utils.js2
-rw-r--r--app/scripts/controllers/user-actions.js17
-rw-r--r--app/scripts/inpage.js23
-rw-r--r--app/scripts/lib/cleanErrorStack.js2
-rw-r--r--app/scripts/lib/createErrorMiddleware.js3
-rw-r--r--app/scripts/lib/createStreamSink.js24
-rw-r--r--app/scripts/lib/diagnostics-reporter.js71
-rw-r--r--app/scripts/lib/extractEthjsErrorMessage.js4
-rw-r--r--app/scripts/lib/get-first-preferred-lang-code.js20
-rw-r--r--app/scripts/lib/getObjStructure.js6
-rw-r--r--app/scripts/lib/local-store.js12
-rw-r--r--app/scripts/lib/notification-manager.js10
-rw-r--r--app/scripts/lib/port-stream.js2
-rw-r--r--app/scripts/lib/reportFailedTxToSentry.js2
-rw-r--r--app/scripts/lib/setupMetamaskMeshMetrics.js2
-rw-r--r--app/scripts/lib/setupRaven.js16
-rw-r--r--app/scripts/metamask-controller.js77
-rw-r--r--app/scripts/migrations/013.js2
-rw-r--r--app/scripts/migrations/023.js4
-rw-r--r--app/scripts/migrations/026.js2
-rw-r--r--app/scripts/notice-controller.js33
-rw-r--r--app/scripts/popup-core.js2
-rw-r--r--app/scripts/ui.js2
41 files changed, 522 insertions, 141 deletions
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 40d362f51..46fbdc1a7 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -146,6 +146,9 @@
"copy": {
"message": "Copy"
},
+ "copyContractAddress": {
+ "message": "Copy Contract Address"
+ },
"copyToClipboard": {
"message": "Copy to clipboard"
},
@@ -253,12 +256,18 @@
"editAccountName": {
"message": "Edit Account Name"
},
+ "editingTransaction": {
+ "message": "Make changes to your transaction"
+ },
"emailUs": {
"message": "Email us!"
},
"encryptNewDen": {
"message": "Encrypt your new DEN"
},
+ "ensNameNotFound": {
+ "message": "ENS name not found"
+ },
"enterPassword": {
"message": "Enter password"
},
@@ -771,6 +780,10 @@
"onlySendToEtherAddress": {
"message": "Only send ETH to an Ethereum address."
},
+ "onlySendTokensToAccountAddress": {
+ "message": "Only send $1 to an Ethereum account address.",
+ "description": "displays token symbol"
+ },
"searchTokens": {
"message": "Search Tokens"
},
@@ -948,6 +961,9 @@
"viewAccount": {
"message": "View Account"
},
+ "viewOnEtherscan": {
+ "message": "View on Etherscan"
+ },
"visitWebSite": {
"message": "Visit our web site"
},
diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json
index 3a664ec00..75deeaddf 100644
--- a/app/_locales/ja/messages.json
+++ b/app/_locales/ja/messages.json
@@ -62,6 +62,9 @@
"message": " $1以上 $2以下にして下さい。",
"description": "helper for inputting hex as decimal input"
},
+ "blockiesIdenticon": {
+ "message": "Blockies Identicon を使用"
+ },
"borrowDharma": {
"message": "Dharmaで借りる(ベータ版)"
},
@@ -95,6 +98,9 @@
"confirmTransaction": {
"message": "トランザクションの確認"
},
+ "continue": {
+ "message": "続行"
+ },
"continueToCoinbase": {
"message": "Coinbaseを開く"
},
@@ -359,6 +365,9 @@
"likeToAddTokens": {
"message": "トークンを追加しますか?"
},
+ "links": {
+ "message": "リンク"
+ },
"limit": {
"message": "リミット"
},
@@ -371,12 +380,18 @@
"localhost": {
"message": "Localhost 8545"
},
+ "login": {
+ "message": "ログイン"
+ },
"logout": {
"message": "ログアウト"
},
"loose": {
"message": "外部秘密鍵"
},
+ "max": {
+ "message": "最大"
+ },
"mainnet": {
"message": "Ethereumメインネットワーク"
},
@@ -417,7 +432,7 @@
"message": "新規コントラクト"
},
"newPassword": {
- "message": "新規パスワード(最低8文字)"
+ "message": "新規パスワード(最低8文字)"
},
"newRecipient": {
"message": "新規受取人"
@@ -453,6 +468,9 @@
"message": "または",
"description": "choice between creating or importing a new account"
},
+ "password": {
+ "message": "パスワード"
+ },
"passwordMismatch": {
"message": "パスワードが一致しません。",
"description": "in password creation process, the two new password fields did not match"
@@ -474,6 +492,9 @@
"popularTokens": {
"message": "人気のトークン"
},
+ "privacyMsg": {
+ "message": "プライバシーポリシー"
+ },
"privateKey": {
"message": "秘密鍵",
"description": "select this type of file to use to import an account"
@@ -546,6 +567,12 @@
"message": "ファイルとして保存",
"description": "Account export process"
},
+ "search": {
+ "message": "検索"
+ },
+ "searchResults": {
+ "message": "検索結果"
+ },
"selectService": {
"message": "サービスを選択"
},
@@ -575,7 +602,7 @@
},
"info": {
"message": "情報"
- },
+ },
"shapeshiftBuy": {
"message": "Shapeshiftで交換"
},
@@ -609,6 +636,9 @@
"takesTooLong": {
"message": "送信に時間がかかりますか?"
},
+ "terms": {
+ "message": "利用規約"
+ },
"testFaucet": {
"message": "Faucetをテスト"
},
@@ -619,6 +649,9 @@
"message": "ShapeShiftで $1をETHにする",
"description": "system will fill in deposit type in start of message"
},
+ "token": {
+ "message": "トークン"
+ },
"tokenAddress": {
"message": "トークンアドレス"
},
@@ -690,6 +723,12 @@
"warning": {
"message": "警告"
},
+ "welcomeBack": {
+ "message": "おかえりなさい!"
+ },
+ "welcomeBeta": {
+ "message": "MetaMask ベータ版へようこそ!"
+ },
"whatsThis": {
"message": "この機能について"
},
diff --git a/app/manifest.json b/app/manifest.json
index 027f9a045..a226adfb0 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,7 +1,7 @@
{
"name": "__MSG_appName__",
"short_name": "__MSG_appName__",
- "version": "4.6.1",
+ "version": "4.8.0",
"manifest_version": 2,
"author": "https://metamask.io",
"description": "__MSG_appDescription__",
@@ -71,6 +71,8 @@
"matches": [
"https://metamask.io/*"
],
- "ids": ["*"]
+ "ids": [
+ "*"
+ ]
}
}
diff --git a/app/scripts/account-import-strategies/index.js b/app/scripts/account-import-strategies/index.js
index 96e2b5912..16ae224ea 100644
--- a/app/scripts/account-import-strategies/index.js
+++ b/app/scripts/account-import-strategies/index.js
@@ -16,7 +16,18 @@ const accountImporter = {
strategies: {
'Private Key': (privateKey) => {
- const stripped = ethUtil.stripHexPrefix(privateKey)
+ if (!privateKey) {
+ throw new Error('Cannot import an empty key.')
+ }
+
+ const prefixed = ethUtil.addHexPrefix(privateKey)
+ const buffer = ethUtil.toBuffer(prefixed)
+
+ if (!ethUtil.isValidPrivate(buffer)) {
+ throw new Error('Cannot import invalid private key.')
+ }
+
+ const stripped = ethUtil.stripHexPrefix(prefixed)
return stripped
},
'JSON File': (input, password) => {
diff --git a/app/scripts/background.js b/app/scripts/background.js
index 610409975..f751867cc 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -16,6 +16,7 @@ const ExtensionPlatform = require('./platforms/extension')
const Migrator = require('./lib/migrator/')
const migrations = require('./migrations/')
const PortStream = require('./lib/port-stream.js')
+const createStreamSink = require('./lib/createStreamSink')
const NotificationManager = require('./lib/notification-manager.js')
const MetamaskController = require('./metamask-controller')
const firstTimeState = require('./first-time-state')
@@ -279,7 +280,7 @@ function setupController (initState, initLangCode) {
asStream(controller.store),
debounce(1000),
storeTransform(versionifyData),
- storeTransform(persistData),
+ createStreamSink(persistData),
(error) => {
log.error('MetaMask - Persistence pipeline failed', error)
}
@@ -295,7 +296,7 @@ function setupController (initState, initLangCode) {
return versionedData
}
- function persistData (state) {
+ async function persistData (state) {
if (!state) {
throw new Error('MetaMask - updated state is missing', state)
}
@@ -303,12 +304,13 @@ function setupController (initState, initLangCode) {
throw new Error('MetaMask - updated state does not have data', state)
}
if (localStore.isSupported) {
- localStore.set(state)
- .catch((err) => {
+ try {
+ await localStore.set(state)
+ } catch (err) {
+ // log error so we dont break the pipeline
log.error('error setting state in local store:', err)
- })
+ }
}
- return state
}
//
@@ -382,7 +384,7 @@ function setupController (initState, initLangCode) {
}
// communication with page or other extension
- function connectExternal(remotePort) {
+ function connectExternal (remotePort) {
const originDomain = urlUtil.parse(remotePort.sender.url).hostname
const portStream = new PortStream(remotePort)
controller.setupUntrustedCommunication(portStream, originDomain)
diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js
index 555902ddf..b35a70dd2 100644
--- a/app/scripts/contentscript.js
+++ b/app/scripts/contentscript.js
@@ -115,8 +115,8 @@ function logStreamDisconnectWarning (remoteLabel, err) {
* @returns {boolean} {@code true} if Web3 should be injected
*/
function shouldInjectWeb3 () {
- return doctypeCheck() && suffixCheck()
- && documentElementCheck() && !blacklistedDomainCheck()
+ return doctypeCheck() && suffixCheck() &&
+ documentElementCheck() && !blacklistedDomainCheck()
}
/**
@@ -176,6 +176,7 @@ function blacklistedDomainCheck () {
'webbyawards.com',
'cdn.shopify.com/s/javascripts/tricorder/xtld-read-only-frame.html',
'adyen.com',
+ 'gravityforms.com',
]
var currentUrl = window.location.href
var currentRegex
diff --git a/app/scripts/controllers/balance.js b/app/scripts/controllers/balance.js
index 86619fce1..4c97810a3 100644
--- a/app/scripts/controllers/balance.js
+++ b/app/scripts/controllers/balance.js
@@ -60,7 +60,7 @@ class BalanceController {
* Sets up listeners and subscriptions which should trigger an update of ethBalance. These updates include:
* - when a transaction changes state to 'submitted', 'confirmed' or 'failed'
* - when the current account changes (i.e. a new account is selected)
- * - when there is a block update
+ * - when there is a block update
*
* @private
*
@@ -100,7 +100,7 @@ class BalanceController {
/**
* Gets the pending transactions (i.e. those with a 'submitted' status). These are accessed from the
- * TransactionController passed to this BalanceController during construction.
+ * TransactionController passed to this BalanceController during construction.
*
* @private
* @returns {Promise<array>} Promises an array of transaction objects.
diff --git a/app/scripts/controllers/blacklist.js b/app/scripts/controllers/blacklist.js
index f100c4525..1d2191433 100644
--- a/app/scripts/controllers/blacklist.js
+++ b/app/scripts/controllers/blacklist.js
@@ -87,7 +87,7 @@ class BlacklistController {
*
* @private
* @param {object} config A config object like that found at {@link https://github.com/MetaMask/eth-phishing-detect/blob/master/src/config.json}
- *
+ *
*/
_setupPhishingDetector (config) {
this._phishingDetector = new PhishingDetector(config)
diff --git a/app/scripts/controllers/computed-balances.js b/app/scripts/controllers/computed-balances.js
index 1a6802f9a..e04ce2ef7 100644
--- a/app/scripts/controllers/computed-balances.js
+++ b/app/scripts/controllers/computed-balances.js
@@ -18,7 +18,7 @@ class ComputedbalancesController {
/**
* Creates a new controller instance
*
- * @param {ComputedBalancesOptions} [opts] Controller configuration parameters
+ * @param {ComputedBalancesOptions} [opts] Controller configuration parameters
*/
constructor (opts = {}) {
const { accountTracker, txController, blockTracker } = opts
diff --git a/app/scripts/controllers/currency.js b/app/scripts/controllers/currency.js
index 480c08b1c..a93aff49b 100644
--- a/app/scripts/controllers/currency.js
+++ b/app/scripts/controllers/currency.js
@@ -16,9 +16,9 @@ class CurrencyController {
* currentCurrency, conversionRate and conversionDate properties
* @property {string} currentCurrency A 2-4 character shorthand that describes a specific currency, currently
* selected by the user
- * @property {number} conversionRate The conversion rate from ETH to the selected currency.
+ * @property {number} conversionRate The conversion rate from ETH to the selected currency.
* @property {string} conversionDate The date at which the conversion rate was set. Expressed in in milliseconds
- * since midnight of January 1, 1970
+ * since midnight of January 1, 1970
* @property {number} conversionInterval The id of the interval created by the scheduleConversionInterval method.
* Used to clear an existing interval on subsequent calls of that method.
*
@@ -59,7 +59,7 @@ class CurrencyController {
/**
* A getter for the conversionRate property
*
- * @returns {string} The conversion rate from ETH to the selected currency.
+ * @returns {string} The conversion rate from ETH to the selected currency.
*
*/
getConversionRate () {
@@ -80,7 +80,7 @@ class CurrencyController {
* A getter for the conversionDate property
*
* @returns {string} The date at which the conversion rate was set. Expressed in milliseconds since midnight of
- * January 1, 1970
+ * January 1, 1970
*
*/
getConversionDate () {
diff --git a/app/scripts/controllers/network/network.js b/app/scripts/controllers/network/network.js
index 93fde7c57..a50f6dc45 100644
--- a/app/scripts/controllers/network/network.js
+++ b/app/scripts/controllers/network/network.js
@@ -89,14 +89,21 @@ module.exports = class NetworkController extends EventEmitter {
type: 'rpc',
rpcTarget,
}
- this.providerStore.updateState(providerConfig)
- this._switchNetwork(providerConfig)
+ this.providerConfig = providerConfig
}
async setProviderType (type) {
assert.notEqual(type, 'rpc', `NetworkController - cannot call "setProviderType" with type 'rpc'. use "setRpcTarget"`)
assert(INFURA_PROVIDER_TYPES.includes(type) || type === LOCALHOST, `NetworkController - Unknown rpc type "${type}"`)
const providerConfig = { type }
+ this.providerConfig = providerConfig
+ }
+
+ resetConnection () {
+ this.providerConfig = this.getProviderConfig()
+ }
+
+ set providerConfig (providerConfig) {
this.providerStore.updateState(providerConfig)
this._switchNetwork(providerConfig)
}
@@ -125,7 +132,7 @@ module.exports = class NetworkController extends EventEmitter {
} else if (type === LOCALHOST) {
this._configureStandardProvider({ rpcUrl: LOCALHOST_RPC_URL })
// url-based rpc endpoints
- } else if (type === 'rpc'){
+ } else if (type === 'rpc') {
this._configureStandardProvider({ rpcUrl: rpcTarget })
} else {
throw new Error(`NetworkController - _configureProvider - unknown type "${type}"`)
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index a4ff1207e..b314745f5 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -2,6 +2,7 @@ const ObservableStore = require('obs-store')
const normalizeAddress = require('eth-sig-util').normalize
const extend = require('xtend')
+
class PreferencesController {
/**
@@ -28,7 +29,11 @@ class PreferencesController {
featureFlags: {},
currentLocale: opts.initLangCode,
identities: {},
+ lostIdentities: {},
}, opts.initState)
+
+ this.diagnostics = opts.diagnostics
+
this.store = new ObservableStore(initState)
}
// PUBLIC METHODS
@@ -63,6 +68,13 @@ class PreferencesController {
this.store.updateState({ currentLocale: key })
}
+ /**
+ * Updates identities to only include specified addresses. Removes identities
+ * not included in addresses array
+ *
+ * @param {string[]} addresses An array of hex addresses
+ *
+ */
setAddresses (addresses) {
const oldIdentities = this.store.getState().identities
const identities = addresses.reduce((ids, address, index) => {
@@ -74,6 +86,68 @@ class PreferencesController {
}
/**
+ * Adds addresses to the identities object without removing identities
+ *
+ * @param {string[]} addresses An array of hex addresses
+ *
+ */
+ addAddresses (addresses) {
+ const identities = this.store.getState().identities
+ addresses.forEach((address) => {
+ // skip if already exists
+ if (identities[address]) return
+ // add missing identity
+ const identityCount = Object.keys(identities).length
+ identities[address] = { name: `Account ${identityCount + 1}`, address }
+ })
+ this.store.updateState({ identities })
+ }
+
+ /*
+ * Synchronizes identity entries with known accounts.
+ * Removes any unknown identities, and returns the resulting selected address.
+ *
+ * @param {Array<string>} addresses known to the vault.
+ * @returns {Promise<string>} selectedAddress the selected address.
+ */
+ syncAddresses (addresses) {
+ const { identities, lostIdentities } = this.store.getState()
+
+ const newlyLost = {}
+ Object.keys(identities).forEach((identity) => {
+ if (!addresses.includes(identity)) {
+ newlyLost[identity] = identities[identity]
+ delete identities[identity]
+ }
+ })
+
+ // Identities are no longer present.
+ if (Object.keys(newlyLost).length > 0) {
+
+ // Notify our servers:
+ if (this.diagnostics) this.diagnostics.reportOrphans(newlyLost)
+
+ // store lost accounts
+ for (const key in newlyLost) {
+ lostIdentities[key] = newlyLost[key]
+ }
+ }
+
+ this.store.updateState({ identities, lostIdentities })
+ this.addAddresses(addresses)
+
+ // If the selected account is no longer valid,
+ // select an arbitrary other account:
+ let selected = this.getSelectedAddress()
+ if (!addresses.includes(selected)) {
+ selected = addresses[0]
+ this.setSelectedAddress(selected)
+ }
+
+ return selected
+ }
+
+ /**
* Setter for the `selectedAddress` property
*
* @param {string} _address A new hex address for an account
@@ -111,7 +185,7 @@ class PreferencesController {
/**
* Adds a new token to the token array, or updates the token if passed an address that already exists.
* Modifies the existing tokens array from the store. All objects in the tokens array array AddedToken objects.
- * @see AddedToken {@link AddedToken}
+ * @see AddedToken {@link AddedToken}
*
* @param {string} rawAddress Hex address of the token contract. May or may not be a checksum address.
* @param {string} symbol The symbol of the token
@@ -173,6 +247,7 @@ class PreferencesController {
* @return {Promise<string>}
*/
setAccountLabel (account, label) {
+ if (!account) throw new Error('setAccountLabel requires a valid address, got ' + String(account))
const address = normalizeAddress(account)
const {identities} = this.store.getState()
identities[address] = identities[address] || {}
@@ -197,7 +272,7 @@ class PreferencesController {
}
/**
- * Setter for the `currentAccountTab` property
+ * Setter for the `currentAccountTab` property
*
* @param {string} currentAccountTab Specifies the new tab to be marked as current
* @returns {Promise<void>} Promise resolves with undefined
@@ -215,7 +290,7 @@ class PreferencesController {
* The returned list will have a max length of 2. If the _url currently exists it the list, it will be moved to the
* end of the list. The current list is modified and returned as a promise.
*
- * @param {string} _url The rpc url to add to the frequentRpcList.
+ * @param {string} _url The rpc url to add to the frequentRpcList.
* @returns {Promise<array>} The updated frequentRpcList.
*
*/
diff --git a/app/scripts/controllers/recent-blocks.js b/app/scripts/controllers/recent-blocks.js
index 033ef1d7e..926268691 100644
--- a/app/scripts/controllers/recent-blocks.js
+++ b/app/scripts/controllers/recent-blocks.js
@@ -117,7 +117,7 @@ class RecentBlocksController {
*
* @returns {Promise<void>} Promises undefined
*/
- async backfill() {
+ async backfill () {
this.blockTracker.once('block', async (block) => {
const currentBlockNumber = Number.parseInt(block.number, 16)
const blocksToFetch = Math.min(currentBlockNumber, this.historyLength)
diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js
index aff5db984..8e2288aed 100644
--- a/app/scripts/controllers/transactions/index.js
+++ b/app/scripts/controllers/transactions/index.js
@@ -10,6 +10,7 @@ const NonceTracker = require('./nonce-tracker')
const txUtils = require('./lib/util')
const cleanErrorStack = require('../../lib/cleanErrorStack')
const log = require('loglevel')
+const recipientBlacklistChecker = require('./lib/recipient-blacklist-checker')
/**
Transaction Controller is an aggregate of sub-controllers and trackers
@@ -157,11 +158,14 @@ class TransactionController extends EventEmitter {
let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams })
this.addTx(txMeta)
this.emit('newUnapprovedTx', txMeta)
- // add default tx params
+
try {
+ // check whether recipient account is blacklisted
+ recipientBlacklistChecker.checkAccount(txMeta.metamaskNetworkId, normalizedTxParams.to)
+ // add default tx params
txMeta = await this.addTxGasDefaults(txMeta)
} catch (error) {
- console.log(error)
+ log.warn(error)
this.txStateManager.setTxStatusFailed(txMeta.id, error)
throw error
}
@@ -260,7 +264,12 @@ class TransactionController extends EventEmitter {
// must set transaction to submitted/failed before releasing lock
nonceLock.releaseLock()
} catch (err) {
- this.txStateManager.setTxStatusFailed(txId, err)
+ // this is try-catch wrapped so that we can guarantee that the nonceLock is released
+ try {
+ this.txStateManager.setTxStatusFailed(txId, err)
+ } catch (err) {
+ log.error(err)
+ }
// must set transaction to submitted/failed before releasing lock
if (nonceLock) nonceLock.releaseLock()
// continue with error chain
diff --git a/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js b/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js
new file mode 100644
index 000000000..e4df2367e
--- /dev/null
+++ b/app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js
@@ -0,0 +1,24 @@
+const Config = require('./recipient-blacklist.js')
+
+/** @module*/
+module.exports = {
+ checkAccount,
+}
+
+/**
+ * Checks if a specified account on a specified network is blacklisted.
+ @param networkId {number}
+ @param account {string}
+*/
+function checkAccount (networkId, account) {
+
+ const mainnetId = 1
+ if (networkId !== mainnetId) {
+ return
+ }
+
+ const accountToCheck = account.toLowerCase()
+ if (Config.blacklist.includes(accountToCheck)) {
+ throw new Error('Recipient is a public account')
+ }
+}
diff --git a/app/scripts/controllers/transactions/lib/recipient-blacklist.js b/app/scripts/controllers/transactions/lib/recipient-blacklist.js
new file mode 100644
index 000000000..08e1a2ccd
--- /dev/null
+++ b/app/scripts/controllers/transactions/lib/recipient-blacklist.js
@@ -0,0 +1,17 @@
+module.exports = {
+ 'blacklist': [
+ // IDEX phisher
+ '0x9bcb0A9d99d815Bb87ee3191b1399b1Bcc46dc77',
+ // Ganache default seed phrases
+ '0x627306090abab3a6e1400e9345bc60c78a8bef57',
+ '0xf17f52151ebef6c7334fad080c5704d77216b732',
+ '0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef',
+ '0x821aea9a577a9b44299b9c15c88cf3087f3b5544',
+ '0x0d1d4e623d10f9fba5db95830f7d3839406c6af2',
+ '0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e',
+ '0x2191ef87e392377ec08e7c08eb105ef5448eced5',
+ '0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5',
+ '0x6330a553fc93768f612722bb8c2ec78ac90b3bbc',
+ '0x5aeda56215b167893e80b4fe645ba6d5bab767de',
+ ],
+}
diff --git a/app/scripts/controllers/transactions/nonce-tracker.js b/app/scripts/controllers/transactions/nonce-tracker.js
index f8cdc5523..35ca08d6c 100644
--- a/app/scripts/controllers/transactions/nonce-tracker.js
+++ b/app/scripts/controllers/transactions/nonce-tracker.js
@@ -49,29 +49,35 @@ class NonceTracker {
await this._globalMutexFree()
// await lock free, then take lock
const releaseLock = await this._takeMutex(address)
- // 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,
+ 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
}
- 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 }
}
async _getCurrentBlock () {
@@ -85,8 +91,8 @@ class NonceTracker {
async _globalMutexFree () {
const globalMutex = this._lookupMutex('global')
- const release = await globalMutex.acquire()
- release()
+ const releaseLock = await globalMutex.acquire()
+ releaseLock()
}
async _takeMutex (lockId) {
diff --git a/app/scripts/controllers/transactions/pending-tx-tracker.js b/app/scripts/controllers/transactions/pending-tx-tracker.js
index 6e2fcb40b..4e41cdaf8 100644
--- a/app/scripts/controllers/transactions/pending-tx-tracker.js
+++ b/app/scripts/controllers/transactions/pending-tx-tracker.js
@@ -196,14 +196,14 @@ class PendingTransactionTracker extends EventEmitter {
async _checkPendingTxs () {
const signedTxList = this.getPendingTransactions()
// in order to keep the nonceTracker accurate we block it while updating pending transactions
- const nonceGlobalLock = await this.nonceTracker.getGlobalLock()
+ const { releaseLock } = await this.nonceTracker.getGlobalLock()
try {
await Promise.all(signedTxList.map((txMeta) => this._checkPendingTx(txMeta)))
} catch (err) {
log.error('PendingTransactionWatcher - Error updating pending transactions')
log.error(err)
}
- nonceGlobalLock.releaseLock()
+ releaseLock()
}
/**
diff --git a/app/scripts/controllers/transactions/tx-gas-utils.js b/app/scripts/controllers/transactions/tx-gas-utils.js
index 36b5cdbc9..ab4031faa 100644
--- a/app/scripts/controllers/transactions/tx-gas-utils.js
+++ b/app/scripts/controllers/transactions/tx-gas-utils.js
@@ -126,4 +126,4 @@ class TxGasUtil {
}
}
-module.exports = TxGasUtil \ No newline at end of file
+module.exports = TxGasUtil
diff --git a/app/scripts/controllers/user-actions.js b/app/scripts/controllers/user-actions.js
new file mode 100644
index 000000000..f777054b8
--- /dev/null
+++ b/app/scripts/controllers/user-actions.js
@@ -0,0 +1,17 @@
+const MessageManager = require('./lib/message-manager')
+const PersonalMessageManager = require('./lib/personal-message-manager')
+const TypedMessageManager = require('./lib/typed-message-manager')
+
+class UserActionController {
+
+ constructor (opts = {}) {
+
+ this.messageManager = new MessageManager()
+ this.personalMessageManager = new PersonalMessageManager()
+ this.typedMessageManager = new TypedMessageManager()
+
+ }
+
+}
+
+module.exports = UserActionController
diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js
index 6d16eebd4..7dd7fda02 100644
--- a/app/scripts/inpage.js
+++ b/app/scripts/inpage.js
@@ -38,9 +38,30 @@ web3.setProvider = function () {
log.debug('MetaMask - overrode web3.setProvider')
}
log.debug('MetaMask - injected web3')
-// export global web3, with usage-detection
+
setupDappAutoReload(web3, inpageProvider.publicConfigStore)
+// export global web3, with usage-detection and deprecation warning
+
+/* TODO: Uncomment this area once auto-reload.js has been deprecated:
+let hasBeenWarned = false
+global.web3 = new Proxy(web3, {
+ get: (_web3, key) => {
+ // show warning once on web3 access
+ if (!hasBeenWarned && key !== 'currentProvider') {
+ console.warn('MetaMask: web3 will be deprecated in the near future in favor of the ethereumProvider \nhttps://github.com/MetaMask/faq/blob/master/detecting_metamask.md#web3-deprecation')
+ hasBeenWarned = true
+ }
+ // return value normally
+ return _web3[key]
+ },
+ set: (_web3, key, value) => {
+ // set value normally
+ _web3[key] = value
+ },
+})
+*/
+
// set web3 defaultAccount
inpageProvider.publicConfigStore.subscribe(function (state) {
web3.eth.defaultAccount = state.selectedAddress
diff --git a/app/scripts/lib/cleanErrorStack.js b/app/scripts/lib/cleanErrorStack.js
index fe1bfb0ce..8adf55db7 100644
--- a/app/scripts/lib/cleanErrorStack.js
+++ b/app/scripts/lib/cleanErrorStack.js
@@ -3,7 +3,7 @@
* @param {Error} err - error
* @returns {Error} Error with clean stack trace.
*/
-function cleanErrorStack(err){
+function cleanErrorStack (err) {
var name = err.name
name = (name === undefined) ? 'Error' : String(name)
diff --git a/app/scripts/lib/createErrorMiddleware.js b/app/scripts/lib/createErrorMiddleware.js
index baed99e45..7f6a4bd73 100644
--- a/app/scripts/lib/createErrorMiddleware.js
+++ b/app/scripts/lib/createErrorMiddleware.js
@@ -59,8 +59,9 @@ function createErrorMiddleware ({ override = true } = {}) {
if (!error) { return done() }
sanitizeRPCError(error)
log.error(`MetaMask - RPC Error: ${error.message}`, error)
+ done()
})
}
}
-module.exports = createErrorMiddleware \ No newline at end of file
+module.exports = createErrorMiddleware
diff --git a/app/scripts/lib/createStreamSink.js b/app/scripts/lib/createStreamSink.js
new file mode 100644
index 000000000..b93dbc089
--- /dev/null
+++ b/app/scripts/lib/createStreamSink.js
@@ -0,0 +1,24 @@
+const WritableStream = require('readable-stream').Writable
+const promiseToCallback = require('promise-to-callback')
+
+module.exports = createStreamSink
+
+
+function createStreamSink (asyncWriteFn, _opts) {
+ return new AsyncWritableStream(asyncWriteFn, _opts)
+}
+
+class AsyncWritableStream extends WritableStream {
+
+ constructor (asyncWriteFn, _opts) {
+ const opts = Object.assign({ objectMode: true }, _opts)
+ super(opts)
+ this._asyncWriteFn = asyncWriteFn
+ }
+
+ // write from incomming stream to state
+ _write (chunk, encoding, callback) {
+ promiseToCallback(this._asyncWriteFn(chunk, encoding))(callback)
+ }
+
+}
diff --git a/app/scripts/lib/diagnostics-reporter.js b/app/scripts/lib/diagnostics-reporter.js
new file mode 100644
index 000000000..569eb3268
--- /dev/null
+++ b/app/scripts/lib/diagnostics-reporter.js
@@ -0,0 +1,71 @@
+class DiagnosticsReporter {
+
+ constructor ({ firstTimeInfo, version }) {
+ this.firstTimeInfo = firstTimeInfo
+ this.version = version
+ }
+
+ async reportOrphans (orphans) {
+ try {
+ return await this.submit({
+ accounts: Object.keys(orphans),
+ metadata: {
+ type: 'orphans',
+ },
+ })
+ } catch (err) {
+ console.error('DiagnosticsReporter - "reportOrphans" encountered an error:')
+ console.error(err)
+ }
+ }
+
+ async reportMultipleKeyrings (rawKeyrings) {
+ try {
+ const keyrings = await Promise.all(rawKeyrings.map(async (keyring, index) => {
+ return {
+ index,
+ type: keyring.type,
+ accounts: await keyring.getAccounts(),
+ }
+ }))
+ return await this.submit({
+ accounts: [],
+ metadata: {
+ type: 'keyrings',
+ keyrings,
+ },
+ })
+ } catch (err) {
+ console.error('DiagnosticsReporter - "reportMultipleKeyrings" encountered an error:')
+ console.error(err)
+ }
+ }
+
+ async submit (message) {
+ try {
+ // add metadata
+ message.metadata.version = this.version
+ message.metadata.firstTimeInfo = this.firstTimeInfo
+ return await postData(message)
+ } catch (err) {
+ console.error('DiagnosticsReporter - "submit" encountered an error:')
+ throw err
+ }
+ }
+
+}
+
+function postData (data) {
+ const uri = 'https://diagnostics.metamask.io/v1/orphanedAccounts'
+ return fetch(uri, {
+ body: JSON.stringify(data), // must match 'Content-Type' header
+ credentials: 'same-origin', // include, same-origin, *omit
+ headers: {
+ 'content-type': 'application/json',
+ },
+ method: 'POST', // *GET, POST, PUT, DELETE, etc.
+ mode: 'cors', // no-cors, cors, *same-origin
+ })
+}
+
+module.exports = DiagnosticsReporter
diff --git a/app/scripts/lib/extractEthjsErrorMessage.js b/app/scripts/lib/extractEthjsErrorMessage.js
index 0f100756f..4891075c3 100644
--- a/app/scripts/lib/extractEthjsErrorMessage.js
+++ b/app/scripts/lib/extractEthjsErrorMessage.js
@@ -10,13 +10,13 @@ module.exports = extractEthjsErrorMessage
*
* @param {string} errorMessage The error message to parse
* @returns {string} Returns an error message, either the same as was passed, or the ending message portion of an isEthjsRpcError
- *
+ *
* @example
* // returns 'Transaction Failed: replacement transaction underpriced'
* extractEthjsErrorMessage(`Error: [ethjs-rpc] rpc error with payload {"id":3947817945380,"jsonrpc":"2.0","params":["0xf8eb8208708477359400830398539406012c8cf97bead5deae237070f9587f8e7a266d80b8843d7d3f5a0000000000000000000000000000000000000000000000000000000000081d1a000000000000000000000000000000000000000000000000001ff973cafa800000000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000000000000000000000000000000000000003f48025a04c32a9b630e0d9e7ff361562d850c86b7a884908135956a7e4a336fa0300d19ca06830776423f25218e8d19b267161db526e66895567147015b1f3fc47aef9a3c7"],"method":"eth_sendRawTransaction"} Error: replacement transaction underpriced`)
*
*/
-function extractEthjsErrorMessage(errorMessage) {
+function extractEthjsErrorMessage (errorMessage) {
const isEthjsRpcError = errorMessage.includes(ethJsRpcSlug)
if (isEthjsRpcError) {
const payloadAndError = errorMessage.slice(ethJsRpcSlug.length)
diff --git a/app/scripts/lib/get-first-preferred-lang-code.js b/app/scripts/lib/get-first-preferred-lang-code.js
index 1e6a83ba6..170d508c1 100644
--- a/app/scripts/lib/get-first-preferred-lang-code.js
+++ b/app/scripts/lib/get-first-preferred-lang-code.js
@@ -2,8 +2,7 @@ const extension = require('extensionizer')
const promisify = require('pify')
const allLocales = require('../../_locales/index.json')
-const isSupported = extension.i18n && extension.i18n.getAcceptLanguages
-const getPreferredLocales = isSupported ? promisify(
+const getPreferredLocales = extension.i18n ? promisify(
extension.i18n.getAcceptLanguages,
{ errorFirst: false }
) : async () => []
@@ -18,7 +17,21 @@ const existingLocaleCodes = allLocales.map(locale => locale.code.toLowerCase().r
*
*/
async function getFirstPreferredLangCode () {
- const userPreferredLocaleCodes = await getPreferredLocales()
+ let userPreferredLocaleCodes
+
+ try {
+ userPreferredLocaleCodes = await getPreferredLocales()
+ } catch (e) {
+ // Brave currently throws when calling getAcceptLanguages, so this handles that.
+ userPreferredLocaleCodes = []
+ }
+
+ // safeguard for Brave Browser until they implement chrome.i18n.getAcceptLanguages
+ // https://github.com/MetaMask/metamask-extension/issues/4270
+ if (!userPreferredLocaleCodes) {
+ userPreferredLocaleCodes = []
+ }
+
const firstPreferredLangCode = userPreferredLocaleCodes
.map(code => code.toLowerCase())
.find(code => existingLocaleCodes.includes(code))
@@ -26,3 +39,4 @@ async function getFirstPreferredLangCode () {
}
module.exports = getFirstPreferredLangCode
+
diff --git a/app/scripts/lib/getObjStructure.js b/app/scripts/lib/getObjStructure.js
index 52250d3fb..9c92879fb 100644
--- a/app/scripts/lib/getObjStructure.js
+++ b/app/scripts/lib/getObjStructure.js
@@ -18,12 +18,12 @@ module.exports = getObjStructure
* Creates an object that represents the structure of the given object. It replaces all values with the result of their
* type.
*
- * @param {object} obj The object for which a 'structure' will be returned. Usually a plain object and not a class.
+ * @param {object} obj The object for which a 'structure' will be returned. Usually a plain object and not a class.
* @returns {object} The "mapped" version of a deep clone of the passed object, with each non-object property value
* replaced with the javascript type of that value.
*
*/
-function getObjStructure(obj) {
+function getObjStructure (obj) {
const structure = clone(obj)
return deepMap(structure, (value) => {
return value === null ? 'null' : typeof value
@@ -38,7 +38,7 @@ function getObjStructure(obj) {
* @param {Function} visit The modifier to apply to each non-object property value
* @returns {object} The modified object
*/
-function deepMap(target = {}, visit) {
+function deepMap (target = {}, visit) {
Object.entries(target).forEach(([key, value]) => {
if (typeof value === 'object' && value !== null) {
target[key] = deepMap(value, visit)
diff --git a/app/scripts/lib/local-store.js b/app/scripts/lib/local-store.js
index 139ff86bd..fbcba09cd 100644
--- a/app/scripts/lib/local-store.js
+++ b/app/scripts/lib/local-store.js
@@ -8,7 +8,7 @@ module.exports = class ExtensionStore {
/**
* @constructor
*/
- constructor() {
+ constructor () {
this.isSupported = !!(extension.storage.local)
if (!this.isSupported) {
log.error('Storage local API not available.')
@@ -19,7 +19,7 @@ module.exports = class ExtensionStore {
* Returns all of the keys currently saved
* @return {Promise<*>}
*/
- async get() {
+ async get () {
if (!this.isSupported) return undefined
const result = await this._get()
// extension.storage.local always returns an obj
@@ -36,7 +36,7 @@ module.exports = class ExtensionStore {
* @param {object} state - The state to set
* @return {Promise<void>}
*/
- async set(state) {
+ async set (state) {
return this._set(state)
}
@@ -45,7 +45,7 @@ module.exports = class ExtensionStore {
* @private
* @return {object} the key-value map from local storage
*/
- _get() {
+ _get () {
const local = extension.storage.local
return new Promise((resolve, reject) => {
local.get(null, (/** @type {any} */ result) => {
@@ -65,7 +65,7 @@ module.exports = class ExtensionStore {
* @return {Promise<void>}
* @private
*/
- _set(obj) {
+ _set (obj) {
const local = extension.storage.local
return new Promise((resolve, reject) => {
local.set(obj, () => {
@@ -85,6 +85,6 @@ module.exports = class ExtensionStore {
* @param {object} obj - The object to check
* @returns {boolean}
*/
-function isEmpty(obj) {
+function isEmpty (obj) {
return Object.keys(obj).length === 0
}
diff --git a/app/scripts/lib/notification-manager.js b/app/scripts/lib/notification-manager.js
index 5dfb42078..969a9459a 100644
--- a/app/scripts/lib/notification-manager.js
+++ b/app/scripts/lib/notification-manager.js
@@ -26,13 +26,15 @@ class NotificationManager {
// bring focus to existing chrome popup
extension.windows.update(popup.id, { focused: true })
} else {
+ const cb = (currentPopup) => { this._popupId = currentPopup.id }
// create new notification popup
- extension.windows.create({
+ const creation = extension.windows.create({
url: 'notification.html',
type: 'popup',
width,
height,
- })
+ }, cb)
+ creation && creation.then && creation.then(cb)
}
})
}
@@ -84,7 +86,7 @@ class NotificationManager {
}
/**
- * Given an array of windows, returns the first that has a 'popup' type, or null if no such window exists.
+ * Given an array of windows, returns the 'popup' that has been opened by MetaMask, or null if no such window exists.
*
* @private
* @param {array} windows An array of objects containing data about the open MetaMask extension windows.
@@ -93,7 +95,7 @@ class NotificationManager {
_getPopupIn (windows) {
return windows ? windows.find((win) => {
// Returns notification popup
- return (win && win.type === 'popup')
+ return (win && win.type === 'popup' && win.id === this._popupId)
}) : null
}
diff --git a/app/scripts/lib/port-stream.js b/app/scripts/lib/port-stream.js
index 5c4224fd9..fd65d94f3 100644
--- a/app/scripts/lib/port-stream.js
+++ b/app/scripts/lib/port-stream.js
@@ -58,7 +58,7 @@ PortDuplexStream.prototype._read = noop
/**
* Called internally when data should be written to
* this writable stream.
- *
+ *
* @private
* @param {*} msg Arbitrary object to write
* @param {string} encoding Encoding to use when writing payload
diff --git a/app/scripts/lib/reportFailedTxToSentry.js b/app/scripts/lib/reportFailedTxToSentry.js
index e09f4f1f8..df5661e59 100644
--- a/app/scripts/lib/reportFailedTxToSentry.js
+++ b/app/scripts/lib/reportFailedTxToSentry.js
@@ -7,7 +7,7 @@ module.exports = reportFailedTxToSentry
// for sending to sentry
//
-function reportFailedTxToSentry({ raven, txMeta }) {
+function reportFailedTxToSentry ({ raven, txMeta }) {
const errorMessage = 'Transaction Failed: ' + extractEthjsErrorMessage(txMeta.err.message)
raven.captureMessage(errorMessage, {
// "extra" key is required by Sentry
diff --git a/app/scripts/lib/setupMetamaskMeshMetrics.js b/app/scripts/lib/setupMetamaskMeshMetrics.js
index 02690a948..fd3b93fc4 100644
--- a/app/scripts/lib/setupMetamaskMeshMetrics.js
+++ b/app/scripts/lib/setupMetamaskMeshMetrics.js
@@ -4,7 +4,7 @@ module.exports = setupMetamaskMeshMetrics
/**
* Injects an iframe into the current document for testing
*/
-function setupMetamaskMeshMetrics() {
+function setupMetamaskMeshMetrics () {
const testingContainer = document.createElement('iframe')
testingContainer.src = 'https://metamask.github.io/mesh-testing/'
console.log('Injecting MetaMask Mesh testing client')
diff --git a/app/scripts/lib/setupRaven.js b/app/scripts/lib/setupRaven.js
index d164827ab..3f69fb3bb 100644
--- a/app/scripts/lib/setupRaven.js
+++ b/app/scripts/lib/setupRaven.js
@@ -7,7 +7,7 @@ const DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496'
module.exports = setupRaven
// Setup raven / sentry remote error reporting
-function setupRaven(opts) {
+function setupRaven (opts) {
const { release } = opts
let ravenTarget
@@ -21,7 +21,7 @@ function setupRaven(opts) {
const client = Raven.config(ravenTarget, {
release,
- transport: function(opts) {
+ transport: function (opts) {
const report = opts.data
try {
// handle error-like non-error exceptions
@@ -42,7 +42,7 @@ function setupRaven(opts) {
return Raven
}
-function rewriteErrorLikeExceptions(report) {
+function rewriteErrorLikeExceptions (report) {
// handle errors that lost their error-ness in serialization (e.g. dnode)
rewriteErrorMessages(report, (errorMessage) => {
if (!errorMessage.includes('Non-Error exception captured with keys:')) return errorMessage
@@ -51,7 +51,7 @@ function rewriteErrorLikeExceptions(report) {
})
}
-function simplifyErrorMessages(report) {
+function simplifyErrorMessages (report) {
rewriteErrorMessages(report, (errorMessage) => {
// simplify ethjs error messages
errorMessage = extractEthjsErrorMessage(errorMessage)
@@ -64,9 +64,9 @@ function simplifyErrorMessages(report) {
})
}
-function rewriteErrorMessages(report, rewriteFn) {
+function rewriteErrorMessages (report, rewriteFn) {
// rewrite top level message
- report.message = rewriteFn(report.message)
+ if (report.message) report.message = rewriteFn(report.message)
// rewrite each exception message
if (report.exception && report.exception.values) {
report.exception.values.forEach(item => {
@@ -75,7 +75,7 @@ function rewriteErrorMessages(report, rewriteFn) {
}
}
-function rewriteReportUrls(report) {
+function rewriteReportUrls (report) {
// update request url
report.request.url = toMetamaskUrl(report.request.url)
// update exception stack trace
@@ -88,7 +88,7 @@ function rewriteReportUrls(report) {
}
}
-function toMetamaskUrl(origUrl) {
+function toMetamaskUrl (origUrl) {
const filePath = origUrl.split(location.origin)[1]
if (!filePath) return origUrl
const metamaskUrl = `metamask${filePath}`
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index a570f2567..b4d39031a 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -139,6 +139,8 @@ module.exports = class MetamaskController extends EventEmitter {
const address = addresses[0]
this.preferencesController.setSelectedAddress(address)
}
+ // ensure preferences + identities controller know about all addresses
+ this.preferencesController.addAddresses(addresses)
this.accountTracker.syncWithAddresses(addresses)
})
@@ -179,9 +181,6 @@ module.exports = class MetamaskController extends EventEmitter {
version,
firstVersion: initState.firstTimeInfo.version,
})
- this.noticeController.updateNoticesList()
- // to be uncommented when retrieving notices from a remote server.
- // this.noticeController.startPolling()
this.shapeshiftController = new ShapeShiftController({
initState: initState.ShapeShiftController,
@@ -354,7 +353,7 @@ module.exports = class MetamaskController extends EventEmitter {
importAccountWithStrategy: nodeify(this.importAccountWithStrategy, this),
// vault management
- submitPassword: nodeify(keyringController.submitPassword, keyringController),
+ submitPassword: nodeify(this.submitPassword, this),
// network management
setProviderType: nodeify(networkController.setProviderType, networkController),
@@ -384,6 +383,8 @@ module.exports = class MetamaskController extends EventEmitter {
updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController),
retryTransaction: nodeify(this.retryTransaction, this),
getFilteredTxList: nodeify(txController.getFilteredTxList, txController),
+ isNonceTaken: nodeify(txController.isNonceTaken, txController),
+ estimateGas: nodeify(this.estimateGas, this),
// messageManager
signMessage: nodeify(this.signMessage, this),
@@ -404,7 +405,6 @@ module.exports = class MetamaskController extends EventEmitter {
}
-
//=============================================================================
// VAULT / KEYRING RELATED METHODS
//=============================================================================
@@ -424,28 +424,24 @@ module.exports = class MetamaskController extends EventEmitter {
* @returns {Object} vault
*/
async createNewVaultAndKeychain (password) {
- const release = await this.createVaultMutex.acquire()
- let vault
-
+ const releaseLock = await this.createVaultMutex.acquire()
try {
+ let vault
const accounts = await this.keyringController.getAccounts()
-
if (accounts.length > 0) {
vault = await this.keyringController.fullUpdate()
-
} else {
vault = await this.keyringController.createNewVaultAndKeychain(password)
const accounts = await this.keyringController.getAccounts()
this.preferencesController.setAddresses(accounts)
this.selectFirstIdentity()
}
- release()
+ releaseLock()
+ return vault
} catch (err) {
- release()
+ releaseLock()
throw err
}
-
- return vault
}
/**
@@ -454,20 +450,46 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {} seed
*/
async createNewVaultAndRestore (password, seed) {
- const release = await this.createVaultMutex.acquire()
+ const releaseLock = await this.createVaultMutex.acquire()
try {
+ // clear known identities
+ this.preferencesController.setAddresses([])
+ // create new vault
const vault = await this.keyringController.createNewVaultAndRestore(password, seed)
+ // set new identities
const accounts = await this.keyringController.getAccounts()
this.preferencesController.setAddresses(accounts)
this.selectFirstIdentity()
- release()
+ releaseLock()
return vault
} catch (err) {
- release()
+ releaseLock()
throw err
}
}
+ /*
+ * Submits the user's password and attempts to unlock the vault.
+ * Also synchronizes the preferencesController, to ensure its schema
+ * is up to date with known accounts once the vault is decrypted.
+ *
+ * @param {string} password - The user's password
+ * @returns {Promise<object>} - The keyringController update.
+ */
+ async submitPassword (password) {
+ await this.keyringController.submitPassword(password)
+ const accounts = await this.keyringController.getAccounts()
+
+ // verify keyrings
+ const nonSimpleKeyrings = this.keyringController.keyrings.filter(keyring => keyring.type !== 'Simple Key Pair')
+ if (nonSimpleKeyrings.length > 1 && this.diagnostics) {
+ await this.diagnostics.reportMultipleKeyrings(nonSimpleKeyrings)
+ }
+
+ await this.preferencesController.syncAddresses(accounts)
+ return this.keyringController.fullUpdate()
+ }
+
/**
* @type Identity
* @property {string} name - The account nickname.
@@ -592,10 +614,7 @@ module.exports = class MetamaskController extends EventEmitter {
async resetAccount () {
const selectedAddress = this.preferencesController.getSelectedAddress()
this.txController.wipeTransactions(selectedAddress)
-
- const networkController = this.networkController
- const oldType = networkController.getProviderConfig().type
- await networkController.setProviderType(oldType, true)
+ this.networkController.resetConnection()
return selectedAddress
}
@@ -922,6 +941,18 @@ module.exports = class MetamaskController extends EventEmitter {
return state
}
+ estimateGas (estimateGasParams) {
+ return new Promise((resolve, reject) => {
+ return this.txController.txGasUtil.query.estimateGas(estimateGasParams, (err, res) => {
+ if (err) {
+ return reject(err)
+ }
+
+ return resolve(res)
+ })
+ })
+ }
+
//=============================================================================
// PASSWORD MANAGEMENT
//=============================================================================
@@ -930,7 +961,7 @@ module.exports = class MetamaskController extends EventEmitter {
* Allows a user to begin the seed phrase recovery process.
* @param {Function} cb - A callback function called when complete.
*/
- markPasswordForgotten(cb) {
+ markPasswordForgotten (cb) {
this.configManager.setPasswordForgotten(true)
this.sendUpdate()
cb()
@@ -940,7 +971,7 @@ module.exports = class MetamaskController extends EventEmitter {
* Allows a user to end the seed phrase recovery process.
* @param {Function} cb - A callback function called when complete.
*/
- unMarkPasswordForgotten(cb) {
+ unMarkPasswordForgotten (cb) {
this.configManager.setPasswordForgotten(false)
this.sendUpdate()
cb()
diff --git a/app/scripts/migrations/013.js b/app/scripts/migrations/013.js
index 15a9b28d4..fb7131f8e 100644
--- a/app/scripts/migrations/013.js
+++ b/app/scripts/migrations/013.js
@@ -28,7 +28,7 @@ module.exports = {
function transformState (state) {
const newState = state
const { config } = newState
- if ( config && config.provider ) {
+ if (config && config.provider) {
if (config.provider.type === 'testnet') {
newState.config.provider.type = 'ropsten'
}
diff --git a/app/scripts/migrations/023.js b/app/scripts/migrations/023.js
index 151496b06..18493a789 100644
--- a/app/scripts/migrations/023.js
+++ b/app/scripts/migrations/023.js
@@ -35,10 +35,10 @@ function transformState (state) {
if (transactions.length <= 40) return newState
- let reverseTxList = transactions.reverse()
+ const reverseTxList = transactions.reverse()
let stripping = true
while (reverseTxList.length > 40 && stripping) {
- let txIndex = reverseTxList.findIndex((txMeta) => {
+ const txIndex = reverseTxList.findIndex((txMeta) => {
return (txMeta.status === 'failed' ||
txMeta.status === 'rejected' ||
txMeta.status === 'confirmed' ||
diff --git a/app/scripts/migrations/026.js b/app/scripts/migrations/026.js
index 1b8a91a45..4e907e09c 100644
--- a/app/scripts/migrations/026.js
+++ b/app/scripts/migrations/026.js
@@ -27,7 +27,7 @@ module.exports = {
function transformState (state) {
if (!state.KeyringController || !state.PreferencesController) {
- return
+ return state
}
if (!state.KeyringController.walletNicknames) {
diff --git a/app/scripts/notice-controller.js b/app/scripts/notice-controller.js
index 14a63eae7..2def4371e 100644
--- a/app/scripts/notice-controller.js
+++ b/app/scripts/notice-controller.js
@@ -2,7 +2,7 @@ const EventEmitter = require('events').EventEmitter
const semver = require('semver')
const extend = require('xtend')
const ObservableStore = require('obs-store')
-const hardCodedNotices = require('../../notices/notices.json')
+const hardCodedNotices = require('../../notices/notices.js')
const uniqBy = require('lodash.uniqby')
module.exports = class NoticeController extends EventEmitter {
@@ -16,8 +16,12 @@ module.exports = class NoticeController extends EventEmitter {
noticesList: [],
}, opts.initState)
this.store = new ObservableStore(initState)
+ // setup memStore
this.memStore = new ObservableStore({})
this.store.subscribe(() => this._updateMemstore())
+ this._updateMemstore()
+ // pull in latest notices
+ this.updateNoticesList()
}
getNoticesList () {
@@ -29,9 +33,9 @@ module.exports = class NoticeController extends EventEmitter {
return notices.filter((notice) => notice.read === false)
}
- getLatestUnreadNotice () {
+ getNextUnreadNotice () {
const unreadNotices = this.getUnreadNotices()
- return unreadNotices[unreadNotices.length - 1]
+ return unreadNotices[0]
}
async setNoticesList (noticesList) {
@@ -47,7 +51,7 @@ module.exports = class NoticeController extends EventEmitter {
notices[index].read = true
notices[index].body = ''
this.setNoticesList(notices)
- const latestNotice = this.getLatestUnreadNotice()
+ const latestNotice = this.getNextUnreadNotice()
cb(null, latestNotice)
} catch (err) {
cb(err)
@@ -64,15 +68,6 @@ module.exports = class NoticeController extends EventEmitter {
return result
}
- startPolling () {
- if (this.noticePoller) {
- clearInterval(this.noticePoller)
- }
- this.noticePoller = setInterval(() => {
- this.noticeController.updateNoticesList()
- }, 300000)
- }
-
_mergeNotices (oldNotices, newNotices) {
return uniqBy(oldNotices.concat(newNotices), 'id')
}
@@ -91,19 +86,15 @@ module.exports = class NoticeController extends EventEmitter {
})
}
- _mapNoticeIds (notices) {
- return notices.map((notice) => notice.id)
- }
-
async _retrieveNoticeData () {
- // Placeholder for the API.
+ // Placeholder for remote notice API.
return hardCodedNotices
}
_updateMemstore () {
- const lastUnreadNotice = this.getLatestUnreadNotice()
- const noActiveNotices = !lastUnreadNotice
- this.memStore.updateState({ lastUnreadNotice, noActiveNotices })
+ const nextUnreadNotice = this.getNextUnreadNotice()
+ const noActiveNotices = !nextUnreadNotice
+ this.memStore.updateState({ nextUnreadNotice, noActiveNotices })
}
}
diff --git a/app/scripts/popup-core.js b/app/scripts/popup-core.js
index 6325b8a8d..db885ec93 100644
--- a/app/scripts/popup-core.js
+++ b/app/scripts/popup-core.js
@@ -12,7 +12,7 @@ module.exports = initializePopup
/**
* Asynchronously initializes the MetaMask popup UI
*
- * @param {{ container: Element, connectionStream: * }} config Popup configuration object
+ * @param {{ container: Element, connectionStream: * }} config Popup configuration object
* @param {Function} cb Called when initialization is complete
*/
function initializePopup ({ container, connectionStream }, cb) {
diff --git a/app/scripts/ui.js b/app/scripts/ui.js
index bdab29c1e..9bf97be87 100644
--- a/app/scripts/ui.js
+++ b/app/scripts/ui.js
@@ -14,7 +14,7 @@ const log = require('loglevel')
start().catch(log.error)
-async function start() {
+async function start () {
// create platform global
global.platform = new ExtensionPlatform()