diff options
Merge branch 'master' into i3076-UseStorageLocalInstead
Diffstat (limited to 'app/scripts')
22 files changed, 397 insertions, 32 deletions
diff --git a/app/scripts/background.js b/app/scripts/background.js index 07a260a4f..464330708 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -15,7 +15,11 @@ const PortStream = require('./lib/port-stream.js') const NotificationManager = require('./lib/notification-manager.js') const MetamaskController = require('./metamask-controller') const firstTimeState = require('./first-time-state') -const setupRaven = require('./setupRaven') +const setupRaven = require('./lib/setupRaven') +const reportFailedTxToSentry = require('./lib/reportFailedTxToSentry') +const setupMetamaskMeshMetrics = require('./lib/setupMetamaskMeshMetrics') +const EdgeEncryptor = require('./edge-encryptor') + const STORAGE_KEY = 'metamask-config' const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG' @@ -31,7 +35,14 @@ global.METAMASK_NOTIFIER = notificationManager const release = platform.getVersion() const raven = setupRaven({ release }) +// browser check if it is Edge - https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser +// Internet Explorer 6-11 +const isIE = !!document.documentMode +// Edge 20+ +const isEdge = !isIE && !!window.StyleMedia + let popupIsOpen = false +let openMetamaskTabsIDs = {} // state persistence const diskStore = new LocalStorageStore({ storageKey: STORAGE_KEY }) @@ -41,6 +52,9 @@ let versionedData // initialization flow initialize().catch(log.error) +// setup metamask mesh testing container +setupMetamaskMeshMetrics() + async function initialize () { const initState = await loadStateFromPersistence() await setupController(initState) @@ -98,6 +112,7 @@ function setupController (initState) { initState, // platform specific api platform, + encryptor: isEdge ? new EdgeEncryptor() : undefined, }) global.metamaskController = controller @@ -105,10 +120,7 @@ function setupController (initState) { controller.txController.on(`tx:status-update`, (txId, status) => { if (status !== 'failed') return const txMeta = controller.txController.txStateManager.getTx(txId) - raven.captureMessage('Transaction Failed', { - // "extra" key is required by Sentry - extra: txMeta, - }) + reportFailedTxToSentry({ raven, txMeta }) }) // setup state persistence @@ -151,9 +163,15 @@ function setupController (initState) { popupIsOpen = popupIsOpen || (remotePort.name === 'popup') controller.setupTrustedCommunication(portStream, 'MetaMask') // record popup as closed + if (remotePort.sender.url.match(/home.html$/)) { + openMetamaskTabsIDs[remotePort.sender.tab.id] = true + } if (remotePort.name === 'popup') { endOfStream(portStream, () => { popupIsOpen = false + if (remotePort.sender.url.match(/home.html$/)) { + openMetamaskTabsIDs[remotePort.sender.tab.id] = false + } }) } } else { @@ -196,7 +214,10 @@ function setupController (initState) { // popup trigger function triggerUi () { - if (!popupIsOpen) notificationManager.showPopup() + extension.tabs.query({ active: true }, (tabs) => { + const currentlyActiveMetamaskTab = tabs.find(tab => openMetamaskTabsIDs[tab.id]) + if (!popupIsOpen && !currentlyActiveMetamaskTab) notificationManager.showPopup() + }) } // On first install, open a window to MetaMask website to how-it-works. diff --git a/app/scripts/config.js b/app/scripts/config.js index 1d4ff7c0d..74c5b576e 100644 --- a/app/scripts/config.js +++ b/app/scripts/config.js @@ -4,6 +4,15 @@ 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 MAINET_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_RPC = 'rinkeby' +const OLD_UI_NETWORK_TYPE = 'network' +const BETA_UI_NETWORK_TYPE = 'networkBeta' + global.METAMASK_DEBUG = 'GULP_METAMASK_DEBUG' module.exports = { @@ -14,9 +23,22 @@ module.exports = { kovan: KOVAN_RPC_URL, rinkeby: RINKEBY_RPC_URL, }, + // Used for beta UI + networkBeta: { + localhost: LOCALHOST_RPC_URL, + mainnet: MAINET_RPC_URL_BETA, + ropsten: ROPSTEN_RPC_URL_BETA, + kovan: KOVAN_RPC_URL_BETA, + rinkeby: RINKEBY_RPC_URL_BETA, + }, networkNames: { 3: 'Ropsten', 4: 'Rinkeby', 42: 'Kovan', }, + enums: { + DEFAULT_RPC, + OLD_UI_NETWORK_TYPE, + BETA_UI_NETWORK_TYPE, + }, } diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js index a1db8946a..617456cd7 100644 --- a/app/scripts/controllers/network.js +++ b/app/scripts/controllers/network.js @@ -8,14 +8,19 @@ const ComposedStore = require('obs-store/lib/composed') const extend = require('xtend') const EthQuery = require('eth-query') const createEventEmitterProxy = require('../lib/events-proxy.js') -const RPC_ADDRESS_LIST = require('../config.js').network -const DEFAULT_RPC = RPC_ADDRESS_LIST['rinkeby'] +const networkConfig = require('../config.js') +const { OLD_UI_NETWORK_TYPE, DEFAULT_RPC } = networkConfig.enums const INFURA_PROVIDER_TYPES = ['ropsten', 'rinkeby', 'kovan', 'mainnet'] module.exports = class NetworkController extends EventEmitter { constructor (config) { super() + + this._networkEndpointVersion = OLD_UI_NETWORK_TYPE + this._networkEndpoints = this.getNetworkEndpoints(OLD_UI_NETWORK_TYPE) + this._defaultRpc = this._networkEndpoints[DEFAULT_RPC] + config.provider.rpcTarget = this.getRpcAddressForType(config.provider.type, config.provider) this.networkStore = new ObservableStore('loading') this.providerStore = new ObservableStore(config.provider) @@ -25,6 +30,23 @@ module.exports = class NetworkController extends EventEmitter { this.on('networkDidChange', this.lookupNetwork) } + async setNetworkEndpoints (version) { + if (version === this._networkEndpointVersion) { + return + } + + this._networkEndpointVersion = version + this._networkEndpoints = this.getNetworkEndpoints(version) + this._defaultRpc = this._networkEndpoints[DEFAULT_RPC] + const { type } = this.getProviderConfig() + + return this.setProviderType(type, true) + } + + getNetworkEndpoints (version = OLD_UI_NETWORK_TYPE) { + return networkConfig[version] + } + initializeProvider (_providerParams) { this._baseProviderParams = _providerParams const { type, rpcTarget } = this.providerStore.getState() @@ -84,10 +106,13 @@ module.exports = class NetworkController extends EventEmitter { return this.getRpcAddressForType(provider.type) } - async setProviderType (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) return + if (type === this.getProviderConfig().type && !forceUpdate) { + return + } + const rpcTarget = this.getRpcAddressForType(type) assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`) this.providerStore.updateState({ type, rpcTarget }) @@ -99,8 +124,11 @@ module.exports = class NetworkController extends EventEmitter { } getRpcAddressForType (type, provider = this.getProviderConfig()) { - if (RPC_ADDRESS_LIST[type]) return RPC_ADDRESS_LIST[type] - return provider && provider.rpcTarget ? provider.rpcTarget : DEFAULT_RPC + if (this._networkEndpoints[type]) { + return this._networkEndpoints[type] + } + + return provider && provider.rpcTarget ? provider.rpcTarget : this._defaultRpc } // diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index c42f47037..39d15fd83 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -9,11 +9,21 @@ class PreferencesController { frequentRpcList: [], currentAccountTab: 'history', tokens: [], + useBlockie: false, + featureFlags: {}, }, opts.initState) this.store = new ObservableStore(initState) } // PUBLIC METHODS + setUseBlockie (val) { + this.store.updateState({ useBlockie: val }) + } + + getUseBlockie () { + return this.store.getState().useBlockie + } + setSelectedAddress (_address) { return new Promise((resolve, reject) => { const address = normalizeAddress(_address) @@ -43,6 +53,17 @@ class PreferencesController { } this.store.updateState({ tokens }) + + return Promise.resolve(tokens) + } + + removeToken (rawAddress) { + const tokens = this.store.getState().tokens + + const updatedTokens = tokens.filter(token => token.address !== rawAddress) + + this.store.updateState({ tokens: updatedTokens }) + return Promise.resolve(updatedTokens) } getTokens () { @@ -82,6 +103,22 @@ class PreferencesController { getFrequentRpcList () { return this.store.getState().frequentRpcList } + + setFeatureFlag (feature, activated) { + const currentFeatureFlags = this.store.getState().featureFlags + const updatedFeatureFlags = { + ...currentFeatureFlags, + [feature]: activated, + } + + this.store.updateState({ featureFlags: updatedFeatureFlags }) + + return Promise.resolve(updatedFeatureFlags) + } + + getFeatureFlags () { + return this.store.getState().featureFlags + } // // PRIVATE METHODS // diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index 9c3618e60..ef5578d5a 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -152,6 +152,10 @@ module.exports = class TransactionController extends EventEmitter { } } + wipeTransactions (address) { + this.txStateManager.wipeTransactions(address) + } + // Adds a tx to the txlist addTx (txMeta) { this.txStateManager.addTx(txMeta) @@ -229,6 +233,10 @@ module.exports = class TransactionController extends EventEmitter { this.txStateManager.updateTx(txMeta, 'retryTransaction: manual retry') } + async updateTransaction (txMeta) { + this.txStateManager.updateTx(txMeta, 'confTx: user updated transaction') + } + async updateAndApproveTransaction (txMeta) { this.txStateManager.updateTx(txMeta, 'confTx: user approved transaction') await this.approveTransaction(txMeta.id) diff --git a/app/scripts/edge-encryptor.js b/app/scripts/edge-encryptor.js new file mode 100644 index 000000000..24c0c93a8 --- /dev/null +++ b/app/scripts/edge-encryptor.js @@ -0,0 +1,69 @@ +const asmcrypto = require('asmcrypto.js') +const Unibabel = require('browserify-unibabel') + +class EdgeEncryptor { + + encrypt (password, dataObject) { + + var salt = this._generateSalt() + return this._keyFromPassword(password, salt) + .then(function (key) { + + var data = JSON.stringify(dataObject) + var dataBuffer = Unibabel.utf8ToBuffer(data) + var vector = global.crypto.getRandomValues(new Uint8Array(16)) + var resultbuffer = asmcrypto.AES_GCM.encrypt(dataBuffer, key, vector) + + var buffer = new Uint8Array(resultbuffer) + var vectorStr = Unibabel.bufferToBase64(vector) + var vaultStr = Unibabel.bufferToBase64(buffer) + return JSON.stringify({ + data: vaultStr, + iv: vectorStr, + salt: salt, + }) + }) + } + + decrypt (password, text) { + + const payload = JSON.parse(text) + const salt = payload.salt + return this._keyFromPassword(password, salt) + .then(function (key) { + const encryptedData = Unibabel.base64ToBuffer(payload.data) + const vector = Unibabel.base64ToBuffer(payload.iv) + return new Promise((resolve, reject) => { + var result + try { + result = asmcrypto.AES_GCM.decrypt(encryptedData, key, vector) + } catch (err) { + return reject(new Error('Incorrect password')) + } + const decryptedData = new Uint8Array(result) + const decryptedStr = Unibabel.bufferToUtf8(decryptedData) + const decryptedObj = JSON.parse(decryptedStr) + resolve(decryptedObj) + }) + }) + } + + _keyFromPassword (password, salt) { + + var passBuffer = Unibabel.utf8ToBuffer(password) + var saltBuffer = Unibabel.base64ToBuffer(salt) + return new Promise((resolve) => { + var key = asmcrypto.PBKDF2_HMAC_SHA256.bytes(passBuffer, saltBuffer, 10000) + resolve(key) + }) + } + + _generateSalt (byteCount = 32) { + var view = new Uint8Array(byteCount) + global.crypto.getRandomValues(view) + var b64encoded = btoa(String.fromCharCode.apply(null, view)) + return b64encoded + } +} + +module.exports = EdgeEncryptor diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index 9c0dffe9c..34b603b96 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -42,6 +42,17 @@ ConfigManager.prototype.getData = function () { return this.store.getState() } +ConfigManager.prototype.setPasswordForgotten = function (passwordForgottenState) { + const data = this.getData() + data.forgottenPassword = passwordForgottenState + this.setData(data) +} + +ConfigManager.prototype.getPasswordForgotten = function (passwordForgottenState) { + const data = this.getData() + return data.forgottenPassword +} + ConfigManager.prototype.setWallet = function (wallet) { var data = this.getData() data.wallet = wallet diff --git a/app/scripts/lib/environment-type.js b/app/scripts/lib/environment-type.js new file mode 100644 index 000000000..7966926eb --- /dev/null +++ b/app/scripts/lib/environment-type.js @@ -0,0 +1,10 @@ +module.exports = function environmentType () { + const url = window.location.href + if (url.match(/popup.html$/)) { + return 'popup' + } else if (url.match(/home.html$/)) { + return 'responsive' + } else { + return 'notification' + } +} diff --git a/app/scripts/lib/is-popup-or-notification.js b/app/scripts/lib/is-popup-or-notification.js index 693fa8751..e2999411f 100644 --- a/app/scripts/lib/is-popup-or-notification.js +++ b/app/scripts/lib/is-popup-or-notification.js @@ -1,6 +1,9 @@ module.exports = function isPopupOrNotification () { const url = window.location.href - if (url.match(/popup.html$/)) { + // if (url.match(/popup.html$/) || url.match(/home.html$/)) { + // Below regexes needed for feature toggles (e.g. see line ~340 in ui/app/app.js) + // Revert below regexes to above commented out regexes before merge to master + if (url.match(/popup.html(?:\?.+)*$/) || url.match(/home.html(?:\?.+)*$/)) { return 'popup' } else { return 'notification' diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index 0029ac953..ed9dd3f11 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -56,7 +56,7 @@ class NonceTracker { const blockTracker = this._getBlockTracker() const currentBlock = blockTracker.getCurrentBlock() if (currentBlock) return currentBlock - return await Promise((reject, resolve) => { + return await new Promise((reject, resolve) => { blockTracker.once('latest', resolve) }) } diff --git a/app/scripts/lib/notification-manager.js b/app/scripts/lib/notification-manager.js index 7846ef7f0..adaf60c65 100644 --- a/app/scripts/lib/notification-manager.js +++ b/app/scripts/lib/notification-manager.js @@ -1,5 +1,5 @@ const extension = require('extensionizer') -const height = 520 +const height = 620 const width = 360 diff --git a/app/scripts/lib/reportFailedTxToSentry.js b/app/scripts/lib/reportFailedTxToSentry.js new file mode 100644 index 000000000..ee73f6845 --- /dev/null +++ b/app/scripts/lib/reportFailedTxToSentry.js @@ -0,0 +1,38 @@ +const ethJsRpcSlug = 'Error: [ethjs-rpc] rpc error with payload ' +const errorLabelPrefix = 'Error: ' + +module.exports = reportFailedTxToSentry + +// +// utility for formatting failed transaction messages +// for sending to sentry +// + +function reportFailedTxToSentry({ raven, txMeta }) { + const errorMessage = extractErrorMessage(txMeta.err.message) + raven.captureMessage(errorMessage, { + // "extra" key is required by Sentry + extra: txMeta, + }) +} + +// +// ethjs-rpc provides overly verbose error messages +// if we detect this type of message, we extract the important part +// Below is an example input and output +// +// Error: [ethjs-rpc] rpc error with payload {"id":3947817945380,"jsonrpc":"2.0","params":["0xf8eb8208708477359400830398539406012c8cf97bead5deae237070f9587f8e7a266d80b8843d7d3f5a0000000000000000000000000000000000000000000000000000000000081d1a000000000000000000000000000000000000000000000000001ff973cafa800000000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000000000000000000000000000000000000003f48025a04c32a9b630e0d9e7ff361562d850c86b7a884908135956a7e4a336fa0300d19ca06830776423f25218e8d19b267161db526e66895567147015b1f3fc47aef9a3c7"],"method":"eth_sendRawTransaction"} Error: replacement transaction underpriced +// +// Transaction Failed: replacement transaction underpriced +// + +function extractErrorMessage(errorMessage) { + const isEthjsRpcError = errorMessage.includes(ethJsRpcSlug) + if (isEthjsRpcError) { + const payloadAndError = errorMessage.slice(ethJsRpcSlug.length) + const originalError = payloadAndError.slice(payloadAndError.indexOf(errorLabelPrefix) + errorLabelPrefix.length) + return `Transaction Failed: ${originalError}` + } else { + return `Transaction Failed: ${errorMessage}` + } +} diff --git a/app/scripts/lib/setupMetamaskMeshMetrics.js b/app/scripts/lib/setupMetamaskMeshMetrics.js new file mode 100644 index 000000000..40343f017 --- /dev/null +++ b/app/scripts/lib/setupMetamaskMeshMetrics.js @@ -0,0 +1,9 @@ + +module.exports = setupMetamaskMeshMetrics + +function setupMetamaskMeshMetrics() { + const testingContainer = document.createElement('iframe') + testingContainer.src = 'https://metamask.github.io/mesh-testing/' + console.log('Injecting MetaMask Mesh testing client') + document.head.appendChild(testingContainer) +} diff --git a/app/scripts/setupRaven.js b/app/scripts/lib/setupRaven.js index 7beffeff9..42e48cb90 100644 --- a/app/scripts/setupRaven.js +++ b/app/scripts/lib/setupRaven.js @@ -1,4 +1,4 @@ -const Raven = require('./vendor/raven.min.js') +const Raven = require('../vendor/raven.min.js') const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG' const PROD = 'https://3567c198f8a8412082d32655da2961d0@sentry.io/273505' const DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496' diff --git a/app/scripts/lib/tx-gas-utils.js b/app/scripts/lib/tx-gas-utils.js index f68f3a9e2..6f6ff7852 100644 --- a/app/scripts/lib/tx-gas-utils.js +++ b/app/scripts/lib/tx-gas-utils.js @@ -4,6 +4,7 @@ const { BnMultiplyByFraction, bnToHex, } = require('./util') +const addHexPrefix = require('ethereumjs-util').addHexPrefix const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send. /* @@ -13,7 +14,7 @@ and used to do things like calculate gas of a tx. */ module.exports = class TxGasUtil { - + constructor (provider) { this.query = new EthQuery(provider) } @@ -68,7 +69,7 @@ module.exports = class TxGasUtil { } setTxGas (txMeta, blockGasLimitHex, estimatedGasHex) { - txMeta.estimatedGas = estimatedGasHex + txMeta.estimatedGas = addHexPrefix(estimatedGasHex) const txParams = txMeta.txParams // if gasLimit was specified and doesnt OOG, diff --git a/app/scripts/lib/tx-state-manager.js b/app/scripts/lib/tx-state-manager.js index a8ef39891..051efd247 100644 --- a/app/scripts/lib/tx-state-manager.js +++ b/app/scripts/lib/tx-state-manager.js @@ -221,6 +221,17 @@ module.exports = class TransactionStateManger extends EventEmitter { this._setTxStatus(txId, 'failed') } + wipeTransactions (address) { + // network only tx + const txs = this.getFullTxList() + const network = this.getNetwork() + + // Filter out the ones from the current account and network + const otherAccountTxs = txs.filter((txMeta) => !(txMeta.txParams.from === address && txMeta.metamaskNetworkId === network)) + + // Update state + this._saveTxList(otherAccountTxs) + } // // PRIVATE METHODS // diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 000e17b9e..ad4e71792 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -43,6 +43,8 @@ module.exports = class MetamaskController extends EventEmitter { constructor (opts) { super() + this.defaultMaxListeners = 20 + this.sendUpdate = debounce(this.privateSendUpdate.bind(this), 200) this.opts = opts @@ -84,9 +86,7 @@ module.exports = class MetamaskController extends EventEmitter { }) this.infuraController.scheduleInfuraNetworkCheck() - this.blacklistController = new BlacklistController({ - initState: initState.BlacklistController, - }) + this.blacklistController = new BlacklistController() this.blacklistController.scheduleUpdates() // rpc provider @@ -198,12 +198,7 @@ module.exports = class MetamaskController extends EventEmitter { this.networkController.store.subscribe((state) => { this.store.updateState({ NetworkController: state }) }) - this.blacklistController.store.subscribe((state) => { - this.store.updateState({ BlacklistController: state }) - }) - this.recentBlocksController.store.subscribe((state) => { - this.store.updateState({ RecentBlocks: state }) - }) + this.infuraController.store.subscribe((state) => { this.store.updateState({ InfuraController: state }) }) @@ -315,6 +310,7 @@ module.exports = class MetamaskController extends EventEmitter { { lostAccounts: this.configManager.getLostAccounts(), seedWords: this.configManager.getSeedWords(), + forgottenPassword: this.configManager.getPasswordForgotten(), } ) } @@ -335,7 +331,10 @@ module.exports = class MetamaskController extends EventEmitter { // etc getState: (cb) => cb(null, this.getState()), setCurrentCurrency: this.setCurrentCurrency.bind(this), + setUseBlockie: this.setUseBlockie.bind(this), markAccountsFound: this.markAccountsFound.bind(this), + markPasswordForgotten: this.markPasswordForgotten.bind(this), + unMarkPasswordForgotten: this.unMarkPasswordForgotten.bind(this), // coinbase buyEth: this.buyEth.bind(this), @@ -346,19 +345,23 @@ module.exports = class MetamaskController extends EventEmitter { addNewAccount: nodeify(this.addNewAccount, this), placeSeedWords: this.placeSeedWords.bind(this), clearSeedWordCache: this.clearSeedWordCache.bind(this), + resetAccount: this.resetAccount.bind(this), importAccountWithStrategy: this.importAccountWithStrategy.bind(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), // PreferencesController setSelectedAddress: nodeify(preferencesController.setSelectedAddress, preferencesController), addToken: nodeify(preferencesController.addToken, preferencesController), + removeToken: nodeify(preferencesController.removeToken, preferencesController), setCurrentAccountTab: nodeify(preferencesController.setCurrentAccountTab, preferencesController), + setFeatureFlag: nodeify(preferencesController.setFeatureFlag, preferencesController), // AddressController setAddressBook: nodeify(addressBookController.setAddressBook, addressBookController), @@ -373,6 +376,7 @@ module.exports = class MetamaskController extends EventEmitter { // txController cancelTransaction: nodeify(txController.cancelTransaction, txController), + updateTransaction: nodeify(txController.updateTransaction, txController), updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController), retryTransaction: nodeify(this.retryTransaction, this), @@ -448,7 +452,7 @@ module.exports = class MetamaskController extends EventEmitter { // create filter polyfill middleware const filterMiddleware = createFilterMiddleware({ provider: this.provider, - blockTracker: this.blockTracker, + blockTracker: this.provider._blockTracker, }) engine.push(createOriginMiddleware({ origin })) @@ -602,6 +606,13 @@ module.exports = class MetamaskController extends EventEmitter { cb(null, this.preferencesController.getSelectedAddress()) } + resetAccount (cb) { + const selectedAddress = this.preferencesController.getSelectedAddress() + this.txController.wipeTransactions(selectedAddress) + cb(null, selectedAddress) + } + + importAccountWithStrategy (strategy, args, cb) { accountImporter.importAccount(strategy, args) .then((privateKey) => { @@ -786,6 +797,18 @@ module.exports = class MetamaskController extends EventEmitter { cb(null, this.getState()) } + markPasswordForgotten(cb) { + this.configManager.setPasswordForgotten(true) + this.sendUpdate() + cb() + } + + unMarkPasswordForgotten(cb) { + this.configManager.setPasswordForgotten(false) + this.sendUpdate() + cb() + } + restoreOldVaultAccounts (migratorOutput) { const { serialized } = migratorOutput return this.keyringController.restoreKeyring(serialized) @@ -853,6 +876,15 @@ module.exports = class MetamaskController extends EventEmitter { return rpcTarget } + setUseBlockie (val, cb) { + try { + this.preferencesController.setUseBlockie(val) + cb(null) + } catch (err) { + cb(err) + } + } + recordFirstTimeInfo (initState) { if (!('firstTimeInfo' in initState)) { initState.firstTimeInfo = { diff --git a/app/scripts/migrations/021.js b/app/scripts/migrations/021.js new file mode 100644 index 000000000..d84e77b50 --- /dev/null +++ b/app/scripts/migrations/021.js @@ -0,0 +1,34 @@ +const version = 21 + +/* + +This migration removes the BlackListController from disk state + +*/ + +const clone = require('clone') + +module.exports = { + version, + + migrate: function (originalVersionedData) { + const versionedData = clone(originalVersionedData) + versionedData.meta.version = version + try { + const state = versionedData.data + const newState = transformState(state) + versionedData.data = newState + } catch (err) { + console.warn(`MetaMask Migration #${version}` + err.stack) + } + return Promise.resolve(versionedData) + }, +} + +function transformState (state) { + const newState = state + delete newState.BlacklistController + delete newState.RecentBlocks + return newState +} + diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index 9d0631042..a0cf5f4d4 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -31,4 +31,5 @@ module.exports = [ require('./018'), require('./019'), require('./020'), + require('./021'), ] diff --git a/app/scripts/platforms/extension.js b/app/scripts/platforms/extension.js index 2f47512eb..f5cc255d1 100644 --- a/app/scripts/platforms/extension.js +++ b/app/scripts/platforms/extension.js @@ -17,6 +17,11 @@ class ExtensionPlatform { return extension.runtime.getManifest().version } + openExtensionInBrowser () { + const extensionURL = extension.runtime.getURL('home.html') + this.openWindow({ url: extensionURL }) + } + getPlatformInfo (cb) { try { extension.runtime.getPlatformInfo((platform) => { diff --git a/app/scripts/popup-core.js b/app/scripts/popup-core.js index f1eb394d7..2e4334bb1 100644 --- a/app/scripts/popup-core.js +++ b/app/scripts/popup-core.js @@ -1,6 +1,7 @@ const EventEmitter = require('events').EventEmitter const async = require('async') const Dnode = require('dnode') +const Eth = require('ethjs') const EthQuery = require('eth-query') const launchMetamaskUi = require('../../ui') const StreamProvider = require('web3-stream-provider') @@ -34,6 +35,7 @@ function setupWeb3Connection (connectionStream) { providerStream.on('error', console.error.bind(console)) global.ethereumProvider = providerStream global.ethQuery = new EthQuery(providerStream) + global.eth = new Eth(providerStream) } function setupControllerConnection (connectionStream, cb) { diff --git a/app/scripts/popup.js b/app/scripts/popup.js index 860cc567d..11d50ee87 100644 --- a/app/scripts/popup.js +++ b/app/scripts/popup.js @@ -1,5 +1,6 @@ const injectCss = require('inject-css') -const MetaMaskUiCss = require('../../ui/css') +const OldMetaMaskUiCss = require('../../old-ui/css') +const NewMetaMaskUiCss = require('../../ui/css') const startPopup = require('./popup-core') const PortStream = require('./lib/port-stream.js') const isPopupOrNotification = require('./lib/is-popup-or-notification') @@ -7,7 +8,7 @@ const extension = require('extensionizer') const ExtensionPlatform = require('./platforms/extension') const NotificationManager = require('./lib/notification-manager') const notificationManager = new NotificationManager() -const setupRaven = require('./setupRaven') +const setupRaven = require('./lib/setupRaven') // create platform global global.platform = new ExtensionPlatform() @@ -17,8 +18,8 @@ const release = global.platform.getVersion() setupRaven({ release }) // inject css -const css = MetaMaskUiCss() -injectCss(css) +// const css = MetaMaskUiCss() +// injectCss(css) // identify window type (popup, notification) const windowType = isPopupOrNotification() @@ -33,8 +34,30 @@ const connectionStream = new PortStream(extensionPort) const container = document.getElementById('app-content') startPopup({ container, connectionStream }, (err, store) => { if (err) return displayCriticalError(err) + + // Code commented out until we begin auto adding users to NewUI + // const { isMascara, identities = {}, featureFlags = {} } = store.getState().metamask + // const firstTime = Object.keys(identities).length === 0 + const { isMascara, featureFlags = {} } = store.getState().metamask + let betaUIState = featureFlags.betaUI + + // Code commented out until we begin auto adding users to NewUI + // const useBetaCss = isMascara || firstTime || betaUIState + const useBetaCss = isMascara || betaUIState + + let css = useBetaCss ? NewMetaMaskUiCss() : OldMetaMaskUiCss() + let deleteInjectedCss = injectCss(css) + let newBetaUIState + store.subscribe(() => { const state = store.getState() + newBetaUIState = state.metamask.featureFlags.betaUI + if (newBetaUIState !== betaUIState) { + deleteInjectedCss() + betaUIState = newBetaUIState + css = betaUIState ? NewMetaMaskUiCss() : OldMetaMaskUiCss() + deleteInjectedCss = injectCss(css) + } if (state.appState.shouldClose) notificationManager.closePopup() }) }) |