aboutsummaryrefslogtreecommitdiffstats
path: root/app/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'app/scripts')
-rw-r--r--app/scripts/background.js25
-rw-r--r--app/scripts/contentscript.js19
-rw-r--r--app/scripts/controllers/blacklist.js25
-rw-r--r--app/scripts/controllers/currency.js80
-rw-r--r--app/scripts/controllers/network/createMetamaskMiddleware.js2
-rw-r--r--app/scripts/controllers/network/network.js76
-rw-r--r--app/scripts/controllers/preferences.js98
-rw-r--r--app/scripts/controllers/transactions/index.js39
-rw-r--r--app/scripts/controllers/transactions/tx-gas-utils.js35
-rw-r--r--app/scripts/controllers/transactions/tx-state-manager.js5
-rw-r--r--app/scripts/inpage.js2
-rw-r--r--app/scripts/lib/ens-ipfs/contracts/registrar.js (renamed from app/scripts/lib/contracts/registrar.js)0
-rw-r--r--app/scripts/lib/ens-ipfs/contracts/resolver.js (renamed from app/scripts/lib/contracts/resolver.js)0
-rw-r--r--app/scripts/lib/ens-ipfs/resolver.js54
-rw-r--r--app/scripts/lib/ens-ipfs/setup.js63
-rw-r--r--app/scripts/lib/ipfsContent.js46
-rw-r--r--app/scripts/lib/reportFailedTxToSentry.js6
-rw-r--r--app/scripts/lib/resolver.js71
-rw-r--r--app/scripts/lib/setupFetchDebugging.js36
-rw-r--r--app/scripts/lib/setupSentry.js (renamed from app/scripts/lib/setupRaven.js)75
-rw-r--r--app/scripts/metamask-controller.js54
-rw-r--r--app/scripts/ui.js14
22 files changed, 548 insertions, 277 deletions
diff --git a/app/scripts/background.js b/app/scripts/background.js
index 0343e134c..2a3c5b08b 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -2,6 +2,9 @@
* @file The entry point for the web extension singleton process.
*/
+// this needs to run before anything else
+require('./lib/setupFetchDebugging')()
+
const urlUtil = require('url')
const endOfStream = require('end-of-stream')
const pump = require('pump')
@@ -20,13 +23,13 @@ const createStreamSink = require('./lib/createStreamSink')
const NotificationManager = require('./lib/notification-manager.js')
const MetamaskController = require('./metamask-controller')
const rawFirstTimeState = require('./first-time-state')
-const setupRaven = require('./lib/setupRaven')
+const setupSentry = require('./lib/setupSentry')
const reportFailedTxToSentry = require('./lib/reportFailedTxToSentry')
const setupMetamaskMeshMetrics = require('./lib/setupMetamaskMeshMetrics')
const EdgeEncryptor = require('./edge-encryptor')
const getFirstPreferredLangCode = require('./lib/get-first-preferred-lang-code')
const getObjStructure = require('./lib/getObjStructure')
-const ipfsContent = require('./lib/ipfsContent.js')
+const setupEnsIpfsResolver = require('./lib/ens-ipfs/setup')
const {
ENVIRONMENT_TYPE_POPUP,
@@ -47,7 +50,7 @@ global.METAMASK_NOTIFIER = notificationManager
// setup sentry error reporting
const release = platform.getVersion()
-const raven = setupRaven({ release })
+const sentry = setupSentry({ 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
@@ -55,7 +58,6 @@ const isIE = !!document.documentMode
// Edge 20+
const isEdge = !isIE && !!window.StyleMedia
-let ipfsHandle
let popupIsOpen = false
let notificationIsOpen = false
const openMetamaskTabsIDs = {}
@@ -161,7 +163,6 @@ async function initialize () {
const initLangCode = await getFirstPreferredLangCode()
await setupController(initState, initLangCode)
log.debug('MetaMask initialization complete.')
- ipfsHandle = ipfsContent(initState.NetworkController.provider)
}
//
@@ -194,14 +195,14 @@ async function loadStateFromPersistence () {
// we were able to recover (though it might be old)
versionedData = diskStoreState
const vaultStructure = getObjStructure(versionedData)
- raven.captureMessage('MetaMask - Empty vault found - recovered from diskStore', {
+ sentry.captureMessage('MetaMask - Empty vault found - recovered from diskStore', {
// "extra" key is required by Sentry
extra: { vaultStructure },
})
} else {
// unable to recover, clear state
versionedData = migrator.generateInitialState(firstTimeState)
- raven.captureMessage('MetaMask - Empty vault found - unable to recover')
+ sentry.captureMessage('MetaMask - Empty vault found - unable to recover')
}
}
@@ -209,7 +210,7 @@ async function loadStateFromPersistence () {
migrator.on('error', (err) => {
// get vault structure without secrets
const vaultStructure = getObjStructure(versionedData)
- raven.captureException(err, {
+ sentry.captureException(err, {
// "extra" key is required by Sentry
extra: { vaultStructure },
})
@@ -266,17 +267,15 @@ function setupController (initState, initLangCode) {
})
global.metamaskController = controller
- controller.networkController.on('networkDidChange', () => {
- ipfsHandle && ipfsHandle.remove()
- ipfsHandle = ipfsContent(controller.networkController.providerStore.getState())
- })
+ const provider = controller.provider
+ setupEnsIpfsResolver({ provider })
// report failed transactions to Sentry
controller.txController.on(`tx:status-update`, (txId, status) => {
if (status !== 'failed') return
const txMeta = controller.txController.txStateManager.getTx(txId)
try {
- reportFailedTxToSentry({ raven, txMeta })
+ reportFailedTxToSentry({ sentry, txMeta })
} catch (e) {
console.error(e)
}
diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js
index d870741d6..33523eb46 100644
--- a/app/scripts/contentscript.js
+++ b/app/scripts/contentscript.js
@@ -135,17 +135,22 @@ function doctypeCheck () {
}
/**
- * Checks the current document extension
+ * Returns whether or not the extension (suffix) of the current document is prohibited
*
- * @returns {boolean} {@code true} if the current extension is not prohibited
+ * This checks {@code window.location.pathname} against a set of file extensions
+ * that should not have web3 injected into them. This check is indifferent of query parameters
+ * in the location.
+ *
+ * @returns {boolean} whether or not the extension of the current document is prohibited
*/
function suffixCheck () {
- var prohibitedTypes = ['xml', 'pdf']
- var currentUrl = window.location.href
- var currentRegex
+ const prohibitedTypes = [
+ /\.xml$/,
+ /\.pdf$/,
+ ]
+ const currentUrl = window.location.pathname
for (let i = 0; i < prohibitedTypes.length; i++) {
- currentRegex = new RegExp(`\\.${prohibitedTypes[i]}$`)
- if (currentRegex.test(currentUrl)) {
+ if (prohibitedTypes[i].test(currentUrl)) {
return false
}
}
diff --git a/app/scripts/controllers/blacklist.js b/app/scripts/controllers/blacklist.js
index 89c7cc888..e55b09d03 100644
--- a/app/scripts/controllers/blacklist.js
+++ b/app/scripts/controllers/blacklist.js
@@ -83,8 +83,25 @@ class BlacklistController {
*
*/
async updatePhishingList () {
- const response = await fetch('https://api.infura.io/v2/blacklist')
- const phishing = await response.json()
+ // make request
+ let response
+ try {
+ response = await fetch('https://api.infura.io/v2/blacklist')
+ } catch (err) {
+ log.error(new Error(`BlacklistController - failed to fetch blacklist:\n${err.stack}`))
+ return
+ }
+ // parse response
+ let rawResponse
+ let phishing
+ try {
+ const rawResponse = await response.text()
+ phishing = JSON.parse(rawResponse)
+ } catch (err) {
+ log.error(new Error(`BlacklistController - failed to parse blacklist:\n${rawResponse}`))
+ return
+ }
+ // update current blacklist
this.store.updateState({ phishing })
this._setupPhishingDetector(phishing)
return phishing
@@ -97,9 +114,9 @@ class BlacklistController {
*/
scheduleUpdates () {
if (this._phishingUpdateIntervalRef) return
- this.updatePhishingList().catch(log.warn)
+ this.updatePhishingList()
this._phishingUpdateIntervalRef = setInterval(() => {
- this.updatePhishingList().catch(log.warn)
+ this.updatePhishingList()
}, POLLING_INTERVAL)
}
diff --git a/app/scripts/controllers/currency.js b/app/scripts/controllers/currency.js
index d5bc5fe2b..fce65fd9c 100644
--- a/app/scripts/controllers/currency.js
+++ b/app/scripts/controllers/currency.js
@@ -21,6 +21,7 @@ class CurrencyController {
* since midnight of January 1, 1970
* @property {number} conversionInterval The id of the interval created by the scheduleConversionInterval method.
* Used to clear an existing interval on subsequent calls of that method.
+ * @property {string} nativeCurrency The ticker/symbol of the native chain currency
*
*/
constructor (opts = {}) {
@@ -28,6 +29,7 @@ class CurrencyController {
currentCurrency: 'usd',
conversionRate: 0,
conversionDate: 'N/A',
+ nativeCurrency: 'ETH',
}, opts.initState)
this.store = new ObservableStore(initState)
}
@@ -37,6 +39,29 @@ class CurrencyController {
//
/**
+ * A getter for the nativeCurrency property
+ *
+ * @returns {string} A 2-4 character shorthand that describes the specific currency
+ *
+ */
+ getNativeCurrency () {
+ return this.store.getState().nativeCurrency
+ }
+
+ /**
+ * A setter for the nativeCurrency property
+ *
+ * @param {string} nativeCurrency The new currency to set as the nativeCurrency in the store
+ *
+ */
+ setNativeCurrency (nativeCurrency) {
+ this.store.updateState({
+ nativeCurrency,
+ ticker: nativeCurrency,
+ })
+ }
+
+ /**
* A getter for the currentCurrency property
*
* @returns {string} A 2-4 character shorthand that describes a specific currency, currently selected by the user
@@ -104,17 +129,60 @@ class CurrencyController {
*
*/
async updateConversionRate () {
- let currentCurrency
+ let currentCurrency, nativeCurrency
try {
currentCurrency = this.getCurrentCurrency()
- const response = await fetch(`https://api.infura.io/v1/ticker/eth${currentCurrency.toLowerCase()}`)
- const parsedResponse = await response.json()
- this.setConversionRate(Number(parsedResponse.bid))
- this.setConversionDate(Number(parsedResponse.timestamp))
+ nativeCurrency = this.getNativeCurrency()
+ // select api
+ let apiUrl
+ if (nativeCurrency === 'ETH') {
+ // ETH
+ apiUrl = `https://api.infura.io/v1/ticker/eth${currentCurrency.toLowerCase()}`
+ } else {
+ // ETC
+ apiUrl = `https://min-api.cryptocompare.com/data/price?fsym=${nativeCurrency.toUpperCase()}&tsyms=${currentCurrency.toUpperCase()}`
+ }
+ // attempt request
+ let response
+ try {
+ response = await fetch(apiUrl)
+ } catch (err) {
+ log.error(new Error(`CurrencyController - Failed to request currency from Infura:\n${err.stack}`))
+ return
+ }
+ // parse response
+ let rawResponse
+ let parsedResponse
+ try {
+ rawResponse = await response.text()
+ parsedResponse = JSON.parse(rawResponse)
+ } catch (err) {
+ log.error(new Error(`CurrencyController - Failed to parse response "${rawResponse}"`))
+ return
+ }
+ // set conversion rate
+ if (nativeCurrency === 'ETH') {
+ // ETH
+ this.setConversionRate(Number(parsedResponse.bid))
+ this.setConversionDate(Number(parsedResponse.timestamp))
+ } else {
+ // ETC
+ if (parsedResponse[currentCurrency.toUpperCase()]) {
+ this.setConversionRate(Number(parsedResponse[currentCurrency.toUpperCase()]))
+ this.setConversionDate(parseInt((new Date()).getTime() / 1000))
+ } else {
+ this.setConversionRate(0)
+ this.setConversionDate('N/A')
+ }
+ }
} catch (err) {
- log.warn(`MetaMask - Failed to query currency conversion:`, currentCurrency, err)
+ // reset current conversion rate
+ log.warn(`MetaMask - Failed to query currency conversion:`, nativeCurrency, currentCurrency, err)
this.setConversionRate(0)
this.setConversionDate('N/A')
+ // throw error
+ log.error(new Error(`CurrencyController - Failed to query rate for currency "${currentCurrency}":\n${err.stack}`))
+ return
}
}
diff --git a/app/scripts/controllers/network/createMetamaskMiddleware.js b/app/scripts/controllers/network/createMetamaskMiddleware.js
index 9e6a45888..319c5bf3e 100644
--- a/app/scripts/controllers/network/createMetamaskMiddleware.js
+++ b/app/scripts/controllers/network/createMetamaskMiddleware.js
@@ -11,6 +11,7 @@ function createMetamaskMiddleware ({
processTransaction,
processEthSignMessage,
processTypedMessage,
+ processTypedMessageV3,
processPersonalMessage,
getPendingNonce,
}) {
@@ -25,6 +26,7 @@ function createMetamaskMiddleware ({
processTransaction,
processEthSignMessage,
processTypedMessage,
+ processTypedMessageV3,
processPersonalMessage,
}),
createPendingNonceMiddleware({ getPendingNonce }),
diff --git a/app/scripts/controllers/network/network.js b/app/scripts/controllers/network/network.js
index c1667d9a6..c21e9c764 100644
--- a/app/scripts/controllers/network/network.js
+++ b/app/scripts/controllers/network/network.js
@@ -11,6 +11,8 @@ const createInfuraClient = require('./createInfuraClient')
const createJsonRpcClient = require('./createJsonRpcClient')
const createLocalhostClient = require('./createLocalhostClient')
const { createSwappableProxy, createEventEmitterProxy } = require('swappable-obj-proxy')
+const extend = require('extend')
+const networks = { networkList: {} }
const {
ROPSTEN,
@@ -29,6 +31,10 @@ const defaultProviderConfig = {
type: testMode ? RINKEBY : MAINNET,
}
+const defaultNetworkConfig = {
+ ticker: 'ETH',
+}
+
module.exports = class NetworkController extends EventEmitter {
constructor (opts = {}) {
@@ -39,7 +45,8 @@ module.exports = class NetworkController extends EventEmitter {
// create stores
this.providerStore = new ObservableStore(providerConfig)
this.networkStore = new ObservableStore('loading')
- this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore })
+ this.networkConfig = new ObservableStore(defaultNetworkConfig)
+ this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore, settings: this.networkConfig })
this.on('networkDidChange', this.lookupNetwork)
// provider and block tracker
this._provider = null
@@ -51,8 +58,8 @@ module.exports = class NetworkController extends EventEmitter {
initializeProvider (providerParams) {
this._baseProviderParams = providerParams
- const { type, rpcTarget } = this.providerStore.getState()
- this._configureProvider({ type, rpcTarget })
+ const { type, rpcTarget, chainId, ticker, nickname } = this.providerStore.getState()
+ this._configureProvider({ type, rpcTarget, chainId, ticker, nickname })
this.lookupNetwork()
}
@@ -72,7 +79,20 @@ module.exports = class NetworkController extends EventEmitter {
return this.networkStore.getState()
}
- setNetworkState (network) {
+ getNetworkConfig () {
+ return this.networkConfig.getState()
+ }
+
+ setNetworkState (network, type) {
+ if (network === 'loading') {
+ return this.networkStore.putState(network)
+ }
+
+ // type must be defined
+ if (!type) {
+ return
+ }
+ network = networks.networkList[type] && networks.networkList[type].chainId ? networks.networkList[type].chainId : network
return this.networkStore.putState(network)
}
@@ -85,18 +105,32 @@ module.exports = class NetworkController extends EventEmitter {
if (!this._provider) {
return log.warn('NetworkController - lookupNetwork aborted due to missing provider')
}
+ var { type } = this.providerStore.getState()
const ethQuery = new EthQuery(this._provider)
- ethQuery.sendAsync({ method: 'net_version' }, (err, network) => {
- if (err) return this.setNetworkState('loading')
- log.info('web3.getNetwork returned ' + network)
- this.setNetworkState(network)
+ // first attempt to perform lookup via eth_chainId
+ ethQuery.sendAsync({ method: 'eth_chainId' }, (err, chainIdHex) => {
+ if (err) {
+ // if eth_chainId is not supported, fallback to net_verion
+ ethQuery.sendAsync({ method: 'net_version' }, (err, network) => {
+ if (err) return this.setNetworkState('loading')
+ log.info(`net_version returned ${network}`)
+ this.setNetworkState(network, type)
+ })
+ return
+ }
+ const chainId = Number.parseInt(chainIdHex, 16)
+ log.info(`net_version returned ${chainId}`)
+ this.setNetworkState(chainId, type)
})
}
- setRpcTarget (rpcTarget) {
+ setRpcTarget (rpcTarget, chainId, ticker = 'ETH', nickname = '') {
const providerConfig = {
type: 'rpc',
rpcTarget,
+ chainId,
+ ticker,
+ nickname,
}
this.providerConfig = providerConfig
}
@@ -132,7 +166,7 @@ module.exports = class NetworkController extends EventEmitter {
}
_configureProvider (opts) {
- const { type, rpcTarget } = opts
+ const { type, rpcTarget, chainId, ticker, nickname } = opts
// infura type-based endpoints
const isInfura = INFURA_PROVIDER_TYPES.includes(type)
if (isInfura) {
@@ -142,7 +176,7 @@ module.exports = class NetworkController extends EventEmitter {
this._configureLocalhostProvider()
// url-based rpc endpoints
} else if (type === 'rpc') {
- this._configureStandardProvider({ rpcUrl: rpcTarget })
+ this._configureStandardProvider({ rpcUrl: rpcTarget, chainId, ticker, nickname })
} else {
throw new Error(`NetworkController - _configureProvider - unknown type "${type}"`)
}
@@ -152,6 +186,11 @@ module.exports = class NetworkController extends EventEmitter {
log.info('NetworkController - configureInfuraProvider', type)
const networkClient = createInfuraClient({ network: type })
this._setNetworkClient(networkClient)
+ // setup networkConfig
+ var settings = {
+ ticker: 'ETH',
+ }
+ this.networkConfig.putState(settings)
}
_configureLocalhostProvider () {
@@ -160,9 +199,22 @@ module.exports = class NetworkController extends EventEmitter {
this._setNetworkClient(networkClient)
}
- _configureStandardProvider ({ rpcUrl }) {
+ _configureStandardProvider ({ rpcUrl, chainId, ticker, nickname }) {
log.info('NetworkController - configureStandardProvider', rpcUrl)
const networkClient = createJsonRpcClient({ rpcUrl })
+ // hack to add a 'rpc' network with chainId
+ networks.networkList['rpc'] = {
+ chainId: chainId,
+ rpcUrl,
+ ticker: ticker || 'ETH',
+ nickname,
+ }
+ // setup networkConfig
+ var settings = {
+ network: chainId,
+ }
+ settings = extend(settings, networks.networkList['rpc'])
+ this.networkConfig.putState(settings)
this._setNetworkClient(networkClient)
}
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index fd6a4866d..eaeaee499 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -25,7 +25,7 @@ class PreferencesController {
*/
constructor (opts = {}) {
const initState = extend({
- frequentRpcList: [],
+ frequentRpcListDetail: [],
currentAccountTab: 'history',
accountTokens: {},
assetImages: {},
@@ -38,6 +38,9 @@ class PreferencesController {
lostIdentities: {},
seedWords: null,
forgottenPassword: false,
+ preferences: {
+ useNativeCurrencyAsPrimaryCurrency: true,
+ },
}, opts.initState)
this.diagnostics = opts.diagnostics
@@ -101,7 +104,7 @@ class PreferencesController {
* @param {Function} - end
*/
async requestWatchAsset (req, res, next, end) {
- if (req.method === 'metamask_watchAsset') {
+ if (req.method === 'metamask_watchAsset' || req.method === 'wallet_watchAsset') {
const { type, options } = req.params
switch (type) {
case 'ERC20':
@@ -372,22 +375,6 @@ class PreferencesController {
}
/**
- * 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
- * @param {bool} remove Remove selected url
- * @returns {Promise<void>} Promise resolves with undefined
- *
- */
- updateFrequentRpcList (_url, remove = false) {
- return this.addToFrequentRpcList(_url, remove)
- .then((rpcList) => {
- this.store.updateState({ frequentRpcList: rpcList })
- return Promise.resolve()
- })
- }
-
- /**
* Setter for the `currentAccountTab` property
*
* @param {string} currentAccountTab Specifies the new tab to be marked as current
@@ -402,35 +389,53 @@ class PreferencesController {
}
/**
- * Returns an updated rpcList based on the passed url and the current list.
- * The returned list will have a max length of 3. 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.
+ * Adds custom RPC url to state.
*
- * @param {string} _url The rpc url to add to the frequentRpcList.
- * @param {bool} remove Remove selected url
- * @returns {Promise<array>} The updated frequentRpcList.
+ * @param {string} url The RPC url to add to frequentRpcList.
+ * @param {number} chainId Optional chainId of the selected network.
+ * @param {string} ticker Optional ticker symbol of the selected network.
+ * @param {string} nickname Optional nickname of the selected network.
+ * @returns {Promise<array>} Promise resolving to updated frequentRpcList.
*
*/
- addToFrequentRpcList (_url, remove = false) {
- const rpcList = this.getFrequentRpcList()
- const index = rpcList.findIndex((element) => { return element === _url })
+ addToFrequentRpcList (url, chainId, ticker = 'ETH', nickname = '') {
+ const rpcList = this.getFrequentRpcListDetail()
+ const index = rpcList.findIndex((element) => { return element.rpcUrl === url })
if (index !== -1) {
rpcList.splice(index, 1)
}
- if (!remove && _url !== 'http://localhost:8545') {
- rpcList.push(_url)
+ if (url !== 'http://localhost:8545') {
+ rpcList.push({ rpcUrl: url, chainId, ticker, nickname })
+ }
+ this.store.updateState({ frequentRpcListDetail: rpcList })
+ return Promise.resolve(rpcList)
+ }
+
+ /**
+ * Removes custom RPC url from state.
+ *
+ * @param {string} url The RPC url to remove from frequentRpcList.
+ * @returns {Promise<array>} Promise resolving to updated frequentRpcList.
+ *
+ */
+ removeFromFrequentRpcList (url) {
+ const rpcList = this.getFrequentRpcListDetail()
+ const index = rpcList.findIndex((element) => { return element.rpcUrl === url })
+ if (index !== -1) {
+ rpcList.splice(index, 1)
}
+ this.store.updateState({ frequentRpcListDetail: rpcList })
return Promise.resolve(rpcList)
}
/**
- * Getter for the `frequentRpcList` property.
+ * Getter for the `frequentRpcListDetail` property.
*
- * @returns {array<string>} An array of one or two rpc urls.
+ * @returns {array<array>} An array of rpc urls.
*
*/
- getFrequentRpcList () {
- return this.store.getState().frequentRpcList
+ getFrequentRpcListDetail () {
+ return this.store.getState().frequentRpcListDetail
}
/**
@@ -463,6 +468,33 @@ class PreferencesController {
getFeatureFlags () {
return this.store.getState().featureFlags
}
+
+ /**
+ * Updates the `preferences` property, which is an object. These are user-controlled features
+ * found in the settings page.
+ * @param {string} preference The preference to enable or disable.
+ * @param {boolean} value Indicates whether or not the preference should be enabled or disabled.
+ * @returns {Promise<object>} Promises a new object; the updated preferences object.
+ */
+ setPreference (preference, value) {
+ const currentPreferences = this.getPreferences()
+ const updatedPreferences = {
+ ...currentPreferences,
+ [preference]: value,
+ }
+
+ this.store.updateState({ preferences: updatedPreferences })
+ return Promise.resolve(updatedPreferences)
+ }
+
+ /**
+ * A getter for the `preferences` property
+ * @returns {object} A key-boolean map of user-selected preferences.
+ */
+ getPreferences () {
+ return this.store.getState().preferences
+ }
+
//
// PRIVATE METHODS
//
diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js
index ebd49f882..9f2290924 100644
--- a/app/scripts/controllers/transactions/index.js
+++ b/app/scripts/controllers/transactions/index.js
@@ -166,6 +166,10 @@ class TransactionController extends EventEmitter {
async addUnapprovedTransaction (txParams) {
// validate
const normalizedTxParams = txUtils.normalizeTxParams(txParams)
+ // Assert the from address is the selected address
+ if (normalizedTxParams.from !== this.getSelectedAddress()) {
+ throw new Error(`Transaction from address isn't valid for this account`)
+ }
txUtils.validateTxParams(normalizedTxParams)
// construct txMeta
let txMeta = this.txStateManager.generateTxMeta({
@@ -362,7 +366,40 @@ class TransactionController extends EventEmitter {
this.txStateManager.setTxStatusSubmitted(txId)
}
- confirmTransaction (txId) {
+ /**
+ * Sets the status of the transaction to confirmed and sets the status of nonce duplicates as
+ * dropped if the txParams have data it will fetch the txReceipt
+ * @param {number} txId - The tx's ID
+ * @returns {Promise<void>}
+ */
+ async confirmTransaction (txId) {
+ // get the txReceipt before marking the transaction confirmed
+ // to ensure the receipt is gotten before the ui revives the tx
+ const txMeta = this.txStateManager.getTx(txId)
+
+ if (!txMeta) {
+ return
+ }
+
+ try {
+ const txReceipt = await this.query.getTransactionReceipt(txMeta.hash)
+
+ // It seems that sometimes the numerical values being returned from
+ // this.query.getTransactionReceipt are BN instances and not strings.
+ const gasUsed = typeof txReceipt.gasUsed !== 'string'
+ ? txReceipt.gasUsed.toString(16)
+ : txReceipt.gasUsed
+
+ txMeta.txReceipt = {
+ ...txReceipt,
+ gasUsed,
+ }
+
+ this.txStateManager.updateTx(txMeta, 'transactions#confirmTransaction - add txReceipt')
+ } catch (err) {
+ log.error(err)
+ }
+
this.txStateManager.setTxStatusConfirmed(txId)
this._markNonceDuplicatesDropped(txId)
}
diff --git a/app/scripts/controllers/transactions/tx-gas-utils.js b/app/scripts/controllers/transactions/tx-gas-utils.js
index 3dd45507f..def67c2c3 100644
--- a/app/scripts/controllers/transactions/tx-gas-utils.js
+++ b/app/scripts/controllers/transactions/tx-gas-utils.js
@@ -7,6 +7,8 @@ const {
const { addHexPrefix } = require('ethereumjs-util')
const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send.
+import { TRANSACTION_NO_CONTRACT_ERROR_KEY } from '../../../../ui/app/constants/error-keys'
+
/**
tx-gas-utils are gas utility methods for Transaction manager
its passed ethquery
@@ -32,6 +34,7 @@ class TxGasUtil {
} catch (err) {
txMeta.simulationFails = {
reason: err.message,
+ errorKey: err.errorKey,
}
return txMeta
}
@@ -56,24 +59,38 @@ class TxGasUtil {
return txParams.gas
}
- // if recipient has no code, gas is 21k max:
const recipient = txParams.to
const hasRecipient = Boolean(recipient)
- let code
- if (recipient) code = await this.query.getCode(recipient)
- if (hasRecipient && (!code || code === '0x')) {
- txParams.gas = SIMPLE_GAS_COST
- txMeta.simpleSend = true // Prevents buffer addition
- return SIMPLE_GAS_COST
+ // see if we can set the gas based on the recipient
+ if (hasRecipient) {
+ const code = await this.query.getCode(recipient)
+ // For an address with no code, geth will return '0x', and ganache-core v2.2.1 will return '0x0'
+ const codeIsEmpty = !code || code === '0x' || code === '0x0'
+
+ if (codeIsEmpty) {
+ // if there's data in the params, but there's no contract code, it's not a valid transaction
+ if (txParams.data) {
+ const err = new Error('TxGasUtil - Trying to call a function on a non-contract address')
+ // set error key so ui can display localized error message
+ err.errorKey = TRANSACTION_NO_CONTRACT_ERROR_KEY
+ throw err
+ }
+
+ // This is a standard ether simple send, gas requirement is exactly 21k
+ txParams.gas = SIMPLE_GAS_COST
+ // prevents buffer addition
+ txMeta.simpleSend = true
+ return SIMPLE_GAS_COST
+ }
}
- // if not, fall back to block gasLimit
+ // fallback to block gasLimit
const blockGasLimitBN = hexToBn(blockGasLimitHex)
const saferGasLimitBN = BnMultiplyByFraction(blockGasLimitBN, 19, 20)
txParams.gas = bnToHex(saferGasLimitBN)
- // run tx
+ // estimate tx gas requirements
return await this.query.estimateGas(txParams)
}
diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js
index daa6cc388..58c48e34e 100644
--- a/app/scripts/controllers/transactions/tx-state-manager.js
+++ b/app/scripts/controllers/transactions/tx-state-manager.js
@@ -400,6 +400,11 @@ class TransactionStateManager extends EventEmitter {
*/
_setTxStatus (txId, status) {
const txMeta = this.getTx(txId)
+
+ if (!txMeta) {
+ return
+ }
+
txMeta.status = status
setTimeout(() => {
try {
diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js
index 431702d63..b885a7e05 100644
--- a/app/scripts/inpage.js
+++ b/app/scripts/inpage.js
@@ -27,6 +27,8 @@ var metamaskStream = new LocalMessageDuplexStream({
// compose the inpage provider
var inpageProvider = new MetamaskInpageProvider(metamaskStream)
+// set a high max listener count to avoid unnecesary warnings
+inpageProvider.setMaxListeners(100)
// Augment the provider with its enable method
inpageProvider.enable = function (options = {}) {
diff --git a/app/scripts/lib/contracts/registrar.js b/app/scripts/lib/ens-ipfs/contracts/registrar.js
index 99ca24458..99ca24458 100644
--- a/app/scripts/lib/contracts/registrar.js
+++ b/app/scripts/lib/ens-ipfs/contracts/registrar.js
diff --git a/app/scripts/lib/contracts/resolver.js b/app/scripts/lib/ens-ipfs/contracts/resolver.js
index 1bf3f90ce..1bf3f90ce 100644
--- a/app/scripts/lib/contracts/resolver.js
+++ b/app/scripts/lib/ens-ipfs/contracts/resolver.js
diff --git a/app/scripts/lib/ens-ipfs/resolver.js b/app/scripts/lib/ens-ipfs/resolver.js
new file mode 100644
index 000000000..fe2dc1134
--- /dev/null
+++ b/app/scripts/lib/ens-ipfs/resolver.js
@@ -0,0 +1,54 @@
+const namehash = require('eth-ens-namehash')
+const multihash = require('multihashes')
+const Eth = require('ethjs-query')
+const EthContract = require('ethjs-contract')
+const registrarAbi = require('./contracts/registrar')
+const resolverAbi = require('./contracts/resolver')
+
+module.exports = resolveEnsToIpfsContentId
+
+
+async function resolveEnsToIpfsContentId ({ provider, name }) {
+ const eth = new Eth(provider)
+ const hash = namehash.hash(name)
+ const contract = new EthContract(eth)
+ // lookup registrar
+ const chainId = Number.parseInt(await eth.net_version(), 10)
+ const registrarAddress = getRegistrarForChainId(chainId)
+ if (!registrarAddress) {
+ throw new Error(`EnsIpfsResolver - no known ens-ipfs registrar for chainId "${chainId}"`)
+ }
+ const Registrar = contract(registrarAbi).at(registrarAddress)
+ // lookup resolver
+ const resolverLookupResult = await Registrar.resolver(hash)
+ const resolverAddress = resolverLookupResult[0]
+ if (hexValueIsEmpty(resolverAddress)) {
+ throw new Error(`EnsIpfsResolver - no resolver found for name "${name}"`)
+ }
+ const Resolver = contract(resolverAbi).at(resolverAddress)
+ // lookup content id
+ const contentLookupResult = await Resolver.content(hash)
+ const contentHash = contentLookupResult[0]
+ if (hexValueIsEmpty(contentHash)) {
+ throw new Error(`EnsIpfsResolver - no content ID found for name "${name}"`)
+ }
+ const nonPrefixedHex = contentHash.slice(2)
+ const buffer = multihash.fromHexString(nonPrefixedHex)
+ const contentId = multihash.toB58String(multihash.encode(buffer, 'sha2-256'))
+ return contentId
+}
+
+function hexValueIsEmpty(value) {
+ return [undefined, null, '0x', '0x0', '0x0000000000000000000000000000000000000000000000000000000000000000'].includes(value)
+}
+
+function getRegistrarForChainId (chainId) {
+ switch (chainId) {
+ // mainnet
+ case 1:
+ return '0x314159265dd8dbb310642f98f50c066173c1259b'
+ // ropsten
+ case 3:
+ return '0x112234455c3a32fd11230c42e7bccd4a84e02010'
+ }
+}
diff --git a/app/scripts/lib/ens-ipfs/setup.js b/app/scripts/lib/ens-ipfs/setup.js
new file mode 100644
index 000000000..45eb1ce14
--- /dev/null
+++ b/app/scripts/lib/ens-ipfs/setup.js
@@ -0,0 +1,63 @@
+const urlUtil = require('url')
+const extension = require('extensionizer')
+const resolveEnsToIpfsContentId = require('./resolver.js')
+
+const supportedTopLevelDomains = ['eth']
+
+module.exports = setupEnsIpfsResolver
+
+function setupEnsIpfsResolver({ provider }) {
+
+ // install listener
+ const urlPatterns = supportedTopLevelDomains.map(tld => `*://*.${tld}/*`)
+ extension.webRequest.onErrorOccurred.addListener(webRequestDidFail, { urls: urlPatterns })
+
+ // return api object
+ return {
+ // uninstall listener
+ remove () {
+ extension.webRequest.onErrorOccurred.removeListener(webRequestDidFail)
+ },
+ }
+
+ async function webRequestDidFail (details) {
+ const { tabId, url } = details
+ // ignore requests that are not associated with tabs
+ if (tabId === -1) return
+ // parse ens name
+ const urlData = urlUtil.parse(url)
+ const { hostname: name, path, search } = urlData
+ const domainParts = name.split('.')
+ const topLevelDomain = domainParts[domainParts.length - 1]
+ // if unsupported TLD, abort
+ if (!supportedTopLevelDomains.includes(topLevelDomain)) return
+ // otherwise attempt resolve
+ attemptResolve({ tabId, name, path, search })
+ }
+
+ async function attemptResolve({ tabId, name, path, search }) {
+ extension.tabs.update(tabId, { url: `loading.html` })
+ try {
+ const ipfsContentId = await resolveEnsToIpfsContentId({ provider, name })
+ let url = `https://gateway.ipfs.io/ipfs/${ipfsContentId}${path}${search || ''}`
+ try {
+ // check if ipfs gateway has result
+ const response = await fetch(url, { method: 'HEAD' })
+ // if failure, redirect to 404 page
+ if (response.status !== 200) {
+ extension.tabs.update(tabId, { url: '404.html' })
+ return
+ }
+ // otherwise redirect to the correct page
+ extension.tabs.update(tabId, { url })
+ } catch (err) {
+ console.warn(err)
+ // if HEAD fetch failed, redirect so user can see relevant error page
+ extension.tabs.update(tabId, { url })
+ }
+ } catch (err) {
+ console.warn(err)
+ extension.tabs.update(tabId, { url: `error.html?name=${name}` })
+ }
+ }
+}
diff --git a/app/scripts/lib/ipfsContent.js b/app/scripts/lib/ipfsContent.js
deleted file mode 100644
index 8b08453c4..000000000
--- a/app/scripts/lib/ipfsContent.js
+++ /dev/null
@@ -1,46 +0,0 @@
-const extension = require('extensionizer')
-const resolver = require('./resolver.js')
-
-module.exports = function (provider) {
- function ipfsContent (details) {
- const name = details.url.substring(7, details.url.length - 1)
- let clearTime = null
- if (/^.+\.eth$/.test(name) === false) return
-
- extension.tabs.query({active: true}, tab => {
- extension.tabs.update(tab.id, { url: 'loading.html' })
-
- clearTime = setTimeout(() => {
- return extension.tabs.update(tab.id, { url: '404.html' })
- }, 60000)
-
- resolver.resolve(name, provider).then(ipfsHash => {
- clearTimeout(clearTime)
- let url = 'https://ipfs.infura.io/ipfs/' + ipfsHash
- return fetch(url, { method: 'HEAD' }).then(response => response.status).then(statusCode => {
- if (statusCode !== 200) return extension.tabs.update(tab.id, { url: '404.html' })
- extension.tabs.update(tab.id, { url: url })
- })
- .catch(err => {
- url = 'https://ipfs.infura.io/ipfs/' + ipfsHash
- extension.tabs.update(tab.id, {url: url})
- return err
- })
- })
- .catch(err => {
- clearTimeout(clearTime)
- const url = err === 'unsupport' ? 'unsupport' : 'error'
- extension.tabs.update(tab.id, {url: `${url}.html?name=${name}`})
- })
- })
- return { cancel: true }
- }
-
- extension.webRequest.onErrorOccurred.addListener(ipfsContent, {urls: ['*://*.eth/'], types: ['main_frame']})
-
- return {
- remove () {
- extension.webRequest.onErrorOccurred.removeListener(ipfsContent)
- },
- }
-}
diff --git a/app/scripts/lib/reportFailedTxToSentry.js b/app/scripts/lib/reportFailedTxToSentry.js
index df5661e59..de4d57145 100644
--- a/app/scripts/lib/reportFailedTxToSentry.js
+++ b/app/scripts/lib/reportFailedTxToSentry.js
@@ -7,10 +7,10 @@ module.exports = reportFailedTxToSentry
// for sending to sentry
//
-function reportFailedTxToSentry ({ raven, txMeta }) {
+function reportFailedTxToSentry ({ sentry, txMeta }) {
const errorMessage = 'Transaction Failed: ' + extractEthjsErrorMessage(txMeta.err.message)
- raven.captureMessage(errorMessage, {
+ sentry.captureMessage(errorMessage, {
// "extra" key is required by Sentry
- extra: txMeta,
+ extra: { txMeta },
})
}
diff --git a/app/scripts/lib/resolver.js b/app/scripts/lib/resolver.js
deleted file mode 100644
index ff0fed161..000000000
--- a/app/scripts/lib/resolver.js
+++ /dev/null
@@ -1,71 +0,0 @@
-const namehash = require('eth-ens-namehash')
-const multihash = require('multihashes')
-const HttpProvider = require('ethjs-provider-http')
-const Eth = require('ethjs-query')
-const EthContract = require('ethjs-contract')
-const registrarAbi = require('./contracts/registrar')
-const resolverAbi = require('./contracts/resolver')
-
-function ens (name, provider) {
- const eth = new Eth(new HttpProvider(getProvider(provider.type)))
- const hash = namehash.hash(name)
- const contract = new EthContract(eth)
- const Registrar = contract(registrarAbi).at(getRegistrar(provider.type))
- return new Promise((resolve, reject) => {
- if (provider.type === 'mainnet' || provider.type === 'ropsten') {
- Registrar.resolver(hash).then((address) => {
- if (address === '0x0000000000000000000000000000000000000000') {
- reject(null)
- } else {
- const Resolver = contract(resolverAbi).at(address['0'])
- return Resolver.content(hash)
- }
- }).then((contentHash) => {
- if (contentHash['0'] === '0x0000000000000000000000000000000000000000000000000000000000000000') reject(null)
- if (contentHash.ret !== '0x') {
- const hex = contentHash['0'].substring(2)
- const buf = multihash.fromHexString(hex)
- resolve(multihash.toB58String(multihash.encode(buf, 'sha2-256')))
- } else {
- reject(null)
- }
- })
- } else {
- return reject('unsupport')
- }
- })
-}
-
-function getProvider (type) {
- switch (type) {
- case 'mainnet':
- return 'https://mainnet.infura.io/'
- case 'ropsten':
- return 'https://ropsten.infura.io/'
- default:
- return 'http://localhost:8545/'
- }
-}
-
-function getRegistrar (type) {
- switch (type) {
- case 'mainnet':
- return '0x314159265dd8dbb310642f98f50c066173c1259b'
- case 'ropsten':
- return '0x112234455c3a32fd11230c42e7bccd4a84e02010'
- default:
- return '0x0000000000000000000000000000000000000000'
- }
-}
-
-module.exports.resolve = function (name, provider) {
- const path = name.split('.')
- const topLevelDomain = path[path.length - 1]
- if (topLevelDomain === 'eth' || topLevelDomain === 'test') {
- return ens(name, provider)
- } else {
- return new Promise((resolve, reject) => {
- reject(null)
- })
- }
-}
diff --git a/app/scripts/lib/setupFetchDebugging.js b/app/scripts/lib/setupFetchDebugging.js
new file mode 100644
index 000000000..c1ef22d21
--- /dev/null
+++ b/app/scripts/lib/setupFetchDebugging.js
@@ -0,0 +1,36 @@
+module.exports = setupFetchDebugging
+
+//
+// This is a utility to help resolve cases where `window.fetch` throws a
+// `TypeError: Failed to Fetch` without any stack or context for the request
+// https://github.com/getsentry/sentry-javascript/pull/1293
+//
+
+function setupFetchDebugging() {
+ if (!global.fetch) return
+ const originalFetch = global.fetch
+
+ global.fetch = wrappedFetch
+
+ async function wrappedFetch(...args) {
+ const initialStack = getCurrentStack()
+ try {
+ return await originalFetch.call(window, ...args)
+ } catch (err) {
+ if (!err.stack) {
+ console.warn('FetchDebugger - fetch encountered an Error without a stack', err)
+ console.warn('FetchDebugger - overriding stack to point of original call')
+ err.stack = initialStack
+ }
+ throw err
+ }
+ }
+}
+
+function getCurrentStack() {
+ try {
+ throw new Error('Fake error for generating stack trace')
+ } catch (err) {
+ return err.stack
+ }
+}
diff --git a/app/scripts/lib/setupRaven.js b/app/scripts/lib/setupSentry.js
index e6e511640..69042bc19 100644
--- a/app/scripts/lib/setupRaven.js
+++ b/app/scripts/lib/setupSentry.js
@@ -1,58 +1,55 @@
-const Raven = require('raven-js')
+const Sentry = require('@sentry/browser')
const METAMASK_DEBUG = process.env.METAMASK_DEBUG
const extractEthjsErrorMessage = require('./extractEthjsErrorMessage')
-const PROD = 'https://3567c198f8a8412082d32655da2961d0@sentry.io/273505'
-const DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496'
+const SENTRY_DSN_PROD = 'https://3567c198f8a8412082d32655da2961d0@sentry.io/273505'
+const SENTRY_DSN_DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496'
-module.exports = setupRaven
+module.exports = setupSentry
-// Setup raven / sentry remote error reporting
-function setupRaven (opts) {
- const { release } = opts
- let ravenTarget
+// Setup sentry remote error reporting
+function setupSentry (opts) {
+ const { release, getState } = opts
+ let sentryTarget
// detect brave
const isBrave = Boolean(window.chrome.ipcRenderer)
if (METAMASK_DEBUG) {
- console.log('Setting up Sentry Remote Error Reporting: DEV')
- ravenTarget = DEV
+ console.log('Setting up Sentry Remote Error Reporting: SENTRY_DSN_DEV')
+ sentryTarget = SENTRY_DSN_DEV
} else {
- console.log('Setting up Sentry Remote Error Reporting: PROD')
- ravenTarget = PROD
+ console.log('Setting up Sentry Remote Error Reporting: SENTRY_DSN_PROD')
+ sentryTarget = SENTRY_DSN_PROD
}
- const client = Raven.config(ravenTarget, {
+ Sentry.init({
+ dsn: sentryTarget,
+ debug: METAMASK_DEBUG,
release,
- transport: function (opts) {
- opts.data.extra.isBrave = isBrave
- const report = opts.data
+ beforeSend: (report) => rewriteReport(report),
+ })
- try {
- // handle error-like non-error exceptions
- rewriteErrorLikeExceptions(report)
- // simplify certain complex error messages (e.g. Ethjs)
- simplifyErrorMessages(report)
- // modify report urls
- rewriteReportUrls(report)
- } catch (err) {
- console.warn(err)
- }
- // make request normally
- client._makeRequest(opts)
- },
+ Sentry.configureScope(scope => {
+ scope.setExtra('isBrave', isBrave)
})
- client.install()
- return Raven
-}
+ function rewriteReport(report) {
+ try {
+ // simplify certain complex error messages (e.g. Ethjs)
+ simplifyErrorMessages(report)
+ // modify report urls
+ rewriteReportUrls(report)
+ // append app state
+ if (getState) {
+ const appState = getState()
+ report.extra.appState = appState
+ }
+ } catch (err) {
+ console.warn(err)
+ }
+ return report
+ }
-function rewriteErrorLikeExceptions (report) {
- // handle errors that lost their error-ness in serialization (e.g. dnode)
- rewriteErrorMessages(report, (errorMessage) => {
- if (!errorMessage.includes('Non-Error exception captured with keys:')) return errorMessage
- if (!(report.extra && report.extra.__serialized__ && report.extra.__serialized__.message)) return errorMessage
- return `Non-Error Exception: ${report.extra.__serialized__.message}`
- })
+ return Sentry
}
function simplifyErrorMessages (report) {
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 493877345..1f6a8659b 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -129,6 +129,7 @@ module.exports = class MetamaskController extends EventEmitter {
provider: this.provider,
blockTracker: this.blockTracker,
})
+
// start and stop polling for balances based on activeControllerConnections
this.on('controllerConnectionChanged', (activeControllerConnections) => {
if (activeControllerConnections > 0) {
@@ -138,6 +139,11 @@ module.exports = class MetamaskController extends EventEmitter {
}
})
+ // ensure accountTracker updates balances after network change
+ this.networkController.on('networkDidChange', () => {
+ this.accountTracker._updateAccounts()
+ })
+
// key mgmt
const additionalKeyrings = [TrezorKeyring, LedgerBridgeKeyring]
this.keyringController = new KeyringController({
@@ -191,6 +197,8 @@ module.exports = class MetamaskController extends EventEmitter {
})
this.networkController.on('networkDidChange', () => {
this.balancesController.updateAllBalances()
+ var currentCurrency = this.currencyController.getCurrentCurrency()
+ this.setCurrentCurrency(currentCurrency, function() {})
})
this.balancesController.updateAllBalances()
@@ -269,6 +277,8 @@ module.exports = class MetamaskController extends EventEmitter {
processTransaction: this.newUnapprovedTransaction.bind(this),
// msg signing
processEthSignMessage: this.newUnsignedMessage.bind(this),
+ processTypedMessage: this.newUnsignedTypedMessage.bind(this),
+ processTypedMessageV3: this.newUnsignedTypedMessage.bind(this),
processPersonalMessage: this.newUnsignedPersonalMessage.bind(this),
getPendingNonce: this.getPendingNonce.bind(this),
}
@@ -387,6 +397,7 @@ module.exports = class MetamaskController extends EventEmitter {
setCurrentAccountTab: nodeify(preferencesController.setCurrentAccountTab, preferencesController),
setAccountLabel: nodeify(preferencesController.setAccountLabel, preferencesController),
setFeatureFlag: nodeify(preferencesController.setFeatureFlag, preferencesController),
+ setPreference: nodeify(preferencesController.setPreference, preferencesController),
// BlacklistController
whitelistPhishingDomain: this.whitelistPhishingDomain.bind(this),
@@ -971,8 +982,8 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {Object} msgParams - The params passed to eth_signTypedData.
* @param {Function} cb - The callback function, called with the signature.
*/
- newUnsignedTypedMessage (msgParams, req) {
- const promise = this.typedMessageManager.addUnapprovedMessageAsync(msgParams, req)
+ newUnsignedTypedMessage (msgParams, req, version) {
+ const promise = this.typedMessageManager.addUnapprovedMessageAsync(msgParams, req, version)
this.sendUpdate()
this.opts.showUnconfirmedMessage()
return promise
@@ -1266,10 +1277,6 @@ module.exports = class MetamaskController extends EventEmitter {
engine.push(subscriptionManager.middleware)
// watch asset
engine.push(this.preferencesController.requestWatchAsset.bind(this.preferencesController))
- // sign typed data middleware
- engine.push(this.createTypedDataMiddleware('eth_signTypedData', 'V1').bind(this))
- engine.push(this.createTypedDataMiddleware('eth_signTypedData_v1', 'V1').bind(this))
- engine.push(this.createTypedDataMiddleware('eth_signTypedData_v3', 'V3', true).bind(this))
// forward to metamask primary provider
engine.push(createProviderMiddleware({ provider }))
@@ -1405,10 +1412,13 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {Function} cb - A callback function returning currency info.
*/
setCurrentCurrency (currencyCode, cb) {
+ const { ticker } = this.networkController.getNetworkConfig()
try {
+ this.currencyController.setNativeCurrency(ticker)
this.currencyController.setCurrentCurrency(currencyCode)
this.currencyController.updateConversionRate()
const data = {
+ nativeCurrency: ticker || 'ETH',
conversionRate: this.currencyController.getConversionRate(),
currentCurrency: this.currencyController.getCurrentCurrency(),
conversionDate: this.currencyController.getConversionDate(),
@@ -1447,11 +1457,14 @@ module.exports = class MetamaskController extends EventEmitter {
/**
* A method for selecting a custom URL for an ethereum RPC provider.
* @param {string} rpcTarget - A URL for a valid Ethereum RPC API.
+ * @param {number} chainId - The chainId of the selected network.
+ * @param {string} ticker - The ticker symbol of the selected network.
+ * @param {string} nickname - Optional nickname of the selected network.
* @returns {Promise<String>} - The RPC Target URL confirmed.
*/
- async setCustomRpc (rpcTarget) {
- this.networkController.setRpcTarget(rpcTarget)
- await this.preferencesController.updateFrequentRpcList(rpcTarget)
+ async setCustomRpc (rpcTarget, chainId, ticker = 'ETH', nickname = '') {
+ this.networkController.setRpcTarget(rpcTarget, chainId, ticker, nickname)
+ await this.preferencesController.addToFrequentRpcList(rpcTarget, chainId, ticker, nickname)
return rpcTarget
}
@@ -1460,7 +1473,7 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {string} rpcTarget - A RPC URL to delete.
*/
async delCustomRpc (rpcTarget) {
- await this.preferencesController.updateFrequentRpcList(rpcTarget, true)
+ await this.preferencesController.removeFromFrequentRpcList(rpcTarget)
}
/**
@@ -1535,27 +1548,6 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {Function} - next
* @param {Function} - end
*/
- createTypedDataMiddleware (methodName, version, reverse) {
- return async (req, res, next, end) => {
- const { method, params } = req
- if (method === methodName) {
- const promise = this.typedMessageManager.addUnapprovedMessageAsync({
- data: reverse ? params[1] : params[0],
- from: reverse ? params[0] : params[1],
- }, req, version)
- this.sendUpdate()
- this.opts.showUnconfirmedMessage()
- try {
- res.result = await promise
- end()
- } catch (error) {
- end(error)
- }
- } else {
- next()
- }
- }
- }
/**
* Adds a domain to the {@link BlacklistController} whitelist
diff --git a/app/scripts/ui.js b/app/scripts/ui.js
index 98a036338..c4f6615db 100644
--- a/app/scripts/ui.js
+++ b/app/scripts/ui.js
@@ -9,7 +9,7 @@ const extension = require('extensionizer')
const ExtensionPlatform = require('./platforms/extension')
const NotificationManager = require('./lib/notification-manager')
const notificationManager = new NotificationManager()
-const setupRaven = require('./lib/setupRaven')
+const setupSentry = require('./lib/setupSentry')
const log = require('loglevel')
start().catch(log.error)
@@ -21,7 +21,17 @@ async function start () {
// setup sentry error reporting
const release = global.platform.getVersion()
- setupRaven({ release })
+ setupSentry({ release, getState })
+ // provide app state to append to error logs
+ function getState() {
+ // get app state
+ const state = window.getCleanAppState()
+ // remove unnecessary data
+ delete state.localeMessages
+ delete state.metamask.recentBlocks
+ // return state to be added to request
+ return state
+ }
// inject css
// const css = MetaMaskUiCss()