mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-29 23:58:06 +01:00
e8b33fb7c8
The state snapshot that was attached to Sentry errors was removed recently in #8794 because it had become too large. The snapshot has now been restored and reduced in size. A utility function has been written to reduce the state object to just the requested properties. This seemed safer than filtering out state that is known to be large or to contain identifiable information. This is not a great solution, as now knowledge about the state shape resides in this large constant, but it will suffice for now. I am hopeful that we can decorate our controllers with this metadata in the future instead, as part of the upcoming background controller refactor. A separate `getSentryState` global function has been added to get the reduced state, so that the old `getCleanAppState` function that we used to use could remain unchanged. It's still useful to get that full state copy while debugging, and in e2e tests.
211 lines
6.3 KiB
JavaScript
211 lines
6.3 KiB
JavaScript
import copyToClipboard from 'copy-to-clipboard'
|
|
import log from 'loglevel'
|
|
import { clone } from 'lodash'
|
|
import React from 'react'
|
|
import { render } from 'react-dom'
|
|
import Root from './app/pages'
|
|
import * as actions from './app/store/actions'
|
|
import configureStore from './app/store/store'
|
|
import txHelper from './lib/tx-helper'
|
|
import { getEnvironmentType } from '../app/scripts/lib/util'
|
|
import { ALERT_TYPES } from '../app/scripts/controllers/alert'
|
|
import { SENTRY_STATE } from '../app/scripts/lib/setupSentry'
|
|
import { ENVIRONMENT_TYPE_POPUP } from '../app/scripts/lib/enums'
|
|
import { fetchLocale, loadRelativeTimeFormatLocaleData } from './app/helpers/utils/i18n-helper'
|
|
import switchDirection from './app/helpers/utils/switch-direction'
|
|
import { getPermittedAccountsForCurrentTab, getSelectedAddress } from './app/selectors'
|
|
import { ALERT_STATE } from './app/ducks/alerts/unconnected-account'
|
|
import {
|
|
getUnconnectedAccountAlertEnabledness,
|
|
getUnconnectedAccountAlertShown,
|
|
} from './app/ducks/metamask/metamask'
|
|
|
|
log.setLevel(global.METAMASK_DEBUG ? 'debug' : 'warn')
|
|
|
|
export default function launchMetamaskUi (opts, cb) {
|
|
const { backgroundConnection } = opts
|
|
actions._setBackgroundConnection(backgroundConnection)
|
|
// check if we are unlocked first
|
|
backgroundConnection.getState(function (err, metamaskState) {
|
|
if (err) {
|
|
return cb(err)
|
|
}
|
|
startApp(metamaskState, backgroundConnection, opts)
|
|
.then((store) => {
|
|
setupDebuggingHelpers(store)
|
|
cb(null, store)
|
|
})
|
|
})
|
|
}
|
|
|
|
async function startApp (metamaskState, backgroundConnection, opts) {
|
|
// parse opts
|
|
if (!metamaskState.featureFlags) {
|
|
metamaskState.featureFlags = {}
|
|
}
|
|
|
|
const currentLocaleMessages = metamaskState.currentLocale
|
|
? await fetchLocale(metamaskState.currentLocale)
|
|
: {}
|
|
const enLocaleMessages = await fetchLocale('en')
|
|
|
|
await loadRelativeTimeFormatLocaleData('en')
|
|
if (metamaskState.currentLocale) {
|
|
await loadRelativeTimeFormatLocaleData(metamaskState.currentLocale)
|
|
}
|
|
|
|
if (metamaskState.textDirection === 'rtl') {
|
|
await switchDirection('rtl')
|
|
}
|
|
|
|
const draftInitialState = {
|
|
activeTab: opts.activeTab,
|
|
|
|
// metamaskState represents the cross-tab state
|
|
metamask: metamaskState,
|
|
|
|
// appState represents the current tab's popup state
|
|
appState: {},
|
|
|
|
localeMessages: {
|
|
current: currentLocaleMessages,
|
|
en: enLocaleMessages,
|
|
},
|
|
}
|
|
|
|
if (getEnvironmentType() === ENVIRONMENT_TYPE_POPUP) {
|
|
const origin = draftInitialState.activeTab.origin
|
|
const permittedAccountsForCurrentTab = getPermittedAccountsForCurrentTab(draftInitialState)
|
|
const selectedAddress = getSelectedAddress(draftInitialState)
|
|
const unconnectedAccountAlertShownOrigins = getUnconnectedAccountAlertShown(draftInitialState)
|
|
const unconnectedAccountAlertIsEnabled = getUnconnectedAccountAlertEnabledness(draftInitialState)
|
|
|
|
if (
|
|
origin &&
|
|
unconnectedAccountAlertIsEnabled &&
|
|
!unconnectedAccountAlertShownOrigins[origin] &&
|
|
permittedAccountsForCurrentTab.length > 0 &&
|
|
!permittedAccountsForCurrentTab.includes(selectedAddress)
|
|
) {
|
|
draftInitialState[ALERT_TYPES.unconnectedAccount] = { state: ALERT_STATE.OPEN }
|
|
actions.setUnconnectedAccountAlertShown(origin)
|
|
}
|
|
}
|
|
|
|
const store = configureStore(draftInitialState)
|
|
|
|
// if unconfirmed txs, start on txConf page
|
|
const unapprovedTxsAll = txHelper(
|
|
metamaskState.unapprovedTxs,
|
|
metamaskState.unapprovedMsgs,
|
|
metamaskState.unapprovedPersonalMsgs,
|
|
metamaskState.unapprovedDecryptMsgs,
|
|
metamaskState.unapprovedEncryptionPublicKeyMsgs,
|
|
metamaskState.unapprovedTypedMessages,
|
|
metamaskState.network,
|
|
)
|
|
const numberOfUnapprivedTx = unapprovedTxsAll.length
|
|
if (numberOfUnapprivedTx > 0) {
|
|
store.dispatch(actions.showConfTxPage({
|
|
id: unapprovedTxsAll[0].id,
|
|
}))
|
|
}
|
|
|
|
backgroundConnection.on('update', function (metamaskState) {
|
|
store.dispatch(actions.updateMetamaskState(metamaskState))
|
|
})
|
|
|
|
// global metamask api - used by tooling
|
|
global.metamask = {
|
|
updateCurrentLocale: (code) => {
|
|
store.dispatch(actions.updateCurrentLocale(code))
|
|
},
|
|
setProviderType: (type) => {
|
|
store.dispatch(actions.setProviderType(type))
|
|
},
|
|
setFeatureFlag: (key, value) => {
|
|
store.dispatch(actions.setFeatureFlag(key, value))
|
|
},
|
|
}
|
|
|
|
// start app
|
|
render(
|
|
<Root
|
|
store={store}
|
|
/>,
|
|
opts.container,
|
|
)
|
|
|
|
return store
|
|
}
|
|
|
|
/**
|
|
* Return a "masked" copy of the given object.
|
|
*
|
|
* The returned object includes only the properties present in the mask. The
|
|
* mask is an object that mirrors the structure of the given object, except
|
|
* the only values are `true` or a sub-mask. `true` implies the property
|
|
* should be included, and a sub-mask implies the property should be further
|
|
* masked according to that sub-mask.
|
|
*
|
|
* @param {Object} object - The object to mask
|
|
* @param {Object<Object|boolean>} mask - The mask to apply to the object
|
|
*/
|
|
function maskObject (object, mask) {
|
|
return Object.keys(object)
|
|
.reduce(
|
|
(state, key) => {
|
|
if (mask[key] === true) {
|
|
state[key] = object[key]
|
|
} else if (mask[key]) {
|
|
state[key] = maskObject(object[key], mask[key])
|
|
}
|
|
return state
|
|
},
|
|
{},
|
|
)
|
|
}
|
|
|
|
function setupDebuggingHelpers (store) {
|
|
window.getCleanAppState = function () {
|
|
const state = clone(store.getState())
|
|
state.version = global.platform.getVersion()
|
|
state.browser = window.navigator.userAgent
|
|
return state
|
|
}
|
|
window.getSentryState = function () {
|
|
const fullState = store.getState()
|
|
const debugState = maskObject(fullState, SENTRY_STATE)
|
|
return {
|
|
browser: window.navigator.userAgent,
|
|
store: debugState,
|
|
version: global.platform.getVersion(),
|
|
}
|
|
}
|
|
}
|
|
|
|
window.logStateString = function (cb) {
|
|
const state = window.getCleanAppState()
|
|
global.platform.getPlatformInfo((err, platform) => {
|
|
if (err) {
|
|
return cb(err)
|
|
}
|
|
state.platform = platform
|
|
const stateString = JSON.stringify(state, null, 2)
|
|
cb(null, stateString)
|
|
})
|
|
}
|
|
|
|
window.logState = function (toClipboard) {
|
|
return window.logStateString((err, result) => {
|
|
if (err) {
|
|
console.error(err.message)
|
|
} else if (toClipboard) {
|
|
copyToClipboard(result)
|
|
console.log('State log copied')
|
|
} else {
|
|
console.log(result)
|
|
}
|
|
})
|
|
}
|