aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/scripts/background.js129
-rw-r--r--app/scripts/config.js79
-rw-r--r--app/scripts/controllers/network/enums.js56
-rw-r--r--app/scripts/controllers/network/index.js2
-rw-r--r--app/scripts/controllers/network/network.js (renamed from app/scripts/controllers/network.js)27
-rw-r--r--app/scripts/controllers/network/util.js65
-rw-r--r--app/scripts/first-time-state.js3
-rw-r--r--app/scripts/lib/config-manager.js23
-rw-r--r--app/scripts/metamask-controller.js461
-rw-r--r--old-ui/app/app.js2
-rw-r--r--old-ui/app/components/buy-button-subview.js4
-rw-r--r--test/unit/network-contoller-test.js42
-rw-r--r--ui/app/components/buy-button-subview.js4
-rw-r--r--ui/app/components/dropdowns/network-dropdown.js14
-rw-r--r--ui/app/components/modals/buy-options-modal.js4
-rw-r--r--ui/app/components/modals/deposit-ether-modal.js4
-rw-r--r--ui/app/components/pages/settings/settings.js2
-rw-r--r--ui/app/components/pages/unlock.js2
-rw-r--r--ui/app/first-time/init-menu.js2
-rw-r--r--ui/app/reducers/metamask.js2
-rw-r--r--ui/app/select-app.js2
-rw-r--r--ui/app/unlock.js2
-rw-r--r--ui/index.js6
23 files changed, 677 insertions, 260 deletions
diff --git a/app/scripts/background.js b/app/scripts/background.js
index 6550e8944..38b871bb5 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -1,3 +1,7 @@
+/**
+ * @file The entry point for the web extension singleton process.
+ */
+
const urlUtil = require('url')
const endOfStream = require('end-of-stream')
const pump = require('pump')
@@ -61,6 +65,90 @@ initialize().catch(log.error)
// setup metamask mesh testing container
setupMetamaskMeshMetrics()
+/**
+ * An object representing a transaction, in whatever state it is in.
+ * @typedef TransactionMeta
+ *
+ * @property {number} id - An internally unique tx identifier.
+ * @property {number} time - Time the tx was first suggested, in unix epoch time (ms).
+ * @property {string} status - The current transaction status (unapproved, signed, submitted, dropped, failed, rejected), as defined in `tx-state-manager.js`.
+ * @property {string} metamaskNetworkId - The transaction's network ID, used for EIP-155 compliance.
+ * @property {boolean} loadingDefaults - TODO: Document
+ * @property {Object} txParams - The tx params as passed to the network provider.
+ * @property {Object[]} history - A history of mutations to this TransactionMeta object.
+ * @property {boolean} gasPriceSpecified - True if the suggesting dapp specified a gas price, prevents auto-estimation.
+ * @property {boolean} gasLimitSpecified - True if the suggesting dapp specified a gas limit, prevents auto-estimation.
+ * @property {string} estimatedGas - A hex string represented the estimated gas limit required to complete the transaction.
+ * @property {string} origin - A string representing the interface that suggested the transaction.
+ * @property {Object} nonceDetails - A metadata object containing information used to derive the suggested nonce, useful for debugging nonce issues.
+ * @property {string} rawTx - A hex string of the final signed transaction, ready to submit to the network.
+ * @property {string} hash - A hex string of the transaction hash, used to identify the transaction on the network.
+ * @property {number} submittedTime - The time the transaction was submitted to the network, in Unix epoch time (ms).
+ */
+
+/**
+ * The data emitted from the MetaMaskController.store EventEmitter, also used to initialize the MetaMaskController. Available in UI on React state as state.metamask.
+ * @typedef MetaMaskState
+ * @property {boolean} isInitialized - Whether the first vault has been created.
+ * @property {boolean} isUnlocked - Whether the vault is currently decrypted and accounts are available for selection.
+ * @property {boolean} isAccountMenuOpen - Represents whether the main account selection UI is currently displayed.
+ * @property {boolean} isMascara - True if the current context is the extensionless MetaMascara project.
+ * @property {boolean} isPopup - Returns true if the current view is an externally-triggered notification.
+ * @property {string} rpcTarget - DEPRECATED - The URL of the current RPC provider.
+ * @property {Object} identities - An object matching lower-case hex addresses to Identity objects with "address" and "name" (nickname) keys.
+ * @property {Object} unapprovedTxs - An object mapping transaction hashes to unapproved transactions.
+ * @property {boolean} noActiveNotices - False if there are notices the user should confirm before using the application.
+ * @property {Array} frequentRpcList - A list of frequently used RPCs, including custom user-provided ones.
+ * @property {Array} addressBook - A list of previously sent to addresses.
+ * @property {address} selectedTokenAddress - Used to indicate if a token is globally selected. Should be deprecated in favor of UI-centric token selection.
+ * @property {Object} tokenExchangeRates - Info about current token prices.
+ * @property {Array} tokens - Tokens held by the current user, including their balances.
+ * @property {Object} send - TODO: Document
+ * @property {Object} coinOptions - TODO: Document
+ * @property {boolean} useBlockie - Indicates preferred user identicon format. True for blockie, false for Jazzicon.
+ * @property {Object} featureFlags - An object for optional feature flags.
+ * @property {string} networkEndpointType - TODO: Document
+ * @property {boolean} isRevealingSeedWords - True if seed words are currently being recovered, and should be shown to user.
+ * @property {boolean} welcomeScreen - True if welcome screen should be shown.
+ * @property {string} currentLocale - A locale string matching the user's preferred display language.
+ * @property {Object} provider - The current selected network provider.
+ * @property {string} provider.rpcTarget - The address for the RPC API, if using an RPC API.
+ * @property {string} provider.type - An identifier for the type of network selected, allows MetaMask to use custom provider strategies for known networks.
+ * @property {string} network - A stringified number of the current network ID.
+ * @property {Object} accounts - An object mapping lower-case hex addresses to objects with "balance" and "address" keys, both storing hex string values.
+ * @property {hex} currentBlockGasLimit - The most recently seen block gas limit, in a lower case hex prefixed string.
+ * @property {TransactionMeta[]} selectedAddressTxList - An array of transactions associated with the currently selected account.
+ * @property {Object} unapprovedMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options.
+ * @property {number} unapprovedMsgCount - The number of messages in unapprovedMsgs.
+ * @property {Object} unapprovedPersonalMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options.
+ * @property {number} unapprovedPersonalMsgCount - The number of messages in unapprovedPersonalMsgs.
+ * @property {Object} unapprovedTypedMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options.
+ * @property {number} unapprovedTypedMsgCount - The number of messages in unapprovedTypedMsgs.
+ * @property {string[]} keyringTypes - An array of unique keyring identifying strings, representing available strategies for creating accounts.
+ * @property {Keyring[]} keyrings - An array of keyring descriptions, summarizing the accounts that are available for use, and what keyrings they belong to.
+ * @property {Object} computedBalances - Maps accounts to their balances, accounting for balance changes from pending transactions.
+ * @property {string} currentAccountTab - A view identifying string for displaying the current displayed view, allows user to have a preferred tab in the old UI (between tokens and history).
+ * @property {string} selectedAddress - A lower case hex string of the currently selected address.
+ * @property {string} currentCurrency - A string identifying the user's preferred display currency, for use in showing conversion rates.
+ * @property {number} conversionRate - A number representing the current exchange rate from the user's preferred currency to Ether.
+ * @property {number} conversionDate - A unix epoch date (ms) for the time the current conversion rate was last retrieved.
+ * @property {Object} infuraNetworkStatus - An object of infura network status checks.
+ * @property {Block[]} recentBlocks - An array of recent blocks, used to calculate an effective but cheap gas price.
+ * @property {Array} shapeShiftTxList - An array of objects describing shapeshift exchange attempts.
+ * @property {Array} lostAccounts - TODO: Remove this feature. A leftover from the version-3 migration where our seed-phrase library changed to fix a bug where some accounts were mis-generated, but we recovered the old accounts as "lost" instead of losing them.
+ * @property {boolean} forgottenPassword - Returns true if the user has initiated the password recovery screen, is recovering from seed phrase.
+ */
+
+/**
+ * @typedef VersionedData
+ * @property {MetaMaskState} data - The data emitted from MetaMask controller, or used to initialize it.
+ * @property {Number} version - The latest migration version that has been run.
+ */
+
+/**
+ * Initializes the MetaMask controller, and sets up all platform configuration.
+ * @returns {Promise} Setup complete.
+ */
async function initialize () {
const initState = await loadStateFromPersistence()
const initLangCode = await getFirstPreferredLangCode()
@@ -72,6 +160,11 @@ async function initialize () {
// State and Persistence
//
+/**
+ * Loads any stored data, prioritizing the latest storage strategy.
+ * Migrates that data schema in case it was last loaded on an older version.
+ * @returns {Promise<MetaMaskState>} Last data emitted from previous instance of MetaMask.
+ */
async function loadStateFromPersistence () {
// migrations
const migrator = new Migrator({ migrations })
@@ -134,6 +227,16 @@ async function loadStateFromPersistence () {
return versionedData.data
}
+/**
+ * Initializes the MetaMask Controller with any initial state and default language.
+ * Configures platform-specific error reporting strategy.
+ * Streams emitted state updates to platform-specific storage strategy.
+ * Creates platform listeners for new Dapps/Contexts, and sets up their data connections to the controller.
+ *
+ * @param {Object} initState - The initial state to start the controller with, matches the state that is emitted from the controller.
+ * @param {String} initLangCode - The region code for the language preferred by the current user.
+ * @returns {Promise} After setup is complete.
+ */
function setupController (initState, initLangCode) {
//
// MetaMask Controller
@@ -172,6 +275,11 @@ function setupController (initState, initLangCode) {
}
)
+ /**
+ * Assigns the given state to the versioned object (with metadata), and returns that.
+ * @param {Object} state - The state object as emitted by the MetaMaskController.
+ * @returns {VersionedData} The state object wrapped in an object that includes a metadata key.
+ */
function versionifyData (state) {
versionedData.data = state
return versionedData
@@ -208,6 +316,18 @@ function setupController (initState, initLangCode) {
return popupIsOpen || Boolean(Object.keys(openMetamaskTabsIDs).length) || notificationIsOpen
}
+ /**
+ * A runtime.Port object, as provided by the browser:
+ * @link https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/Port
+ * @typedef Port
+ * @type Object
+ */
+
+ /**
+ * Connects a Port to the MetaMask controller via a multiplexed duplex stream.
+ * This method identifies trusted (MetaMask) interfaces, and connects them differently from untrusted (web pages).
+ * @param {Port} remotePort - The port provided by a new context.
+ */
function connectRemote (remotePort) {
const processName = remotePort.name
const isMetaMaskInternalProcess = metamaskInternalProcessHash[processName]
@@ -261,7 +381,10 @@ function setupController (initState, initLangCode) {
controller.messageManager.on('updateBadge', updateBadge)
controller.personalMessageManager.on('updateBadge', updateBadge)
- // plugin badge text
+ /**
+ * Updates the Web Extension's "badge" number, on the little fox in the toolbar.
+ * The number reflects the current number of pending transactions or message signatures needing user approval.
+ */
function updateBadge () {
var label = ''
var unapprovedTxCount = controller.txController.getUnapprovedTxCount()
@@ -283,7 +406,9 @@ function setupController (initState, initLangCode) {
// Etc...
//
-// popup trigger
+/**
+ * Opens the browser popup for user confirmation
+ */
function triggerUi () {
extension.tabs.query({ active: true }, tabs => {
const currentlyActiveMetamaskTab = Boolean(tabs.find(tab => openMetamaskTabsIDs[tab.id]))
diff --git a/app/scripts/config.js b/app/scripts/config.js
deleted file mode 100644
index e6f70ca2b..000000000
--- a/app/scripts/config.js
+++ /dev/null
@@ -1,79 +0,0 @@
-const MAINET_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 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 = process.env.METAMASK_DEBUG
-
-/**
- * @typedef {Object} UrlConfig
- * @property {string} localhost URL of local RPC provider
- * @property {string} mainnet URL of mainnet RPC provider
- * @property {string} ropsten URL of Ropsten testnet RPC provider
- * @property {string} kovan URL of Kovan testnet RPC provider
- * @property {string} rinkeby URL of Rinkeby testnet RPC provider
- */
-
-/**
- * @typedef {Object} NameConfig
- * @property {string} 3 URL of local RPC provider
- * @property {string} 4 URL of mainnet RPC provider
- * @property {string} 42 URL of Ropsten testnet RPC provider
- */
-
-/**
- * @typedef {Object} EnumConfig
- * @property {string} DEFAULT_RPC Default network provider URL
- * @property {string} OLD_UI_NETWORK_TYPE Network associated with old UI
- * @property {string} BETA_UI_NETWORK_TYPE Network associated with new UI
- */
-
-/**
- * @typedef {Object} Config
- * @property {UrlConfig} network Network configuration parameters
- * @property {UrlConfig} networkBeta Beta UI network configuration parameters
- * @property {NameConfig} networkNames Network name configuration parameters
- * @property {EnumConfig} enums Application-wide string constants
- */
-
-/**
- * @type {Config}
- **/
-const config = {
- network: {
- localhost: LOCALHOST_RPC_URL,
- mainnet: MAINET_RPC_URL,
- ropsten: ROPSTEN_RPC_URL,
- kovan: KOVAN_RPC_URL,
- rinkeby: RINKEBY_RPC_URL,
- },
- 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,
- },
-}
-
-module.exports = config
diff --git a/app/scripts/controllers/network/enums.js b/app/scripts/controllers/network/enums.js
new file mode 100644
index 000000000..4f29e301b
--- /dev/null
+++ b/app/scripts/controllers/network/enums.js
@@ -0,0 +1,56 @@
+const ROPSTEN = 'ropsten'
+const RINKEBY = 'rinkeby'
+const KOVAN = 'kovan'
+const MAINNET = 'mainnet'
+const LOCALHOST = 'localhost'
+
+const ROPSTEN_CODE = 3
+const RINKEYBY_CODE = 4
+const KOVAN_CODE = 42
+
+const ROPSTEN_DISPLAY_NAME = 'Ropsten'
+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,
+ RINKEBY,
+ KOVAN,
+ MAINNET,
+ LOCALHOST,
+ ROPSTEN_CODE,
+ RINKEYBY_CODE,
+ KOVAN_CODE,
+ ROPSTEN_DISPLAY_NAME,
+ 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/index.js b/app/scripts/controllers/network/index.js
new file mode 100644
index 000000000..fb095bf33
--- /dev/null
+++ b/app/scripts/controllers/network/index.js
@@ -0,0 +1,2 @@
+const NetworkController = require('./network')
+module.exports = NetworkController
diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network/network.js
index 45574e673..6fd983bb2 100644
--- a/app/scripts/controllers/network.js
+++ b/app/scripts/controllers/network/network.js
@@ -7,11 +7,18 @@ const ObservableStore = require('obs-store')
const ComposedStore = require('obs-store/lib/composed')
const extend = require('xtend')
const EthQuery = require('eth-query')
-const createEventEmitterProxy = require('../lib/events-proxy.js')
-const networkConfig = require('../config.js')
+const createEventEmitterProxy = require('../../lib/events-proxy.js')
const log = require('loglevel')
-const { OLD_UI_NETWORK_TYPE, DEFAULT_RPC } = networkConfig.enums
-const INFURA_PROVIDER_TYPES = ['ropsten', 'rinkeby', 'kovan', 'mainnet']
+const {
+ ROPSTEN,
+ RINKEBY,
+ KOVAN,
+ MAINNET,
+ OLD_UI_NETWORK_TYPE,
+ DEFAULT_NETWORK,
+} = require('./enums')
+const { getNetworkEndpoints } = require('./util')
+const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET]
module.exports = class NetworkController extends EventEmitter {
@@ -19,8 +26,8 @@ module.exports = class NetworkController extends EventEmitter {
super()
this._networkEndpointVersion = OLD_UI_NETWORK_TYPE
- this._networkEndpoints = this.getNetworkEndpoints(OLD_UI_NETWORK_TYPE)
- this._defaultRpc = this._networkEndpoints[DEFAULT_RPC]
+ this._networkEndpoints = getNetworkEndpoints(OLD_UI_NETWORK_TYPE)
+ this._defaultRpc = this._networkEndpoints[DEFAULT_NETWORK]
config.provider.rpcTarget = this.getRpcAddressForType(config.provider.type, config.provider)
this.networkStore = new ObservableStore('loading')
@@ -37,17 +44,13 @@ module.exports = class NetworkController extends EventEmitter {
}
this._networkEndpointVersion = version
- this._networkEndpoints = this.getNetworkEndpoints(version)
- this._defaultRpc = this._networkEndpoints[DEFAULT_RPC]
+ this._networkEndpoints = getNetworkEndpoints(version)
+ this._defaultRpc = this._networkEndpoints[DEFAULT_NETWORK]
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()
diff --git a/app/scripts/controllers/network/util.js b/app/scripts/controllers/network/util.js
new file mode 100644
index 000000000..4f38ccda4
--- /dev/null
+++ b/app/scripts/controllers/network/util.js
@@ -0,0 +1,65 @@
+const {
+ ROPSTEN,
+ RINKEBY,
+ KOVAN,
+ MAINNET,
+ LOCALHOST,
+ ROPSTEN_CODE,
+ RINKEYBY_CODE,
+ KOVAN_CODE,
+ ROPSTEN_DISPLAY_NAME,
+ 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 = {
+ [ROPSTEN]: ROPSTEN_DISPLAY_NAME,
+ [RINKEBY]: RINKEBY_DISPLAY_NAME,
+ [KOVAN]: KOVAN_DISPLAY_NAME,
+ [MAINNET]: MAINNET_DISPLAY_NAME,
+ [ROPSTEN_CODE]: ROPSTEN_DISPLAY_NAME,
+ [RINKEYBY_CODE]: RINKEBY_DISPLAY_NAME,
+ [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/first-time-state.js b/app/scripts/first-time-state.js
index 144534f43..c49d89288 100644
--- a/app/scripts/first-time-state.js
+++ b/app/scripts/first-time-state.js
@@ -1,6 +1,7 @@
// 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
@@ -15,7 +16,7 @@ const initialState = {
config: {},
NetworkController: {
provider: {
- type: (METAMASK_DEBUG || env === 'test') ? 'rinkeby' : 'mainnet',
+ type: (METAMASK_DEBUG || env === 'test') ? DEFAULT_NETWORK : MAINNET,
},
},
}
diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js
index 63d27c40e..c10ff2f4e 100644
--- a/app/scripts/lib/config-manager.js
+++ b/app/scripts/lib/config-manager.js
@@ -1,12 +1,11 @@
const ethUtil = require('ethereumjs-util')
const normalize = require('eth-sig-util').normalize
-const MetamaskConfig = require('../config.js')
-
-
-const MAINNET_RPC = MetamaskConfig.network.mainnet
-const ROPSTEN_RPC = MetamaskConfig.network.ropsten
-const KOVAN_RPC = MetamaskConfig.network.kovan
-const RINKEBY_RPC = MetamaskConfig.network.rinkeby
+const {
+ MAINNET_RPC_URL,
+ ROPSTEN_RPC_URL,
+ KOVAN_RPC_URL,
+ RINKEBY_RPC_URL,
+} = require('../controllers/network/enums')
/* The config-manager is a convenience object
* wrapping a pojo-migrator.
@@ -174,19 +173,19 @@ ConfigManager.prototype.getCurrentRpcAddress = function () {
switch (provider.type) {
case 'mainnet':
- return MAINNET_RPC
+ return MAINNET_RPC_URL
case 'ropsten':
- return ROPSTEN_RPC
+ return ROPSTEN_RPC_URL
case 'kovan':
- return KOVAN_RPC
+ return KOVAN_RPC_URL
case 'rinkeby':
- return RINKEBY_RPC
+ return RINKEBY_RPC_URL
default:
- return provider && provider.rpcTarget ? provider.rpcTarget : RINKEBY_RPC
+ return provider && provider.rpcTarget ? provider.rpcTarget : RINKEBY_RPC_URL
}
}
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 782bc50ac..edde38819 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -263,6 +263,7 @@ module.exports = class MetamaskController extends EventEmitter {
/**
* Constructor helper: initialize a public config store.
+ * This store is used to make some config info available to Dapps synchronously.
*/
initPublicConfigStore () {
// get init state
@@ -314,9 +315,11 @@ module.exports = class MetamaskController extends EventEmitter {
}
/**
- * Returns an api-object which is consumed by the UI
+ * Returns an Object containing API Callback Functions.
+ * These functions are the interface for the UI.
+ * The API object can be transmitted over a stream with dnode.
*
- * @returns {Object}
+ * @returns {Object} Object containing API functions.
*/
getApi () {
const keyringController = this.keyringController
@@ -407,16 +410,18 @@ module.exports = class MetamaskController extends EventEmitter {
//=============================================================================
/**
- * Creates a new Vault(?) and create a new keychain(?)
+ * Creates a new Vault and create a new keychain.
*
- * A vault is ...
+ * A vault, or KeyringController, is a controller that contains
+ * many different account strategies, currently called Keyrings.
+ * Creating it new means wiping all previous keyrings.
*
- * A keychain is ...
+ * A keychain, or keyring, controls many accounts with a single backup and signing strategy.
+ * For example, a mnemonic phrase can generate many accounts, and is a keyring.
*
+ * @param {string} password
*
- * @param {} password
- *
- * @returns {} vault
+ * @returns {Object} vault
*/
async createNewVaultAndKeychain (password) {
const release = await this.createVaultMutex.acquire()
@@ -442,7 +447,7 @@ module.exports = class MetamaskController extends EventEmitter {
}
/**
- * Create a new Vault and restore an existent keychain
+ * Create a new Vault and restore an existent keyring.
* @param {} password
* @param {} seed
*/
@@ -460,10 +465,16 @@ module.exports = class MetamaskController extends EventEmitter {
}
/**
+ * @type Identity
+ * @property {string} name - The account nickname.
+ * @property {string} address - The account's ethereum address, in lower case.
+ * @property {boolean} mayBeFauceting - Whether this account is currently
+ * receiving funds from our automatic Ropsten faucet.
+ */
+
+ /**
* Retrieves the first Identiy from the passed Vault and selects the related address
*
- * An Identity is ...
- *
* @param {} vault
*/
selectFirstIdentity (vault) {
@@ -472,12 +483,12 @@ module.exports = class MetamaskController extends EventEmitter {
this.preferencesController.setSelectedAddress(address)
}
- // ?
- // Opinionated Keyring Management
+ //
+ // Account Management
//
/**
- * Adds a new account to ...
+ * Adds a new account to the default (first) HD seed phrase Keyring.
*
* @returns {} keyState
*/
@@ -507,6 +518,8 @@ module.exports = class MetamaskController extends EventEmitter {
*
* Used when creating a first vault, to allow confirmation.
* Also used when revealing the seed words in the confirmation view.
+ *
+ * @param {Function} cb - A callback called on completion.
*/
placeSeedWords (cb) {
@@ -526,6 +539,8 @@ module.exports = class MetamaskController extends EventEmitter {
* Validity: seed phrase restores the accounts belonging to the current vault.
*
* Called when the first account is created and on unlocking the vault.
+ *
+ * @returns {Promise<string>} Seed phrase to be confirmed by the user.
*/
async verifySeedPhrase () {
@@ -556,6 +571,7 @@ module.exports = class MetamaskController extends EventEmitter {
*
* The seed phrase remains available in the background process.
*
+ * @param {function} cb Callback function called with the current address.
*/
clearSeedWordCache (cb) {
this.configManager.setSeedWords(null)
@@ -563,9 +579,13 @@ module.exports = class MetamaskController extends EventEmitter {
}
/**
- * ?
+ * Clears the transaction history, to allow users to force-reset their nonces.
+ * Mostly used in development environments, when networks are restarted with
+ * the same network ID.
+ *
+ * @returns Promise<string> The current selected address.
*/
- async resetAccount (cb) {
+ async resetAccount () {
const selectedAddress = this.preferencesController.getSelectedAddress()
this.txController.wipeTransactions(selectedAddress)
@@ -577,11 +597,13 @@ module.exports = class MetamaskController extends EventEmitter {
}
/**
- * Imports an account ... ?
+ * Imports an account with the specified import strategy.
+ * These are defined in app/scripts/account-import-strategies
+ * Each strategy represents a different way of serializing an Ethereum key pair.
*
- * @param {} strategy
- * @param {} args
- * @param {} cb
+ * @param {string} strategy - A unique identifier for an account import strategy.
+ * @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)
@@ -595,13 +617,42 @@ module.exports = class MetamaskController extends EventEmitter {
}
// ---------------------------------------------------------------------------
- // Identity Management (sign)
+ // Identity Management (signature operations)
+
+ // eth_sign methods:
+
+ /**
+ * Called when a Dapp uses the eth_sign method, to request user approval.
+ * eth_sign is a pure signature of arbitrary data. It is on a deprecation
+ * path, since this data can be a transaction, or can leak private key
+ * information.
+ *
+ * @param {Object} msgParams - The params passed to eth_sign.
+ * @param {Function} cb = The callback function called with the signature.
+ */
+ newUnsignedMessage (msgParams, cb) {
+ const msgId = this.messageManager.addUnapprovedMessage(msgParams)
+ this.sendUpdate()
+ this.opts.showUnconfirmedMessage()
+ this.messageManager.once(`${msgId}:finished`, (data) => {
+ switch (data.status) {
+ case 'signed':
+ return cb(null, data.rawSig)
+ case 'rejected':
+ return cb(new Error('MetaMask Message Signature: User denied message signature.'))
+ default:
+ return cb(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
+ }
+ })
+ }
/**
- * @param {} msgParams
- * @param {} cb
+ * Signifies user intent to complete an eth_sign method.
+ *
+ * @param {Object} msgParams The params passed to eth_call.
+ * @returns {Promise<Object>} Full state update.
*/
- signMessage (msgParams, cb) {
+ signMessage (msgParams) {
log.info('MetaMaskController - signMessage')
const msgId = msgParams.metamaskId
@@ -620,14 +671,37 @@ module.exports = class MetamaskController extends EventEmitter {
})
}
- // Prefixed Style Message Signing Methods:
+ /**
+ * Used to cancel a message submitted via eth_sign.
+ *
+ * @param {string} msgId - The id of the message to cancel.
+ */
+ cancelMessage (msgId, cb) {
+ const messageManager = this.messageManager
+ messageManager.rejectMsg(msgId)
+ if (cb && typeof cb === 'function') {
+ cb(null, this.getState())
+ }
+ }
+
+ // personal_sign methods:
/**
+ * Called when a dapp uses the personal_sign method.
+ * This is identical to the Geth eth_sign method, and may eventually replace
+ * eth_sign.
*
- * @param {} msgParams
- * @param {} cb
+ * We currently define our eth_sign and personal_sign mostly for legacy Dapps.
+ *
+ * @param {Object} msgParams - The params of the message to sign & return to the Dapp.
+ * @param {Function} cb - The callback function called with the signature.
+ * Passed back to the requesting Dapp.
*/
- approvePersonalMessage (msgParams, cb) {
+ newUnsignedPersonalMessage (msgParams, cb) {
+ if (!msgParams.from) {
+ return cb(new Error('MetaMask Message Signature: from field is required.'))
+ }
+
const msgId = this.personalMessageManager.addUnapprovedMessage(msgParams)
this.sendUpdate()
this.opts.showUnconfirmedMessage()
@@ -636,7 +710,7 @@ module.exports = class MetamaskController extends EventEmitter {
case 'signed':
return cb(null, data.rawSig)
case 'rejected':
- return cb(new Error('MetaMask Message Signature: User denied transaction signature.'))
+ return cb(new Error('MetaMask Message Signature: User denied message signature.'))
default:
return cb(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
}
@@ -644,7 +718,11 @@ module.exports = class MetamaskController extends EventEmitter {
}
/**
- * @param {} msgParams
+ * Signifies a user's approval to sign a personal_sign message in queue.
+ * Triggers signing, and the callback function from newUnsignedPersonalMessage.
+ *
+ * @param {Object} msgParams - The params of the message to sign & return to the Dapp.
+ * @returns {Promise<Object>} - A full state update.
*/
signPersonalMessage (msgParams) {
log.info('MetaMaskController - signPersonalMessage')
@@ -665,7 +743,54 @@ module.exports = class MetamaskController extends EventEmitter {
}
/**
- * @param {} msgParams
+ * Used to cancel a personal_sign type message.
+ * @param {string} msgId - The ID of the message to cancel.
+ * @param {Function} cb - The callback function called with a full state update.
+ */
+ cancelPersonalMessage (msgId, cb) {
+ const messageManager = this.personalMessageManager
+ messageManager.rejectMsg(msgId)
+ if (cb && typeof cb === 'function') {
+ cb(null, this.getState())
+ }
+ }
+
+ // eth_signTypedData methods
+
+ /**
+ * Called when a dapp uses the eth_signTypedData method, per EIP 712.
+ *
+ * @param {Object} msgParams - The params passed to eth_signTypedData.
+ * @param {Function} cb - The callback function, called with the signature.
+ */
+ newUnsignedTypedMessage (msgParams, cb) {
+ let msgId
+ try {
+ msgId = this.typedMessageManager.addUnapprovedMessage(msgParams)
+ this.sendUpdate()
+ this.opts.showUnconfirmedMessage()
+ } catch (e) {
+ return cb(e)
+ }
+
+ this.typedMessageManager.once(`${msgId}:finished`, (data) => {
+ switch (data.status) {
+ case 'signed':
+ return cb(null, data.rawSig)
+ case 'rejected':
+ return cb(new Error('MetaMask Message Signature: User denied message signature.'))
+ default:
+ return cb(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
+ }
+ })
+ }
+
+ /**
+ * The method for a user approving a call to eth_signTypedData, per EIP 712.
+ * Triggers the callback in newUnsignedTypedMessage.
+ *
+ * @param {Object} msgParams - The params passed to eth_signTypedData.
+ * @returns {Object} Full state update.
*/
signTypedMessage (msgParams) {
log.info('MetaMaskController - signTypedMessage')
@@ -685,12 +810,30 @@ module.exports = class MetamaskController extends EventEmitter {
})
}
+ /**
+ * Used to cancel a eth_signTypedData type message.
+ * @param {string} msgId - The ID of the message to cancel.
+ * @param {Function} cb - The callback function called with a full state update.
+ */
+ cancelTypedMessage (msgId, cb) {
+ const messageManager = this.typedMessageManager
+ messageManager.rejectMsg(msgId)
+ if (cb && typeof cb === 'function') {
+ cb(null, this.getState())
+ }
+ }
+
// ---------------------------------------------------------------------------
- // Account Restauration
+ // MetaMask Version 3 Migration Account Restauration Methods
/**
- * ?
+ * A legacy method (probably dead code) that was used when we swapped out our
+ * key management library that we depended on.
*
+ * Described in:
+ * https://medium.com/metamask/metamask-3-migration-guide-914b79533cdd
+ *
+ * @deprecated
* @param {} migratorOutput
*/
restoreOldVaultAccounts (migratorOutput) {
@@ -700,8 +843,26 @@ module.exports = class MetamaskController extends EventEmitter {
}
/**
- * ?
+ * A legacy method used to record user confirmation that they understand
+ * that some of their accounts have been recovered but should be backed up.
+ *
+ * @deprecated
+ * @param {Function} cb - A callback function called with a full state update.
+ */
+ markAccountsFound (cb) {
+ this.configManager.setLostAccounts([])
+ this.sendUpdate()
+ cb(null, this.getState())
+ }
+
+ /**
+ * A legacy method (probably dead code) that was used when we swapped out our
+ * key management library that we depended on.
*
+ * Described in:
+ * https://medium.com/metamask/metamask-3-migration-guide-914b79533cdd
+ *
+ * @deprecated
* @param {} migratorOutput
*/
restoreOldLostAccounts (migratorOutput) {
@@ -714,12 +875,23 @@ module.exports = class MetamaskController extends EventEmitter {
}
/**
- * Import (lost) Accounts
+ * An account object
+ * @typedef Account
+ * @property string privateKey - The private key of the account.
+ */
+
+ /**
+ * Probably no longer needed, related to the Version 3 migration.
+ * Imports a hash of accounts to private keys into the vault.
*
- * @param {Object} {lostAccounts} @Array accounts <{ address, privateKey }>
+ * Described in:
+ * https://medium.com/metamask/metamask-3-migration-guide-914b79533cdd
*
* Uses the array's private keys to create a new Simple Key Pair keychain
* and add it to the keyring controller.
+ * @deprecated
+ * @param {Account[]} lostAccounts -
+ * @returns {Keyring[]} An array of the restored keyrings.
*/
importLostAccounts ({ lostAccounts }) {
const privKeys = lostAccounts.map(acct => acct.privateKey)
@@ -733,113 +905,37 @@ module.exports = class MetamaskController extends EventEmitter {
// END (VAULT / KEYRING RELATED METHODS)
//=============================================================================
-//
-
-//=============================================================================
-// MESSAGES
-//=============================================================================
-
+ /**
+ * Allows a user to try to speed up a transaction by retrying it
+ * with higher gas.
+ *
+ * @param {string} txId - The ID of the transaction to speed up.
+ * @param {Function} cb - The callback function called with a full state update.
+ */
async retryTransaction (txId, cb) {
await this.txController.retryTransaction(txId)
const state = await this.getState()
return state
}
+//=============================================================================
+// PASSWORD MANAGEMENT
+//=============================================================================
- newUnsignedMessage (msgParams, cb) {
- const msgId = this.messageManager.addUnapprovedMessage(msgParams)
- this.sendUpdate()
- this.opts.showUnconfirmedMessage()
- this.messageManager.once(`${msgId}:finished`, (data) => {
- switch (data.status) {
- case 'signed':
- return cb(null, data.rawSig)
- case 'rejected':
- return cb(new Error('MetaMask Message Signature: User denied message signature.'))
- default:
- return cb(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
- }
- })
- }
-
- newUnsignedPersonalMessage (msgParams, cb) {
- if (!msgParams.from) {
- return cb(new Error('MetaMask Message Signature: from field is required.'))
- }
-
- const msgId = this.personalMessageManager.addUnapprovedMessage(msgParams)
- this.sendUpdate()
- this.opts.showUnconfirmedMessage()
- this.personalMessageManager.once(`${msgId}:finished`, (data) => {
- switch (data.status) {
- case 'signed':
- return cb(null, data.rawSig)
- case 'rejected':
- return cb(new Error('MetaMask Message Signature: User denied message signature.'))
- default:
- return cb(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
- }
- })
- }
-
- newUnsignedTypedMessage (msgParams, cb) {
- let msgId
- try {
- msgId = this.typedMessageManager.addUnapprovedMessage(msgParams)
- this.sendUpdate()
- this.opts.showUnconfirmedMessage()
- } catch (e) {
- return cb(e)
- }
-
- this.typedMessageManager.once(`${msgId}:finished`, (data) => {
- switch (data.status) {
- case 'signed':
- return cb(null, data.rawSig)
- case 'rejected':
- return cb(new Error('MetaMask Message Signature: User denied message signature.'))
- default:
- return cb(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
- }
- })
- }
-
- cancelMessage (msgId, cb) {
- const messageManager = this.messageManager
- messageManager.rejectMsg(msgId)
- if (cb && typeof cb === 'function') {
- cb(null, this.getState())
- }
- }
-
- cancelPersonalMessage (msgId, cb) {
- const messageManager = this.personalMessageManager
- messageManager.rejectMsg(msgId)
- if (cb && typeof cb === 'function') {
- cb(null, this.getState())
- }
- }
-
- cancelTypedMessage (msgId, cb) {
- const messageManager = this.typedMessageManager
- messageManager.rejectMsg(msgId)
- if (cb && typeof cb === 'function') {
- cb(null, this.getState())
- }
- }
-
- markAccountsFound (cb) {
- this.configManager.setLostAccounts([])
- this.sendUpdate()
- cb(null, this.getState())
- }
-
+ /**
+ * Allows a user to begin the seed phrase recovery process.
+ * @param {Function} cb - A callback function called when complete.
+ */
markPasswordForgotten(cb) {
this.configManager.setPasswordForgotten(true)
this.sendUpdate()
cb()
}
+ /**
+ * Allows a user to end the seed phrase recovery process.
+ * @param {Function} cb - A callback function called when complete.
+ */
unMarkPasswordForgotten(cb) {
this.configManager.setPasswordForgotten(false)
this.sendUpdate()
@@ -850,6 +946,13 @@ module.exports = class MetamaskController extends EventEmitter {
// SETUP
//=============================================================================
+ /**
+ * Used to create a multiplexed stream for connecting to an untrusted context
+ * like a Dapp or other extension.
+ * @param {*} connectionStream - The Duplex stream to connect to.
+ * @param {string} originDomain - The domain requesting the stream, which
+ * may trigger a blacklist reload.
+ */
setupUntrustedCommunication (connectionStream, originDomain) {
// Check if new connection is blacklisted
if (this.blacklistController.checkForPhishing(originDomain)) {
@@ -865,6 +968,16 @@ module.exports = class MetamaskController extends EventEmitter {
this.setupPublicConfig(mux.createStream('publicConfig'))
}
+ /**
+ * Used to create a multiplexed stream for connecting to a trusted context,
+ * like our own user interfaces, which have the provider APIs, but also
+ * receive the exported API from this controller, which includes trusted
+ * functions, like the ability to approve transactions or sign messages.
+ *
+ * @param {*} connectionStream - The duplex stream to connect to.
+ * @param {string} originDomain - The domain requesting the connection,
+ * used in logging and error reporting.
+ */
setupTrustedCommunication (connectionStream, originDomain) {
// setup multiplexing
const mux = setupMultiplex(connectionStream)
@@ -873,12 +986,25 @@ module.exports = class MetamaskController extends EventEmitter {
this.setupProviderConnection(mux.createStream('provider'), originDomain)
}
+ /**
+ * Called when we detect a suspicious domain. Requests the browser redirects
+ * to our anti-phishing page.
+ *
+ * @private
+ * @param {*} connectionStream - The duplex stream to the per-page script,
+ * for sending the reload attempt to.
+ * @param {string} hostname - The URL that triggered the suspicion.
+ */
sendPhishingWarning (connectionStream, hostname) {
const mux = setupMultiplex(connectionStream)
const phishingStream = mux.createStream('phishing')
phishingStream.write({ hostname })
}
+ /**
+ * A method for providing our API over a stream using Dnode.
+ * @param {*} outStream - The stream to provide our API over.
+ */
setupControllerConnection (outStream) {
const api = this.getApi()
const dnode = Dnode(api)
@@ -897,6 +1023,11 @@ module.exports = class MetamaskController extends EventEmitter {
})
}
+ /**
+ * A method for serving our ethereum provider over a given stream.
+ * @param {*} outStream - The stream to provide over.
+ * @param {string} origin - The URI of the requesting resource.
+ */
setupProviderConnection (outStream, origin) {
// setup json rpc engine stack
const engine = new RpcEngine()
@@ -926,6 +1057,16 @@ module.exports = class MetamaskController extends EventEmitter {
)
}
+ /**
+ * A method for providing our public config info over a stream.
+ * This includes info we like to be synchronous if possible, like
+ * the current selected account, and network ID.
+ *
+ * Since synchronous methods have been deprecated in web3,
+ * this is a good candidate for deprecation.
+ *
+ * @param {*} outStream - The stream to provide public config over.
+ */
setupPublicConfig (outStream) {
pump(
asStream(this.publicConfigStore),
@@ -936,10 +1077,21 @@ module.exports = class MetamaskController extends EventEmitter {
)
}
+ /**
+ * A method for emitting the full MetaMask state to all registered listeners.
+ * @private
+ */
privateSendUpdate () {
this.emit('update', this.getState())
}
+ /**
+ * A method for estimating a good gas price at recent prices.
+ * Returns the lowest price that would have been included in
+ * 50% of recent blocks.
+ *
+ * @returns {string} A hex representation of the suggested wei gas price.
+ */
getGasPrice () {
const { recentBlocksController } = this
const { recentBlocks } = recentBlocksController.store.getState()
@@ -973,6 +1125,11 @@ module.exports = class MetamaskController extends EventEmitter {
// Log blocks
+ /**
+ * A method for setting the user's preferred display currency.
+ * @param {string} currencyCode - The code of the preferred currency.
+ * @param {Function} cb - A callback function returning currency info.
+ */
setCurrentCurrency (currencyCode, cb) {
try {
this.currencyController.setCurrentCurrency(currencyCode)
@@ -988,6 +1145,13 @@ module.exports = class MetamaskController extends EventEmitter {
}
}
+ /**
+ * A method for forwarding the user to the easiest way to obtain ether,
+ * or the network "gas" currency, for the current selected network.
+ *
+ * @param {string} address - The address to fund.
+ * @param {string} amount - The amount of ether desired, as a base 10 string.
+ */
buyEth (address, amount) {
if (!amount) amount = '5'
const network = this.networkController.getNetworkState()
@@ -995,18 +1159,33 @@ module.exports = class MetamaskController extends EventEmitter {
if (url) this.platform.openWindow({ url })
}
+ /**
+ * A method for triggering a shapeshift currency transfer.
+ * @param {string} depositAddress - The address to deposit to.
+ * @property {string} depositType - An abbreviation of the type of crypto currency to be deposited.
+ */
createShapeShiftTx (depositAddress, depositType) {
this.shapeshiftController.createShapeShiftTx(depositAddress, depositType)
}
// network
- async setCustomRpc (rpcTarget, rpcList) {
+ /**
+ * A method for selecting a custom URL for an ethereum RPC provider.
+ * @param {string} rpcTarget - A URL for a valid Ethereum RPC API.
+ * @returns {Promise<String>} - The RPC Target URL confirmed.
+ */
+ async setCustomRpc (rpcTarget) {
this.networkController.setRpcTarget(rpcTarget)
await this.preferencesController.updateFrequentRpcList(rpcTarget)
return rpcTarget
}
+ /**
+ * Sets whether or not to use the blockie identicon format.
+ * @param {boolean} val - True for bockie, false for jazzicon.
+ * @param {Function} cb - A callback function called when complete.
+ */
setUseBlockie (val, cb) {
try {
this.preferencesController.setUseBlockie(val)
@@ -1016,6 +1195,11 @@ module.exports = class MetamaskController extends EventEmitter {
}
}
+ /**
+ * A method for setting a user's current locale, affecting the language rendered.
+ * @param {string} key - Locale identifier.
+ * @param {Function} cb - A callback function called when complete.
+ */
setCurrentLocale (key, cb) {
try {
this.preferencesController.setCurrentLocale(key)
@@ -1025,6 +1209,11 @@ module.exports = class MetamaskController extends EventEmitter {
}
}
+ /**
+ * A method for initializing storage the first time.
+ * @param {Object} initState - The default state to initialize with.
+ * @private
+ */
recordFirstTimeInfo (initState) {
if (!('firstTimeInfo' in initState)) {
initState.firstTimeInfo = {
@@ -1034,11 +1223,21 @@ module.exports = class MetamaskController extends EventEmitter {
}
}
+ /**
+ * A method for recording whether the MetaMask user interface is open or not.
+ * @private
+ * @param {boolean} open
+ */
set isClientOpen (open) {
this._isClientOpen = open
this.isClientOpenAndUnlocked = this.getState().isUnlocked && open
}
+ /**
+ * A method for activating the retrieval of price data, which should only be fetched when the UI is visible.
+ * @private
+ * @param {boolean} active - True if price data should be getting fetched.
+ */
set isClientOpenAndUnlocked (active) {
this.tokenRatesController.isActive = active
}
diff --git a/old-ui/app/app.js b/old-ui/app/app.js
index fc0d634aa..3aa845b3a 100644
--- a/old-ui/app/app.js
+++ b/old-ui/app/app.js
@@ -35,7 +35,7 @@ const HDCreateVaultComplete = require('./keychains/hd/create-vault-complete')
const HDRestoreVaultScreen = require('./keychains/hd/restore-vault')
const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation')
const AccountDropdowns = require('./components/account-dropdowns').AccountDropdowns
-const { BETA_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums
+const { BETA_UI_NETWORK_TYPE } = require('../../app/scripts/controllers/network/enums')
module.exports = connect(mapStateToProps)(App)
diff --git a/old-ui/app/components/buy-button-subview.js b/old-ui/app/components/buy-button-subview.js
index 56d173839..8bb73ae3e 100644
--- a/old-ui/app/components/buy-button-subview.js
+++ b/old-ui/app/components/buy-button-subview.js
@@ -8,7 +8,7 @@ const ShapeshiftForm = require('./shapeshift-form')
const Loading = require('./loading')
const AccountPanel = require('./account-panel')
const RadioList = require('./custom-radio-list')
-const networkNames = require('../../../app/scripts/config.js').networkNames
+const { getNetworkDisplayName } = require('../../../app/scripts/controllers/network/util')
module.exports = connect(mapStateToProps)(BuyButtonSubview)
@@ -142,7 +142,7 @@ BuyButtonSubview.prototype.primarySubview = function () {
case '3':
case '4':
case '42':
- const networkName = networkNames[network]
+ const networkName = getNetworkDisplayName(network)
const label = `${networkName} Test Faucet`
return (
h('div.flex-column', {
diff --git a/test/unit/network-contoller-test.js b/test/unit/network-contoller-test.js
index dd0b52365..2b905718b 100644
--- a/test/unit/network-contoller-test.js
+++ b/test/unit/network-contoller-test.js
@@ -1,6 +1,10 @@
const assert = require('assert')
const nock = require('nock')
const NetworkController = require('../../app/scripts/controllers/network')
+const {
+ getNetworkDisplayName,
+ getNetworkEndpoints,
+} = require('../../app/scripts/controllers/network/util')
const { createTestProviderTools } = require('../stub/provider')
const providerResultStub = {}
@@ -79,4 +83,40 @@ describe('# Network Controller', function () {
})
})
})
-}) \ No newline at end of file
+})
+
+describe('# Network utils', () => {
+ it('getNetworkDisplayName should return the correct network name', () => {
+ const tests = [
+ {
+ input: 3,
+ expected: 'Ropsten',
+ }, {
+ input: 4,
+ expected: 'Rinkeby',
+ }, {
+ input: 42,
+ expected: 'Kovan',
+ }, {
+ input: 'ropsten',
+ expected: 'Ropsten',
+ }, {
+ input: 'rinkeby',
+ expected: 'Rinkeby',
+ }, {
+ input: 'kovan',
+ expected: 'Kovan',
+ }, {
+ input: 'mainnet',
+ expected: 'Main Ethereum Network',
+ },
+ ]
+
+ tests.forEach(({ input, expected }) => assert.equal(getNetworkDisplayName(input), expected))
+ })
+
+ it('getNetworkEndpoints should return the correct endpoints', () => {
+ assert.equal(getNetworkEndpoints('networkBeta').ropsten, 'https://ropsten.infura.io/metamask2')
+ assert.equal(getNetworkEndpoints('network').rinkeby, 'https://rinkeby.infura.io/metamask')
+ })
+})
diff --git a/ui/app/components/buy-button-subview.js b/ui/app/components/buy-button-subview.js
index 9ac565cf4..fda7c3e17 100644
--- a/ui/app/components/buy-button-subview.js
+++ b/ui/app/components/buy-button-subview.js
@@ -9,7 +9,7 @@ const ShapeshiftForm = require('./shapeshift-form')
const Loading = require('./loading')
const AccountPanel = require('./account-panel')
const RadioList = require('./custom-radio-list')
-const networkNames = require('../../../app/scripts/config.js').networkNames
+const { getNetworkDisplayName } = require('../../../app/scripts/controllers/network/util')
BuyButtonSubview.contextTypes = {
t: PropTypes.func,
@@ -148,7 +148,7 @@ BuyButtonSubview.prototype.primarySubview = function () {
case '3':
case '4':
case '42':
- const networkName = networkNames[network]
+ const networkName = getNetworkDisplayName(network)
const label = `${networkName} ${this.context.t('testFaucet')}`
return (
h('div.flex-column', {
diff --git a/ui/app/components/dropdowns/network-dropdown.js b/ui/app/components/dropdowns/network-dropdown.js
index 9e47f38ef..dcd6b4370 100644
--- a/ui/app/components/dropdowns/network-dropdown.js
+++ b/ui/app/components/dropdowns/network-dropdown.js
@@ -3,12 +3,14 @@ const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
+const { withRouter } = require('react-router-dom')
+const { compose } = require('recompose')
const actions = require('../../actions')
const Dropdown = require('./components/dropdown').Dropdown
const DropdownMenuItem = require('./components/dropdown').DropdownMenuItem
const NetworkDropdownIcon = require('./components/network-dropdown-icon')
const R = require('ramda')
-
+const { SETTINGS_ROUTE } = require('../../routes')
// classes from nodes of the toggle element.
const notToggleElementClassnames = [
@@ -41,9 +43,6 @@ function mapDispatchToProps (dispatch) {
setRpcTarget: (target) => {
dispatch(actions.setRpcTarget(target))
},
- showConfigPage: () => {
- dispatch(actions.showConfigPage())
- },
showNetworkDropdown: () => dispatch(actions.showNetworkDropdown()),
hideNetworkDropdown: () => dispatch(actions.hideNetworkDropdown()),
}
@@ -59,7 +58,10 @@ NetworkDropdown.contextTypes = {
t: PropTypes.func,
}
-module.exports = connect(mapStateToProps, mapDispatchToProps)(NetworkDropdown)
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps)
+)(NetworkDropdown)
// TODO: specify default props and proptypes
@@ -227,7 +229,7 @@ NetworkDropdown.prototype.render = function () {
DropdownMenuItem,
{
closeMenu: () => this.props.hideNetworkDropdown(),
- onClick: () => this.props.showConfigPage(),
+ onClick: () => this.props.history.push(SETTINGS_ROUTE),
style: dropdownMenuItemStyle,
},
[
diff --git a/ui/app/components/modals/buy-options-modal.js b/ui/app/components/modals/buy-options-modal.js
index d871e7516..c70510b5f 100644
--- a/ui/app/components/modals/buy-options-modal.js
+++ b/ui/app/components/modals/buy-options-modal.js
@@ -4,7 +4,7 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../actions')
-const networkNames = require('../../../../app/scripts/config.js').networkNames
+const { getNetworkDisplayName } = require('../../../../app/scripts/controllers/network/util')
function mapStateToProps (state) {
return {
@@ -52,7 +52,7 @@ BuyOptions.prototype.renderModalContentOption = function (title, header, onClick
BuyOptions.prototype.render = function () {
const { network, toCoinbase, address, toFaucet } = this.props
const isTestNetwork = ['3', '4', '42'].find(n => n === network)
- const networkName = networkNames[network]
+ const networkName = getNetworkDisplayName(network)
return h('div', {}, [
h('div.buy-modal-content.transfers-subview', {
diff --git a/ui/app/components/modals/deposit-ether-modal.js b/ui/app/components/modals/deposit-ether-modal.js
index 0dc611f50..ad5f9b695 100644
--- a/ui/app/components/modals/deposit-ether-modal.js
+++ b/ui/app/components/modals/deposit-ether-modal.js
@@ -4,7 +4,7 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../actions')
-const networkNames = require('../../../../app/scripts/config.js').networkNames
+const { getNetworkDisplayName } = require('../../../../app/scripts/controllers/network/util')
const ShapeshiftForm = require('../shapeshift-form')
let DIRECT_DEPOSIT_ROW_TITLE
@@ -122,7 +122,7 @@ DepositEtherModal.prototype.render = function () {
const { buyingWithShapeshift } = this.state
const isTestNetwork = ['3', '4', '42'].find(n => n === network)
- const networkName = networkNames[network]
+ const networkName = getNetworkDisplayName(network)
return h('div.page-container.page-container--full-width.page-container--full-height', {}, [
diff --git a/ui/app/components/pages/settings/settings.js b/ui/app/components/pages/settings/settings.js
index 05a7379fb..bdefe56f8 100644
--- a/ui/app/components/pages/settings/settings.js
+++ b/ui/app/components/pages/settings/settings.js
@@ -12,7 +12,7 @@ const SimpleDropdown = require('../../dropdowns/simple-dropdown')
const ToggleButton = require('react-toggle-button')
const { REVEAL_SEED_ROUTE } = require('../../../routes')
const locales = require('../../../../../app/_locales/index.json')
-const { OLD_UI_NETWORK_TYPE } = require('../../../../../app/scripts/config').enums
+const { OLD_UI_NETWORK_TYPE } = require('../../../../../app/scripts/controllers/network/enums')
const getInfuraCurrencyOptions = () => {
const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => {
diff --git a/ui/app/components/pages/unlock.js b/ui/app/components/pages/unlock.js
index 567b72518..30144b978 100644
--- a/ui/app/components/pages/unlock.js
+++ b/ui/app/components/pages/unlock.js
@@ -16,7 +16,7 @@ const { getEnvironmentType } = require('../../../../app/scripts/lib/util')
const getCaretCoordinates = require('textarea-caret')
const EventEmitter = require('events').EventEmitter
const Mascot = require('../mascot')
-const { OLD_UI_NETWORK_TYPE } = require('../../../../app/scripts/config').enums
+const { OLD_UI_NETWORK_TYPE } = require('../../../../app/scripts/controllers/network/enums')
const { DEFAULT_ROUTE, RESTORE_VAULT_ROUTE } = require('../../routes')
class UnlockScreen extends Component {
diff --git a/ui/app/first-time/init-menu.js b/ui/app/first-time/init-menu.js
index 3df040922..6cb548bb9 100644
--- a/ui/app/first-time/init-menu.js
+++ b/ui/app/first-time/init-menu.js
@@ -10,7 +10,7 @@ const getCaretCoordinates = require('textarea-caret')
const { RESTORE_VAULT_ROUTE, DEFAULT_ROUTE } = require('../routes')
const { getEnvironmentType } = require('../../../app/scripts/lib/util')
const { ENVIRONMENT_TYPE_POPUP } = require('../../../app/scripts/lib/enums')
-const { OLD_UI_NETWORK_TYPE } = require('../../../app/scripts/config').enums
+const { OLD_UI_NETWORK_TYPE } = require('../../../app/scripts/controllers/network/enums')
class InitializeMenuScreen extends Component {
constructor (props) {
diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js
index 5f965fbe0..bb35cf990 100644
--- a/ui/app/reducers/metamask.js
+++ b/ui/app/reducers/metamask.js
@@ -3,7 +3,7 @@ const actions = require('../actions')
const MetamascaraPlatform = require('../../../app/scripts/platforms/window')
const { getEnvironmentType } = require('../../../app/scripts/lib/util')
const { ENVIRONMENT_TYPE_POPUP } = require('../../../app/scripts/lib/enums')
-const { OLD_UI_NETWORK_TYPE } = require('../../../app/scripts/config').enums
+const { OLD_UI_NETWORK_TYPE } = require('../../../app/scripts/controllers/network/enums')
module.exports = reduceMetamask
diff --git a/ui/app/select-app.js b/ui/app/select-app.js
index d1565e2fb..808f9ba56 100644
--- a/ui/app/select-app.js
+++ b/ui/app/select-app.js
@@ -7,7 +7,7 @@ const App = require('./app')
const OldApp = require('../../old-ui/app/app')
const { autoAddToBetaUI } = require('./selectors')
const { setFeatureFlag, setNetworkEndpoints } = require('./actions')
-const { BETA_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums
+const { BETA_UI_NETWORK_TYPE } = require('../../app/scripts/controllers/network/enums')
const I18nProvider = require('./i18n-provider')
function mapStateToProps (state) {
diff --git a/ui/app/unlock.js b/ui/app/unlock.js
index 1325656af..099e5f9c6 100644
--- a/ui/app/unlock.js
+++ b/ui/app/unlock.js
@@ -6,7 +6,7 @@ const connect = require('react-redux').connect
const actions = require('./actions')
const getCaretCoordinates = require('textarea-caret')
const EventEmitter = require('events').EventEmitter
-const { OLD_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums
+const { OLD_UI_NETWORK_TYPE } = require('../../app/scripts/controllers/network/enums')
const { getEnvironmentType } = require('../../app/scripts/lib/util')
const { ENVIRONMENT_TYPE_POPUP } = require('../../app/scripts/lib/enums')
diff --git a/ui/index.js b/ui/index.js
index 7b6faac76..075faf66d 100644
--- a/ui/index.js
+++ b/ui/index.js
@@ -5,7 +5,11 @@ const actions = require('./app/actions')
const configureStore = require('./app/store')
const txHelper = require('./lib/tx-helper')
const { fetchLocale } = require('./i18n-helper')
-const { OLD_UI_NETWORK_TYPE, BETA_UI_NETWORK_TYPE } = require('../app/scripts/config').enums
+const {
+ OLD_UI_NETWORK_TYPE,
+ BETA_UI_NETWORK_TYPE,
+} = require('../app/scripts/controllers/network/enums')
+
const log = require('loglevel')
module.exports = launchMetamaskUi