aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/scripts/background.js12
-rw-r--r--app/scripts/controllers/blacklist.js25
-rw-r--r--app/scripts/controllers/currency.js23
-rw-r--r--app/scripts/lib/reportFailedTxToSentry.js6
-rw-r--r--app/scripts/lib/setupFetchDebugging.js10
-rw-r--r--app/scripts/lib/setupSentry.js (renamed from app/scripts/lib/setupRaven.js)75
-rw-r--r--app/scripts/ui.js14
-rw-r--r--package-lock.json111
-rw-r--r--package.json6
-rw-r--r--ui/app/reducers.js26
10 files changed, 216 insertions, 92 deletions
diff --git a/app/scripts/background.js b/app/scripts/background.js
index 509a0001d..2455608aa 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -23,7 +23,7 @@ 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')
@@ -50,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
@@ -197,14 +197,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')
}
}
@@ -212,7 +212,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 },
})
@@ -279,7 +279,7 @@ function setupController (initState, initLangCode) {
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/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..6b82265f6 100644
--- a/app/scripts/controllers/currency.js
+++ b/app/scripts/controllers/currency.js
@@ -107,14 +107,31 @@ class CurrencyController {
let currentCurrency
try {
currentCurrency = this.getCurrentCurrency()
- const response = await fetch(`https://api.infura.io/v1/ticker/eth${currentCurrency.toLowerCase()}`)
- const parsedResponse = await response.json()
+ let response
+ try {
+ response = await fetch(`https://api.infura.io/v1/ticker/eth${currentCurrency.toLowerCase()}`)
+ } catch (err) {
+ log.error(new Error(`CurrencyController - Failed to request currency from Infura:\n${err.stack}`))
+ return
+ }
+ 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
+ }
this.setConversionRate(Number(parsedResponse.bid))
this.setConversionDate(Number(parsedResponse.timestamp))
} catch (err) {
- log.warn(`MetaMask - Failed to query currency conversion:`, currentCurrency, err)
+ // reset current conversion rate
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/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/setupFetchDebugging.js b/app/scripts/lib/setupFetchDebugging.js
index dd87b65a6..c1ef22d21 100644
--- a/app/scripts/lib/setupFetchDebugging.js
+++ b/app/scripts/lib/setupFetchDebugging.js
@@ -2,7 +2,7 @@ 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
+// `TypeError: Failed to Fetch` without any stack or context for the request
// https://github.com/getsentry/sentry-javascript/pull/1293
//
@@ -17,9 +17,11 @@ function setupFetchDebugging() {
try {
return await originalFetch.call(window, ...args)
} catch (err) {
- console.warn('FetchDebugger - fetch encountered an Error', err)
- console.warn('FetchDebugger - overriding stack to point of original call')
- err.stack = initialStack
+ 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
}
}
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/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()
diff --git a/package-lock.json b/package-lock.json
index e3322d21d..7b694fdea 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -489,6 +489,16 @@
}
}
},
+ "@sentry/browser": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-4.1.1.tgz",
+ "integrity": "sha512-rmkGlTh0AL3Jf0DvF3BluChIyzPkkYpNgIwEHjxTUiLp6BQdgwakZuzBqSPJrEs+jMsKMoesOuJ/fAAG0K7+Ew==",
+ "requires": {
+ "@sentry/core": "4.1.1",
+ "@sentry/types": "4.1.0",
+ "@sentry/utils": "4.1.1"
+ }
+ },
"@sentry/cli": {
"version": "1.30.3",
"resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.30.3.tgz",
@@ -531,6 +541,48 @@
}
}
},
+ "@sentry/core": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/@sentry/core/-/core-4.1.1.tgz",
+ "integrity": "sha512-QJExTxZ1ZA5P/To5gOwd3sowukXW0N/Q9nfu8biRDNa+YURn6ElLjO0fD6eIBqX1f3npo/kTiWZwFBc7LXEzSg==",
+ "requires": {
+ "@sentry/hub": "4.1.1",
+ "@sentry/minimal": "4.1.1",
+ "@sentry/types": "4.1.0",
+ "@sentry/utils": "4.1.1"
+ }
+ },
+ "@sentry/hub": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-4.1.1.tgz",
+ "integrity": "sha512-VmcZOgcbFjJzK1oQNwcFP/wgfoWQr24dFv1C0uwdXldNXx3mwyUVkomvklBHz90HwiahsI/gCc+ZmbC3ECQk2Q==",
+ "requires": {
+ "@sentry/types": "4.1.0",
+ "@sentry/utils": "4.1.1"
+ }
+ },
+ "@sentry/minimal": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-4.1.1.tgz",
+ "integrity": "sha512-xRKWA46OGnZinJyTljDUel53emPP9mb/XNi/kF6SBaVDOUXl7HAB8kP7Bn7eLBwOanxN8PbYoAzh/lIQXWTmDg==",
+ "requires": {
+ "@sentry/hub": "4.1.1",
+ "@sentry/types": "4.1.0"
+ }
+ },
+ "@sentry/types": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@sentry/types/-/types-4.1.0.tgz",
+ "integrity": "sha512-KY7B9wYs1NACHlYzG4OuP6k4uQJkyDPJppftjj3NJYShfwdDTO1I2Swkhhb5dJMEMMMpBJGxXmiqZ2mX5ErISQ=="
+ },
+ "@sentry/utils": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-4.1.1.tgz",
+ "integrity": "sha512-XMvGqAWATBrRkOF0lkt0Ij8of2mRmp4WeFTUAgiKzCekxfUBLBaTb4wTaFXz1cnnnjVTwcAq72qBRMhHwQ0IIg==",
+ "requires": {
+ "@sentry/types": "4.1.0"
+ }
+ },
"@sinonjs/formatio": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz",
@@ -6131,9 +6183,9 @@
}
},
"clone": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz",
- "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs="
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+ "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18="
},
"clone-buffer": {
"version": "1.0.0",
@@ -9899,29 +9951,26 @@
}
},
"eth-json-rpc-middleware": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/eth-json-rpc-middleware/-/eth-json-rpc-middleware-3.1.3.tgz",
- "integrity": "sha512-glp/mCefhsqrgVOTTuYlHYiTL+9mMPfaZsuQv4vnRg3kqNigblS1nqARaMeVW9WOM8ssh9TqIFpuUr7JDgNmKQ==",
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/eth-json-rpc-middleware/-/eth-json-rpc-middleware-3.1.5.tgz",
+ "integrity": "sha512-68POkWUj0zYvxincExjRWXE49cxKDUSrSZn4oDVER5O5mWm0q/xcsLxlcurNFdHPov9APVqCXqzK6n2jUCDfRQ==",
"dev": true,
"requires": {
- "async": "^2.5.0",
"btoa": "^1.2.1",
"clone": "^2.1.1",
"eth-query": "^2.1.2",
"eth-sig-util": "^1.4.2",
- "eth-tx-summary": "^3.1.2",
+ "eth-tx-summary": "^3.2.3",
"ethereumjs-block": "^1.6.0",
"ethereumjs-tx": "^1.3.3",
"ethereumjs-util": "^5.1.2",
- "ethereumjs-vm": "^2.1.0",
+ "ethereumjs-vm": "^2.4.0",
"fetch-ponyfill": "^4.0.0",
- "json-rpc-engine": "^3.6.3",
+ "json-rpc-engine": "^3.8.0",
"json-rpc-error": "^2.0.0",
"json-stable-stringify": "^1.0.1",
"pify": "^3.0.0",
- "promise-to-callback": "^1.0.0",
- "safe-event-emitter": "^1.0.1",
- "tape": "^4.6.3"
+ "safe-event-emitter": "^1.0.1"
},
"dependencies": {
"eth-sig-util": {
@@ -9966,6 +10015,31 @@
"safe-buffer": "^5.1.1",
"secp256k1": "^3.0.1"
}
+ },
+ "ethereumjs-vm": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/ethereumjs-vm/-/ethereumjs-vm-2.4.0.tgz",
+ "integrity": "sha512-MJ4lCWa5c6LhahhhvoDKW+YGjK00ZQn0RHHLh4L+WaH1k6Qv7/q3uTluew6sJGNCZdlO0yYMDXYW9qyxLHKlgQ==",
+ "dev": true,
+ "requires": {
+ "async": "^2.1.2",
+ "async-eventemitter": "^0.2.2",
+ "ethereumjs-account": "^2.0.3",
+ "ethereumjs-block": "~1.7.0",
+ "ethereumjs-common": "~0.4.0",
+ "ethereumjs-util": "^5.2.0",
+ "fake-merkle-patricia-tree": "^1.0.1",
+ "functional-red-black-tree": "^1.0.1",
+ "merkle-patricia-tree": "^2.1.2",
+ "rustbn.js": "~0.2.0",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "rustbn.js": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz",
+ "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==",
+ "dev": true
}
}
},
@@ -10916,6 +10990,12 @@
}
}
},
+ "ethereumjs-common": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/ethereumjs-common/-/ethereumjs-common-0.4.1.tgz",
+ "integrity": "sha512-ywYGsOeGCsMNWso5Y4GhjWI24FJv9FK7+VyVKiQgXg8ZRDPXJ7F/kJ1CnjtkjTvDF4e0yqU+FWswlqR3bmZQ9Q==",
+ "dev": true
+ },
"ethereumjs-tx": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-1.3.3.tgz",
@@ -27446,11 +27526,6 @@
"eve-raphael": "0.5.0"
}
},
- "raven-js": {
- "version": "3.24.2",
- "resolved": "https://registry.npmjs.org/raven-js/-/raven-js-3.24.2.tgz",
- "integrity": "sha512-Dy/FHDxuo5pXywVf8Nrs5utB2juMATpkxWGqHjVbpFD3m8CaWYLvkB5SEXjWFUZ5GvUsrBVVQ+Dfcp0x6Z7SOg=="
- },
"raw-body": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
diff --git a/package.json b/package.json
index ef72d7bfb..f522b8833 100644
--- a/package.json
+++ b/package.json
@@ -82,6 +82,7 @@
},
"dependencies": {
"@material-ui/core": "1.0.0",
+ "@sentry/browser": "^4.1.1",
"@zxing/library": "^0.8.0",
"abi-decoder": "^1.0.9",
"asmcrypto.js": "0.22.0",
@@ -97,7 +98,7 @@
"browserify-derequire": "^0.9.4",
"browserify-unibabel": "^3.0.0",
"classnames": "^2.2.5",
- "clone": "^2.1.1",
+ "clone": "^2.1.2",
"copy-to-clipboard": "^3.0.8",
"css-loader": "^0.28.11",
"currency-formatter": "^1.4.2",
@@ -186,7 +187,6 @@
"pumpify": "^1.3.4",
"qrcode-npm": "0.0.3",
"ramda": "^0.24.1",
- "raven-js": "^3.24.2",
"react": "^15.6.2",
"react-addons-css-transition-group": "^15.6.0",
"react-dom": "^15.6.2",
@@ -261,7 +261,7 @@
"eslint-plugin-json": "^1.2.0",
"eslint-plugin-mocha": "^5.0.0",
"eslint-plugin-react": "^7.4.0",
- "eth-json-rpc-middleware": "^3.1.3",
+ "eth-json-rpc-middleware": "^3.1.5",
"eth-keyring-controller": "^3.3.1",
"fetch-mock": "^6.5.2",
"file-loader": "^1.1.11",
diff --git a/ui/app/reducers.js b/ui/app/reducers.js
index 80e76d570..e1a982f93 100644
--- a/ui/app/reducers.js
+++ b/ui/app/reducers.js
@@ -1,3 +1,4 @@
+const clone = require('clone')
const extend = require('xtend')
const copyToClipboard = require('copy-to-clipboard')
@@ -52,19 +53,24 @@ function rootReducer (state, action) {
return state
}
+window.getCleanAppState = function () {
+ const state = clone(window.METAMASK_CACHED_LOG_STATE)
+ // append additional information
+ state.version = global.platform.getVersion()
+ state.browser = window.navigator.userAgent
+ // ensure seedWords are not included
+ if (state.metamask) delete state.metamask.seedWords
+ if (state.appState.currentView) delete state.appState.currentView.seedWords
+ return state
+}
+
window.logStateString = function (cb) {
- const state = window.METAMASK_CACHED_LOG_STATE
- const version = global.platform.getVersion()
- const browser = window.navigator.userAgent
- return global.platform.getPlatformInfo((err, platform) => {
- if (err) {
- return cb(err)
- }
- state.version = version
+ const state = window.getCleanAppState()
+ global.platform.getPlatformInfo((err, platform) => {
+ if (err) return cb(err)
state.platform = platform
- state.browser = browser
const stateString = JSON.stringify(state, removeSeedWords, 2)
- return cb(null, stateString)
+ cb(null, stateString)
})
}