diff options
author | Thomas Huang <tmashuang@users.noreply.github.com> | 2018-05-31 07:04:02 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-05-31 07:04:02 +0800 |
commit | dc5477be3cc62dff912a9447c702edab66200f02 (patch) | |
tree | 4c4c4293bfbc2a80812d231af9c7e22877cebfbd /app/scripts | |
parent | 5fc24930a7febd919ec6a8f6e9c14f2bac0ef2b2 (diff) | |
parent | e59f606adb65de85484b0fb258980543967ee5e1 (diff) | |
download | tangerine-wallet-browser-dc5477be3cc62dff912a9447c702edab66200f02.tar tangerine-wallet-browser-dc5477be3cc62dff912a9447c702edab66200f02.tar.gz tangerine-wallet-browser-dc5477be3cc62dff912a9447c702edab66200f02.tar.bz2 tangerine-wallet-browser-dc5477be3cc62dff912a9447c702edab66200f02.tar.lz tangerine-wallet-browser-dc5477be3cc62dff912a9447c702edab66200f02.tar.xz tangerine-wallet-browser-dc5477be3cc62dff912a9447c702edab66200f02.tar.zst tangerine-wallet-browser-dc5477be3cc62dff912a9447c702edab66200f02.zip |
Merge pull request #4408 from MetaMask/v4.7.0rc2
Version 4.7.0 - rc2
Diffstat (limited to 'app/scripts')
-rw-r--r-- | app/scripts/background.js | 2 | ||||
-rw-r--r-- | app/scripts/contentscript.js | 4 | ||||
-rw-r--r-- | app/scripts/controllers/address-book.js | 27 | ||||
-rw-r--r-- | app/scripts/controllers/network/enums.js | 26 | ||||
-rw-r--r-- | app/scripts/controllers/network/network.js | 121 | ||||
-rw-r--r-- | app/scripts/controllers/network/util.js | 36 | ||||
-rw-r--r-- | app/scripts/controllers/preferences.js | 30 | ||||
-rw-r--r-- | app/scripts/controllers/recent-blocks.js | 22 | ||||
-rw-r--r-- | app/scripts/controllers/transactions/lib/tx-state-history-helper.js | 21 | ||||
-rw-r--r-- | app/scripts/controllers/transactions/tx-state-manager.js | 4 | ||||
-rw-r--r-- | app/scripts/first-time-state.js | 9 | ||||
-rw-r--r-- | app/scripts/lib/createErrorMiddleware.js | 66 | ||||
-rw-r--r-- | app/scripts/lib/get-first-preferred-lang-code.js | 11 | ||||
-rw-r--r-- | app/scripts/lib/inpage-provider.js | 2 | ||||
-rw-r--r-- | app/scripts/lib/setupRaven.js | 42 | ||||
-rw-r--r-- | app/scripts/metamask-controller.js | 47 | ||||
-rw-r--r-- | app/scripts/migrations/026.js | 47 | ||||
-rw-r--r-- | app/scripts/migrations/index.js | 1 |
18 files changed, 277 insertions, 241 deletions
diff --git a/app/scripts/background.js b/app/scripts/background.js index 37e2b68fc..56e190f97 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -323,7 +323,7 @@ function setupController (initState, initLangCode) { /** * A runtime.Port object, as provided by the browser: - * @link https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/Port + * @see https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/Port * @typedef Port * @type Object */ diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index dbf1c6d4c..555902ddf 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -166,7 +166,7 @@ function documentElementCheck () { /** * Checks if the current domain is blacklisted - * + * * @returns {boolean} {@code true} if the current domain is blacklisted */ function blacklistedDomainCheck () { @@ -174,6 +174,8 @@ function blacklistedDomainCheck () { 'uscourts.gov', 'dropbox.com', 'webbyawards.com', + 'cdn.shopify.com/s/javascripts/tricorder/xtld-read-only-frame.html', + 'adyen.com', ] var currentUrl = window.location.href var currentRegex diff --git a/app/scripts/controllers/address-book.js b/app/scripts/controllers/address-book.js index c91e6b2e4..4697e074c 100644 --- a/app/scripts/controllers/address-book.js +++ b/app/scripts/controllers/address-book.js @@ -13,19 +13,17 @@ class AddressBookController { * @param {object} opts Overrides the defaults for the initial state of this.store * @property {array} opts.initState initializes the the state of the AddressBookController. Can contain an * addressBook property to initialize the addressBook array - * @param {KeyringController} keyringController (Soon to be deprecated) The keyringController used in the current - * MetamaskController. Contains the identities used in this AddressBookController. + * @property {object} opts.preferencesStore the {@code PreferencesController} store * @property {object} store The the store of the current users address book * @property {array} store.addressBook An array of addresses and nicknames. These are set by the user when sending * to a new address. * */ - constructor (opts = {}, keyringController) { - const initState = extend({ + constructor ({initState, preferencesStore}) { + this.store = new ObservableStore(extend({ addressBook: [], - }, opts.initState) - this.store = new ObservableStore(initState) - this.keyringController = keyringController + }, initState)) + this._preferencesStore = preferencesStore } // @@ -62,7 +60,7 @@ class AddressBookController { */ _addToAddressBook (address, name) { const addressBook = this._getAddressBook() - const identities = this._getIdentities() + const {identities} = this._preferencesStore.getState() const addressBookIndex = addressBook.findIndex((element) => { return element.address.toLowerCase() === address.toLowerCase() || element.name === name }) const identitiesIndex = Object.keys(identities).findIndex((element) => { return element.toLowerCase() === address.toLowerCase() }) @@ -95,19 +93,6 @@ class AddressBookController { _getAddressBook () { return this.store.getState().addressBook } - - /** - * Retrieves identities from the keyring controller in order to avoid - * duplication - * - * @deprecated - * @returns {array} Returns the identies array from the keyringContoller's state - * - */ - _getIdentities () { - return this.keyringController.memStore.getState().identities - } - } module.exports = AddressBookController diff --git a/app/scripts/controllers/network/enums.js b/app/scripts/controllers/network/enums.js index 4f29e301b..9da7f309c 100644 --- a/app/scripts/controllers/network/enums.js +++ b/app/scripts/controllers/network/enums.js @@ -13,20 +13,6 @@ const RINKEBY_DISPLAY_NAME = 'Rinkeby' const KOVAN_DISPLAY_NAME = 'Kovan' const MAINNET_DISPLAY_NAME = 'Main Ethereum Network' -const MAINNET_RPC_URL = 'https://mainnet.infura.io/metamask' -const ROPSTEN_RPC_URL = 'https://ropsten.infura.io/metamask' -const KOVAN_RPC_URL = 'https://kovan.infura.io/metamask' -const RINKEBY_RPC_URL = 'https://rinkeby.infura.io/metamask' -const LOCALHOST_RPC_URL = 'http://localhost:8545' - -const MAINNET_RPC_URL_BETA = 'https://mainnet.infura.io/metamask2' -const ROPSTEN_RPC_URL_BETA = 'https://ropsten.infura.io/metamask2' -const KOVAN_RPC_URL_BETA = 'https://kovan.infura.io/metamask2' -const RINKEBY_RPC_URL_BETA = 'https://rinkeby.infura.io/metamask2' - -const DEFAULT_NETWORK = 'rinkeby' -const OLD_UI_NETWORK_TYPE = 'network' -const BETA_UI_NETWORK_TYPE = 'networkBeta' module.exports = { ROPSTEN, @@ -41,16 +27,4 @@ module.exports = { RINKEBY_DISPLAY_NAME, KOVAN_DISPLAY_NAME, MAINNET_DISPLAY_NAME, - MAINNET_RPC_URL, - ROPSTEN_RPC_URL, - KOVAN_RPC_URL, - RINKEBY_RPC_URL, - LOCALHOST_RPC_URL, - MAINNET_RPC_URL_BETA, - ROPSTEN_RPC_URL_BETA, - KOVAN_RPC_URL_BETA, - RINKEBY_RPC_URL_BETA, - DEFAULT_NETWORK, - OLD_UI_NETWORK_TYPE, - BETA_UI_NETWORK_TYPE, } diff --git a/app/scripts/controllers/network/network.js b/app/scripts/controllers/network/network.js index 2f5b81cd2..93fde7c57 100644 --- a/app/scripts/controllers/network/network.js +++ b/app/scripts/controllers/network/network.js @@ -14,52 +14,40 @@ const { RINKEBY, KOVAN, MAINNET, - OLD_UI_NETWORK_TYPE, - DEFAULT_NETWORK, + LOCALHOST, } = require('./enums') -const { getNetworkEndpoints } = require('./util') +const LOCALHOST_RPC_URL = 'http://localhost:8545' const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET] +const env = process.env.METAMASK_ENV +const METAMASK_DEBUG = process.env.METAMASK_DEBUG +const testMode = (METAMASK_DEBUG || env === 'test') + +const defaultProviderConfig = { + type: testMode ? RINKEBY : MAINNET, +} + module.exports = class NetworkController extends EventEmitter { - constructor (config) { + constructor (opts = {}) { super() - this._networkEndpointVersion = OLD_UI_NETWORK_TYPE - this._networkEndpoints = getNetworkEndpoints(OLD_UI_NETWORK_TYPE) - this._defaultRpc = this._networkEndpoints[DEFAULT_NETWORK] - - config.provider.rpcTarget = this.getRpcAddressForType(config.provider.type, config.provider) + // parse options + const providerConfig = opts.provider || defaultProviderConfig + // create stores + this.providerStore = new ObservableStore(providerConfig) this.networkStore = new ObservableStore('loading') - this.providerStore = new ObservableStore(config.provider) this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore }) + // create event emitter proxy this._proxy = createEventEmitterProxy() this.on('networkDidChange', this.lookupNetwork) } - async setNetworkEndpoints (version) { - if (version === this._networkEndpointVersion) { - return - } - - this._networkEndpointVersion = version - this._networkEndpoints = getNetworkEndpoints(version) - this._defaultRpc = this._networkEndpoints[DEFAULT_NETWORK] - const { type } = this.getProviderConfig() - - return this.setProviderType(type, true) - } - initializeProvider (_providerParams) { this._baseProviderParams = _providerParams const { type, rpcTarget } = this.providerStore.getState() - // map rpcTarget to rpcUrl - const opts = { - type, - rpcUrl: rpcTarget, - } - this._configureProvider(opts) + this._configureProvider({ type, rpcTarget }) this._proxy.on('block', this._logBlock.bind(this)) this._proxy.on('error', this.verifyNetwork.bind(this)) this.ethQuery = new EthQuery(this._proxy) @@ -96,45 +84,27 @@ module.exports = class NetworkController extends EventEmitter { }) } - setRpcTarget (rpcUrl) { - this.providerStore.updateState({ + setRpcTarget (rpcTarget) { + const providerConfig = { type: 'rpc', - rpcTarget: rpcUrl, - }) - this._switchNetwork({ rpcUrl }) - } - - getCurrentRpcAddress () { - const provider = this.getProviderConfig() - if (!provider) return null - return this.getRpcAddressForType(provider.type) - } - - async setProviderType (type, forceUpdate = false) { - assert(type !== 'rpc', `NetworkController.setProviderType - cannot connect by type "rpc"`) - // skip if type already matches - if (type === this.getProviderConfig().type && !forceUpdate) { - return + rpcTarget, } + this.providerStore.updateState(providerConfig) + this._switchNetwork(providerConfig) + } - const rpcTarget = this.getRpcAddressForType(type) - assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`) - this.providerStore.updateState({ type, rpcTarget }) - this._switchNetwork({ type }) + 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.providerStore.updateState(providerConfig) + this._switchNetwork(providerConfig) } getProviderConfig () { return this.providerStore.getState() } - getRpcAddressForType (type, provider = this.getProviderConfig()) { - if (this._networkEndpoints[type]) { - return this._networkEndpoints[type] - } - - return provider && provider.rpcTarget ? provider.rpcTarget : this._defaultRpc - } - // // Private // @@ -146,32 +116,27 @@ module.exports = class NetworkController extends EventEmitter { } _configureProvider (opts) { - // type-based rpc endpoints - const { type } = opts - if (type) { - // type-based infura rpc endpoints - const isInfura = INFURA_PROVIDER_TYPES.includes(type) - opts.rpcUrl = this.getRpcAddressForType(type) - if (isInfura) { - this._configureInfuraProvider(opts) - // other type-based rpc endpoints - } else { - this._configureStandardProvider(opts) - } + const { type, rpcTarget } = opts + // infura type-based endpoints + const isInfura = INFURA_PROVIDER_TYPES.includes(type) + if (isInfura) { + this._configureInfuraProvider(opts) + // other type-based rpc endpoints + } else if (type === LOCALHOST) { + this._configureStandardProvider({ rpcUrl: LOCALHOST_RPC_URL }) // url-based rpc endpoints + } else if (type === 'rpc'){ + this._configureStandardProvider({ rpcUrl: rpcTarget }) } else { - this._configureStandardProvider(opts) + throw new Error(`NetworkController - _configureProvider - unknown type "${type}"`) } } - _configureInfuraProvider (opts) { - log.info('_configureInfuraProvider', opts) - const infuraProvider = createInfuraProvider({ - network: opts.type, - }) + _configureInfuraProvider ({ type }) { + log.info('_configureInfuraProvider', type) + const infuraProvider = createInfuraProvider({ network: type }) const infuraSubprovider = new SubproviderFromProvider(infuraProvider) const providerParams = extend(this._baseProviderParams, { - rpcUrl: opts.rpcUrl, engineParams: { pollingInterval: 8000, blockTrackerProvider: infuraProvider, diff --git a/app/scripts/controllers/network/util.js b/app/scripts/controllers/network/util.js index 4f38ccda4..261dae721 100644 --- a/app/scripts/controllers/network/util.js +++ b/app/scripts/controllers/network/util.js @@ -3,7 +3,6 @@ const { RINKEBY, KOVAN, MAINNET, - LOCALHOST, ROPSTEN_CODE, RINKEYBY_CODE, KOVAN_CODE, @@ -11,17 +10,6 @@ const { RINKEBY_DISPLAY_NAME, KOVAN_DISPLAY_NAME, MAINNET_DISPLAY_NAME, - MAINNET_RPC_URL, - ROPSTEN_RPC_URL, - KOVAN_RPC_URL, - RINKEBY_RPC_URL, - LOCALHOST_RPC_URL, - MAINNET_RPC_URL_BETA, - ROPSTEN_RPC_URL_BETA, - KOVAN_RPC_URL_BETA, - RINKEBY_RPC_URL_BETA, - OLD_UI_NETWORK_TYPE, - BETA_UI_NETWORK_TYPE, } = require('./enums') const networkToNameMap = { @@ -34,32 +22,8 @@ const networkToNameMap = { [KOVAN_CODE]: KOVAN_DISPLAY_NAME, } -const networkEndpointsMap = { - [OLD_UI_NETWORK_TYPE]: { - [LOCALHOST]: LOCALHOST_RPC_URL, - [MAINNET]: MAINNET_RPC_URL, - [ROPSTEN]: ROPSTEN_RPC_URL, - [KOVAN]: KOVAN_RPC_URL, - [RINKEBY]: RINKEBY_RPC_URL, - }, - [BETA_UI_NETWORK_TYPE]: { - [LOCALHOST]: LOCALHOST_RPC_URL, - [MAINNET]: MAINNET_RPC_URL_BETA, - [ROPSTEN]: ROPSTEN_RPC_URL_BETA, - [KOVAN]: KOVAN_RPC_URL_BETA, - [RINKEBY]: RINKEBY_RPC_URL_BETA, - }, -} - const getNetworkDisplayName = key => networkToNameMap[key] -const getNetworkEndpoints = (networkType = OLD_UI_NETWORK_TYPE) => { - return { - ...networkEndpointsMap[networkType], - } -} - module.exports = { getNetworkDisplayName, - getNetworkEndpoints, } diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 1d3308d36..a4ff1207e 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -27,6 +27,7 @@ class PreferencesController { useBlockie: false, featureFlags: {}, currentLocale: opts.initLangCode, + identities: {}, }, opts.initState) this.store = new ObservableStore(initState) } @@ -62,6 +63,16 @@ class PreferencesController { this.store.updateState({ currentLocale: key }) } + setAddresses (addresses) { + const oldIdentities = this.store.getState().identities + const identities = addresses.reduce((ids, address, index) => { + const oldId = oldIdentities[address] || {} + ids[address] = {name: `Account ${index + 1}`, address, ...oldId} + return ids + }, {}) + this.store.updateState({ identities }) + } + /** * Setter for the `selectedAddress` property * @@ -156,6 +167,21 @@ class PreferencesController { } /** + * Sets a custom label for an account + * @param {string} account the account to set a label for + * @param {string} label the custom label for the account + * @return {Promise<string>} + */ + setAccountLabel (account, label) { + const address = normalizeAddress(account) + const {identities} = this.store.getState() + identities[address] = identities[address] || {} + identities[address].name = label + this.store.updateState({ identities }) + return Promise.resolve(label) + } + + /** * Gets an updated rpc list from this.addToFrequentRpcList() and sets the `frequentRpcList` to this update list. * * @param {string} _url The the new rpc url to add to the updated list @@ -189,8 +215,8 @@ 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. - * @returns {Promise<array>} The updated frequentRpcList. + * @param {string} _url The rpc url to add to the frequentRpcList. + * @returns {Promise<array>} The updated frequentRpcList. * */ addToFrequentRpcList (_url) { diff --git a/app/scripts/controllers/recent-blocks.js b/app/scripts/controllers/recent-blocks.js index 1377c1ba9..033ef1d7e 100644 --- a/app/scripts/controllers/recent-blocks.js +++ b/app/scripts/controllers/recent-blocks.js @@ -119,29 +119,21 @@ class RecentBlocksController { */ async backfill() { this.blockTracker.once('block', async (block) => { - let blockNum = block.number - let recentBlocks - let state = this.store.getState() - recentBlocks = state.recentBlocks - - while (recentBlocks.length < this.historyLength) { + const currentBlockNumber = Number.parseInt(block.number, 16) + const blocksToFetch = Math.min(currentBlockNumber, this.historyLength) + const prevBlockNumber = currentBlockNumber - 1 + const targetBlockNumbers = Array(blocksToFetch).fill().map((_, index) => prevBlockNumber - index) + await Promise.all(targetBlockNumbers.map(async (targetBlockNumber) => { try { - let blockNumBn = new BN(blockNum.substr(2), 16) - const newNum = blockNumBn.subn(1).toString(10) - const newBlock = await this.getBlockByNumber(newNum) + const newBlock = await this.getBlockByNumber(targetBlockNumber) if (newBlock) { this.backfillBlock(newBlock) - blockNum = newBlock.number } - - state = this.store.getState() - recentBlocks = state.recentBlocks } catch (e) { log.error(e) } - await this.wait() - } + })) }) } diff --git a/app/scripts/controllers/transactions/lib/tx-state-history-helper.js b/app/scripts/controllers/transactions/lib/tx-state-history-helper.js index 59a4b562c..4562568e9 100644 --- a/app/scripts/controllers/transactions/lib/tx-state-history-helper.js +++ b/app/scripts/controllers/transactions/lib/tx-state-history-helper.js @@ -25,26 +25,31 @@ function migrateFromSnapshotsToDiffs (longHistory) { } /** - generates an array of history objects sense the previous state. - The object has the keys opp(the operation preformed), - path(the key and if a nested object then each key will be seperated with a `/`) - value - with the first entry having the note + Generates an array of history objects sense the previous state. + The object has the keys + op (the operation performed), + path (the key and if a nested object then each key will be seperated with a `/`) + value + with the first entry having the note and a timestamp when the change took place @param previousState {object} - the previous state of the object @param newState {object} - the update object @param note {string} - a optional note for the state change - @reurns {array} + @returns {array} */ function generateHistoryEntry (previousState, newState, note) { const entry = jsonDiffer.compare(previousState, newState) // Add a note to the first op, since it breaks if we append it to the entry - if (note && entry[0]) entry[0].note = note + if (entry[0]) { + if (note) entry[0].note = note + + entry[0].timestamp = Date.now() + } return entry } /** Recovers previous txMeta state obj - @return {object} + @returns {object} */ function replayHistory (_shortHistory) { const shortHistory = clone(_shortHistory) diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js index f05c7d095..0aae4774b 100644 --- a/app/scripts/controllers/transactions/tx-state-manager.js +++ b/app/scripts/controllers/transactions/tx-state-manager.js @@ -159,7 +159,7 @@ class TransactionStateManager extends EventEmitter { /** updates the txMeta in the list and adds a history entry @param txMeta {Object} - the txMeta to update - @param [note] {string} - a not about the update for history + @param [note] {string} - a note about the update for history */ updateTx (txMeta, note) { // validate txParams @@ -263,7 +263,7 @@ class TransactionStateManager extends EventEmitter { */ getTxsByMetaData (key, value, txList = this.getTxList()) { return txList.filter((txMeta) => { - if (txMeta.txParams[key]) { + if (key in txMeta.txParams) { return txMeta.txParams[key] === value } else { return txMeta[key] === value diff --git a/app/scripts/first-time-state.js b/app/scripts/first-time-state.js index c49d89288..13119bc04 100644 --- a/app/scripts/first-time-state.js +++ b/app/scripts/first-time-state.js @@ -1,7 +1,3 @@ -// test and development environment variables -const env = process.env.METAMASK_ENV -const METAMASK_DEBUG = process.env.METAMASK_DEBUG -const { DEFAULT_NETWORK, MAINNET } = require('./controllers/network/enums') /** * @typedef {Object} FirstTimeState @@ -14,11 +10,6 @@ const { DEFAULT_NETWORK, MAINNET } = require('./controllers/network/enums') */ const initialState = { config: {}, - NetworkController: { - provider: { - type: (METAMASK_DEBUG || env === 'test') ? DEFAULT_NETWORK : MAINNET, - }, - }, } module.exports = initialState diff --git a/app/scripts/lib/createErrorMiddleware.js b/app/scripts/lib/createErrorMiddleware.js new file mode 100644 index 000000000..baed99e45 --- /dev/null +++ b/app/scripts/lib/createErrorMiddleware.js @@ -0,0 +1,66 @@ +const log = require('loglevel') + +/** + * JSON-RPC error object + * + * @typedef {Object} RpcError + * @property {number} code - Indicates the error type that occurred + * @property {Object} [data] - Contains additional information about the error + * @property {string} [message] - Short description of the error + */ + +/** + * Middleware configuration object + * + * @typedef {Object} MiddlewareConfig + * @property {boolean} [override] - Use RPC_ERRORS message in place of provider message + */ + +/** + * Map of standard and non-standard RPC error codes to messages + */ +const RPC_ERRORS = { + 1: 'An unauthorized action was attempted.', + 2: 'A disallowed action was attempted.', + 3: 'An execution error occurred.', + [-32600]: 'The JSON sent is not a valid Request object.', + [-32601]: 'The method does not exist / is not available.', + [-32602]: 'Invalid method parameter(s).', + [-32603]: 'Internal JSON-RPC error.', + [-32700]: 'Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.', + internal: 'Internal server error.', + unknown: 'Unknown JSON-RPC error.', +} + +/** + * Modifies a JSON-RPC error object in-place to add a human-readable message, + * optionally overriding any provider-supplied message + * + * @param {RpcError} error - JSON-RPC error object + * @param {boolean} override - Use RPC_ERRORS message in place of provider message + */ +function sanitizeRPCError (error, override) { + if (error.message && !override) { return error } + const message = error.code > -31099 && error.code < -32100 ? RPC_ERRORS.internal : RPC_ERRORS[error.code] + error.message = message || RPC_ERRORS.unknown +} + +/** + * json-rpc-engine middleware that both logs standard and non-standard error + * messages and ends middleware stack traversal if an error is encountered + * + * @param {MiddlewareConfig} [config={override:true}] - Middleware configuration + * @returns {Function} json-rpc-engine middleware function + */ +function createErrorMiddleware ({ override = true } = {}) { + return (req, res, next) => { + next(done => { + const { error } = res + if (!error) { return done() } + sanitizeRPCError(error) + log.error(`MetaMask - RPC Error: ${error.message}`, error) + }) + } +} + +module.exports = createErrorMiddleware
\ No newline at end of file diff --git a/app/scripts/lib/get-first-preferred-lang-code.js b/app/scripts/lib/get-first-preferred-lang-code.js index 5473fccf0..1e6a83ba6 100644 --- a/app/scripts/lib/get-first-preferred-lang-code.js +++ b/app/scripts/lib/get-first-preferred-lang-code.js @@ -2,6 +2,12 @@ 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( + extension.i18n.getAcceptLanguages, + { errorFirst: false } +) : async () => [] + const existingLocaleCodes = allLocales.map(locale => locale.code.toLowerCase().replace('_', '-')) /** @@ -12,10 +18,7 @@ const existingLocaleCodes = allLocales.map(locale => locale.code.toLowerCase().r * */ async function getFirstPreferredLangCode () { - const userPreferredLocaleCodes = await promisify( - extension.i18n.getAcceptLanguages, - { errorFirst: false } - )() + const userPreferredLocaleCodes = await getPreferredLocales() const firstPreferredLangCode = userPreferredLocaleCodes .map(code => code.toLowerCase()) .find(code => existingLocaleCodes.includes(code)) diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js index 99cc5d2cf..4e65f0a23 100644 --- a/app/scripts/lib/inpage-provider.js +++ b/app/scripts/lib/inpage-provider.js @@ -1,5 +1,6 @@ const pump = require('pump') const RpcEngine = require('json-rpc-engine') +const createErrorMiddleware = require('./createErrorMiddleware') const createIdRemapMiddleware = require('json-rpc-engine/src/idRemapMiddleware') const createStreamMiddleware = require('json-rpc-middleware-stream') const LocalStorageStore = require('obs-store') @@ -44,6 +45,7 @@ function MetamaskInpageProvider (connectionStream) { // handle sendAsync requests via dapp-side rpc engine const rpcEngine = new RpcEngine() rpcEngine.push(createIdRemapMiddleware()) + rpcEngine.push(createErrorMiddleware()) rpcEngine.push(streamMiddleware) self.rpcEngine = rpcEngine } diff --git a/app/scripts/lib/setupRaven.js b/app/scripts/lib/setupRaven.js index b1b67f771..d164827ab 100644 --- a/app/scripts/lib/setupRaven.js +++ b/app/scripts/lib/setupRaven.js @@ -25,7 +25,7 @@ function setupRaven(opts) { const report = opts.data try { // handle error-like non-error exceptions - nonErrorException(report) + rewriteErrorLikeExceptions(report) // simplify certain complex error messages (e.g. Ethjs) simplifyErrorMessages(report) // modify report urls @@ -42,27 +42,35 @@ function setupRaven(opts) { return Raven } -function nonErrorException(report) { - // handle errors that lost their error-ness in serialization - if (report.message.includes('Non-Error exception captured with keys: message')) { - if (!(report.extra && report.extra.__serialized__)) return - report.message = `Non-Error Exception: ${report.extra.__serialized__.message}` - } +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 + if (!(report.extra && report.extra.__serialized__ && report.extra.__serialized__.message)) return errorMessage + return `Non-Error Exception: ${report.extra.__serialized__.message}` + }) } function simplifyErrorMessages(report) { + rewriteErrorMessages(report, (errorMessage) => { + // simplify ethjs error messages + errorMessage = extractEthjsErrorMessage(errorMessage) + // simplify 'Transaction Failed: known transaction' + if (errorMessage.indexOf('Transaction Failed: known transaction') === 0) { + // cut the hash from the error message + errorMessage = 'Transaction Failed: known transaction' + } + return errorMessage + }) +} + +function rewriteErrorMessages(report, rewriteFn) { + // rewrite top level message + report.message = rewriteFn(report.message) + // rewrite each exception message if (report.exception && report.exception.values) { report.exception.values.forEach(item => { - let errorMessage = item.value - // simplify ethjs error messages - errorMessage = extractEthjsErrorMessage(errorMessage) - // simplify 'Transaction Failed: known transaction' - if (errorMessage.indexOf('Transaction Failed: known transaction') === 0) { - // cut the hash from the error message - errorMessage = 'Transaction Failed: known transaction' - } - // finalize - item.value = errorMessage + item.value = rewriteFn(item.value) }) } } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index b0666d9f9..a570f2567 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -145,7 +145,8 @@ module.exports = class MetamaskController extends EventEmitter { // address book controller this.addressBookController = new AddressBookController({ initState: initState.AddressBookController, - }, this.keyringController) + preferencesStore: this.preferencesController.store, + }) // tx mgmt this.txController = new TransactionController({ @@ -350,13 +351,12 @@ module.exports = class MetamaskController extends EventEmitter { verifySeedPhrase: nodeify(this.verifySeedPhrase, this), clearSeedWordCache: this.clearSeedWordCache.bind(this), resetAccount: nodeify(this.resetAccount, this), - importAccountWithStrategy: this.importAccountWithStrategy.bind(this), + importAccountWithStrategy: nodeify(this.importAccountWithStrategy, this), // vault management submitPassword: nodeify(keyringController.submitPassword, keyringController), // network management - setNetworkEndpoints: nodeify(networkController.setNetworkEndpoints, networkController), setProviderType: nodeify(networkController.setProviderType, networkController), setCustomRpc: nodeify(this.setCustomRpc, this), @@ -365,6 +365,7 @@ module.exports = class MetamaskController extends EventEmitter { addToken: nodeify(preferencesController.addToken, preferencesController), removeToken: nodeify(preferencesController.removeToken, preferencesController), setCurrentAccountTab: nodeify(preferencesController.setCurrentAccountTab, preferencesController), + setAccountLabel: nodeify(preferencesController.setAccountLabel, preferencesController), setFeatureFlag: nodeify(preferencesController.setFeatureFlag, preferencesController), // AddressController @@ -375,7 +376,6 @@ module.exports = class MetamaskController extends EventEmitter { createNewVaultAndKeychain: nodeify(this.createNewVaultAndKeychain, this), createNewVaultAndRestore: nodeify(this.createNewVaultAndRestore, this), addNewKeyring: nodeify(keyringController.addNewKeyring, keyringController), - saveAccountLabel: nodeify(keyringController.saveAccountLabel, keyringController), exportAccount: nodeify(keyringController.exportAccount, keyringController), // txController @@ -383,6 +383,7 @@ module.exports = class MetamaskController extends EventEmitter { updateTransaction: nodeify(txController.updateTransaction, txController), updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController), retryTransaction: nodeify(this.retryTransaction, this), + getFilteredTxList: nodeify(txController.getFilteredTxList, txController), // messageManager signMessage: nodeify(this.signMessage, this), @@ -434,7 +435,9 @@ module.exports = class MetamaskController extends EventEmitter { } else { vault = await this.keyringController.createNewVaultAndKeychain(password) - this.selectFirstIdentity(vault) + const accounts = await this.keyringController.getAccounts() + this.preferencesController.setAddresses(accounts) + this.selectFirstIdentity() } release() } catch (err) { @@ -454,7 +457,9 @@ module.exports = class MetamaskController extends EventEmitter { const release = await this.createVaultMutex.acquire() try { const vault = await this.keyringController.createNewVaultAndRestore(password, seed) - this.selectFirstIdentity(vault) + const accounts = await this.keyringController.getAccounts() + this.preferencesController.setAddresses(accounts) + this.selectFirstIdentity() release() return vault } catch (err) { @@ -472,12 +477,10 @@ module.exports = class MetamaskController extends EventEmitter { */ /** - * Retrieves the first Identiy from the passed Vault and selects the related address - * - * @param {} vault + * Sets the first address in the state to the selected address */ - selectFirstIdentity (vault) { - const { identities } = vault + selectFirstIdentity () { + const { identities } = this.preferencesController.store.getState() const address = Object.keys(identities)[0] this.preferencesController.setSelectedAddress(address) } @@ -503,13 +506,15 @@ module.exports = class MetamaskController extends EventEmitter { await this.verifySeedPhrase() + this.preferencesController.setAddresses(newAccounts) newAccounts.forEach((address) => { if (!oldAccounts.includes(address)) { this.preferencesController.setSelectedAddress(address) } }) - return keyState + const {identities} = this.preferencesController.store.getState() + return {...keyState, identities} } /** @@ -604,15 +609,15 @@ module.exports = class MetamaskController extends EventEmitter { * @param {any} args - The data required by that strategy to import an account. * @param {Function} cb - A callback function called with a state update on success. */ - importAccountWithStrategy (strategy, args, cb) { - accountImporter.importAccount(strategy, args) - .then((privateKey) => { - return this.keyringController.addNewKeyring('Simple Key Pair', [ privateKey ]) - }) - .then(keyring => keyring.getAccounts()) - .then((accounts) => this.preferencesController.setSelectedAddress(accounts[0])) - .then(() => { cb(null, this.keyringController.fullUpdate()) }) - .catch((reason) => { cb(reason) }) + async importAccountWithStrategy (strategy, args) { + const privateKey = await accountImporter.importAccount(strategy, args) + const keyring = await this.keyringController.addNewKeyring('Simple Key Pair', [ privateKey ]) + const accounts = await keyring.getAccounts() + // update accounts in preferences controller + const allAccounts = await this.keyringController.getAccounts() + this.preferencesController.setAddresses(allAccounts) + // set new account as selected + await this.preferencesController.setSelectedAddress(accounts[0]) } // --------------------------------------------------------------------------- diff --git a/app/scripts/migrations/026.js b/app/scripts/migrations/026.js new file mode 100644 index 000000000..1b8a91a45 --- /dev/null +++ b/app/scripts/migrations/026.js @@ -0,0 +1,47 @@ +const version = 26 + +/* + +This migration moves the identities stored in the KeyringController + into the PreferencesController + +*/ + +const clone = require('clone') + +module.exports = { + version, + migrate (originalVersionedData) { + const versionedData = clone(originalVersionedData) + versionedData.meta.version = version + try { + const state = versionedData.data + versionedData.data = transformState(state) + } catch (err) { + console.warn(`MetaMask Migration #${version}` + err.stack) + return Promise.reject(err) + } + return Promise.resolve(versionedData) + }, +} + +function transformState (state) { + if (!state.KeyringController || !state.PreferencesController) { + return + } + + if (!state.KeyringController.walletNicknames) { + return state + } + + state.PreferencesController.identities = Object.keys(state.KeyringController.walletNicknames) + .reduce((identities, address) => { + identities[address] = { + name: state.KeyringController.walletNicknames[address], + address, + } + return identities + }, {}) + delete state.KeyringController.walletNicknames + return state +} diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index 6c4a51b32..04d90bfff 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -36,4 +36,5 @@ module.exports = [ require('./023'), require('./024'), require('./025'), + require('./026'), ] |